@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.mjs CHANGED
@@ -2,107 +2,12 @@
2
2
  import * as React from "react";
3
3
  import { Slot } from "@radix-ui/react-slot";
4
4
 
5
- // src/theme/variables.ts
6
- var LEGO_TOKENS = {
7
- twColors: {
8
- mode1: {
9
- slate50: "hsla(210, 40, 98, 1)",
10
- slate100: "hsla(210, 40, 96, 1)",
11
- slate200: "hsla(214, 32, 91, 1)",
12
- slate300: "hsla(213, 27, 84, 1)",
13
- slate400: "hsla(215, 20, 65, 1)",
14
- slate500: "hsla(215, 14, 47, 1)",
15
- slate600: "hsla(215, 19, 35, 1)",
16
- slate700: "hsla(215, 25, 27, 1)",
17
- slate800: "hsla(217, 33, 17, 1)",
18
- slate900: "hsla(222, 47, 11, 1)",
19
- slate950: "hsla(229, 84, 5, 1)",
20
- white: "hsla(0, 0, 100, 1)",
21
- black: "hsla(0, 0, 0, 1)"
22
- }
23
- },
24
- uIColors: {
25
- lightMode: {
26
- background: "#f5f5f5",
27
- foreground: "#0a0a0a",
28
- border: "#e5e5e5",
29
- destructive: "#dc2626",
30
- secondary: "#e5e5e5",
31
- mutedForeground: "#737373",
32
- muted: "#f5f5f5",
33
- backgroundColor: "#0000004c",
34
- primary: "#1d4885",
35
- secondaryForeground: "#0a0a0a",
36
- accentForeground: "#171717",
37
- accent: "#f5f5f5",
38
- ring: "#0090ff",
39
- chart3: "#0588f0",
40
- chart2: "#0090ff",
41
- chart4: "#0d74ce",
42
- chart1: "#5eb1ef",
43
- chart5: "#113264",
44
- card: "#ffffff",
45
- cardForeground: "#0a0a0a",
46
- popover: "#ffffff",
47
- popoverForeground: "#0a0a0a",
48
- primaryForeground: "#fafafa",
49
- input: "#f5f5f5",
50
- sidebar: "#fafafa",
51
- sidebarForeground: "#0a0a0a",
52
- sidebarPrimary: "#171717",
53
- sidebarPrimaryForeground: "#fafafa",
54
- sidebarAccent: "#f5f5f5",
55
- sidebarAccentForeground: "#171717",
56
- sidebarBorder: "#d4d4d4",
57
- sidebarRing: "#737373",
58
- semanticBackground: "#696867",
59
- semanticBorder: "#898887",
60
- semanticForeground: "#ffffff",
61
- disabled: "#d4d4d4",
62
- primary10: "#1d488519",
63
- success: "#047857"
64
- },
65
- darkMode: {
66
- background: "#1e1e1e",
67
- foreground: "#fafafa",
68
- border: "#404040",
69
- destructive: "#f87171",
70
- secondary: "#262626",
71
- mutedForeground: "#a3a3a3",
72
- muted: "#262626",
73
- backgroundColor: "#0000004c",
74
- primary: "#b5d4fd",
75
- secondaryForeground: "#fafafa",
76
- accentForeground: "#fafafa",
77
- accent: "#262626",
78
- ring: "#acd8fc",
79
- chart3: "#0588f0",
80
- chart2: "#0090ff",
81
- chart4: "#0d74ce",
82
- chart1: "#5eb1ef",
83
- chart5: "#113264",
84
- card: "#171717",
85
- cardForeground: "#fafafa",
86
- popover: "#262626",
87
- popoverForeground: "#fafafa",
88
- primaryForeground: "#171717",
89
- input: "#2e3034",
90
- sidebar: "#171717",
91
- sidebarForeground: "#fafafa",
92
- sidebarPrimary: "#0588f0",
93
- sidebarPrimaryForeground: "#fafafa",
94
- sidebarAccent: "#262626",
95
- sidebarAccentForeground: "#fafafa",
96
- sidebarBorder: "#ffffffcc",
97
- sidebarRing: "#737373",
98
- semanticBackground: "#272625",
99
- semanticBorder: "#535151",
100
- semanticForeground: "#ffffff",
101
- disabled: "#404040",
102
- primary10: "#e6f1ff19",
103
- success: "#a7f3d0"
104
- }
105
- },
5
+ // src/theme/default-theme.ts
6
+ var DEFAULT_THEME = {
7
+ /**
8
+ * Design tokens (numbers) used by components.
9
+ * This file is the single source of truth (do not duplicate these elsewhere).
10
+ */
106
11
  twPadding: {
107
12
  mode1: {
108
13
  px0: 0,
@@ -194,457 +99,7 @@ var LEGO_TOKENS = {
194
99
  "275": 2.75
195
100
  }
196
101
  },
197
- components: {
198
- mode1: {
199
- inputRadius: 16,
200
- inputHeight: 48,
201
- inputHeightLarge: 64,
202
- inputPaddingHorizontal: 16,
203
- inputPaddingVertical: 8,
204
- inputActiveBg: "hsla(0, 0, 96, 1)",
205
- inputFocussedBg: "hsla(0, 0, 98, 1)",
206
- inputFocussedRing: "hsla(200, 64, 39, 1)",
207
- buttonHeight: 40,
208
- buttonRadius: 24
209
- }
210
- }
211
- };
212
- var clamp01 = (n) => Math.min(1, Math.max(0, n));
213
- function hexToHslVarValue(hex) {
214
- const normalized = hex.trim();
215
- if (!normalized.startsWith("#")) {
216
- throw new Error(`hexToHslVarValue expected a hex string, got: ${hex}`);
217
- }
218
- const h = normalized.slice(1);
219
- const isShort = h.length === 3 || h.length === 4;
220
- const isLong = h.length === 6 || h.length === 8;
221
- if (!isShort && !isLong) {
222
- throw new Error(`hexToHslVarValue expected #RGB/#RRGGBB (optionally with alpha), got: ${hex}`);
223
- }
224
- const hexToByte = (s) => parseInt(s, 16);
225
- const r = isShort ? hexToByte(h[0] + h[0]) : hexToByte(h.slice(0, 2));
226
- const g = isShort ? hexToByte(h[1] + h[1]) : hexToByte(h.slice(2, 4));
227
- const b = isShort ? hexToByte(h[2] + h[2]) : hexToByte(h.slice(4, 6));
228
- const rf = r / 255;
229
- const gf = g / 255;
230
- const bf = b / 255;
231
- const max = Math.max(rf, gf, bf);
232
- const min = Math.min(rf, gf, bf);
233
- const delta = max - min;
234
- let hue = 0;
235
- if (delta !== 0) {
236
- if (max === rf) hue = (gf - bf) / delta % 6;
237
- else if (max === gf) hue = (bf - rf) / delta + 2;
238
- else hue = (rf - gf) / delta + 4;
239
- hue *= 60;
240
- if (hue < 0) hue += 360;
241
- }
242
- const light = (max + min) / 2;
243
- const sat = delta === 0 ? 0 : delta / (1 - Math.abs(2 * light - 1));
244
- const H = Math.round(hue);
245
- const S = Math.round(clamp01(sat) * 100);
246
- const L = Math.round(clamp01(light) * 100);
247
- return `${H} ${S}% ${L}%`;
248
- }
249
- var UI_TO_CSS_VAR = {
250
- background: "--background",
251
- foreground: "--foreground",
252
- border: "--border",
253
- input: "--input",
254
- ring: "--ring",
255
- primary: "--primary",
256
- primaryForeground: "--primary-foreground",
257
- secondary: "--secondary",
258
- secondaryForeground: "--secondary-foreground",
259
- destructive: "--destructive",
260
- // Tailwind shadcn-style also supports this var; we set it if present later.
261
- destructiveForeground: "--destructive-foreground",
262
- muted: "--muted",
263
- mutedForeground: "--muted-foreground",
264
- accent: "--accent",
265
- accentForeground: "--accent-foreground",
266
- card: "--card",
267
- cardForeground: "--card-foreground",
268
- popover: "--popover",
269
- popoverForeground: "--popover-foreground"
270
- };
271
- function getLegoLandThemeCSSVars(theme) {
272
- var _a;
273
- const ui = theme === "dark" ? LEGO_TOKENS.uIColors.darkMode : LEGO_TOKENS.uIColors.lightMode;
274
- const out = {
275
- colorScheme: theme,
276
- "--radius": `${LEGO_TOKENS.radius.mode1.roundedMd}px`
277
- };
278
- for (const [uiKey, cssVar] of Object.entries(UI_TO_CSS_VAR)) {
279
- const value = ui[uiKey];
280
- if (typeof value === "string" && value.startsWith("#")) {
281
- out[cssVar] = hexToHslVarValue(value);
282
- }
283
- }
284
- if (!out["--destructive-foreground"]) {
285
- out["--destructive-foreground"] = (_a = out["--primary-foreground"]) != null ? _a : hexToHslVarValue("#ffffff");
286
- }
287
- return out;
288
- }
289
-
290
- // src/button/variables.ts
291
- var BUTTON_TOKENS = {
292
- 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",
293
- variant: {
294
- primary: "bg-primary text-primary-foreground hover:bg-primary/90",
295
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
296
- destructive: "bg-destructive text-primary-foreground hover:bg-destructive/90",
297
- outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground text-foreground",
298
- link: "text-foreground underline-offset-4 hover:underline"
299
- },
300
- size: {
301
- default: "h-10 px-4 py-2",
302
- sm: "h-9 px-3",
303
- lg: "h-10 px-8",
304
- icon: "h-10 w-10"
305
- },
306
- radius: {
307
- none: `${LEGO_TOKENS.radius.mode1.roundedNone}px`,
308
- soft: `${LEGO_TOKENS.radius.mode1.roundedMd}px`,
309
- rounded: `${LEGO_TOKENS.components.mode1.buttonRadius}px`,
310
- pill: `${LEGO_TOKENS.radius.mode1.roundedFull}px`
311
- }
312
- };
313
-
314
- // src/lib/utils.ts
315
- import { clsx } from "clsx";
316
- import { twMerge } from "tailwind-merge";
317
- function cn(...inputs) {
318
- return twMerge(clsx(inputs));
319
- }
320
-
321
- // src/button/button.tsx
322
- import { jsx } from "react/jsx-runtime";
323
- var Button = React.forwardRef(
324
- ({
325
- className,
326
- variant = "primary",
327
- size,
328
- asChild = false,
329
- button_border_radius = "rounded",
330
- style,
331
- ...props
332
- }, ref) => {
333
- const Comp = asChild ? Slot : "button";
334
- return /* @__PURE__ */ jsx(
335
- Comp,
336
- {
337
- ref,
338
- className: cn(
339
- BUTTON_TOKENS.base,
340
- BUTTON_TOKENS.variant[variant],
341
- BUTTON_TOKENS.size[size != null ? size : "default"],
342
- className
343
- ),
344
- style: {
345
- borderRadius: BUTTON_TOKENS.radius[button_border_radius],
346
- ...style
347
- },
348
- ...props
349
- }
350
- );
351
- }
352
- );
353
- Button.displayName = "Button";
354
-
355
- // src/input/input.tsx
356
- import * as React2 from "react";
357
-
358
- // src/input/variables.ts
359
- var INPUT_TOKENS = {
360
- root: "flex w-full flex-col gap-1",
361
- labelRow: "flex items-center gap-1 text-xs font-medium leading-4 tracking-normal text-foreground",
362
- mandatory: "text-destructive",
363
- field: "flex h-12 w-full items-center justify-between overflow-hidden bg-input px-4 py-2 text-sm shadow-none",
364
- fieldBase: "border border-transparent focus-within:border-ring disabled:pointer-events-none",
365
- fieldDisabled: "opacity-50",
366
- input: "w-full bg-transparent outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-100",
367
- supportingTextBase: "text-xs font-normal leading-4 tracking-normal text-muted-foreground",
368
- supportingTextError: "text-destructive",
369
- radius: {
370
- none: `${LEGO_TOKENS.radius.mode1.roundedNone}px`,
371
- soft: `${LEGO_TOKENS.radius.mode1.roundedMd}px`,
372
- rounded: `${LEGO_TOKENS.components.mode1.inputRadius}px`,
373
- pill: `${LEGO_TOKENS.radius.mode1.roundedFull}px`
374
- }
375
- };
376
-
377
- // src/input/input.tsx
378
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
379
- var Input = React2.forwardRef(
380
- ({
381
- className,
382
- label,
383
- mandatory = false,
384
- supportingText,
385
- supportingTextType = "help",
386
- hideSupportingText = false,
387
- input_border_radius = "rounded",
388
- forceState,
389
- disabled,
390
- style,
391
- ...props
392
- }, ref) => {
393
- const isDisabled = forceState === "disabled" || disabled;
394
- const isFocussed = forceState === "focussed";
395
- return /* @__PURE__ */ jsxs(
396
- "div",
397
- {
398
- className: cn(INPUT_TOKENS.root, isDisabled && INPUT_TOKENS.fieldDisabled, className),
399
- "data-state": forceState,
400
- children: [
401
- label ? /* @__PURE__ */ jsxs("div", { className: INPUT_TOKENS.labelRow, children: [
402
- /* @__PURE__ */ jsx2("span", { children: label }),
403
- mandatory ? /* @__PURE__ */ jsx2("span", { className: INPUT_TOKENS.mandatory, children: "*" }) : null
404
- ] }) : null,
405
- /* @__PURE__ */ jsx2(
406
- "div",
407
- {
408
- className: cn(
409
- INPUT_TOKENS.field,
410
- INPUT_TOKENS.fieldBase,
411
- isFocussed && "border-ring"
412
- ),
413
- style: {
414
- borderRadius: INPUT_TOKENS.radius[input_border_radius],
415
- ...style
416
- },
417
- children: /* @__PURE__ */ jsx2(
418
- "input",
419
- {
420
- ref,
421
- className: cn(INPUT_TOKENS.input),
422
- disabled: isDisabled,
423
- ...props
424
- }
425
- )
426
- }
427
- ),
428
- !hideSupportingText && supportingText ? /* @__PURE__ */ jsx2(
429
- "p",
430
- {
431
- className: cn(
432
- INPUT_TOKENS.supportingTextBase,
433
- supportingTextType === "error" && INPUT_TOKENS.supportingTextError
434
- ),
435
- children: supportingText
436
- }
437
- ) : null
438
- ]
439
- }
440
- );
441
- }
442
- );
443
- Input.displayName = "Input";
444
-
445
- // src/input/file-input.tsx
446
- import * as React3 from "react";
447
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
448
- var FileInput = React3.forwardRef(
449
- ({
450
- className,
451
- label,
452
- mandatory = false,
453
- buttonLabel = "Choose File",
454
- placeholder = "No file chosen",
455
- fileName,
456
- input_border_radius = "rounded",
457
- disabled,
458
- onFileChange,
459
- style,
460
- ...props
461
- }, ref) => {
462
- const internalRef = React3.useRef(null);
463
- React3.useImperativeHandle(ref, () => internalRef.current, []);
464
- const [internalName, setInternalName] = React3.useState("");
465
- const effectiveName = fileName != null ? fileName : internalName;
466
- const handleChoose = () => {
467
- var _a;
468
- if (disabled) return;
469
- (_a = internalRef.current) == null ? void 0 : _a.click();
470
- };
471
- const handleChange = (e) => {
472
- var _a, _b, _c;
473
- const file = (_b = (_a = e.target.files) == null ? void 0 : _a[0]) != null ? _b : null;
474
- if (!fileName) {
475
- setInternalName((_c = file == null ? void 0 : file.name) != null ? _c : "");
476
- }
477
- onFileChange == null ? void 0 : onFileChange(file);
478
- };
479
- return /* @__PURE__ */ jsxs2("div", { className: cn(INPUT_TOKENS.root, className), children: [
480
- label ? /* @__PURE__ */ jsxs2("div", { className: INPUT_TOKENS.labelRow, children: [
481
- /* @__PURE__ */ jsx3("span", { children: label }),
482
- mandatory ? /* @__PURE__ */ jsx3("span", { className: INPUT_TOKENS.mandatory, children: "*" }) : null
483
- ] }) : null,
484
- /* @__PURE__ */ jsxs2(
485
- "div",
486
- {
487
- className: cn(
488
- INPUT_TOKENS.field,
489
- INPUT_TOKENS.fieldBase,
490
- "shadow-[0px_1px_2px_rgba(0,0,0,0.1)]",
491
- disabled && INPUT_TOKENS.fieldDisabled
492
- ),
493
- style: {
494
- borderRadius: INPUT_TOKENS.radius[input_border_radius],
495
- ...style
496
- },
497
- children: [
498
- /* @__PURE__ */ jsx3(
499
- "button",
500
- {
501
- type: "button",
502
- onClick: handleChoose,
503
- disabled,
504
- className: cn(
505
- "mr-1 shrink-0 rounded-sm px-1.5 py-px text-sm font-medium leading-5 text-foreground",
506
- disabled && "cursor-not-allowed"
507
- ),
508
- children: buttonLabel
509
- }
510
- ),
511
- /* @__PURE__ */ jsx3("span", { className: "truncate text-sm font-normal leading-5 text-muted-foreground", children: effectiveName || placeholder }),
512
- /* @__PURE__ */ jsx3(
513
- "input",
514
- {
515
- ref: internalRef,
516
- type: "file",
517
- tabIndex: -1,
518
- className: "sr-only",
519
- disabled,
520
- onChange: handleChange,
521
- ...props
522
- }
523
- )
524
- ]
525
- }
526
- )
527
- ] });
528
- }
529
- );
530
- FileInput.displayName = "FileInput";
531
-
532
- // src/switch/switch.tsx
533
- import * as React4 from "react";
534
-
535
- // src/switch/variables.ts
536
- var SWITCH_TOKENS = {
537
- 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",
538
- rootDisabled: "opacity-50 cursor-not-allowed",
539
- // Track colors (matches Figma: Off = neutral/200, On = primary)
540
- trackOff: "bg-[var(--neutral-200)]",
541
- trackOn: "bg-primary",
542
- // Thumb colors (matches Figma: Off = card, On = primary-foreground)
543
- thumbOff: "bg-card",
544
- thumbOn: "bg-primary-foreground",
545
- thumbBase: "absolute top-[2px] left-[2px] rounded-full transition-transform shadow-sm",
546
- size: {
547
- // Figma default: track 44x24, thumb 20
548
- default: {
549
- track: "h-6 w-11",
550
- thumb: "h-5 w-5",
551
- translateOn: "translate-x-5",
552
- translateOff: "translate-x-0"
553
- },
554
- // Figma small: track 22x12, thumb appears smaller
555
- small: {
556
- track: "h-3 w-[22px]",
557
- thumb: "h-2 w-2",
558
- translateOn: "translate-x-[10px]",
559
- translateOff: "translate-x-0"
560
- // thumb top/left needs to be tighter for small; we'll override via inline classes
561
- }
562
- }
563
- };
564
-
565
- // src/switch/switch.tsx
566
- import { jsx as jsx4 } from "react/jsx-runtime";
567
- var Switch = React4.forwardRef(
568
- ({
569
- checked,
570
- defaultChecked,
571
- onCheckedChange,
572
- disabled,
573
- size = "default",
574
- className,
575
- style,
576
- id
577
- }, ref) => {
578
- const isControlled = checked !== void 0;
579
- const [uncontrolled, setUncontrolled] = React4.useState(
580
- defaultChecked != null ? defaultChecked : false
581
- );
582
- const isOn = isControlled ? !!checked : uncontrolled;
583
- const sz = SWITCH_TOKENS.size[size];
584
- const toggle = () => {
585
- if (disabled) return;
586
- const next = !isOn;
587
- if (!isControlled) setUncontrolled(next);
588
- onCheckedChange == null ? void 0 : onCheckedChange(next);
589
- };
590
- const onKeyDown = (e) => {
591
- if (disabled) return;
592
- if (e.key === "Enter" || e.key === " ") {
593
- e.preventDefault();
594
- toggle();
595
- }
596
- };
597
- const smallNudge = size === "small" ? "top-[1px] left-[1px]" : "";
598
- return /* @__PURE__ */ jsx4(
599
- "button",
600
- {
601
- id,
602
- ref,
603
- type: "button",
604
- role: "switch",
605
- "aria-checked": isOn,
606
- "aria-disabled": disabled || void 0,
607
- disabled,
608
- onClick: toggle,
609
- onKeyDown,
610
- className: cn(
611
- SWITCH_TOKENS.rootBase,
612
- sz.track,
613
- isOn ? SWITCH_TOKENS.trackOn : SWITCH_TOKENS.trackOff,
614
- disabled && SWITCH_TOKENS.rootDisabled,
615
- className
616
- ),
617
- style,
618
- "data-state": isOn ? "checked" : "unchecked",
619
- "data-size": size,
620
- children: /* @__PURE__ */ jsx4(
621
- "span",
622
- {
623
- "aria-hidden": "true",
624
- className: cn(
625
- SWITCH_TOKENS.thumbBase,
626
- smallNudge,
627
- sz.thumb,
628
- isOn ? SWITCH_TOKENS.thumbOn : SWITCH_TOKENS.thumbOff,
629
- isOn ? sz.translateOn : sz.translateOff
630
- )
631
- }
632
- )
633
- }
634
- );
635
- }
636
- );
637
- Switch.displayName = "Switch";
638
-
639
- // src/theme/theme-context.tsx
640
- import * as React6 from "react";
641
-
642
- // src/theme/lego-land-wrapper.tsx
643
- import * as React5 from "react";
644
-
645
- // src/theme/default-theme.ts
646
- var DEFAULT_THEME = {
647
- twColors: {
102
+ twColors: {
648
103
  mode1: {
649
104
  slate50: "#f8fafc",
650
105
  slate100: "#f1f5f9",
@@ -901,7 +356,7 @@ var DEFAULT_THEME = {
901
356
  secondary: "#e5e5e5",
902
357
  mutedForeground: "#737373",
903
358
  muted: "#f5f5f5",
904
- backgroundColor: "#0000004c",
359
+ backgroundColor: "#0000004d",
905
360
  primary: "#1d4885",
906
361
  secondaryForeground: "#0a0a0a",
907
362
  accentForeground: "#171717",
@@ -941,8 +396,8 @@ var DEFAULT_THEME = {
941
396
  secondary: "#262626",
942
397
  mutedForeground: "#a3a3a3",
943
398
  muted: "#262626",
944
- backgroundColor: "#0000004c",
945
- primary: "#b5d4fd",
399
+ backgroundColor: "#0000004d",
400
+ primary: "#b5d5fd",
946
401
  secondaryForeground: "#fafafa",
947
402
  accentForeground: "#fafafa",
948
403
  accent: "#262626",
@@ -970,48 +425,433 @@ var DEFAULT_THEME = {
970
425
  semanticBorder: "#535151",
971
426
  semanticForeground: "#ffffff",
972
427
  disabled: "#404040",
973
- primary10: "#e6f1ff19",
428
+ primary10: "#e6f1ff1a",
974
429
  success: "#a7f3d0"
975
430
  }
976
431
  },
977
432
  components: {
978
433
  mode1: {
979
- inputActiveBg: "#f5f5f5",
980
- inputFocussedBg: "#fafafa",
981
- inputFocussedRing: "#0284c7"
434
+ // Component token defaults (numbers + colors)
435
+ inputRadius: 16,
436
+ inputHeight: 48,
437
+ inputHeightLarge: 64,
438
+ inputPaddingHorizontal: 16,
439
+ inputPaddingVertical: 8,
440
+ buttonHeight: 40,
441
+ buttonRadius: 24,
442
+ // These are semantic per-mode in `uIColors`, but kept here for convenience.
443
+ // (Figma "Components" page uses dark mode values.)
444
+ inputActiveBg: "#2e3034",
445
+ inputFocussedBg: "#2e3034",
446
+ inputFocussedRing: "#acd8fc"
982
447
  }
983
448
  }
984
449
  };
985
450
 
986
- // src/theme/theme-utils.ts
987
- function clamp012(n) {
988
- return Math.min(1, Math.max(0, n));
989
- }
990
- function hexToRgb(hex) {
991
- const raw = hex.trim().replace(/^#/, "");
992
- 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;
993
- if (!normalized) return null;
994
- const int = Number.parseInt(normalized, 16);
995
- if (Number.isNaN(int)) return null;
996
- return {
997
- r: int >> 16 & 255,
998
- g: int >> 8 & 255,
999
- b: int & 255
1000
- };
451
+ // src/button/variables.ts
452
+ var _a, _b;
453
+ var BUTTON_TOKENS = {
454
+ 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",
455
+ 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",
456
+ variant: {
457
+ primary: "bg-primary text-primary-foreground disabled:opacity-50",
458
+ secondary: "bg-secondary text-secondary-foreground disabled:bg-muted-foreground disabled:text-background disabled:opacity-100",
459
+ destructive: "bg-destructive text-destructive-foreground disabled:opacity-50",
460
+ outline: "border border-border bg-transparent text-foreground hover:bg-foreground/5 disabled:border-[#b2b2b2] disabled:text-[#b2b2b2] disabled:opacity-100",
461
+ link: "bg-transparent text-foreground hover:text-primary disabled:text-muted-foreground disabled:opacity-100",
462
+ // Icon-only variants (pair with size `icon` / `iconSm`)
463
+ iconPrimary: "bg-primary text-primary-foreground disabled:opacity-50",
464
+ iconSecondary: "bg-secondary text-secondary-foreground disabled:bg-muted-foreground disabled:text-secondary disabled:opacity-100",
465
+ iconOutline: "border border-border bg-transparent text-foreground hover:bg-foreground/5 disabled:border-[#b2b2b2] disabled:text-[#b2b2b2] disabled:opacity-100"
466
+ },
467
+ size: {
468
+ default: "h-10 px-4 py-2",
469
+ sm: "h-9 px-3",
470
+ lg: "h-10 px-8",
471
+ icon: "h-10 w-10",
472
+ iconSm: "h-9 w-9"
473
+ },
474
+ radius: {
475
+ none: `${DEFAULT_THEME.radius.mode1.roundedNone}px`,
476
+ soft: `${DEFAULT_THEME.radius.mode1.roundedMd}px`,
477
+ rounded: `${(_b = (_a = DEFAULT_THEME.components) == null ? void 0 : _a.mode1.buttonRadius) != null ? _b : 24}px`,
478
+ pill: `${DEFAULT_THEME.radius.mode1.roundedFull}px`
479
+ }
480
+ };
481
+
482
+ // src/lib/utils.ts
483
+ import { clsx } from "clsx";
484
+ import { twMerge } from "tailwind-merge";
485
+ function cn(...inputs) {
486
+ return twMerge(clsx(inputs));
1001
487
  }
1002
- function hexToHslChannels(hex) {
1003
- const rgb = hexToRgb(hex);
1004
- if (!rgb) return null;
1005
- const r = rgb.r / 255;
1006
- const g = rgb.g / 255;
1007
- const b = rgb.b / 255;
1008
- const max = Math.max(r, g, b);
1009
- const min = Math.min(r, g, b);
1010
- const delta = max - min;
1011
- let h = 0;
1012
- let s = 0;
1013
- const l = (max + min) / 2;
1014
- if (delta !== 0) {
488
+
489
+ // src/button/button.tsx
490
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
491
+ var Button = React.forwardRef(
492
+ ({
493
+ className,
494
+ variant = "primary",
495
+ size,
496
+ asChild = false,
497
+ button_border_radius = "rounded",
498
+ leadingIcon,
499
+ trailingIcon,
500
+ showLeadingIcon = true,
501
+ showTrailingIcon = true,
502
+ style,
503
+ children,
504
+ ...props
505
+ }, ref) => {
506
+ const Comp = asChild ? Slot : "button";
507
+ const withHoverOverlay = variant === "primary" || variant === "secondary" || variant === "destructive" || variant === "iconPrimary" || variant === "iconSecondary";
508
+ return /* @__PURE__ */ jsx(
509
+ Comp,
510
+ {
511
+ ref,
512
+ className: cn(
513
+ BUTTON_TOKENS.base,
514
+ BUTTON_TOKENS.variant[variant],
515
+ withHoverOverlay && BUTTON_TOKENS.hoverOverlay,
516
+ BUTTON_TOKENS.size[size != null ? size : "default"],
517
+ className
518
+ ),
519
+ style: {
520
+ borderRadius: BUTTON_TOKENS.radius[button_border_radius],
521
+ ...style
522
+ },
523
+ ...props,
524
+ children: asChild ? children : /* @__PURE__ */ jsxs(Fragment, { children: [
525
+ showLeadingIcon ? leadingIcon : null,
526
+ children,
527
+ showTrailingIcon ? trailingIcon : null
528
+ ] })
529
+ }
530
+ );
531
+ }
532
+ );
533
+ Button.displayName = "Button";
534
+
535
+ // src/input/input.tsx
536
+ import * as React2 from "react";
537
+
538
+ // src/input/variables.ts
539
+ var _a2, _b2;
540
+ var INPUT_TOKENS = {
541
+ root: "flex w-full flex-col gap-1",
542
+ labelRow: "flex items-center gap-1 text-xs font-medium leading-4 tracking-normal text-foreground",
543
+ mandatory: "text-destructive",
544
+ field: "flex h-12 w-full items-center justify-between overflow-hidden bg-input px-4 py-2 text-sm shadow-none",
545
+ fieldBase: "border border-transparent focus-within:border-ring disabled:pointer-events-none",
546
+ fieldDisabled: "opacity-50",
547
+ input: "w-full bg-transparent outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-100",
548
+ supportingTextBase: "text-xs font-normal leading-4 tracking-normal text-muted-foreground",
549
+ supportingTextError: "text-destructive",
550
+ radius: {
551
+ none: `${DEFAULT_THEME.radius.mode1.roundedNone}px`,
552
+ soft: `${DEFAULT_THEME.radius.mode1.roundedMd}px`,
553
+ rounded: `${(_b2 = (_a2 = DEFAULT_THEME.components) == null ? void 0 : _a2.mode1.inputRadius) != null ? _b2 : 16}px`,
554
+ pill: `${DEFAULT_THEME.radius.mode1.roundedFull}px`
555
+ }
556
+ };
557
+
558
+ // src/input/input.tsx
559
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
560
+ var Input = React2.forwardRef(
561
+ ({
562
+ className,
563
+ label,
564
+ mandatory = false,
565
+ supportingText,
566
+ supportingTextType = "help",
567
+ hideSupportingText = false,
568
+ input_border_radius = "rounded",
569
+ forceState,
570
+ disabled,
571
+ style,
572
+ ...props
573
+ }, ref) => {
574
+ const isDisabled = forceState === "disabled" || disabled;
575
+ const isFocussed = forceState === "focussed";
576
+ return /* @__PURE__ */ jsxs2(
577
+ "div",
578
+ {
579
+ className: cn(INPUT_TOKENS.root, isDisabled && INPUT_TOKENS.fieldDisabled, className),
580
+ "data-state": forceState,
581
+ children: [
582
+ label ? /* @__PURE__ */ jsxs2("div", { className: INPUT_TOKENS.labelRow, children: [
583
+ /* @__PURE__ */ jsx2("span", { children: label }),
584
+ mandatory ? /* @__PURE__ */ jsx2("span", { className: INPUT_TOKENS.mandatory, children: "*" }) : null
585
+ ] }) : null,
586
+ /* @__PURE__ */ jsx2(
587
+ "div",
588
+ {
589
+ className: cn(
590
+ INPUT_TOKENS.field,
591
+ INPUT_TOKENS.fieldBase,
592
+ isFocussed && "border-ring"
593
+ ),
594
+ style: {
595
+ borderRadius: INPUT_TOKENS.radius[input_border_radius],
596
+ ...style
597
+ },
598
+ children: /* @__PURE__ */ jsx2(
599
+ "input",
600
+ {
601
+ ref,
602
+ className: cn(INPUT_TOKENS.input),
603
+ disabled: isDisabled,
604
+ ...props
605
+ }
606
+ )
607
+ }
608
+ ),
609
+ !hideSupportingText && supportingText ? /* @__PURE__ */ jsx2(
610
+ "p",
611
+ {
612
+ className: cn(
613
+ INPUT_TOKENS.supportingTextBase,
614
+ supportingTextType === "error" && INPUT_TOKENS.supportingTextError
615
+ ),
616
+ children: supportingText
617
+ }
618
+ ) : null
619
+ ]
620
+ }
621
+ );
622
+ }
623
+ );
624
+ Input.displayName = "Input";
625
+
626
+ // src/input/file-input.tsx
627
+ import * as React3 from "react";
628
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
629
+ var FileInput = React3.forwardRef(
630
+ ({
631
+ className,
632
+ label,
633
+ mandatory = false,
634
+ buttonLabel = "Choose File",
635
+ placeholder = "No file chosen",
636
+ fileName,
637
+ input_border_radius = "rounded",
638
+ disabled,
639
+ onFileChange,
640
+ style,
641
+ ...props
642
+ }, ref) => {
643
+ const internalRef = React3.useRef(null);
644
+ React3.useImperativeHandle(ref, () => internalRef.current, []);
645
+ const [internalName, setInternalName] = React3.useState("");
646
+ const effectiveName = fileName != null ? fileName : internalName;
647
+ const handleChoose = () => {
648
+ var _a3;
649
+ if (disabled) return;
650
+ (_a3 = internalRef.current) == null ? void 0 : _a3.click();
651
+ };
652
+ const handleChange = (e) => {
653
+ var _a3, _b3, _c;
654
+ const file = (_b3 = (_a3 = e.target.files) == null ? void 0 : _a3[0]) != null ? _b3 : null;
655
+ if (!fileName) {
656
+ setInternalName((_c = file == null ? void 0 : file.name) != null ? _c : "");
657
+ }
658
+ onFileChange == null ? void 0 : onFileChange(file);
659
+ };
660
+ return /* @__PURE__ */ jsxs3("div", { className: cn(INPUT_TOKENS.root, className), children: [
661
+ label ? /* @__PURE__ */ jsxs3("div", { className: INPUT_TOKENS.labelRow, children: [
662
+ /* @__PURE__ */ jsx3("span", { children: label }),
663
+ mandatory ? /* @__PURE__ */ jsx3("span", { className: INPUT_TOKENS.mandatory, children: "*" }) : null
664
+ ] }) : null,
665
+ /* @__PURE__ */ jsxs3(
666
+ "div",
667
+ {
668
+ className: cn(
669
+ INPUT_TOKENS.field,
670
+ INPUT_TOKENS.fieldBase,
671
+ "shadow-[0px_1px_2px_rgba(0,0,0,0.1)]",
672
+ disabled && INPUT_TOKENS.fieldDisabled
673
+ ),
674
+ style: {
675
+ borderRadius: INPUT_TOKENS.radius[input_border_radius],
676
+ ...style
677
+ },
678
+ children: [
679
+ /* @__PURE__ */ jsx3(
680
+ "button",
681
+ {
682
+ type: "button",
683
+ onClick: handleChoose,
684
+ disabled,
685
+ className: cn(
686
+ "mr-1 shrink-0 rounded-sm px-1.5 py-px text-sm font-medium leading-5 text-foreground",
687
+ disabled && "cursor-not-allowed"
688
+ ),
689
+ children: buttonLabel
690
+ }
691
+ ),
692
+ /* @__PURE__ */ jsx3("span", { className: "truncate text-sm font-normal leading-5 text-muted-foreground", children: effectiveName || placeholder }),
693
+ /* @__PURE__ */ jsx3(
694
+ "input",
695
+ {
696
+ ref: internalRef,
697
+ type: "file",
698
+ tabIndex: -1,
699
+ className: "sr-only",
700
+ disabled,
701
+ onChange: handleChange,
702
+ ...props
703
+ }
704
+ )
705
+ ]
706
+ }
707
+ )
708
+ ] });
709
+ }
710
+ );
711
+ FileInput.displayName = "FileInput";
712
+
713
+ // src/switch/switch.tsx
714
+ import * as React4 from "react";
715
+
716
+ // src/switch/variables.ts
717
+ var SWITCH_TOKENS = {
718
+ 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",
719
+ rootDisabled: "opacity-50 cursor-not-allowed",
720
+ // Track colors (matches Figma: Off = neutral/200, On = primary)
721
+ trackOff: "bg-[var(--neutral-200)]",
722
+ trackOn: "bg-primary",
723
+ // Thumb colors (matches Figma: Off = card, On = primary-foreground)
724
+ thumbOff: "bg-card",
725
+ thumbOn: "bg-primary-foreground",
726
+ thumbBase: "absolute top-[2px] left-[2px] rounded-full transition-transform shadow-sm",
727
+ size: {
728
+ // Figma default: track 44x24, thumb 20
729
+ default: {
730
+ track: "h-6 w-11",
731
+ thumb: "h-5 w-5",
732
+ translateOn: "translate-x-5",
733
+ translateOff: "translate-x-0"
734
+ },
735
+ // Figma small: track 22x12, thumb appears smaller
736
+ small: {
737
+ track: "h-3 w-[22px]",
738
+ thumb: "h-2 w-2",
739
+ translateOn: "translate-x-[10px]",
740
+ translateOff: "translate-x-0"
741
+ // thumb top/left needs to be tighter for small; we'll override via inline classes
742
+ }
743
+ }
744
+ };
745
+
746
+ // src/switch/switch.tsx
747
+ import { jsx as jsx4 } from "react/jsx-runtime";
748
+ var Switch = React4.forwardRef(
749
+ ({
750
+ checked,
751
+ defaultChecked,
752
+ onCheckedChange,
753
+ disabled,
754
+ size = "default",
755
+ className,
756
+ style,
757
+ id
758
+ }, ref) => {
759
+ const isControlled = checked !== void 0;
760
+ const [uncontrolled, setUncontrolled] = React4.useState(
761
+ defaultChecked != null ? defaultChecked : false
762
+ );
763
+ const isOn = isControlled ? !!checked : uncontrolled;
764
+ const sz = SWITCH_TOKENS.size[size];
765
+ const toggle = () => {
766
+ if (disabled) return;
767
+ const next = !isOn;
768
+ if (!isControlled) setUncontrolled(next);
769
+ onCheckedChange == null ? void 0 : onCheckedChange(next);
770
+ };
771
+ const onKeyDown = (e) => {
772
+ if (disabled) return;
773
+ if (e.key === "Enter" || e.key === " ") {
774
+ e.preventDefault();
775
+ toggle();
776
+ }
777
+ };
778
+ const smallNudge = size === "small" ? "top-[1px] left-[1px]" : "";
779
+ return /* @__PURE__ */ jsx4(
780
+ "button",
781
+ {
782
+ id,
783
+ ref,
784
+ type: "button",
785
+ role: "switch",
786
+ "aria-checked": isOn,
787
+ "aria-disabled": disabled || void 0,
788
+ disabled,
789
+ onClick: toggle,
790
+ onKeyDown,
791
+ className: cn(
792
+ SWITCH_TOKENS.rootBase,
793
+ sz.track,
794
+ isOn ? SWITCH_TOKENS.trackOn : SWITCH_TOKENS.trackOff,
795
+ disabled && SWITCH_TOKENS.rootDisabled,
796
+ className
797
+ ),
798
+ style,
799
+ "data-state": isOn ? "checked" : "unchecked",
800
+ "data-size": size,
801
+ children: /* @__PURE__ */ jsx4(
802
+ "span",
803
+ {
804
+ "aria-hidden": "true",
805
+ className: cn(
806
+ SWITCH_TOKENS.thumbBase,
807
+ smallNudge,
808
+ sz.thumb,
809
+ isOn ? SWITCH_TOKENS.thumbOn : SWITCH_TOKENS.thumbOff,
810
+ isOn ? sz.translateOn : sz.translateOff
811
+ )
812
+ }
813
+ )
814
+ }
815
+ );
816
+ }
817
+ );
818
+ Switch.displayName = "Switch";
819
+
820
+ // src/theme/theme-context.tsx
821
+ import * as React6 from "react";
822
+
823
+ // src/theme/lego-land-wrapper.tsx
824
+ import * as React5 from "react";
825
+
826
+ // src/theme/theme-utils.ts
827
+ function clamp01(n) {
828
+ return Math.min(1, Math.max(0, n));
829
+ }
830
+ function hexToRgb(hex) {
831
+ const raw = hex.trim().replace(/^#/, "");
832
+ 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;
833
+ if (!normalized) return null;
834
+ const int = Number.parseInt(normalized, 16);
835
+ if (Number.isNaN(int)) return null;
836
+ return {
837
+ r: int >> 16 & 255,
838
+ g: int >> 8 & 255,
839
+ b: int & 255
840
+ };
841
+ }
842
+ function hexToHslChannels(hex) {
843
+ const rgb = hexToRgb(hex);
844
+ if (!rgb) return null;
845
+ const r = rgb.r / 255;
846
+ const g = rgb.g / 255;
847
+ const b = rgb.b / 255;
848
+ const max = Math.max(r, g, b);
849
+ const min = Math.min(r, g, b);
850
+ const delta = max - min;
851
+ let h = 0;
852
+ let s = 0;
853
+ const l = (max + min) / 2;
854
+ if (delta !== 0) {
1015
855
  s = delta / (1 - Math.abs(2 * l - 1));
1016
856
  switch (max) {
1017
857
  case r:
@@ -1028,11 +868,11 @@ function hexToHslChannels(hex) {
1028
868
  if (h < 0) h += 360;
1029
869
  }
1030
870
  const hRound = Math.round(h);
1031
- const sRound = Math.round(clamp012(s) * 100);
1032
- const lRound = Math.round(clamp012(l) * 100);
871
+ const sRound = Math.round(clamp01(s) * 100);
872
+ const lRound = Math.round(clamp01(l) * 100);
1033
873
  return `${hRound} ${sRound}% ${lRound}%`;
1034
874
  }
1035
- var UI_TO_CSS_VAR2 = {
875
+ var UI_TO_CSS_VAR = {
1036
876
  background: "--background",
1037
877
  foreground: "--foreground",
1038
878
  border: "--border",
@@ -1054,10 +894,10 @@ var UI_TO_CSS_VAR2 = {
1054
894
  popoverForeground: "--popover-foreground"
1055
895
  };
1056
896
  function themeJsonToCssVars(theme, mode) {
1057
- var _a, _b;
1058
- const ui = (_b = (_a = theme.uIColors) == null ? void 0 : _a[mode]) != null ? _b : {};
897
+ var _a3, _b3;
898
+ const ui = (_b3 = (_a3 = theme.uIColors) == null ? void 0 : _a3[mode]) != null ? _b3 : {};
1059
899
  const out = {};
1060
- for (const [uiKey, cssVar] of Object.entries(UI_TO_CSS_VAR2)) {
900
+ for (const [uiKey, cssVar] of Object.entries(UI_TO_CSS_VAR)) {
1061
901
  const val = ui[uiKey];
1062
902
  if (!val) continue;
1063
903
  const hsl = val.startsWith("#") ? hexToHslChannels(val) : null;
@@ -1081,9 +921,9 @@ var LegoLandWrapper = ({
1081
921
  }) => {
1082
922
  const mode = theme === "dark" ? "darkMode" : "lightMode";
1083
923
  const cssVars = React5.useMemo(() => {
1084
- var _a, _b;
924
+ var _a3, _b3;
1085
925
  const vars = themeJsonToCssVars(DEFAULT_THEME, mode);
1086
- const palette = (_b = (_a = DEFAULT_THEME.twColors) == null ? void 0 : _a.mode1) != null ? _b : {};
926
+ const palette = (_b3 = (_a3 = DEFAULT_THEME.twColors) == null ? void 0 : _a3.mode1) != null ? _b3 : {};
1087
927
  for (const [k, v] of Object.entries(palette)) {
1088
928
  vars[twColorKeyToCssVarName(k)] = v;
1089
929
  }
@@ -1129,7 +969,7 @@ var useTheme = () => {
1129
969
  // src/shadow-dom-wrapper.tsx
1130
970
  import * as React7 from "react";
1131
971
  import { createPortal } from "react-dom";
1132
- import { Fragment, jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
972
+ import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
1133
973
  var ShadowDOMWrapper = ({
1134
974
  children,
1135
975
  styles,
@@ -1229,7 +1069,7 @@ var ShadowDOMWrapper = ({
1229
1069
  return () => {
1230
1070
  };
1231
1071
  }, [styles, mode, additionalStyles, copyCSSVariables]);
1232
- return /* @__PURE__ */ jsxs3(Fragment, { children: [
1072
+ return /* @__PURE__ */ jsxs4(Fragment2, { children: [
1233
1073
  /* @__PURE__ */ jsx7("div", { ref: containerRef }),
1234
1074
  shadowRoot && createPortal(children, shadowRoot)
1235
1075
  ] });
@@ -1237,15 +1077,13 @@ var ShadowDOMWrapper = ({
1237
1077
  ShadowDOMWrapper.displayName = "ShadowDOMWrapper";
1238
1078
  export {
1239
1079
  Button,
1080
+ DEFAULT_THEME,
1240
1081
  FileInput,
1241
1082
  Input,
1242
- LEGO_TOKENS,
1243
1083
  LegoLandWrapper,
1244
1084
  NurixThemeProvider,
1245
1085
  ShadowDOMWrapper,
1246
1086
  Switch,
1247
1087
  ThemeProvider,
1248
- getLegoLandThemeCSSVars,
1249
- hexToHslVarValue,
1250
1088
  useTheme
1251
1089
  };