jfs-components 0.1.34 → 0.1.36
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.
- package/CHANGELOG.md +14 -0
- package/D2C.md +82 -87
- package/lib/commonjs/components/LottiePlayer/LottiePlayer.js +5 -5
- package/lib/commonjs/components/LottiePlayer/LottiePlayer.web.js +2 -2
- package/lib/commonjs/components/PageHero/PageHero.js +73 -76
- package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +42204 -1
- package/lib/commonjs/design-tokens/figma-modes.generated.js +52 -37
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/module/components/LottiePlayer/LottiePlayer.js +5 -5
- package/lib/module/components/LottiePlayer/LottiePlayer.web.js +2 -2
- package/lib/module/components/PageHero/PageHero.js +73 -76
- package/lib/module/design-tokens/Coin Variables-variables-full.json +42204 -1
- package/lib/module/design-tokens/figma-modes.generated.js +52 -37
- package/lib/module/icons/registry.js +1 -1
- package/lib/typescript/src/components/LottiePlayer/LottiePlayer.d.ts +6 -6
- package/lib/typescript/src/components/PageHero/PageHero.d.ts +33 -25
- package/lib/typescript/src/design-tokens/figma-modes.generated.d.ts +14 -1
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/package.json +3 -1
- package/scripts/extract-figma-modes.js +150 -121
- package/src/components/LottiePlayer/LottiePlayer.tsx +8 -8
- package/src/components/LottiePlayer/LottiePlayer.web.tsx +2 -2
- package/src/components/PageHero/PageHero.tsx +142 -106
- package/src/design-tokens/Coin Variables-variables-full.json +42204 -1
- package/src/design-tokens/figma-modes.generated.ts +52 -37
- package/src/icons/registry.ts +1 -1
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
* GET /v1/files/:key call per extraction.
|
|
22
22
|
*
|
|
23
23
|
* Usage:
|
|
24
|
-
*
|
|
24
|
+
* # Either add FIGMA_ACCESS_TOKEN=figd_... to .env in the current project,
|
|
25
|
+
* # or export it in the shell (shell values take precedence over .env).
|
|
25
26
|
* npx jfs-extract-modes "https://www.figma.com/design/<fileKey>/...?node-id=1-10636"
|
|
26
27
|
* npx jfs-extract-modes <fileKey> <nodeId> [--out modes.json]
|
|
27
28
|
*
|
|
@@ -41,105 +42,132 @@
|
|
|
41
42
|
* }
|
|
42
43
|
*/
|
|
43
44
|
|
|
44
|
-
"use strict"
|
|
45
|
+
"use strict"
|
|
45
46
|
|
|
46
|
-
const fs = require("fs")
|
|
47
|
-
const path = require("path")
|
|
47
|
+
const fs = require("fs")
|
|
48
|
+
const path = require("path")
|
|
49
|
+
const dotenv = require("dotenv")
|
|
48
50
|
|
|
49
|
-
const API_BASE = "https://api.figma.com/v1"
|
|
50
|
-
const TOKENS_DIR = path.join(__dirname, "..", "src", "design-tokens")
|
|
51
|
-
const COLLECTION_KEY_PATTERN = /^VariableCollectionId:([0-9a-f]{40})
|
|
51
|
+
const API_BASE = "https://api.figma.com/v1"
|
|
52
|
+
const TOKENS_DIR = path.join(__dirname, "..", "src", "design-tokens")
|
|
53
|
+
const COLLECTION_KEY_PATTERN = /^VariableCollectionId:([0-9a-f]{40})\//
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Load the consumer project's root .env without replacing values that were
|
|
57
|
+
* explicitly exported in the shell. The CLI is normally launched by npx from
|
|
58
|
+
* the consumer project, so process.cwd() is the correct project root rather
|
|
59
|
+
* than the installed package directory.
|
|
60
|
+
*/
|
|
61
|
+
function loadProjectEnv(cwd = process.cwd()) {
|
|
62
|
+
const envPath = path.resolve(cwd, ".env")
|
|
63
|
+
if (!fs.existsSync(envPath)) return null
|
|
64
|
+
|
|
65
|
+
const result = dotenv.config({
|
|
66
|
+
path: envPath,
|
|
67
|
+
override: false,
|
|
68
|
+
quiet: true,
|
|
69
|
+
})
|
|
70
|
+
if (result.error) {
|
|
71
|
+
throw new Error(`Could not load ${envPath}: ${result.error.message}`)
|
|
72
|
+
}
|
|
73
|
+
return envPath
|
|
74
|
+
}
|
|
52
75
|
|
|
53
76
|
function usage(exitCode) {
|
|
54
77
|
console.error(
|
|
55
78
|
`Usage:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
79
|
+
jfs-extract-modes <figma-url> [--out modes.json]
|
|
80
|
+
jfs-extract-modes <fileKey> <nodeId> [--out modes.json]
|
|
81
|
+
jfs-extract-modes --add-keys <master-library-fileKey-or-url>
|
|
82
|
+
|
|
83
|
+
Set FIGMA_ACCESS_TOKEN in the shell or in .env in the current project.`,
|
|
84
|
+
)
|
|
85
|
+
process.exit(exitCode)
|
|
61
86
|
}
|
|
62
87
|
|
|
63
88
|
function fileKeyFromUrlOrKey(value) {
|
|
64
89
|
if (/^https?:\/\//.test(value)) {
|
|
65
|
-
const match = new URL(value).pathname.match(/\/(?:design|file)\/([^/]+)/)
|
|
90
|
+
const match = new URL(value).pathname.match(/\/(?:design|file)\/([^/]+)/)
|
|
66
91
|
if (!match) {
|
|
67
|
-
console.error("Could not parse fileKey from URL.")
|
|
68
|
-
process.exit(1)
|
|
92
|
+
console.error("Could not parse fileKey from URL.")
|
|
93
|
+
process.exit(1)
|
|
69
94
|
}
|
|
70
|
-
return match[1]
|
|
95
|
+
return match[1]
|
|
71
96
|
}
|
|
72
|
-
return value
|
|
97
|
+
return value
|
|
73
98
|
}
|
|
74
99
|
|
|
75
100
|
function parseArgs(argv) {
|
|
76
|
-
const args = argv.slice(2)
|
|
77
|
-
const out = { fileKey: null, nodeId: null, outPath: null, addKeysFile: null }
|
|
101
|
+
const args = argv.slice(2)
|
|
102
|
+
const out = { fileKey: null, nodeId: null, outPath: null, addKeysFile: null }
|
|
78
103
|
|
|
79
104
|
for (let i = 0; i < args.length; i += 1) {
|
|
80
|
-
const arg = args[i]
|
|
105
|
+
const arg = args[i]
|
|
81
106
|
if (arg === "--out") {
|
|
82
|
-
out.outPath = args[++i]
|
|
107
|
+
out.outPath = args[++i]
|
|
83
108
|
} else if (arg === "--add-keys") {
|
|
84
|
-
out.addKeysFile = fileKeyFromUrlOrKey(args[++i] ?? "")
|
|
109
|
+
out.addKeysFile = fileKeyFromUrlOrKey(args[++i] ?? "")
|
|
85
110
|
} else if (arg === "--help" || arg === "-h") {
|
|
86
|
-
usage(0)
|
|
111
|
+
usage(0)
|
|
87
112
|
} else if (/^https?:\/\//.test(arg)) {
|
|
88
|
-
const url = new URL(arg)
|
|
89
|
-
out.fileKey = fileKeyFromUrlOrKey(arg)
|
|
90
|
-
const nodeParam = url.searchParams.get("node-id")
|
|
91
|
-
if (nodeParam) out.nodeId = nodeParam.replace(/-/g, ":")
|
|
113
|
+
const url = new URL(arg)
|
|
114
|
+
out.fileKey = fileKeyFromUrlOrKey(arg)
|
|
115
|
+
const nodeParam = url.searchParams.get("node-id")
|
|
116
|
+
if (nodeParam) out.nodeId = nodeParam.replace(/-/g, ":")
|
|
92
117
|
} else if (!out.fileKey) {
|
|
93
|
-
out.fileKey = arg
|
|
118
|
+
out.fileKey = arg
|
|
94
119
|
} else if (!out.nodeId) {
|
|
95
|
-
out.nodeId = arg.replace(/-/g, ":")
|
|
120
|
+
out.nodeId = arg.replace(/-/g, ":")
|
|
96
121
|
} else {
|
|
97
|
-
usage(1)
|
|
122
|
+
usage(1)
|
|
98
123
|
}
|
|
99
124
|
}
|
|
100
125
|
|
|
101
|
-
if (out.addKeysFile) return out
|
|
102
|
-
if (!out.fileKey || !out.nodeId) usage(1)
|
|
103
|
-
return out
|
|
126
|
+
if (out.addKeysFile) return out
|
|
127
|
+
if (!out.fileKey || !out.nodeId) usage(1)
|
|
128
|
+
return out
|
|
104
129
|
}
|
|
105
130
|
|
|
106
131
|
function getToken() {
|
|
107
|
-
const token = process.env.FIGMA_ACCESS_TOKEN || process.env.FIGMA_TOKEN
|
|
132
|
+
const token = process.env.FIGMA_ACCESS_TOKEN || process.env.FIGMA_TOKEN
|
|
108
133
|
if (!token) {
|
|
109
|
-
console.error(
|
|
110
|
-
|
|
134
|
+
console.error(
|
|
135
|
+
"Set FIGMA_ACCESS_TOKEN in the shell or in the project-root .env " +
|
|
136
|
+
"(requires only the Figma file_content:read scope).",
|
|
137
|
+
)
|
|
138
|
+
process.exit(1)
|
|
111
139
|
}
|
|
112
|
-
return token
|
|
140
|
+
return token
|
|
113
141
|
}
|
|
114
142
|
|
|
115
143
|
async function figmaGet(urlPath, token) {
|
|
116
144
|
const response = await fetch(`${API_BASE}${urlPath}`, {
|
|
117
145
|
headers: { "X-Figma-Token": token },
|
|
118
|
-
})
|
|
119
|
-
const body = await response.json().catch(() => ({}))
|
|
146
|
+
})
|
|
147
|
+
const body = await response.json().catch(() => ({}))
|
|
120
148
|
if (!response.ok) {
|
|
121
149
|
throw new Error(
|
|
122
150
|
`Figma API ${response.status} for ${urlPath}: ${body.err || body.message || "request failed"}`,
|
|
123
|
-
)
|
|
151
|
+
)
|
|
124
152
|
}
|
|
125
|
-
return body
|
|
153
|
+
return body
|
|
126
154
|
}
|
|
127
155
|
|
|
128
156
|
function listVariablesJsonFiles() {
|
|
129
|
-
let files
|
|
157
|
+
let files
|
|
130
158
|
try {
|
|
131
159
|
files = fs
|
|
132
160
|
.readdirSync(TOKENS_DIR)
|
|
133
161
|
.filter((name) => name.endsWith("-variables-full.json"))
|
|
134
|
-
.map((name) => path.join(TOKENS_DIR, name))
|
|
162
|
+
.map((name) => path.join(TOKENS_DIR, name))
|
|
135
163
|
} catch {
|
|
136
|
-
files = []
|
|
164
|
+
files = []
|
|
137
165
|
}
|
|
138
166
|
if (files.length === 0) {
|
|
139
|
-
console.error(`No *-variables-full.json found in ${TOKENS_DIR}.`)
|
|
140
|
-
process.exit(1)
|
|
167
|
+
console.error(`No *-variables-full.json found in ${TOKENS_DIR}.`)
|
|
168
|
+
process.exit(1)
|
|
141
169
|
}
|
|
142
|
-
return files
|
|
170
|
+
return files
|
|
143
171
|
}
|
|
144
172
|
|
|
145
173
|
/**
|
|
@@ -148,22 +176,22 @@ function listVariablesJsonFiles() {
|
|
|
148
176
|
* resolution is the only supported path.
|
|
149
177
|
*/
|
|
150
178
|
function buildKeyIndex() {
|
|
151
|
-
const byKey = new Map()
|
|
152
|
-
const missingKeys = []
|
|
153
|
-
const files = listVariablesJsonFiles()
|
|
179
|
+
const byKey = new Map()
|
|
180
|
+
const missingKeys = []
|
|
181
|
+
const files = listVariablesJsonFiles()
|
|
154
182
|
|
|
155
183
|
for (const file of files) {
|
|
156
|
-
const data = JSON.parse(fs.readFileSync(file, "utf8"))
|
|
184
|
+
const data = JSON.parse(fs.readFileSync(file, "utf8"))
|
|
157
185
|
for (const collection of data.collections ?? []) {
|
|
158
186
|
if (typeof collection.key !== "string" || collection.key.length === 0) {
|
|
159
|
-
missingKeys.push(`${collection.name} (${path.basename(file)})`)
|
|
160
|
-
continue
|
|
187
|
+
missingKeys.push(`${collection.name} (${path.basename(file)})`)
|
|
188
|
+
continue
|
|
161
189
|
}
|
|
162
|
-
const modes = new Map()
|
|
190
|
+
const modes = new Map()
|
|
163
191
|
for (const mode of collection.modes ?? []) {
|
|
164
|
-
modes.set(mode.modeId, mode.name)
|
|
192
|
+
modes.set(mode.modeId, mode.name)
|
|
165
193
|
}
|
|
166
|
-
byKey.set(collection.key, { name: collection.name, modes })
|
|
194
|
+
byKey.set(collection.key, { name: collection.name, modes })
|
|
167
195
|
}
|
|
168
196
|
}
|
|
169
197
|
|
|
@@ -172,48 +200,48 @@ function buildKeyIndex() {
|
|
|
172
200
|
"The variables JSON has no collection keys, so modes cannot be resolved.\n" +
|
|
173
201
|
"Run this once against the master variables library file to patch them in:\n" +
|
|
174
202
|
" jfs-extract-modes --add-keys <master-library-fileKey-or-url>",
|
|
175
|
-
)
|
|
176
|
-
process.exit(1)
|
|
203
|
+
)
|
|
204
|
+
process.exit(1)
|
|
177
205
|
}
|
|
178
206
|
if (missingKeys.length > 0) {
|
|
179
207
|
console.error(
|
|
180
208
|
`Warning: ${missingKeys.length} collection(s) have no key and were skipped:\n ` +
|
|
181
209
|
missingKeys.slice(0, 10).join("\n "),
|
|
182
|
-
)
|
|
210
|
+
)
|
|
183
211
|
}
|
|
184
212
|
|
|
185
213
|
console.error(
|
|
186
214
|
`Indexed ${byKey.size} collection(s) by key from ${files.length} variables JSON file(s).`,
|
|
187
|
-
)
|
|
188
|
-
return byKey
|
|
215
|
+
)
|
|
216
|
+
return byKey
|
|
189
217
|
}
|
|
190
218
|
|
|
191
219
|
function findNodePath(node, targetId, trail = []) {
|
|
192
|
-
const next = [...trail, node]
|
|
193
|
-
if (node.id === targetId) return next
|
|
220
|
+
const next = [...trail, node]
|
|
221
|
+
if (node.id === targetId) return next
|
|
194
222
|
for (const child of node.children ?? []) {
|
|
195
|
-
const found = findNodePath(child, targetId, next)
|
|
196
|
-
if (found) return found
|
|
223
|
+
const found = findNodePath(child, targetId, next)
|
|
224
|
+
if (found) return found
|
|
197
225
|
}
|
|
198
|
-
return null
|
|
226
|
+
return null
|
|
199
227
|
}
|
|
200
228
|
|
|
201
229
|
function extractModes(fileResponse, nodeId, byKey) {
|
|
202
|
-
const pathToRoot = findNodePath(fileResponse.document, nodeId)
|
|
230
|
+
const pathToRoot = findNodePath(fileResponse.document, nodeId)
|
|
203
231
|
if (!pathToRoot) {
|
|
204
|
-
throw new Error(`Node "${nodeId}" not found in file.`)
|
|
232
|
+
throw new Error(`Node "${nodeId}" not found in file.`)
|
|
205
233
|
}
|
|
206
234
|
|
|
207
|
-
const root = pathToRoot[pathToRoot.length - 1]
|
|
208
|
-
const layers = []
|
|
209
|
-
const unresolved = []
|
|
235
|
+
const root = pathToRoot[pathToRoot.length - 1]
|
|
236
|
+
const layers = []
|
|
237
|
+
const unresolved = []
|
|
210
238
|
|
|
211
239
|
function resolveNodeModes(node) {
|
|
212
|
-
const modes = {}
|
|
240
|
+
const modes = {}
|
|
213
241
|
for (const [collectionId, modeId] of Object.entries(
|
|
214
242
|
node.explicitVariableModes ?? {},
|
|
215
243
|
)) {
|
|
216
|
-
const keyMatch = String(collectionId).match(COLLECTION_KEY_PATTERN)
|
|
244
|
+
const keyMatch = String(collectionId).match(COLLECTION_KEY_PATTERN)
|
|
217
245
|
if (!keyMatch) {
|
|
218
246
|
// Local (non-subscribed) collection in the consumer file: not part of
|
|
219
247
|
// the published library, nothing to resolve against.
|
|
@@ -222,66 +250,66 @@ function extractModes(fileResponse, nodeId, byKey) {
|
|
|
222
250
|
collectionId,
|
|
223
251
|
modeId,
|
|
224
252
|
reason: "local collection (no library key in id)",
|
|
225
|
-
})
|
|
226
|
-
continue
|
|
253
|
+
})
|
|
254
|
+
continue
|
|
227
255
|
}
|
|
228
|
-
const collection = byKey.get(keyMatch[1])
|
|
256
|
+
const collection = byKey.get(keyMatch[1])
|
|
229
257
|
if (!collection) {
|
|
230
258
|
unresolved.push({
|
|
231
259
|
layer: node.name,
|
|
232
260
|
collectionId,
|
|
233
261
|
modeId,
|
|
234
262
|
reason: "key not in variables JSON (library out of date?)",
|
|
235
|
-
})
|
|
236
|
-
continue
|
|
263
|
+
})
|
|
264
|
+
continue
|
|
237
265
|
}
|
|
238
|
-
const modeName = collection.modes.get(modeId)
|
|
266
|
+
const modeName = collection.modes.get(modeId)
|
|
239
267
|
if (!modeName) {
|
|
240
268
|
unresolved.push({
|
|
241
269
|
layer: node.name,
|
|
242
270
|
collectionId,
|
|
243
271
|
modeId,
|
|
244
272
|
reason: `mode not in collection "${collection.name}" (re-export variables JSON?)`,
|
|
245
|
-
})
|
|
246
|
-
continue
|
|
273
|
+
})
|
|
274
|
+
continue
|
|
247
275
|
}
|
|
248
|
-
modes[collection.name] = modeName
|
|
276
|
+
modes[collection.name] = modeName
|
|
249
277
|
}
|
|
250
|
-
return modes
|
|
278
|
+
return modes
|
|
251
279
|
}
|
|
252
280
|
|
|
253
281
|
// Modes set on ancestors above the requested node (inherited by everything below)
|
|
254
|
-
const inherited = {}
|
|
282
|
+
const inherited = {}
|
|
255
283
|
for (const ancestor of pathToRoot.slice(0, -1)) {
|
|
256
|
-
Object.assign(inherited, resolveNodeModes(ancestor))
|
|
284
|
+
Object.assign(inherited, resolveNodeModes(ancestor))
|
|
257
285
|
}
|
|
258
286
|
|
|
259
287
|
function walk(node, trail) {
|
|
260
|
-
const modes = resolveNodeModes(node)
|
|
288
|
+
const modes = resolveNodeModes(node)
|
|
261
289
|
if (Object.keys(modes).length > 0) {
|
|
262
290
|
layers.push({
|
|
263
291
|
layer: node.name,
|
|
264
292
|
nodeId: node.id,
|
|
265
293
|
path: trail.join(" > "),
|
|
266
294
|
modes,
|
|
267
|
-
})
|
|
295
|
+
})
|
|
268
296
|
}
|
|
269
297
|
for (const child of node.children ?? []) {
|
|
270
|
-
walk(child, [...trail, child.name])
|
|
298
|
+
walk(child, [...trail, child.name])
|
|
271
299
|
}
|
|
272
300
|
}
|
|
273
301
|
|
|
274
|
-
walk(root, [root.name])
|
|
302
|
+
walk(root, [root.name])
|
|
275
303
|
|
|
276
304
|
const result = {
|
|
277
305
|
file: fileResponse.name ?? null,
|
|
278
306
|
root: root.name,
|
|
279
307
|
note: "explicit variable modes per layer; children inherit ancestor modes unless overridden",
|
|
280
308
|
modes: layers,
|
|
281
|
-
}
|
|
282
|
-
if (Object.keys(inherited).length > 0) result.inheritedFromAncestors = inherited
|
|
283
|
-
if (unresolved.length > 0) result.unresolved = unresolved
|
|
284
|
-
return result
|
|
309
|
+
}
|
|
310
|
+
if (Object.keys(inherited).length > 0) result.inheritedFromAncestors = inherited
|
|
311
|
+
if (unresolved.length > 0) result.unresolved = unresolved
|
|
312
|
+
return result
|
|
285
313
|
}
|
|
286
314
|
|
|
287
315
|
/**
|
|
@@ -293,67 +321,68 @@ async function addKeys(masterFileKey, token) {
|
|
|
293
321
|
const response = await figmaGet(
|
|
294
322
|
`/files/${masterFileKey}/variables/local`,
|
|
295
323
|
token,
|
|
296
|
-
)
|
|
297
|
-
const remote = Object.values(response.meta?.variableCollections ?? {})
|
|
324
|
+
)
|
|
325
|
+
const remote = Object.values(response.meta?.variableCollections ?? {})
|
|
298
326
|
const keyById = new Map(
|
|
299
327
|
remote
|
|
300
328
|
.filter((collection) => collection.key)
|
|
301
329
|
.map((collection) => [collection.id, collection.key]),
|
|
302
|
-
)
|
|
330
|
+
)
|
|
303
331
|
console.error(
|
|
304
332
|
`Fetched ${keyById.size} collection key(s) from ${masterFileKey}.`,
|
|
305
|
-
)
|
|
333
|
+
)
|
|
306
334
|
|
|
307
335
|
for (const file of listVariablesJsonFiles()) {
|
|
308
|
-
const data = JSON.parse(fs.readFileSync(file, "utf8"))
|
|
309
|
-
let patched = 0
|
|
310
|
-
let missing = 0
|
|
336
|
+
const data = JSON.parse(fs.readFileSync(file, "utf8"))
|
|
337
|
+
let patched = 0
|
|
338
|
+
let missing = 0
|
|
311
339
|
for (const collection of data.collections ?? []) {
|
|
312
|
-
const key = keyById.get(collection.id)
|
|
340
|
+
const key = keyById.get(collection.id)
|
|
313
341
|
if (key) {
|
|
314
|
-
collection.key = key
|
|
315
|
-
patched += 1
|
|
342
|
+
collection.key = key
|
|
343
|
+
patched += 1
|
|
316
344
|
} else if (!collection.key) {
|
|
317
|
-
missing += 1
|
|
345
|
+
missing += 1
|
|
318
346
|
}
|
|
319
347
|
}
|
|
320
|
-
fs.writeFileSync(file, `${JSON.stringify(data, null, 2)}\n`)
|
|
348
|
+
fs.writeFileSync(file, `${JSON.stringify(data, null, 2)}\n`)
|
|
321
349
|
console.error(
|
|
322
350
|
`${path.basename(file)}: patched ${patched} key(s)` +
|
|
323
351
|
(missing ? `, ${missing} collection(s) still without key` : ""),
|
|
324
|
-
)
|
|
352
|
+
)
|
|
325
353
|
}
|
|
326
354
|
}
|
|
327
355
|
|
|
328
356
|
async function main() {
|
|
329
|
-
const args = parseArgs(process.argv)
|
|
330
|
-
|
|
357
|
+
const args = parseArgs(process.argv)
|
|
358
|
+
loadProjectEnv()
|
|
359
|
+
const token = getToken()
|
|
331
360
|
|
|
332
361
|
if (args.addKeysFile) {
|
|
333
|
-
await addKeys(args.addKeysFile, token)
|
|
334
|
-
return
|
|
362
|
+
await addKeys(args.addKeysFile, token)
|
|
363
|
+
return
|
|
335
364
|
}
|
|
336
365
|
|
|
337
|
-
const byKey = buildKeyIndex()
|
|
366
|
+
const byKey = buildKeyIndex()
|
|
338
367
|
const fileResponse = await figmaGet(
|
|
339
368
|
`/files/${args.fileKey}?ids=${encodeURIComponent(args.nodeId)}`,
|
|
340
369
|
token,
|
|
341
|
-
)
|
|
342
|
-
const result = extractModes(fileResponse, args.nodeId, byKey)
|
|
370
|
+
)
|
|
371
|
+
const result = extractModes(fileResponse, args.nodeId, byKey)
|
|
343
372
|
|
|
344
|
-
const json = JSON.stringify(result, null, 2)
|
|
373
|
+
const json = JSON.stringify(result, null, 2)
|
|
345
374
|
if (args.outPath) {
|
|
346
|
-
fs.writeFileSync(path.resolve(args.outPath), `${json}\n`)
|
|
347
|
-
console.error(`Wrote ${args.outPath}`)
|
|
375
|
+
fs.writeFileSync(path.resolve(args.outPath), `${json}\n`)
|
|
376
|
+
console.error(`Wrote ${args.outPath}`)
|
|
348
377
|
}
|
|
349
|
-
process.stdout.write(`${json}\n`)
|
|
378
|
+
process.stdout.write(`${json}\n`)
|
|
350
379
|
}
|
|
351
380
|
|
|
352
381
|
if (require.main === module) {
|
|
353
382
|
main().catch((error) => {
|
|
354
|
-
console.error(error.message || error)
|
|
355
|
-
process.exit(1)
|
|
356
|
-
})
|
|
383
|
+
console.error(error.message || error)
|
|
384
|
+
process.exit(1)
|
|
385
|
+
})
|
|
357
386
|
}
|
|
358
387
|
|
|
359
|
-
module.exports = { buildKeyIndex, extractModes }
|
|
388
|
+
module.exports = { buildKeyIndex, extractModes, loadProjectEnv }
|
|
@@ -29,10 +29,10 @@ export type LottiePlayerProps = {
|
|
|
29
29
|
* Override the rendered size. Pass a number for a square box, or
|
|
30
30
|
* `{ width, height }` for non-square.
|
|
31
31
|
*
|
|
32
|
-
* When omitted, size is resolved from the `
|
|
33
|
-
* design tokens (default `117 × 117`). The `
|
|
32
|
+
* When omitted, size is resolved from the `video/width` and `video/height`
|
|
33
|
+
* design tokens (default `117 × 117`). The `Video / Output` collection
|
|
34
34
|
* exposes `L | M | S` modes (117 / 70 / 20) — pass
|
|
35
|
-
* `modes={{ '
|
|
35
|
+
* `modes={{ 'Video / Output': 'M' }}` to render at 70×70, etc.
|
|
36
36
|
*/
|
|
37
37
|
size?: number | { width: number; height: number }
|
|
38
38
|
/** Play the animation on mount. Defaults to `true`. */
|
|
@@ -57,9 +57,9 @@ function resolveSize(
|
|
|
57
57
|
if (typeof size === 'number') return { width: size, height: size }
|
|
58
58
|
if (size && typeof size === 'object') return size
|
|
59
59
|
const width =
|
|
60
|
-
Number(getVariableByName('
|
|
60
|
+
Number(getVariableByName('video/width', modes)) || DEFAULT_SIZE
|
|
61
61
|
const height =
|
|
62
|
-
Number(getVariableByName('
|
|
62
|
+
Number(getVariableByName('video/height', modes)) || DEFAULT_SIZE
|
|
63
63
|
return { width, height }
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -83,8 +83,8 @@ function resolveSize(
|
|
|
83
83
|
* webpack via platform extensions — same pattern as `MediaCard/GlassFill`.
|
|
84
84
|
*
|
|
85
85
|
* Token-driven sizing: when `size` is omitted, `LottiePlayer` reads
|
|
86
|
-
* `
|
|
87
|
-
* animation matches the surrounding component's `
|
|
86
|
+
* `video/width` and `video/height` from the Figma variables resolver, so the
|
|
87
|
+
* animation matches the surrounding component's `Video / Output` mode
|
|
88
88
|
* automatically. This is the same sizing contract `PageHero` and
|
|
89
89
|
* `LottieIntroBlock` use for their `media` slots.
|
|
90
90
|
*
|
|
@@ -95,7 +95,7 @@ function resolveSize(
|
|
|
95
95
|
*
|
|
96
96
|
* <LottiePlayer source={animation} /> // 117 × 117 (default)
|
|
97
97
|
* <LottiePlayer source={animation} size={70} /> // 70 × 70
|
|
98
|
-
* <LottiePlayer source={animation} modes={{ '
|
|
98
|
+
* <LottiePlayer source={animation} modes={{ 'Video / Output': 'S' }} /> // 20 × 20
|
|
99
99
|
* <PageHero media={<LottiePlayer source={animation} />} />
|
|
100
100
|
* ```
|
|
101
101
|
*/
|
|
@@ -28,9 +28,9 @@ function resolveSize(
|
|
|
28
28
|
if (typeof size === 'number') return { width: size, height: size }
|
|
29
29
|
if (size && typeof size === 'object') return size
|
|
30
30
|
const width =
|
|
31
|
-
Number(getVariableByName('
|
|
31
|
+
Number(getVariableByName('video/width', modes)) || DEFAULT_SIZE
|
|
32
32
|
const height =
|
|
33
|
-
Number(getVariableByName('
|
|
33
|
+
Number(getVariableByName('video/height', modes)) || DEFAULT_SIZE
|
|
34
34
|
return { width, height }
|
|
35
35
|
}
|
|
36
36
|
|