@nurix/ui-component-library 1.1.3-stage.78 → 1.1.3-stage.80

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.js CHANGED
@@ -31,16 +31,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  Button: () => Button,
34
+ DEFAULT_THEME: () => DEFAULT_THEME,
34
35
  FileInput: () => FileInput,
35
36
  Input: () => Input,
36
- LEGO_TOKENS: () => LEGO_TOKENS,
37
37
  LegoLandWrapper: () => LegoLandWrapper,
38
38
  NurixThemeProvider: () => NurixThemeProvider,
39
39
  ShadowDOMWrapper: () => ShadowDOMWrapper,
40
40
  Switch: () => Switch,
41
41
  ThemeProvider: () => ThemeProvider,
42
- getLegoLandThemeCSSVars: () => getLegoLandThemeCSSVars,
43
- hexToHslVarValue: () => hexToHslVarValue,
44
42
  useTheme: () => useTheme
45
43
  });
46
44
  module.exports = __toCommonJS(index_exports);
@@ -49,107 +47,12 @@ module.exports = __toCommonJS(index_exports);
49
47
  var React = __toESM(require("react"));
50
48
  var import_react_slot = require("@radix-ui/react-slot");
51
49
 
52
- // src/theme/variables.ts
53
- var LEGO_TOKENS = {
54
- twColors: {
55
- mode1: {
56
- slate50: "hsla(210, 40, 98, 1)",
57
- slate100: "hsla(210, 40, 96, 1)",
58
- slate200: "hsla(214, 32, 91, 1)",
59
- slate300: "hsla(213, 27, 84, 1)",
60
- slate400: "hsla(215, 20, 65, 1)",
61
- slate500: "hsla(215, 14, 47, 1)",
62
- slate600: "hsla(215, 19, 35, 1)",
63
- slate700: "hsla(215, 25, 27, 1)",
64
- slate800: "hsla(217, 33, 17, 1)",
65
- slate900: "hsla(222, 47, 11, 1)",
66
- slate950: "hsla(229, 84, 5, 1)",
67
- white: "hsla(0, 0, 100, 1)",
68
- black: "hsla(0, 0, 0, 1)"
69
- }
70
- },
71
- uIColors: {
72
- lightMode: {
73
- background: "#f5f5f5",
74
- foreground: "#0a0a0a",
75
- border: "#e5e5e5",
76
- destructive: "#dc2626",
77
- secondary: "#e5e5e5",
78
- mutedForeground: "#737373",
79
- muted: "#f5f5f5",
80
- backgroundColor: "#0000004c",
81
- primary: "#1d4885",
82
- secondaryForeground: "#0a0a0a",
83
- accentForeground: "#171717",
84
- accent: "#f5f5f5",
85
- ring: "#0090ff",
86
- chart3: "#0588f0",
87
- chart2: "#0090ff",
88
- chart4: "#0d74ce",
89
- chart1: "#5eb1ef",
90
- chart5: "#113264",
91
- card: "#ffffff",
92
- cardForeground: "#0a0a0a",
93
- popover: "#ffffff",
94
- popoverForeground: "#0a0a0a",
95
- primaryForeground: "#fafafa",
96
- input: "#f5f5f5",
97
- sidebar: "#fafafa",
98
- sidebarForeground: "#0a0a0a",
99
- sidebarPrimary: "#171717",
100
- sidebarPrimaryForeground: "#fafafa",
101
- sidebarAccent: "#f5f5f5",
102
- sidebarAccentForeground: "#171717",
103
- sidebarBorder: "#d4d4d4",
104
- sidebarRing: "#737373",
105
- semanticBackground: "#696867",
106
- semanticBorder: "#898887",
107
- semanticForeground: "#ffffff",
108
- disabled: "#d4d4d4",
109
- primary10: "#1d488519",
110
- success: "#047857"
111
- },
112
- darkMode: {
113
- background: "#1e1e1e",
114
- foreground: "#fafafa",
115
- border: "#404040",
116
- destructive: "#f87171",
117
- secondary: "#262626",
118
- mutedForeground: "#a3a3a3",
119
- muted: "#262626",
120
- backgroundColor: "#0000004c",
121
- primary: "#b5d4fd",
122
- secondaryForeground: "#fafafa",
123
- accentForeground: "#fafafa",
124
- accent: "#262626",
125
- ring: "#acd8fc",
126
- chart3: "#0588f0",
127
- chart2: "#0090ff",
128
- chart4: "#0d74ce",
129
- chart1: "#5eb1ef",
130
- chart5: "#113264",
131
- card: "#171717",
132
- cardForeground: "#fafafa",
133
- popover: "#262626",
134
- popoverForeground: "#fafafa",
135
- primaryForeground: "#171717",
136
- input: "#2e3034",
137
- sidebar: "#171717",
138
- sidebarForeground: "#fafafa",
139
- sidebarPrimary: "#0588f0",
140
- sidebarPrimaryForeground: "#fafafa",
141
- sidebarAccent: "#262626",
142
- sidebarAccentForeground: "#fafafa",
143
- sidebarBorder: "#ffffffcc",
144
- sidebarRing: "#737373",
145
- semanticBackground: "#272625",
146
- semanticBorder: "#535151",
147
- semanticForeground: "#ffffff",
148
- disabled: "#404040",
149
- primary10: "#e6f1ff19",
150
- success: "#a7f3d0"
151
- }
152
- },
50
+ // src/theme/default-theme.ts
51
+ var DEFAULT_THEME = {
52
+ /**
53
+ * Design tokens (numbers) used by components.
54
+ * This file is the single source of truth (do not duplicate these elsewhere).
55
+ */
153
56
  twPadding: {
154
57
  mode1: {
155
58
  px0: 0,
@@ -241,457 +144,7 @@ var LEGO_TOKENS = {
241
144
  "275": 2.75
242
145
  }
243
146
  },
244
- components: {
245
- mode1: {
246
- inputRadius: 16,
247
- inputHeight: 48,
248
- inputHeightLarge: 64,
249
- inputPaddingHorizontal: 16,
250
- inputPaddingVertical: 8,
251
- inputActiveBg: "hsla(0, 0, 96, 1)",
252
- inputFocussedBg: "hsla(0, 0, 98, 1)",
253
- inputFocussedRing: "hsla(200, 64, 39, 1)",
254
- buttonHeight: 40,
255
- buttonRadius: 24
256
- }
257
- }
258
- };
259
- var clamp01 = (n) => Math.min(1, Math.max(0, n));
260
- function hexToHslVarValue(hex) {
261
- const normalized = hex.trim();
262
- if (!normalized.startsWith("#")) {
263
- throw new Error(`hexToHslVarValue expected a hex string, got: ${hex}`);
264
- }
265
- const h = normalized.slice(1);
266
- const isShort = h.length === 3 || h.length === 4;
267
- const isLong = h.length === 6 || h.length === 8;
268
- if (!isShort && !isLong) {
269
- throw new Error(`hexToHslVarValue expected #RGB/#RRGGBB (optionally with alpha), got: ${hex}`);
270
- }
271
- const hexToByte = (s) => parseInt(s, 16);
272
- const r = isShort ? hexToByte(h[0] + h[0]) : hexToByte(h.slice(0, 2));
273
- const g = isShort ? hexToByte(h[1] + h[1]) : hexToByte(h.slice(2, 4));
274
- const b = isShort ? hexToByte(h[2] + h[2]) : hexToByte(h.slice(4, 6));
275
- const rf = r / 255;
276
- const gf = g / 255;
277
- const bf = b / 255;
278
- const max = Math.max(rf, gf, bf);
279
- const min = Math.min(rf, gf, bf);
280
- const delta = max - min;
281
- let hue = 0;
282
- if (delta !== 0) {
283
- if (max === rf) hue = (gf - bf) / delta % 6;
284
- else if (max === gf) hue = (bf - rf) / delta + 2;
285
- else hue = (rf - gf) / delta + 4;
286
- hue *= 60;
287
- if (hue < 0) hue += 360;
288
- }
289
- const light = (max + min) / 2;
290
- const sat = delta === 0 ? 0 : delta / (1 - Math.abs(2 * light - 1));
291
- const H = Math.round(hue);
292
- const S = Math.round(clamp01(sat) * 100);
293
- const L = Math.round(clamp01(light) * 100);
294
- return `${H} ${S}% ${L}%`;
295
- }
296
- var UI_TO_CSS_VAR = {
297
- background: "--background",
298
- foreground: "--foreground",
299
- border: "--border",
300
- input: "--input",
301
- ring: "--ring",
302
- primary: "--primary",
303
- primaryForeground: "--primary-foreground",
304
- secondary: "--secondary",
305
- secondaryForeground: "--secondary-foreground",
306
- destructive: "--destructive",
307
- // Tailwind shadcn-style also supports this var; we set it if present later.
308
- destructiveForeground: "--destructive-foreground",
309
- muted: "--muted",
310
- mutedForeground: "--muted-foreground",
311
- accent: "--accent",
312
- accentForeground: "--accent-foreground",
313
- card: "--card",
314
- cardForeground: "--card-foreground",
315
- popover: "--popover",
316
- popoverForeground: "--popover-foreground"
317
- };
318
- function getLegoLandThemeCSSVars(theme) {
319
- var _a;
320
- const ui = theme === "dark" ? LEGO_TOKENS.uIColors.darkMode : LEGO_TOKENS.uIColors.lightMode;
321
- const out = {
322
- colorScheme: theme,
323
- "--radius": `${LEGO_TOKENS.radius.mode1.roundedMd}px`
324
- };
325
- for (const [uiKey, cssVar] of Object.entries(UI_TO_CSS_VAR)) {
326
- const value = ui[uiKey];
327
- if (typeof value === "string" && value.startsWith("#")) {
328
- out[cssVar] = hexToHslVarValue(value);
329
- }
330
- }
331
- if (!out["--destructive-foreground"]) {
332
- out["--destructive-foreground"] = (_a = out["--primary-foreground"]) != null ? _a : hexToHslVarValue("#ffffff");
333
- }
334
- return out;
335
- }
336
-
337
- // src/button/variables.ts
338
- var BUTTON_TOKENS = {
339
- base: "inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-medium transition-colors focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50",
340
- variant: {
341
- primary: "bg-primary text-primary-foreground hover:bg-primary/90",
342
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
343
- destructive: "bg-destructive text-primary-foreground hover:bg-destructive/90",
344
- outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground text-foreground",
345
- link: "text-foreground underline-offset-4 hover:underline"
346
- },
347
- size: {
348
- default: "h-10 px-4 py-2",
349
- sm: "h-9 px-3",
350
- lg: "h-10 px-8",
351
- icon: "h-10 w-10"
352
- },
353
- radius: {
354
- none: `${LEGO_TOKENS.radius.mode1.roundedNone}px`,
355
- soft: `${LEGO_TOKENS.radius.mode1.roundedMd}px`,
356
- rounded: `${LEGO_TOKENS.components.mode1.buttonRadius}px`,
357
- pill: `${LEGO_TOKENS.radius.mode1.roundedFull}px`
358
- }
359
- };
360
-
361
- // src/lib/utils.ts
362
- var import_clsx = require("clsx");
363
- var import_tailwind_merge = require("tailwind-merge");
364
- function cn(...inputs) {
365
- return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
366
- }
367
-
368
- // src/button/button.tsx
369
- var import_jsx_runtime = require("react/jsx-runtime");
370
- var Button = React.forwardRef(
371
- ({
372
- className,
373
- variant = "primary",
374
- size,
375
- asChild = false,
376
- button_border_radius = "rounded",
377
- style,
378
- ...props
379
- }, ref) => {
380
- const Comp = asChild ? import_react_slot.Slot : "button";
381
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
382
- Comp,
383
- {
384
- ref,
385
- className: cn(
386
- BUTTON_TOKENS.base,
387
- BUTTON_TOKENS.variant[variant],
388
- BUTTON_TOKENS.size[size != null ? size : "default"],
389
- className
390
- ),
391
- style: {
392
- borderRadius: BUTTON_TOKENS.radius[button_border_radius],
393
- ...style
394
- },
395
- ...props
396
- }
397
- );
398
- }
399
- );
400
- Button.displayName = "Button";
401
-
402
- // src/input/input.tsx
403
- var React2 = __toESM(require("react"));
404
-
405
- // src/input/variables.ts
406
- var INPUT_TOKENS = {
407
- root: "flex w-full flex-col gap-1",
408
- labelRow: "flex items-center gap-1 text-xs font-medium leading-4 tracking-normal text-foreground",
409
- mandatory: "text-destructive",
410
- field: "flex h-12 w-full items-center justify-between overflow-hidden bg-input px-4 py-2 text-sm shadow-none",
411
- fieldBase: "border border-transparent focus-within:border-ring disabled:pointer-events-none",
412
- fieldDisabled: "opacity-50",
413
- input: "w-full bg-transparent outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-100",
414
- supportingTextBase: "text-xs font-normal leading-4 tracking-normal text-muted-foreground",
415
- supportingTextError: "text-destructive",
416
- radius: {
417
- none: `${LEGO_TOKENS.radius.mode1.roundedNone}px`,
418
- soft: `${LEGO_TOKENS.radius.mode1.roundedMd}px`,
419
- rounded: `${LEGO_TOKENS.components.mode1.inputRadius}px`,
420
- pill: `${LEGO_TOKENS.radius.mode1.roundedFull}px`
421
- }
422
- };
423
-
424
- // src/input/input.tsx
425
- var import_jsx_runtime2 = require("react/jsx-runtime");
426
- var Input = React2.forwardRef(
427
- ({
428
- className,
429
- label,
430
- mandatory = false,
431
- supportingText,
432
- supportingTextType = "help",
433
- hideSupportingText = false,
434
- input_border_radius = "rounded",
435
- forceState,
436
- disabled,
437
- style,
438
- ...props
439
- }, ref) => {
440
- const isDisabled = forceState === "disabled" || disabled;
441
- const isFocussed = forceState === "focussed";
442
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
443
- "div",
444
- {
445
- className: cn(INPUT_TOKENS.root, isDisabled && INPUT_TOKENS.fieldDisabled, className),
446
- "data-state": forceState,
447
- children: [
448
- label ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: INPUT_TOKENS.labelRow, children: [
449
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: label }),
450
- mandatory ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: INPUT_TOKENS.mandatory, children: "*" }) : null
451
- ] }) : null,
452
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
453
- "div",
454
- {
455
- className: cn(
456
- INPUT_TOKENS.field,
457
- INPUT_TOKENS.fieldBase,
458
- isFocussed && "border-ring"
459
- ),
460
- style: {
461
- borderRadius: INPUT_TOKENS.radius[input_border_radius],
462
- ...style
463
- },
464
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
465
- "input",
466
- {
467
- ref,
468
- className: cn(INPUT_TOKENS.input),
469
- disabled: isDisabled,
470
- ...props
471
- }
472
- )
473
- }
474
- ),
475
- !hideSupportingText && supportingText ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
476
- "p",
477
- {
478
- className: cn(
479
- INPUT_TOKENS.supportingTextBase,
480
- supportingTextType === "error" && INPUT_TOKENS.supportingTextError
481
- ),
482
- children: supportingText
483
- }
484
- ) : null
485
- ]
486
- }
487
- );
488
- }
489
- );
490
- Input.displayName = "Input";
491
-
492
- // src/input/file-input.tsx
493
- var React3 = __toESM(require("react"));
494
- var import_jsx_runtime3 = require("react/jsx-runtime");
495
- var FileInput = React3.forwardRef(
496
- ({
497
- className,
498
- label,
499
- mandatory = false,
500
- buttonLabel = "Choose File",
501
- placeholder = "No file chosen",
502
- fileName,
503
- input_border_radius = "rounded",
504
- disabled,
505
- onFileChange,
506
- style,
507
- ...props
508
- }, ref) => {
509
- const internalRef = React3.useRef(null);
510
- React3.useImperativeHandle(ref, () => internalRef.current, []);
511
- const [internalName, setInternalName] = React3.useState("");
512
- const effectiveName = fileName != null ? fileName : internalName;
513
- const handleChoose = () => {
514
- var _a;
515
- if (disabled) return;
516
- (_a = internalRef.current) == null ? void 0 : _a.click();
517
- };
518
- const handleChange = (e) => {
519
- var _a, _b, _c;
520
- const file = (_b = (_a = e.target.files) == null ? void 0 : _a[0]) != null ? _b : null;
521
- if (!fileName) {
522
- setInternalName((_c = file == null ? void 0 : file.name) != null ? _c : "");
523
- }
524
- onFileChange == null ? void 0 : onFileChange(file);
525
- };
526
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: cn(INPUT_TOKENS.root, className), children: [
527
- label ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: INPUT_TOKENS.labelRow, children: [
528
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: label }),
529
- mandatory ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: INPUT_TOKENS.mandatory, children: "*" }) : null
530
- ] }) : null,
531
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
532
- "div",
533
- {
534
- className: cn(
535
- INPUT_TOKENS.field,
536
- INPUT_TOKENS.fieldBase,
537
- "shadow-[0px_1px_2px_rgba(0,0,0,0.1)]",
538
- disabled && INPUT_TOKENS.fieldDisabled
539
- ),
540
- style: {
541
- borderRadius: INPUT_TOKENS.radius[input_border_radius],
542
- ...style
543
- },
544
- children: [
545
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
546
- "button",
547
- {
548
- type: "button",
549
- onClick: handleChoose,
550
- disabled,
551
- className: cn(
552
- "mr-1 shrink-0 rounded-sm px-1.5 py-px text-sm font-medium leading-5 text-foreground",
553
- disabled && "cursor-not-allowed"
554
- ),
555
- children: buttonLabel
556
- }
557
- ),
558
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-muted-foreground", children: effectiveName || placeholder }),
559
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
560
- "input",
561
- {
562
- ref: internalRef,
563
- type: "file",
564
- tabIndex: -1,
565
- className: "sr-only",
566
- disabled,
567
- onChange: handleChange,
568
- ...props
569
- }
570
- )
571
- ]
572
- }
573
- )
574
- ] });
575
- }
576
- );
577
- FileInput.displayName = "FileInput";
578
-
579
- // src/switch/switch.tsx
580
- var React4 = __toESM(require("react"));
581
-
582
- // src/switch/variables.ts
583
- var SWITCH_TOKENS = {
584
- rootBase: "relative inline-flex items-center shrink-0 rounded-full transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ring-offset-background",
585
- rootDisabled: "opacity-50 cursor-not-allowed",
586
- // Track colors (matches Figma: Off = neutral/200, On = primary)
587
- trackOff: "bg-[var(--neutral-200)]",
588
- trackOn: "bg-primary",
589
- // Thumb colors (matches Figma: Off = card, On = primary-foreground)
590
- thumbOff: "bg-card",
591
- thumbOn: "bg-primary-foreground",
592
- thumbBase: "absolute top-[2px] left-[2px] rounded-full transition-transform shadow-sm",
593
- size: {
594
- // Figma default: track 44x24, thumb 20
595
- default: {
596
- track: "h-6 w-11",
597
- thumb: "h-5 w-5",
598
- translateOn: "translate-x-5",
599
- translateOff: "translate-x-0"
600
- },
601
- // Figma small: track 22x12, thumb appears smaller
602
- small: {
603
- track: "h-3 w-[22px]",
604
- thumb: "h-2 w-2",
605
- translateOn: "translate-x-[10px]",
606
- translateOff: "translate-x-0"
607
- // thumb top/left needs to be tighter for small; we'll override via inline classes
608
- }
609
- }
610
- };
611
-
612
- // src/switch/switch.tsx
613
- var import_jsx_runtime4 = require("react/jsx-runtime");
614
- var Switch = React4.forwardRef(
615
- ({
616
- checked,
617
- defaultChecked,
618
- onCheckedChange,
619
- disabled,
620
- size = "default",
621
- className,
622
- style,
623
- id
624
- }, ref) => {
625
- const isControlled = checked !== void 0;
626
- const [uncontrolled, setUncontrolled] = React4.useState(
627
- defaultChecked != null ? defaultChecked : false
628
- );
629
- const isOn = isControlled ? !!checked : uncontrolled;
630
- const sz = SWITCH_TOKENS.size[size];
631
- const toggle = () => {
632
- if (disabled) return;
633
- const next = !isOn;
634
- if (!isControlled) setUncontrolled(next);
635
- onCheckedChange == null ? void 0 : onCheckedChange(next);
636
- };
637
- const onKeyDown = (e) => {
638
- if (disabled) return;
639
- if (e.key === "Enter" || e.key === " ") {
640
- e.preventDefault();
641
- toggle();
642
- }
643
- };
644
- const smallNudge = size === "small" ? "top-[1px] left-[1px]" : "";
645
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
646
- "button",
647
- {
648
- id,
649
- ref,
650
- type: "button",
651
- role: "switch",
652
- "aria-checked": isOn,
653
- "aria-disabled": disabled || void 0,
654
- disabled,
655
- onClick: toggle,
656
- onKeyDown,
657
- className: cn(
658
- SWITCH_TOKENS.rootBase,
659
- sz.track,
660
- isOn ? SWITCH_TOKENS.trackOn : SWITCH_TOKENS.trackOff,
661
- disabled && SWITCH_TOKENS.rootDisabled,
662
- className
663
- ),
664
- style,
665
- "data-state": isOn ? "checked" : "unchecked",
666
- "data-size": size,
667
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
668
- "span",
669
- {
670
- "aria-hidden": "true",
671
- className: cn(
672
- SWITCH_TOKENS.thumbBase,
673
- smallNudge,
674
- sz.thumb,
675
- isOn ? SWITCH_TOKENS.thumbOn : SWITCH_TOKENS.thumbOff,
676
- isOn ? sz.translateOn : sz.translateOff
677
- )
678
- }
679
- )
680
- }
681
- );
682
- }
683
- );
684
- Switch.displayName = "Switch";
685
-
686
- // src/theme/theme-context.tsx
687
- var React6 = __toESM(require("react"));
688
-
689
- // src/theme/lego-land-wrapper.tsx
690
- var React5 = __toESM(require("react"));
691
-
692
- // src/theme/default-theme.ts
693
- var DEFAULT_THEME = {
694
- twColors: {
147
+ twColors: {
695
148
  mode1: {
696
149
  slate50: "#f8fafc",
697
150
  slate100: "#f1f5f9",
@@ -948,7 +401,7 @@ var DEFAULT_THEME = {
948
401
  secondary: "#e5e5e5",
949
402
  mutedForeground: "#737373",
950
403
  muted: "#f5f5f5",
951
- backgroundColor: "#0000004c",
404
+ backgroundColor: "#0000004d",
952
405
  primary: "#1d4885",
953
406
  secondaryForeground: "#0a0a0a",
954
407
  accentForeground: "#171717",
@@ -988,8 +441,8 @@ var DEFAULT_THEME = {
988
441
  secondary: "#262626",
989
442
  mutedForeground: "#a3a3a3",
990
443
  muted: "#262626",
991
- backgroundColor: "#0000004c",
992
- primary: "#b5d4fd",
444
+ backgroundColor: "#0000004d",
445
+ primary: "#b5d5fd",
993
446
  secondaryForeground: "#fafafa",
994
447
  accentForeground: "#fafafa",
995
448
  accent: "#262626",
@@ -1017,40 +470,425 @@ var DEFAULT_THEME = {
1017
470
  semanticBorder: "#535151",
1018
471
  semanticForeground: "#ffffff",
1019
472
  disabled: "#404040",
1020
- primary10: "#e6f1ff19",
473
+ primary10: "#e6f1ff1a",
1021
474
  success: "#a7f3d0"
1022
475
  }
1023
476
  },
1024
477
  components: {
1025
478
  mode1: {
1026
- inputActiveBg: "#f5f5f5",
1027
- inputFocussedBg: "#fafafa",
1028
- inputFocussedRing: "#0284c7"
479
+ // Component token defaults (numbers + colors)
480
+ inputRadius: 16,
481
+ inputHeight: 48,
482
+ inputHeightLarge: 64,
483
+ inputPaddingHorizontal: 16,
484
+ inputPaddingVertical: 8,
485
+ buttonHeight: 40,
486
+ buttonRadius: 24,
487
+ // These are semantic per-mode in `uIColors`, but kept here for convenience.
488
+ // (Figma "Components" page uses dark mode values.)
489
+ inputActiveBg: "#2e3034",
490
+ inputFocussedBg: "#2e3034",
491
+ inputFocussedRing: "#acd8fc"
1029
492
  }
1030
493
  }
1031
494
  };
1032
495
 
1033
- // src/theme/theme-utils.ts
1034
- function clamp012(n) {
1035
- return Math.min(1, Math.max(0, n));
1036
- }
1037
- function hexToRgb(hex) {
1038
- const raw = hex.trim().replace(/^#/, "");
1039
- const normalized = raw.length === 3 ? raw.split("").map((c) => c + c).join("") : raw.length === 4 ? raw.slice(0, 3).split("").map((c) => c + c).join("") : raw.length === 6 ? raw : raw.length === 8 ? raw.slice(0, 6) : null;
1040
- if (!normalized) return null;
1041
- const int = Number.parseInt(normalized, 16);
1042
- if (Number.isNaN(int)) return null;
1043
- return {
1044
- r: int >> 16 & 255,
1045
- g: int >> 8 & 255,
1046
- b: int & 255
1047
- };
496
+ // src/button/variables.ts
497
+ var _a, _b;
498
+ var BUTTON_TOKENS = {
499
+ base: "relative inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none [&>svg]:h-4 [&>svg]:w-4",
500
+ hoverOverlay: "overflow-hidden after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:rounded-[inherit] after:bg-black/10 after:opacity-0 after:transition-opacity hover:after:opacity-100",
501
+ variant: {
502
+ primary: "bg-primary text-primary-foreground disabled:opacity-50",
503
+ secondary: "bg-secondary text-secondary-foreground disabled:bg-muted-foreground disabled:text-background disabled:opacity-100",
504
+ destructive: "bg-destructive text-destructive-foreground disabled:opacity-50",
505
+ outline: "border border-border bg-transparent text-foreground hover:bg-foreground/5 disabled:border-[#b2b2b2] disabled:text-[#b2b2b2] disabled:opacity-100",
506
+ link: "bg-transparent text-foreground hover:text-primary disabled:text-muted-foreground disabled:opacity-100",
507
+ // Icon-only variants (pair with size `icon` / `iconSm`)
508
+ iconPrimary: "bg-primary text-primary-foreground disabled:opacity-50",
509
+ iconSecondary: "bg-secondary text-secondary-foreground disabled:bg-muted-foreground disabled:text-secondary disabled:opacity-100",
510
+ iconOutline: "border border-border bg-transparent text-foreground hover:bg-foreground/5 disabled:border-[#b2b2b2] disabled:text-[#b2b2b2] disabled:opacity-100"
511
+ },
512
+ size: {
513
+ default: "h-10 px-4 py-2",
514
+ sm: "h-9 px-3",
515
+ lg: "h-10 px-8",
516
+ icon: "h-10 w-10",
517
+ iconSm: "h-9 w-9"
518
+ },
519
+ radius: {
520
+ none: `${DEFAULT_THEME.radius.mode1.roundedNone}px`,
521
+ soft: `${DEFAULT_THEME.radius.mode1.roundedMd}px`,
522
+ rounded: `${(_b = (_a = DEFAULT_THEME.components) == null ? void 0 : _a.mode1.buttonRadius) != null ? _b : 24}px`,
523
+ pill: `${DEFAULT_THEME.radius.mode1.roundedFull}px`
524
+ }
525
+ };
526
+
527
+ // src/lib/utils.ts
528
+ var import_clsx = require("clsx");
529
+ var import_tailwind_merge = require("tailwind-merge");
530
+ function cn(...inputs) {
531
+ return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
1048
532
  }
1049
- function hexToHslChannels(hex) {
1050
- const rgb = hexToRgb(hex);
1051
- if (!rgb) return null;
1052
- const r = rgb.r / 255;
1053
- const g = rgb.g / 255;
533
+
534
+ // src/button/button.tsx
535
+ var import_jsx_runtime = require("react/jsx-runtime");
536
+ var Button = React.forwardRef(
537
+ ({
538
+ className,
539
+ variant = "primary",
540
+ size,
541
+ asChild = false,
542
+ button_border_radius = "rounded",
543
+ leadingIcon,
544
+ trailingIcon,
545
+ showLeadingIcon = true,
546
+ showTrailingIcon = true,
547
+ style,
548
+ children,
549
+ ...props
550
+ }, ref) => {
551
+ const Comp = asChild ? import_react_slot.Slot : "button";
552
+ const withHoverOverlay = variant === "primary" || variant === "secondary" || variant === "destructive" || variant === "iconPrimary" || variant === "iconSecondary";
553
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
554
+ Comp,
555
+ {
556
+ ref,
557
+ className: cn(
558
+ BUTTON_TOKENS.base,
559
+ BUTTON_TOKENS.variant[variant],
560
+ withHoverOverlay && BUTTON_TOKENS.hoverOverlay,
561
+ BUTTON_TOKENS.size[size != null ? size : "default"],
562
+ className
563
+ ),
564
+ style: {
565
+ borderRadius: BUTTON_TOKENS.radius[button_border_radius],
566
+ ...style
567
+ },
568
+ ...props,
569
+ children: asChild ? children : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
570
+ showLeadingIcon ? leadingIcon : null,
571
+ children,
572
+ showTrailingIcon ? trailingIcon : null
573
+ ] })
574
+ }
575
+ );
576
+ }
577
+ );
578
+ Button.displayName = "Button";
579
+
580
+ // src/input/input.tsx
581
+ var React2 = __toESM(require("react"));
582
+
583
+ // src/input/variables.ts
584
+ var _a2, _b2;
585
+ var INPUT_TOKENS = {
586
+ root: "flex w-full flex-col gap-1",
587
+ labelRow: "flex items-center gap-1 text-xs font-medium leading-4 tracking-normal text-foreground",
588
+ mandatory: "text-destructive",
589
+ field: "flex h-12 w-full items-center justify-between overflow-hidden bg-input px-4 py-2 text-sm shadow-none",
590
+ fieldBase: "border border-transparent focus-within:border-ring disabled:pointer-events-none",
591
+ fieldDisabled: "opacity-50",
592
+ input: "w-full bg-transparent outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-100",
593
+ supportingTextBase: "text-xs font-normal leading-4 tracking-normal text-muted-foreground",
594
+ supportingTextError: "text-destructive",
595
+ radius: {
596
+ none: `${DEFAULT_THEME.radius.mode1.roundedNone}px`,
597
+ soft: `${DEFAULT_THEME.radius.mode1.roundedMd}px`,
598
+ rounded: `${(_b2 = (_a2 = DEFAULT_THEME.components) == null ? void 0 : _a2.mode1.inputRadius) != null ? _b2 : 16}px`,
599
+ pill: `${DEFAULT_THEME.radius.mode1.roundedFull}px`
600
+ }
601
+ };
602
+
603
+ // src/input/input.tsx
604
+ var import_jsx_runtime2 = require("react/jsx-runtime");
605
+ var Input = React2.forwardRef(
606
+ ({
607
+ className,
608
+ label,
609
+ mandatory = false,
610
+ supportingText,
611
+ supportingTextType = "help",
612
+ hideSupportingText = false,
613
+ input_border_radius = "rounded",
614
+ forceState,
615
+ disabled,
616
+ style,
617
+ ...props
618
+ }, ref) => {
619
+ const isDisabled = forceState === "disabled" || disabled;
620
+ const isFocussed = forceState === "focussed";
621
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
622
+ "div",
623
+ {
624
+ className: cn(INPUT_TOKENS.root, isDisabled && INPUT_TOKENS.fieldDisabled, className),
625
+ "data-state": forceState,
626
+ children: [
627
+ label ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: INPUT_TOKENS.labelRow, children: [
628
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: label }),
629
+ mandatory ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: INPUT_TOKENS.mandatory, children: "*" }) : null
630
+ ] }) : null,
631
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
632
+ "div",
633
+ {
634
+ className: cn(
635
+ INPUT_TOKENS.field,
636
+ INPUT_TOKENS.fieldBase,
637
+ isFocussed && "border-ring"
638
+ ),
639
+ style: {
640
+ borderRadius: INPUT_TOKENS.radius[input_border_radius],
641
+ ...style
642
+ },
643
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
644
+ "input",
645
+ {
646
+ ref,
647
+ className: cn(INPUT_TOKENS.input),
648
+ disabled: isDisabled,
649
+ ...props
650
+ }
651
+ )
652
+ }
653
+ ),
654
+ !hideSupportingText && supportingText ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
655
+ "p",
656
+ {
657
+ className: cn(
658
+ INPUT_TOKENS.supportingTextBase,
659
+ supportingTextType === "error" && INPUT_TOKENS.supportingTextError
660
+ ),
661
+ children: supportingText
662
+ }
663
+ ) : null
664
+ ]
665
+ }
666
+ );
667
+ }
668
+ );
669
+ Input.displayName = "Input";
670
+
671
+ // src/input/file-input.tsx
672
+ var React3 = __toESM(require("react"));
673
+ var import_jsx_runtime3 = require("react/jsx-runtime");
674
+ var FileInput = React3.forwardRef(
675
+ ({
676
+ className,
677
+ label,
678
+ mandatory = false,
679
+ buttonLabel = "Choose File",
680
+ placeholder = "No file chosen",
681
+ fileName,
682
+ input_border_radius = "rounded",
683
+ disabled,
684
+ onFileChange,
685
+ style,
686
+ ...props
687
+ }, ref) => {
688
+ const internalRef = React3.useRef(null);
689
+ React3.useImperativeHandle(ref, () => internalRef.current, []);
690
+ const [internalName, setInternalName] = React3.useState("");
691
+ const effectiveName = fileName != null ? fileName : internalName;
692
+ const handleChoose = () => {
693
+ var _a3;
694
+ if (disabled) return;
695
+ (_a3 = internalRef.current) == null ? void 0 : _a3.click();
696
+ };
697
+ const handleChange = (e) => {
698
+ var _a3, _b3, _c;
699
+ const file = (_b3 = (_a3 = e.target.files) == null ? void 0 : _a3[0]) != null ? _b3 : null;
700
+ if (!fileName) {
701
+ setInternalName((_c = file == null ? void 0 : file.name) != null ? _c : "");
702
+ }
703
+ onFileChange == null ? void 0 : onFileChange(file);
704
+ };
705
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: cn(INPUT_TOKENS.root, className), children: [
706
+ label ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: INPUT_TOKENS.labelRow, children: [
707
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: label }),
708
+ mandatory ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: INPUT_TOKENS.mandatory, children: "*" }) : null
709
+ ] }) : null,
710
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
711
+ "div",
712
+ {
713
+ className: cn(
714
+ INPUT_TOKENS.field,
715
+ INPUT_TOKENS.fieldBase,
716
+ "shadow-[0px_1px_2px_rgba(0,0,0,0.1)]",
717
+ disabled && INPUT_TOKENS.fieldDisabled
718
+ ),
719
+ style: {
720
+ borderRadius: INPUT_TOKENS.radius[input_border_radius],
721
+ ...style
722
+ },
723
+ children: [
724
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
725
+ "button",
726
+ {
727
+ type: "button",
728
+ onClick: handleChoose,
729
+ disabled,
730
+ className: cn(
731
+ "mr-1 shrink-0 rounded-sm px-1.5 py-px text-sm font-medium leading-5 text-foreground",
732
+ disabled && "cursor-not-allowed"
733
+ ),
734
+ children: buttonLabel
735
+ }
736
+ ),
737
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "truncate text-sm font-normal leading-5 text-muted-foreground", children: effectiveName || placeholder }),
738
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
739
+ "input",
740
+ {
741
+ ref: internalRef,
742
+ type: "file",
743
+ tabIndex: -1,
744
+ className: "sr-only",
745
+ disabled,
746
+ onChange: handleChange,
747
+ ...props
748
+ }
749
+ )
750
+ ]
751
+ }
752
+ )
753
+ ] });
754
+ }
755
+ );
756
+ FileInput.displayName = "FileInput";
757
+
758
+ // src/switch/switch.tsx
759
+ var React4 = __toESM(require("react"));
760
+
761
+ // src/switch/variables.ts
762
+ var SWITCH_TOKENS = {
763
+ rootBase: "relative inline-flex items-center shrink-0 rounded-full transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ring-offset-background",
764
+ rootDisabled: "opacity-50 cursor-not-allowed",
765
+ // Track colors (matches Figma: Off = neutral/200, On = primary)
766
+ trackOff: "bg-[var(--neutral-200)]",
767
+ trackOn: "bg-primary",
768
+ // Thumb colors (matches Figma: Off = card, On = primary-foreground)
769
+ thumbOff: "bg-card",
770
+ thumbOn: "bg-primary-foreground",
771
+ thumbBase: "absolute top-[2px] left-[2px] rounded-full transition-transform shadow-sm",
772
+ size: {
773
+ // Figma default: track 44x24, thumb 20
774
+ default: {
775
+ track: "h-6 w-11",
776
+ thumb: "h-5 w-5",
777
+ translateOn: "translate-x-5",
778
+ translateOff: "translate-x-0"
779
+ },
780
+ // Figma small: track 22x12, thumb appears smaller
781
+ small: {
782
+ track: "h-3 w-[22px]",
783
+ thumb: "h-2 w-2",
784
+ translateOn: "translate-x-[10px]",
785
+ translateOff: "translate-x-0"
786
+ // thumb top/left needs to be tighter for small; we'll override via inline classes
787
+ }
788
+ }
789
+ };
790
+
791
+ // src/switch/switch.tsx
792
+ var import_jsx_runtime4 = require("react/jsx-runtime");
793
+ var Switch = React4.forwardRef(
794
+ ({
795
+ checked,
796
+ defaultChecked,
797
+ onCheckedChange,
798
+ disabled,
799
+ size = "default",
800
+ className,
801
+ style,
802
+ id
803
+ }, ref) => {
804
+ const isControlled = checked !== void 0;
805
+ const [uncontrolled, setUncontrolled] = React4.useState(
806
+ defaultChecked != null ? defaultChecked : false
807
+ );
808
+ const isOn = isControlled ? !!checked : uncontrolled;
809
+ const sz = SWITCH_TOKENS.size[size];
810
+ const toggle = () => {
811
+ if (disabled) return;
812
+ const next = !isOn;
813
+ if (!isControlled) setUncontrolled(next);
814
+ onCheckedChange == null ? void 0 : onCheckedChange(next);
815
+ };
816
+ const onKeyDown = (e) => {
817
+ if (disabled) return;
818
+ if (e.key === "Enter" || e.key === " ") {
819
+ e.preventDefault();
820
+ toggle();
821
+ }
822
+ };
823
+ const smallNudge = size === "small" ? "top-[1px] left-[1px]" : "";
824
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
825
+ "button",
826
+ {
827
+ id,
828
+ ref,
829
+ type: "button",
830
+ role: "switch",
831
+ "aria-checked": isOn,
832
+ "aria-disabled": disabled || void 0,
833
+ disabled,
834
+ onClick: toggle,
835
+ onKeyDown,
836
+ className: cn(
837
+ SWITCH_TOKENS.rootBase,
838
+ sz.track,
839
+ isOn ? SWITCH_TOKENS.trackOn : SWITCH_TOKENS.trackOff,
840
+ disabled && SWITCH_TOKENS.rootDisabled,
841
+ className
842
+ ),
843
+ style,
844
+ "data-state": isOn ? "checked" : "unchecked",
845
+ "data-size": size,
846
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
847
+ "span",
848
+ {
849
+ "aria-hidden": "true",
850
+ className: cn(
851
+ SWITCH_TOKENS.thumbBase,
852
+ smallNudge,
853
+ sz.thumb,
854
+ isOn ? SWITCH_TOKENS.thumbOn : SWITCH_TOKENS.thumbOff,
855
+ isOn ? sz.translateOn : sz.translateOff
856
+ )
857
+ }
858
+ )
859
+ }
860
+ );
861
+ }
862
+ );
863
+ Switch.displayName = "Switch";
864
+
865
+ // src/theme/theme-context.tsx
866
+ var React6 = __toESM(require("react"));
867
+
868
+ // src/theme/lego-land-wrapper.tsx
869
+ var React5 = __toESM(require("react"));
870
+
871
+ // src/theme/theme-utils.ts
872
+ function clamp01(n) {
873
+ return Math.min(1, Math.max(0, n));
874
+ }
875
+ function hexToRgb(hex) {
876
+ const raw = hex.trim().replace(/^#/, "");
877
+ const normalized = raw.length === 3 ? raw.split("").map((c) => c + c).join("") : raw.length === 4 ? raw.slice(0, 3).split("").map((c) => c + c).join("") : raw.length === 6 ? raw : raw.length === 8 ? raw.slice(0, 6) : null;
878
+ if (!normalized) return null;
879
+ const int = Number.parseInt(normalized, 16);
880
+ if (Number.isNaN(int)) return null;
881
+ return {
882
+ r: int >> 16 & 255,
883
+ g: int >> 8 & 255,
884
+ b: int & 255
885
+ };
886
+ }
887
+ function hexToHslChannels(hex) {
888
+ const rgb = hexToRgb(hex);
889
+ if (!rgb) return null;
890
+ const r = rgb.r / 255;
891
+ const g = rgb.g / 255;
1054
892
  const b = rgb.b / 255;
1055
893
  const max = Math.max(r, g, b);
1056
894
  const min = Math.min(r, g, b);
@@ -1075,11 +913,11 @@ function hexToHslChannels(hex) {
1075
913
  if (h < 0) h += 360;
1076
914
  }
1077
915
  const hRound = Math.round(h);
1078
- const sRound = Math.round(clamp012(s) * 100);
1079
- const lRound = Math.round(clamp012(l) * 100);
916
+ const sRound = Math.round(clamp01(s) * 100);
917
+ const lRound = Math.round(clamp01(l) * 100);
1080
918
  return `${hRound} ${sRound}% ${lRound}%`;
1081
919
  }
1082
- var UI_TO_CSS_VAR2 = {
920
+ var UI_TO_CSS_VAR = {
1083
921
  background: "--background",
1084
922
  foreground: "--foreground",
1085
923
  border: "--border",
@@ -1101,10 +939,10 @@ var UI_TO_CSS_VAR2 = {
1101
939
  popoverForeground: "--popover-foreground"
1102
940
  };
1103
941
  function themeJsonToCssVars(theme, mode) {
1104
- var _a, _b;
1105
- const ui = (_b = (_a = theme.uIColors) == null ? void 0 : _a[mode]) != null ? _b : {};
942
+ var _a3, _b3;
943
+ const ui = (_b3 = (_a3 = theme.uIColors) == null ? void 0 : _a3[mode]) != null ? _b3 : {};
1106
944
  const out = {};
1107
- for (const [uiKey, cssVar] of Object.entries(UI_TO_CSS_VAR2)) {
945
+ for (const [uiKey, cssVar] of Object.entries(UI_TO_CSS_VAR)) {
1108
946
  const val = ui[uiKey];
1109
947
  if (!val) continue;
1110
948
  const hsl = val.startsWith("#") ? hexToHslChannels(val) : null;
@@ -1128,9 +966,9 @@ var LegoLandWrapper = ({
1128
966
  }) => {
1129
967
  const mode = theme === "dark" ? "darkMode" : "lightMode";
1130
968
  const cssVars = React5.useMemo(() => {
1131
- var _a, _b;
969
+ var _a3, _b3;
1132
970
  const vars = themeJsonToCssVars(DEFAULT_THEME, mode);
1133
- const palette = (_b = (_a = DEFAULT_THEME.twColors) == null ? void 0 : _a.mode1) != null ? _b : {};
971
+ const palette = (_b3 = (_a3 = DEFAULT_THEME.twColors) == null ? void 0 : _a3.mode1) != null ? _b3 : {};
1134
972
  for (const [k, v] of Object.entries(palette)) {
1135
973
  vars[twColorKeyToCssVarName(k)] = v;
1136
974
  }
@@ -1285,15 +1123,13 @@ ShadowDOMWrapper.displayName = "ShadowDOMWrapper";
1285
1123
  // Annotate the CommonJS export names for ESM import in node:
1286
1124
  0 && (module.exports = {
1287
1125
  Button,
1126
+ DEFAULT_THEME,
1288
1127
  FileInput,
1289
1128
  Input,
1290
- LEGO_TOKENS,
1291
1129
  LegoLandWrapper,
1292
1130
  NurixThemeProvider,
1293
1131
  ShadowDOMWrapper,
1294
1132
  Switch,
1295
1133
  ThemeProvider,
1296
- getLegoLandThemeCSSVars,
1297
- hexToHslVarValue,
1298
1134
  useTheme
1299
1135
  });