@devalok/shilp-sutra 0.51.0 → 0.52.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.
Files changed (47) hide show
  1. package/AGENTS.md +1 -1
  2. package/MIGRATION.md +6 -0
  3. package/dist/tokens/semantic.css +20 -1
  4. package/dist/ui/button-group.js +2 -0
  5. package/dist/ui/button-group.js.map +1 -1
  6. package/dist/ui/button-processing.d.ts.map +1 -1
  7. package/dist/ui/button-processing.js +1 -0
  8. package/dist/ui/button-processing.js.map +1 -1
  9. package/dist/ui/button.d.ts +3 -3
  10. package/dist/ui/button.d.ts.map +1 -1
  11. package/dist/ui/button.js +26 -0
  12. package/dist/ui/button.js.map +1 -1
  13. package/dist/ui/color-input.d.ts.map +1 -1
  14. package/dist/ui/color-input.js +82 -82
  15. package/dist/ui/color-input.js.map +1 -1
  16. package/dist/ui/icon.d.ts +2 -0
  17. package/dist/ui/icon.d.ts.map +1 -1
  18. package/dist/ui/icon.js +31 -26
  19. package/dist/ui/icon.js.map +1 -1
  20. package/dist/ui/search-input.d.ts.map +1 -1
  21. package/dist/ui/search-input.js +1 -0
  22. package/dist/ui/search-input.js.map +1 -1
  23. package/dist/ui/segmented-control.d.ts +28 -6
  24. package/dist/ui/segmented-control.d.ts.map +1 -1
  25. package/dist/ui/segmented-control.js +61 -43
  26. package/dist/ui/segmented-control.js.map +1 -1
  27. package/dist/ui/split-button.d.ts.map +1 -1
  28. package/dist/ui/split-button.js +7 -0
  29. package/dist/ui/split-button.js.map +1 -1
  30. package/docs/components/ui/button.md +2 -2
  31. package/docs/components/ui/segmented-control.md +31 -11
  32. package/docs/recipes/install-remix.md +3 -3
  33. package/docs/recipes/install-vite.md +3 -3
  34. package/docs/recipes/server-components.md +2 -2
  35. package/llms.txt +1 -1
  36. package/make-kit/foundations/color.md +20 -0
  37. package/make-kit/foundations/dark-mode.md +6 -9
  38. package/make-kit/foundations/icons.md +3 -3
  39. package/make-kit/setup.md +4 -4
  40. package/mcp-manifest.json +61 -10
  41. package/package.json +1 -1
  42. package/scripts/welcome.mjs +59 -1
  43. package/skill/SKILL.md +1 -1
  44. package/skill/references/components.md +1 -1
  45. package/skill/references/server-components.md +2 -2
  46. package/skill/references/setup-remix.md +3 -3
  47. package/skill/references/setup-vite.md +3 -3
