@nibin-org/tokens 1.0.0

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/dist/index.cjs ADDED
@@ -0,0 +1,700 @@
1
+ "use client";
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ ColorGrid: () => ColorGrid,
25
+ RadiusShowcase: () => RadiusShowcase,
26
+ SizeScale: () => SizeScale,
27
+ SpacingScale: () => SpacingScale,
28
+ TokenDocumentation: () => TokenDocumentation,
29
+ TokenDocumentationDefault: () => TokenDocumentation_default,
30
+ copyToClipboard: () => copyToClipboard,
31
+ getContrastColor: () => getContrastColor,
32
+ parseBaseColors: () => parseBaseColors,
33
+ parseRadiusTokens: () => parseRadiusTokens,
34
+ parseSemanticColors: () => parseSemanticColors,
35
+ parseSizeTokens: () => parseSizeTokens,
36
+ parseSpacingTokens: () => parseSpacingTokens
37
+ });
38
+ module.exports = __toCommonJS(index_exports);
39
+
40
+ // src/components/TokenDocumentation.tsx
41
+ var import_react5 = require("react");
42
+
43
+ // src/components/ColorGrid.tsx
44
+ var import_react = require("react");
45
+
46
+ // src/utils.ts
47
+ function isTokenValue(obj) {
48
+ return typeof obj === "object" && obj !== null && "value" in obj && "type" in obj;
49
+ }
50
+ function parseNumericValue(value) {
51
+ const match = value.match(/^([\d.]+)/);
52
+ return match ? parseFloat(match[1]) : 0;
53
+ }
54
+ function getContrastColor(hexColor) {
55
+ const hex = hexColor.replace("#", "");
56
+ const r = parseInt(hex.substring(0, 2), 16);
57
+ const g = parseInt(hex.substring(2, 4), 16);
58
+ const b = parseInt(hex.substring(4, 6), 16);
59
+ const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
60
+ return luminance > 0.5 ? "black" : "white";
61
+ }
62
+ function parseBaseColors(tokens) {
63
+ const families = [];
64
+ for (const [familyName, shades] of Object.entries(tokens)) {
65
+ if (typeof shades !== "object" || shades === null) continue;
66
+ const family = {
67
+ name: familyName,
68
+ primaryColor: "",
69
+ shades: []
70
+ };
71
+ for (const [shadeName, token] of Object.entries(shades)) {
72
+ if (isTokenValue(token)) {
73
+ const colorToken = {
74
+ name: `${familyName}-${shadeName}`,
75
+ value: token.value,
76
+ cssVariable: `--base-${familyName}-${shadeName}`,
77
+ shade: shadeName,
78
+ family: familyName
79
+ };
80
+ family.shades.push(colorToken);
81
+ if (shadeName === "50") {
82
+ family.primaryColor = token.value;
83
+ }
84
+ }
85
+ }
86
+ family.shades.sort((a, b) => {
87
+ const aNum = parseInt(a.shade || "0");
88
+ const bNum = parseInt(b.shade || "0");
89
+ return aNum - bNum;
90
+ });
91
+ if (!family.primaryColor && family.shades.length > 0) {
92
+ family.primaryColor = family.shades[Math.floor(family.shades.length / 2)]?.value || "";
93
+ }
94
+ if (family.shades.length > 0) {
95
+ families.push(family);
96
+ }
97
+ }
98
+ return families;
99
+ }
100
+ function parseSemanticColors(tokens, prefix) {
101
+ const colors = [];
102
+ for (const [name, token] of Object.entries(tokens)) {
103
+ if (isTokenValue(token)) {
104
+ let value = token.value;
105
+ if (value.startsWith("{") && value.endsWith("}")) {
106
+ value = token.value;
107
+ }
108
+ colors.push({
109
+ name,
110
+ value,
111
+ cssVariable: `--${prefix}-${name}`
112
+ });
113
+ }
114
+ }
115
+ return colors;
116
+ }
117
+ function parseSpacingTokens(tokens) {
118
+ const spacings = [];
119
+ for (const [name, token] of Object.entries(tokens)) {
120
+ if (isTokenValue(token)) {
121
+ const cleanName = name.replace(/^space-/, "");
122
+ spacings.push({
123
+ name: cleanName,
124
+ value: token.value,
125
+ cssVariable: `--space-${cleanName}`,
126
+ numericValue: parseNumericValue(token.value)
127
+ });
128
+ }
129
+ }
130
+ return spacings.sort((a, b) => a.numericValue - b.numericValue);
131
+ }
132
+ function parseRadiusTokens(tokens) {
133
+ const radiuses = [];
134
+ for (const [name, token] of Object.entries(tokens)) {
135
+ if (isTokenValue(token)) {
136
+ const cleanName = name.replace(/^radius-/, "");
137
+ radiuses.push({
138
+ name: cleanName,
139
+ value: token.value,
140
+ cssVariable: `--radius-${cleanName}`,
141
+ numericValue: parseNumericValue(token.value)
142
+ });
143
+ }
144
+ }
145
+ return radiuses.sort((a, b) => a.numericValue - b.numericValue);
146
+ }
147
+ function parseSizeTokens(tokens) {
148
+ const sizes = [];
149
+ for (const [name, token] of Object.entries(tokens)) {
150
+ if (isTokenValue(token)) {
151
+ const cleanName = name.replace(/^size-/, "");
152
+ sizes.push({
153
+ name: cleanName,
154
+ value: token.value,
155
+ cssVariable: `--size-${cleanName}`,
156
+ numericValue: parseNumericValue(token.value)
157
+ });
158
+ }
159
+ }
160
+ return sizes.sort((a, b) => a.numericValue - b.numericValue);
161
+ }
162
+ async function copyToClipboard(text) {
163
+ try {
164
+ await navigator.clipboard.writeText(text);
165
+ return true;
166
+ } catch {
167
+ const textarea = document.createElement("textarea");
168
+ textarea.value = text;
169
+ document.body.appendChild(textarea);
170
+ textarea.select();
171
+ const success = document.execCommand("copy");
172
+ document.body.removeChild(textarea);
173
+ return success;
174
+ }
175
+ }
176
+
177
+ // src/components/ColorGrid.tsx
178
+ var import_jsx_runtime = require("react/jsx-runtime");
179
+ function ColorGrid({
180
+ baseColors,
181
+ fillColors,
182
+ strokeColors,
183
+ textColors,
184
+ onColorClick
185
+ }) {
186
+ const [copiedValue, setCopiedValue] = (0, import_react.useState)(null);
187
+ const handleCopy = (0, import_react.useCallback)(async (color) => {
188
+ const success = await copyToClipboard(color.value);
189
+ if (success) {
190
+ setCopiedValue(color.value);
191
+ setTimeout(() => setCopiedValue(null), 2e3);
192
+ }
193
+ onColorClick?.(color);
194
+ }, [onColorClick]);
195
+ const colorFamilies = baseColors ? parseBaseColors(baseColors) : [];
196
+ const semanticFill = fillColors ? parseSemanticColors(fillColors, "fill") : [];
197
+ const semanticStroke = strokeColors ? parseSemanticColors(strokeColors, "stroke") : [];
198
+ const semanticText = textColors ? parseSemanticColors(textColors, "text") : [];
199
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ftd-color-container", children: [
200
+ colorFamilies.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ftd-section", children: [
201
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ftd-section-header", children: [
202
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ftd-section-icon", children: "\u{1F3A8}" }),
203
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "ftd-section-title", children: "Base Colors" }),
204
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "ftd-section-count", children: [
205
+ colorFamilies.length,
206
+ " families"
207
+ ] })
208
+ ] }),
209
+ colorFamilies.map((family) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ftd-color-family", children: [
210
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ftd-color-family-header", children: [
211
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
212
+ "div",
213
+ {
214
+ className: "ftd-color-family-swatch",
215
+ style: { backgroundColor: family.primaryColor }
216
+ }
217
+ ),
218
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h4", { className: "ftd-color-family-name", children: family.name })
219
+ ] }),
220
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ftd-color-scale", children: family.shades.map((shade) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
221
+ "div",
222
+ {
223
+ className: "ftd-color-shade",
224
+ style: {
225
+ backgroundColor: shade.value,
226
+ color: getContrastColor(shade.value)
227
+ },
228
+ onClick: () => handleCopy(shade),
229
+ title: `Click to copy: ${shade.value}`,
230
+ children: [
231
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ftd-color-shade-label", children: shade.shade }),
232
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ftd-color-shade-value", children: shade.value.substring(0, 7) })
233
+ ]
234
+ },
235
+ shade.name
236
+ )) })
237
+ ] }, family.name))
238
+ ] }),
239
+ semanticFill.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ftd-section", children: [
240
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ftd-section-header", children: [
241
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ftd-section-icon", children: "\u{1F5BC}\uFE0F" }),
242
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "ftd-section-title", children: "Fill Colors" }),
243
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "ftd-section-count", children: [
244
+ semanticFill.length,
245
+ " tokens"
246
+ ] })
247
+ ] }),
248
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ftd-token-grid", children: semanticFill.map((color) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
249
+ ColorCard,
250
+ {
251
+ color,
252
+ onClick: () => handleCopy(color)
253
+ },
254
+ color.name
255
+ )) })
256
+ ] }),
257
+ semanticStroke.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ftd-section", children: [
258
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ftd-section-header", children: [
259
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ftd-section-icon", children: "\u270F\uFE0F" }),
260
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "ftd-section-title", children: "Stroke Colors" }),
261
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "ftd-section-count", children: [
262
+ semanticStroke.length,
263
+ " tokens"
264
+ ] })
265
+ ] }),
266
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ftd-token-grid", children: semanticStroke.map((color) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
267
+ ColorCard,
268
+ {
269
+ color,
270
+ onClick: () => handleCopy(color)
271
+ },
272
+ color.name
273
+ )) })
274
+ ] }),
275
+ semanticText.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ftd-section", children: [
276
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ftd-section-header", children: [
277
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ftd-section-icon", children: "\u{1F4DD}" }),
278
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "ftd-section-title", children: "Text Colors" }),
279
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "ftd-section-count", children: [
280
+ semanticText.length,
281
+ " tokens"
282
+ ] })
283
+ ] }),
284
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ftd-token-grid", children: semanticText.map((color) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
285
+ ColorCard,
286
+ {
287
+ color,
288
+ onClick: () => handleCopy(color)
289
+ },
290
+ color.name
291
+ )) })
292
+ ] }),
293
+ copiedValue && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ftd-copied-toast", children: [
294
+ "\u2713 Copied: ",
295
+ copiedValue
296
+ ] })
297
+ ] });
298
+ }
299
+ function ColorCard({ color, onClick }) {
300
+ const isAlias = color.value.startsWith("{");
301
+ const displayValue = isAlias ? color.value : color.value.substring(0, 9);
302
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ftd-token-card", onClick, children: [
303
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
304
+ "div",
305
+ {
306
+ className: "ftd-token-swatch",
307
+ style: {
308
+ backgroundColor: isAlias ? "var(--ftd-bg-tertiary)" : color.value
309
+ },
310
+ children: isAlias && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: {
311
+ fontSize: "0.625rem",
312
+ color: "var(--ftd-text-secondary)",
313
+ padding: "4px"
314
+ }, children: "Alias \u2192" })
315
+ }
316
+ ),
317
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ftd-token-info", children: [
318
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "ftd-token-name", children: color.name }),
319
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "ftd-token-value", children: [
320
+ displayValue,
321
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
322
+ "svg",
323
+ {
324
+ className: "ftd-copy-icon",
325
+ width: "12",
326
+ height: "12",
327
+ viewBox: "0 0 24 24",
328
+ fill: "none",
329
+ stroke: "currentColor",
330
+ strokeWidth: "2",
331
+ children: [
332
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
333
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
334
+ ]
335
+ }
336
+ )
337
+ ] })
338
+ ] })
339
+ ] });
340
+ }
341
+
342
+ // src/components/SpacingScale.tsx
343
+ var import_react2 = require("react");
344
+ var import_jsx_runtime2 = require("react/jsx-runtime");
345
+ function SpacingScale({ tokens, onTokenClick }) {
346
+ const [copiedValue, setCopiedValue] = (0, import_react2.useState)(null);
347
+ const spacingTokens = parseSpacingTokens(tokens);
348
+ const maxValue = Math.max(...spacingTokens.map((t) => t.numericValue), 1);
349
+ const handleCopy = (0, import_react2.useCallback)(async (token) => {
350
+ const success = await copyToClipboard(token.value);
351
+ if (success) {
352
+ setCopiedValue(token.value);
353
+ setTimeout(() => setCopiedValue(null), 2e3);
354
+ }
355
+ onTokenClick?.(token);
356
+ }, [onTokenClick]);
357
+ if (spacingTokens.length === 0) {
358
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ftd-empty", children: [
359
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ftd-empty-icon", children: "\u{1F4CF}" }),
360
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h4", { className: "ftd-empty-title", children: "No spacing tokens found" }),
361
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "ftd-empty-text", children: "Add spacing tokens to your tokens.json file" })
362
+ ] });
363
+ }
364
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ftd-section", children: [
365
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ftd-section-header", children: [
366
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ftd-section-icon", children: "\u{1F4CF}" }),
367
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "ftd-section-title", children: "Spacing Scale" }),
368
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "ftd-section-count", children: [
369
+ spacingTokens.length,
370
+ " tokens"
371
+ ] })
372
+ ] }),
373
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ftd-spacing-list", children: spacingTokens.map((token) => {
374
+ const widthPercent = token.numericValue / maxValue * 100;
375
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
376
+ "div",
377
+ {
378
+ className: "ftd-spacing-item",
379
+ onClick: () => handleCopy(token),
380
+ style: { cursor: "pointer" },
381
+ children: [
382
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "ftd-spacing-label", children: token.name }),
383
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ftd-spacing-bar-container", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
384
+ "div",
385
+ {
386
+ className: "ftd-spacing-bar",
387
+ style: { width: `${Math.max(widthPercent, 5)}%` }
388
+ }
389
+ ) }),
390
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "ftd-spacing-value", children: token.value })
391
+ ]
392
+ },
393
+ token.name
394
+ );
395
+ }) }),
396
+ copiedValue && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ftd-copied-toast", children: [
397
+ "\u2713 Copied: ",
398
+ copiedValue
399
+ ] })
400
+ ] });
401
+ }
402
+
403
+ // src/components/RadiusShowcase.tsx
404
+ var import_react3 = require("react");
405
+ var import_jsx_runtime3 = require("react/jsx-runtime");
406
+ function RadiusShowcase({ tokens, onTokenClick }) {
407
+ const [copiedValue, setCopiedValue] = (0, import_react3.useState)(null);
408
+ const radiusTokens = parseRadiusTokens(tokens);
409
+ const handleCopy = (0, import_react3.useCallback)(async (token) => {
410
+ const success = await copyToClipboard(token.value);
411
+ if (success) {
412
+ setCopiedValue(token.value);
413
+ setTimeout(() => setCopiedValue(null), 2e3);
414
+ }
415
+ onTokenClick?.(token);
416
+ }, [onTokenClick]);
417
+ if (radiusTokens.length === 0) {
418
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ftd-empty", children: [
419
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "ftd-empty-icon", children: "\u2B1C" }),
420
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h4", { className: "ftd-empty-title", children: "No radius tokens found" }),
421
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "ftd-empty-text", children: "Add radius tokens to your tokens.json file" })
422
+ ] });
423
+ }
424
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ftd-section", children: [
425
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ftd-section-header", children: [
426
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "ftd-section-icon", children: "\u2B1C" }),
427
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { className: "ftd-section-title", children: "Border Radius" }),
428
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "ftd-section-count", children: [
429
+ radiusTokens.length,
430
+ " tokens"
431
+ ] })
432
+ ] }),
433
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "ftd-radius-grid", children: radiusTokens.map((token) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
434
+ "div",
435
+ {
436
+ className: "ftd-radius-item",
437
+ onClick: () => handleCopy(token),
438
+ style: { cursor: "pointer" },
439
+ children: [
440
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
441
+ "div",
442
+ {
443
+ className: "ftd-radius-preview",
444
+ style: { borderRadius: token.value }
445
+ }
446
+ ),
447
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "ftd-radius-label", children: token.name }),
448
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "ftd-radius-value", children: token.value })
449
+ ]
450
+ },
451
+ token.name
452
+ )) }),
453
+ copiedValue && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ftd-copied-toast", children: [
454
+ "\u2713 Copied: ",
455
+ copiedValue
456
+ ] })
457
+ ] });
458
+ }
459
+
460
+ // src/components/SizeScale.tsx
461
+ var import_react4 = require("react");
462
+ var import_jsx_runtime4 = require("react/jsx-runtime");
463
+ function SizeScale({ tokens, onTokenClick }) {
464
+ const [copiedValue, setCopiedValue] = (0, import_react4.useState)(null);
465
+ const sizeTokens = parseSizeTokens(tokens);
466
+ const maxValue = Math.max(...sizeTokens.map((t) => t.numericValue), 1);
467
+ const handleCopy = (0, import_react4.useCallback)(async (token) => {
468
+ const success = await copyToClipboard(token.value);
469
+ if (success) {
470
+ setCopiedValue(token.value);
471
+ setTimeout(() => setCopiedValue(null), 2e3);
472
+ }
473
+ onTokenClick?.(token);
474
+ }, [onTokenClick]);
475
+ if (sizeTokens.length === 0) {
476
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "ftd-empty", children: [
477
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "ftd-empty-icon", children: "\u{1F4D0}" }),
478
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h4", { className: "ftd-empty-title", children: "No size tokens found" }),
479
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "ftd-empty-text", children: "Add size tokens to your tokens.json file" })
480
+ ] });
481
+ }
482
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "ftd-section", children: [
483
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "ftd-section-header", children: [
484
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "ftd-section-icon", children: "\u{1F4D0}" }),
485
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { className: "ftd-section-title", children: "Size Scale" }),
486
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "ftd-section-count", children: [
487
+ sizeTokens.length,
488
+ " tokens"
489
+ ] })
490
+ ] }),
491
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "ftd-size-grid", children: sizeTokens.map((token) => {
492
+ const heightPercent = token.numericValue / maxValue * 200;
493
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
494
+ "div",
495
+ {
496
+ className: "ftd-size-item",
497
+ onClick: () => handleCopy(token),
498
+ style: { cursor: "pointer" },
499
+ title: `${token.name}: ${token.value}`,
500
+ children: [
501
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
502
+ "div",
503
+ {
504
+ className: "ftd-size-bar",
505
+ style: {
506
+ height: `${Math.max(heightPercent, 8)}px`,
507
+ width: "32px"
508
+ }
509
+ }
510
+ ),
511
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "ftd-size-label", children: token.name })
512
+ ]
513
+ },
514
+ token.name
515
+ );
516
+ }) }),
517
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { marginTop: "24px" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "ftd-spacing-list", children: sizeTokens.map((token) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
518
+ "div",
519
+ {
520
+ className: "ftd-spacing-item",
521
+ onClick: () => handleCopy(token),
522
+ style: { cursor: "pointer" },
523
+ children: [
524
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "ftd-spacing-label", children: token.name }),
525
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "ftd-spacing-bar-container", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
526
+ "div",
527
+ {
528
+ className: "ftd-spacing-bar",
529
+ style: { width: `${token.numericValue / maxValue * 100}%` }
530
+ }
531
+ ) }),
532
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "ftd-spacing-value", children: token.value })
533
+ ]
534
+ },
535
+ token.name
536
+ )) }) }),
537
+ copiedValue && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "ftd-copied-toast", children: [
538
+ "\u2713 Copied: ",
539
+ copiedValue
540
+ ] })
541
+ ] });
542
+ }
543
+
544
+ // src/components/TokenDocumentation.tsx
545
+ var import_jsx_runtime5 = require("react/jsx-runtime");
546
+ function TokenDocumentation({
547
+ tokens,
548
+ title = "Design Tokens",
549
+ subtitle = "Interactive documentation for your design system",
550
+ defaultTab = "colors",
551
+ showSearch = true,
552
+ darkMode: initialDarkMode = false,
553
+ onTokenClick
554
+ }) {
555
+ const [activeTab, setActiveTab] = (0, import_react5.useState)(defaultTab);
556
+ const [searchQuery, setSearchQuery] = (0, import_react5.useState)("");
557
+ const [isDarkMode, setIsDarkMode] = (0, import_react5.useState)(initialDarkMode);
558
+ const colorsValue = tokens["Colors/Value"];
559
+ const spacingTokens = tokens["Spacing/Mode 1"] || {};
560
+ const sizeTokens = tokens["Size/Mode 1"] || {};
561
+ const radiusTokens = tokens["Radius/Mode 1"] || {};
562
+ const availableTabs = (0, import_react5.useMemo)(() => {
563
+ const tabs = [];
564
+ if (colorsValue) {
565
+ tabs.push({ id: "colors", label: "Colors", icon: "\u{1F3A8}" });
566
+ }
567
+ if (Object.keys(spacingTokens).length > 0) {
568
+ tabs.push({ id: "spacing", label: "Spacing", icon: "\u{1F4CF}" });
569
+ }
570
+ if (Object.keys(sizeTokens).length > 0) {
571
+ tabs.push({ id: "sizes", label: "Sizes", icon: "\u{1F4D0}" });
572
+ }
573
+ if (Object.keys(radiusTokens).length > 0) {
574
+ tabs.push({ id: "radius", label: "Radius", icon: "\u2B1C" });
575
+ }
576
+ return tabs;
577
+ }, [colorsValue, spacingTokens, sizeTokens, radiusTokens]);
578
+ const validActiveTab = availableTabs.some((t) => t.id === activeTab) ? activeTab : availableTabs[0]?.id || "colors";
579
+ const toggleDarkMode = () => {
580
+ setIsDarkMode(!isDarkMode);
581
+ };
582
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ftd-container", "data-theme": isDarkMode ? "dark" : "light", children: [
583
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("header", { className: "ftd-header", children: [
584
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
585
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h1", { className: "ftd-title", children: title }),
586
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "ftd-subtitle", children: subtitle })
587
+ ] }),
588
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
589
+ "button",
590
+ {
591
+ className: "ftd-theme-toggle",
592
+ onClick: toggleDarkMode,
593
+ "aria-label": isDarkMode ? "Switch to light mode" : "Switch to dark mode",
594
+ children: [
595
+ isDarkMode ? "\u2600\uFE0F" : "\u{1F319}",
596
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: isDarkMode ? "Light" : "Dark" })
597
+ ]
598
+ }
599
+ )
600
+ ] }),
601
+ showSearch && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ftd-search-container", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ftd-search-wrapper", children: [
602
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
603
+ "svg",
604
+ {
605
+ className: "ftd-search-icon",
606
+ width: "16",
607
+ height: "16",
608
+ viewBox: "0 0 24 24",
609
+ fill: "none",
610
+ stroke: "currentColor",
611
+ strokeWidth: "2",
612
+ children: [
613
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: "11", cy: "11", r: "8" }),
614
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M21 21l-4.35-4.35" })
615
+ ]
616
+ }
617
+ ),
618
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
619
+ "input",
620
+ {
621
+ type: "text",
622
+ className: "ftd-search-input",
623
+ placeholder: "Search tokens...",
624
+ value: searchQuery,
625
+ onChange: (e) => setSearchQuery(e.target.value)
626
+ }
627
+ )
628
+ ] }) }),
629
+ availableTabs.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("nav", { className: "ftd-tabs", role: "tablist", children: availableTabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
630
+ "button",
631
+ {
632
+ className: `ftd-tab ${validActiveTab === tab.id ? "active" : ""}`,
633
+ onClick: () => setActiveTab(tab.id),
634
+ role: "tab",
635
+ "aria-selected": validActiveTab === tab.id,
636
+ children: [
637
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { marginRight: "8px" }, children: tab.icon }),
638
+ tab.label
639
+ ]
640
+ },
641
+ tab.id
642
+ )) }),
643
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { role: "tabpanel", children: [
644
+ validActiveTab === "colors" && colorsValue && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
645
+ ColorGrid,
646
+ {
647
+ baseColors: colorsValue.base,
648
+ fillColors: colorsValue.fill,
649
+ strokeColors: colorsValue.stroke,
650
+ textColors: colorsValue.text,
651
+ onColorClick: onTokenClick
652
+ }
653
+ ),
654
+ validActiveTab === "spacing" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
655
+ SpacingScale,
656
+ {
657
+ tokens: spacingTokens,
658
+ onTokenClick
659
+ }
660
+ ),
661
+ validActiveTab === "sizes" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
662
+ SizeScale,
663
+ {
664
+ tokens: sizeTokens,
665
+ onTokenClick
666
+ }
667
+ ),
668
+ validActiveTab === "radius" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
669
+ RadiusShowcase,
670
+ {
671
+ tokens: radiusTokens,
672
+ onTokenClick
673
+ }
674
+ )
675
+ ] }),
676
+ availableTabs.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ftd-empty", children: [
677
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ftd-empty-icon", children: "\u{1F4E6}" }),
678
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h4", { className: "ftd-empty-title", children: "No tokens found" }),
679
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "ftd-empty-text", children: "Pass a valid tokens.json file from Figma Token Studio" })
680
+ ] })
681
+ ] });
682
+ }
683
+ var TokenDocumentation_default = TokenDocumentation;
684
+ // Annotate the CommonJS export names for ESM import in node:
685
+ 0 && (module.exports = {
686
+ ColorGrid,
687
+ RadiusShowcase,
688
+ SizeScale,
689
+ SpacingScale,
690
+ TokenDocumentation,
691
+ TokenDocumentationDefault,
692
+ copyToClipboard,
693
+ getContrastColor,
694
+ parseBaseColors,
695
+ parseRadiusTokens,
696
+ parseSemanticColors,
697
+ parseSizeTokens,
698
+ parseSpacingTokens
699
+ });
700
+ //# sourceMappingURL=index.cjs.map