@dannote/figma-use 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Figma Variable Bindings (StyleX-inspired API)
3
+ *
4
+ * @example
5
+ * ```tsx
6
+ * // tokens.figma.ts - with explicit values (recommended)
7
+ * export const colors = defineVars({
8
+ * primary: { name: 'Colors/Gray/50', value: '#F8FAFC' },
9
+ * accent: { name: 'Colors/Blue/500', value: '#3B82F6' },
10
+ * })
11
+ *
12
+ * // tokens.figma.ts - name only (value loaded from Figma)
13
+ * export const colors = defineVars({
14
+ * primary: 'Colors/Gray/50',
15
+ * accent: 'Colors/Blue/500',
16
+ * })
17
+ *
18
+ * // Card.figma.tsx
19
+ * <Frame style={{ backgroundColor: colors.primary }}>
20
+ * ```
21
+ */
22
+
23
+ import { parseColor } from '../color.ts'
24
+
25
+ const VAR_SYMBOL = Symbol.for('figma.variable')
26
+
27
+ /** Variable definition - either string name or object with name and value */
28
+ export type VarDef = string | { name: string; value: string }
29
+
30
+ export interface FigmaVariable {
31
+ [VAR_SYMBOL]: true
32
+ name: string // Variable name like "Colors/Gray/50"
33
+ value?: string // Fallback color value like "#F8FAFC"
34
+ _resolved?: { // Filled in at render time
35
+ id: string
36
+ sessionID: number
37
+ localID: number
38
+ }
39
+ }
40
+
41
+ export interface ResolvedVariable {
42
+ id: string
43
+ sessionID: number
44
+ localID: number
45
+ }
46
+
47
+ /**
48
+ * Check if value is a Figma variable reference
49
+ */
50
+ export function isVariable(value: unknown): value is FigmaVariable {
51
+ return typeof value === 'object' && value !== null && VAR_SYMBOL in value
52
+ }
53
+
54
+ /**
55
+ * Variable registry - maps names to IDs
56
+ * Populated by loadVariables() before render
57
+ */
58
+ const variableRegistry = new Map<string, ResolvedVariable>()
59
+
60
+ /**
61
+ * Load variables from Figma into registry
62
+ */
63
+ export function loadVariablesIntoRegistry(variables: Array<{ id: string; name: string }>) {
64
+ variableRegistry.clear()
65
+ for (const v of variables) {
66
+ const match = v.id.match(/VariableID:(\d+):(\d+)/)
67
+ if (match) {
68
+ variableRegistry.set(v.name, {
69
+ id: v.id,
70
+ sessionID: parseInt(match[1], 10),
71
+ localID: parseInt(match[2], 10),
72
+ })
73
+ }
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Resolve a variable name to its ID
79
+ * @throws if variable not found in registry
80
+ */
81
+ export function resolveVariable(variable: FigmaVariable): ResolvedVariable {
82
+ // Already resolved?
83
+ if (variable._resolved) {
84
+ return variable._resolved
85
+ }
86
+
87
+ // Check if it's an ID format (legacy support)
88
+ const idMatch = variable.name.match(/^(?:VariableID:)?(\d+):(\d+)$/)
89
+ if (idMatch) {
90
+ const resolved = {
91
+ id: `VariableID:${idMatch[1]}:${idMatch[2]}`,
92
+ sessionID: parseInt(idMatch[1], 10),
93
+ localID: parseInt(idMatch[2], 10),
94
+ }
95
+ variable._resolved = resolved
96
+ return resolved
97
+ }
98
+
99
+ // Lookup by name
100
+ const resolved = variableRegistry.get(variable.name)
101
+ if (!resolved) {
102
+ const available = Array.from(variableRegistry.keys()).slice(0, 5).join(', ')
103
+ throw new Error(
104
+ `Variable "${variable.name}" not found. ` +
105
+ `Available: ${available}${variableRegistry.size > 5 ? '...' : ''}. ` +
106
+ `Make sure variables are loaded before render.`
107
+ )
108
+ }
109
+
110
+ variable._resolved = resolved
111
+ return resolved
112
+ }
113
+
114
+ /**
115
+ * Check if variable registry is populated
116
+ */
117
+ export function isRegistryLoaded(): boolean {
118
+ return variableRegistry.size > 0
119
+ }
120
+
121
+ /**
122
+ * Get registry size (for debugging)
123
+ */
124
+ export function getRegistrySize(): number {
125
+ return variableRegistry.size
126
+ }
127
+
128
+ /**
129
+ * Define Figma variables for use in styles
130
+ *
131
+ * @example
132
+ * ```ts
133
+ * // With explicit fallback values (recommended)
134
+ * export const colors = defineVars({
135
+ * primary: { name: 'Colors/Gray/50', value: '#F8FAFC' },
136
+ * accent: { name: 'Colors/Blue/500', value: '#3B82F6' },
137
+ * })
138
+ *
139
+ * // Name only (value loaded from Figma registry)
140
+ * export const colors = defineVars({
141
+ * primary: 'Colors/Gray/50',
142
+ * })
143
+ *
144
+ * // Use in components:
145
+ * <Frame style={{ backgroundColor: colors.primary }} />
146
+ * ```
147
+ */
148
+ export function defineVars<T extends Record<string, VarDef>>(
149
+ vars: T
150
+ ): { [K in keyof T]: FigmaVariable } {
151
+ const result = {} as { [K in keyof T]: FigmaVariable }
152
+
153
+ for (const [key, def] of Object.entries(vars)) {
154
+ if (typeof def === 'string') {
155
+ result[key as keyof T] = {
156
+ [VAR_SYMBOL]: true,
157
+ name: def,
158
+ }
159
+ } else {
160
+ result[key as keyof T] = {
161
+ [VAR_SYMBOL]: true,
162
+ name: def.name,
163
+ value: def.value,
164
+ }
165
+ }
166
+ }
167
+
168
+ return result
169
+ }
170
+
171
+ /**
172
+ * Shorthand for single variable
173
+ *
174
+ * @example
175
+ * ```ts
176
+ * const primaryColor = figmaVar('Colors/Gray/50', '#F8FAFC')
177
+ * ```
178
+ */
179
+ export function figmaVar(name: string, value?: string): FigmaVariable {
180
+ return {
181
+ [VAR_SYMBOL]: true,
182
+ name,
183
+ value,
184
+ }
185
+ }