package/dist/ui/icon.js CHANGED
@@ -79,10 +79,10 @@ var f = {
79
79
  lg: "md",
80
80
  xl: "lg",
81
81
  "2xl": "lg"
82
- }, g = a.forwardRef(({ icon: a, size: g, stroke: _, label: v, animate: y, state: b, className: x }, S) => {
83
- let C = e(), w = g ?? C.size ?? "md", T = _ ?? C.stroke ?? "regular", E = f[w], D = p[T][w], O = d();
82
+ }, g = a.forwardRef(({ icon: a, size: g, stroke: _, label: v, animate: y, state: b, className: x, style: S }, C) => {
83
+ let w = e(), T = g ?? w.size ?? "md", E = _ ?? w.stroke ?? "regular", D = f[T], O = p[E][T], k = d();
84
84
  if (b && b !== "idle") {
85
- let e = h[w], t = b === "loading" ? "spinning" : b;
85
+ let e = h[T], t = b === "loading" ? "spinning" : b;
86
86
  return /* @__PURE__ */ s(l, {
87
87
  mode: "wait",
88
88
  children: /* @__PURE__ */ s(u.span, {
@@ -91,6 +91,7 @@ var f = {
91
91
  exit: { opacity: 0 },
92
92
  transition: n.fade,
93
93
  className: "inline-flex",
94
+ style: S,
94
95
  children: /* @__PURE__ */ s(i, {
95
96
  size: e,
96
97
  state: t,
@@ -99,7 +100,7 @@ var f = {
99
100
  }, "spinner")
100
101
  });
101
102
  }
102
- if (y === "draw" && !O) {
103
+ if (y === "draw" && !k) {
103
104
  let e = {
104
105
  initial: {
105
106
  pathLength: 0,
@@ -118,16 +119,17 @@ var f = {
118
119
  }
119
120
  }, t = a.displayName ?? "", n = t === "Check" || t === "IconCheck" || t === "CircleCheck";
120
121
  if (n || t === "X" || t === "IconX") return /* @__PURE__ */ s("svg", {
121
- ref: S,
122
- width: E,
123
- height: E,
122
+ ref: C,
123
+ width: D,
124
+ height: D,
124
125
  viewBox: "0 0 24 24",
125
126
  fill: "none",
126
127
  stroke: "currentColor",
127
- strokeWidth: D,
128
+ strokeWidth: O,
128
129
  strokeLinecap: "round",
129
130
  strokeLinejoin: "round",
130
131
  className: x,
132
+ style: S,
131
133
  "aria-hidden": v ? void 0 : "true",
132
134
  "aria-label": v,
133
135
  role: v ? "img" : void 0,
@@ -160,41 +162,44 @@ var f = {
160
162
  })] })
161
163
  });
162
164
  }
163
- let k = typeof y == "string" && y !== "none" && y !== "draw" ? m[y] : null, A = typeof y == "object" && y && (y.rotate !== void 0 || y.scale !== void 0) ? y : null;
164
- if (O || !k && !A) return v ? /* @__PURE__ */ s(a, {
165
- ref: S,
166
- size: E,
167
- stroke: D,
165
+ let A = typeof y == "string" && y !== "none" && y !== "draw" ? m[y] : null, j = typeof y == "object" && y && (y.rotate !== void 0 || y.scale !== void 0) ? y : null;
166
+ if (k || !A && !j) return v ? /* @__PURE__ */ s(a, {
167
+ ref: C,
168
+ size: D,
169
+ stroke: O,
168
170
  className: x,
171
+ style: S,
169
172
  title: v,
170
173
  "aria-label": v,
171
174
  role: "img"
172
175
  }) : /* @__PURE__ */ s(a, {
173
- ref: S,
174
- size: E,
175
- stroke: D,
176
+ ref: C,
177
+ size: D,
178
+ stroke: O,
176
179
  className: x,
180
+ style: S,
177
181
  "aria-hidden": "true"
178
182
  });
179
- let j = k ?? {
180
- animate: A,
183
+ let M = A ?? {
184
+ animate: j,
181
185
  transition: t.snappy
182
- }, M = v ? /* @__PURE__ */ s(a, {
183
- size: E,
184
- stroke: D,
186
+ }, N = v ? /* @__PURE__ */ s(a, {
187
+ size: D,
188
+ stroke: O,
185
189
  title: v,
186
190
  "aria-label": v,
187
191
  role: "img"
188
192
  }) : /* @__PURE__ */ s(a, {
189
- size: E,
190
- stroke: D,
193
+ size: D,
194
+ stroke: O,
191
195
  "aria-hidden": "true"
192
196
  });
193
197
  return /* @__PURE__ */ s(u.span, {
194
- ref: S,
198
+ ref: C,
195
199
  className: r("inline-flex", x),
196
- ...j,
197
- children: M
200
+ style: S,
201
+ ...M,
202
+ children: N
198
203
  });
199
204
  });
200
205
  g.displayName = "Icon";
@@ -1 +1 @@
1
- {"version":3,"file":"icon.js","names":[],"sources":["../../src/ui/icon.tsx"],"sourcesContent":["'use client'\n\nimport { AnimatePresence, motion, useReducedMotion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { type IconSize, type IconStroke,useIconContext } from './icon-context'\nimport { springs, tweens } from './lib/motion'\nimport { cn } from './lib/utils'\nimport { Spinner } from './spinner'\n\n/** Icon size tier → pixel dimensions */\nconst SIZE_PX: Record<IconSize, number> = {\n xs: 14, sm: 16, md: 18, lg: 20, xl: 24, '2xl': 32,\n}\n\n/** Stroke weight → strokeWidth per size tier (lighter strokes on smaller icons) */\nconst STROKE_MAP: Record<IconStroke, Record<IconSize, number>> = {\n light: { xs: 1.25, sm: 1.5, md: 1.5, lg: 1.75, xl: 2, '2xl': 2 },\n regular: { xs: 1.5, sm: 2, md: 2, lg: 2, xl: 2, '2xl': 2.25 },\n bold: { xs: 2, sm: 2.5, md: 2.5, lg: 2.5, xl: 2.5, '2xl': 2.5 },\n}\n\n/** Props accepted by Tabler icon components (and most SVG icon libraries) */\ntype IconComponentProps = Partial<Omit<React.ComponentPropsWithoutRef<'svg'>, 'stroke'>> & {\n size?: string | number\n stroke?: string | number\n title?: string\n}\n\nconst ANIMATION_PRESETS = {\n spin: {\n animate: { rotate: 360 },\n transition: { duration: 1, repeat: Infinity, ease: 'linear' as const },\n },\n pulse: {\n animate: { scale: [1, 1.15, 1] },\n transition: { duration: 2, repeat: Infinity, ease: 'easeInOut' as const },\n },\n bounce: {\n animate: { y: [0, -4, 0] },\n transition: { duration: 1.5, repeat: Infinity, ease: 'easeInOut' as const },\n },\n}\n\nconst ICON_TO_SPINNER_SIZE: Record<IconSize, 'sm' | 'md' | 'lg'> = {\n xs: 'sm', sm: 'sm', md: 'md', lg: 'md', xl: 'lg', '2xl': 'lg',\n}\n\nexport interface IconProps {\n /** The Tabler icon component (or any ForwardRef SVG icon component) */\n icon: React.ForwardRefExoticComponent<IconComponentProps & React.RefAttributes<SVGSVGElement>>\n /** Size tier — reads from IconContext if not set */\n size?: IconSize\n /** Stroke weight — reads from IconContext if not set */\n stroke?: IconStroke\n /** Accessible label — renders <title> + sets aria-label. Without this, icon is aria-hidden. */\n label?: string\n /**\n * Preset animation or controlled motion. 'none' disables inherited animation.\n * 'draw' renders the icon as an SVG path that draws in like a pen stroke\n * (works with IconCheck and IconX — others fall back to fade-in).\n */\n animate?: 'spin' | 'pulse' | 'bounce' | 'draw' | 'none' | { rotate?: number; scale?: number }\n /**\n * State machine for loading→success/error transitions. Delegates to Spinner (bare variant).\n *\n * **Priority rule:** If both `state` and `animate` are set, `state` wins —\n * the state machine check runs first and short-circuits rendering.\n */\n state?: 'idle' | 'loading' | 'success' | 'error'\n className?: string\n}\n\n/**\n * Icon — context-aware wrapper for Tabler icons with standardized sizing,\n * stroke weights, accessibility, animation presets, and loading state machine.\n *\n * Reads size and stroke from IconContext (provided by Button, IconGroup, etc.).\n * Explicit props always override context.\n *\n * **Priority rule:** If both `state` and `animate` are set, `state` wins.\n *\n * @example\n * <Icon icon={IconPlus} /> // md size, regular stroke\n * <Icon icon={IconPlus} size=\"xs\" stroke=\"light\" /> // 14px, stroke 1.25\n * <Icon icon={IconPlus} label=\"Add item\" /> // accessible, not decorative\n * <Icon icon={IconPlus} animate=\"spin\" /> // continuous rotation\n * <Icon icon={IconPlus} animate=\"pulse\" /> // scale pulse\n * <Icon icon={IconPlus} state=\"loading\" /> // bare spinner\n * <Icon icon={IconPlus} state=\"success\" /> // animated checkmark\n */\nexport const Icon = React.forwardRef<SVGSVGElement, IconProps>(\n ({ icon: TablerIcon, size, stroke, label, animate, state, className }, ref) => {\n const ctx = useIconContext()\n const resolvedSize = size ?? ctx.size ?? 'md'\n const resolvedStroke = stroke ?? ctx.stroke ?? 'regular'\n const px = SIZE_PX[resolvedSize]\n const sw = STROKE_MAP[resolvedStroke][resolvedSize]\n const prefersReduced = useReducedMotion()\n\n // ── State machine (priority: state > animate) ──────────────────────\n if (state && state !== 'idle') {\n const spinnerSize = ICON_TO_SPINNER_SIZE[resolvedSize]\n const spinnerState = state === 'loading' ? 'spinning' : state\n return (\n <AnimatePresence mode=\"wait\">\n <motion.span\n key=\"spinner\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n className=\"inline-flex\"\n >\n <Spinner size={spinnerSize} state={spinnerState} variant=\"bare\" />\n </motion.span>\n </AnimatePresence>\n )\n }\n\n // ── Draw animation (SVG pathLength) ─────────────────────────────────\n if (animate === 'draw' && !prefersReduced) {\n const drawProps = {\n initial: { pathLength: 0, opacity: 0 },\n animate: { pathLength: 1, opacity: 1 },\n transition: { pathLength: { duration: 0.35, ease: 'easeOut' as const }, opacity: { duration: 0.1 } },\n }\n // Check which icon this is to render the appropriate SVG path.\n // Tabler displayNames: IconCheck → \"Check\", IconX → \"X\"\n const name = TablerIcon.displayName ?? ''\n const isCheck = name === 'Check' || name === 'IconCheck' || name === 'CircleCheck'\n const isX = name === 'X' || name === 'IconX'\n\n if (isCheck || isX) {\n return (\n <svg\n ref={ref}\n width={px}\n height={px}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={sw}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n aria-hidden={label ? undefined : 'true'}\n aria-label={label}\n role={label ? 'img' : undefined}\n >\n {isCheck ? (\n <motion.polyline points=\"4 12 10 18 20 6\" {...drawProps} />\n ) : (\n <>\n <motion.line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" {...drawProps} />\n <motion.line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"\n {...drawProps}\n transition={{\n pathLength: { duration: 0.25, ease: 'easeOut', delay: 0.1 },\n opacity: { duration: 0.1, delay: 0.1 },\n }}\n />\n </>\n )}\n </svg>\n )\n }\n // Unknown icon: fall through to fade-in via static render\n }\n\n // ── Determine animation ────────────────────────────────────────────\n const animatePreset =\n typeof animate === 'string' && animate !== 'none' && animate !== 'draw'\n ? ANIMATION_PRESETS[animate as keyof typeof ANIMATION_PRESETS]\n : null\n const animateObject =\n typeof animate === 'object' && animate !== null && (animate.rotate !== undefined || animate.scale !== undefined)\n ? animate\n : null\n\n if (prefersReduced || (!animatePreset && !animateObject)) {\n // Static render (no animation or reduced motion)\n return label ? (\n <TablerIcon ref={ref as any} size={px} stroke={sw} className={className} title={label} aria-label={label} role=\"img\" />\n ) : (\n <TablerIcon ref={ref as any} size={px} stroke={sw} className={className} aria-hidden=\"true\" />\n )\n }\n\n // ── Animated render ────────────────────────────────────────────────\n const motionProps = animatePreset ?? {\n animate: animateObject!,\n transition: springs.snappy,\n }\n\n const iconEl = label ? (\n <TablerIcon size={px} stroke={sw} title={label} aria-label={label} role=\"img\" />\n ) : (\n <TablerIcon size={px} stroke={sw} aria-hidden=\"true\" />\n )\n\n return (\n <motion.span\n ref={ref as any}\n className={cn('inline-flex', className)}\n {...motionProps}\n >\n {iconEl}\n </motion.span>\n )\n },\n)\nIcon.displayName = 'Icon'\n"],"mappings":";;;;;;;;;AAWA,IAAM,IAAoC;CACxC,IAAI;CAAI,IAAI;CAAI,IAAI;CAAI,IAAI;CAAI,IAAI;CAAI,OAAO;AACjD,GAGM,IAA2D;CAC/D,OAAS;EAAE,IAAI;EAAM,IAAI;EAAK,IAAI;EAAK,IAAI;EAAM,IAAI;EAAM,OAAO;CAAE;CACpE,SAAS;EAAE,IAAI;EAAM,IAAI;EAAK,IAAI;EAAK,IAAI;EAAM,IAAI;EAAM,OAAO;CAAK;CACvE,MAAS;EAAE,IAAI;EAAM,IAAI;EAAK,IAAI;EAAK,IAAI;EAAM,IAAI;EAAM,OAAO;CAAI;AACxE,GASM,IAAoB;CACxB,MAAM;EACJ,SAAS,EAAE,QAAQ,IAAI;EACvB,YAAY;GAAE,UAAU;GAAG,QAAQ;GAAU,MAAM;EAAkB;CACvE;CACA,OAAO;EACL,SAAS,EAAE,OAAO;GAAC;GAAG;GAAM;EAAC,EAAE;EAC/B,YAAY;GAAE,UAAU;GAAG,QAAQ;GAAU,MAAM;EAAqB;CAC1E;CACA,QAAQ;EACN,SAAS,EAAE,GAAG;GAAC;GAAG;GAAI;EAAC,EAAE;EACzB,YAAY;GAAE,UAAU;GAAK,QAAQ;GAAU,MAAM;EAAqB;CAC5E;AACF,GAEM,IAA6D;CACjE,IAAI;CAAM,IAAI;CAAM,IAAI;CAAM,IAAI;CAAM,IAAI;CAAM,OAAO;AAC3D,GA6Ca,IAAO,EAAM,YACvB,EAAE,MAAM,GAAY,SAAM,WAAQ,UAAO,YAAS,UAAO,gBAAa,MAAQ;CAC7E,IAAM,IAAM,EAAe,GACrB,IAAe,KAAQ,EAAI,QAAQ,MACnC,IAAiB,KAAU,EAAI,UAAU,WACzC,IAAK,EAAQ,IACb,IAAK,EAAW,EAAe,CAAC,IAChC,IAAiB,EAAiB;CAGxC,IAAI,KAAS,MAAU,QAAQ;EAC7B,IAAM,IAAc,EAAqB,IACnC,IAAe,MAAU,YAAY,aAAa;EACxD,OACE,kBAAC,GAAD;GAAiB,MAAK;aACpB,kBAAC,EAAO,MAAR;IAEE,SAAS,EAAE,SAAS,EAAE;IACtB,SAAS,EAAE,SAAS,EAAE;IACtB,MAAM,EAAE,SAAS,EAAE;IACnB,YAAY,EAAO;IACnB,WAAU;cAEV,kBAAC,GAAD;KAAS,MAAM;KAAa,OAAO;KAAc,SAAQ;IAAQ,CAAA;GACtD,GARP,SAQO;EACE,CAAA;CAErB;CAGA,IAAI,MAAY,UAAU,CAAC,GAAgB;EACzC,IAAM,IAAY;GAChB,SAAS;IAAE,YAAY;IAAG,SAAS;GAAE;GACrC,SAAS;IAAE,YAAY;IAAG,SAAS;GAAE;GACrC,YAAY;IAAE,YAAY;KAAE,UAAU;KAAM,MAAM;IAAmB;IAAG,SAAS,EAAE,UAAU,GAAI;GAAE;EACrG,GAGM,IAAO,EAAW,eAAe,IACjC,IAAU,MAAS,WAAW,MAAS,eAAe,MAAS;EAGrE,IAAI,KAFQ,MAAS,OAAO,MAAS,SAGnC,OACE,kBAAC,OAAD;GACO;GACL,OAAO;GACP,QAAQ;GACR,SAAQ;GACR,MAAK;GACL,QAAO;GACP,aAAa;GACb,eAAc;GACd,gBAAe;GACJ;GACX,eAAa,IAAQ,KAAA,IAAY;GACjC,cAAY;GACZ,MAAM,IAAQ,QAAQ,KAAA;aAErB,IACC,kBAAC,EAAO,UAAR;IAAiB,QAAO;IAAkB,GAAI;GAAY,CAAA,IAE1D,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,EAAO,MAAR;IAAa,IAAG;IAAI,IAAG;IAAI,IAAG;IAAK,IAAG;IAAK,GAAI;GAAY,CAAA,GAC3D,kBAAC,EAAO,MAAR;IAAa,IAAG;IAAK,IAAG;IAAI,IAAG;IAAI,IAAG;IACpC,GAAI;IACJ,YAAY;KACV,YAAY;MAAE,UAAU;MAAM,MAAM;MAAW,OAAO;KAAI;KAC1D,SAAS;MAAE,UAAU;MAAK,OAAO;KAAI;IACvC;GACD,CAAA,CACD,EAAA,CAAA;EAED,CAAA;CAIX;CAGA,IAAM,IACJ,OAAO,KAAY,YAAY,MAAY,UAAU,MAAY,SAC7D,EAAkB,KAClB,MACA,IACJ,OAAO,KAAY,YAAY,MAAqB,EAAQ,WAAW,KAAA,KAAa,EAAQ,UAAU,KAAA,KAClG,IACA;CAEN,IAAI,KAAmB,CAAC,KAAiB,CAAC,GAExC,OAAO,IACL,kBAAC,GAAD;EAAiB;EAAY,MAAM;EAAI,QAAQ;EAAe;EAAW,OAAO;EAAO,cAAY;EAAO,MAAK;CAAO,CAAA,IAEtH,kBAAC,GAAD;EAAiB;EAAY,MAAM;EAAI,QAAQ;EAAe;EAAW,eAAY;CAAQ,CAAA;CAKjG,IAAM,IAAc,KAAiB;EACnC,SAAS;EACT,YAAY,EAAQ;CACtB,GAEM,IAAS,IACb,kBAAC,GAAD;EAAY,MAAM;EAAI,QAAQ;EAAI,OAAO;EAAO,cAAY;EAAO,MAAK;CAAO,CAAA,IAE/E,kBAAC,GAAD;EAAY,MAAM;EAAI,QAAQ;EAAI,eAAY;CAAQ,CAAA;CAGxD,OACE,kBAAC,EAAO,MAAR;EACO;EACL,WAAW,EAAG,eAAe,CAAS;EACtC,GAAI;YAEH;CACU,CAAA;AAEjB,CACF;AACA,EAAK,cAAc"}
1
+ {"version":3,"file":"icon.js","names":[],"sources":["../../src/ui/icon.tsx"],"sourcesContent":["'use client'\n\nimport { AnimatePresence, motion, useReducedMotion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { type IconSize, type IconStroke,useIconContext } from './icon-context'\nimport { springs, tweens } from './lib/motion'\nimport { cn } from './lib/utils'\nimport { Spinner } from './spinner'\n\n/** Icon size tier → pixel dimensions */\nconst SIZE_PX: Record<IconSize, number> = {\n xs: 14, sm: 16, md: 18, lg: 20, xl: 24, '2xl': 32,\n}\n\n/** Stroke weight → strokeWidth per size tier (lighter strokes on smaller icons) */\nconst STROKE_MAP: Record<IconStroke, Record<IconSize, number>> = {\n light: { xs: 1.25, sm: 1.5, md: 1.5, lg: 1.75, xl: 2, '2xl': 2 },\n regular: { xs: 1.5, sm: 2, md: 2, lg: 2, xl: 2, '2xl': 2.25 },\n bold: { xs: 2, sm: 2.5, md: 2.5, lg: 2.5, xl: 2.5, '2xl': 2.5 },\n}\n\n/** Props accepted by Tabler icon components (and most SVG icon libraries) */\ntype IconComponentProps = Partial<Omit<React.ComponentPropsWithoutRef<'svg'>, 'stroke'>> & {\n size?: string | number\n stroke?: string | number\n title?: string\n}\n\nconst ANIMATION_PRESETS = {\n spin: {\n animate: { rotate: 360 },\n transition: { duration: 1, repeat: Infinity, ease: 'linear' as const },\n },\n pulse: {\n animate: { scale: [1, 1.15, 1] },\n transition: { duration: 2, repeat: Infinity, ease: 'easeInOut' as const },\n },\n bounce: {\n animate: { y: [0, -4, 0] },\n transition: { duration: 1.5, repeat: Infinity, ease: 'easeInOut' as const },\n },\n}\n\nconst ICON_TO_SPINNER_SIZE: Record<IconSize, 'sm' | 'md' | 'lg'> = {\n xs: 'sm', sm: 'sm', md: 'md', lg: 'md', xl: 'lg', '2xl': 'lg',\n}\n\nexport interface IconProps {\n /** The Tabler icon component (or any ForwardRef SVG icon component) */\n icon: React.ForwardRefExoticComponent<IconComponentProps & React.RefAttributes<SVGSVGElement>>\n /** Size tier — reads from IconContext if not set */\n size?: IconSize\n /** Stroke weight — reads from IconContext if not set */\n stroke?: IconStroke\n /** Accessible label — renders <title> + sets aria-label. Without this, icon is aria-hidden. */\n label?: string\n /**\n * Preset animation or controlled motion. 'none' disables inherited animation.\n * 'draw' renders the icon as an SVG path that draws in like a pen stroke\n * (works with IconCheck and IconX — others fall back to fade-in).\n */\n animate?: 'spin' | 'pulse' | 'bounce' | 'draw' | 'none' | { rotate?: number; scale?: number }\n /**\n * State machine for loading→success/error transitions. Delegates to Spinner (bare variant).\n *\n * **Priority rule:** If both `state` and `animate` are set, `state` wins —\n * the state machine check runs first and short-circuits rendering.\n */\n state?: 'idle' | 'loading' | 'success' | 'error'\n className?: string\n /** Inline styles forwarded to the rendered icon (e.g. decorative opacity/transform). */\n style?: React.CSSProperties\n}\n\n/**\n * Icon — context-aware wrapper for Tabler icons with standardized sizing,\n * stroke weights, accessibility, animation presets, and loading state machine.\n *\n * Reads size and stroke from IconContext (provided by Button, IconGroup, etc.).\n * Explicit props always override context.\n *\n * **Priority rule:** If both `state` and `animate` are set, `state` wins.\n *\n * @example\n * <Icon icon={IconPlus} /> // md size, regular stroke\n * <Icon icon={IconPlus} size=\"xs\" stroke=\"light\" /> // 14px, stroke 1.25\n * <Icon icon={IconPlus} label=\"Add item\" /> // accessible, not decorative\n * <Icon icon={IconPlus} animate=\"spin\" /> // continuous rotation\n * <Icon icon={IconPlus} animate=\"pulse\" /> // scale pulse\n * <Icon icon={IconPlus} state=\"loading\" /> // bare spinner\n * <Icon icon={IconPlus} state=\"success\" /> // animated checkmark\n */\nexport const Icon = React.forwardRef<SVGSVGElement, IconProps>(\n ({ icon: TablerIcon, size, stroke, label, animate, state, className, style }, ref) => {\n const ctx = useIconContext()\n const resolvedSize = size ?? ctx.size ?? 'md'\n const resolvedStroke = stroke ?? ctx.stroke ?? 'regular'\n const px = SIZE_PX[resolvedSize]\n const sw = STROKE_MAP[resolvedStroke][resolvedSize]\n const prefersReduced = useReducedMotion()\n\n // ── State machine (priority: state > animate) ──────────────────────\n if (state && state !== 'idle') {\n const spinnerSize = ICON_TO_SPINNER_SIZE[resolvedSize]\n const spinnerState = state === 'loading' ? 'spinning' : state\n return (\n <AnimatePresence mode=\"wait\">\n <motion.span\n key=\"spinner\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n className=\"inline-flex\"\n style={style}\n >\n <Spinner size={spinnerSize} state={spinnerState} variant=\"bare\" />\n </motion.span>\n </AnimatePresence>\n )\n }\n\n // ── Draw animation (SVG pathLength) ─────────────────────────────────\n if (animate === 'draw' && !prefersReduced) {\n const drawProps = {\n initial: { pathLength: 0, opacity: 0 },\n animate: { pathLength: 1, opacity: 1 },\n transition: { pathLength: { duration: 0.35, ease: 'easeOut' as const }, opacity: { duration: 0.1 } },\n }\n // Check which icon this is to render the appropriate SVG path.\n // Tabler displayNames: IconCheck → \"Check\", IconX → \"X\"\n const name = TablerIcon.displayName ?? ''\n const isCheck = name === 'Check' || name === 'IconCheck' || name === 'CircleCheck'\n const isX = name === 'X' || name === 'IconX'\n\n if (isCheck || isX) {\n return (\n <svg\n ref={ref}\n width={px}\n height={px}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={sw}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n style={style}\n aria-hidden={label ? undefined : 'true'}\n aria-label={label}\n role={label ? 'img' : undefined}\n >\n {isCheck ? (\n <motion.polyline points=\"4 12 10 18 20 6\" {...drawProps} />\n ) : (\n <>\n <motion.line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" {...drawProps} />\n <motion.line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"\n {...drawProps}\n transition={{\n pathLength: { duration: 0.25, ease: 'easeOut', delay: 0.1 },\n opacity: { duration: 0.1, delay: 0.1 },\n }}\n />\n </>\n )}\n </svg>\n )\n }\n // Unknown icon: fall through to fade-in via static render\n }\n\n // ── Determine animation ────────────────────────────────────────────\n const animatePreset =\n typeof animate === 'string' && animate !== 'none' && animate !== 'draw'\n ? ANIMATION_PRESETS[animate as keyof typeof ANIMATION_PRESETS]\n : null\n const animateObject =\n typeof animate === 'object' && animate !== null && (animate.rotate !== undefined || animate.scale !== undefined)\n ? animate\n : null\n\n if (prefersReduced || (!animatePreset && !animateObject)) {\n // Static render (no animation or reduced motion)\n return label ? (\n <TablerIcon ref={ref as any} size={px} stroke={sw} className={className} style={style} title={label} aria-label={label} role=\"img\" />\n ) : (\n <TablerIcon ref={ref as any} size={px} stroke={sw} className={className} style={style} aria-hidden=\"true\" />\n )\n }\n\n // ── Animated render ────────────────────────────────────────────────\n const motionProps = animatePreset ?? {\n animate: animateObject!,\n transition: springs.snappy,\n }\n\n const iconEl = label ? (\n <TablerIcon size={px} stroke={sw} title={label} aria-label={label} role=\"img\" />\n ) : (\n <TablerIcon size={px} stroke={sw} aria-hidden=\"true\" />\n )\n\n return (\n <motion.span\n ref={ref as any}\n className={cn('inline-flex', className)}\n style={style}\n {...motionProps}\n >\n {iconEl}\n </motion.span>\n )\n },\n)\nIcon.displayName = 'Icon'\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"search-input.d.ts","sourceRoot":"","sources":["../../src/ui/search-input.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAQ9B,KAAK,eAAe,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAEhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACjG,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,oBAAoB;IACpB,IAAI,CAAC,EAAE,eAAe,CAAA;CACvB;AAED,QAAA,MAAM,WAAW,2FA6ChB,CAAA;AAGD,OAAO,EAAE,WAAW,EAAE,CAAA"}
1
+ {"version":3,"file":"search-input.d.ts","sourceRoot":"","sources":["../../src/ui/search-input.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAQ9B,KAAK,eAAe,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAEhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACjG,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,oBAAoB;IACpB,IAAI,CAAC,EAAE,eAAe,CAAA;CACvB;AAED,QAAA,MAAM,WAAW,2FA8ChB,CAAA;AAGD,OAAO,EAAE,WAAW,EAAE,CAAA"}
@@ -11,6 +11,7 @@ import { AnimatePresence as l, motion as u } from "framer-motion";
11
11
  //#region src/ui/search-input.tsx
12
12
  var d = a.forwardRef(({ className: a, value: d, onClear: f, loading: p, size: m = "md", placeholder: h, ...g }, _) => {
13
13
  let v = d !== void 0 && d !== "", y = p ? /* @__PURE__ */ o(t, { size: "sm" }) : /* @__PURE__ */ o(l, { children: v && f && /* @__PURE__ */ o(u.div, {
14
+ className: "flex items-center justify-center",
14
15
  initial: {
15
16
  opacity: 0,
16
17
  scale: .8
@@ -1 +1 @@
1
- {"version":3,"file":"search-input.js","names":[],"sources":["../../src/ui/search-input.tsx"],"sourcesContent":["'use client'\n\nimport { IconSearch, IconX } from '@tabler/icons-react'\nimport { AnimatePresence, motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Button } from './button'\nimport { Icon } from './icon'\nimport { Input } from './input'\nimport { springs } from './lib/motion'\nimport { Spinner } from './spinner'\n\ntype SearchInputSize = 'xs' | 'sm' | 'md' | 'lg'\n\n/**\n * Props for SearchInput — a search field with a built-in leading magnifier icon, optional loading\n * spinner, and an auto-shown clear button when `value` is non-empty and `onClear` is provided.\n *\n * **Sizes:** `sm` | `md` (default) | `lg` — matches Input's `size` prop API.\n * HTML's native `size` attribute is excluded — use CSS width instead.\n *\n * **Clear button:** Appears automatically when `value !== ''` and `onClear` is provided.\n * When `loading` is true, a spinning loader replaces the clear button.\n *\n * @example\n * // Controlled search with clear:\n * <SearchInput\n * value={query}\n * onChange={(e) => setQuery(e.target.value)}\n * onClear={() => setQuery('')}\n * placeholder=\"Search tasks...\"\n * />\n *\n * @example\n * // Async search with loading state while fetching results:\n * <SearchInput\n * value={query}\n * onChange={handleSearch}\n * loading={isSearching}\n * placeholder=\"Search clients...\"\n * />\n *\n * @example\n * // Compact search bar in a toolbar:\n * <SearchInput size=\"sm\" value={q} onChange={(e) => setQ(e.target.value)} onClear={() => setQ('')} />\n * // These are just a few ways — feel free to combine props creatively!\n */\nexport interface SearchInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {\n onClear?: () => void\n loading?: boolean\n /** @default 'md' */\n size?: SearchInputSize\n}\n\nconst SearchInput = React.forwardRef<HTMLInputElement, SearchInputProps>(\n ({ className, value, onClear, loading, size = 'md', placeholder, ...props }, ref) => {\n const hasValue = value !== undefined && value !== ''\n\n const endContent = loading ? (\n <Spinner size=\"sm\" />\n ) : (\n <AnimatePresence>\n {hasValue && onClear && (\n <motion.div\n initial={{ opacity: 0, scale: 0.8 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.8 }}\n transition={springs.snappy}\n >\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n onClick={onClear}\n aria-label=\"Clear search\"\n title=\"Clear\"\n >\n <Icon icon={IconX} />\n </Button>\n </motion.div>\n )}\n </AnimatePresence>\n )\n\n return (\n <Input\n ref={ref}\n size={size}\n startSection={<Icon icon={IconSearch} />}\n endSection={endContent}\n endSectionClickable={!!hasValue && !loading}\n placeholder={placeholder}\n value={value}\n aria-busy={loading || undefined}\n className={className}\n {...props}\n />\n )\n },\n)\nSearchInput.displayName = 'SearchInput'\n\nexport { SearchInput }\n"],"mappings":";;;;;;;;;;;AAsDA,IAAM,IAAc,EAAM,YACvB,EAAE,cAAW,UAAO,YAAS,YAAS,UAAO,MAAM,gBAAa,GAAG,KAAS,MAAQ;CACnF,IAAM,IAAW,MAAU,KAAA,KAAa,MAAU,IAE5C,IAAa,IACjB,kBAAC,GAAD,EAAS,MAAK,KAAM,CAAA,IAEpB,kBAAC,GAAD,EAAA,UACG,KAAY,KACX,kBAAC,EAAO,KAAR;EACE,SAAS;GAAE,SAAS;GAAG,OAAO;EAAI;EAClC,SAAS;GAAE,SAAS;GAAG,OAAO;EAAE;EAChC,MAAM;GAAE,SAAS;GAAG,OAAO;EAAI;EAC/B,YAAY,EAAQ;YAEpB,kBAAC,GAAD;GACE,MAAK;GACL,SAAQ;GACR,MAAK;GACL,SAAS;GACT,cAAW;GACX,OAAM;aAEN,kBAAC,GAAD,EAAM,MAAM,EAAQ,CAAA;EACd,CAAA;CACE,CAAA,EAEC,CAAA;CAGnB,OACE,kBAAC,GAAD;EACO;EACC;EACN,cAAc,kBAAC,GAAD,EAAM,MAAM,EAAa,CAAA;EACvC,YAAY;EACZ,qBAAqB,CAAC,CAAC,KAAY,CAAC;EACvB;EACN;EACP,aAAW,KAAW,KAAA;EACX;EACX,GAAI;CACL,CAAA;AAEL,CACF;AACA,EAAY,cAAc"}
1
+ {"version":3,"file":"search-input.js","names":[],"sources":["../../src/ui/search-input.tsx"],"sourcesContent":["'use client'\n\nimport { IconSearch, IconX } from '@tabler/icons-react'\nimport { AnimatePresence, motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Button } from './button'\nimport { Icon } from './icon'\nimport { Input } from './input'\nimport { springs } from './lib/motion'\nimport { Spinner } from './spinner'\n\ntype SearchInputSize = 'xs' | 'sm' | 'md' | 'lg'\n\n/**\n * Props for SearchInput — a search field with a built-in leading magnifier icon, optional loading\n * spinner, and an auto-shown clear button when `value` is non-empty and `onClear` is provided.\n *\n * **Sizes:** `sm` | `md` (default) | `lg` — matches Input's `size` prop API.\n * HTML's native `size` attribute is excluded — use CSS width instead.\n *\n * **Clear button:** Appears automatically when `value !== ''` and `onClear` is provided.\n * When `loading` is true, a spinning loader replaces the clear button.\n *\n * @example\n * // Controlled search with clear:\n * <SearchInput\n * value={query}\n * onChange={(e) => setQuery(e.target.value)}\n * onClear={() => setQuery('')}\n * placeholder=\"Search tasks...\"\n * />\n *\n * @example\n * // Async search with loading state while fetching results:\n * <SearchInput\n * value={query}\n * onChange={handleSearch}\n * loading={isSearching}\n * placeholder=\"Search clients...\"\n * />\n *\n * @example\n * // Compact search bar in a toolbar:\n * <SearchInput size=\"sm\" value={q} onChange={(e) => setQ(e.target.value)} onClear={() => setQ('')} />\n * // These are just a few ways — feel free to combine props creatively!\n */\nexport interface SearchInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {\n onClear?: () => void\n loading?: boolean\n /** @default 'md' */\n size?: SearchInputSize\n}\n\nconst SearchInput = React.forwardRef<HTMLInputElement, SearchInputProps>(\n ({ className, value, onClear, loading, size = 'md', placeholder, ...props }, ref) => {\n const hasValue = value !== undefined && value !== ''\n\n const endContent = loading ? (\n <Spinner size=\"sm\" />\n ) : (\n <AnimatePresence>\n {hasValue && onClear && (\n <motion.div\n className=\"flex items-center justify-center\"\n initial={{ opacity: 0, scale: 0.8 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.8 }}\n transition={springs.snappy}\n >\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n onClick={onClear}\n aria-label=\"Clear search\"\n title=\"Clear\"\n >\n <Icon icon={IconX} />\n </Button>\n </motion.div>\n )}\n </AnimatePresence>\n )\n\n return (\n <Input\n ref={ref}\n size={size}\n startSection={<Icon icon={IconSearch} />}\n endSection={endContent}\n endSectionClickable={!!hasValue && !loading}\n placeholder={placeholder}\n value={value}\n aria-busy={loading || undefined}\n className={className}\n {...props}\n />\n )\n },\n)\nSearchInput.displayName = 'SearchInput'\n\nexport { SearchInput }\n"],"mappings":";;;;;;;;;;;AAsDA,IAAM,IAAc,EAAM,YACvB,EAAE,cAAW,UAAO,YAAS,YAAS,UAAO,MAAM,gBAAa,GAAG,KAAS,MAAQ;CACnF,IAAM,IAAW,MAAU,KAAA,KAAa,MAAU,IAE5C,IAAa,IACjB,kBAAC,GAAD,EAAS,MAAK,KAAM,CAAA,IAEpB,kBAAC,GAAD,EAAA,UACG,KAAY,KACX,kBAAC,EAAO,KAAR;EACE,WAAU;EACV,SAAS;GAAE,SAAS;GAAG,OAAO;EAAI;EAClC,SAAS;GAAE,SAAS;GAAG,OAAO;EAAE;EAChC,MAAM;GAAE,SAAS;GAAG,OAAO;EAAI;EAC/B,YAAY,EAAQ;YAEpB,kBAAC,GAAD;GACE,MAAK;GACL,SAAQ;GACR,MAAK;GACL,SAAS;GACT,cAAW;GACX,OAAM;aAEN,kBAAC,GAAD,EAAM,MAAM,EAAQ,CAAA;EACd,CAAA;CACE,CAAA,EAEC,CAAA;CAGnB,OACE,kBAAC,GAAD;EACO;EACC;EACN,cAAc,kBAAC,GAAD,EAAM,MAAM,EAAa,CAAA;EACvC,YAAY;EACZ,qBAAqB,CAAC,CAAC,KAAY,CAAC;EACvB;EACN;EACP,aAAW,KAAW,KAAA;EACX;EACX,GAAI;CACL,CAAA;AAEL,CACF;AACA,EAAY,cAAc"}
@@ -2,20 +2,42 @@
2
2
  import { IconInput } from './lib/icon-input';
3
3
  import * as React from 'react';
4
4
  export type SegmentedControlSize = 'sm' | 'md' | 'lg';
5
- export type SegmentedControlVariant = 'default' | 'solid';
5
+ export type SegmentedControlVariant = 'soft' | 'solid';
6
+ /** @deprecated `variant="default"` was renamed to `"soft"`. Still accepted at
7
+ * runtime (maps to `"soft"`); update call sites — the alias is removed in a
8
+ * future major. */
9
+ type DeprecatedVariant = 'default';
6
10
  export interface SegmentedControlOption {
7
11
  id: string;
8
- text: string;
12
+ /** Visible label. Accepts a `ReactNode` so an option can carry a count badge
13
+ * or custom node, not just a string. Optional — omit for an icon-only
14
+ * segment, in which case set `ariaLabel`. */
15
+ text?: React.ReactNode;
9
16
  /** Optional icon rendered before the text label. Accepts any `IconInput`. */
10
17
  icon?: IconInput;
18
+ /** Accessible name for the segment. Required for icon-only segments (no
19
+ * `text`); otherwise the visible text provides the name. */
20
+ ariaLabel?: string;
11
21
  }
12
- export interface SegmentedControlProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onSelect'> {
22
+ export interface SegmentedControlProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onSelect' | 'defaultValue'> {
13
23
  size?: SegmentedControlSize;
14
- variant?: SegmentedControlVariant;
24
+ variant?: SegmentedControlVariant | DeprecatedVariant;
15
25
  options: SegmentedControlOption[];
16
- selectedId: string;
17
- onSelect: (id: string) => void;
26
+ /** Selected option id (controlled). Aligns with Tabs/ToggleGroup. */
27
+ value?: string;
28
+ /** Initial selected id for uncontrolled mode. Ignored when `value` is set;
29
+ * defaults to the first option. */
30
+ defaultValue?: string;
31
+ /** Fires with the newly-selected id (both modes). */
32
+ onValueChange?: (id: string) => void;
18
33
  disabled?: boolean;
34
+ /** Fill the container: segments split the available width equally instead of
35
+ * hugging their content. Use for full-width toggles and view switchers. */
36
+ fullWidth?: boolean;
37
+ /** @deprecated Use `value`. Still accepted; kept for back-compat. */
38
+ selectedId?: string;
39
+ /** @deprecated Use `onValueChange`. Still accepted; kept for back-compat. */
40
+ onSelect?: (id: string) => void;
19
41
  }
20
42
  declare const SegmentedControl: React.ForwardRefExoticComponent<SegmentedControlProps & React.RefAttributes<HTMLDivElement>>;
21
43
  export { SegmentedControl };
@@ -1 +1 @@
1
- {"version":3,"file":"segmented-control.d.ts","sourceRoot":"","sources":["../../src/ui/segmented-control.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAMjD,MAAM,MAAM,oBAAoB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AACrD,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,OAAO,CAAA;AAEzD,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,6EAA6E;IAC7E,IAAI,CAAC,EAAE,SAAS,CAAA;CACjB;AAED,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC;IACnG,IAAI,CAAC,EAAE,oBAAoB,CAAA;IAC3B,OAAO,CAAC,EAAE,uBAAuB,CAAA;IACjC,OAAO,EAAE,sBAAsB,EAAE,CAAA;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AA6BD,QAAA,MAAM,gBAAgB,8FAkIrB,CAAA;AAKD,OAAO,EAAE,gBAAgB,EAAE,CAAA"}
1
+ {"version":3,"file":"segmented-control.d.ts","sourceRoot":"","sources":["../../src/ui/segmented-control.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAMjD,MAAM,MAAM,oBAAoB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AACrD,MAAM,MAAM,uBAAuB,GAAG,MAAM,GAAG,OAAO,CAAA;AAEtD;;oBAEoB;AACpB,KAAK,iBAAiB,GAAG,SAAS,CAAA;AAElC,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAA;IACV;;kDAE8C;IAC9C,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;iEAC6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,GAAG,cAAc,CAAC;IACpH,IAAI,CAAC,EAAE,oBAAoB,CAAA;IAC3B,OAAO,CAAC,EAAE,uBAAuB,GAAG,iBAAiB,CAAA;IACrD,OAAO,EAAE,sBAAsB,EAAE,CAAA;IACjC,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;wCACoC;IACpC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,qDAAqD;IACrD,aAAa,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;gFAC4E;IAC5E,SAAS,CAAC,EAAE,OAAO,CAAA;IAEnB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;CAChC;AA2CD,QAAA,MAAM,gBAAgB,8FAkLrB,CAAA;AAKD,OAAO,EAAE,gBAAgB,EAAE,CAAA"}
@@ -4,9 +4,19 @@ import { cn as t } from "./lib/utils.js";
4
4
  import { normalizeIcon as n } from "../_chunks/normalize-icon.js";
5
5
  import * as r from "react";
6
6
  import { jsx as i, jsxs as a } from "react/jsx-runtime";
7
- import { LayoutGroup as o, motion as s } from "framer-motion";
7
+ import { LayoutGroup as o, motion as s, useReducedMotion as c } from "framer-motion";
8
8
  //#region src/ui/segmented-control.tsx
9
- var c = {
9
+ function l(e) {
10
+ if (!e) return !1;
11
+ let t = e.closest("[dir]");
12
+ if (t) return (t.getAttribute("dir") ?? "").toLowerCase() === "rtl";
13
+ try {
14
+ return getComputedStyle(e).direction === "rtl";
15
+ } catch {
16
+ return !1;
17
+ }
18
+ }
19
+ var u = {
10
20
  sm: {
11
21
  button: "h-7 px-ds-04 text-body-sm",
12
22
  icon: "h-3.5 w-3.5"
@@ -19,76 +29,84 @@ var c = {
19
29
  button: "h-10 px-ds-06 text-body-md",
20
30
  icon: "h-4 w-4"
21
31
  }
22
- }, l = {
23
- default: "bg-surface-overlay shadow-raised",
24
- solid: "bg-accent-9"
25
- }, u = {
26
- default: "text-surface-fg",
27
- solid: "text-accent-fg"
28
32
  }, d = {
29
- type: "spring",
30
- stiffness: 400,
31
- damping: 30
32
- }, f = r.forwardRef(function({ size: f = "md", variant: p = "default", options: m, selectedId: h, onSelect: g, disabled: _ = !1, className: v, ...y }, b) {
33
- let x = r.useId(), S = r.useRef(null), C = r.useCallback((e) => {
34
- S.current = e, typeof b == "function" ? b(e) : b && (b.current = e);
35
- }, [b]), w = (e) => {
36
- if (_) return;
37
- let t = m.findIndex((e) => e.id === h), n = t;
33
+ soft: "bg-segment-thumb shadow-segment",
34
+ solid: "bg-accent-9 shadow-segment"
35
+ }, f = {
36
+ soft: "text-surface-fg",
37
+ solid: "text-accent-fg"
38
+ }, p = r.forwardRef(function({ size: p = "md", variant: m = "soft", options: h, value: g, defaultValue: _, onValueChange: v, selectedId: y, onSelect: b, disabled: x = !1, fullWidth: S = !1, className: C, ...w }, T) {
39
+ let E = r.useId(), D = r.useRef(null), O = c(), k = m === "default" ? "soft" : m, A = g ?? y, j = A !== void 0, [M, N] = r.useState(_ ?? h[0]?.id), P = j ? A : M, F = r.useCallback((e) => {
40
+ j || N(e), (v ?? b)?.(e);
41
+ }, [
42
+ j,
43
+ v,
44
+ b
45
+ ]), I = O ? { duration: 0 } : {
46
+ type: "spring",
47
+ duration: .3,
48
+ bounce: 0
49
+ }, L = r.useCallback((e) => {
50
+ D.current = e, typeof T == "function" ? T(e) : T && (T.current = e);
51
+ }, [T]), R = (e) => {
52
+ if (x) return;
53
+ let t = h.findIndex((e) => e.id === P), n = t, r = t > 0 ? t - 1 : h.length - 1, i = t < h.length - 1 ? t + 1 : 0, a = l(D.current);
38
54
  switch (e.key) {
39
55
  case "ArrowLeft":
40
- e.preventDefault(), n = t > 0 ? t - 1 : m.length - 1;
56
+ e.preventDefault(), n = a ? i : r;
41
57
  break;
42
58
  case "ArrowRight":
43
- e.preventDefault(), n = t < m.length - 1 ? t + 1 : 0;
59
+ e.preventDefault(), n = a ? r : i;
44
60
  break;
45
61
  case "Home":
46
62
  e.preventDefault(), n = 0;
47
63
  break;
48
64
  case "End":
49
- e.preventDefault(), n = m.length - 1;
65
+ e.preventDefault(), n = h.length - 1;
50
66
  break;
51
67
  default: return;
52
68
  }
53
- g(m[n].id), requestAnimationFrame(() => {
54
- (S.current?.querySelectorAll("[role=\"radio\"]"))?.[n]?.focus();
69
+ F(h[n].id), requestAnimationFrame(() => {
70
+ (D.current?.querySelectorAll("[role=\"radio\"]"))?.[n]?.focus();
55
71
  });
56
- }, { button: T, icon: E } = c[f];
72
+ }, { button: z, icon: B } = u[p];
57
73
  return /* @__PURE__ */ i("div", {
58
- ref: C,
74
+ ref: L,
59
75
  role: "radiogroup",
60
76
  tabIndex: -1,
61
- "aria-label": y["aria-label"] ?? "Segmented control",
62
- onKeyDown: w,
63
- className: t("inline-flex p-[3px] rounded-pill", "bg-surface-raised-hover border border-surface-border-subtle shadow-inset", _ && "opacity-action-disabled pointer-events-none", v),
64
- ...y,
77
+ "aria-label": w["aria-label"] ?? "Segmented control",
78
+ onKeyDown: R,
79
+ className: t("p-ds-01 rounded-surface bg-segment-track", S ? "flex w-full" : "inline-flex w-fit", x && "opacity-action-disabled pointer-events-none", C),
80
+ ...w,
65
81
  children: /* @__PURE__ */ i(o, {
66
- id: x,
67
- children: m.map((r) => {
68
- let o = r.id === h;
82
+ id: E,
83
+ children: h.map((r) => {
84
+ let o = r.id === P;
69
85
  return /* @__PURE__ */ a("button", {
70
86
  type: "button",
71
87
  role: "radio",
72
88
  "aria-checked": o,
89
+ "aria-label": r.ariaLabel,
73
90
  tabIndex: o ? 0 : -1,
74
- disabled: _,
75
- onClick: () => g(r.id),
76
- className: t("relative inline-flex items-center justify-center gap-ds-02 rounded-pill", "font-medium transition-colors duration-fast-02 ease-productive-standard", "outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2", T, o ? u[p] : "text-surface-fg-muted hover:text-surface-fg"),
91
+ disabled: x,
92
+ onClick: () => F(r.id),
93
+ className: t("relative inline-flex items-center justify-center gap-ds-02 rounded-control", "font-medium outline-hidden", "touch-target", "transition-[color,transform] duration-fast-02 ease-productive-standard", "motion-safe:active:scale-[0.97]", "focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 focus-visible:ring-offset-transparent", z, S && "flex-1 min-w-16", o ? f[k] : "text-surface-fg-muted hover:text-surface-fg"),
77
94
  children: [
78
95
  o && /* @__PURE__ */ i(s.span, {
79
- layoutId: "segment-pill",
80
- className: t("absolute inset-0 rounded-pill pointer-events-none", l[p]),
81
- transition: d
96
+ layoutId: "segment-thumb",
97
+ initial: !1,
98
+ className: t("absolute inset-0 rounded-control pointer-events-none", d[k]),
99
+ transition: I
82
100
  }),
83
101
  r.icon && /* @__PURE__ */ i("span", {
84
- className: t("relative z-[1] shrink-0 inline-flex items-center justify-center", E),
102
+ className: t("relative shrink-0 inline-flex items-center justify-center", B),
85
103
  children: /* @__PURE__ */ i(e, {
86
- size: f === "lg" ? "sm" : "xs",
104
+ size: p === "lg" ? "sm" : "xs",
87
105
  children: n(r.icon)
88
106
  })
89
107
  }),
90
- /* @__PURE__ */ i("span", {
91
- className: "relative z-[1]",
108
+ r.text != null && /* @__PURE__ */ i("span", {
109
+ className: "relative",
92
110
  children: r.text
93
111
  })
94
112
  ]
@@ -97,6 +115,6 @@ var c = {
97
115
  })
98
116
  });
99
117
  });
100
- f.displayName = "SegmentedControl";
118
+ p.displayName = "SegmentedControl";
101
119
  //#endregion
102
- export { f as SegmentedControl };
120
+ export { p as SegmentedControl };
@@ -1 +1 @@
1
- {"version":3,"file":"segmented-control.js","names":[],"sources":["../../src/ui/segmented-control.tsx"],"sourcesContent":["'use client'\n\nimport { LayoutGroup,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { IconProvider } from './icon-context'\nimport type { IconInput } from './lib/icon-input'\nimport { normalizeIcon } from './lib/normalize-icon'\nimport { cn } from './lib/utils'\n\n/* ── Types ─────────────────────────────────────────────────── */\n\nexport type SegmentedControlSize = 'sm' | 'md' | 'lg'\nexport type SegmentedControlVariant = 'default' | 'solid'\n\nexport interface SegmentedControlOption {\n id: string\n text: string\n /** Optional icon rendered before the text label. Accepts any `IconInput`. */\n icon?: IconInput\n}\n\nexport interface SegmentedControlProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onSelect'> {\n size?: SegmentedControlSize\n variant?: SegmentedControlVariant\n options: SegmentedControlOption[]\n selectedId: string\n onSelect: (id: string) => void\n disabled?: boolean\n}\n\n/* ── Size config ───────────────────────────────────────────── */\n\nconst sizeConfig = {\n sm: { button: 'h-7 px-ds-04 text-body-sm', icon: 'h-3.5 w-3.5' },\n md: { button: 'h-8 px-ds-05 text-body-md', icon: 'h-4 w-4' },\n lg: { button: 'h-10 px-ds-06 text-body-md', icon: 'h-4 w-4' },\n} as const\n\n/* ── Pill styles per variant ───────────────────────────────── */\n\nconst pillStyles = {\n default: 'bg-surface-overlay shadow-raised',\n solid: 'bg-accent-9',\n} as const\n\nconst selectedTextStyles = {\n default: 'text-surface-fg',\n solid: 'text-accent-fg',\n} as const\n\n/* ── Spring config (snappy, minimal overshoot) ─────────────── */\n/* Intentionally softer than springs.snappy (500/30/0.5) for pill slide feel */\n\nconst pillSpring = { type: 'spring' as const, stiffness: 400, damping: 30 }\n\n/* ── SegmentedControl ──────────────────────────────────────── */\n\nconst SegmentedControl = React.forwardRef<HTMLDivElement, SegmentedControlProps>(\n function SegmentedControl(\n {\n size = 'md',\n variant = 'default',\n options,\n selectedId,\n onSelect,\n disabled = false,\n className,\n ...props\n },\n ref,\n ) {\n const instanceId = React.useId()\n const tablistRef = React.useRef<HTMLDivElement | null>(null)\n\n // Compose refs\n const mergedRef = React.useCallback(\n (node: HTMLDivElement | null) => {\n tablistRef.current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node\n },\n [ref],\n )\n\n // Keyboard navigation\n const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n if (disabled) return\n\n const currentIndex = options.findIndex((o) => o.id === selectedId)\n let nextIndex = currentIndex\n\n switch (e.key) {\n case 'ArrowLeft':\n e.preventDefault()\n nextIndex = currentIndex > 0 ? currentIndex - 1 : options.length - 1\n break\n case 'ArrowRight':\n e.preventDefault()\n nextIndex = currentIndex < options.length - 1 ? currentIndex + 1 : 0\n break\n case 'Home':\n e.preventDefault()\n nextIndex = 0\n break\n case 'End':\n e.preventDefault()\n nextIndex = options.length - 1\n break\n default:\n return\n }\n\n onSelect(options[nextIndex].id)\n requestAnimationFrame(() => {\n const buttons = tablistRef.current?.querySelectorAll<HTMLButtonElement>('[role=\"radio\"]')\n buttons?.[nextIndex]?.focus()\n })\n }\n\n const { button: buttonSize, icon: iconSize } = sizeConfig[size]\n\n return (\n <div\n ref={mergedRef}\n role=\"radiogroup\"\n tabIndex={-1}\n aria-label={props['aria-label'] ?? 'Segmented control'}\n onKeyDown={handleKeyDown}\n className={cn(\n 'inline-flex p-[3px] rounded-pill',\n 'bg-surface-raised-hover border border-surface-border-subtle shadow-inset',\n disabled && 'opacity-action-disabled pointer-events-none',\n className,\n )}\n {...props}\n >\n <LayoutGroup id={instanceId}>\n {options.map((option) => {\n const isSelected = option.id === selectedId\n\n return (\n <button\n key={option.id}\n type=\"button\"\n role=\"radio\"\n aria-checked={isSelected}\n tabIndex={isSelected ? 0 : -1}\n disabled={disabled}\n onClick={() => onSelect(option.id)}\n className={cn(\n 'relative inline-flex items-center justify-center gap-ds-02 rounded-pill',\n 'font-medium transition-colors duration-fast-02 ease-productive-standard',\n 'outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2',\n buttonSize,\n isSelected\n ? selectedTextStyles[variant]\n : 'text-surface-fg-muted hover:text-surface-fg',\n )}\n >\n {/* Sliding pill indicator */}\n {isSelected && (\n <motion.span\n layoutId=\"segment-pill\"\n className={cn(\n 'absolute inset-0 rounded-pill pointer-events-none',\n pillStyles[variant],\n )}\n transition={pillSpring}\n />\n )}\n\n {/* Content (above pill via z-index) */}\n {option.icon && (\n <span className={cn('relative z-[1] shrink-0 inline-flex items-center justify-center', iconSize)}>\n <IconProvider size={size === 'lg' ? 'sm' : 'xs'}>\n {normalizeIcon(option.icon)}\n </IconProvider>\n </span>\n )}\n <span className=\"relative z-[1]\">{option.text}</span>\n </button>\n )\n })}\n </LayoutGroup>\n </div>\n )\n },\n)\nSegmentedControl.displayName = 'SegmentedControl'\n\n/* ── Exports ───────────────────────────────────────────────── */\n\nexport { SegmentedControl }\n"],"mappings":";;;;;;;;AAiCA,IAAM,IAAa;CACjB,IAAI;EAAE,QAAQ;EAA6B,MAAM;CAAc;CAC/D,IAAI;EAAE,QAAQ;EAA6B,MAAM;CAAU;CAC3D,IAAI;EAAE,QAAQ;EAA8B,MAAM;CAAU;AAC9D,GAIM,IAAa;CACjB,SAAS;CACT,OAAO;AACT,GAEM,IAAqB;CACzB,SAAS;CACT,OAAO;AACT,GAKM,IAAa;CAAE,MAAM;CAAmB,WAAW;CAAK,SAAS;AAAG,GAIpE,IAAmB,EAAM,WAC7B,SACE,EACE,UAAO,MACP,aAAU,WACV,YACA,eACA,aACA,cAAW,IACX,cACA,GAAG,KAEL,GACA;CACA,IAAM,IAAa,EAAM,MAAM,GACzB,IAAa,EAAM,OAA8B,IAAI,GAGrD,IAAY,EAAM,aACrB,MAAgC;EAE/B,AADA,EAAW,UAAU,GACjB,OAAO,KAAQ,aAAY,EAAI,CAAI,IAC9B,MAAK,EAAuD,UAAU;CACjF,GACA,CAAC,CAAG,CACN,GAGM,KAAiB,MAA2C;EAChE,IAAI,GAAU;EAEd,IAAM,IAAe,EAAQ,WAAW,MAAM,EAAE,OAAO,CAAU,GAC7D,IAAY;EAEhB,QAAQ,EAAE,KAAV;GACE,KAAK;IAEH,AADA,EAAE,eAAe,GACjB,IAAY,IAAe,IAAI,IAAe,IAAI,EAAQ,SAAS;IACnE;GACF,KAAK;IAEH,AADA,EAAE,eAAe,GACjB,IAAY,IAAe,EAAQ,SAAS,IAAI,IAAe,IAAI;IACnE;GACF,KAAK;IAEH,AADA,EAAE,eAAe,GACjB,IAAY;IACZ;GACF,KAAK;IAEH,AADA,EAAE,eAAe,GACjB,IAAY,EAAQ,SAAS;IAC7B;GACF,SACE;EACJ;EAGA,AADA,EAAS,EAAQ,EAAU,CAAC,EAAE,GAC9B,4BAA4B;GAE1B,CADgB,EAAW,SAAS,iBAAoC,kBAAgB,EAAA,GAC9E,EAAU,EAAE,MAAM;EAC9B,CAAC;CACH,GAEM,EAAE,QAAQ,GAAY,MAAM,MAAa,EAAW;CAE1D,OACE,kBAAC,OAAD;EACE,KAAK;EACL,MAAK;EACL,UAAU;EACV,cAAY,EAAM,iBAAiB;EACnC,WAAW;EACX,WAAW,EACT,oCACA,4EACA,KAAY,+CACZ,CACF;EACA,GAAI;YAEJ,kBAAC,GAAD;GAAa,IAAI;aACd,EAAQ,KAAK,MAAW;IACvB,IAAM,IAAa,EAAO,OAAO;IAEjC,OACE,kBAAC,UAAD;KAEE,MAAK;KACL,MAAK;KACL,gBAAc;KACd,UAAU,IAAa,IAAI;KACjB;KACV,eAAe,EAAS,EAAO,EAAE;KACjC,WAAW,EACT,2EACA,2EACA,+FACA,GACA,IACI,EAAmB,KACnB,6CACN;eAhBF;MAmBG,KACC,kBAAC,EAAO,MAAR;OACE,UAAS;OACT,WAAW,EACT,qDACA,EAAW,EACb;OACA,YAAY;MACb,CAAA;MAIF,EAAO,QACN,kBAAC,QAAD;OAAM,WAAW,EAAG,mEAAmE,CAAQ;iBAC7F,kBAAC,GAAD;QAAc,MAAM,MAAS,OAAO,OAAO;kBACxC,EAAc,EAAO,IAAI;OACd,CAAA;MACV,CAAA;MAER,kBAAC,QAAD;OAAM,WAAU;iBAAkB,EAAO;MAAW,CAAA;KAC9C;OAtCD,EAAO,EAsCN;GAEZ,CAAC;EACU,CAAA;CACV,CAAA;AAET,CACF;AACA,EAAiB,cAAc"}
1
+ {"version":3,"file":"segmented-control.js","names":[],"sources":["../../src/ui/segmented-control.tsx"],"sourcesContent":["'use client'\n\nimport { LayoutGroup, motion, useReducedMotion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { IconProvider } from './icon-context'\nimport type { IconInput } from './lib/icon-input'\nimport { normalizeIcon } from './lib/normalize-icon'\nimport { cn } from './lib/utils'\n\n/* ── Types ─────────────────────────────────────────────────── */\n\nexport type SegmentedControlSize = 'sm' | 'md' | 'lg'\nexport type SegmentedControlVariant = 'soft' | 'solid'\n\n/** @deprecated `variant=\"default\"` was renamed to `\"soft\"`. Still accepted at\n * runtime (maps to `\"soft\"`); update call sites — the alias is removed in a\n * future major. */\ntype DeprecatedVariant = 'default'\n\nexport interface SegmentedControlOption {\n id: string\n /** Visible label. Accepts a `ReactNode` so an option can carry a count badge\n * or custom node, not just a string. Optional — omit for an icon-only\n * segment, in which case set `ariaLabel`. */\n text?: React.ReactNode\n /** Optional icon rendered before the text label. Accepts any `IconInput`. */\n icon?: IconInput\n /** Accessible name for the segment. Required for icon-only segments (no\n * `text`); otherwise the visible text provides the name. */\n ariaLabel?: string\n}\n\nexport interface SegmentedControlProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onSelect' | 'defaultValue'> {\n size?: SegmentedControlSize\n variant?: SegmentedControlVariant | DeprecatedVariant\n options: SegmentedControlOption[]\n /** Selected option id (controlled). Aligns with Tabs/ToggleGroup. */\n value?: string\n /** Initial selected id for uncontrolled mode. Ignored when `value` is set;\n * defaults to the first option. */\n defaultValue?: string\n /** Fires with the newly-selected id (both modes). */\n onValueChange?: (id: string) => void\n disabled?: boolean\n /** Fill the container: segments split the available width equally instead of\n * hugging their content. Use for full-width toggles and view switchers. */\n fullWidth?: boolean\n\n /** @deprecated Use `value`. Still accepted; kept for back-compat. */\n selectedId?: string\n /** @deprecated Use `onValueChange`. Still accepted; kept for back-compat. */\n onSelect?: (id: string) => void\n}\n\n/* ── RTL detection ─────────────────────────────────────────────\n * Reads the nearest `dir` attribute (testable, the common case), falling\n * back to the computed style for CSS-set direction. In RTL the horizontal\n * arrow keys invert so Arrow keys always track reading order. */\n\nfunction isRtl(el: HTMLElement | null): boolean {\n if (!el) return false\n const dirEl = el.closest('[dir]') as HTMLElement | null\n if (dirEl) return (dirEl.getAttribute('dir') ?? '').toLowerCase() === 'rtl'\n try {\n return getComputedStyle(el).direction === 'rtl'\n } catch {\n return false\n }\n}\n\n/* ── Size config ───────────────────────────────────────────── */\n\nconst sizeConfig = {\n sm: { button: 'h-7 px-ds-04 text-body-sm', icon: 'h-3.5 w-3.5' },\n md: { button: 'h-8 px-ds-05 text-body-md', icon: 'h-4 w-4' },\n lg: { button: 'h-10 px-ds-06 text-body-md', icon: 'h-4 w-4' },\n} as const\n\n/* ── Thumb (sliding indicator) styles per variant ──────────────\n * The track carries no border/inset (see tokens/semantic.css\n * --color-segment-track), so the thumb defines its own edge with a\n * single ring-less soft shadow (--shadow-segment). */\n\nconst thumbStyles = {\n soft: 'bg-segment-thumb shadow-segment',\n solid: 'bg-accent-9 shadow-segment',\n} as const\n\nconst selectedTextStyles = {\n soft: 'text-surface-fg',\n solid: 'text-accent-fg',\n} as const\n\n/* ── SegmentedControl ──────────────────────────────────────── */\n\nconst SegmentedControl = React.forwardRef<HTMLDivElement, SegmentedControlProps>(\n function SegmentedControl(\n {\n size = 'md',\n variant = 'soft',\n options,\n value,\n defaultValue,\n onValueChange,\n selectedId,\n onSelect,\n disabled = false,\n fullWidth = false,\n className,\n ...props\n },\n ref,\n ) {\n const instanceId = React.useId()\n const tablistRef = React.useRef<HTMLDivElement | null>(null)\n const reduceMotion = useReducedMotion()\n\n // Back-compat: 'default' was renamed to 'soft'. Map silently.\n const resolvedVariant: SegmentedControlVariant = variant === 'default' ? 'soft' : variant\n\n // Controlled/uncontrolled resolution. `value` is canonical; `selectedId` is\n // the deprecated alias. Controlled when either is provided; otherwise the\n // hook owns state, seeded from `defaultValue` (or the first option).\n const controlledValue = value ?? selectedId\n const isControlled = controlledValue !== undefined\n const [uncontrolled, setUncontrolled] = React.useState(defaultValue ?? options[0]?.id)\n const selected = isControlled ? controlledValue : uncontrolled\n\n const emit = React.useCallback(\n (id: string) => {\n if (!isControlled) setUncontrolled(id)\n ;(onValueChange ?? onSelect)?.(id)\n },\n [isControlled, onValueChange, onSelect],\n )\n\n // Thumb glide. This is a frequent, functional toggle, so the motion is\n // crisp and bounce-free (Apple spring notation: settles ~300ms, no\n // overshoot) rather than playful. Snaps instantly under reduced motion —\n // the slide is the only movement here.\n const thumbTransition = reduceMotion\n ? { duration: 0 }\n : { type: 'spring' as const, duration: 0.3, bounce: 0 }\n\n // Compose refs\n const mergedRef = React.useCallback(\n (node: HTMLDivElement | null) => {\n tablistRef.current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node\n },\n [ref],\n )\n\n // Keyboard navigation\n const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n if (disabled) return\n\n const currentIndex = options.findIndex((o) => o.id === selected)\n let nextIndex = currentIndex\n\n const prev = currentIndex > 0 ? currentIndex - 1 : options.length - 1\n const next = currentIndex < options.length - 1 ? currentIndex + 1 : 0\n const rtl = isRtl(tablistRef.current)\n\n switch (e.key) {\n case 'ArrowLeft':\n e.preventDefault()\n nextIndex = rtl ? next : prev\n break\n case 'ArrowRight':\n e.preventDefault()\n nextIndex = rtl ? prev : next\n break\n case 'Home':\n e.preventDefault()\n nextIndex = 0\n break\n case 'End':\n e.preventDefault()\n nextIndex = options.length - 1\n break\n default:\n return\n }\n\n emit(options[nextIndex].id)\n requestAnimationFrame(() => {\n const buttons = tablistRef.current?.querySelectorAll<HTMLButtonElement>('[role=\"radio\"]')\n buttons?.[nextIndex]?.focus()\n })\n }\n\n const { button: buttonSize, icon: iconSize } = sizeConfig[size]\n\n return (\n <div\n ref={mergedRef}\n role=\"radiogroup\"\n tabIndex={-1}\n aria-label={props['aria-label'] ?? 'Segmented control'}\n onKeyDown={handleKeyDown}\n className={cn(\n 'p-ds-01 rounded-surface bg-segment-track',\n fullWidth ? 'flex w-full' : 'inline-flex w-fit',\n disabled && 'opacity-action-disabled pointer-events-none',\n className,\n )}\n {...props}\n >\n <LayoutGroup id={instanceId}>\n {options.map((option) => {\n const isSelected = option.id === selected\n\n return (\n <button\n key={option.id}\n type=\"button\"\n role=\"radio\"\n aria-checked={isSelected}\n aria-label={option.ariaLabel}\n tabIndex={isSelected ? 0 : -1}\n disabled={disabled}\n onClick={() => emit(option.id)}\n className={cn(\n 'relative inline-flex items-center justify-center gap-ds-02 rounded-control',\n 'font-medium outline-hidden',\n // `touch-target` adds a 44px min hit area via ::before without\n // changing the visual (dense) height.\n 'touch-target',\n // Exact properties (not `all`); press-scale gives instant\n // tactile feedback, gated to motion-safe so reduced-motion\n // users keep only the color change.\n 'transition-[color,transform] duration-fast-02 ease-productive-standard',\n 'motion-safe:active:scale-[0.97]',\n 'focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 focus-visible:ring-offset-transparent',\n buttonSize,\n fullWidth && 'flex-1 min-w-16',\n isSelected\n ? selectedTextStyles[resolvedVariant]\n : 'text-surface-fg-muted hover:text-surface-fg',\n )}\n >\n {/* Sliding thumb indicator. Rendered before the content so the\n positioned content siblings paint above it (no z-index). */}\n {isSelected && (\n <motion.span\n layoutId=\"segment-thumb\"\n initial={false}\n className={cn(\n 'absolute inset-0 rounded-control pointer-events-none',\n thumbStyles[resolvedVariant],\n )}\n transition={thumbTransition}\n />\n )}\n\n {/* Content — `relative` so it paints over the thumb */}\n {option.icon && (\n <span className={cn('relative shrink-0 inline-flex items-center justify-center', iconSize)}>\n <IconProvider size={size === 'lg' ? 'sm' : 'xs'}>\n {normalizeIcon(option.icon)}\n </IconProvider>\n </span>\n )}\n {option.text != null && <span className=\"relative\">{option.text}</span>}\n </button>\n )\n })}\n </LayoutGroup>\n </div>\n )\n },\n)\nSegmentedControl.displayName = 'SegmentedControl'\n\n/* ── Exports ───────────────────────────────────────────────── */\n\nexport { SegmentedControl }\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"split-button.d.ts","sourceRoot":"","sources":["../../src/ui/split-button.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACjD,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAMzC,KAAK,kBAAkB,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;AACtD,KAAK,gBAAgB,GAAG,WAAW,CAAC,YAAY,CAAC,OAAO,cAAc,CAAC,CAAC,OAAO,CAAC,CAAC,CAAA;AACjF,KAAK,eAAe,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAA;AAE7E,MAAM,WAAW,gBAAgB;IAC/B,0CAA0C;IAC1C,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,yCAAyC;IACzC,OAAO,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAA;IACzD,2DAA2D;IAC3D,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACjC,6BAA6B;IAC7B,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,+DAA+D;IAC/D,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACtC,uBAAuB;IACvB,OAAO,CAAC,EAAE,kBAAkB,CAAA;IAC5B,wBAAwB;IACxB,KAAK,CAAC,EAAE,gBAAgB,CAAA;IACxB,oBAAoB;IACpB,IAAI,CAAC,EAAE,eAAe,CAAA;IACtB,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,yCAAyC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,mEAAmE;IACnE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,sEAAsE;IACtE,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC9B,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IAC9B,qEAAqE;IACrE,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,2DAA2D;IAC3D,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AA2FD,QAAA,MAAM,WAAW,yFAuIhB,CAAA;AAID,OAAO,EAAE,WAAW,EAAE,CAAA;AACtB,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAC,kBAAkB,EAAE,CAAA"}
1
+ {"version":3,"file":"split-button.d.ts","sourceRoot":"","sources":["../../src/ui/split-button.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACjD,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAMzC,KAAK,kBAAkB,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;AACtD,KAAK,gBAAgB,GAAG,WAAW,CAAC,YAAY,CAAC,OAAO,cAAc,CAAC,CAAC,OAAO,CAAC,CAAC,CAAA;AACjF,KAAK,eAAe,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAA;AAE7E,MAAM,WAAW,gBAAgB;IAC/B,0CAA0C;IAC1C,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,yCAAyC;IACzC,OAAO,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAA;IACzD,2DAA2D;IAC3D,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACjC,6BAA6B;IAC7B,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,+DAA+D;IAC/D,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACtC,uBAAuB;IACvB,OAAO,CAAC,EAAE,kBAAkB,CAAA;IAC5B,wBAAwB;IACxB,KAAK,CAAC,EAAE,gBAAgB,CAAA;IACxB,oBAAoB;IACpB,IAAI,CAAC,EAAE,eAAe,CAAA;IACtB,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,yCAAyC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,mEAAmE;IACnE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,sEAAsE;IACtE,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC9B,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IAC9B,qEAAqE;IACrE,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,2DAA2D;IAC3D,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AA8FD,QAAA,MAAM,WAAW,yFAuIhB,CAAA;AAID,OAAO,EAAE,WAAW,EAAE,CAAA;AACtB,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAC,kBAAkB,EAAE,CAAA"}
@@ -10,6 +10,7 @@ var c = {
10
10
  error: "bg-error-11/20",
11
11
  success: "bg-success-11/20",
12
12
  warning: "bg-warning-11/20",
13
+ info: "bg-info-11/20",
13
14
  neutral: "bg-neutral-8/30"
14
15
  },
15
16
  soft: {
@@ -17,6 +18,7 @@ var c = {
17
18
  error: "bg-error-6",
18
19
  success: "bg-success-6",
19
20
  warning: "bg-warning-6",
21
+ info: "bg-info-6",
20
22
  neutral: "bg-surface-border"
21
23
  },
22
24
  outline: {
@@ -24,6 +26,7 @@ var c = {
24
26
  error: "bg-error-7",
25
27
  success: "bg-success-7",
26
28
  warning: "bg-warning-7",
29
+ info: "bg-info-7",
27
30
  neutral: "bg-surface-border-strong"
28
31
  }
29
32
  }, l = {
@@ -69,6 +72,7 @@ function m(e, t) {
69
72
  error: "bg-error-9 text-error-fg hover:bg-error-10 active:bg-error-11",
70
73
  success: "bg-success-9 text-success-fg hover:bg-success-10 active:bg-success-11",
71
74
  warning: "bg-warning-9 text-warning-fg hover:bg-warning-10 active:bg-warning-11",
75
+ info: "bg-info-9 text-info-fg hover:bg-info-10 active:bg-info-11",
72
76
  neutral: "bg-neutral-5 text-surface-fg hover:bg-neutral-7 active:bg-neutral-8"
73
77
  },
74
78
  soft: {
@@ -76,6 +80,7 @@ function m(e, t) {
76
80
  error: "bg-error-3 text-error-11 hover:bg-error-4 active:bg-error-5",
77
81
  success: "bg-success-3 text-success-11 hover:bg-success-4 active:bg-success-5",
78
82
  warning: "bg-warning-3 text-warning-11 hover:bg-warning-4 active:bg-warning-5",
83
+ info: "bg-info-3 text-info-11 hover:bg-info-4 active:bg-info-5",
79
84
  neutral: "bg-surface-raised-hover text-surface-fg-muted hover:bg-surface-raised-active active:bg-neutral-5"
80
85
  },
81
86
  outline: {
@@ -83,6 +88,7 @@ function m(e, t) {
83
88
  error: "bg-transparent text-error-11 hover:bg-error-3 active:bg-error-4",
84
89
  success: "bg-transparent text-success-11 hover:bg-success-3 active:bg-success-4",
85
90
  warning: "bg-transparent text-warning-11 hover:bg-warning-3 active:bg-warning-4",
91
+ info: "bg-transparent text-info-11 hover:bg-info-3 active:bg-info-4",
86
92
  neutral: "bg-transparent text-surface-fg hover:bg-surface-raised-hover active:bg-surface-raised-active"
87
93
  }
88
94
  };
@@ -94,6 +100,7 @@ function h(e) {
94
100
  error: "border-error-7",
95
101
  success: "border-success-7",
96
102
  warning: "border-warning-7",
103
+ info: "border-info-7",
97
104
  neutral: "border-surface-border-strong"
98
105
  };
99
106
  return t[e] ?? t.accent;