@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.
- package/AGENTS.md +1 -1
- package/MIGRATION.md +6 -0
- package/dist/tokens/semantic.css +20 -1
- package/dist/ui/button-group.js +2 -0
- package/dist/ui/button-group.js.map +1 -1
- package/dist/ui/button-processing.d.ts.map +1 -1
- package/dist/ui/button-processing.js +1 -0
- package/dist/ui/button-processing.js.map +1 -1
- package/dist/ui/button.d.ts +3 -3
- package/dist/ui/button.d.ts.map +1 -1
- package/dist/ui/button.js +26 -0
- package/dist/ui/button.js.map +1 -1
- package/dist/ui/color-input.d.ts.map +1 -1
- package/dist/ui/color-input.js +82 -82
- package/dist/ui/color-input.js.map +1 -1
- package/dist/ui/icon.d.ts +2 -0
- package/dist/ui/icon.d.ts.map +1 -1
- package/dist/ui/icon.js +31 -26
- package/dist/ui/icon.js.map +1 -1
- package/dist/ui/search-input.d.ts.map +1 -1
- package/dist/ui/search-input.js +1 -0
- package/dist/ui/search-input.js.map +1 -1
- package/dist/ui/segmented-control.d.ts +28 -6
- package/dist/ui/segmented-control.d.ts.map +1 -1
- package/dist/ui/segmented-control.js +61 -43
- package/dist/ui/segmented-control.js.map +1 -1
- package/dist/ui/split-button.d.ts.map +1 -1
- package/dist/ui/split-button.js +7 -0
- package/dist/ui/split-button.js.map +1 -1
- package/docs/components/ui/button.md +2 -2
- package/docs/components/ui/segmented-control.md +31 -11
- package/docs/recipes/install-remix.md +3 -3
- package/docs/recipes/install-vite.md +3 -3
- package/docs/recipes/server-components.md +2 -2
- package/llms.txt +1 -1
- package/make-kit/foundations/color.md +20 -0
- package/make-kit/foundations/dark-mode.md +6 -9
- package/make-kit/foundations/icons.md +3 -3
- package/make-kit/setup.md +4 -4
- package/mcp-manifest.json +61 -10
- package/package.json +1 -1
- package/scripts/welcome.mjs +59 -1
- package/skill/SKILL.md +1 -1
- package/skill/references/components.md +1 -1
- package/skill/references/server-components.md +2 -2
- package/skill/references/setup-remix.md +3 -3
- 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 },
|
|
83
|
-
let
|
|
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[
|
|
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" && !
|
|
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:
|
|
122
|
-
width:
|
|
123
|
-
height:
|
|
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:
|
|
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
|
|
164
|
-
if (
|
|
165
|
-
ref:
|
|
166
|
-
size:
|
|
167
|
-
stroke:
|
|
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:
|
|
174
|
-
size:
|
|
175
|
-
stroke:
|
|
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
|
|
180
|
-
animate:
|
|
183
|
+
let M = A ?? {
|
|
184
|
+
animate: j,
|
|
181
185
|
transition: t.snappy
|
|
182
|
-
},
|
|
183
|
-
size:
|
|
184
|
-
stroke:
|
|
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:
|
|
190
|
-
stroke:
|
|
193
|
+
size: D,
|
|
194
|
+
stroke: O,
|
|
191
195
|
"aria-hidden": "true"
|
|
192
196
|
});
|
|
193
197
|
return /* @__PURE__ */ s(u.span, {
|
|
194
|
-
ref:
|
|
198
|
+
ref: C,
|
|
195
199
|
className: r("inline-flex", x),
|
|
196
|
-
|
|
197
|
-
|
|
200
|
+
style: S,
|
|
201
|
+
...M,
|
|
202
|
+
children: N
|
|
198
203
|
});
|
|
199
204
|
});
|
|
200
205
|
g.displayName = "Icon";
|
package/dist/ui/icon.js.map
CHANGED
|
@@ -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":"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"}
|
|
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,
|
|
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"}
|
package/dist/ui/search-input.js
CHANGED
|
@@ -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 = '
|
|
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
|
-
|
|
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
|
-
|
|
17
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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 =
|
|
56
|
+
e.preventDefault(), n = a ? i : r;
|
|
41
57
|
break;
|
|
42
58
|
case "ArrowRight":
|
|
43
|
-
e.preventDefault(), n =
|
|
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 =
|
|
65
|
+
e.preventDefault(), n = h.length - 1;
|
|
50
66
|
break;
|
|
51
67
|
default: return;
|
|
52
68
|
}
|
|
53
|
-
|
|
54
|
-
(
|
|
69
|
+
F(h[n].id), requestAnimationFrame(() => {
|
|
70
|
+
(D.current?.querySelectorAll("[role=\"radio\"]"))?.[n]?.focus();
|
|
55
71
|
});
|
|
56
|
-
}, { button:
|
|
72
|
+
}, { button: z, icon: B } = u[p];
|
|
57
73
|
return /* @__PURE__ */ i("div", {
|
|
58
|
-
ref:
|
|
74
|
+
ref: L,
|
|
59
75
|
role: "radiogroup",
|
|
60
76
|
tabIndex: -1,
|
|
61
|
-
"aria-label":
|
|
62
|
-
onKeyDown:
|
|
63
|
-
className: t("
|
|
64
|
-
...
|
|
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:
|
|
67
|
-
children:
|
|
68
|
-
let o = r.id ===
|
|
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: () =>
|
|
76
|
-
className: t("relative inline-flex items-center justify-center gap-ds-02 rounded-
|
|
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-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
|
102
|
+
className: t("relative shrink-0 inline-flex items-center justify-center", B),
|
|
85
103
|
children: /* @__PURE__ */ i(e, {
|
|
86
|
-
size:
|
|
104
|
+
size: p === "lg" ? "sm" : "xs",
|
|
87
105
|
children: n(r.icon)
|
|
88
106
|
})
|
|
89
107
|
}),
|
|
90
|
-
/* @__PURE__ */ i("span", {
|
|
91
|
-
className: "relative
|
|
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
|
-
|
|
118
|
+
p.displayName = "SegmentedControl";
|
|
101
119
|
//#endregion
|
|
102
|
-
export {
|
|
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;
|
|
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"}
|
package/dist/ui/split-button.js
CHANGED
|
@@ -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;
|