109 lines
3.8 KiB
JavaScript
109 lines
3.8 KiB
JavaScript
/**
|
|
* createSemanticTokens
|
|
*
|
|
* Creates a batch of Figma variables in the given collection, one per entry in
|
|
* `tokenMap`. Supports raw values, variable alias references, code syntax, and
|
|
* scopes. Returns a map of token name → Variable for use in subsequent steps.
|
|
*
|
|
* @param {VariableCollection} collection - The target variable collection.
|
|
* @param {Record<string, string>} modeIds - Map of {modeName: modeId} from createVariableCollection.
|
|
* @param {Array<{
|
|
* name: string,
|
|
* type: 'COLOR' | 'FLOAT' | 'STRING' | 'BOOLEAN',
|
|
* values: Record<string, string | number | boolean | {type: 'VARIABLE_ALIAS', id: string}>,
|
|
* scopes?: VariableScope[],
|
|
* codeSyntax?: {WEB?: string, ANDROID?: string, iOS?: string}
|
|
* }>} tokenMap - Ordered list of token definitions.
|
|
* - `name`: Variable name using slash hierarchy (e.g. "color/bg/primary").
|
|
* - `type`: Figma variable type.
|
|
* - `values`: Map of {modeName: value}. Values can be raw (hex string for COLOR,
|
|
* number for FLOAT) or alias objects {type: 'VARIABLE_ALIAS', id: variableId}.
|
|
* For COLOR, raw values are accepted as hex strings ("#rrggbb" or "#rrggbbaa")
|
|
* and converted to {r, g, b, a} automatically.
|
|
* - `scopes`: Array of VariableScope strings. Omit to use [] (hidden/primitive).
|
|
* - `codeSyntax`: Platform code syntax strings. Omit to skip.
|
|
* @param {string} [runId] - Optional dsb_run_id to tag every variable.
|
|
* @returns {Promise<{variables: Record<string, Variable>}>}
|
|
* `variables` maps each token name to its created Variable object.
|
|
*/
|
|
async function createSemanticTokens(collection, modeIds, tokenMap, runId) {
|
|
const variables = {}
|
|
|
|
for (const token of tokenMap) {
|
|
// Create the variable
|
|
const variable = figma.variables.createVariable(token.name, collection, token.type)
|
|
|
|
// Tag for cleanup
|
|
variable.setPluginData('dsb_key', `variable/${token.name}`)
|
|
if (runId) {
|
|
variable.setPluginData('dsb_run_id', runId)
|
|
}
|
|
|
|
// Set values for each mode
|
|
for (const [modeName, rawValue] of Object.entries(token.values)) {
|
|
const modeId = modeIds[modeName]
|
|
if (!modeId) {
|
|
throw new Error(
|
|
`createSemanticTokens: mode "${modeName}" not found in modeIds for token "${token.name}". ` +
|
|
`Available modes: ${Object.keys(modeIds).join(', ')}`,
|
|
)
|
|
}
|
|
|
|
let value = rawValue
|
|
|
|
// Convert hex strings to Figma RGBA for COLOR type
|
|
if (token.type === 'COLOR' && typeof rawValue === 'string' && rawValue.startsWith('#')) {
|
|
value = hexToFigmaColor(rawValue)
|
|
}
|
|
|
|
variable.setValueForMode(modeId, value)
|
|
}
|
|
|
|
// Set scopes (default: empty array = hidden from property pickers / primitives)
|
|
variable.scopes = token.scopes || []
|
|
|
|
// Set code syntax per platform
|
|
if (token.codeSyntax) {
|
|
if (token.codeSyntax.WEB) {
|
|
variable.setVariableCodeSyntax('WEB', token.codeSyntax.WEB)
|
|
}
|
|
if (token.codeSyntax.ANDROID) {
|
|
variable.setVariableCodeSyntax('ANDROID', token.codeSyntax.ANDROID)
|
|
}
|
|
if (token.codeSyntax.iOS) {
|
|
variable.setVariableCodeSyntax('iOS', token.codeSyntax.iOS)
|
|
}
|
|
}
|
|
|
|
variables[token.name] = variable
|
|
}
|
|
|
|
return { variables }
|
|
}
|
|
|
|
/**
|
|
* Converts a hex color string to a Figma RGBA object.
|
|
* Supports "#rgb", "#rrggbb", and "#rrggbbaa".
|
|
*
|
|
* @param {string} hex
|
|
* @returns {{ r: number, g: number, b: number, a: number }}
|
|
*/
|
|
function hexToFigmaColor(hex) {
|
|
let h = hex.replace('#', '')
|
|
|
|
// Expand shorthand #rgb → #rrggbb
|
|
if (h.length === 3) {
|
|
h = h
|
|
.split('')
|
|
.map((c) => c + c)
|
|
.join('')
|
|
}
|
|
|
|
const r = parseInt(h.substring(0, 2), 16) / 255
|
|
const g = parseInt(h.substring(2, 4), 16) / 255
|
|
const b = parseInt(h.substring(4, 6), 16) / 255
|
|
const a = h.length === 8 ? parseInt(h.substring(6, 8), 16) / 255 : 1
|
|
|
|
return { r, g, b, a }
|
|
}
|