chore(ui,icons,ui-preset,toolbox): Move design system packages to monorepo (#5470)

This commit is contained in:
Kasper Fabricius Kristensen
2023-11-07 22:17:44 +01:00
committed by GitHub
parent 71853eafdd
commit e4ce2f4e07
722 changed files with 30300 additions and 186 deletions
@@ -0,0 +1,42 @@
import {
Effect,
EffectBlur,
EffectShadow,
EffectType,
Paint,
PaintGradient,
PaintImage,
PaintSolid,
PaintType,
} from "./types"
export function isEffectShadow(effect: Effect): effect is EffectShadow {
return (
effect.type === EffectType.DROP_SHADOW ||
effect.type === EffectType.INNER_SHADOW
)
}
export function isEffectBlur(effect: Effect): effect is EffectBlur {
return (
effect.type === EffectType.BACKGROUND_BLUR ||
effect.type === EffectType.LAYER_BLUR
)
}
export function isPaintSolid(paint: Paint): paint is PaintSolid {
return paint.type === PaintType.SOLID
}
export function isPaintGradient(paint: Paint): paint is PaintGradient {
return (
paint.type === PaintType.GRADIENT_ANGULAR ||
paint.type === PaintType.GRADIENT_DIAMOND ||
paint.type === PaintType.GRADIENT_LINEAR ||
paint.type === PaintType.GRADIENT_RADIAL
)
}
export function isPaintImage(paint: Paint): paint is PaintImage {
return paint.type === PaintType.IMAGE
}
@@ -0,0 +1,56 @@
import axios, { AxiosInstance, AxiosRequestConfig, Method } from "axios"
import axiosRetry from "axios-retry"
export type ClientArgs = {
accessToken: string
baseURL: string
maxRetries?: number
}
class Client {
private _axios: AxiosInstance
constructor({ accessToken, baseURL, maxRetries = 3 }: ClientArgs) {
const instance = axios.create({
baseURL,
headers: {
"X-FIGMA-TOKEN": accessToken,
},
})
// Typecast to any because our monorepo is using an older version of axios-retry and axios
axiosRetry(instance as any, {
retries: maxRetries,
retryDelay: axiosRetry.exponentialDelay,
})
this._axios = instance
}
async request(
method: Method,
url: string,
payload: Record<string, any> = {},
config?: AxiosRequestConfig
) {
const requestConfig: AxiosRequestConfig = {
method,
url,
...config,
}
if (["POST", "DELETE"].includes(method)) {
requestConfig.data = payload
}
const response = await this._axios.request(requestConfig)
if (Math.floor(response.status / 100) !== 2) {
throw response.statusText
}
return response.data
}
}
export default Client
@@ -0,0 +1,308 @@
import axios from "axios"
import Client, { ClientArgs } from "./client"
import {
FrameOffset,
GetCommentsResult,
GetComponentResult,
GetComponentSetResult,
GetFileComponentSetsResult,
GetFileComponentsResult,
GetFileNodesResult,
GetFileResult,
GetFileStylesResult,
GetImageFillsResult,
GetImageResult,
GetProjectFilesResult,
GetStyleResult,
GetTeamComponentSetsResult,
GetTeamComponentsResult,
GetTeamProjectsResult,
GetTeamStylesResult,
GetVersionsResult,
NodeType,
PostCommentResult,
Vector,
} from "./types"
import { encodeQuery } from "./utils"
type FigmaArgs = Omit<ClientArgs, "baseURL">
const FIGMA_BASE_URL = "https://api.figma.com/v1/"
class Figma {
private _api: Client
constructor({ accessToken, maxRetries = 3 }: FigmaArgs) {
this._api = new Client({
accessToken,
baseURL: FIGMA_BASE_URL,
maxRetries,
})
}
/**
* Get a resource by its URL.
*/
async getResource(url: string) {
const response = await axios.get(url)
if (Math.floor(response.status / 100) !== 2) {
throw response.statusText
}
return response.data
}
async getFile(
file_key: string,
options?: {
/** A specific version ID to get. Omitting this will get the current version of the file */
version?: string
/** If specified, only a subset of the document will be returned corresponding to the nodes listed, their children, and everything between the root node and the listed nodes */
ids?: string[]
/** Positive integer representing how deep into the document tree to traverse. For example, setting this to 1 returns only Pages, setting it to 2 returns Pages and all top level objects on each page. Not setting this parameter returns all nodes */
depth?: number
/** Set to "paths" to export vector data */
geometry?: "paths"
/** A comma separated list of plugin IDs and/or the string "shared". Any data present in the document written by those plugins will be included in the result in the `pluginData` and `sharedPluginData` properties. */
plugin_data?: string
/** Set to returns branch metadata for the requested file */
branch_data?: boolean
}
): Promise<GetFileResult> {
const queryString = options
? `?${encodeQuery({
...options,
ids: options.ids && options.ids.join(","),
})}`
: ""
return this._api.request("GET", `files/${file_key}${queryString}`)
}
async getFileNodes<TNode extends NodeType = "DOCUMENT">(
file_key: string,
options: {
/** A comma separated list of node IDs to retrieve and convert */
ids: string[]
/** A specific version ID to get. Omitting this will get the current version of the file */
version?: string
/** Positive integer representing how deep into the document tree to traverse. For example, setting this to 1 returns only Pages, setting it to 2 returns Pages and all top level objects on each page. Not setting this parameter returns all nodes */
depth?: number
/** Set to "paths" to export vector data */
geometry?: "paths"
/** A comma separated list of plugin IDs and/or the string "shared". Any data present in the document written by those plugins will be included in the result in the `pluginData` and `sharedPluginData` properties. */
plugin_data?: string
}
): Promise<GetFileNodesResult<TNode>> {
const queryString = `?${encodeQuery({
...options,
ids: options.ids.join(","),
})}`
return this._api.request("GET", `files/${file_key}/nodes${queryString}`)
}
async getImage(
file_key: string,
options: {
/** A comma separated list of node IDs to render */
ids: string[]
/** A number between 0.01 and 4, the image scaling factor */
scale: number
/** A string enum for the image output format */
format: "jpg" | "png" | "svg" | "pdf"
/** Whether to include id attributes for all SVG elements. `Default: false` */
svg_include_id?: boolean
/** Whether to simplify inside/outside strokes and use stroke attribute if possible instead of <mask>. `Default: true` */
svg_simplify_stroke?: boolean
/** Use the full dimensions of the node regardless of whether or not it is cropped or the space around it is empty. Use this to export text nodes without cropping. `Default: false` */
use_absolute_bounds?: boolean
/** A specific version ID to get. Omitting this will get the current version of the file */
version?: string
}
): Promise<GetImageResult> {
const queryString = options
? `?${encodeQuery({
...options,
ids: options.ids && options.ids.join(","),
})}`
: ""
return this._api.request("GET", `images/${file_key}${queryString}`)
}
async getImageFills(file_key: string): Promise<GetImageFillsResult> {
return this._api.request("GET", `files/${file_key}/images`)
}
async getComments(file_key: string): Promise<GetCommentsResult> {
return this._api.request("GET", `files/${file_key}/comments`)
}
async postComment(
file_key: string,
/** The text contents of the comment to post */
message: string,
/** The position of where to place the comment. This can either be an absolute canvas position or the relative position within a frame. */
client_meta: Vector | FrameOffset,
/** (Optional) The comment to reply to, if any. This must be a root comment, that is, you cannot reply to a comment that is a reply itself (a reply has a parent_id). */
comment_id?: string
): Promise<PostCommentResult> {
return this._api.request("POST", `files/${file_key}/comments`, {
message,
client_meta,
comment_id,
})
}
async deleteComment(file_key: string, comment_id: string): Promise<void> {
return this._api.request(
"DELETE",
`files/${file_key}/comments/${comment_id}`
)
}
async getVersions(file_key: string): Promise<GetVersionsResult> {
return this._api.request("GET", `files/${file_key}/versions`)
}
async getTeamProjects(teamId: string): Promise<GetTeamProjectsResult> {
return this._api.request("GET", `teams/${teamId}/projects`)
}
async getProjectFiles(
project_id: string,
options: {
/** Set to returns branch metadata for the requested file */
branch_data?: boolean
}
): Promise<GetProjectFilesResult> {
const queryString = options
? `?${encodeQuery({
...options,
})}`
: ""
return this._api.request(
"GET",
`projects/${project_id}/files${queryString}`
)
}
/**
* Get a paginated list of published components within a team library
*/
async getTeamComponents(
team_id: string,
options: {
/** Number of items in a paged list of results. Defaults to 30. */
page_size?: number
/** Cursor indicating which id after which to start retrieving components for. Exclusive with before. The cursor value is an internally tracked integer that doesn't correspond to any Ids */
after?: number
/** Cursor indicating which id before which to start retrieving components for. Exclusive with after. The cursor value is an internally tracked integer that doesn't correspond to any Ids */
before?: number
}
): Promise<GetTeamComponentsResult> {
const queryString = options ? `?${encodeQuery(options)}` : ""
return this._api.request("GET", `teams/${team_id}/components${queryString}`)
}
/**
* Get a list of published components within a file library
*/
async getFileComponents(file_key: string): Promise<GetFileComponentsResult> {
return this._api.request("GET", `files/${file_key}/components`)
}
/**
* Get metadata on a component by key.
*/
async getComponent(component_key: string): Promise<GetComponentResult> {
return this._api.request("GET", `components/${component_key}`)
}
/**
* Get a paginated list of published component_sets within a team library
*/
async getTeamComponentSets(
team_id: string,
options: {
/** Number of items in a paged list of results. Defaults to 30. */
page_size?: number
/** Cursor indicating which id after which to start retrieving component sets for. Exclusive with before. The cursor value is an internally tracked integer that doesn't correspond to any Ids */
after?: number
/** Cursor indicating which id before which to start retrieving component sets for. Exclusive with after. The cursor value is an internally tracked integer that doesn't correspond to any Ids */
before?: number
}
): Promise<GetTeamComponentSetsResult> {
const queryString = options ? `?${encodeQuery(options)}` : ""
return this._api.request(
"GET",
`teams/${team_id}/component_sets${queryString}`
)
}
async getFileComponentSets(
file_key: string
): Promise<GetFileComponentSetsResult> {
return this._api.request("GET", `files/${file_key}/component_sets`)
}
/**
* Get metadata on a component_set by key.
*/
async getComponentSet(key: string): Promise<GetComponentSetResult> {
return this._api.request("GET", `component_sets/${key}`)
}
/**
* Get a paginated list of published styles within a team library
*/
async getTeamStyles(
team_id: string,
options: {
/** Number of items in a paged list of results. Defaults to 30. */
page_size?: number
/** Cursor indicating which id after which to start retrieving styles for. Exclusive with before. The cursor value is an internally tracked integer that doesn't correspond to any Ids */
after?: number
/** Cursor indicating which id before which to start retrieving styles for. Exclusive with after. The cursor value is an internally tracked integer that doesn't correspond to any Ids */
before?: number
}
): Promise<GetTeamStylesResult> {
const queryString = options ? `?${encodeQuery(options)}` : ""
return this._api.request("GET", `teams/${team_id}/styles${queryString}`)
}
/**
* Get a list of published styles within a file library
*/
async getFileStyles(file_key: string): Promise<GetFileStylesResult> {
return this._api.request("GET", `files/${file_key}/styles`)
}
/**
* Get metadata on a style by key.
*/
async getStyle(key: string): Promise<GetStyleResult> {
return this._api.request("GET", `styles/${key}`)
}
// Variables - Beta API (TODO)
// Webhooks - Beta API (TODO)
// Activity Logs - Beta API (TODO)
// Payments - Beta API (TODO)
// Dev Resources - Beta API (TODO)
}
export * from "./assertions"
export * from "./types"
export default Figma
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,25 @@
import { Node, NodeType } from "./types"
/**
* Checks if a node is of a certain type.
*/
export function isNodeType<NType extends NodeType, R = Node<NType>>(
node: Node<any>,
type: NType
): node is R {
return node.type === type
}
/**
* Encodes an object into a query string.
*/
export function encodeQuery(obj: any): string {
if (!obj) {
return ""
}
return Object.entries(obj)
.map(([k, v]) => k && v && `${k}=${encodeURIComponent(v as any)}`)
.filter(Boolean)
.join("&")
}