@vettvangur/design-system 2.0.46 → 2.0.49

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.
Files changed (2) hide show
  1. package/dist/index.js +260 -62
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2193,70 +2193,267 @@ async function generateFile(variables, config) {
2193
2193
  await fs$1.writeFile(outPath, css, 'utf8');
2194
2194
  }
2195
2195
 
2196
- /**
2197
- * design-system :: src/generators/tailwind/buttons.mjs.
2198
- *
2199
- * Design token tooling: pulls from Figma and generates platform outputs (Razor/Astro/Tailwind).
2200
- *
2201
- * These docs are generated from inline JSDoc in the repo and are intended for contributors.
2202
- *
2203
- * @generated
2204
- * @module design-system
2205
- */
2206
-
2207
- /**
2208
- * Normalize utility name.
2209
- *
2210
- * @param key - Value.
2211
- * @returns Result of the operation.
2212
- *
2213
- * @generated
2214
- * @example
2215
- * // design-system (src/tools/javascript/design-system/src/generators/tailwind/buttons.mjs)
2216
- * // import { normalizeUtilityName } from '@vettvangur/design-system'
2217
- *
2218
- * normalizeUtilityName(key)
2219
- */
2220
- function normalizeUtilityName(key) {
2221
- return String(key).trim().toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
2222
- }
2223
-
2224
- /**
2225
- * Render utility.
2226
- *
2227
- * @param name - Name or identifier.
2228
- * @returns String result.
2229
- *
2230
- * @generated
2231
- * @example
2232
- * // design-system (src/tools/javascript/design-system/src/generators/tailwind/buttons.mjs)
2233
- * // import { renderUtility } from '@vettvangur/design-system'
2234
- *
2235
- * renderUtility(name)
2236
- */
2196
+ const FIGMA_API_BASE_URL = 'https://api.figma.com/v1';
2197
+ const FIGMA_PAGE = 'Buttons';
2198
+ const assertNonEmptyString = (value, name) => {
2199
+ if (typeof value !== 'string' || value.trim().length === 0) {
2200
+ throw new TypeError(`${name} must be a non-empty string`);
2201
+ }
2202
+ };
2203
+ const walkFigmaNode = (node, visit) => {
2204
+ if (!node || typeof node !== 'object') {
2205
+ return;
2206
+ }
2207
+ visit(node);
2208
+ const children = node.children;
2209
+ if (!Array.isArray(children)) {
2210
+ return;
2211
+ }
2212
+ for (const child of children) {
2213
+ walkFigmaNode(child, visit);
2214
+ }
2215
+ };
2216
+ const stripButtonVariantPrefix = name => {
2217
+ if (typeof name !== 'string') {
2218
+ return null;
2219
+ }
2220
+ return name.replace(/^\s*button\s*variant\s*=\s*/i, '');
2221
+ };
2222
+ const findPageNodeId = (fileResponse, pageName) => {
2223
+ const pages = fileResponse?.document?.children;
2224
+ if (!Array.isArray(pages)) {
2225
+ return null;
2226
+ }
2227
+ const target = String(pageName).trim().toLowerCase();
2228
+ const canvases = pages.filter(child => child?.type === 'CANVAS' && typeof child?.name === 'string');
2229
+ const exact = canvases.find(child => child.name.trim().toLowerCase() === target);
2230
+ if (typeof exact?.id === 'string') {
2231
+ return exact.id;
2232
+ }
2233
+ const partial = canvases.find(child => child.name.trim().toLowerCase().includes(target));
2234
+ return typeof partial?.id === 'string' ? partial.id : null;
2235
+ };
2236
+ const isButtonComponentSet = node => {
2237
+ if (node?.type !== 'COMPONENT_SET') {
2238
+ return false;
2239
+ }
2240
+ const name = typeof node?.name === 'string' ? node.name : '';
2241
+ return /\bbutton\b/i.test(name);
2242
+ };
2243
+ const fetchButtonsPageNodes = async (fileKey, pageName = FIGMA_PAGE) => {
2244
+ assertNonEmptyString(fileKey, 'fileKey');
2245
+ assertNonEmptyString(pageName, 'pageName');
2246
+ const fileResponse = await jget(`${FIGMA_API_BASE_URL}/files/${encodeURIComponent(fileKey)}`);
2247
+ const pageNodeId = findPageNodeId(fileResponse, pageName);
2248
+ if (!pageNodeId) {
2249
+ const availablePages = Array.isArray(fileResponse?.document?.children) ? fileResponse.document.children.map(p => p?.name).filter(Boolean) : [];
2250
+ throw new Error(`Page not found: ${pageName}. Available pages: ${availablePages.join(', ')}`);
2251
+ }
2252
+ const nodesResponse = await jget(`${FIGMA_API_BASE_URL}/files/${encodeURIComponent(fileKey)}/nodes?ids=${encodeURIComponent(pageNodeId)}`);
2253
+ return {
2254
+ pageNodeId,
2255
+ nodesResponse
2256
+ };
2257
+ };
2258
+ const fetchFigmaLocalVariables = async fileKey => {
2259
+ assertNonEmptyString(fileKey, 'fileKey');
2260
+ return jget(`${FIGMA_API_BASE_URL}/files/${encodeURIComponent(fileKey)}/variables/local`);
2261
+ };
2262
+ const asArray = value => {
2263
+ if (!value) {
2264
+ return [];
2265
+ }
2266
+ if (Array.isArray(value)) {
2267
+ return value;
2268
+ }
2269
+ if (typeof value === 'object') {
2270
+ return Object.values(value);
2271
+ }
2272
+ return [];
2273
+ };
2274
+ const formatVariableValue = value => {
2275
+ if (value && typeof value === 'object' && typeof value.r === 'number' && typeof value.g === 'number' && typeof value.b === 'number') {
2276
+ return {
2277
+ kind: 'COLOR',
2278
+ hex: colorToHexRgba(value),
2279
+ rgba: rgba255(value),
2280
+ raw: value
2281
+ };
2282
+ }
2283
+ return {
2284
+ kind: 'RAW',
2285
+ raw: value
2286
+ };
2287
+ };
2288
+ const buildVariableResolver = variablesResponse => {
2289
+ const meta = variablesResponse?.meta ?? variablesResponse;
2290
+ const variablesRaw = meta?.variables ?? {};
2291
+ const collectionsRaw = meta?.variableCollections ?? {};
2292
+ const variablesById = new Map();
2293
+ for (const variable of asArray(variablesRaw)) {
2294
+ if (variable?.id) {
2295
+ variablesById.set(variable.id, variable);
2296
+ }
2297
+ }
2298
+ const collectionsById = new Map();
2299
+ for (const collection of asArray(collectionsRaw)) {
2300
+ if (collection?.id) {
2301
+ collectionsById.set(collection.id, collection);
2302
+ }
2303
+ }
2304
+ const modeNameById = new Map();
2305
+ for (const collection of collectionsById.values()) {
2306
+ for (const mode of asArray(collection?.modes)) {
2307
+ if (mode?.modeId && mode?.name) {
2308
+ modeNameById.set(mode.modeId, mode.name);
2309
+ }
2310
+ }
2311
+ }
2312
+ const resolve = variableId => {
2313
+ const variable = variablesById.get(variableId);
2314
+ if (!variable) {
2315
+ return null;
2316
+ }
2317
+ const collection = variable.variableCollectionId ? collectionsById.get(variable.variableCollectionId) : null;
2318
+ const valuesByMode = variable.valuesByMode && typeof variable.valuesByMode === 'object' ? variable.valuesByMode : {};
2319
+ return {
2320
+ id: variable.id,
2321
+ key: variable.key ?? null,
2322
+ name: variable.name ?? null,
2323
+ nameKebab: toKebab(variable.name),
2324
+ resolvedType: variable.resolvedType ?? null,
2325
+ collection: collection ? {
2326
+ id: collection.id,
2327
+ name: collection.name ?? null
2328
+ } : null,
2329
+ valuesByMode: Object.entries(valuesByMode).map(([modeId, value]) => ({
2330
+ modeId,
2331
+ modeName: modeNameById.get(modeId) ?? null,
2332
+ value: formatVariableValue(value)
2333
+ }))
2334
+ };
2335
+ };
2336
+ return {
2337
+ resolve
2338
+ };
2339
+ };
2340
+ const enrichVariableAliases = (value, resolveVariable) => {
2341
+ if (!value) {
2342
+ return value;
2343
+ }
2344
+ if (Array.isArray(value)) {
2345
+ return value.map(item => enrichVariableAliases(item, resolveVariable));
2346
+ }
2347
+ if (typeof value !== 'object') {
2348
+ return value;
2349
+ }
2350
+ if (value.type === 'VARIABLE_ALIAS' && typeof value.id === 'string') {
2351
+ return {
2352
+ ...value,
2353
+ variable: resolveVariable(value.id)
2354
+ };
2355
+ }
2356
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, enrichVariableAliases(child, resolveVariable)]));
2357
+ };
2358
+ const enrichBoundVariablesInNodeTree = (root, resolveVariable) => {
2359
+ walkFigmaNode(root, node => {
2360
+ if (node?.boundVariables && typeof node.boundVariables === 'object') {
2361
+ node.boundVariables = enrichVariableAliases(node.boundVariables, resolveVariable);
2362
+ }
2363
+ const fills = Array.isArray(node?.fills) ? node.fills : null;
2364
+ if (fills) {
2365
+ for (const paint of fills) {
2366
+ if (paint?.boundVariables && typeof paint.boundVariables === 'object') {
2367
+ paint.boundVariables = enrichVariableAliases(paint.boundVariables, resolveVariable);
2368
+ }
2369
+ }
2370
+ }
2371
+ });
2372
+ };
2373
+ const extractButtonGroupsByComponentSet = (nodesResponse, {
2374
+ pageNodeId
2375
+ }) => {
2376
+ const nodes = nodesResponse?.nodes;
2377
+ if (!nodes || typeof nodes !== 'object') {
2378
+ return [];
2379
+ }
2380
+ const pageDocument = nodes?.[pageNodeId]?.document;
2381
+ if (!pageDocument || typeof pageDocument !== 'object') {
2382
+ return [];
2383
+ }
2384
+ const groups = [];
2385
+ walkFigmaNode(pageDocument, node => {
2386
+ if (!isButtonComponentSet(node)) {
2387
+ return;
2388
+ }
2389
+ const variants = Array.isArray(node?.children) ? node.children : [];
2390
+ for (const variant of variants) {
2391
+ if (!variant || typeof variant !== 'object') {
2392
+ continue;
2393
+ }
2394
+ variant.nameKebab = toKebab(stripButtonVariantPrefix(variant?.name));
2395
+ }
2396
+ groups.push({
2397
+ componentSet: {
2398
+ id: typeof node?.id === 'string' ? node.id : null,
2399
+ name: typeof node?.name === 'string' ? node.name : null,
2400
+ nameKebab: toKebab(node?.name),
2401
+ variants
2402
+ }
2403
+ });
2404
+ });
2405
+ return groups;
2406
+ };
2237
2407
  function renderUtility(name) {
2238
- return `@utility ${name} {\n @apply button;\n\n &:hover {\n \n }\n}\n`;
2408
+ return `@utility ${name} {\n @apply button;\n\n &:not([disabled]):hover {\n \n }\n}\n`;
2239
2409
  }
