@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/AGENTS.md
CHANGED
|
@@ -20,7 +20,7 @@ If you are a human, read [README.md](./README.md) instead.
|
|
|
20
20
|
|
|
21
21
|
## How to get details, in priority order
|
|
22
22
|
|
|
23
|
-
1. **shilp-sutra MCP** — version-exact answers as JSON, cheapest on context. Hosted at `https://shilp-sutra.devalok.in/mcp` (
|
|
23
|
+
1. **shilp-sutra MCP** — version-exact answers as JSON, cheapest on context. Hosted at `https://shilp-sutra.devalok.in/mcp`. On install the package writes a project-scoped `.mcp.json` declaring this server, so your client (Claude Code / Cursor / Codex) should prompt you to approve it — approve it. If it is not wired yet, add it in one command: `claude mcp add --transport http shilp-sutra https://shilp-sutra.devalok.in/mcp` (or the equivalent in your agent). It beats reading the frozen docs in `node_modules`. The tool surface is listed under "MCP tools" below. Always pass the consumer's installed version (from `node_modules/@devalok/shilp-sutra/package.json`) as `version`.
|
|
24
24
|
2. **`packages/core/llms.txt`** — the ~3K-token router: what exists + where to get detail. Load this by default; it is deliberately tiny.
|
|
25
25
|
3. **`packages/core/docs/components/<tier>/<name>.md`** — single-component doc (~3K tokens: props, examples, composability, gotchas). Read ONLY the components you're using — never bulk-read the directory or concatenate these.
|
|
26
26
|
4. **`packages/core/mcp-manifest.json`** — everything machine-readable (props, tokens, composition; react-docgen shape). Prefer targeted reads over prose when you need structured data without the MCP.
|
package/MIGRATION.md
CHANGED
|
@@ -4,6 +4,12 @@ This page indexes all breaking changes across `@devalok/shilp-sutra` versions. F
|
|
|
4
4
|
|
|
5
5
|
> **Upgrading from < 0.36?** Start here, then read each intermediate version section. Breaking changes stack — skipping versions means stacking migrations.
|
|
6
6
|
|
|
7
|
+
## v0.52.0 — additive (no migration required)
|
|
8
|
+
|
|
9
|
+
Nothing breaks at the TypeScript level; this release is additive.
|
|
10
|
+
|
|
11
|
+
> Auto-stubbed by `version-packages` so the release audit's MIGRATION-section gate can't block. If any behavioral or visual change needs consumer action, replace this stub with the specifics on the Version Packages PR before publishing.
|
|
12
|
+
|
|
7
13
|
## v0.51.0 — additive (no migration required)
|
|
8
14
|
|
|
9
15
|
Nothing breaks at the TypeScript level; this release is additive.
|
package/dist/tokens/semantic.css
CHANGED
|
@@ -188,6 +188,14 @@
|
|
|
188
188
|
--color-surface-border-card: color-mix(in oklch, var(--neutral-5) 30%, transparent);
|
|
189
189
|
--color-backdrop: oklch(0 0 0 / 0.4);
|
|
190
190
|
|
|
191
|
+
/* Segmented control — translucent recess + raised thumb. Alpha, NOT a fixed
|
|
192
|
+
neutral tier: the track must read as a groove on ANY parent (page, card,
|
|
193
|
+
chrome), and a fixed lightness loses contrast against an unknown surface.
|
|
194
|
+
Elevation inverts in dark (see .dark override): recess in light, faint
|
|
195
|
+
lift in dark, thumb always a step toward the foreground. */
|
|
196
|
+
--color-segment-track: color-mix(in oklch, var(--neutral-12) 6%, transparent);
|
|
197
|
+
--color-segment-thumb: var(--neutral-1);
|
|
198
|
+
|
|
191
199
|
/* Overlay + disabled — promoted from internal :root into @theme so
|
|
192
200
|
`bg-overlay`, `text-overlay`, `bg-disabled`, `text-disabled` emit. */
|
|
193
201
|
--color-overlay: oklch(0 0 0 / 0.50);
|
|
@@ -374,8 +382,10 @@
|
|
|
374
382
|
--radius-overlay-sm rounded-overlay-sm Tooltip, Toast
|
|
375
383
|
--radius-overlay rounded-overlay Popover, HoverCard, DropdownMenu / ContextMenu / Menubar content, listboxes, NavigationMenu viewport
|
|
376
384
|
--radius-overlay-lg rounded-overlay-lg Dialog, AlertDialog, Sheet, BottomSheet, ColorInput picker
|
|
377
|
-
--radius-pill rounded-pill Badge, Dot, Radio, Switch, Slider, Progress, Avatar circle
|
|
385
|
+
--radius-pill rounded-pill Badge, Dot, Radio, Switch, Slider, Progress, Avatar circle
|
|
378
386
|
--radius-bubble rounded-bubble ChatMessage bubble
|
|
387
|
+
(SegmentedControl is a rounded-RECT, not a pill: track rounded-surface,
|
|
388
|
+
thumb rounded-control — thumb inner radius sits tighter inside the track.)
|
|
379
389
|
─────────────────────────────────────────────────────────── */
|
|
380
390
|
--radius-control: 6px;
|
|
381
391
|
--radius-control-inner: 2px;
|
|
@@ -418,6 +428,10 @@
|
|
|
418
428
|
─────────────────────────────────────────────────────────── */
|
|
419
429
|
--shadow-raised: var(--shadow-xs-internal);
|
|
420
430
|
--shadow-raised-hover: var(--shadow-sm-internal);
|
|
431
|
+
/* Segmented-control thumb — soft float, deliberately RING-LESS (the track
|
|
432
|
+
carries no border/inset, so the thumb defines its own edge with shadow
|
|
433
|
+
alone). Uses --shadow-color/strength so it deepens in dark (strength 2.5). */
|
|
434
|
+
--shadow-segment: 0 1px 2px -0.5px oklch(var(--shadow-color) / calc(0.10 * var(--shadow-strength))), 0 3px 8px -2px oklch(var(--shadow-color) / calc(0.09 * var(--shadow-strength)));
|
|
421
435
|
--shadow-floating: var(--shadow-md-internal);
|
|
422
436
|
--shadow-overlay: var(--shadow-lg-internal);
|
|
423
437
|
|
|
@@ -601,6 +615,11 @@
|
|
|
601
615
|
--color-surface-border-card: color-mix(in oklch, var(--neutral-6) 45%, transparent);
|
|
602
616
|
--color-backdrop: oklch(0 0 0 / 0.6);
|
|
603
617
|
|
|
618
|
+
/* Segmented control — inverted elevation in dark: track is a faint LIGHTER
|
|
619
|
+
fill (can't go darker than a near-black page), thumb steps lighter still. */
|
|
620
|
+
--color-segment-track: color-mix(in oklch, oklch(1 0 0) 7%, transparent);
|
|
621
|
+
--color-segment-thumb: var(--neutral-3);
|
|
622
|
+
|
|
604
623
|
/* Overlay */
|
|
605
624
|
--color-overlay: oklch(0 0 0 / 0.70);
|
|
606
625
|
|
package/dist/ui/button-group.js
CHANGED
|
@@ -49,6 +49,7 @@ function l(e, t) {
|
|
|
49
49
|
error: "bg-error-11/20",
|
|
50
50
|
success: "bg-success-11/20",
|
|
51
51
|
warning: "bg-warning-11/20",
|
|
52
|
+
info: "bg-info-11/20",
|
|
52
53
|
neutral: "bg-neutral-8/30"
|
|
53
54
|
};
|
|
54
55
|
return e[t ?? "accent"] ?? e.accent;
|
|
@@ -59,6 +60,7 @@ function l(e, t) {
|
|
|
59
60
|
error: "bg-error-5",
|
|
60
61
|
success: "bg-success-5",
|
|
61
62
|
warning: "bg-warning-5",
|
|
63
|
+
info: "bg-info-5",
|
|
62
64
|
neutral: "bg-neutral-4"
|
|
63
65
|
};
|
|
64
66
|
return e[t ?? "accent"] ?? e.accent;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button-group.js","names":[],"sources":["../../src/ui/button-group.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\n\nimport type { ButtonProps } from './button'\nimport { cn } from './lib/utils'\n\n// ── Context ─────────────────────────────────────────────────────\n\nexport type ButtonGroupPosition = 'first' | 'middle' | 'last' | 'only'\n\ninterface ButtonGroupContextValue {\n variant?: ButtonProps['variant']\n color?: ButtonProps['color']\n weight?: ButtonProps['weight']\n shape?: ButtonProps['shape']\n size?: ButtonProps['size']\n disabled?: boolean\n orientation?: 'horizontal' | 'vertical'\n attached?: boolean\n /** Internal: signals Button children to stretch to fill available width */\n _stretch?: boolean\n}\n\n// Separate context for position (changes per child) vs group settings (same for all)\ninterface ButtonGroupItemContext {\n position: ButtonGroupPosition\n}\n\nconst ButtonGroupContext = React.createContext<ButtonGroupContextValue>({})\nconst ButtonGroupItemContext = React.createContext<ButtonGroupItemContext | null>(null)\n\nexport function useButtonGroup() {\n return React.useContext(ButtonGroupContext)\n}\n\nexport function useButtonGroupItem() {\n return React.useContext(ButtonGroupItemContext)\n}\n\n// ── Radius helper (consumed by Button internally) ───────────────\n\n/**\n * Returns inline style for border-radius AND border-collapse based on position\n * in an attached ButtonGroup. Inner borders are removed (not overlapped) to\n * guarantee consistent 1px borders regardless of wrapper nesting.\n */\nexport function getGroupPositionStyle(\n position: ButtonGroupPosition,\n orientation: 'horizontal' | 'vertical',\n): React.CSSProperties | undefined {\n if (position === 'only') return undefined\n\n if (orientation === 'horizontal') {\n switch (position) {\n case 'first':\n return { borderTopRightRadius: 0, borderBottomRightRadius: 0, borderRightWidth: 0 }\n case 'middle':\n return { borderRadius: 0, borderRightWidth: 0 }\n case 'last':\n return { borderTopLeftRadius: 0, borderBottomLeftRadius: 0 }\n }\n } else {\n switch (position) {\n case 'first':\n return { borderBottomLeftRadius: 0, borderBottomRightRadius: 0, borderBottomWidth: 0 }\n case 'middle':\n return { borderRadius: 0, borderBottomWidth: 0 }\n case 'last':\n return { borderTopLeftRadius: 0, borderTopRightRadius: 0 }\n }\n }\n}\n\n// ── Props ───────────────────────────────────────────────────────\n\nexport interface ButtonGroupProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'color'> {\n /** Shared variant applied to all child Buttons (children can override). */\n variant?: ButtonProps['variant']\n /** Shared color applied to all child Buttons (children can override). */\n color?: ButtonProps['color']\n /** Shared weight applied to all child Buttons (children can override). */\n weight?: ButtonProps['weight']\n /** Shared shape applied to all child Buttons (children can override). */\n shape?: ButtonProps['shape']\n /** Shared size applied to all child Buttons (children can override). */\n size?: ButtonProps['size']\n /** Disable all child Buttons. */\n disabled?: boolean\n /** Layout direction. @default 'horizontal' */\n orientation?: 'horizontal' | 'vertical'\n /** Whether buttons are visually attached (merged) or spaced apart. @default true */\n attached?: boolean\n /** Stretch to full width of parent container. */\n fullWidth?: boolean\n}\n\n// ── Divider colors (for non-outline variants that lack visible borders) ──\n\nfunction getDividerColor(variant?: string, color?: string): string {\n if (variant === 'solid') {\n const map: Record<string, string> = {\n accent: 'bg-accent-11/20', error: 'bg-error-11/20', success: 'bg-success-11/20',\n warning: 'bg-warning-11/20', neutral: 'bg-neutral-8/30',\n }\n return map[color ?? 'accent'] ?? map.accent\n }\n if (variant === 'soft') {\n const map: Record<string, string> = {\n accent: 'bg-accent-5', error: 'bg-error-5', success: 'bg-success-5',\n warning: 'bg-warning-5', neutral: 'bg-neutral-4',\n }\n return map[color ?? 'accent'] ?? map.accent\n }\n // ghost, link, etc.\n return 'bg-surface-border'\n}\n\n// ── Component ───────────────────────────────────────────────────\n\nconst ButtonGroup = React.forwardRef<HTMLDivElement, ButtonGroupProps>(\n ({ className, variant, color, weight, shape, size, disabled, orientation = 'horizontal', attached = true, fullWidth, children, ...props }, ref) => {\n const shouldStretch = fullWidth || orientation === 'vertical'\n const groupValue = React.useMemo(\n () => ({ variant, color, weight, shape, size, disabled, orientation, attached, _stretch: shouldStretch }),\n [variant, color, weight, shape, size, disabled, orientation, attached, shouldStretch],\n )\n\n const validChildren = React.Children.toArray(children).filter(React.isValidElement)\n const total = validChildren.length\n\n function getPosition(index: number): ButtonGroupPosition {\n if (total === 1) return 'only'\n if (index === 0) return 'first'\n if (index === total - 1) return 'last'\n return 'middle'\n }\n\n return (\n <ButtonGroupContext.Provider value={groupValue}>\n <div\n ref={ref}\n role=\"group\"\n className={cn(\n 'inline-flex',\n fullWidth && 'w-full',\n fullWidth && orientation === 'horizontal' && '[&>*]:flex-1',\n orientation === 'horizontal' ? 'flex-row' : 'flex-col items-stretch',\n !attached && 'gap-ds-02',\n // Focus isolation — lift focused/hovered button above siblings\n '[&>*:focus-within]:z-10 [&>*:hover]:z-10',\n className,\n )}\n {...props}\n >\n {attached\n ? validChildren.map((child, i) => {\n const needsDivider = i > 0 && variant !== 'outline'\n return (\n <React.Fragment key={i}>\n {needsDivider && (\n <div\n aria-hidden\n className={cn(\n 'shrink-0 self-stretch',\n orientation === 'horizontal' ? 'w-px' : 'h-px',\n getDividerColor(variant ?? undefined, color ?? undefined),\n )}\n />\n )}\n <ButtonGroupItemContext.Provider value={{ position: getPosition(i) }}>\n {child}\n </ButtonGroupItemContext.Provider>\n </React.Fragment>\n )\n })\n : children}\n </div>\n </ButtonGroupContext.Provider>\n )\n },\n)\nButtonGroup.displayName = 'ButtonGroup'\n\nexport { ButtonGroup }\n"],"mappings":";;;;;AA6BA,IAAM,IAAqB,EAAM,cAAuC,CAAC,CAAC,GACpE,IAAyB,EAAM,cAA6C,IAAI;AAEtF,SAAgB,IAAiB;CAC/B,OAAO,EAAM,WAAW,CAAkB;AAC5C;AAEA,SAAgB,IAAqB;CACnC,OAAO,EAAM,WAAW,CAAsB;AAChD;AASA,SAAgB,EACd,GACA,GACiC;CAC7B,UAAa,QAEjB,IAAI,MAAgB,cAClB,QAAQ,GAAR;EACE,KAAK,SACH,OAAO;GAAE,sBAAsB;GAAG,yBAAyB;GAAG,kBAAkB;EAAE;EACpF,KAAK,UACH,OAAO;GAAE,cAAc;GAAG,kBAAkB;EAAE;EAChD,KAAK,QACH,OAAO;GAAE,qBAAqB;GAAG,wBAAwB;EAAE;CAC/D;MAEA,QAAQ,GAAR;EACE,KAAK,SACH,OAAO;GAAE,wBAAwB;GAAG,yBAAyB;GAAG,mBAAmB;EAAE;EACvF,KAAK,UACH,OAAO;GAAE,cAAc;GAAG,mBAAmB;EAAE;EACjD,KAAK,QACH,OAAO;GAAE,qBAAqB;GAAG,sBAAsB;EAAE;CAC7D;AAEJ;AA2BA,SAAS,EAAgB,GAAkB,GAAwB;CACjE,IAAI,MAAY,SAAS;EACvB,IAAM,IAA8B;GAClC,QAAQ;GAAmB,OAAO;GAAkB,SAAS;GAC7D,SAAS;GAAoB,SAAS;
|
|
1
|
+
{"version":3,"file":"button-group.js","names":[],"sources":["../../src/ui/button-group.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\n\nimport type { ButtonProps } from './button'\nimport { cn } from './lib/utils'\n\n// ── Context ─────────────────────────────────────────────────────\n\nexport type ButtonGroupPosition = 'first' | 'middle' | 'last' | 'only'\n\ninterface ButtonGroupContextValue {\n variant?: ButtonProps['variant']\n color?: ButtonProps['color']\n weight?: ButtonProps['weight']\n shape?: ButtonProps['shape']\n size?: ButtonProps['size']\n disabled?: boolean\n orientation?: 'horizontal' | 'vertical'\n attached?: boolean\n /** Internal: signals Button children to stretch to fill available width */\n _stretch?: boolean\n}\n\n// Separate context for position (changes per child) vs group settings (same for all)\ninterface ButtonGroupItemContext {\n position: ButtonGroupPosition\n}\n\nconst ButtonGroupContext = React.createContext<ButtonGroupContextValue>({})\nconst ButtonGroupItemContext = React.createContext<ButtonGroupItemContext | null>(null)\n\nexport function useButtonGroup() {\n return React.useContext(ButtonGroupContext)\n}\n\nexport function useButtonGroupItem() {\n return React.useContext(ButtonGroupItemContext)\n}\n\n// ── Radius helper (consumed by Button internally) ───────────────\n\n/**\n * Returns inline style for border-radius AND border-collapse based on position\n * in an attached ButtonGroup. Inner borders are removed (not overlapped) to\n * guarantee consistent 1px borders regardless of wrapper nesting.\n */\nexport function getGroupPositionStyle(\n position: ButtonGroupPosition,\n orientation: 'horizontal' | 'vertical',\n): React.CSSProperties | undefined {\n if (position === 'only') return undefined\n\n if (orientation === 'horizontal') {\n switch (position) {\n case 'first':\n return { borderTopRightRadius: 0, borderBottomRightRadius: 0, borderRightWidth: 0 }\n case 'middle':\n return { borderRadius: 0, borderRightWidth: 0 }\n case 'last':\n return { borderTopLeftRadius: 0, borderBottomLeftRadius: 0 }\n }\n } else {\n switch (position) {\n case 'first':\n return { borderBottomLeftRadius: 0, borderBottomRightRadius: 0, borderBottomWidth: 0 }\n case 'middle':\n return { borderRadius: 0, borderBottomWidth: 0 }\n case 'last':\n return { borderTopLeftRadius: 0, borderTopRightRadius: 0 }\n }\n }\n}\n\n// ── Props ───────────────────────────────────────────────────────\n\nexport interface ButtonGroupProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'color'> {\n /** Shared variant applied to all child Buttons (children can override). */\n variant?: ButtonProps['variant']\n /** Shared color applied to all child Buttons (children can override). */\n color?: ButtonProps['color']\n /** Shared weight applied to all child Buttons (children can override). */\n weight?: ButtonProps['weight']\n /** Shared shape applied to all child Buttons (children can override). */\n shape?: ButtonProps['shape']\n /** Shared size applied to all child Buttons (children can override). */\n size?: ButtonProps['size']\n /** Disable all child Buttons. */\n disabled?: boolean\n /** Layout direction. @default 'horizontal' */\n orientation?: 'horizontal' | 'vertical'\n /** Whether buttons are visually attached (merged) or spaced apart. @default true */\n attached?: boolean\n /** Stretch to full width of parent container. */\n fullWidth?: boolean\n}\n\n// ── Divider colors (for non-outline variants that lack visible borders) ──\n\nfunction getDividerColor(variant?: string, color?: string): string {\n if (variant === 'solid') {\n const map: Record<string, string> = {\n accent: 'bg-accent-11/20', error: 'bg-error-11/20', success: 'bg-success-11/20',\n warning: 'bg-warning-11/20', info: 'bg-info-11/20', neutral: 'bg-neutral-8/30',\n }\n return map[color ?? 'accent'] ?? map.accent\n }\n if (variant === 'soft') {\n const map: Record<string, string> = {\n accent: 'bg-accent-5', error: 'bg-error-5', success: 'bg-success-5',\n warning: 'bg-warning-5', info: 'bg-info-5', neutral: 'bg-neutral-4',\n }\n return map[color ?? 'accent'] ?? map.accent\n }\n // ghost, link, etc.\n return 'bg-surface-border'\n}\n\n// ── Component ───────────────────────────────────────────────────\n\nconst ButtonGroup = React.forwardRef<HTMLDivElement, ButtonGroupProps>(\n ({ className, variant, color, weight, shape, size, disabled, orientation = 'horizontal', attached = true, fullWidth, children, ...props }, ref) => {\n const shouldStretch = fullWidth || orientation === 'vertical'\n const groupValue = React.useMemo(\n () => ({ variant, color, weight, shape, size, disabled, orientation, attached, _stretch: shouldStretch }),\n [variant, color, weight, shape, size, disabled, orientation, attached, shouldStretch],\n )\n\n const validChildren = React.Children.toArray(children).filter(React.isValidElement)\n const total = validChildren.length\n\n function getPosition(index: number): ButtonGroupPosition {\n if (total === 1) return 'only'\n if (index === 0) return 'first'\n if (index === total - 1) return 'last'\n return 'middle'\n }\n\n return (\n <ButtonGroupContext.Provider value={groupValue}>\n <div\n ref={ref}\n role=\"group\"\n className={cn(\n 'inline-flex',\n fullWidth && 'w-full',\n fullWidth && orientation === 'horizontal' && '[&>*]:flex-1',\n orientation === 'horizontal' ? 'flex-row' : 'flex-col items-stretch',\n !attached && 'gap-ds-02',\n // Focus isolation — lift focused/hovered button above siblings\n '[&>*:focus-within]:z-10 [&>*:hover]:z-10',\n className,\n )}\n {...props}\n >\n {attached\n ? validChildren.map((child, i) => {\n const needsDivider = i > 0 && variant !== 'outline'\n return (\n <React.Fragment key={i}>\n {needsDivider && (\n <div\n aria-hidden\n className={cn(\n 'shrink-0 self-stretch',\n orientation === 'horizontal' ? 'w-px' : 'h-px',\n getDividerColor(variant ?? undefined, color ?? undefined),\n )}\n />\n )}\n <ButtonGroupItemContext.Provider value={{ position: getPosition(i) }}>\n {child}\n </ButtonGroupItemContext.Provider>\n </React.Fragment>\n )\n })\n : children}\n </div>\n </ButtonGroupContext.Provider>\n )\n },\n)\nButtonGroup.displayName = 'ButtonGroup'\n\nexport { ButtonGroup }\n"],"mappings":";;;;;AA6BA,IAAM,IAAqB,EAAM,cAAuC,CAAC,CAAC,GACpE,IAAyB,EAAM,cAA6C,IAAI;AAEtF,SAAgB,IAAiB;CAC/B,OAAO,EAAM,WAAW,CAAkB;AAC5C;AAEA,SAAgB,IAAqB;CACnC,OAAO,EAAM,WAAW,CAAsB;AAChD;AASA,SAAgB,EACd,GACA,GACiC;CAC7B,UAAa,QAEjB,IAAI,MAAgB,cAClB,QAAQ,GAAR;EACE,KAAK,SACH,OAAO;GAAE,sBAAsB;GAAG,yBAAyB;GAAG,kBAAkB;EAAE;EACpF,KAAK,UACH,OAAO;GAAE,cAAc;GAAG,kBAAkB;EAAE;EAChD,KAAK,QACH,OAAO;GAAE,qBAAqB;GAAG,wBAAwB;EAAE;CAC/D;MAEA,QAAQ,GAAR;EACE,KAAK,SACH,OAAO;GAAE,wBAAwB;GAAG,yBAAyB;GAAG,mBAAmB;EAAE;EACvF,KAAK,UACH,OAAO;GAAE,cAAc;GAAG,mBAAmB;EAAE;EACjD,KAAK,QACH,OAAO;GAAE,qBAAqB;GAAG,sBAAsB;EAAE;CAC7D;AAEJ;AA2BA,SAAS,EAAgB,GAAkB,GAAwB;CACjE,IAAI,MAAY,SAAS;EACvB,IAAM,IAA8B;GAClC,QAAQ;GAAmB,OAAO;GAAkB,SAAS;GAC7D,SAAS;GAAoB,MAAM;GAAiB,SAAS;EAC/D;EACA,OAAO,EAAI,KAAS,aAAa,EAAI;CACvC;CACA,IAAI,MAAY,QAAQ;EACtB,IAAM,IAA8B;GAClC,QAAQ;GAAe,OAAO;GAAc,SAAS;GACrD,SAAS;GAAgB,MAAM;GAAa,SAAS;EACvD;EACA,OAAO,EAAI,KAAS,aAAa,EAAI;CACvC;CAEA,OAAO;AACT;AAIA,IAAM,IAAc,EAAM,YACvB,EAAE,cAAW,YAAS,UAAO,WAAQ,UAAO,SAAM,aAAU,iBAAc,cAAc,cAAW,IAAM,cAAW,aAAU,GAAG,KAAS,MAAQ;CACjJ,IAAM,IAAgB,KAAa,MAAgB,YAC7C,IAAa,EAAM,eAChB;EAAE;EAAS;EAAO;EAAQ;EAAO;EAAM;EAAU;EAAa;EAAU,UAAU;CAAc,IACvG;EAAC;EAAS;EAAO;EAAQ;EAAO;EAAM;EAAU;EAAa;EAAU;CAAa,CACtF,GAEM,IAAgB,EAAM,SAAS,QAAQ,CAAQ,CAAC,CAAC,OAAO,EAAM,cAAc,GAC5E,IAAQ,EAAc;CAE5B,SAAS,EAAY,GAAoC;EAIvD,OAHI,MAAU,IAAU,SACpB,MAAU,IAAU,UACpB,MAAU,IAAQ,IAAU,SACzB;CACT;CAEA,OACE,kBAAC,EAAmB,UAApB;EAA6B,OAAO;YAClC,kBAAC,OAAD;GACO;GACL,MAAK;GACL,WAAW,EACT,eACA,KAAa,UACb,KAAa,MAAgB,gBAAgB,gBAC7C,MAAgB,eAAe,aAAa,0BAC5C,CAAC,KAAY,aAEb,4CACA,CACF;GACA,GAAI;aAEH,IACG,EAAc,KAAK,GAAO,MAAM;IAC9B,IAAM,IAAe,IAAI,KAAK,MAAY;IAC1C,OACE,kBAAC,EAAM,UAAP,EAAA,UAAA,CACG,KACC,kBAAC,OAAD;KACE,eAAA;KACA,WAAW,EACT,yBACA,MAAgB,eAAe,SAAS,QACxC,EAAgB,KAAW,KAAA,GAAW,KAAS,KAAA,CAAS,CAC1D;IACD,CAAA,GAEH,kBAAC,EAAuB,UAAxB;KAAiC,OAAO,EAAE,UAAU,EAAY,CAAC,EAAE;eAChE;IAC8B,CAAA,CACnB,EAAA,GAdK,CAcL;GAEpB,CAAC,IACD;EACD,CAAA;CACsB,CAAA;AAEjC,CACF;AACA,EAAY,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button-processing.d.ts","sourceRoot":"","sources":["../../src/ui/button-processing.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAM9B,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAA;AAE9D,UAAU,sBAAsB;IAC9B,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,eAAe,CAAA;IACtB,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAA;CACd;
|
|
1
|
+
{"version":3,"file":"button-processing.d.ts","sourceRoot":"","sources":["../../src/ui/button-processing.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAM9B,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAA;AAE9D,UAAU,sBAAsB;IAC9B,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,eAAe,CAAA;IACtB,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAA;CACd;AAyBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,sBAAsB,qBA0GjF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button-processing.js","names":[],"sources":["../../src/ui/button-processing.tsx"],"sourcesContent":["'use client'\n\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { durations } from './lib/motion'\n\n// ── Types ──────────────────────────────────────────────────────────\n\nexport type ProcessingSpeed = 'ambient' | 'working' | 'urgent'\n\ninterface ProcessingOverlayProps {\n active: boolean\n speed: ProcessingSpeed\n /** Resolved color name — maps to CSS token `--color-{name}-9` */\n color: string\n}\n\n// ── Color mapping ──────────────────────────────────────────────────\n\n// Use text-level color tokens (step 11) so ants match the button's text color.\n// This ensures visibility on all variants — especially neutral where step-9 is too close to the soft bg.\nconst COLOR_MAP: Record<string, string> = {\n accent: 'var(--color-accent-11)',\n error: 'var(--color-error-11)',\n success: 'var(--color-success-11)',\n warning: 'var(--color-warning-11)',\n neutral: 'var(--color-surface-fg)',\n}\n\n// ── Speed → duration (seconds) ─────────────────────────────────────\n\nconst SPEED_SECONDS: Record<ProcessingSpeed, number> = {\n ambient: 3,\n working: 2,\n urgent: 1,\n}\n\n// ── Component ──────────────────────────────────────────────────────\n\n/**\n * Internal overlay component for button processing state.\n * Renders marching ants (SVG dashed rect with animated stroke-dashoffset).\n * Not exported from the barrel — used only by Button.\n */\nexport function ProcessingOverlay({ active, speed, color }: ProcessingOverlayProps) {\n const solidColor = COLOR_MAP[color] ?? COLOR_MAP.accent\n const prefersReduced = useReducedMotion()\n const duration = SPEED_SECONDS[speed]\n const svgRef = React.useRef<SVGRectElement>(null)\n const [borderRadius, setBorderRadius] = React.useState(8)\n const [dashInfo, setDashInfo] = React.useState({ array: '8 6', cycle: 14 })\n // Explicit pixel dimensions of the button — drives BOTH the SVG box and the\n // rect geometry. Relying on CSS `w-full`/`calc(100% - 2px)` made the ants'\n // outline depend on the wrapper's rendered size, which can drift from the\n // button's own size during width transitions — leaving a visible gap\n // between the button edge and the ants.\n const [size, setSize] = React.useState<{ w: number; h: number } | null>(null)\n\n // Read the button's dimensions + border-radius, compute dash pattern\n React.useEffect(() => {\n const wrapper = svgRef.current?.closest('span')\n const btnEl = wrapper?.previousElementSibling as HTMLElement | null\n if (!btnEl) return\n\n const measure = () => {\n const style = getComputedStyle(btnEl)\n const rawR = parseFloat(style.borderRadius) || 8\n const btnW = btnEl.offsetWidth\n const btnH = btnEl.offsetHeight\n if (btnW === 0 || btnH === 0) return\n // Inset 1px so the 2px stroke (centered on the edge) lands exactly on\n // the button's visual border instead of outside it.\n const w = btnW - 2\n const h = btnH - 2\n // Cap radius at half the shorter dimension (pill buttons report 9999px)\n const r = Math.min(rawR, h / 2, w / 2)\n setBorderRadius(r)\n setSize({ w: btnW, h: btnH })\n\n // Perimeter of a rounded rect:\n // 4 straight edges + 4 quarter-circle arcs (= one full circle of radius r)\n const perimeter = 2 * (w - 2 * r) + 2 * (h - 2 * r) + 2 * Math.PI * r\n\n // Target: 8px dash, 6px gap — adjust gap so dashes fit evenly (no seam)\n const dashPx = 8\n const gapPx = 6\n const approxCycle = dashPx + gapPx\n const count = Math.round(perimeter / approxCycle)\n const adjustedGap = (perimeter - count * dashPx) / count\n const finalGap = Math.max(2, adjustedGap)\n setDashInfo({ array: `${dashPx} ${finalGap.toFixed(1)}`, cycle: dashPx + finalGap })\n }\n\n measure()\n // Keep the ants glued to the button when it resizes (text change,\n // async-feedback icon swap, layout animation settling).\n const ro = typeof ResizeObserver !== 'undefined' ? new ResizeObserver(measure) : null\n ro?.observe(btnEl)\n return () => ro?.disconnect()\n }, [active])\n\n // Render the overlay even before the first measurement settles — the\n // aria-hidden anchor must exist for discoverability, and the SVG harmlessly\n // renders at 0x0 until measurement lands a real size (one animation frame).\n const w = size?.w ?? 0\n const h = size?.h ?? 0\n\n return (\n <AnimatePresence>\n {active && (\n <motion.span\n key=\"processing-ants\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: durations.moderate01b }}\n aria-hidden=\"true\"\n className=\"absolute top-0 left-0 pointer-events-none\"\n style={{ width: w, height: h }}\n >\n <svg\n width={w}\n height={h}\n style={{ overflow: 'visible', display: 'block' }}\n >\n <motion.rect\n ref={svgRef}\n x={1}\n y={1}\n width={Math.max(0, w - 2)}\n height={Math.max(0, h - 2)}\n rx={borderRadius}\n ry={borderRadius}\n fill=\"none\"\n stroke={solidColor}\n strokeWidth={2}\n strokeDasharray={dashInfo.array}\n style={{ transition: 'stroke 0.3s ease' }}\n animate={prefersReduced ? {} : { strokeDashoffset: [0, -dashInfo.cycle] }}\n transition={prefersReduced ? {} : {\n duration,\n ease: 'linear',\n repeat: Infinity,\n }}\n />\n </svg>\n </motion.span>\n )}\n </AnimatePresence>\n )\n}\n\n// ── Reduced motion hook ────────────────────────────────────────────\n\nfunction useReducedMotion(): boolean {\n const [reduced, setReduced] = React.useState(() => {\n if (typeof window === 'undefined') return false\n return window.matchMedia('(prefers-reduced-motion: reduce)').matches\n })\n\n React.useEffect(() => {\n const mql = window.matchMedia('(prefers-reduced-motion: reduce)')\n const handler = (e: MediaQueryListEvent) => setReduced(e.matches)\n mql.addEventListener('change', handler)\n return () => mql.removeEventListener('change', handler)\n }, [])\n\n return reduced\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"button-processing.js","names":[],"sources":["../../src/ui/button-processing.tsx"],"sourcesContent":["'use client'\n\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { durations } from './lib/motion'\n\n// ── Types ──────────────────────────────────────────────────────────\n\nexport type ProcessingSpeed = 'ambient' | 'working' | 'urgent'\n\ninterface ProcessingOverlayProps {\n active: boolean\n speed: ProcessingSpeed\n /** Resolved color name — maps to CSS token `--color-{name}-9` */\n color: string\n}\n\n// ── Color mapping ──────────────────────────────────────────────────\n\n// Use text-level color tokens (step 11) so ants match the button's text color.\n// This ensures visibility on all variants — especially neutral where step-9 is too close to the soft bg.\nconst COLOR_MAP: Record<string, string> = {\n accent: 'var(--color-accent-11)',\n error: 'var(--color-error-11)',\n success: 'var(--color-success-11)',\n warning: 'var(--color-warning-11)',\n info: 'var(--color-info-11)',\n neutral: 'var(--color-surface-fg)',\n}\n\n// ── Speed → duration (seconds) ─────────────────────────────────────\n\nconst SPEED_SECONDS: Record<ProcessingSpeed, number> = {\n ambient: 3,\n working: 2,\n urgent: 1,\n}\n\n// ── Component ──────────────────────────────────────────────────────\n\n/**\n * Internal overlay component for button processing state.\n * Renders marching ants (SVG dashed rect with animated stroke-dashoffset).\n * Not exported from the barrel — used only by Button.\n */\nexport function ProcessingOverlay({ active, speed, color }: ProcessingOverlayProps) {\n const solidColor = COLOR_MAP[color] ?? COLOR_MAP.accent\n const prefersReduced = useReducedMotion()\n const duration = SPEED_SECONDS[speed]\n const svgRef = React.useRef<SVGRectElement>(null)\n const [borderRadius, setBorderRadius] = React.useState(8)\n const [dashInfo, setDashInfo] = React.useState({ array: '8 6', cycle: 14 })\n // Explicit pixel dimensions of the button — drives BOTH the SVG box and the\n // rect geometry. Relying on CSS `w-full`/`calc(100% - 2px)` made the ants'\n // outline depend on the wrapper's rendered size, which can drift from the\n // button's own size during width transitions — leaving a visible gap\n // between the button edge and the ants.\n const [size, setSize] = React.useState<{ w: number; h: number } | null>(null)\n\n // Read the button's dimensions + border-radius, compute dash pattern\n React.useEffect(() => {\n const wrapper = svgRef.current?.closest('span')\n const btnEl = wrapper?.previousElementSibling as HTMLElement | null\n if (!btnEl) return\n\n const measure = () => {\n const style = getComputedStyle(btnEl)\n const rawR = parseFloat(style.borderRadius) || 8\n const btnW = btnEl.offsetWidth\n const btnH = btnEl.offsetHeight\n if (btnW === 0 || btnH === 0) return\n // Inset 1px so the 2px stroke (centered on the edge) lands exactly on\n // the button's visual border instead of outside it.\n const w = btnW - 2\n const h = btnH - 2\n // Cap radius at half the shorter dimension (pill buttons report 9999px)\n const r = Math.min(rawR, h / 2, w / 2)\n setBorderRadius(r)\n setSize({ w: btnW, h: btnH })\n\n // Perimeter of a rounded rect:\n // 4 straight edges + 4 quarter-circle arcs (= one full circle of radius r)\n const perimeter = 2 * (w - 2 * r) + 2 * (h - 2 * r) + 2 * Math.PI * r\n\n // Target: 8px dash, 6px gap — adjust gap so dashes fit evenly (no seam)\n const dashPx = 8\n const gapPx = 6\n const approxCycle = dashPx + gapPx\n const count = Math.round(perimeter / approxCycle)\n const adjustedGap = (perimeter - count * dashPx) / count\n const finalGap = Math.max(2, adjustedGap)\n setDashInfo({ array: `${dashPx} ${finalGap.toFixed(1)}`, cycle: dashPx + finalGap })\n }\n\n measure()\n // Keep the ants glued to the button when it resizes (text change,\n // async-feedback icon swap, layout animation settling).\n const ro = typeof ResizeObserver !== 'undefined' ? new ResizeObserver(measure) : null\n ro?.observe(btnEl)\n return () => ro?.disconnect()\n }, [active])\n\n // Render the overlay even before the first measurement settles — the\n // aria-hidden anchor must exist for discoverability, and the SVG harmlessly\n // renders at 0x0 until measurement lands a real size (one animation frame).\n const w = size?.w ?? 0\n const h = size?.h ?? 0\n\n return (\n <AnimatePresence>\n {active && (\n <motion.span\n key=\"processing-ants\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: durations.moderate01b }}\n aria-hidden=\"true\"\n className=\"absolute top-0 left-0 pointer-events-none\"\n style={{ width: w, height: h }}\n >\n <svg\n width={w}\n height={h}\n style={{ overflow: 'visible', display: 'block' }}\n >\n <motion.rect\n ref={svgRef}\n x={1}\n y={1}\n width={Math.max(0, w - 2)}\n height={Math.max(0, h - 2)}\n rx={borderRadius}\n ry={borderRadius}\n fill=\"none\"\n stroke={solidColor}\n strokeWidth={2}\n strokeDasharray={dashInfo.array}\n style={{ transition: 'stroke 0.3s ease' }}\n animate={prefersReduced ? {} : { strokeDashoffset: [0, -dashInfo.cycle] }}\n transition={prefersReduced ? {} : {\n duration,\n ease: 'linear',\n repeat: Infinity,\n }}\n />\n </svg>\n </motion.span>\n )}\n </AnimatePresence>\n )\n}\n\n// ── Reduced motion hook ────────────────────────────────────────────\n\nfunction useReducedMotion(): boolean {\n const [reduced, setReduced] = React.useState(() => {\n if (typeof window === 'undefined') return false\n return window.matchMedia('(prefers-reduced-motion: reduce)').matches\n })\n\n React.useEffect(() => {\n const mql = window.matchMedia('(prefers-reduced-motion: reduce)')\n const handler = (e: MediaQueryListEvent) => setReduced(e.matches)\n mql.addEventListener('change', handler)\n return () => mql.removeEventListener('change', handler)\n }, [])\n\n return reduced\n}\n"],"mappings":"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"}
|
package/dist/ui/button.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { IconInput } from './lib/icon-input';
|
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
export declare const buttonVariants: (props?: ({
|
|
6
6
|
variant?: "link" | "outline" | "solid" | "soft" | "ghost" | null | undefined;
|
|
7
|
-
color?: "accent" | "success" | "error" | "warning" | "neutral" | null | undefined;
|
|
7
|
+
color?: "accent" | "success" | "error" | "info" | "warning" | "neutral" | null | undefined;
|
|
8
8
|
weight?: "normal" | "semibold" | null | undefined;
|
|
9
9
|
size?: "xs" | "sm" | "md" | "lg" | "icon" | "compact-xs" | "compact-sm" | "compact-md" | "icon-xs" | "icon-sm" | "icon-md" | "icon-lg" | null | undefined;
|
|
10
10
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
@@ -16,7 +16,7 @@ export declare const buttonVariants: (props?: ({
|
|
|
16
16
|
* - `variant` controls **visual style**: `"solid"` (default, filled) | `"soft"` (tinted bg) |
|
|
17
17
|
* `"outline"` (bordered) | `"ghost"` (transparent, for toolbars) | `"link"` (underline, inline)
|
|
18
18
|
* - `color` controls **semantic intent**: `"accent"` (default, brand) | `"error"` (destructive) |
|
|
19
|
-
* `"success"` | `"warning"` | `"neutral"` (subdued)
|
|
19
|
+
* `"success"` | `"warning"` | `"info"` | `"neutral"` (subdued)
|
|
20
20
|
*
|
|
21
21
|
* **Sizes:** `xs` | `sm` | `md` (default) | `lg` for text buttons;
|
|
22
22
|
* `compact-xs` | `compact-sm` | `compact-md` for height-less inline buttons;
|
|
@@ -92,7 +92,7 @@ export interface ButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonE
|
|
|
92
92
|
*/
|
|
93
93
|
processing?: boolean | 'ambient' | 'working' | 'urgent';
|
|
94
94
|
/** Override processing animation color. Defaults to button's own color. */
|
|
95
|
-
processingColor?: 'accent' | 'error' | 'success' | 'warning' | 'neutral';
|
|
95
|
+
processingColor?: 'accent' | 'error' | 'success' | 'warning' | 'info' | 'neutral';
|
|
96
96
|
/** Disable button during processing. Default: true. Set false for cancel-by-click patterns. */
|
|
97
97
|
processingDisabled?: boolean;
|
|
98
98
|
}
|
package/dist/ui/button.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../src/ui/button.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEjE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAQ9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAMjD,eAAO,MAAM,cAAc;;;;;
|
|
1
|
+
{"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../src/ui/button.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEjE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAQ9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAMjD,eAAO,MAAM,cAAc;;;;;8EAuF1B,CAAA;AAoDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,MAAM,WAAW,WACf,SAAQ,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC,EAClE,YAAY,CAAC,OAAO,cAAc,CAAC;IACrC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,2EAA2E;IAC3E,KAAK,CAAC,EAAE,SAAS,GAAG,MAAM,CAAA;IAC1B,sEAAsE;IACtE,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,qEAAqE;IACrE,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,8CAA8C;IAC9C,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,yFAAyF;IACzF,eAAe,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAA;IAC5C,sCAAsC;IACtC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC,iBAAiB,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACxE,oFAAoF;IACpF,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAE9B;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAA;IAEvD,2EAA2E;IAC3E,eAAe,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAA;IAEjF,+FAA+F;IAC/F,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B;AAED,QAAA,MAAM,MAAM,uFAgSX,CAAA;AAGD,OAAO,EAAE,MAAM,EAAE,CAAA"}
|
package/dist/ui/button.js
CHANGED
|
@@ -28,6 +28,7 @@ var u = r("relative inline-flex items-center justify-center whitespace-nowrap fo
|
|
|
28
28
|
error: "",
|
|
29
29
|
success: "",
|
|
30
30
|
warning: "",
|
|
31
|
+
info: "",
|
|
31
32
|
neutral: ""
|
|
32
33
|
},
|
|
33
34
|
weight: {
|
|
@@ -70,6 +71,11 @@ var u = r("relative inline-flex items-center justify-center whitespace-nowrap fo
|
|
|
70
71
|
color: "warning",
|
|
71
72
|
className: "bg-warning-9 text-warning-fg hover:bg-warning-10 shadow-raised"
|
|
72
73
|
},
|
|
74
|
+
{
|
|
75
|
+
variant: "solid",
|
|
76
|
+
color: "info",
|
|
77
|
+
className: "bg-info-9 text-info-fg hover:bg-info-10 shadow-raised"
|
|
78
|
+
},
|
|
73
79
|
{
|
|
74
80
|
variant: "solid",
|
|
75
81
|
color: "neutral",
|
|
@@ -95,6 +101,11 @@ var u = r("relative inline-flex items-center justify-center whitespace-nowrap fo
|
|
|
95
101
|
color: "warning",
|
|
96
102
|
className: "bg-warning-3 text-warning-11 hover:bg-warning-4 active:bg-warning-5"
|
|
97
103
|
},
|
|
104
|
+
{
|
|
105
|
+
variant: "soft",
|
|
106
|
+
color: "info",
|
|
107
|
+
className: "bg-info-3 text-info-11 hover:bg-info-4 active:bg-info-5"
|
|
108
|
+
},
|
|
98
109
|
{
|
|
99
110
|
variant: "soft",
|
|
100
111
|
color: "neutral",
|
|
@@ -120,6 +131,11 @@ var u = r("relative inline-flex items-center justify-center whitespace-nowrap fo
|
|
|
120
131
|
color: "warning",
|
|
121
132
|
className: "bg-transparent text-warning-11 border-warning-7 hover:bg-warning-3 active:bg-warning-4"
|
|
122
133
|
},
|
|
134
|
+
{
|
|
135
|
+
variant: "outline",
|
|
136
|
+
color: "info",
|
|
137
|
+
className: "bg-transparent text-info-11 border-info-7 hover:bg-info-3 active:bg-info-4"
|
|
138
|
+
},
|
|
123
139
|
{
|
|
124
140
|
variant: "outline",
|
|
125
141
|
color: "neutral",
|
|
@@ -145,6 +161,11 @@ var u = r("relative inline-flex items-center justify-center whitespace-nowrap fo
|
|
|
145
161
|
color: "warning",
|
|
146
162
|
className: "bg-transparent text-warning-11 hover:bg-warning-3 active:bg-warning-4"
|
|
147
163
|
},
|
|
164
|
+
{
|
|
165
|
+
variant: "ghost",
|
|
166
|
+
color: "info",
|
|
167
|
+
className: "bg-transparent text-info-11 hover:bg-info-3 active:bg-info-4"
|
|
168
|
+
},
|
|
148
169
|
{
|
|
149
170
|
variant: "ghost",
|
|
150
171
|
color: "neutral",
|
|
@@ -170,6 +191,11 @@ var u = r("relative inline-flex items-center justify-center whitespace-nowrap fo
|
|
|
170
191
|
color: "warning",
|
|
171
192
|
className: "text-warning-11"
|
|
172
193
|
},
|
|
194
|
+
{
|
|
195
|
+
variant: "link",
|
|
196
|
+
color: "info",
|
|
197
|
+
className: "text-info-11"
|
|
198
|
+
},
|
|
173
199
|
{
|
|
174
200
|
variant: "link",
|
|
175
201
|
color: "neutral",
|
package/dist/ui/button.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.js","names":[],"sources":["../../src/ui/button.tsx"],"sourcesContent":["'use client'\n\nimport { Slot, Slottable } from '@primitives/react-slot'\nimport { IconCheck, IconX } from '@tabler/icons-react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { AnimatePresence, motion, useReducedMotion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { getGroupPositionStyle,useButtonGroup, useButtonGroupItem } from './button-group'\nimport type { ProcessingSpeed } from './button-processing'\nimport { ProcessingOverlay } from './button-processing'\nimport { Icon } from './icon'\nimport type { IconSize } from './icon-context'\nimport { IconProvider } from './icon-context'\nimport type { IconInput } from './lib/icon-input'\nimport { motionProps,springs, tweens } from './lib/motion'\nimport { normalizeIcon } from './lib/normalize-icon'\nimport { cn } from './lib/utils'\nimport { Spinner } from './spinner'\n\nexport const buttonVariants = cva(\n 'relative inline-flex items-center justify-center whitespace-nowrap font-sans select-none overflow-hidden isolate focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-action-disabled disabled:cursor-not-allowed disabled:saturate-[0.3] [&>span:not([data-grain])]:relative [&>span:not([data-grain])]:z-[2]',\n {\n variants: {\n variant: {\n solid: '',\n soft: '',\n outline: 'border',\n ghost: '',\n link: 'underline-offset-4 hover:underline active:opacity-80',\n },\n color: {\n accent: '',\n error: '',\n success: '',\n warning: '',\n neutral: '',\n },\n weight: {\n semibold: 'font-semibold',\n normal: 'font-normal',\n },\n size: {\n xs: 'h-ds-xs-plus rounded-control px-ds-03 text-body-sm gap-ds-02',\n sm: 'h-ds-sm rounded-control px-ds-04 text-body-sm gap-ds-02b',\n md: 'h-ds-md rounded-control px-ds-05 text-body-md gap-ds-03',\n lg: 'h-ds-lg rounded-control px-ds-06 text-body-lg gap-2.5',\n 'compact-xs': 'rounded-control px-ds-02 py-[3px] text-body-sm gap-ds-02',\n 'compact-sm': 'rounded-control px-ds-03 py-[5px] text-body-sm gap-ds-02b',\n 'compact-md': 'rounded-control px-ds-04 py-ds-03 text-body-md gap-ds-03',\n icon: 'h-ds-md w-ds-md rounded-control',\n 'icon-xs': 'h-ds-xs-plus w-ds-xs-plus rounded-control',\n 'icon-sm': 'h-ds-sm w-ds-sm rounded-control',\n 'icon-md': 'h-ds-md w-ds-md rounded-control',\n 'icon-lg': 'h-ds-lg w-ds-lg rounded-control',\n },\n },\n compoundVariants: [\n // ============ SOLID ============ (tonal raised shadow; fill deepens on hover — no coloured bloom)\n { variant: 'solid', color: 'accent', className: 'bg-accent-9 text-accent-fg hover:bg-accent-10 shadow-raised' },\n { variant: 'solid', color: 'error', className: 'bg-error-9 text-error-fg hover:bg-error-10 shadow-raised' },\n { variant: 'solid', color: 'success', className: 'bg-success-9 text-success-fg hover:bg-success-10 shadow-raised' },\n { variant: 'solid', color: 'warning', className: 'bg-warning-9 text-warning-fg hover:bg-warning-10 shadow-raised' },\n { variant: 'solid', color: 'neutral', className: 'bg-neutral-5 text-surface-fg hover:bg-neutral-7 shadow-raised' },\n\n // ============ SOFT ============\n { variant: 'soft', color: 'accent', className: 'bg-accent-3 text-accent-11 hover:bg-accent-4 active:bg-accent-5' },\n { variant: 'soft', color: 'error', className: 'bg-error-3 text-error-11 hover:bg-error-4 active:bg-error-5' },\n { variant: 'soft', color: 'success', className: 'bg-success-3 text-success-11 hover:bg-success-4 active:bg-success-5' },\n { variant: 'soft', color: 'warning', className: 'bg-warning-3 text-warning-11 hover:bg-warning-4 active:bg-warning-5' },\n { variant: 'soft', color: 'neutral', className: 'bg-surface-raised-hover text-surface-fg-muted hover:bg-surface-raised-active active:bg-neutral-5' },\n\n // ============ OUTLINE ============\n { variant: 'outline', color: 'accent', className: 'bg-transparent text-accent-11 border-accent-7 hover:bg-accent-3 active:bg-accent-4' },\n { variant: 'outline', color: 'error', className: 'bg-transparent text-error-11 border-error-7 hover:bg-error-3 active:bg-error-4' },\n { variant: 'outline', color: 'success', className: 'bg-transparent text-success-11 border-success-7 hover:bg-success-3 active:bg-success-4' },\n { variant: 'outline', color: 'warning', className: 'bg-transparent text-warning-11 border-warning-7 hover:bg-warning-3 active:bg-warning-4' },\n { variant: 'outline', color: 'neutral', className: 'bg-transparent text-surface-fg border-surface-border-strong hover:bg-surface-raised-hover active:bg-surface-raised-active' },\n\n // ============ GHOST ============\n // ghost+accent uses neutral look for backward compat (most common ghost is toolbar/icon ghost)\n { variant: 'ghost', color: 'accent', className: 'bg-transparent text-surface-fg-muted hover:bg-surface-raised-hover hover:text-surface-fg active:bg-surface-raised-active' },\n { variant: 'ghost', color: 'error', className: 'bg-transparent text-error-11 hover:bg-error-3 active:bg-error-4' },\n { variant: 'ghost', color: 'success', className: 'bg-transparent text-success-11 hover:bg-success-3 active:bg-success-4' },\n { variant: 'ghost', color: 'warning', className: 'bg-transparent text-warning-11 hover:bg-warning-3 active:bg-warning-4' },\n { variant: 'ghost', color: 'neutral', className: 'bg-transparent text-surface-fg-muted hover:bg-surface-raised-hover hover:text-surface-fg active:bg-surface-raised-active' },\n\n // ============ LINK ============\n { variant: 'link', color: 'accent', className: 'text-accent-11' },\n { variant: 'link', color: 'error', className: 'text-error-11' },\n { variant: 'link', color: 'success', className: 'text-success-11' },\n { variant: 'link', color: 'warning', className: 'text-warning-11' },\n { variant: 'link', color: 'neutral', className: 'text-surface-fg-muted' },\n ],\n defaultVariants: {\n variant: 'solid',\n color: 'accent',\n weight: 'semibold',\n size: 'md',\n },\n },\n)\n\n/** Extra horizontal padding for pill shape — rounded ends eat into visual space */\nconst pillPaddingClass: Record<string, string> = {\n xs: 'px-ds-04',\n sm: 'px-ds-05',\n md: 'px-ds-06',\n lg: 'px-ds-07',\n 'compact-xs': 'px-ds-03',\n 'compact-sm': 'px-ds-04',\n 'compact-md': 'px-ds-05',\n}\n\n/** Negative margin to tighten icon side padding — pulls icon closer to button edge */\nconst iconInsetClass: Record<string, { start: string; end: string }> = {\n xs: { start: '-ml-0.5', end: '-mr-0.5' },\n sm: { start: '-ml-1', end: '-mr-1' },\n md: { start: '-ml-1.5', end: '-mr-1.5' },\n lg: { start: '-ml-2', end: '-mr-2' },\n 'compact-xs': { start: '-ml-0.5', end: '-mr-0.5' },\n 'compact-sm': { start: '-ml-0.5', end: '-mr-0.5' },\n 'compact-md': { start: '-ml-1', end: '-mr-1' },\n icon: { start: '', end: '' },\n 'icon-xs': { start: '', end: '' },\n 'icon-sm': { start: '', end: '' },\n 'icon-md': { start: '', end: '' },\n 'icon-lg': { start: '', end: '' },\n}\n\n/** Map button size to spinner size */\nconst BUTTON_TO_SPINNER_SIZE: Record<string, 'sm' | 'md'> = {\n xs: 'sm',\n sm: 'sm',\n md: 'sm',\n lg: 'md',\n 'compact-xs': 'sm',\n 'compact-sm': 'sm',\n 'compact-md': 'sm',\n icon: 'sm',\n 'icon-xs': 'sm',\n 'icon-sm': 'sm',\n 'icon-md': 'sm',\n 'icon-lg': 'md',\n}\n\n/** Map button size to Icon context size */\nconst BUTTON_TO_ICON_SIZE: Record<string, IconSize> = {\n xs: 'xs', sm: 'sm', md: 'md', lg: 'lg',\n 'compact-xs': 'xs', 'compact-sm': 'sm', 'compact-md': 'md',\n icon: 'md', 'icon-xs': 'xs', 'icon-sm': 'sm', 'icon-md': 'md', 'icon-lg': 'lg',\n}\n\n/**\n * Props for Button — the primary action component with a two-axis variant system,\n * multiple size options, icon slots, and a built-in loading state.\n *\n * **Two axes:**\n * - `variant` controls **visual style**: `\"solid\"` (default, filled) | `\"soft\"` (tinted bg) |\n * `\"outline\"` (bordered) | `\"ghost\"` (transparent, for toolbars) | `\"link\"` (underline, inline)\n * - `color` controls **semantic intent**: `\"accent\"` (default, brand) | `\"error\"` (destructive) |\n * `\"success\"` | `\"warning\"` | `\"neutral\"` (subdued)\n *\n * **Sizes:** `xs` | `sm` | `md` (default) | `lg` for text buttons;\n * `compact-xs` | `compact-sm` | `compact-md` for height-less inline buttons;\n * `icon` | `icon-xs` | `icon-sm` | `icon-md` | `icon-lg` for square icon-only buttons.\n *\n * **Shape:** `\"default\"` uses the `--radius-control` token; `\"pill\"` applies `rounded-pill` (fully-rounded).\n *\n * **Weight:** `\"semibold\"` (default) | `\"normal\"` for lighter labels.\n *\n * **Note:** `ghost` + `accent` renders with neutral styling for backward compat —\n * most ghost buttons are toolbar/icon actions where neutral is expected.\n *\n * **Loading:** When `loading={true}` the button is disabled and aria-busy is set.\n * Use `loadingPosition` to control where the spinner appears.\n *\n * @example\n * // Primary save action (default variant + color):\n * <Button onClick={handleSave}>Save changes</Button>\n *\n * @example\n * // Soft success feedback:\n * <Button variant=\"soft\" color=\"success\" startIcon={<Icon icon={IconCheck} />}>\n * Approved\n * </Button>\n *\n * @example\n * // Destructive delete with loading state:\n * <Button variant=\"solid\" color=\"error\" startIcon={<Icon icon={IconTrash} />} loading={isDeleting}>\n * Delete project\n * </Button>\n *\n * @example\n * // Compact pill tag:\n * <Button variant=\"soft\" color=\"warning\" size=\"compact-sm\" shape=\"pill\">\n * Overdue\n * </Button>\n *\n * @example\n * // Ghost toolbar action:\n * <Button variant=\"ghost\" size=\"sm\" startIcon={<Icon icon={IconEdit} />}>\n * Edit\n * </Button>\n */\nexport interface ButtonProps\n extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'color'>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean\n /** Button shape — 'pill' applies rounded-pill for chip/tag-like buttons */\n shape?: 'default' | 'pill'\n /** Icon element rendered before children — use <Icon icon={...} /> */\n startIcon?: IconInput\n /** Icon element rendered after children — use <Icon icon={...} /> */\n endIcon?: IconInput\n /** Show loading spinner and disable button */\n loading?: boolean\n /** Where to render the spinner: replaces startIcon, endIcon, or centers over children */\n loadingPosition?: 'start' | 'end' | 'center'\n /** Stretch to full width of parent */\n fullWidth?: boolean\n /**\n * Async click handler — auto-manages loading → success/error states.\n * When provided, the button shows a spinner while the promise is pending,\n * then briefly flashes green (success) or red (error) before reverting.\n * Overrides `onClick` and `loading` when active.\n */\n onClickAsync?: (e: React.MouseEvent<HTMLButtonElement>) => Promise<void>\n /** How long (ms) to show the success/error state before reverting. Default: 1500 */\n asyncFeedbackDuration?: number\n\n /**\n * Show processing animation — animated border/glow while content stays visible.\n * `true` = \"working\" speed. Semantic speeds:\n * - `\"ambient\"` (3s) — background sync, file upload\n * - `\"working\"` (2s) — standard API call, generation\n * - `\"urgent\"` (1s) — retry, nearly done\n */\n processing?: boolean | 'ambient' | 'working' | 'urgent'\n\n /** Override processing animation color. Defaults to button's own color. */\n processingColor?: 'accent' | 'error' | 'success' | 'warning' | 'neutral'\n\n /** Disable button during processing. Default: true. Set false for cancel-by-click patterns. */\n processingDisabled?: boolean\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n className,\n variant,\n color,\n weight,\n size,\n asChild = false,\n shape,\n startIcon,\n endIcon,\n loading: loadingProp = false,\n loadingPosition = 'start',\n fullWidth = false,\n disabled,\n onClick,\n onClickAsync,\n asyncFeedbackDuration = 1500,\n processing: processingProp,\n processingColor,\n processingDisabled = true,\n children,\n ...props\n },\n ref,\n ) => {\n const group = useButtonGroup()\n const groupItem = useButtonGroupItem()\n const resolvedVariant = variant ?? group.variant\n const resolvedColor = color ?? group.color\n const resolvedWeight = weight ?? group.weight ?? 'semibold'\n const resolvedShape = shape ?? group.shape ?? 'default'\n const resolvedSize = size ?? group.size ?? 'md'\n // ButtonGroup can propagate disabled to all children\n const resolvedDisabled = disabled ?? group.disabled\n\n // When inside an attached ButtonGroup, adjust border-radius based on position\n const groupRadiusStyle = groupItem && group.attached !== false\n ? getGroupPositionStyle(groupItem.position, group.orientation ?? 'horizontal')\n : undefined\n // Stretch to fill when inside a vertical or fullWidth ButtonGroup\n const groupStretch = group._stretch ?? false\n\n const prefersReduced = useReducedMotion()\n\n // Refs + effect for smooth width transitions (must be before any early returns)\n const wrapperRef = React.useRef<HTMLSpanElement>(null)\n const btnRef = React.useRef<HTMLButtonElement | null>(null)\n const rafRef = React.useRef<number | undefined>(undefined)\n\n \n React.useEffect(() => {\n const btn = btnRef.current\n const wrapper = wrapperRef.current\n if (!btn || !wrapper || fullWidth || groupStretch || prefersReduced) return\n\n rafRef.current = requestAnimationFrame(() => {\n wrapper.style.width = 'auto'\n const w = btn.offsetWidth\n if (w > 0) {\n wrapper.getBoundingClientRect()\n wrapper.style.width = `${w}px`\n }\n })\n return () => { if (rafRef.current) cancelAnimationFrame(rafRef.current) }\n }) // run on every render to catch content changes\n\n // Async state machine: idle → loading → success | error → idle\n type AsyncState = 'idle' | 'loading' | 'success' | 'error'\n const [asyncState, setAsyncState] = React.useState<AsyncState>('idle')\n const timeoutRef = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n const isMountedRef = React.useRef(true)\n\n React.useEffect(() => () => {\n isMountedRef.current = false\n clearTimeout(timeoutRef.current)\n }, [])\n\n const handleAsyncClick = (e: React.MouseEvent<HTMLButtonElement>) => {\n if (!onClickAsync || asyncState !== 'idle') return\n setAsyncState('loading')\n onClickAsync(e)\n .then(() => { if (isMountedRef.current) setAsyncState('success') })\n .catch(() => { if (isMountedRef.current) setAsyncState('error') })\n .finally(() => {\n if (isMountedRef.current) {\n timeoutRef.current = setTimeout(() => {\n if (isMountedRef.current) setAsyncState('idle')\n }, asyncFeedbackDuration)\n }\n })\n }\n\n const isAsync = !!onClickAsync\n const loading = isAsync ? asyncState === 'loading' : loadingProp\n const isAsyncFeedback = asyncState === 'success' || asyncState === 'error'\n\n // Processing: explicit prop takes priority, otherwise auto-processing during onClickAsync.\n // Processing is always OFF during async feedback (success/error) so the feedback colors show.\n const autoProcessing = isAsync && asyncState === 'loading' && !processingProp\n const processingSpeed: ProcessingSpeed | undefined = isAsyncFeedback ? undefined\n : processingProp === true ? 'working'\n : processingProp === false || !processingProp\n ? (autoProcessing ? 'working' : undefined)\n : processingProp\n const isProcessing = !!processingSpeed\n const resolvedProcessingColor = processingColor ?? resolvedColor ?? 'accent'\n\n if (asChild) {\n // Slot merges all props into the child element via cloneElement at runtime.\n // We cast here because SlotProps only types React.HTMLAttributes<HTMLElement>,\n // which excludes button-specific attrs like `disabled`.\n const slotProps = {\n className: cn(\n buttonVariants({ variant: resolvedVariant, color: resolvedColor, weight: resolvedWeight, size: resolvedSize }),\n resolvedShape === 'pill' && 'rounded-pill',\n resolvedShape === 'pill' && pillPaddingClass[resolvedSize],\n (fullWidth || groupStretch) && 'w-full',\n className,\n ),\n ref,\n disabled: resolvedDisabled || loading,\n 'aria-busy': loading || undefined,\n ...props,\n } as React.ComponentPropsWithRef<typeof Slot>\n return (\n <Slot {...slotProps}>\n <Slottable>{children}</Slottable>\n </Slot>\n )\n }\n\n const inset = iconInsetClass[resolvedSize] ?? { start: '', end: '' }\n // Dim icons on filled variants (Radix pattern — icon is decorative, text is primary)\n const dimIcon = resolvedVariant === 'solid' || resolvedVariant === 'soft' || resolvedVariant === 'outline'\n\n const renderStartSlot = () => {\n if (loading && loadingPosition === 'start') {\n return (\n <span className={cn('inline-flex shrink-0 items-center justify-center', startIcon && inset.start)}>\n <Spinner size={BUTTON_TO_SPINNER_SIZE[resolvedSize]} variant=\"bare\" />\n </span>\n )\n }\n if (startIcon) {\n return (\n <span className={cn('inline-flex shrink-0 items-center justify-center pointer-events-none', inset.start, dimIcon && 'opacity-90')}>\n {normalizeIcon(startIcon)}\n </span>\n )\n }\n return null\n }\n\n const renderEndSlot = () => {\n if (loading && loadingPosition === 'end') {\n return (\n <span className={cn('inline-flex shrink-0 items-center justify-center', endIcon && inset.end)}>\n <Spinner size={BUTTON_TO_SPINNER_SIZE[resolvedSize]} variant=\"bare\" />\n </span>\n )\n }\n if (endIcon) {\n return (\n <span className={cn('inline-flex shrink-0 items-center justify-center pointer-events-none', inset.end, dimIcon && 'opacity-90')}>\n {normalizeIcon(endIcon)}\n </span>\n )\n }\n return null\n }\n\n // Separate DevalokGrain elements (need to be direct button children for absolute positioning)\n // from text/other content (needs z-elevation above grain)\n const childArray = React.Children.toArray(children)\n const grainElements: React.ReactNode[] = []\n const contentElements: React.ReactNode[] = []\n childArray.forEach((child) => {\n if (React.isValidElement(child) && (child.type as { displayName?: string })?.displayName === 'DevalokGrain') {\n grainElements.push(child)\n } else {\n contentElements.push(child)\n }\n })\n\n const renderChildren = () => {\n if (loading && loadingPosition === 'center') {\n return (\n <span className=\"relative inline-flex items-center justify-center\">\n <span className=\"invisible\">{contentElements}</span>\n <span className=\"absolute inset-0 flex items-center justify-center\">\n <Spinner size={BUTTON_TO_SPINNER_SIZE[resolvedSize]} variant=\"bare\" />\n </span>\n </span>\n )\n }\n // Only wrap in z-elevated span when grain is present (to sit above grain layers).\n // Without grain, render children directly to preserve flex layout.\n if (grainElements.length > 0) {\n return <span>{contentElements}</span>\n }\n return <>{contentElements}</>\n }\n\n // Async feedback: override color to show green/red\n const feedbackColorClass = isAsyncFeedback\n ? asyncState === 'success'\n ? 'bg-success-9 text-success-fg border-transparent hover:bg-success-9'\n : 'bg-error-9 text-error-fg border-transparent hover:bg-error-9'\n : undefined\n\n // Async feedback icon — uses Icon's draw animation for pen-stroke effect\n const asyncFeedbackIcon = isAsyncFeedback ? (\n <span className={cn('inline-flex shrink-0 items-center justify-center pointer-events-none', startIcon && inset.start)}>\n <Icon icon={asyncState === 'success' ? IconCheck : IconX} animate=\"draw\" />\n </span>\n ) : null\n\n const iconSize = BUTTON_TO_ICON_SIZE[resolvedSize] ?? 'md'\n\n const buttonEl = (\n <motion.button\n {...motionProps(props)}\n style={{ ...props.style, ...groupRadiusStyle }}\n className={cn(\n buttonVariants({\n // When processing (and not showing async feedback), force soft variant so ants pop\n variant: isProcessing && !isAsyncFeedback && resolvedVariant !== 'ghost' && resolvedVariant !== 'outline' && resolvedVariant !== 'link' ? 'soft' : resolvedVariant,\n color: resolvedColor,\n weight: resolvedWeight,\n size: resolvedSize,\n }),\n resolvedShape === 'pill' && 'rounded-pill',\n resolvedShape === 'pill' && pillPaddingClass[resolvedSize],\n (fullWidth || groupStretch) && 'w-full',\n // Asymmetric timing: hover-out is slow+relaxed, hover-in is fast+snappy (applied via hover: override)\n !loading && !isAsyncFeedback && 'transition-[color,background-color,border-color,box-shadow,transform,filter] duration-moderate-01 ease-productive-exit',\n !loading && !isAsyncFeedback && 'hover:duration-fast-02 hover:ease-productive-entrance',\n !loading && !isAsyncFeedback && 'active:scale-[0.95] active:brightness-[0.92] active:saturate-[1.1] active:duration-[0ms]',\n feedbackColorClass,\n isAsyncFeedback && 'transition-colors duration-moderate-01 ease-productive-standard',\n isProcessing && processingDisabled && 'pointer-events-none cursor-default',\n className,\n )}\n ref={(el: HTMLButtonElement | null) => {\n btnRef.current = el\n if (typeof ref === 'function') ref(el)\n else if (ref) (ref as React.MutableRefObject<HTMLButtonElement | null>).current = el\n }}\n disabled={resolvedDisabled || (loading && !isProcessing)}\n aria-busy={loading || isProcessing || undefined}\n aria-disabled={isProcessing && processingDisabled || undefined}\n onClick={isAsync ? handleAsyncClick : (isProcessing && processingDisabled ? undefined : onClick)}\n >\n {/* Grain layers render as direct children (need absolute positioning) */}\n {grainElements}\n {/* Content renders above grain via z-[2] span wrapper */}\n {isAsyncFeedback ? asyncFeedbackIcon : renderStartSlot()}\n {isAsyncFeedback ? contentElements : renderChildren()}\n {isAsyncFeedback ? null : renderEndSlot()}\n </motion.button>\n )\n\n // Always wrap in a positioned container so the overlay can render\n // as a sibling OUTSIDE the button (not affected by disabled opacity).\n // Keeping the wrapper always-on prevents React unmount/remount on\n // processing toggle, enabling smooth CSS transitions between states.\n return (\n <IconProvider size={iconSize}>\n <span\n ref={wrapperRef}\n className={cn('relative inline-flex', fullWidth && 'w-full')}\n style={{\n ...(fullWidth || groupStretch || prefersReduced ? undefined : { transition: 'width 0.2s cubic-bezier(0.25, 0.1, 0.25, 1)' }),\n ...groupRadiusStyle,\n }}\n >\n {buttonEl}\n <ProcessingOverlay\n active={isProcessing}\n speed={processingSpeed ?? 'working'}\n color={resolvedProcessingColor}\n />\n </span>\n </IconProvider>\n )\n },\n)\nButton.displayName = 'Button'\n\nexport { Button }\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"button.js","names":[],"sources":["../../src/ui/button.tsx"],"sourcesContent":["'use client'\n\nimport { Slot, Slottable } from '@primitives/react-slot'\nimport { IconCheck, IconX } from '@tabler/icons-react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { AnimatePresence, motion, useReducedMotion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { getGroupPositionStyle,useButtonGroup, useButtonGroupItem } from './button-group'\nimport type { ProcessingSpeed } from './button-processing'\nimport { ProcessingOverlay } from './button-processing'\nimport { Icon } from './icon'\nimport type { IconSize } from './icon-context'\nimport { IconProvider } from './icon-context'\nimport type { IconInput } from './lib/icon-input'\nimport { motionProps,springs, tweens } from './lib/motion'\nimport { normalizeIcon } from './lib/normalize-icon'\nimport { cn } from './lib/utils'\nimport { Spinner } from './spinner'\n\nexport const buttonVariants = cva(\n 'relative inline-flex items-center justify-center whitespace-nowrap font-sans select-none overflow-hidden isolate focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-action-disabled disabled:cursor-not-allowed disabled:saturate-[0.3] [&>span:not([data-grain])]:relative [&>span:not([data-grain])]:z-[2]',\n {\n variants: {\n variant: {\n solid: '',\n soft: '',\n outline: 'border',\n ghost: '',\n link: 'underline-offset-4 hover:underline active:opacity-80',\n },\n color: {\n accent: '',\n error: '',\n success: '',\n warning: '',\n info: '',\n neutral: '',\n },\n weight: {\n semibold: 'font-semibold',\n normal: 'font-normal',\n },\n size: {\n xs: 'h-ds-xs-plus rounded-control px-ds-03 text-body-sm gap-ds-02',\n sm: 'h-ds-sm rounded-control px-ds-04 text-body-sm gap-ds-02b',\n md: 'h-ds-md rounded-control px-ds-05 text-body-md gap-ds-03',\n lg: 'h-ds-lg rounded-control px-ds-06 text-body-lg gap-2.5',\n 'compact-xs': 'rounded-control px-ds-02 py-[3px] text-body-sm gap-ds-02',\n 'compact-sm': 'rounded-control px-ds-03 py-[5px] text-body-sm gap-ds-02b',\n 'compact-md': 'rounded-control px-ds-04 py-ds-03 text-body-md gap-ds-03',\n icon: 'h-ds-md w-ds-md rounded-control',\n 'icon-xs': 'h-ds-xs-plus w-ds-xs-plus rounded-control',\n 'icon-sm': 'h-ds-sm w-ds-sm rounded-control',\n 'icon-md': 'h-ds-md w-ds-md rounded-control',\n 'icon-lg': 'h-ds-lg w-ds-lg rounded-control',\n },\n },\n compoundVariants: [\n // ============ SOLID ============ (tonal raised shadow; fill deepens on hover — no coloured bloom)\n { variant: 'solid', color: 'accent', className: 'bg-accent-9 text-accent-fg hover:bg-accent-10 shadow-raised' },\n { variant: 'solid', color: 'error', className: 'bg-error-9 text-error-fg hover:bg-error-10 shadow-raised' },\n { variant: 'solid', color: 'success', className: 'bg-success-9 text-success-fg hover:bg-success-10 shadow-raised' },\n { variant: 'solid', color: 'warning', className: 'bg-warning-9 text-warning-fg hover:bg-warning-10 shadow-raised' },\n { variant: 'solid', color: 'info', className: 'bg-info-9 text-info-fg hover:bg-info-10 shadow-raised' },\n { variant: 'solid', color: 'neutral', className: 'bg-neutral-5 text-surface-fg hover:bg-neutral-7 shadow-raised' },\n\n // ============ SOFT ============\n { variant: 'soft', color: 'accent', className: 'bg-accent-3 text-accent-11 hover:bg-accent-4 active:bg-accent-5' },\n { variant: 'soft', color: 'error', className: 'bg-error-3 text-error-11 hover:bg-error-4 active:bg-error-5' },\n { variant: 'soft', color: 'success', className: 'bg-success-3 text-success-11 hover:bg-success-4 active:bg-success-5' },\n { variant: 'soft', color: 'warning', className: 'bg-warning-3 text-warning-11 hover:bg-warning-4 active:bg-warning-5' },\n { variant: 'soft', color: 'info', className: 'bg-info-3 text-info-11 hover:bg-info-4 active:bg-info-5' },\n { variant: 'soft', color: 'neutral', className: 'bg-surface-raised-hover text-surface-fg-muted hover:bg-surface-raised-active active:bg-neutral-5' },\n\n // ============ OUTLINE ============\n { variant: 'outline', color: 'accent', className: 'bg-transparent text-accent-11 border-accent-7 hover:bg-accent-3 active:bg-accent-4' },\n { variant: 'outline', color: 'error', className: 'bg-transparent text-error-11 border-error-7 hover:bg-error-3 active:bg-error-4' },\n { variant: 'outline', color: 'success', className: 'bg-transparent text-success-11 border-success-7 hover:bg-success-3 active:bg-success-4' },\n { variant: 'outline', color: 'warning', className: 'bg-transparent text-warning-11 border-warning-7 hover:bg-warning-3 active:bg-warning-4' },\n { variant: 'outline', color: 'info', className: 'bg-transparent text-info-11 border-info-7 hover:bg-info-3 active:bg-info-4' },\n { variant: 'outline', color: 'neutral', className: 'bg-transparent text-surface-fg border-surface-border-strong hover:bg-surface-raised-hover active:bg-surface-raised-active' },\n\n // ============ GHOST ============\n // ghost+accent uses neutral look for backward compat (most common ghost is toolbar/icon ghost)\n { variant: 'ghost', color: 'accent', className: 'bg-transparent text-surface-fg-muted hover:bg-surface-raised-hover hover:text-surface-fg active:bg-surface-raised-active' },\n { variant: 'ghost', color: 'error', className: 'bg-transparent text-error-11 hover:bg-error-3 active:bg-error-4' },\n { variant: 'ghost', color: 'success', className: 'bg-transparent text-success-11 hover:bg-success-3 active:bg-success-4' },\n { variant: 'ghost', color: 'warning', className: 'bg-transparent text-warning-11 hover:bg-warning-3 active:bg-warning-4' },\n { variant: 'ghost', color: 'info', className: 'bg-transparent text-info-11 hover:bg-info-3 active:bg-info-4' },\n { variant: 'ghost', color: 'neutral', className: 'bg-transparent text-surface-fg-muted hover:bg-surface-raised-hover hover:text-surface-fg active:bg-surface-raised-active' },\n\n // ============ LINK ============\n { variant: 'link', color: 'accent', className: 'text-accent-11' },\n { variant: 'link', color: 'error', className: 'text-error-11' },\n { variant: 'link', color: 'success', className: 'text-success-11' },\n { variant: 'link', color: 'warning', className: 'text-warning-11' },\n { variant: 'link', color: 'info', className: 'text-info-11' },\n { variant: 'link', color: 'neutral', className: 'text-surface-fg-muted' },\n ],\n defaultVariants: {\n variant: 'solid',\n color: 'accent',\n weight: 'semibold',\n size: 'md',\n },\n },\n)\n\n/** Extra horizontal padding for pill shape — rounded ends eat into visual space */\nconst pillPaddingClass: Record<string, string> = {\n xs: 'px-ds-04',\n sm: 'px-ds-05',\n md: 'px-ds-06',\n lg: 'px-ds-07',\n 'compact-xs': 'px-ds-03',\n 'compact-sm': 'px-ds-04',\n 'compact-md': 'px-ds-05',\n}\n\n/** Negative margin to tighten icon side padding — pulls icon closer to button edge */\nconst iconInsetClass: Record<string, { start: string; end: string }> = {\n xs: { start: '-ml-0.5', end: '-mr-0.5' },\n sm: { start: '-ml-1', end: '-mr-1' },\n md: { start: '-ml-1.5', end: '-mr-1.5' },\n lg: { start: '-ml-2', end: '-mr-2' },\n 'compact-xs': { start: '-ml-0.5', end: '-mr-0.5' },\n 'compact-sm': { start: '-ml-0.5', end: '-mr-0.5' },\n 'compact-md': { start: '-ml-1', end: '-mr-1' },\n icon: { start: '', end: '' },\n 'icon-xs': { start: '', end: '' },\n 'icon-sm': { start: '', end: '' },\n 'icon-md': { start: '', end: '' },\n 'icon-lg': { start: '', end: '' },\n}\n\n/** Map button size to spinner size */\nconst BUTTON_TO_SPINNER_SIZE: Record<string, 'sm' | 'md'> = {\n xs: 'sm',\n sm: 'sm',\n md: 'sm',\n lg: 'md',\n 'compact-xs': 'sm',\n 'compact-sm': 'sm',\n 'compact-md': 'sm',\n icon: 'sm',\n 'icon-xs': 'sm',\n 'icon-sm': 'sm',\n 'icon-md': 'sm',\n 'icon-lg': 'md',\n}\n\n/** Map button size to Icon context size */\nconst BUTTON_TO_ICON_SIZE: Record<string, IconSize> = {\n xs: 'xs', sm: 'sm', md: 'md', lg: 'lg',\n 'compact-xs': 'xs', 'compact-sm': 'sm', 'compact-md': 'md',\n icon: 'md', 'icon-xs': 'xs', 'icon-sm': 'sm', 'icon-md': 'md', 'icon-lg': 'lg',\n}\n\n/**\n * Props for Button — the primary action component with a two-axis variant system,\n * multiple size options, icon slots, and a built-in loading state.\n *\n * **Two axes:**\n * - `variant` controls **visual style**: `\"solid\"` (default, filled) | `\"soft\"` (tinted bg) |\n * `\"outline\"` (bordered) | `\"ghost\"` (transparent, for toolbars) | `\"link\"` (underline, inline)\n * - `color` controls **semantic intent**: `\"accent\"` (default, brand) | `\"error\"` (destructive) |\n * `\"success\"` | `\"warning\"` | `\"info\"` | `\"neutral\"` (subdued)\n *\n * **Sizes:** `xs` | `sm` | `md` (default) | `lg` for text buttons;\n * `compact-xs` | `compact-sm` | `compact-md` for height-less inline buttons;\n * `icon` | `icon-xs` | `icon-sm` | `icon-md` | `icon-lg` for square icon-only buttons.\n *\n * **Shape:** `\"default\"` uses the `--radius-control` token; `\"pill\"` applies `rounded-pill` (fully-rounded).\n *\n * **Weight:** `\"semibold\"` (default) | `\"normal\"` for lighter labels.\n *\n * **Note:** `ghost` + `accent` renders with neutral styling for backward compat —\n * most ghost buttons are toolbar/icon actions where neutral is expected.\n *\n * **Loading:** When `loading={true}` the button is disabled and aria-busy is set.\n * Use `loadingPosition` to control where the spinner appears.\n *\n * @example\n * // Primary save action (default variant + color):\n * <Button onClick={handleSave}>Save changes</Button>\n *\n * @example\n * // Soft success feedback:\n * <Button variant=\"soft\" color=\"success\" startIcon={<Icon icon={IconCheck} />}>\n * Approved\n * </Button>\n *\n * @example\n * // Destructive delete with loading state:\n * <Button variant=\"solid\" color=\"error\" startIcon={<Icon icon={IconTrash} />} loading={isDeleting}>\n * Delete project\n * </Button>\n *\n * @example\n * // Compact pill tag:\n * <Button variant=\"soft\" color=\"warning\" size=\"compact-sm\" shape=\"pill\">\n * Overdue\n * </Button>\n *\n * @example\n * // Ghost toolbar action:\n * <Button variant=\"ghost\" size=\"sm\" startIcon={<Icon icon={IconEdit} />}>\n * Edit\n * </Button>\n */\nexport interface ButtonProps\n extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'color'>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean\n /** Button shape — 'pill' applies rounded-pill for chip/tag-like buttons */\n shape?: 'default' | 'pill'\n /** Icon element rendered before children — use <Icon icon={...} /> */\n startIcon?: IconInput\n /** Icon element rendered after children — use <Icon icon={...} /> */\n endIcon?: IconInput\n /** Show loading spinner and disable button */\n loading?: boolean\n /** Where to render the spinner: replaces startIcon, endIcon, or centers over children */\n loadingPosition?: 'start' | 'end' | 'center'\n /** Stretch to full width of parent */\n fullWidth?: boolean\n /**\n * Async click handler — auto-manages loading → success/error states.\n * When provided, the button shows a spinner while the promise is pending,\n * then briefly flashes green (success) or red (error) before reverting.\n * Overrides `onClick` and `loading` when active.\n */\n onClickAsync?: (e: React.MouseEvent<HTMLButtonElement>) => Promise<void>\n /** How long (ms) to show the success/error state before reverting. Default: 1500 */\n asyncFeedbackDuration?: number\n\n /**\n * Show processing animation — animated border/glow while content stays visible.\n * `true` = \"working\" speed. Semantic speeds:\n * - `\"ambient\"` (3s) — background sync, file upload\n * - `\"working\"` (2s) — standard API call, generation\n * - `\"urgent\"` (1s) — retry, nearly done\n */\n processing?: boolean | 'ambient' | 'working' | 'urgent'\n\n /** Override processing animation color. Defaults to button's own color. */\n processingColor?: 'accent' | 'error' | 'success' | 'warning' | 'info' | 'neutral'\n\n /** Disable button during processing. Default: true. Set false for cancel-by-click patterns. */\n processingDisabled?: boolean\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n className,\n variant,\n color,\n weight,\n size,\n asChild = false,\n shape,\n startIcon,\n endIcon,\n loading: loadingProp = false,\n loadingPosition = 'start',\n fullWidth = false,\n disabled,\n onClick,\n onClickAsync,\n asyncFeedbackDuration = 1500,\n processing: processingProp,\n processingColor,\n processingDisabled = true,\n children,\n ...props\n },\n ref,\n ) => {\n const group = useButtonGroup()\n const groupItem = useButtonGroupItem()\n const resolvedVariant = variant ?? group.variant\n const resolvedColor = color ?? group.color\n const resolvedWeight = weight ?? group.weight ?? 'semibold'\n const resolvedShape = shape ?? group.shape ?? 'default'\n const resolvedSize = size ?? group.size ?? 'md'\n // ButtonGroup can propagate disabled to all children\n const resolvedDisabled = disabled ?? group.disabled\n\n // When inside an attached ButtonGroup, adjust border-radius based on position\n const groupRadiusStyle = groupItem && group.attached !== false\n ? getGroupPositionStyle(groupItem.position, group.orientation ?? 'horizontal')\n : undefined\n // Stretch to fill when inside a vertical or fullWidth ButtonGroup\n const groupStretch = group._stretch ?? false\n\n const prefersReduced = useReducedMotion()\n\n // Refs + effect for smooth width transitions (must be before any early returns)\n const wrapperRef = React.useRef<HTMLSpanElement>(null)\n const btnRef = React.useRef<HTMLButtonElement | null>(null)\n const rafRef = React.useRef<number | undefined>(undefined)\n\n \n React.useEffect(() => {\n const btn = btnRef.current\n const wrapper = wrapperRef.current\n if (!btn || !wrapper || fullWidth || groupStretch || prefersReduced) return\n\n rafRef.current = requestAnimationFrame(() => {\n wrapper.style.width = 'auto'\n const w = btn.offsetWidth\n if (w > 0) {\n wrapper.getBoundingClientRect()\n wrapper.style.width = `${w}px`\n }\n })\n return () => { if (rafRef.current) cancelAnimationFrame(rafRef.current) }\n }) // run on every render to catch content changes\n\n // Async state machine: idle → loading → success | error → idle\n type AsyncState = 'idle' | 'loading' | 'success' | 'error'\n const [asyncState, setAsyncState] = React.useState<AsyncState>('idle')\n const timeoutRef = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n const isMountedRef = React.useRef(true)\n\n React.useEffect(() => () => {\n isMountedRef.current = false\n clearTimeout(timeoutRef.current)\n }, [])\n\n const handleAsyncClick = (e: React.MouseEvent<HTMLButtonElement>) => {\n if (!onClickAsync || asyncState !== 'idle') return\n setAsyncState('loading')\n onClickAsync(e)\n .then(() => { if (isMountedRef.current) setAsyncState('success') })\n .catch(() => { if (isMountedRef.current) setAsyncState('error') })\n .finally(() => {\n if (isMountedRef.current) {\n timeoutRef.current = setTimeout(() => {\n if (isMountedRef.current) setAsyncState('idle')\n }, asyncFeedbackDuration)\n }\n })\n }\n\n const isAsync = !!onClickAsync\n const loading = isAsync ? asyncState === 'loading' : loadingProp\n const isAsyncFeedback = asyncState === 'success' || asyncState === 'error'\n\n // Processing: explicit prop takes priority, otherwise auto-processing during onClickAsync.\n // Processing is always OFF during async feedback (success/error) so the feedback colors show.\n const autoProcessing = isAsync && asyncState === 'loading' && !processingProp\n const processingSpeed: ProcessingSpeed | undefined = isAsyncFeedback ? undefined\n : processingProp === true ? 'working'\n : processingProp === false || !processingProp\n ? (autoProcessing ? 'working' : undefined)\n : processingProp\n const isProcessing = !!processingSpeed\n const resolvedProcessingColor = processingColor ?? resolvedColor ?? 'accent'\n\n if (asChild) {\n // Slot merges all props into the child element via cloneElement at runtime.\n // We cast here because SlotProps only types React.HTMLAttributes<HTMLElement>,\n // which excludes button-specific attrs like `disabled`.\n const slotProps = {\n className: cn(\n buttonVariants({ variant: resolvedVariant, color: resolvedColor, weight: resolvedWeight, size: resolvedSize }),\n resolvedShape === 'pill' && 'rounded-pill',\n resolvedShape === 'pill' && pillPaddingClass[resolvedSize],\n (fullWidth || groupStretch) && 'w-full',\n className,\n ),\n ref,\n disabled: resolvedDisabled || loading,\n 'aria-busy': loading || undefined,\n ...props,\n } as React.ComponentPropsWithRef<typeof Slot>\n return (\n <Slot {...slotProps}>\n <Slottable>{children}</Slottable>\n </Slot>\n )\n }\n\n const inset = iconInsetClass[resolvedSize] ?? { start: '', end: '' }\n // Dim icons on filled variants (Radix pattern — icon is decorative, text is primary)\n const dimIcon = resolvedVariant === 'solid' || resolvedVariant === 'soft' || resolvedVariant === 'outline'\n\n const renderStartSlot = () => {\n if (loading && loadingPosition === 'start') {\n return (\n <span className={cn('inline-flex shrink-0 items-center justify-center', startIcon && inset.start)}>\n <Spinner size={BUTTON_TO_SPINNER_SIZE[resolvedSize]} variant=\"bare\" />\n </span>\n )\n }\n if (startIcon) {\n return (\n <span className={cn('inline-flex shrink-0 items-center justify-center pointer-events-none', inset.start, dimIcon && 'opacity-90')}>\n {normalizeIcon(startIcon)}\n </span>\n )\n }\n return null\n }\n\n const renderEndSlot = () => {\n if (loading && loadingPosition === 'end') {\n return (\n <span className={cn('inline-flex shrink-0 items-center justify-center', endIcon && inset.end)}>\n <Spinner size={BUTTON_TO_SPINNER_SIZE[resolvedSize]} variant=\"bare\" />\n </span>\n )\n }\n if (endIcon) {\n return (\n <span className={cn('inline-flex shrink-0 items-center justify-center pointer-events-none', inset.end, dimIcon && 'opacity-90')}>\n {normalizeIcon(endIcon)}\n </span>\n )\n }\n return null\n }\n\n // Separate DevalokGrain elements (need to be direct button children for absolute positioning)\n // from text/other content (needs z-elevation above grain)\n const childArray = React.Children.toArray(children)\n const grainElements: React.ReactNode[] = []\n const contentElements: React.ReactNode[] = []\n childArray.forEach((child) => {\n if (React.isValidElement(child) && (child.type as { displayName?: string })?.displayName === 'DevalokGrain') {\n grainElements.push(child)\n } else {\n contentElements.push(child)\n }\n })\n\n const renderChildren = () => {\n if (loading && loadingPosition === 'center') {\n return (\n <span className=\"relative inline-flex items-center justify-center\">\n <span className=\"invisible\">{contentElements}</span>\n <span className=\"absolute inset-0 flex items-center justify-center\">\n <Spinner size={BUTTON_TO_SPINNER_SIZE[resolvedSize]} variant=\"bare\" />\n </span>\n </span>\n )\n }\n // Only wrap in z-elevated span when grain is present (to sit above grain layers).\n // Without grain, render children directly to preserve flex layout.\n if (grainElements.length > 0) {\n return <span>{contentElements}</span>\n }\n return <>{contentElements}</>\n }\n\n // Async feedback: override color to show green/red\n const feedbackColorClass = isAsyncFeedback\n ? asyncState === 'success'\n ? 'bg-success-9 text-success-fg border-transparent hover:bg-success-9'\n : 'bg-error-9 text-error-fg border-transparent hover:bg-error-9'\n : undefined\n\n // Async feedback icon — uses Icon's draw animation for pen-stroke effect\n const asyncFeedbackIcon = isAsyncFeedback ? (\n <span className={cn('inline-flex shrink-0 items-center justify-center pointer-events-none', startIcon && inset.start)}>\n <Icon icon={asyncState === 'success' ? IconCheck : IconX} animate=\"draw\" />\n </span>\n ) : null\n\n const iconSize = BUTTON_TO_ICON_SIZE[resolvedSize] ?? 'md'\n\n const buttonEl = (\n <motion.button\n {...motionProps(props)}\n style={{ ...props.style, ...groupRadiusStyle }}\n className={cn(\n buttonVariants({\n // When processing (and not showing async feedback), force soft variant so ants pop\n variant: isProcessing && !isAsyncFeedback && resolvedVariant !== 'ghost' && resolvedVariant !== 'outline' && resolvedVariant !== 'link' ? 'soft' : resolvedVariant,\n color: resolvedColor,\n weight: resolvedWeight,\n size: resolvedSize,\n }),\n resolvedShape === 'pill' && 'rounded-pill',\n resolvedShape === 'pill' && pillPaddingClass[resolvedSize],\n (fullWidth || groupStretch) && 'w-full',\n // Asymmetric timing: hover-out is slow+relaxed, hover-in is fast+snappy (applied via hover: override)\n !loading && !isAsyncFeedback && 'transition-[color,background-color,border-color,box-shadow,transform,filter] duration-moderate-01 ease-productive-exit',\n !loading && !isAsyncFeedback && 'hover:duration-fast-02 hover:ease-productive-entrance',\n !loading && !isAsyncFeedback && 'active:scale-[0.95] active:brightness-[0.92] active:saturate-[1.1] active:duration-[0ms]',\n feedbackColorClass,\n isAsyncFeedback && 'transition-colors duration-moderate-01 ease-productive-standard',\n isProcessing && processingDisabled && 'pointer-events-none cursor-default',\n className,\n )}\n ref={(el: HTMLButtonElement | null) => {\n btnRef.current = el\n if (typeof ref === 'function') ref(el)\n else if (ref) (ref as React.MutableRefObject<HTMLButtonElement | null>).current = el\n }}\n disabled={resolvedDisabled || (loading && !isProcessing)}\n aria-busy={loading || isProcessing || undefined}\n aria-disabled={isProcessing && processingDisabled || undefined}\n onClick={isAsync ? handleAsyncClick : (isProcessing && processingDisabled ? undefined : onClick)}\n >\n {/* Grain layers render as direct children (need absolute positioning) */}\n {grainElements}\n {/* Content renders above grain via z-[2] span wrapper */}\n {isAsyncFeedback ? asyncFeedbackIcon : renderStartSlot()}\n {isAsyncFeedback ? contentElements : renderChildren()}\n {isAsyncFeedback ? null : renderEndSlot()}\n </motion.button>\n )\n\n // Always wrap in a positioned container so the overlay can render\n // as a sibling OUTSIDE the button (not affected by disabled opacity).\n // Keeping the wrapper always-on prevents React unmount/remount on\n // processing toggle, enabling smooth CSS transitions between states.\n return (\n <IconProvider size={iconSize}>\n <span\n ref={wrapperRef}\n className={cn('relative inline-flex', fullWidth && 'w-full')}\n style={{\n ...(fullWidth || groupStretch || prefersReduced ? undefined : { transition: 'width 0.2s cubic-bezier(0.25, 0.1, 0.25, 1)' }),\n ...groupRadiusStyle,\n }}\n >\n {buttonEl}\n <ProcessingOverlay\n active={isProcessing}\n speed={processingSpeed ?? 'working'}\n color={resolvedProcessingColor}\n />\n </span>\n </IconProvider>\n )\n },\n)\nButton.displayName = 'Button'\n\nexport { Button }\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"color-input.d.ts","sourceRoot":"","sources":["../../src/ui/color-input.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAoF9B,KAAK,WAAW,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAA;AAIxC,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC;IAC7F,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oCAAoC;IACpC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,gFAAgF;IAChF,OAAO,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,GAAG,MAAM,EAAE,GAAG,KAAK,CAAA;IAC7D,oCAAoC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,wDAAwD;IACxD,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,2DAA2D;IAC3D,aAAa,CAAC,EAAE,WAAW,CAAA;IAC3B,2CAA2C;IAC3C,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAA;IAClC;;;;OAIG;IACH,OAAO,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;CAC/B;AAuDD,QAAA,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"color-input.d.ts","sourceRoot":"","sources":["../../src/ui/color-input.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAoF9B,KAAK,WAAW,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAA;AAIxC,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC;IAC7F,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oCAAoC;IACpC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,gFAAgF;IAChF,OAAO,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,GAAG,MAAM,EAAE,GAAG,KAAK,CAAA;IAC7D,oCAAoC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,wDAAwD;IACxD,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,2DAA2D;IAC3D,aAAa,CAAC,EAAE,WAAW,CAAA;IAC3B,2CAA2C;IAC3C,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAA;IAClC;;;;OAIG;IACH,OAAO,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;CAC/B;AAuDD,QAAA,MAAM,UAAU,wFA2Yf,CAAA;AAGD,OAAO,EAAE,UAAU,EAAE,CAAA"}
|