2240
-
2241
- /**
2242
- * Generate buttons.
2243
- *
2244
- * @param components - Value.
2245
- * @param config - Configuration object.
2246
- * @returns Nothing.
2247
- *
2248
- * @example
2249
- * // design-system (src/tools/javascript/design-system/src/generators/tailwind/buttons.mjs)
2250
- * // import { generateButtons } from '@vettvangur/design-system'
2251
- *
2252
- * await generateButtons(components, {})
2253
- */
2254
- async function generateButtons$1(components, config) {
2255
- const list = Array.isArray(components) ? components : [];
2256
- const blocks = list.filter(x => x && x.kind === 'COMPONENT' && x.key).map(x => `button-${normalizeUtilityName(x.key)}`).map(renderUtility);
2410
+ async function generateButtons$1(_components, config) {
2411
+ message('generating buttons (from figma Buttons page)...');
2412
+ const fileKey = config?.figma?.fileKey;
2413
+ assertNonEmptyString(fileKey, 'config.figma.fileKey');
2414
+ const coreOutDir = path.join(config.paths.styles, 'core');
2415
+ await fs$1.mkdir(coreOutDir, {
2416
+ recursive: true
2417
+ });
2418
+ const {
2419
+ pageNodeId,
2420
+ nodesResponse
2421
+ } = await fetchButtonsPageNodes(fileKey, FIGMA_PAGE);
2422
+ const groups = extractButtonGroupsByComponentSet(nodesResponse, {
2423
+ pageNodeId
2424
+ });
2425
+ const variablesResponse = await fetchFigmaLocalVariables(fileKey);
2426
+ const {
2427
+ resolve
2428
+ } = buildVariableResolver(variablesResponse);
2429
+ for (const group of groups) {
2430
+ for (const variant of group.componentSet?.variants ?? []) {
2431
+ enrichBoundVariablesInNodeTree(variant, resolve);
2432
+ }
2433
+ }
2434
+ const snapshotPath = path.join(coreOutDir, 'button-variants.figma.json');
2435
+ await fs$1.writeFile(snapshotPath, `${JSON.stringify({
2436
+ fileKey,
2437
+ page: FIGMA_PAGE,
2438
+ pageNodeId,
2439
+ groups
2440
+ }, null, 2)}\n`, 'utf8');
2441
+ const utilityNames = [];
2442
+ for (const group of groups) {
2443
+ const setKey = group.componentSet?.nameKebab;
2444
+ for (const variant of group.componentSet?.variants ?? []) {
2445
+ const variantKey = variant?.nameKebab;
2446
+ if (!setKey || !variantKey) {
2447
+ continue;
2448
+ }
2449
+ utilityNames.push(`button-${setKey}-${variantKey}`);
2450
+ }
2451
+ }
2452
+ const blocks = Array.from(new Set(utilityNames)).sort().map(renderUtility);
2257
2453
  const css = `/* AUTO-GENERATED ONCE - Safe to edit */\n${blocks.join('\n')}`;
2258
- const outPath = path.join(config.paths.styles, 'core', 'button-variants.css');
2454
+ const outPath = path.join(coreOutDir, 'button-variants.css');
2259
2455
  await fs$1.writeFile(outPath, css, 'utf8');
2456
+ message(`finished generating button variants (count=${blocks.length})`);
2260
2457
  }
2261
2458
 
2262
2459
  /**
@@ -3604,11 +3801,12 @@ async function generateScreens(figmaVariables, config) {
3604
3801
  }
3605
3802
  }
3606
3803
  const fluidStyles = `/* AUTO-GENERATED - DO NOT EDIT BY HAND */
3804
+
3607
3805
  @utility fluid-html {
3608
3806
  /* unfortunatelly min-width values in media queries can't come from variables. */
3609
3807
 
3610
3808
  /* mobile */
3611
- font-size: calc((100 / {extrasUnitless['mobile-design-width']}) * 1vw);
3809
+ font-size: calc((100 / ${extrasUnitless['mobile-design-width']}) * 1vw);
3612
3810
 
3613
3811
  /* this needs to match --breakpoint-desktop-xs */
3614
3812
  @media (min-width: ${BASE_SCREENS['desktop-xs']}px) {
@@ -3618,7 +3816,7 @@ async function generateScreens(figmaVariables, config) {
3618
3816
  /* this needs to match --breakpoint ultrawide-design-width */
3619
3817
  @media (min-width: ${extrasUnitless['ultrawide-design-width']}px) {
3620
3818
  font-size: calc(
3621
- ((100 / ${extrasUnitless['desktop-design-width']}') * ${extrasUnitless['ultrawide-design-width']} / 16) * 0.01rem
3819
+ ((100 / ${extrasUnitless['desktop-design-width']}) * ${extrasUnitless['ultrawide-design-width']} / 16) * 0.01rem
3622
3820
  );
3623
3821
  }
3624
3822
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vettvangur/design-system",
3
- "version": "2.0.46",
3
+ "version": "2.0.49",
4
4
  "description": "",
5
5
  "access": "public",
6
6
  "type": "module",