@devalok/shilp-sutra 0.59.0 → 0.60.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/MIGRATION.md +72 -0
- package/dist/composed/avatar-group.js +3 -3
- package/dist/composed/avatar-group.js.map +1 -1
- package/dist/composed/bulk-action-bar.d.ts +9 -35
- package/dist/composed/bulk-action-bar.d.ts.map +1 -1
- package/dist/composed/bulk-action-bar.js +2 -139
- package/dist/composed/index.js +6 -5
- package/dist/composed/master-detail.d.ts.map +1 -1
- package/dist/composed/master-detail.js +1 -1
- package/dist/composed/master-detail.js.map +1 -1
- package/dist/composed/multi-select-popover.d.ts.map +1 -1
- package/dist/composed/multi-select-popover.js +1 -1
- package/dist/composed/multi-select-popover.js.map +1 -1
- package/dist/tokens/semantic.css +85 -3
- package/dist/ui/avatar.d.ts.map +1 -1
- package/dist/ui/avatar.js +3 -3
- package/dist/ui/avatar.js.map +1 -1
- package/dist/ui/bulk-action-bar.d.ts +59 -0
- package/dist/ui/bulk-action-bar.d.ts.map +1 -0
- package/dist/ui/bulk-action-bar.js +145 -0
- package/dist/ui/bulk-action-bar.js.map +1 -0
- package/dist/ui/checkbox.d.ts.map +1 -1
- package/dist/ui/checkbox.js +1 -1
- package/dist/ui/checkbox.js.map +1 -1
- package/dist/ui/combobox.d.ts.map +1 -1
- package/dist/ui/combobox.js +10 -10
- package/dist/ui/combobox.js.map +1 -1
- package/dist/ui/data-table-body.d.ts.map +1 -1
- package/dist/ui/data-table-body.js +2 -1
- package/dist/ui/data-table-body.js.map +1 -1
- package/dist/ui/data-table-bulk-actions.d.ts +26 -1
- package/dist/ui/data-table-bulk-actions.d.ts.map +1 -1
- package/dist/ui/data-table-bulk-actions.js +21 -46
- package/dist/ui/data-table-bulk-actions.js.map +1 -1
- package/dist/ui/data-table-context.d.ts +36 -2
- package/dist/ui/data-table-context.d.ts.map +1 -1
- package/dist/ui/data-table-context.js +25 -21
- package/dist/ui/data-table-context.js.map +1 -1
- package/dist/ui/data-table-header.d.ts.map +1 -1
- package/dist/ui/data-table-header.js +2 -1
- package/dist/ui/data-table-header.js.map +1 -1
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +58 -57
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/input.d.ts.map +1 -1
- package/dist/ui/input.js +6 -6
- package/dist/ui/input.js.map +1 -1
- package/dist/ui/radio.d.ts.map +1 -1
- package/dist/ui/radio.js +4 -4
- package/dist/ui/radio.js.map +1 -1
- package/dist/ui/select.d.ts.map +1 -1
- package/dist/ui/select.js +6 -6
- package/dist/ui/select.js.map +1 -1
- package/dist/ui/switch.d.ts.map +1 -1
- package/dist/ui/switch.js +2 -2
- package/dist/ui/switch.js.map +1 -1
- package/dist/ui/table.d.ts.map +1 -1
- package/dist/ui/table.js +1 -1
- package/dist/ui/table.js.map +1 -1
- package/dist/ui/textarea.d.ts.map +1 -1
- package/dist/ui/textarea.js +6 -6
- package/dist/ui/textarea.js.map +1 -1
- package/dist/ui/toast.d.ts.map +1 -1
- package/dist/ui/toast.js +1 -1
- package/dist/ui/toast.js.map +1 -1
- package/docs/components/composed/bulk-action-bar.md +14 -7
- package/docs/components/ui/bulk-action-bar.md +72 -0
- package/llms.txt +2 -2
- package/make-kit/foundations/color.md +3 -1
- package/mcp-manifest.json +178 -150
- package/package.json +6 -1
- package/skill/SKILL.md +1 -1
- package/skill/references/components.md +2 -2
- package/dist/composed/bulk-action-bar.js.map +0 -1
package/dist/ui/radio.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"radio.js","names":[],"sources":["../../src/ui/radio.tsx"],"sourcesContent":["'use client'\n\nimport * as RadioGroupPrimitive from '@primitives/react-radio-group'\nimport { IconCircle } from '@tabler/icons-react'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { MotionPreference } from '../motion/motion-preference'\nimport { useFormField } from './form'\nimport { type FieldState, resolveFieldState } from './lib/field-state'\nimport { springs } from './lib/motion'\nimport { cn } from './lib/utils'\n\n// Thread the group's resolved state to items so they show the matching border tint.\nconst RadioStateContext = React.createContext<Exclude<FieldState, 'default'> | undefined>(undefined)\n\n/** Border tint per validation state, applied to each item. */\nconst stateBorderClasses: Record<Exclude<FieldState, 'default'>, string> = {\n error: 'border-error-7',\n warning: 'border-warning-7',\n success: 'border-success-7',\n}\n\nexport interface RadioGroupProps extends React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root> {\n /** Validation/feedback state for the whole group. `'error'` also sets `aria-invalid`. Inherited from `FormField` when omitted. */\n state?: FieldState\n}\n\nconst RadioGroup = React.forwardRef<\n React.ElementRef<typeof RadioGroupPrimitive.Root>,\n RadioGroupProps\n>(({ className, state: stateProp, ...props }, ref) => {\n const fieldCtx = useFormField()\n const state = resolveFieldState(stateProp, fieldCtx.state)\n const ariaDescribedBy = props['aria-describedby'] ?? fieldCtx.helperTextId\n const ariaRequired = props['aria-required'] ?? fieldCtx.required\n\n return (\n <RadioStateContext.Provider value={state}>\n <RadioGroupPrimitive.Root\n className={cn('grid gap-ds-03', className)}\n aria-invalid={state === 'error' || undefined}\n aria-describedby={ariaDescribedBy}\n aria-required={ariaRequired || undefined}\n {...props}\n ref={ref}\n />\n </RadioStateContext.Provider>\n )\n})\nRadioGroup.displayName = RadioGroupPrimitive.Root.displayName\n\nexport interface RadioGroupItemProps extends React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item> {\n /** Control size — `sm` (20px), `md` (24px, default, WCAG compliant), `lg` (28px). */\n size?: 'sm' | 'md' | 'lg'\n}\n\nconst radioSizeClasses = {\n sm: 'h-5 w-5',\n md: 'h-6 w-6',\n lg: 'h-7 w-7',\n} as const\n\nconst radioIndicatorClasses = {\n sm: 'h-
|
|
1
|
+
{"version":3,"file":"radio.js","names":[],"sources":["../../src/ui/radio.tsx"],"sourcesContent":["'use client'\n\nimport * as RadioGroupPrimitive from '@primitives/react-radio-group'\nimport { IconCircle } from '@tabler/icons-react'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { MotionPreference } from '../motion/motion-preference'\nimport { useFormField } from './form'\nimport { type FieldState, resolveFieldState } from './lib/field-state'\nimport { springs } from './lib/motion'\nimport { cn } from './lib/utils'\n\n// Thread the group's resolved state to items so they show the matching border tint.\nconst RadioStateContext = React.createContext<Exclude<FieldState, 'default'> | undefined>(undefined)\n\n/** Border tint per validation state, applied to each item. */\nconst stateBorderClasses: Record<Exclude<FieldState, 'default'>, string> = {\n error: 'border-error-7',\n warning: 'border-warning-7',\n success: 'border-success-7',\n}\n\nexport interface RadioGroupProps extends React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root> {\n /** Validation/feedback state for the whole group. `'error'` also sets `aria-invalid`. Inherited from `FormField` when omitted. */\n state?: FieldState\n}\n\nconst RadioGroup = React.forwardRef<\n React.ElementRef<typeof RadioGroupPrimitive.Root>,\n RadioGroupProps\n>(({ className, state: stateProp, ...props }, ref) => {\n const fieldCtx = useFormField()\n const state = resolveFieldState(stateProp, fieldCtx.state)\n const ariaDescribedBy = props['aria-describedby'] ?? fieldCtx.helperTextId\n const ariaRequired = props['aria-required'] ?? fieldCtx.required\n\n return (\n <RadioStateContext.Provider value={state}>\n <RadioGroupPrimitive.Root\n className={cn('grid gap-ds-03', className)}\n aria-invalid={state === 'error' || undefined}\n aria-describedby={ariaDescribedBy}\n aria-required={ariaRequired || undefined}\n {...props}\n ref={ref}\n />\n </RadioStateContext.Provider>\n )\n})\nRadioGroup.displayName = RadioGroupPrimitive.Root.displayName\n\nexport interface RadioGroupItemProps extends React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item> {\n /** Control size — `sm` (20px), `md` (24px, default, WCAG compliant), `lg` (28px). */\n size?: 'sm' | 'md' | 'lg'\n}\n\nconst radioSizeClasses = {\n sm: 'h-5 w-5',\n md: 'h-6 w-6',\n lg: 'h-7 w-7',\n} as const\n\n// The dial is sized so the ring of empty space between it and the control's\n// outer edge is exactly 4px at every size — i.e. control − 8 (design\n// 2026-08-24). 20→12, 24→16, 28→20. Previously 6/8/10, which left a 7/8/9px\n// gap and made the selected dot read as a speck at md and lg.\nconst radioIndicatorClasses = {\n sm: 'h-ds-04 w-ds-04',\n md: 'h-ds-05 w-ds-05',\n lg: 'h-ds-05b w-ds-05b',\n} as const\n\nconst RadioGroupItem = React.forwardRef<\n React.ElementRef<typeof RadioGroupPrimitive.Item>,\n RadioGroupItemProps\n>(({ className, size = 'md', ...props }, ref) => {\n const state = React.useContext(RadioStateContext)\n return (\n <MotionPreference>\n <RadioGroupPrimitive.Item\n ref={ref}\n className={cn(\n 'touch-target aspect-square rounded-pill',\n radioSizeClasses[size],\n 'border border-surface-border-interactive bg-surface-panel-hover',\n 'transition-colors duration-fast-01',\n 'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2',\n 'disabled:cursor-not-allowed disabled:opacity-action-disabled',\n // Hover darkens the dial only; the edge keeps the default border\n // colour (\"same as default\", 2026-08-24). Gated on unchecked so a\n // selected radio does not grey out under the pointer — the shape the\n // `no-ungated-hover-over-selection` rule exists to stop.\n 'data-[state=unchecked]:hover:bg-neutral-4',\n 'data-[state=checked]:border-accent-7',\n state && stateBorderClasses[state],\n className,\n )}\n {...props}\n >\n <RadioGroupPrimitive.Indicator asChild className=\"flex items-center justify-center\">\n <motion.span\n className=\"flex items-center justify-center\"\n initial={{ scale: 0 }}\n animate={{ scale: 1 }}\n transition={springs.snappy}\n >\n <IconCircle className={cn(radioIndicatorClasses[size], 'fill-accent-9 text-accent-11')} />\n </motion.span>\n </RadioGroupPrimitive.Indicator>\n </RadioGroupPrimitive.Item>\n </MotionPreference>\n )\n})\nRadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName\n\nexport { RadioGroup, RadioGroupItem }\n"],"mappings":";;;;;;;;;;;;AAcA,IAAM,IAAoB,EAAM,cAA0D,KAAA,CAAS,GAG7F,IAAqE;CACzE,OAAO;CACP,SAAS;CACT,SAAS;AACX,GAOM,IAAa,EAAM,YAGtB,EAAE,cAAW,OAAO,GAAW,GAAG,KAAS,MAAQ;CACpD,IAAM,IAAW,EAAa,GACxB,IAAQ,EAAkB,GAAW,EAAS,KAAK,GACnD,IAAkB,EAAM,uBAAuB,EAAS,cACxD,IAAe,EAAM,oBAAoB,EAAS;CAExD,OACE,kBAAC,EAAkB,UAAnB;EAA4B,OAAO;YACjC,kBAAC,GAAD;GACE,WAAW,EAAG,kBAAkB,CAAS;GACzC,gBAAc,MAAU,WAAW,KAAA;GACnC,oBAAkB;GAClB,iBAAe,KAAgB,KAAA;GAC/B,GAAI;GACC;EACN,CAAA;CACyB,CAAA;AAEhC,CAAC;AACD,EAAW,cAAA,EAAuC;AAOlD,IAAM,IAAmB;CACvB,IAAI;CACJ,IAAI;CACJ,IAAI;AACN,GAMM,IAAwB;CAC5B,IAAI;CACJ,IAAI;CACJ,IAAI;AACN,GAEM,IAAiB,EAAM,YAG1B,EAAE,cAAW,UAAO,MAAM,GAAG,KAAS,MAAQ;CAC/C,IAAM,IAAQ,EAAM,WAAW,CAAiB;CAChD,OACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;EACO;EACL,WAAW,EACT,2CACA,EAAiB,IACjB,mEACA,sCACA,6GACA,gEAKA,6CACA,wCACA,KAAS,EAAmB,IAC5B,CACF;EACA,GAAI;YAEJ,kBAAC,GAAD;GAA+B,SAAA;GAAQ,WAAU;aAC/C,kBAAC,EAAO,MAAR;IACE,WAAU;IACV,SAAS,EAAE,OAAO,EAAE;IACpB,SAAS,EAAE,OAAO,EAAE;IACpB,YAAY,EAAQ;cAEpB,kBAAC,GAAD,EAAY,WAAW,EAAG,EAAsB,IAAO,8BAA8B,EAAI,CAAA;GAC9E,CAAA;EACgB,CAAA;CACP,CAAA,EACV,CAAA;AAEtB,CAAC;AACD,EAAe,cAAA,EAAuC"}
|
package/dist/ui/select.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../src/ui/select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,eAAe,MAAM,0BAA0B,CAAA;AAE3D,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEjE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAS9B;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,QAAA,MAAM,MAAM,uCAAuB,CAAA;AAEnC,QAAA,MAAM,WAAW,yGAAwB,CAAA;AAEzC,QAAA,MAAM,WAAW,0GAAwB,CAAA;AAEzC,eAAO,MAAM,qBAAqB;;;;
|
|
1
|
+
{"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../src/ui/select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,eAAe,MAAM,0BAA0B,CAAA;AAE3D,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEjE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAS9B;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,QAAA,MAAM,MAAM,uCAAuB,CAAA;AAEnC,QAAA,MAAM,WAAW,yGAAwB,CAAA;AAEzC,QAAA,MAAM,WAAW,0GAAwB,CAAA;AAEzC,eAAO,MAAM,qBAAqB;;;;8EA8BjC,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,kBACf,SAAQ,IAAI,CAAC,KAAK,CAAC,wBAAwB,CAAC,OAAO,eAAe,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,EACnF,YAAY,CAAC,OAAO,qBAAqB,CAAC;CAAG;AAEjD,QAAA,MAAM,aAAa,8FA2BjB,CAAA;AAGF,QAAA,MAAM,oBAAoB,qKAcxB,CAAA;AAGF,QAAA,MAAM,sBAAsB,uKAc1B,CAAA;AAIF,QAAA,MAAM,aAAa,8JAsCjB,CAAA;AAGF,QAAA,MAAM,WAAW,4JASf,CAAA;AAGF,QAAA,MAAM,UAAU,2JAmBd,CAAA;AAGF,QAAA,MAAM,eAAe,gKASnB,CAAA;AAGF,OAAO,EACL,MAAM,EACN,aAAa,EACb,WAAW,EACX,UAAU,EACV,WAAW,EACX,sBAAsB,EACtB,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,WAAW,GACZ,CAAA"}
|
package/dist/ui/select.js
CHANGED
|
@@ -15,21 +15,21 @@ import { cva as k } from "class-variance-authority";
|
|
|
15
15
|
var A = h, j = c, M = b, N = k("flex w-full items-center justify-between whitespace-nowrap rounded-control placeholder:text-surface-fg-muted focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-action-disabled [&>span]:line-clamp-1 [&>span]:min-w-0", {
|
|
16
16
|
variants: {
|
|
17
17
|
variant: {
|
|
18
|
-
default: "border border-surface-border-interactive bg-
|
|
18
|
+
default: "border border-surface-border-interactive bg-field hover:bg-field-hover focus-visible:border-accent-7",
|
|
19
19
|
outline: "border border-surface-border-interactive bg-transparent focus-visible:border-accent-7",
|
|
20
20
|
ghost: "border border-transparent bg-transparent hover:bg-surface-panel-hover focus-visible:border-accent-7"
|
|
21
21
|
},
|
|
22
22
|
state: {
|
|
23
23
|
default: "",
|
|
24
|
-
error: "border-error-
|
|
25
|
-
success: "border-success-
|
|
24
|
+
error: "border-error-8 text-error-11 focus-visible:ring-error-9",
|
|
25
|
+
success: "border-success-8",
|
|
26
26
|
warning: "border-warning-7"
|
|
27
27
|
},
|
|
28
28
|
size: {
|
|
29
|
-
xs: "h-ds-xs-plus text-body-sm px-ds-
|
|
30
|
-
sm: "h-ds-sm text-body-sm px-ds-
|
|
29
|
+
xs: "h-ds-xs-plus text-body-sm px-ds-04",
|
|
30
|
+
sm: "h-ds-sm text-body-sm px-ds-04",
|
|
31
31
|
md: "h-ds-md text-body-md px-ds-04",
|
|
32
|
-
lg: "h-ds-lg text-body-md px-ds-
|
|
32
|
+
lg: "h-ds-lg text-body-md px-ds-04"
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
35
|
defaultVariants: {
|
package/dist/ui/select.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select.js","names":[],"sources":["../../src/ui/select.tsx"],"sourcesContent":["'use client'\n\nimport * as SelectPrimitive from '@primitives/react-select'\nimport { IconCheck, IconChevronDown, IconChevronUp } from '@tabler/icons-react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { MotionPreference } from '../motion/motion-preference'\nimport { useFormField } from './form'\nimport { Icon } from './icon'\nimport { type FieldState, resolveFieldState } from './lib/field-state'\nimport { springs, tweens } from './lib/motion'\nimport { cn } from './lib/utils'\n\n/**\n * Select root — manages open/close state and selected value.\n *\n * **Important:** `size` is NOT a prop on `Select`. Set it on `SelectTrigger` instead.\n * Passing `size` directly to `Select` produces no TypeScript error but has no effect.\n *\n * @example\n * // CORRECT — size goes on SelectTrigger:\n * <Select onValueChange={setValue}>\n * <SelectTrigger size=\"lg\">\n * <SelectValue placeholder=\"Choose...\" />\n * </SelectTrigger>\n * <SelectContent>\n * <SelectItem value=\"a\">Option A</SelectItem>\n * <SelectItem value=\"b\">Option B</SelectItem>\n * </SelectContent>\n * </Select>\n *\n * // WRONG — size on Select root is silently ignored (no TypeScript error):\n * // <Select size=\"lg\">...</Select>\n */\nconst Select = SelectPrimitive.Root\n\nconst SelectGroup = SelectPrimitive.Group\n\nconst SelectValue = SelectPrimitive.Value\n\nexport const selectTriggerVariants = cva(\n 'flex w-full items-center justify-between whitespace-nowrap rounded-control placeholder:text-surface-fg-muted focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-action-disabled [&>span]:line-clamp-1 [&>span]:min-w-0',\n {\n variants: {\n variant: {\n default:\n 'border border-surface-border-interactive bg-surface-panel-hover focus-visible:border-accent-7',\n outline:\n 'border border-surface-border-interactive bg-transparent focus-visible:border-accent-7',\n ghost:\n 'border border-transparent bg-transparent hover:bg-surface-panel-hover focus-visible:border-accent-7',\n },\n state: {\n default: '',\n error: 'border-error-7 text-error-11 focus-visible:ring-error-9',\n success: 'border-success-7',\n warning: 'border-warning-7',\n },\n size: {\n xs: 'h-ds-xs-plus text-body-sm px-ds-02',\n sm: 'h-ds-sm text-body-sm px-ds-03',\n md: 'h-ds-md text-body-md px-ds-04',\n lg: 'h-ds-lg text-body-md px-ds-05',\n },\n },\n defaultVariants: { variant: 'default', state: 'default', size: 'md' },\n },\n)\n\n/**\n * Props for SelectTrigger. Use `size` here (not on the `Select` root).\n *\n * @example\n * <SelectTrigger size=\"lg\" className=\"w-[200px]\">\n * <SelectValue placeholder=\"Select an option\" />\n * </SelectTrigger>\n */\nexport interface SelectTriggerProps\n extends Omit<React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>, 'color'>,\n VariantProps<typeof selectTriggerVariants> {}\n\nconst SelectTrigger = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Trigger>,\n SelectTriggerProps\n>(({ className, children, variant, state: stateProp, size, ...props }, ref) => {\n const fieldCtx = useFormField()\n // Explicit `state` prop wins over FormField context (shared precedence).\n const state = resolveFieldState((stateProp ?? undefined) as FieldState | undefined, fieldCtx.state)\n const ariaDescribedBy = props['aria-describedby'] ?? fieldCtx.helperTextId\n const ariaRequired = props['aria-required'] ?? fieldCtx.required\n\n return (\n <SelectPrimitive.Trigger\n ref={ref}\n className={cn(selectTriggerVariants({ variant, state: state ?? 'default', size }), className)}\n aria-invalid={state === 'error' || undefined}\n aria-describedby={ariaDescribedBy}\n aria-required={ariaRequired || undefined}\n {...props}\n // Explicit id wins; otherwise adopt FormField's inputId so <Label htmlFor> resolves.\n id={props.id ?? fieldCtx.inputId}\n >\n {children}\n <SelectPrimitive.Icon asChild>\n <Icon icon={IconChevronDown} size=\"sm\" className=\"opacity-50\" />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n )\n})\nSelectTrigger.displayName = SelectPrimitive.Trigger.displayName\n\nconst SelectScrollUpButton = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.ScrollUpButton\n ref={ref}\n className={cn(\n 'flex cursor-default items-center justify-center py-ds-02',\n className,\n )}\n {...props}\n >\n <Icon icon={IconChevronUp} size=\"sm\" />\n </SelectPrimitive.ScrollUpButton>\n))\nSelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName\n\nconst SelectScrollDownButton = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.ScrollDownButton\n ref={ref}\n className={cn(\n 'flex cursor-default items-center justify-center py-ds-02',\n className,\n )}\n {...props}\n >\n <Icon icon={IconChevronDown} size=\"sm\" />\n </SelectPrimitive.ScrollDownButton>\n))\nSelectScrollDownButton.displayName =\n SelectPrimitive.ScrollDownButton.displayName\n\nconst SelectContent = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>\n>(({ className, children, position = 'popper', ...props }, ref) => (\n <MotionPreference>\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n ref={ref}\n position={position}\n asChild\n {...props}\n >\n <motion.div\n initial={{ opacity: 0, scale: 0.95 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={{ ...springs.snappy, opacity: tweens.fade }}\n className={cn(\n 'relative z-popover max-h-96 min-w-[8rem] overflow-hidden rounded-overlay bg-surface-overlay text-surface-fg shadow-floating',\n position === 'popper' &&\n 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',\n className,\n )}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.Viewport\n className={cn(\n 'p-ds-02',\n position === 'popper' &&\n 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]',\n )}\n >\n {children}\n </SelectPrimitive.Viewport>\n <SelectScrollDownButton />\n </motion.div>\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n </MotionPreference>\n))\nSelectContent.displayName = SelectPrimitive.Content.displayName\n\nconst SelectLabel = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Label>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Label\n ref={ref}\n className={cn('px-ds-03 py-ds-02b text-body-md font-semibold', className)}\n {...props}\n />\n))\nSelectLabel.displayName = SelectPrimitive.Label.displayName\n\nconst SelectItem = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Item\n ref={ref}\n className={cn(\n 'relative flex w-full cursor-default select-none items-center rounded-control py-ds-02b pl-ds-03 pr-ds-07 text-body-md outline-hidden transition-colors duration-fast-01 ease-productive-standard hover:bg-surface-panel-hover focus:bg-surface-panel-hover focus:text-surface-fg data-[disabled]:pointer-events-none data-[disabled]:opacity-action-disabled',\n className,\n )}\n {...props}\n >\n <span className=\"absolute right-ds-03 flex h-ico-sm w-ico-sm items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <Icon icon={IconCheck} size=\"sm\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText className=\"min-w-0 line-clamp-1\">{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n))\nSelectItem.displayName = SelectPrimitive.Item.displayName\n\nconst SelectSeparator = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Separator\n ref={ref}\n className={cn('-mx-ds-01 my-ds-02 h-px bg-surface-border', className)}\n {...props}\n />\n))\nSelectSeparator.displayName = SelectPrimitive.Separator.displayName\n\nexport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectScrollDownButton,\n SelectScrollUpButton,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"select.js","names":[],"sources":["../../src/ui/select.tsx"],"sourcesContent":["'use client'\n\nimport * as SelectPrimitive from '@primitives/react-select'\nimport { IconCheck, IconChevronDown, IconChevronUp } from '@tabler/icons-react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { MotionPreference } from '../motion/motion-preference'\nimport { useFormField } from './form'\nimport { Icon } from './icon'\nimport { type FieldState, resolveFieldState } from './lib/field-state'\nimport { springs, tweens } from './lib/motion'\nimport { cn } from './lib/utils'\n\n/**\n * Select root — manages open/close state and selected value.\n *\n * **Important:** `size` is NOT a prop on `Select`. Set it on `SelectTrigger` instead.\n * Passing `size` directly to `Select` produces no TypeScript error but has no effect.\n *\n * @example\n * // CORRECT — size goes on SelectTrigger:\n * <Select onValueChange={setValue}>\n * <SelectTrigger size=\"lg\">\n * <SelectValue placeholder=\"Choose...\" />\n * </SelectTrigger>\n * <SelectContent>\n * <SelectItem value=\"a\">Option A</SelectItem>\n * <SelectItem value=\"b\">Option B</SelectItem>\n * </SelectContent>\n * </Select>\n *\n * // WRONG — size on Select root is silently ignored (no TypeScript error):\n * // <Select size=\"lg\">...</Select>\n */\nconst Select = SelectPrimitive.Root\n\nconst SelectGroup = SelectPrimitive.Group\n\nconst SelectValue = SelectPrimitive.Value\n\nexport const selectTriggerVariants = cva(\n 'flex w-full items-center justify-between whitespace-nowrap rounded-control placeholder:text-surface-fg-muted focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-action-disabled [&>span]:line-clamp-1 [&>span]:min-w-0',\n {\n variants: {\n variant: {\n default:\n 'border border-surface-border-interactive bg-field hover:bg-field-hover focus-visible:border-accent-7',\n outline:\n 'border border-surface-border-interactive bg-transparent focus-visible:border-accent-7',\n ghost:\n 'border border-transparent bg-transparent hover:bg-surface-panel-hover focus-visible:border-accent-7',\n },\n state: {\n default: '',\n // Step 8 clears the 3:1 non-text bar that step 7 missed. Warning stays\n // at 7 per the design note (\"border color no changed\").\n error: 'border-error-8 text-error-11 focus-visible:ring-error-9',\n success: 'border-success-8',\n warning: 'border-warning-7',\n },\n // 12px inline padding on every size (design 2026-08-24).\n size: {\n xs: 'h-ds-xs-plus text-body-sm px-ds-04',\n sm: 'h-ds-sm text-body-sm px-ds-04',\n md: 'h-ds-md text-body-md px-ds-04',\n lg: 'h-ds-lg text-body-md px-ds-04',\n },\n },\n defaultVariants: { variant: 'default', state: 'default', size: 'md' },\n },\n)\n\n/**\n * Props for SelectTrigger. Use `size` here (not on the `Select` root).\n *\n * @example\n * <SelectTrigger size=\"lg\" className=\"w-[200px]\">\n * <SelectValue placeholder=\"Select an option\" />\n * </SelectTrigger>\n */\nexport interface SelectTriggerProps\n extends Omit<React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>, 'color'>,\n VariantProps<typeof selectTriggerVariants> {}\n\nconst SelectTrigger = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Trigger>,\n SelectTriggerProps\n>(({ className, children, variant, state: stateProp, size, ...props }, ref) => {\n const fieldCtx = useFormField()\n // Explicit `state` prop wins over FormField context (shared precedence).\n const state = resolveFieldState((stateProp ?? undefined) as FieldState | undefined, fieldCtx.state)\n const ariaDescribedBy = props['aria-describedby'] ?? fieldCtx.helperTextId\n const ariaRequired = props['aria-required'] ?? fieldCtx.required\n\n return (\n <SelectPrimitive.Trigger\n ref={ref}\n className={cn(selectTriggerVariants({ variant, state: state ?? 'default', size }), className)}\n aria-invalid={state === 'error' || undefined}\n aria-describedby={ariaDescribedBy}\n aria-required={ariaRequired || undefined}\n {...props}\n // Explicit id wins; otherwise adopt FormField's inputId so <Label htmlFor> resolves.\n id={props.id ?? fieldCtx.inputId}\n >\n {children}\n <SelectPrimitive.Icon asChild>\n <Icon icon={IconChevronDown} size=\"sm\" className=\"opacity-50\" />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n )\n})\nSelectTrigger.displayName = SelectPrimitive.Trigger.displayName\n\nconst SelectScrollUpButton = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.ScrollUpButton\n ref={ref}\n className={cn(\n 'flex cursor-default items-center justify-center py-ds-02',\n className,\n )}\n {...props}\n >\n <Icon icon={IconChevronUp} size=\"sm\" />\n </SelectPrimitive.ScrollUpButton>\n))\nSelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName\n\nconst SelectScrollDownButton = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.ScrollDownButton\n ref={ref}\n className={cn(\n 'flex cursor-default items-center justify-center py-ds-02',\n className,\n )}\n {...props}\n >\n <Icon icon={IconChevronDown} size=\"sm\" />\n </SelectPrimitive.ScrollDownButton>\n))\nSelectScrollDownButton.displayName =\n SelectPrimitive.ScrollDownButton.displayName\n\nconst SelectContent = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>\n>(({ className, children, position = 'popper', ...props }, ref) => (\n <MotionPreference>\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n ref={ref}\n position={position}\n asChild\n {...props}\n >\n <motion.div\n initial={{ opacity: 0, scale: 0.95 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={{ ...springs.snappy, opacity: tweens.fade }}\n className={cn(\n 'relative z-popover max-h-96 min-w-[8rem] overflow-hidden rounded-overlay bg-surface-overlay text-surface-fg shadow-floating',\n position === 'popper' &&\n 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',\n className,\n )}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.Viewport\n className={cn(\n 'p-ds-02',\n position === 'popper' &&\n 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]',\n )}\n >\n {children}\n </SelectPrimitive.Viewport>\n <SelectScrollDownButton />\n </motion.div>\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n </MotionPreference>\n))\nSelectContent.displayName = SelectPrimitive.Content.displayName\n\nconst SelectLabel = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Label>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Label\n ref={ref}\n className={cn('px-ds-03 py-ds-02b text-body-md font-semibold', className)}\n {...props}\n />\n))\nSelectLabel.displayName = SelectPrimitive.Label.displayName\n\nconst SelectItem = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Item\n ref={ref}\n className={cn(\n 'relative flex w-full cursor-default select-none items-center rounded-control py-ds-02b pl-ds-03 pr-ds-07 text-body-md outline-hidden transition-colors duration-fast-01 ease-productive-standard hover:bg-surface-panel-hover focus:bg-surface-panel-hover focus:text-surface-fg data-[disabled]:pointer-events-none data-[disabled]:opacity-action-disabled',\n className,\n )}\n {...props}\n >\n <span className=\"absolute right-ds-03 flex h-ico-sm w-ico-sm items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <Icon icon={IconCheck} size=\"sm\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText className=\"min-w-0 line-clamp-1\">{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n))\nSelectItem.displayName = SelectPrimitive.Item.displayName\n\nconst SelectSeparator = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Separator\n ref={ref}\n className={cn('-mx-ds-01 my-ds-02 h-px bg-surface-border', className)}\n {...props}\n />\n))\nSelectSeparator.displayName = SelectPrimitive.Separator.displayName\n\nexport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectScrollDownButton,\n SelectScrollUpButton,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n}\n"],"mappings":"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"}
|
package/dist/ui/switch.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"switch.d.ts","sourceRoot":"","sources":["../../src/ui/switch.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,gBAAgB,MAAM,0BAA0B,CAAA;AAE5D,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,OAAO,EAAE,KAAK,UAAU,EAAqB,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"switch.d.ts","sourceRoot":"","sources":["../../src/ui/switch.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,gBAAgB,MAAM,0BAA0B,CAAA;AAE5D,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,OAAO,EAAE,KAAK,UAAU,EAAqB,MAAM,mBAAmB,CAAA;AAyCtE,MAAM,WAAW,WAAY,SAAQ,KAAK,CAAC,wBAAwB,CAAC,OAAO,gBAAgB,CAAC,IAAI,CAAC;IAC/F,yJAAyJ;IACzJ,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IACzB,KAAK,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAA;IACxC,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC5B;AAED,QAAA,MAAM,MAAM,uFAoFV,CAAA;AAGF,OAAO,EAAE,MAAM,EAAE,CAAA"}
|
package/dist/ui/switch.js
CHANGED
|
@@ -12,7 +12,7 @@ var u = {
|
|
|
12
12
|
sm: {
|
|
13
13
|
track: "h-6 w-[38px]",
|
|
14
14
|
thumb: "h-5 w-5",
|
|
15
|
-
travel:
|
|
15
|
+
travel: 14
|
|
16
16
|
},
|
|
17
17
|
md: {
|
|
18
18
|
track: "h-6 w-11",
|
|
@@ -37,7 +37,7 @@ var u = {
|
|
|
37
37
|
v === void 0 && O(e), b?.(e);
|
|
38
38
|
}, [v, b]);
|
|
39
39
|
return /* @__PURE__ */ s(i, {
|
|
40
|
-
className: t("touch-target peer inline-flex shrink-0 cursor-pointer items-center rounded-pill border-2 border-
|
|
40
|
+
className: t("touch-target peer inline-flex shrink-0 cursor-pointer items-center rounded-pill border-2 border-transparent shadow-raised transition-colors duration-fast-01 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-action-disabled data-[state=unchecked]:bg-neutral-5 data-[state=unchecked]:hover:bg-neutral-6", M, f[g], w && d[w], p),
|
|
41
41
|
checked: v,
|
|
42
42
|
defaultChecked: y,
|
|
43
43
|
onCheckedChange: F,
|
package/dist/ui/switch.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"switch.js","names":[],"sources":["../../src/ui/switch.tsx"],"sourcesContent":["'use client'\n\nimport * as SwitchPrimitives from \"@primitives/react-switch\"\nimport { motion, useReducedMotion } from 'framer-motion'\nimport * as React from \"react\"\n\nimport { useFormField } from './form'\nimport { type FieldState, resolveFieldState } from './lib/field-state'\nimport { springs } from './lib/motion'\nimport { cn } from \"./lib/utils\"\n\nconst sizeConfig = {\n sm: { track: 'h-6 w-[38px]', thumb: 'h-5 w-5', travel:
|
|
1
|
+
{"version":3,"file":"switch.js","names":[],"sources":["../../src/ui/switch.tsx"],"sourcesContent":["'use client'\n\nimport * as SwitchPrimitives from \"@primitives/react-switch\"\nimport { motion, useReducedMotion } from 'framer-motion'\nimport * as React from \"react\"\n\nimport { useFormField } from './form'\nimport { type FieldState, resolveFieldState } from './lib/field-state'\nimport { springs } from './lib/motion'\nimport { cn } from \"./lib/utils\"\n\n// Travel must equal (track width − 2×border − thumb), so the thumb lands 2px\n// from the inner edge at both ends. sm was 16, which overshot by 2px and left\n// the checked thumb flush against the right border with no inset at all —\n// \"Switch sm: increase the right padding to 2px\" (design 2026-08-24).\n// md (44−4−20) and lg (52−4−24) were already correct.\nconst sizeConfig = {\n sm: { track: 'h-6 w-[38px]', thumb: 'h-5 w-5', travel: 14 },\n md: { track: 'h-6 w-11', thumb: 'h-ico-md w-ico-md', travel: 20 },\n lg: { track: 'h-7 w-[52px]', thumb: 'h-6 w-6', travel: 24 },\n} as const\n\n/**\n * Validation border + checked-track tint per state (overrides `color` when set).\n *\n * The border is only VISIBLE here. The track always reserves 2px of it —\n * `border-transparent` in the base classes — because the border sits inside the\n * box and the thumb's travel is computed as (track − 2×border − thumb). Making\n * it appear and disappear would move the thumb by 4px whenever a field went\n * into an error state.\n *\n * That reservation is also what lets the 2026-08-24 refresh remove the resting\n * stroke without losing validation: unchecked, this border is the ONLY thing\n * distinguishing error/warning/success from default, since the coloured track\n * applies solely when checked.\n */\nconst stateTintClasses: Record<Exclude<FieldState, 'default'>, string> = {\n error: 'border-error-7 data-[state=checked]:bg-error-9',\n warning: 'border-warning-7 data-[state=checked]:bg-warning-9',\n success: 'border-success-7 data-[state=checked]:bg-success-9',\n}\n\nconst colorMap = {\n accent: 'data-[state=checked]:bg-accent-9',\n success: 'data-[state=checked]:bg-success-9',\n warning: 'data-[state=checked]:bg-warning-9',\n} as const\n\nexport interface SwitchProps extends React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root> {\n /** Validation/feedback state. `'error'` also sets `aria-invalid`. Inherited from `FormField` when omitted. Distinct from `color` (the ON-track tint). */\n state?: FieldState\n size?: 'sm' | 'md' | 'lg'\n color?: 'accent' | 'success' | 'warning'\n thumbIcon?: React.ReactNode\n}\n\nconst Switch = React.forwardRef<\n React.ElementRef<typeof SwitchPrimitives.Root>,\n SwitchProps\n>(({ className, state: stateProp, size = 'md', color = 'accent', thumbIcon, checked, defaultChecked, onCheckedChange, ...props }, ref) => {\n const fieldCtx = useFormField()\n const state = resolveFieldState(stateProp, fieldCtx.state)\n const ariaDescribedBy = props['aria-describedby'] ?? fieldCtx.helperTextId\n const ariaRequired = props['aria-required'] ?? fieldCtx.required\n\n // Track checked state internally to drive Framer Motion animation\n const [internalChecked, setInternalChecked] = React.useState(defaultChecked ?? false)\n const isChecked = checked !== undefined ? checked : internalChecked\n const reduced = useReducedMotion()\n // Thumb travels toward the inline-end — mirror it under RTL.\n const isRtl =\n typeof document !== 'undefined' &&\n (document.dir === 'rtl' || document.documentElement.dir === 'rtl')\n const { track, thumb, travel } = sizeConfig[size]\n\n const handleCheckedChange = React.useCallback(\n (value: boolean) => {\n if (checked === undefined) {\n setInternalChecked(value)\n }\n onCheckedChange?.(value)\n },\n [checked, onCheckedChange],\n )\n\n return (\n <SwitchPrimitives.Root\n // OFF track moved from `surface-border-strong` to neutral-5 and its hover\n // from `surface-panel-active` (neutral-3) to neutral-6 (design 2026-08-24).\n // Both darken the track, which is what makes the thumb readable against\n // it: track-vs-thumb goes 1.350:1 → 1.598:1, and hover reaches 1.955:1.\n // Direction holds in dark too (n-5 0.377 → n-6 0.417, both above panel).\n //\n // The design also asked to DROP the 2px border. Not done: unchecked, that\n // border is the only edge the control has, and it is the stronger of the\n // two (neutral-6 at 2.006:1 vs the fill's 1.639:1). Removing it lowers the\n // component boundary against WCAG 1.4.11, which both values already miss.\n // Raised as a question rather than shipped.\n className={cn(\n // `border-transparent`, not \"no border\": the 2px is still reserved so\n // the thumb's travel stays (track − 2×border − thumb) whether or not a\n // validation state paints it. Removing the width outright would shift\n // the thumb 4px the moment a field errored.\n // The resting stroke is gone per the 2026-08-24 refresh, so an\n // unchecked switch is now carried by its fill alone (1.639:1, down from\n // the border's 2.006:1). deviation: SWITCH-RESTING-STROKE-REMOVED\n \"touch-target peer inline-flex shrink-0 cursor-pointer items-center rounded-pill border-2 border-transparent shadow-raised transition-colors duration-fast-01 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-action-disabled data-[state=unchecked]:bg-neutral-5 data-[state=unchecked]:hover:bg-neutral-6\",\n track,\n colorMap[color],\n state && stateTintClasses[state],\n className\n )}\n checked={checked}\n defaultChecked={defaultChecked}\n onCheckedChange={handleCheckedChange}\n aria-invalid={state === 'error' || undefined}\n aria-describedby={ariaDescribedBy}\n aria-required={ariaRequired || undefined}\n {...props}\n // Explicit id wins; otherwise adopt FormField's inputId so <Label htmlFor>\n // resolves. Without this the label points at nothing and the switch has NO\n // accessible name, despite the consumer wiring FormField + Label correctly.\n id={props.id ?? fieldCtx.inputId}\n ref={ref}\n >\n <SwitchPrimitives.Thumb asChild>\n <motion.span\n className={cn(\n \"pointer-events-none flex items-center justify-center rounded-pill bg-accent-fg shadow-raised-hover ring-0\",\n thumb\n )}\n animate={{ x: (isChecked ? travel : 0) * (isRtl ? -1 : 1) }}\n whileTap={reduced ? undefined : { scale: 0.85 }}\n transition={reduced ? { duration: 0 } : springs.snappy}\n >\n {thumbIcon}\n </motion.span>\n </SwitchPrimitives.Thumb>\n </SwitchPrimitives.Root>\n )\n})\nSwitch.displayName = SwitchPrimitives.Root.displayName\n\nexport { Switch }\n"],"mappings":";;;;;;;;;;AAgBA,IAAM,IAAa;CACjB,IAAI;EAAE,OAAO;EAAgB,OAAO;EAAW,QAAQ;CAAG;CAC1D,IAAI;EAAE,OAAO;EAAY,OAAO;EAAqB,QAAQ;CAAG;CAChE,IAAI;EAAE,OAAO;EAAgB,OAAO;EAAW,QAAQ;CAAG;AAC5D,GAgBM,IAAmE;CACvE,OAAO;CACP,SAAS;CACT,SAAS;AACX,GAEM,IAAW;CACf,QAAQ;CACR,SAAS;CACT,SAAS;AACX,GAUM,IAAS,EAAM,YAGlB,EAAE,cAAW,OAAO,GAAW,UAAO,MAAM,WAAQ,UAAU,cAAW,YAAS,mBAAgB,oBAAiB,GAAG,KAAS,MAAQ;CACxI,IAAM,IAAW,EAAa,GACxB,IAAQ,EAAkB,GAAW,EAAS,KAAK,GACnD,IAAkB,EAAM,uBAAuB,EAAS,cACxD,IAAe,EAAM,oBAAoB,EAAS,UAGlD,CAAC,GAAiB,KAAsB,EAAM,SAAS,KAAkB,EAAK,GAC9E,IAAY,MAAY,KAAA,IAAsB,IAAV,GACpC,IAAU,EAAiB,GAE3B,IACJ,OAAO,WAAa,QACnB,SAAS,QAAQ,SAAS,SAAS,gBAAgB,QAAQ,QACxD,EAAE,UAAO,UAAO,cAAW,EAAW,IAEtC,IAAsB,EAAM,aAC/B,MAAmB;EAIlB,AAHI,MAAY,KAAA,KACd,EAAmB,CAAK,GAE1B,IAAkB,CAAK;CACzB,GACA,CAAC,GAAS,CAAe,CAC3B;CAEA,OACE,kBAAC,GAAD;EAYE,WAAW,EAQT,qZACA,GACA,EAAS,IACT,KAAS,EAAiB,IAC1B,CACF;EACS;EACO;EAChB,iBAAiB;EACjB,gBAAc,MAAU,WAAW,KAAA;EACnC,oBAAkB;EAClB,iBAAe,KAAgB,KAAA;EAC/B,GAAI;EAIJ,IAAI,EAAM,MAAM,EAAS;EACpB;YAEL,kBAAC,GAAD;GAAwB,SAAA;aACtB,kBAAC,EAAO,MAAR;IACE,WAAW,EACT,6GACA,CACF;IACA,SAAS,EAAE,IAAI,IAAY,IAAS,MAAM,IAAQ,KAAK,GAAG;IAC1D,UAAU,IAAU,KAAA,IAAY,EAAE,OAAO,IAAK;IAC9C,YAAY,IAAU,EAAE,UAAU,EAAE,IAAI,EAAQ;cAE/C;GACU,CAAA;EACS,CAAA;CACH,CAAA;AAE3B,CAAC;AACD,EAAO,cAAA,EAAoC"}
|
package/dist/ui/table.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../src/ui/table.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,4EAA4E;AAC5E,KAAK,YAAY,GAAG,SAAS,GAAG,UAAU,GAAG,aAAa,CAAA;AAa1D,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,UAAW,SAAQ,KAAK,CAAC,cAAc,CAAC,gBAAgB,CAAC;IACxE,iFAAiF;IACjF,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,QAAA,MAAM,KAAK,
|
|
1
|
+
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../src/ui/table.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,4EAA4E;AAC5E,KAAK,YAAY,GAAG,SAAS,GAAG,UAAU,GAAG,aAAa,CAAA;AAa1D,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,UAAW,SAAQ,KAAK,CAAC,cAAc,CAAC,gBAAgB,CAAC;IACxE,iFAAiF;IACjF,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,QAAA,MAAM,KAAK,qFAiCV,CAAA;AAGD,QAAA,MAAM,WAAW,+HAKf,CAAA;AAGF,QAAA,MAAM,SAAS,+HASb,CAAA;AAGF,QAAA,MAAM,WAAW,+HAef,CAAA;AAGF,QAAA,MAAM,QAAQ,uHAoBZ,CAAA;AAGF,QAAA,MAAM,SAAS,gJAgBb,CAAA;AAGF,QAAA,MAAM,SAAS,gJAab,CAAA;AAGF,MAAM,WAAW,oBAAqB,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IAChF;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED;;;;;;;;;;;;;;;GAeG;AACH,QAAA,MAAM,eAAe,6FAcpB,CAAA;AAGD,QAAA,MAAM,YAAY,+HAShB,CAAA;AAGF,MAAM,MAAM,aAAa,GAAG,KAAK,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAA;AACrE,MAAM,MAAM,cAAc,GAAG,KAAK,CAAC,gBAAgB,CAAC,oBAAoB,CAAC,CAAA;AACzE,YAAY,EAAE,YAAY,EAAE,CAAA;AAE5B,OAAO,EACL,KAAK,EACL,SAAS,EACT,YAAY,EACZ,SAAS,EACT,WAAW,EACX,SAAS,EACT,WAAW,EACX,QAAQ,EACR,eAAe,GAChB,CAAA"}
|
package/dist/ui/table.js
CHANGED
|
@@ -10,7 +10,7 @@ var r = {
|
|
|
10
10
|
className: "relative w-full overflow-auto",
|
|
11
11
|
children: /* @__PURE__ */ n("table", {
|
|
12
12
|
ref: s,
|
|
13
|
-
className: e("w-full caption-bottom text-body-md overflow-x-clip [--table-edge:var(--card-spacing,var(--spacing-ds-04))]", r[i], a && "[&_tbody_tr:nth-child(even)]:bg-surface-panel-hover", t),
|
|
13
|
+
className: e("w-full caption-bottom text-body-md overflow-x-clip [--table-edge:var(--card-spacing,var(--spacing-ds-04))]", r[i], a && "[&_tbody_tr:nth-child(even):not([data-state=selected])]:bg-surface-panel-hover", a && "[&_tbody_tr:nth-child(even):not([data-state=selected])_[data-pinned]]:bg-surface-panel-hover", t),
|
|
14
14
|
...o
|
|
15
15
|
})
|
|
16
16
|
}));
|
package/dist/ui/table.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table.js","names":[],"sources":["../../src/ui/table.tsx"],"sourcesContent":["// @server-safe\nimport * as React from \"react\"\n\nimport { cn } from \"./lib/utils\"\n\n/** Row density — sets --table-py, which header and body cells both read. */\ntype TableDensity = 'compact' | 'standard' | 'comfortable'\n\n// One variable pair drives the table's spacing (same pattern as Card):\n// --table-py vertical cell padding per density (4 / 8 / 12px → rows ≈ 29 / 37 / 45px)\n// --table-edge first/last-cell inline padding — inherits --card-spacing when the table\n// sits inside a Card, so edge columns align with the card's slots;\n// falls back to ds-04 (12px) standalone.\nconst densityClasses: Record<TableDensity, string> = {\n compact: '[--table-py:var(--spacing-ds-02)]',\n standard: '[--table-py:var(--spacing-ds-03)]',\n comfortable: '[--table-py:var(--spacing-ds-04)]',\n}\n\nexport interface TableCellBaseProps {\n /**\n * Quantitative column: right-aligns and uses tabular figures so digits line up.\n * Keep decimal places consistent per column; identifier-numbers (dates, phones,\n * IDs) stay left-aligned — they're names, not quantities.\n */\n numeric?: boolean\n}\n\nexport interface TableProps extends React.HTMLAttributes<HTMLTableElement> {\n /** Row density — vertical cell padding for header + body. @default 'standard' */\n density?: TableDensity\n /**\n * Zebra striping (even body rows get the faintest surface step). Opt-in only —\n * for very wide/dense tables; hairline separators are the default row cue.\n */\n striped?: boolean\n}\n\nconst Table = React.forwardRef<HTMLTableElement, TableProps>(\n ({ className, density = 'standard', striped, ...props }, ref) => (\n <div className=\"relative w-full overflow-auto\">\n <table\n ref={ref}\n className={cn(\n // overflow-x-clip contains TableRowLink's 100vw stretch pseudo-element\n // without creating a horizontal scrollbar (the wrapper owns scrolling).\n \"w-full caption-bottom text-body-md overflow-x-clip [--table-edge:var(--card-spacing,var(--spacing-ds-04))]\",\n densityClasses[density],\n striped && \"[&_tbody_tr:nth-child(even)]:bg-surface-panel-hover\",\n className,\n )}\n {...props}\n />\n </div>\n ),\n)\nTable.displayName = \"Table\"\n\nconst TableHeader = React.forwardRef<\n HTMLTableSectionElement,\n React.HTMLAttributes<HTMLTableSectionElement>\n>(({ className, ...props }, ref) => (\n <thead ref={ref} className={cn(\"[&_tr]:border-b [&_tr]:border-surface-border-subtle\", className)} {...props} />\n))\nTableHeader.displayName = \"TableHeader\"\n\nconst TableBody = React.forwardRef<\n HTMLTableSectionElement,\n React.HTMLAttributes<HTMLTableSectionElement>\n>(({ className, ...props }, ref) => (\n <tbody\n ref={ref}\n className={cn(\"[&_tr:last-child]:border-0\", className)}\n {...props}\n />\n))\nTableBody.displayName = \"TableBody\"\n\nconst TableFooter = React.forwardRef<\n HTMLTableSectionElement,\n React.HTMLAttributes<HTMLTableSectionElement>\n>(({ className, ...props }, ref) => (\n <tfoot\n ref={ref}\n className={cn(\n // surface-base band, not raised@50% — the footer must read against the\n // card surface the table lives on (same mis-mapped shadcn muted/50 family\n // as the row-hover bug).\n \"border-t border-surface-border-subtle bg-surface-panel-hover font-medium [&>tr]:last:border-b-0\",\n className\n )}\n {...props}\n />\n))\nTableFooter.displayName = \"TableFooter\"\n\nconst TableRow = React.forwardRef<\n HTMLTableRowElement,\n React.HTMLAttributes<HTMLTableRowElement>\n>(({ className, ...props }, ref) => (\n <tr\n ref={ref}\n className={cn(\n // panel-hover, NOT panel — tables live on cards (surface-panel), so a\n // surface-panel hover would be invisible (the 0.44-era port bug).\n // selected+hover gets its own explicit step — without it the hover and\n // selected classes tie on specificity and stylesheet order decides.\n // `group/row` lets TableRowActions reveal on row hover/focus; the has-[]\n // rule draws a row-level focus ring when a TableRowLink inside is\n // keyboard-focused (the anchor itself suppresses its own ring).\n \"group/row border-b border-surface-border-subtle transition-colors hover:bg-surface-panel-hover data-[state=selected]:bg-accent-4 data-[state=selected]:hover:bg-accent-5 data-[state=selected]:forced-colors:outline data-[state=selected]:forced-colors:outline-1\",\n \"has-[[data-slot=row-link]:focus-visible]:outline-2 has-[[data-slot=row-link]:focus-visible]:outline-accent-9 has-[[data-slot=row-link]:focus-visible]:-outline-offset-2\",\n className,\n )}\n {...props}\n />\n))\nTableRow.displayName = \"TableRow\"\n\nconst TableHead = React.forwardRef<\n HTMLTableCellElement,\n React.ThHTMLAttributes<HTMLTableCellElement> & TableCellBaseProps\n>(({ className, numeric, ...props }, ref) => (\n <th\n ref={ref}\n scope=\"col\"\n className={cn(\n // Header is quieter than the data: one step smaller, medium, muted.\n // Height tracks density via the same --table-py the body cells read.\n \"py-(--table-py) px-ds-04 first:pl-(--table-edge) last:pr-(--table-edge) text-left align-middle text-body-sm font-medium text-surface-fg-muted [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]\",\n numeric && \"text-right\",\n className,\n )}\n {...props}\n />\n))\nTableHead.displayName = \"TableHead\"\n\nconst TableCell = React.forwardRef<\n HTMLTableCellElement,\n React.TdHTMLAttributes<HTMLTableCellElement> & TableCellBaseProps\n>(({ className, numeric, ...props }, ref) => (\n <td\n ref={ref}\n className={cn(\n \"py-(--table-py) px-ds-04 first:pl-(--table-edge) last:pr-(--table-edge) align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]\",\n numeric && \"text-right tabular-nums\",\n className,\n )}\n {...props}\n />\n))\nTableCell.displayName = \"TableCell\"\n\nexport interface TableRowActionsProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Always show the actions instead of revealing on row hover/focus. Use for\n * tables where actions must be permanently discoverable (the GitLab stance),\n * or when the row has few columns and the density win doesn't matter.\n */\n persist?: boolean\n}\n\n/**\n * Right-aligned action cluster for a table row, revealed on row hover — and,\n * critically, on keyboard focus: the buttons stay in the tab order permanently\n * (opacity reveal, never display:none) and appear the moment focus enters the\n * row (WCAG 1.4.13). On touch devices (no hover) they are always visible.\n *\n * Give the actions column a visually-hidden header: `<TableHead><span className=\"sr-only\">Actions</span></TableHead>`.\n *\n * @example\n * <TableCell>\n * <TableRowActions>\n * <IconButton size=\"xs\" variant=\"ghost\" aria-label={`Download ${name}`} icon={<IconDownload />} />\n * <IconButton size=\"xs\" variant=\"ghost\" aria-label={`Delete ${name}`} icon={<IconTrash />} />\n * </TableRowActions>\n * </TableCell>\n */\nconst TableRowActions = React.forwardRef<HTMLDivElement, TableRowActionsProps>(\n ({ className, persist, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n \"flex items-center justify-end gap-ds-01 transition-opacity duration-fast-01 ease-productive-standard\",\n persist\n ? \"opacity-100\"\n : \"opacity-0 group-hover/row:opacity-100 group-focus-within/row:opacity-100 pointer-coarse:opacity-100\",\n className,\n )}\n {...props}\n />\n ),\n)\nTableRowActions.displayName = \"TableRowActions\"\n\nconst TableCaption = React.forwardRef<\n HTMLTableCaptionElement,\n React.HTMLAttributes<HTMLTableCaptionElement>\n>(({ className, ...props }, ref) => (\n <caption\n ref={ref}\n className={cn(\"mt-ds-05 text-body-md text-surface-fg-muted\", className)}\n {...props}\n />\n))\nTableCaption.displayName = \"TableCaption\"\n\nexport type TableRowProps = React.HTMLAttributes<HTMLTableRowElement>\nexport type TableCellProps = React.TdHTMLAttributes<HTMLTableCellElement>\nexport type { TableDensity }\n\nexport {\n Table,\n TableBody,\n TableCaption,\n TableCell,\n TableFooter,\n TableHead,\n TableHeader,\n TableRow,\n TableRowActions,\n}\n"],"mappings":";;;;AAaA,IAAM,IAA+C;CACnD,SAAS;CACT,UAAU;CACV,aAAa;AACf,GAqBM,IAAQ,EAAM,YACjB,EAAE,cAAW,aAAU,YAAY,YAAS,GAAG,KAAS,MACvD,kBAAC,OAAD;CAAK,WAAU;WACb,kBAAC,SAAD;EACO;EACL,WAAW,EAGT,8GACA,EAAe,IACf,KAAW,uDACX,CACF;EACA,GAAI;CACL,CAAA;AACE,CAAA,CAET;AACA,EAAM,cAAc;AAEpB,IAAM,IAAc,EAAM,YAGvB,EAAE,cAAW,GAAG,KAAS,MAC1B,kBAAC,SAAD;CAAY;CAAK,WAAW,EAAG,uDAAuD,CAAS;CAAG,GAAI;AAAQ,CAAA,CAC/G;AACD,EAAY,cAAc;AAE1B,IAAM,IAAY,EAAM,YAGrB,EAAE,cAAW,GAAG,KAAS,MAC1B,kBAAC,SAAD;CACO;CACL,WAAW,EAAG,8BAA8B,CAAS;CACrD,GAAI;AACL,CAAA,CACF;AACD,EAAU,cAAc;AAExB,IAAM,IAAc,EAAM,YAGvB,EAAE,cAAW,GAAG,KAAS,MAC1B,kBAAC,SAAD;CACO;CACL,WAAW,EAIT,mGACA,CACF;CACA,GAAI;AACL,CAAA,CACF;AACD,EAAY,cAAc;AAE1B,IAAM,IAAW,EAAM,YAGpB,EAAE,cAAW,GAAG,KAAS,MAC1B,kBAAC,MAAD;CACO;CACL,WAAW,EAQT,sQACA,2KACA,CACF;CACA,GAAI;AACL,CAAA,CACF;AACD,EAAS,cAAc;AAEvB,IAAM,IAAY,EAAM,YAGrB,EAAE,cAAW,YAAS,GAAG,KAAS,MACnC,kBAAC,MAAD;CACO;CACL,OAAM;CACN,WAAW,EAGT,qNACA,KAAW,cACX,CACF;CACA,GAAI;AACL,CAAA,CACF;AACD,EAAU,cAAc;AAExB,IAAM,IAAY,EAAM,YAGrB,EAAE,cAAW,YAAS,GAAG,KAAS,MACnC,kBAAC,MAAD;CACO;CACL,WAAW,EACT,4JACA,KAAW,2BACX,CACF;CACA,GAAI;AACL,CAAA,CACF;AACD,EAAU,cAAc;AA2BxB,IAAM,IAAkB,EAAM,YAC3B,EAAE,cAAW,YAAS,GAAG,KAAS,MACjC,kBAAC,OAAD;CACO;CACL,WAAW,EACT,wGACA,IACI,gBACA,uGACJ,CACF;CACA,GAAI;AACL,CAAA,CAEL;AACA,EAAgB,cAAc;AAE9B,IAAM,IAAe,EAAM,YAGxB,EAAE,cAAW,GAAG,KAAS,MAC1B,kBAAC,WAAD;CACO;CACL,WAAW,EAAG,+CAA+C,CAAS;CACtE,GAAI;AACL,CAAA,CACF;AACD,EAAa,cAAc"}
|
|
1
|
+
{"version":3,"file":"table.js","names":[],"sources":["../../src/ui/table.tsx"],"sourcesContent":["// @server-safe\nimport * as React from \"react\"\n\nimport { cn } from \"./lib/utils\"\n\n/** Row density — sets --table-py, which header and body cells both read. */\ntype TableDensity = 'compact' | 'standard' | 'comfortable'\n\n// One variable pair drives the table's spacing (same pattern as Card):\n// --table-py vertical cell padding per density (4 / 8 / 12px → rows ≈ 29 / 37 / 45px)\n// --table-edge first/last-cell inline padding — inherits --card-spacing when the table\n// sits inside a Card, so edge columns align with the card's slots;\n// falls back to ds-04 (12px) standalone.\nconst densityClasses: Record<TableDensity, string> = {\n compact: '[--table-py:var(--spacing-ds-02)]',\n standard: '[--table-py:var(--spacing-ds-03)]',\n comfortable: '[--table-py:var(--spacing-ds-04)]',\n}\n\nexport interface TableCellBaseProps {\n /**\n * Quantitative column: right-aligns and uses tabular figures so digits line up.\n * Keep decimal places consistent per column; identifier-numbers (dates, phones,\n * IDs) stay left-aligned — they're names, not quantities.\n */\n numeric?: boolean\n}\n\nexport interface TableProps extends React.HTMLAttributes<HTMLTableElement> {\n /** Row density — vertical cell padding for header + body. @default 'standard' */\n density?: TableDensity\n /**\n * Zebra striping (even body rows get the faintest surface step). Opt-in only —\n * for very wide/dense tables; hairline separators are the default row cue.\n */\n striped?: boolean\n}\n\nconst Table = React.forwardRef<HTMLTableElement, TableProps>(\n ({ className, density = 'standard', striped, ...props }, ref) => (\n <div className=\"relative w-full overflow-auto\">\n <table\n ref={ref}\n className={cn(\n // overflow-x-clip contains TableRowLink's 100vw stretch pseudo-element\n // without creating a horizontal scrollbar (the wrapper owns scrolling).\n \"w-full caption-bottom text-body-md overflow-x-clip [--table-edge:var(--card-spacing,var(--spacing-ds-04))]\",\n densityClasses[density],\n // `:not([data-state=selected])` is load-bearing, not defensive. The\n // stripe selector compiles to (0,2,2) and TableRow's own\n // `data-[state=selected]:bg-accent-4` to (0,2,0), so the stripe WON\n // and a selected even row rendered grey — selection was invisible on\n // half the rows, revealing itself only on hover where (0,3,0) took\n // over. Excluding selected rows from the match makes the contest moot\n // rather than escalating specificity, which the next stripe variant\n // would only have to escalate again.\n striped &&\n \"[&_tbody_tr:nth-child(even):not([data-state=selected])]:bg-surface-panel-hover\",\n // A pinned cell is opaque by necessity (it occludes scrolled content),\n // so the row's stripe cannot show through it — the cell has to be\n // striped too or a striped table shows a white notch in the pinned\n // column. Keyed off `data-pinned` so this repaints only those cells,\n // not every cell in the row.\n striped &&\n \"[&_tbody_tr:nth-child(even):not([data-state=selected])_[data-pinned]]:bg-surface-panel-hover\",\n className,\n )}\n {...props}\n />\n </div>\n ),\n)\nTable.displayName = \"Table\"\n\nconst TableHeader = React.forwardRef<\n HTMLTableSectionElement,\n React.HTMLAttributes<HTMLTableSectionElement>\n>(({ className, ...props }, ref) => (\n <thead ref={ref} className={cn(\"[&_tr]:border-b [&_tr]:border-surface-border-subtle\", className)} {...props} />\n))\nTableHeader.displayName = \"TableHeader\"\n\nconst TableBody = React.forwardRef<\n HTMLTableSectionElement,\n React.HTMLAttributes<HTMLTableSectionElement>\n>(({ className, ...props }, ref) => (\n <tbody\n ref={ref}\n className={cn(\"[&_tr:last-child]:border-0\", className)}\n {...props}\n />\n))\nTableBody.displayName = \"TableBody\"\n\nconst TableFooter = React.forwardRef<\n HTMLTableSectionElement,\n React.HTMLAttributes<HTMLTableSectionElement>\n>(({ className, ...props }, ref) => (\n <tfoot\n ref={ref}\n className={cn(\n // surface-base band, not raised@50% — the footer must read against the\n // card surface the table lives on (same mis-mapped shadcn muted/50 family\n // as the row-hover bug).\n \"border-t border-surface-border-subtle bg-surface-panel-hover font-medium [&>tr]:last:border-b-0\",\n className\n )}\n {...props}\n />\n))\nTableFooter.displayName = \"TableFooter\"\n\nconst TableRow = React.forwardRef<\n HTMLTableRowElement,\n React.HTMLAttributes<HTMLTableRowElement>\n>(({ className, ...props }, ref) => (\n <tr\n ref={ref}\n className={cn(\n // panel-hover, NOT panel — tables live on cards (surface-panel), so a\n // surface-panel hover would be invisible (the 0.44-era port bug).\n // selected+hover gets its own explicit step — without it the hover and\n // selected classes tie on specificity and stylesheet order decides.\n // `group/row` lets TableRowActions reveal on row hover/focus; the has-[]\n // rule draws a row-level focus ring when a TableRowLink inside is\n // keyboard-focused (the anchor itself suppresses its own ring).\n \"group/row border-b border-surface-border-subtle transition-colors hover:bg-surface-panel-hover data-[state=selected]:bg-accent-4 data-[state=selected]:hover:bg-accent-5 data-[state=selected]:forced-colors:outline data-[state=selected]:forced-colors:outline-1\",\n \"has-[[data-slot=row-link]:focus-visible]:outline-2 has-[[data-slot=row-link]:focus-visible]:outline-accent-9 has-[[data-slot=row-link]:focus-visible]:-outline-offset-2\",\n className,\n )}\n {...props}\n />\n))\nTableRow.displayName = \"TableRow\"\n\nconst TableHead = React.forwardRef<\n HTMLTableCellElement,\n React.ThHTMLAttributes<HTMLTableCellElement> & TableCellBaseProps\n>(({ className, numeric, ...props }, ref) => (\n <th\n ref={ref}\n scope=\"col\"\n className={cn(\n // Header is quieter than the data: one step smaller, medium, muted.\n // Height tracks density via the same --table-py the body cells read.\n \"py-(--table-py) px-ds-04 first:pl-(--table-edge) last:pr-(--table-edge) text-left align-middle text-body-sm font-medium text-surface-fg-muted [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]\",\n numeric && \"text-right\",\n className,\n )}\n {...props}\n />\n))\nTableHead.displayName = \"TableHead\"\n\nconst TableCell = React.forwardRef<\n HTMLTableCellElement,\n React.TdHTMLAttributes<HTMLTableCellElement> & TableCellBaseProps\n>(({ className, numeric, ...props }, ref) => (\n <td\n ref={ref}\n className={cn(\n \"py-(--table-py) px-ds-04 first:pl-(--table-edge) last:pr-(--table-edge) align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]\",\n numeric && \"text-right tabular-nums\",\n className,\n )}\n {...props}\n />\n))\nTableCell.displayName = \"TableCell\"\n\nexport interface TableRowActionsProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Always show the actions instead of revealing on row hover/focus. Use for\n * tables where actions must be permanently discoverable (the GitLab stance),\n * or when the row has few columns and the density win doesn't matter.\n */\n persist?: boolean\n}\n\n/**\n * Right-aligned action cluster for a table row, revealed on row hover — and,\n * critically, on keyboard focus: the buttons stay in the tab order permanently\n * (opacity reveal, never display:none) and appear the moment focus enters the\n * row (WCAG 1.4.13). On touch devices (no hover) they are always visible.\n *\n * Give the actions column a visually-hidden header: `<TableHead><span className=\"sr-only\">Actions</span></TableHead>`.\n *\n * @example\n * <TableCell>\n * <TableRowActions>\n * <IconButton size=\"xs\" variant=\"ghost\" aria-label={`Download ${name}`} icon={<IconDownload />} />\n * <IconButton size=\"xs\" variant=\"ghost\" aria-label={`Delete ${name}`} icon={<IconTrash />} />\n * </TableRowActions>\n * </TableCell>\n */\nconst TableRowActions = React.forwardRef<HTMLDivElement, TableRowActionsProps>(\n ({ className, persist, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n \"flex items-center justify-end gap-ds-01 transition-opacity duration-fast-01 ease-productive-standard\",\n persist\n ? \"opacity-100\"\n : \"opacity-0 group-hover/row:opacity-100 group-focus-within/row:opacity-100 pointer-coarse:opacity-100\",\n className,\n )}\n {...props}\n />\n ),\n)\nTableRowActions.displayName = \"TableRowActions\"\n\nconst TableCaption = React.forwardRef<\n HTMLTableCaptionElement,\n React.HTMLAttributes<HTMLTableCaptionElement>\n>(({ className, ...props }, ref) => (\n <caption\n ref={ref}\n className={cn(\"mt-ds-05 text-body-md text-surface-fg-muted\", className)}\n {...props}\n />\n))\nTableCaption.displayName = \"TableCaption\"\n\nexport type TableRowProps = React.HTMLAttributes<HTMLTableRowElement>\nexport type TableCellProps = React.TdHTMLAttributes<HTMLTableCellElement>\nexport type { TableDensity }\n\nexport {\n Table,\n TableBody,\n TableCaption,\n TableCell,\n TableFooter,\n TableHead,\n TableHeader,\n TableRow,\n TableRowActions,\n}\n"],"mappings":";;;;AAaA,IAAM,IAA+C;CACnD,SAAS;CACT,UAAU;CACV,aAAa;AACf,GAqBM,IAAQ,EAAM,YACjB,EAAE,cAAW,aAAU,YAAY,YAAS,GAAG,KAAS,MACvD,kBAAC,OAAD;CAAK,WAAU;WACb,kBAAC,SAAD;EACO;EACL,WAAW,EAGT,8GACA,EAAe,IASf,KACE,kFAMF,KACE,gGACF,CACF;EACA,GAAI;CACL,CAAA;AACE,CAAA,CAET;AACA,EAAM,cAAc;AAEpB,IAAM,IAAc,EAAM,YAGvB,EAAE,cAAW,GAAG,KAAS,MAC1B,kBAAC,SAAD;CAAY;CAAK,WAAW,EAAG,uDAAuD,CAAS;CAAG,GAAI;AAAQ,CAAA,CAC/G;AACD,EAAY,cAAc;AAE1B,IAAM,IAAY,EAAM,YAGrB,EAAE,cAAW,GAAG,KAAS,MAC1B,kBAAC,SAAD;CACO;CACL,WAAW,EAAG,8BAA8B,CAAS;CACrD,GAAI;AACL,CAAA,CACF;AACD,EAAU,cAAc;AAExB,IAAM,IAAc,EAAM,YAGvB,EAAE,cAAW,GAAG,KAAS,MAC1B,kBAAC,SAAD;CACO;CACL,WAAW,EAIT,mGACA,CACF;CACA,GAAI;AACL,CAAA,CACF;AACD,EAAY,cAAc;AAE1B,IAAM,IAAW,EAAM,YAGpB,EAAE,cAAW,GAAG,KAAS,MAC1B,kBAAC,MAAD;CACO;CACL,WAAW,EAQT,sQACA,2KACA,CACF;CACA,GAAI;AACL,CAAA,CACF;AACD,EAAS,cAAc;AAEvB,IAAM,IAAY,EAAM,YAGrB,EAAE,cAAW,YAAS,GAAG,KAAS,MACnC,kBAAC,MAAD;CACO;CACL,OAAM;CACN,WAAW,EAGT,qNACA,KAAW,cACX,CACF;CACA,GAAI;AACL,CAAA,CACF;AACD,EAAU,cAAc;AAExB,IAAM,IAAY,EAAM,YAGrB,EAAE,cAAW,YAAS,GAAG,KAAS,MACnC,kBAAC,MAAD;CACO;CACL,WAAW,EACT,4JACA,KAAW,2BACX,CACF;CACA,GAAI;AACL,CAAA,CACF;AACD,EAAU,cAAc;AA2BxB,IAAM,IAAkB,EAAM,YAC3B,EAAE,cAAW,YAAS,GAAG,KAAS,MACjC,kBAAC,OAAD;CACO;CACL,WAAW,EACT,wGACA,IACI,gBACA,uGACJ,CACF;CACA,GAAI;AACL,CAAA,CAEL;AACA,EAAgB,cAAc;AAE9B,IAAM,IAAe,EAAM,YAGxB,EAAE,cAAW,GAAG,KAAS,MAC1B,kBAAC,WAAD;CACO;CACL,WAAW,EAAG,+CAA+C,CAAS;CACtE,GAAI;AACL,CAAA,CACF;AACD,EAAa,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"textarea.d.ts","sourceRoot":"","sources":["../../src/ui/textarea.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEjE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,OAAO,EAAE,KAAK,UAAU,EAAqB,MAAM,mBAAmB,CAAA;AAItE,QAAA,MAAM,gBAAgB;;
|
|
1
|
+
{"version":3,"file":"textarea.d.ts","sourceRoot":"","sources":["../../src/ui/textarea.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEjE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,OAAO,EAAE,KAAK,UAAU,EAAqB,MAAM,mBAAmB,CAAA;AAItE,QAAA,MAAM,gBAAgB;;8EA2BrB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,WAAW,aACf,SAAQ,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC,EACrE,YAAY,CAAC,OAAO,gBAAgB,CAAC;IACvC,KAAK,CAAC,EAAE,UAAU,CAAA;CACnB;AAED,QAAA,MAAM,QAAQ,2FA6Bb,CAAA;AAGD,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAA"}
|
package/dist/ui/textarea.js
CHANGED
|
@@ -10,20 +10,20 @@ import { cva as s } from "class-variance-authority";
|
|
|
10
10
|
//#region src/ui/textarea.tsx
|
|
11
11
|
var c = s([
|
|
12
12
|
"flex w-full font-sans resize-y",
|
|
13
|
-
"bg-
|
|
13
|
+
"bg-field text-surface-fg",
|
|
14
14
|
"border border-surface-border-interactive rounded-control",
|
|
15
15
|
"placeholder:text-surface-fg-muted",
|
|
16
|
-
"hover:bg-
|
|
16
|
+
"hover:bg-field-hover",
|
|
17
17
|
"transition-colors duration-fast-01 ease-productive-standard",
|
|
18
18
|
"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 focus-visible:border-accent-7",
|
|
19
19
|
"disabled:cursor-not-allowed disabled:opacity-action-disabled",
|
|
20
20
|
"read-only:bg-surface-sunken read-only:cursor-default"
|
|
21
21
|
], {
|
|
22
22
|
variants: { size: {
|
|
23
|
-
xs: "min-h-ds-09 text-body-sm px-ds-
|
|
24
|
-
sm: "min-h-[60px] text-body-sm px-ds-
|
|
23
|
+
xs: "min-h-ds-09 text-body-sm px-ds-04 py-ds-03",
|
|
24
|
+
sm: "min-h-[60px] text-body-sm px-ds-04 py-ds-03",
|
|
25
25
|
md: "min-h-ds-11 text-body-md px-ds-04 py-ds-03",
|
|
26
|
-
lg: "min-h-[120px] text-body-md px-ds-
|
|
26
|
+
lg: "min-h-[120px] text-body-md px-ds-04 py-ds-03"
|
|
27
27
|
} },
|
|
28
28
|
defaultVariants: { size: "md" }
|
|
29
29
|
}), l = i.forwardRef(({ className: i, state: s, size: l, ...u }, d) => {
|
|
@@ -32,7 +32,7 @@ var c = s([
|
|
|
32
32
|
"aria-invalid": p === "error" || void 0,
|
|
33
33
|
"aria-describedby": m,
|
|
34
34
|
"aria-required": h || void 0,
|
|
35
|
-
className: t(c({ size: l }), p === "error" && "border-error-
|
|
35
|
+
className: t(c({ size: l }), p === "error" && "border-error-8 focus-visible:ring-error-7", p === "warning" && "border-warning-7 focus-visible:ring-warning-7", p === "success" && "border-success-8 focus-visible:ring-success-7", i),
|
|
36
36
|
ref: d,
|
|
37
37
|
...e(u),
|
|
38
38
|
id: g
|
package/dist/ui/textarea.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"textarea.js","names":[],"sources":["../../src/ui/textarea.tsx"],"sourcesContent":["'use client'\n\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useFormField } from './form'\nimport { type FieldState, resolveFieldState } from './lib/field-state'\nimport { motionProps } from './lib/motion'\nimport { cn } from './lib/utils'\n\nconst textareaVariants = cva(\n [\n 'flex w-full font-sans resize-y',\n 'bg-
|
|
1
|
+
{"version":3,"file":"textarea.js","names":[],"sources":["../../src/ui/textarea.tsx"],"sourcesContent":["'use client'\n\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useFormField } from './form'\nimport { type FieldState, resolveFieldState } from './lib/field-state'\nimport { motionProps } from './lib/motion'\nimport { cn } from './lib/utils'\n\nconst textareaVariants = cva(\n [\n 'flex w-full font-sans resize-y',\n 'bg-field text-surface-fg',\n 'border border-surface-border-interactive rounded-control',\n 'placeholder:text-surface-fg-muted',\n 'hover:bg-field-hover',\n 'transition-colors duration-fast-01 ease-productive-standard',\n 'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 focus-visible:border-accent-7',\n 'disabled:cursor-not-allowed disabled:opacity-action-disabled',\n 'read-only:bg-surface-sunken read-only:cursor-default',\n ],\n {\n variants: {\n // Min-height ramp 48/60/80/120: 48/80 map to spacing tokens (ds-09/ds-11);\n // 60/120 have no exact token so stay arbitrary (allowed). Rendered sizes unchanged.\n // Padding is uniform across sizes (design 2026-08-24): 12px inline, 8px\n // block. Only the min-height ramp still varies.\n size: {\n xs: 'min-h-ds-09 text-body-sm px-ds-04 py-ds-03',\n sm: 'min-h-[60px] text-body-sm px-ds-04 py-ds-03',\n md: 'min-h-ds-11 text-body-md px-ds-04 py-ds-03',\n lg: 'min-h-[120px] text-body-md px-ds-04 py-ds-03',\n },\n },\n defaultVariants: { size: 'md' },\n },\n)\n\n/**\n * Props for Textarea — a resizable multi-line text input with size variants and validation state coloring,\n * sharing the same `FieldState` type as every form control.\n *\n * **Sizes:** `sm` (min 60px) | `md` (min 80px, default) | `lg` (min 120px) — all are vertically resizable.\n *\n * **Validation states:** `state=\"error\"` | `\"warning\"` | `\"success\"` — changes border color and focus ring.\n * HTML's native `size` attribute is omitted; use `rows` for initial height or CSS `min-height`.\n *\n * **Pair with FormField:** Use inside `<FormField>` to get a label, helper text, and associated `state`.\n *\n * @example\n * // Basic description textarea in a form:\n * <Textarea placeholder=\"Describe the issue...\" rows={4} />\n *\n * @example\n * // Error state when validation fails:\n * <Textarea state=\"error\" value={bio} onChange={(e) => setBio(e.target.value)} />\n *\n * @example\n * // Large textarea for a full email draft composer:\n * <Textarea size=\"lg\" placeholder=\"Write your message...\" className=\"min-h-[200px]\" />\n *\n * @example\n * // Read-only view of a previously submitted note:\n * <Textarea readOnly value={submission.notes} size=\"md\" />\n * // These are just a few ways — feel free to combine props creatively!\n */\nexport interface TextareaProps\n extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'>,\n VariantProps<typeof textareaVariants> {\n state?: FieldState\n}\n\nconst Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(\n ({ className, state: stateProp, size, ...props }, ref) => {\n const fieldCtx = useFormField()\n // Merge FormField context — explicit props always win (shared precedence)\n const state = resolveFieldState(stateProp, fieldCtx.state)\n const ariaDescribedBy = props['aria-describedby'] ?? fieldCtx.helperTextId\n const ariaRequired = props['aria-required'] ?? fieldCtx.required\n // Explicit id wins; otherwise adopt FormField's inputId so <Label htmlFor> resolves.\n const textareaId = props.id ?? fieldCtx.inputId\n\n return (\n <motion.textarea\n aria-invalid={state === 'error' || undefined}\n aria-describedby={ariaDescribedBy}\n aria-required={ariaRequired || undefined}\n className={cn(\n textareaVariants({ size }),\n // Step 8 clears the 3:1 non-text bar; warning stays at 7 per the note.\n state === 'error' && 'border-error-8 focus-visible:ring-error-7',\n state === 'warning' && 'border-warning-7 focus-visible:ring-warning-7',\n state === 'success' && 'border-success-8 focus-visible:ring-success-7',\n className,\n )}\n ref={ref}\n {...motionProps(props)}\n id={textareaId}\n />\n )\n },\n)\nTextarea.displayName = 'Textarea'\n\nexport { Textarea, textareaVariants }\n"],"mappings":";;;;;;;;;;AAWA,IAAM,IAAmB,EACvB;CACE;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,GACA;CACE,UAAU,EAKR,MAAM;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;CACN,EACF;CACA,iBAAiB,EAAE,MAAM,KAAK;AAChC,CACF,GAoCM,IAAW,EAAM,YACpB,EAAE,cAAW,OAAO,GAAW,SAAM,GAAG,KAAS,MAAQ;CACxD,IAAM,IAAW,EAAa,GAExB,IAAQ,EAAkB,GAAW,EAAS,KAAK,GACnD,IAAkB,EAAM,uBAAuB,EAAS,cACxD,IAAe,EAAM,oBAAoB,EAAS,UAElD,IAAa,EAAM,MAAM,EAAS;CAExC,OACE,kBAAC,EAAO,UAAR;EACE,gBAAc,MAAU,WAAW,KAAA;EACnC,oBAAkB;EAClB,iBAAe,KAAgB,KAAA;EAC/B,WAAW,EACT,EAAiB,EAAE,QAAK,CAAC,GAEzB,MAAU,WAAW,6CACrB,MAAU,aAAa,iDACvB,MAAU,aAAa,iDACvB,CACF;EACK;EACL,GAAI,EAAY,CAAK;EACrB,IAAI;CACL,CAAA;AAEL,CACF;AACA,EAAS,cAAc"}
|
package/dist/ui/toast.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toast.d.ts","sourceRoot":"","sources":["../../src/ui/toast.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,KAAK,aAAa,EAAuB,MAAM,QAAQ,CAAA;AAShE,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,kBAAkB,EAClB,UAAU,EACX,MAAM,eAAe,CAAA;AAiEtB,iBAAS,QAAQ,CAAC,EAChB,QAAQ,EACR,IAAI,EACJ,MAAM,GACP,EAAE;IACD,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,SAAS,CAAA;IACf,MAAM,EAAE,OAAO,CAAA;CAChB,
|
|
1
|
+
{"version":3,"file":"toast.d.ts","sourceRoot":"","sources":["../../src/ui/toast.tsx"],"names":[],"mappings":"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"}
|
package/dist/ui/toast.js
CHANGED
|
@@ -53,7 +53,7 @@ var T = 5e3, E = 8e3, D = 3e3, O = {
|
|
|
53
53
|
function k({ duration: e, type: t, paused: n }) {
|
|
54
54
|
let i = O[t];
|
|
55
55
|
return /* @__PURE__ */ l("div", {
|
|
56
|
-
className: "absolute bottom-0 left-0 right-0 h-ds-01 overflow-hidden",
|
|
56
|
+
className: "absolute bottom-0 left-0 right-0 h-ds-01 overflow-hidden bg-surface-panel-hover",
|
|
57
57
|
children: /* @__PURE__ */ l("div", {
|
|
58
58
|
className: r("h-full w-full origin-left opacity-30", i.timerBarClass, "motion-safe:animate-timer-bar"),
|
|
59
59
|
style: {
|
package/dist/ui/toast.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toast.js","names":[],"sources":["../../src/ui/toast.tsx"],"sourcesContent":["'use client'\n\nimport {\n IconAlertCircle,\n IconAlertTriangle,\n IconCheck,\n IconCircleCheck,\n IconCircleX,\n IconFile,\n IconInfoCircle,\n IconPhoto,\n IconRefresh,\n IconUpload,\n IconX,\n} from '@tabler/icons-react'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\nimport { type ExternalToast,toast as sonnerToast } from 'sonner'\n\nimport { MotionPreference } from '../motion/motion-preference'\nimport { Icon } from './icon'\nimport { springs, tweens } from './lib/motion'\nimport { cn } from './lib/utils'\nimport { Progress } from './progress'\nimport { Spinner } from './spinner'\nimport { assertToasterMounted } from './toast-registry'\nimport type {\n ToastActionOptions,\n ToastOptions,\n ToastType,\n ToastUndoOptions,\n ToastUploadOptions,\n UploadFile,\n} from './toast-types'\n\n/* ---------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------ */\n\nconst DEFAULT_DURATION = 5000\nconst UNDO_DURATION = 8000\nconst UPLOAD_COMPLETE_DELAY = 3000\n\n/* ---------------------------------------------------------------------------\n * Accent bar & icon config per toast type\n * ------------------------------------------------------------------------ */\n\nconst TOAST_TYPE_CONFIG: Record<\n ToastType,\n {\n accentClass: string\n iconClass: string\n icon: React.ForwardRefExoticComponent<any> | null\n timerBarClass: string\n }\n> = {\n message: {\n accentClass: '',\n iconClass: '',\n icon: null,\n timerBarClass: 'bg-surface-border',\n },\n success: {\n accentClass: 'bg-success-9',\n iconClass: 'text-success-11',\n icon: IconCircleCheck,\n timerBarClass: 'bg-success-9',\n },\n error: {\n accentClass: 'bg-error-9',\n iconClass: 'text-error-11',\n icon: IconCircleX,\n timerBarClass: 'bg-error-9',\n },\n warning: {\n accentClass: 'bg-warning-9',\n iconClass: 'text-warning-11',\n icon: IconAlertTriangle,\n timerBarClass: 'bg-warning-9',\n },\n info: {\n accentClass: 'bg-info-9',\n iconClass: 'text-info-11',\n icon: IconInfoCircle,\n timerBarClass: 'bg-info-9',\n },\n loading: {\n accentClass: 'bg-accent-9',\n iconClass: 'text-accent-11',\n icon: null,\n timerBarClass: 'bg-accent-9',\n },\n}\n\n/* ---------------------------------------------------------------------------\n * Timer bar — shows auto-dismiss countdown\n * ------------------------------------------------------------------------ */\n\nfunction TimerBar({\n duration,\n type,\n paused,\n}: {\n duration: number\n type: ToastType\n paused: boolean\n}) {\n const config = TOAST_TYPE_CONFIG[type]\n return (\n <div className=\"absolute bottom-0 left-0 right-0 h-ds-01 overflow-hidden\">\n <div\n className={cn(\n 'h-full w-full origin-left opacity-30',\n config.timerBarClass,\n 'motion-safe:animate-timer-bar',\n )}\n style={{\n animationDuration: `${duration}ms`,\n animationPlayState: paused ? 'paused' : 'running',\n }}\n />\n </div>\n )\n}\n\n/* ---------------------------------------------------------------------------\n * Base toast UI — used by all typed toasts\n * ------------------------------------------------------------------------ */\n\nfunction ToastContent({\n type,\n title,\n description,\n action,\n cancel,\n duration = DEFAULT_DURATION,\n showTimerBar = true,\n showAccent = false,\n selfDismissId,\n}: {\n type: ToastType\n title?: React.ReactNode\n description?: React.ReactNode\n action?: ToastActionOptions\n cancel?: ToastActionOptions\n duration?: number\n showTimerBar?: boolean\n /** Render the colored left accent bar. Off by default (status = icon + timer bar). */\n showAccent?: boolean\n /** When set, this component manages its own dismiss timer (hover-aware). */\n selfDismissId?: string | number\n}) {\n const [hovered, setHovered] = React.useState(false)\n const [focused, setFocused] = React.useState(false)\n const paused = hovered || focused\n const config = TOAST_TYPE_CONFIG[type]\n const StatusIcon = config.icon\n\n // Self-managed dismiss timer that pauses on hover or keyboard focus\n const remainingRef = React.useRef(duration)\n const startTimeRef = React.useRef(0)\n\n React.useEffect(() => {\n if (selfDismissId == null || duration === Infinity) return\n if (paused) {\n // Pause: save remaining time\n const elapsed = Date.now() - startTimeRef.current\n remainingRef.current = Math.max(0, remainingRef.current - elapsed)\n return\n }\n // Start/resume timer\n startTimeRef.current = Date.now()\n const timer = setTimeout(() => {\n sonnerToast.dismiss(selfDismissId)\n }, remainingRef.current)\n return () => clearTimeout(timer)\n }, [selfDismissId, duration, paused])\n\n // Error toasts announce assertively so screen readers interrupt;\n // warnings stay polite but atomically re-read the message.\n const isUrgent = type === 'error'\n return (\n <MotionPreference>\n <motion.div\n layout=\"position\"\n role={isUrgent ? 'alert' : 'status'}\n aria-live={isUrgent ? 'assertive' : 'polite'}\n aria-atomic=\"true\"\n className={cn(\n 'group relative flex w-full overflow-hidden rounded-overlay-sm bg-surface-overlay shadow-floating',\n // Faint surface tint on error only — the one you must not miss.\n type === 'error' && 'bg-error-2',\n )}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n onFocusCapture={() => setFocused(true)}\n onBlurCapture={() => setFocused(false)}\n transition={springs.smooth}\n >\n {/* Left accent bar — opt-in via showAccent (status is carried by icon + timer bar) */}\n {showAccent && config.accentClass && (\n <div\n className={cn('w-1 shrink-0 rounded-l-overlay-sm', config.accentClass)}\n />\n )}\n\n {/* Content */}\n <div className=\"flex min-w-0 flex-1 items-start gap-ds-03 p-ds-04\">\n {/* Status icon — animates between spinner ↔ typed icon */}\n <AnimatePresence mode=\"wait\">\n {type === 'loading' ? (\n <motion.div\n key=\"loading\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={tweens.fade}\n className=\"mt-0.5 shrink-0\"\n >\n <Spinner size=\"sm\" className=\"h-4 w-4\" />\n </motion.div>\n ) : StatusIcon ? (\n <motion.div\n key={type}\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n className=\"mt-0.5 shrink-0\"\n >\n <Icon icon={StatusIcon} size=\"sm\" className={config.iconClass} />\n </motion.div>\n ) : null}\n </AnimatePresence>\n\n {/* Text */}\n <div className=\"min-w-0 flex-1\">\n {title && (\n <p className=\"text-body-md font-semibold text-surface-fg\">\n {title}\n </p>\n )}\n {description && (\n <p\n className={cn(\n 'text-body-sm text-surface-fg-muted',\n title && 'mt-0.5',\n )}\n >\n {description}\n </p>\n )}\n\n {/* Action / cancel buttons */}\n {(action || cancel) && (\n <div className=\"mt-ds-03 flex items-center gap-ds-03\">\n {action && (\n <button\n type=\"button\"\n onClick={action.onClick}\n className=\"text-body-sm font-medium text-accent-11 underline-offset-2 hover:underline hover:bg-surface-panel-hover rounded-control-inner px-ds-02 py-ds-01 transition-[color,background-color] duration-fast-01 ease-productive-standard focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:rounded-control-inner\"\n >\n {action.label}\n </button>\n )}\n {cancel && (\n <button\n type=\"button\"\n onClick={cancel.onClick}\n className=\"text-body-sm text-surface-fg-muted hover:text-surface-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:rounded-control-inner\"\n >\n {cancel.label}\n </button>\n )}\n </div>\n )}\n </div>\n </div>\n\n {/* Timer bar */}\n {showTimerBar && type !== 'loading' && (\n <TimerBar duration={duration} type={type} paused={paused} />\n )}\n </motion.div>\n </MotionPreference>\n )\n}\n\n/* ---------------------------------------------------------------------------\n * Upload toast UI\n * ------------------------------------------------------------------------ */\n\nconst IMAGE_EXTENSIONS = new Set([\n 'jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp', 'ico', 'avif',\n])\n\nfunction isImageFile(file: UploadFile): boolean {\n if (file.previewUrl) return true\n const ext = file.name.split('.').pop()?.toLowerCase() ?? ''\n return IMAGE_EXTENSIONS.has(ext)\n}\n\nfunction getProgressColor(\n status: UploadFile['status'],\n): 'accent' | 'success' | 'error' {\n switch (status) {\n case 'complete':\n return 'success'\n case 'error':\n return 'error'\n default:\n return 'accent'\n }\n}\n\nfunction getProgressValue(file: UploadFile): number | undefined {\n switch (file.status) {\n case 'pending':\n return 0\n case 'uploading':\n return file.progress ?? 0\n case 'processing':\n return undefined\n case 'complete':\n return 100\n case 'error':\n return file.progress ?? 0\n }\n}\n\nfunction formatFileSize(bytes: number): string {\n if (!bytes || bytes < 0 || !Number.isFinite(bytes)) return '0 B'\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n if (bytes < 1024 * 1024 * 1024)\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`\n}\n\nfunction UploadFileRow({\n file,\n onRetry,\n onRemove,\n}: {\n file: UploadFile\n onRetry?: (fileId: string) => void\n onRemove?: (fileId: string) => void\n}) {\n const isTerminal = file.status === 'complete' || file.status === 'error'\n const isUploading =\n file.status === 'uploading' || file.status === 'processing'\n\n return (\n <MotionPreference>\n <motion.div\n layout\n className=\"flex items-center gap-ds-02 py-1\"\n initial={false}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, x: -20, height: 0 }}\n transition={springs.snappy}\n >\n {/* File icon — animates between states */}\n <div className=\"flex h-5 w-5 shrink-0 items-center justify-center\">\n <AnimatePresence mode=\"wait\">\n {file.status === 'complete' ? (\n <motion.div\n key=\"complete\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n >\n <Icon icon={IconCheck} size=\"xs\" className=\"text-success-11\" />\n </motion.div>\n ) : file.status === 'error' ? (\n <motion.div\n key=\"error\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={tweens.fade}\n >\n <Icon icon={IconAlertCircle} size=\"xs\" className=\"text-error-11\" />\n </motion.div>\n ) : file.status === 'processing' ? (\n <motion.div\n key=\"processing\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Spinner size=\"sm\" className=\"h-3.5 w-3.5\" />\n </motion.div>\n ) : isImageFile(file) ? (\n <motion.div\n key=\"image\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Icon icon={IconPhoto} size=\"xs\" className=\"text-surface-fg-muted\" />\n </motion.div>\n ) : (\n <motion.div\n key=\"file\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Icon icon={IconFile} size=\"xs\" className=\"text-surface-fg-muted\" />\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n\n {/* Filename */}\n <span className=\"min-w-0 flex-1 truncate text-body-xs text-surface-fg\">\n {file.name}\n </span>\n\n {/* Progress bar or status */}\n {isUploading ? (\n <div className=\"flex w-16 items-center gap-1\">\n <Progress\n size=\"sm\"\n color={getProgressColor(file.status)}\n value={getProgressValue(file)}\n className=\"flex-1\"\n />\n {file.progress !== undefined && (\n <span className=\"shrink-0 text-caption tabular-nums text-surface-fg-muted\">\n {file.progress}%\n </span>\n )}\n </div>\n ) : file.status === 'complete' ? (\n <span className=\"text-body-xs text-success-11\">Done</span>\n ) : file.status === 'error' ? (\n <span className=\"max-w-[60px] truncate text-body-xs text-error-11\">\n {file.error || 'Failed'}\n </span>\n ) : (\n <span className=\"text-caption text-surface-fg-muted\">\n {formatFileSize(file.size)}\n </span>\n )}\n\n {/* Actions */}\n {file.status === 'error' && onRetry && (\n <button\n type=\"button\"\n onClick={() => onRetry(file.id)}\n className=\"touch-target flex min-h-6 min-w-6 items-center justify-center rounded-control-inner text-surface-fg-muted hover:text-surface-fg\"\n aria-label={`Retry ${file.name}`}\n title=\"Retry\"\n >\n <IconRefresh className=\"h-3.5 w-3.5\" />\n </button>\n )}\n {!isTerminal && onRemove && (\n <button\n type=\"button\"\n onClick={() => onRemove(file.id)}\n className=\"touch-target flex min-h-6 min-w-6 items-center justify-center rounded-control-inner text-surface-fg-muted hover:text-surface-fg\"\n aria-label={`Cancel ${file.name}`}\n title=\"Remove\"\n >\n <IconX className=\"h-3.5 w-3.5\" />\n </button>\n )}\n </motion.div>\n </MotionPreference>\n )\n}\n\nfunction UploadToastContent({\n files,\n onRetry,\n onRemove,\n selfDismissId,\n showAccent = false,\n}: {\n files: UploadFile[]\n onRetry?: (fileId: string) => void\n onRemove?: (fileId: string) => void\n selfDismissId?: string | number\n /** Render the colored left accent bar. Off by default. */\n showAccent?: boolean\n}) {\n const [hovered, setHovered] = React.useState(false)\n const [focused, setFocused] = React.useState(false)\n const paused = hovered || focused\n const completeCount = files.filter((f) => f.status === 'complete').length\n const errorCount = files.filter((f) => f.status === 'error').length\n const allTerminal = files.every(\n (f) => f.status === 'complete' || f.status === 'error',\n )\n\n // Self-managed dismiss timer that pauses on hover or keyboard focus\n const remainingRef = React.useRef(UPLOAD_COMPLETE_DELAY)\n const startTimeRef = React.useRef(0)\n\n React.useEffect(() => {\n if (selfDismissId == null || !allTerminal) return\n if (paused) {\n const elapsed = Date.now() - startTimeRef.current\n remainingRef.current = Math.max(0, remainingRef.current - elapsed)\n return\n }\n startTimeRef.current = Date.now()\n const timer = setTimeout(() => {\n sonnerToast.dismiss(selfDismissId)\n }, remainingRef.current)\n return () => clearTimeout(timer)\n }, [selfDismissId, allTerminal, paused])\n\n const accentClass = allTerminal\n ? errorCount > 0\n ? 'bg-error-7'\n : 'bg-success-7'\n : 'bg-accent-9'\n\n const timerBarType: ToastType = allTerminal\n ? errorCount > 0\n ? 'error'\n : 'success'\n : 'loading'\n\n const uploadUrgent = allTerminal && errorCount > 0\n return (\n <MotionPreference>\n <motion.div\n layout\n role={uploadUrgent ? 'alert' : 'status'}\n aria-live={uploadUrgent ? 'assertive' : 'polite'}\n aria-label=\"File uploads\"\n className={cn(\n 'group relative flex w-full overflow-hidden rounded-overlay-sm bg-surface-overlay shadow-floating',\n // Faint surface tint when the batch ended with failures.\n uploadUrgent && 'bg-error-2',\n )}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n onFocusCapture={() => setFocused(true)}\n onBlurCapture={() => setFocused(false)}\n transition={springs.smooth}\n >\n {/* Left accent bar — opt-in via showAccent */}\n {showAccent && (\n <div className={cn('w-1 shrink-0 rounded-l-overlay-sm', accentClass)} />\n )}\n\n {/* Content */}\n <div className=\"min-w-0 flex-1 p-ds-04\">\n {/* Header — icon morphs between upload ↔ result */}\n <div className=\"flex items-center gap-ds-02\">\n <AnimatePresence mode=\"wait\">\n {allTerminal ? (\n errorCount > 0 ? (\n <motion.div\n key=\"error-icon\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n >\n <Icon icon={IconAlertCircle} size=\"sm\" className=\"shrink-0 text-error-11\" />\n </motion.div>\n ) : (\n <motion.div\n key=\"success-icon\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n >\n <Icon icon={IconCheck} size=\"sm\" className=\"shrink-0 text-success-11\" />\n </motion.div>\n )\n ) : (\n <motion.div\n key=\"upload-icon\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Icon icon={IconUpload} size=\"sm\" className=\"shrink-0 text-surface-fg-muted\" />\n </motion.div>\n )}\n </AnimatePresence>\n <p className=\"text-body-md font-semibold text-surface-fg\">\n {allTerminal\n ? errorCount > 0\n ? `${completeCount} of ${files.length} uploaded`\n : `${files.length} file${files.length > 1 ? 's' : ''} uploaded`\n : `Uploading ${files.length} file${files.length > 1 ? 's' : ''}`}\n </p>\n </div>\n {!allTerminal && (\n <p className=\"mt-0.5 text-caption text-surface-fg-muted\">\n {completeCount} of {files.length} complete\n {errorCount > 0 && ` · ${errorCount} failed`}\n </p>\n )}\n\n {/* File list */}\n <div className=\"mt-ds-02 max-h-[140px] overflow-y-auto\">\n <AnimatePresence>\n {files.map((file) => (\n <UploadFileRow\n key={file.id}\n file={file}\n onRetry={onRetry}\n onRemove={onRemove}\n />\n ))}\n </AnimatePresence>\n </div>\n\n {/* Screen reader announcement */}\n <div className=\"sr-only\" aria-live=\"polite\">\n {completeCount} of {files.length} files uploaded\n {errorCount > 0 && `, ${errorCount} failed`}\n </div>\n </div>\n\n {/* Timer bar — only after all terminal */}\n {allTerminal && (\n <TimerBar\n duration={UPLOAD_COMPLETE_DELAY}\n type={timerBarType}\n paused={false}\n />\n )}\n </motion.div>\n </MotionPreference>\n )\n}\n\n/* ---------------------------------------------------------------------------\n * Toast API — wraps Sonner with our styled UI\n * ------------------------------------------------------------------------ */\n\nfunction createSonnerOptions(\n options?: ToastOptions,\n duration?: number,\n): ExternalToast {\n return {\n id: options?.id,\n duration: options?.duration ?? duration ?? DEFAULT_DURATION,\n unstyled: true,\n classNames: { toast: 'w-full' },\n }\n}\n\nfunction createTypedToast(\n type: ToastType,\n message: string | React.ReactNode,\n options?: ToastOptions,\n) {\n assertToasterMounted()\n const duration = options?.duration ?? DEFAULT_DURATION\n return sonnerToast.custom(\n () => (\n <ToastContent\n type={type}\n title={message}\n description={options?.description}\n action={options?.action}\n cancel={options?.cancel}\n duration={duration}\n showAccent={options?.showAccent}\n />\n ),\n createSonnerOptions(options, duration),\n )\n}\n\n/**\n * Toast API — imperative toast notifications with Sonner rendering engine.\n *\n * Mount `<Toaster />` once at your root layout. Then call `toast()` from anywhere.\n *\n * @example\n * toast('Something happened')\n * toast.success('Saved!')\n * toast.error('Failed to save')\n * toast.promise(saveFn(), { loading: 'Saving...', success: 'Done!', error: 'Failed' })\n * toast.undo('Deleted', { onUndo: () => restore() })\n * toast.upload({ files, onRetry, onRemove })\n */\nfunction toast(message: string | React.ReactNode, options?: ToastOptions) {\n return createTypedToast('message', message, options)\n}\n\ntoast.message = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('message', message, options)\n}\n\ntoast.success = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('success', message, options)\n}\n\ntoast.error = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('error', message, options)\n}\n\ntoast.warning = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('warning', message, options)\n}\n\ntoast.info = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('info', message, options)\n}\n\ntoast.loading = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('loading', message, {\n ...options,\n duration: options?.duration ?? Infinity,\n })\n}\n\ntoast.promise = <T,>(\n promise: Promise<T>,\n options: {\n loading: string | React.ReactNode\n success: string | React.ReactNode | ((data: T) => string | React.ReactNode)\n error:\n | string\n | React.ReactNode\n | ((error: unknown) => string | React.ReactNode)\n },\n) => {\n const id = sonnerToast.custom(\n () => (\n <ToastContent type=\"loading\" title={options.loading} showTimerBar={false} />\n ),\n { duration: Infinity, unstyled: true, classNames: { toast: 'w-full' } },\n )\n\n const replaceWith = (type: ToastType, message: React.ReactNode) => {\n // Sonner doesn't reset its auto-dismiss timer when updating a custom toast\n // in-place from duration: Infinity, so ToastContent manages its own\n // hover-aware dismiss timer via selfDismissId.\n sonnerToast.custom(\n () => (\n <ToastContent\n type={type}\n title={message}\n duration={DEFAULT_DURATION}\n selfDismissId={id}\n />\n ),\n { id, duration: Infinity, unstyled: true, classNames: { toast: 'w-full' } },\n )\n }\n\n promise\n .then((data) => {\n const message =\n typeof options.success === 'function'\n ? options.success(data)\n : options.success\n replaceWith('success', message)\n })\n .catch((error) => {\n const message =\n typeof options.error === 'function'\n ? options.error(error)\n : options.error\n replaceWith('error', message)\n })\n\n return id\n}\n\ntoast.undo = (message: string | React.ReactNode, options: ToastUndoOptions) => {\n const duration = options.duration ?? UNDO_DURATION\n return sonnerToast.custom(\n () => (\n <ToastContent\n type=\"message\"\n title={message}\n description={options.description}\n action={{ label: 'Undo', onClick: options.onUndo }}\n duration={duration}\n />\n ),\n { duration, unstyled: true, classNames: { toast: 'w-full' } },\n )\n}\n\ntoast.upload = (options: ToastUploadOptions) => {\n // Pre-determine the id so we can pass it to UploadToastContent for self-dismiss.\n const toastId = options.id ?? String(Date.now())\n\n sonnerToast.custom(\n () => (\n <UploadToastContent\n files={options.files}\n onRetry={options.onRetry}\n onRemove={options.onRemove}\n selfDismissId={toastId}\n showAccent={options.showAccent}\n />\n ),\n {\n id: toastId,\n duration: Infinity,\n unstyled: true,\n classNames: { toast: 'w-full' },\n },\n )\n\n return toastId\n}\n\ntoast.custom = (\n render: (id: string | number) => React.ReactElement,\n options?: ExternalToast,\n) => {\n return sonnerToast.custom(render, {\n ...options,\n unstyled: true,\n classNames: { toast: 'w-full', ...(options?.classNames as Record<string, string>) },\n })\n}\n\ntoast.dismiss = (id?: string | number) => {\n if (id !== undefined) {\n sonnerToast.dismiss(id)\n } else {\n sonnerToast.dismiss()\n }\n}\n\n/* ---------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------ */\n\nexport type ToastProps = ToastOptions\n\nexport {\n formatFileSize,\n TimerBar,\n toast,\n ToastContent,\n UploadFileRow,\n UploadToastContent,\n}\nexport type {\n ToastActionOptions,\n ToastOptions,\n ToastType,\n ToastUndoOptions,\n ToastUploadOptions,\n UploadFile,\n} from './toast-types'\n"],"mappings":";;;;;;;;;;;;;;AAuCA,IAAM,IAAmB,KACnB,IAAgB,KAChB,IAAwB,KAMxB,IAQF;CACF,SAAS;EACP,aAAa;EACb,WAAW;EACX,MAAM;EACN,eAAe;CACjB;CACA,SAAS;EACP,aAAa;EACb,WAAW;EACX,MAAM;EACN,eAAe;CACjB;CACA,OAAO;EACL,aAAa;EACb,WAAW;EACX,MAAM;EACN,eAAe;CACjB;CACA,SAAS;EACP,aAAa;EACb,WAAW;EACX,MAAM;EACN,eAAe;CACjB;CACA,MAAM;EACJ,aAAa;EACb,WAAW;EACX,MAAM;EACN,eAAe;CACjB;CACA,SAAS;EACP,aAAa;EACb,WAAW;EACX,MAAM;EACN,eAAe;CACjB;AACF;AAMA,SAAS,EAAS,EAChB,aACA,SACA,aAKC;CACD,IAAM,IAAS,EAAkB;CACjC,OACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GACE,WAAW,EACT,wCACA,EAAO,eACP,+BACF;GACA,OAAO;IACL,mBAAmB,GAAG,EAAS;IAC/B,oBAAoB,IAAS,WAAW;GAC1C;EACD,CAAA;CACE,CAAA;AAET;AAMA,SAAS,EAAa,EACpB,SACA,UACA,gBACA,WACA,WACA,cAAW,GACX,kBAAe,IACf,gBAAa,IACb,oBAaC;CACD,IAAM,CAAC,GAAS,KAAc,EAAM,SAAS,EAAK,GAC5C,CAAC,GAAS,KAAc,EAAM,SAAS,EAAK,GAC5C,IAAS,KAAW,GACpB,IAAS,EAAkB,IAC3B,IAAa,EAAO,MAGpB,IAAe,EAAM,OAAO,CAAQ,GACpC,IAAe,EAAM,OAAO,CAAC;CAEnC,EAAM,gBAAgB;EACpB,IAAI,KAAiB,QAAQ,MAAa,UAAU;EACpD,IAAI,GAAQ;GAEV,IAAM,IAAU,KAAK,IAAI,IAAI,EAAa;GAC1C,EAAa,UAAU,KAAK,IAAI,GAAG,EAAa,UAAU,CAAO;GACjE;EACF;EAEA,EAAa,UAAU,KAAK,IAAI;EAChC,IAAM,IAAQ,iBAAiB;GAC7B,EAAY,QAAQ,CAAa;EACnC,GAAG,EAAa,OAAO;EACvB,aAAa,aAAa,CAAK;CACjC,GAAG;EAAC;EAAe;EAAU;CAAM,CAAC;CAIpC,IAAM,IAAW,MAAS;CAC1B,OACE,kBAAC,GAAD,EAAA,UACE,kBAAC,EAAO,KAAR;EACE,QAAO;EACP,MAAM,IAAW,UAAU;EAC3B,aAAW,IAAW,cAAc;EACpC,eAAY;EACZ,WAAW,EACT,oGAEA,MAAS,WAAW,YACtB;EACA,oBAAoB,EAAW,EAAI;EACnC,oBAAoB,EAAW,EAAK;EACpC,sBAAsB,EAAW,EAAI;EACrC,qBAAqB,EAAW,EAAK;EACrC,YAAY,EAAQ;YAdtB;GAiBG,KAAc,EAAO,eACpB,kBAAC,OAAD,EACE,WAAW,EAAG,qCAAqC,EAAO,WAAW,EACtE,CAAA;GAIH,kBAAC,OAAD;IAAK,WAAU;cAAf,CAEE,kBAAC,GAAD;KAAiB,MAAK;eACnB,MAAS,YACR,kBAAC,EAAO,KAAR;MAEE,SAAS;OAAE,SAAS;OAAG,OAAO;MAAI;MAClC,SAAS;OAAE,SAAS;OAAG,OAAO;MAAE;MAChC,MAAM;OAAE,SAAS;OAAG,OAAO;MAAI;MAC/B,YAAY,EAAO;MACnB,WAAU;gBAEV,kBAAC,GAAD;OAAS,MAAK;OAAK,WAAU;MAAW,CAAA;KAC9B,GARN,SAQM,IACV,IACF,kBAAC,EAAO,KAAR;MAEE,SAAS;OAAE,SAAS;OAAG,OAAO;MAAI;MAClC,SAAS;OAAE,SAAS;OAAG,OAAO;MAAE;MAChC,MAAM;OAAE,SAAS;OAAG,OAAO;MAAI;MAC/B,YAAY,EAAQ;MACpB,WAAU;gBAEV,kBAAC,GAAD;OAAM,MAAM;OAAY,MAAK;OAAK,WAAW,EAAO;MAAY,CAAA;KACtD,GARL,CAQK,IACV;IACW,CAAA,GAGjB,kBAAC,OAAD;KAAK,WAAU;eAAf;MACG,KACC,kBAAC,KAAD;OAAG,WAAU;iBACV;MACA,CAAA;MAEJ,KACC,kBAAC,KAAD;OACE,WAAW,EACT,sCACA,KAAS,QACX;iBAEC;MACA,CAAA;OAIH,KAAU,MACV,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACG,KACC,kBAAC,UAAD;QACE,MAAK;QACL,SAAS,EAAO;QAChB,WAAU;kBAET,EAAO;OACF,CAAA,GAET,KACC,kBAAC,UAAD;QACE,MAAK;QACL,SAAS,EAAO;QAChB,WAAU;kBAET,EAAO;OACF,CAAA,CAEP;;KAEJ;MACF;;GAGJ,KAAgB,MAAS,aACxB,kBAAC,GAAD;IAAoB;IAAgB;IAAc;GAAS,CAAA;EAEnD;IACI,CAAA;AAEtB;AAMA,IAAM,oBAAmB,IAAI,IAAI;CAC/B;CAAO;CAAQ;CAAO;CAAO;CAAQ;CAAO;CAAO;CAAO;AAC5D,CAAC;AAED,SAAS,EAAY,GAA2B;CAC9C,IAAI,EAAK,YAAY,OAAO;CAC5B,IAAM,IAAM,EAAK,KAAK,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,YAAY,KAAK;CACzD,OAAO,EAAiB,IAAI,CAAG;AACjC;AAEA,SAAS,EACP,GACgC;CAChC,QAAQ,GAAR;EACE,KAAK,YACH,OAAO;EACT,KAAK,SACH,OAAO;EACT,SACE,OAAO;CACX;AACF;AAEA,SAAS,EAAiB,GAAsC;CAC9D,QAAQ,EAAK,QAAb;EACE,KAAK,WACH,OAAO;EACT,KAAK,aACH,OAAO,EAAK,YAAY;EAC1B,KAAK,cACH;EACF,KAAK,YACH,OAAO;EACT,KAAK,SACH,OAAO,EAAK,YAAY;CAC5B;AACF;AAEA,SAAS,EAAe,GAAuB;CAM7C,OALI,CAAC,KAAS,IAAQ,KAAK,CAAC,OAAO,SAAS,CAAK,IAAU,QACvD,IAAQ,OAAa,GAAG,EAAM,MAC9B,IAAQ,OAAO,OAAa,IAAI,IAAQ,KAAA,CAAM,QAAQ,CAAC,EAAE,OACzD,IAAQ,OAAO,OAAO,OACjB,IAAI,KAAS,OAAO,MAAA,CAAO,QAAQ,CAAC,EAAE,OACxC,IAAI,KAAS,OAAO,OAAO,MAAA,CAAO,QAAQ,CAAC,EAAE;AACtD;AAEA,SAAS,EAAc,EACrB,SACA,YACA,eAKC;CACD,IAAM,IAAa,EAAK,WAAW,cAAc,EAAK,WAAW,SAC3D,IACJ,EAAK,WAAW,eAAe,EAAK,WAAW;CAEjD,OACE,kBAAC,GAAD,EAAA,UACE,kBAAC,EAAO,KAAR;EACE,QAAA;EACA,WAAU;EACV,SAAS;EACT,SAAS;GAAE,SAAS;GAAG,GAAG;EAAE;EAC5B,MAAM;GAAE,SAAS;GAAG,GAAG;GAAK,QAAQ;EAAE;EACtC,YAAY,EAAQ;YANtB;GASE,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KAAiB,MAAK;eACnB,EAAK,WAAW,aACf,kBAAC,EAAO,KAAR;MAEE,SAAS;OAAE,SAAS;OAAG,OAAO;MAAI;MAClC,SAAS;OAAE,SAAS;OAAG,OAAO;MAAE;MAChC,MAAM;OAAE,SAAS;OAAG,OAAO;MAAI;MAC/B,YAAY,EAAQ;gBAEpB,kBAAC,GAAD;OAAM,MAAM;OAAW,MAAK;OAAK,WAAU;MAAmB,CAAA;KACpD,GAPN,UAOM,IACV,EAAK,WAAW,UAClB,kBAAC,EAAO,KAAR;MAEE,SAAS;OAAE,SAAS;OAAG,OAAO;MAAI;MAClC,SAAS;OAAE,SAAS;OAAG,OAAO;MAAE;MAChC,MAAM;OAAE,SAAS;OAAG,OAAO;MAAI;MAC/B,YAAY,EAAO;gBAEnB,kBAAC,GAAD;OAAM,MAAM;OAAiB,MAAK;OAAK,WAAU;MAAiB,CAAA;KACxD,GAPN,OAOM,IACV,EAAK,WAAW,eAClB,kBAAC,EAAO,KAAR;MAEE,SAAS,EAAE,SAAS,EAAE;MACtB,SAAS,EAAE,SAAS,EAAE;MACtB,MAAM,EAAE,SAAS,EAAE;MACnB,YAAY,EAAO;gBAEnB,kBAAC,GAAD;OAAS,MAAK;OAAK,WAAU;MAAe,CAAA;KAClC,GAPN,YAOM,IACV,EAAY,CAAI,IAClB,kBAAC,EAAO,KAAR;MAEE,SAAS,EAAE,SAAS,EAAE;MACtB,SAAS,EAAE,SAAS,EAAE;MACtB,MAAM,EAAE,SAAS,EAAE;MACnB,YAAY,EAAO;gBAEnB,kBAAC,GAAD;OAAM,MAAM;OAAW,MAAK;OAAK,WAAU;MAAyB,CAAA;KAC1D,GAPN,OAOM,IAEZ,kBAAC,EAAO,KAAR;MAEE,SAAS,EAAE,SAAS,EAAE;MACtB,SAAS,EAAE,SAAS,EAAE;MACtB,MAAM,EAAE,SAAS,EAAE;MACnB,YAAY,EAAO;gBAEnB,kBAAC,GAAD;OAAM,MAAM;OAAU,MAAK;OAAK,WAAU;MAAyB,CAAA;KACzD,GAPN,MAOM;IAEC,CAAA;GACd,CAAA;GAGL,kBAAC,QAAD;IAAM,WAAU;cACb,EAAK;GACF,CAAA;GAGL,IACC,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KACE,MAAK;KACL,OAAO,EAAiB,EAAK,MAAM;KACnC,OAAO,EAAiB,CAAI;KAC5B,WAAU;IACX,CAAA,GACA,EAAK,aAAa,KAAA,KACjB,kBAAC,QAAD;KAAM,WAAU;eAAhB,CACG,EAAK,UAAS,GACX;MAEL;QACH,EAAK,WAAW,aAClB,kBAAC,QAAD;IAAM,WAAU;cAA+B;GAAU,CAAA,IACvD,EAAK,WAAW,UAClB,kBAAC,QAAD;IAAM,WAAU;cACb,EAAK,SAAS;GACX,CAAA,IAEN,kBAAC,QAAD;IAAM,WAAU;cACb,EAAe,EAAK,IAAI;GACrB,CAAA;GAIP,EAAK,WAAW,WAAW,KAC1B,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAQ,EAAK,EAAE;IAC9B,WAAU;IACV,cAAY,SAAS,EAAK;IAC1B,OAAM;cAEN,kBAAC,GAAD,EAAa,WAAU,cAAe,CAAA;GAChC,CAAA;GAET,CAAC,KAAc,KACd,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAS,EAAK,EAAE;IAC/B,WAAU;IACV,cAAY,UAAU,EAAK;IAC3B,OAAM;cAEN,kBAAC,GAAD,EAAO,WAAU,cAAe,CAAA;GAC1B,CAAA;EAEA;IACI,CAAA;AAEtB;AAEA,SAAS,EAAmB,EAC1B,UACA,YACA,aACA,kBACA,gBAAa,MAQZ;CACD,IAAM,CAAC,GAAS,KAAc,EAAM,SAAS,EAAK,GAC5C,CAAC,GAAS,KAAc,EAAM,SAAS,EAAK,GAC5C,IAAS,KAAW,GACpB,IAAgB,EAAM,QAAQ,MAAM,EAAE,WAAW,UAAU,CAAC,CAAC,QAC7D,IAAa,EAAM,QAAQ,MAAM,EAAE,WAAW,OAAO,CAAC,CAAC,QACvD,IAAc,EAAM,OACvB,MAAM,EAAE,WAAW,cAAc,EAAE,WAAW,OACjD,GAGM,IAAe,EAAM,OAAO,CAAqB,GACjD,IAAe,EAAM,OAAO,CAAC;CAEnC,EAAM,gBAAgB;EACpB,IAAI,KAAiB,QAAQ,CAAC,GAAa;EAC3C,IAAI,GAAQ;GACV,IAAM,IAAU,KAAK,IAAI,IAAI,EAAa;GAC1C,EAAa,UAAU,KAAK,IAAI,GAAG,EAAa,UAAU,CAAO;GACjE;EACF;EACA,EAAa,UAAU,KAAK,IAAI;EAChC,IAAM,IAAQ,iBAAiB;GAC7B,EAAY,QAAQ,CAAa;EACnC,GAAG,EAAa,OAAO;EACvB,aAAa,aAAa,CAAK;CACjC,GAAG;EAAC;EAAe;EAAa;CAAM,CAAC;CAEvC,IAAM,IAAc,IAChB,IAAa,IACX,eACA,iBACF,eAEE,IAA0B,IAC5B,IAAa,IACX,UACA,YACF,WAEE,IAAe,KAAe,IAAa;CACjD,OACE,kBAAC,GAAD,EAAA,UACE,kBAAC,EAAO,KAAR;EACE,QAAA;EACA,MAAM,IAAe,UAAU;EAC/B,aAAW,IAAe,cAAc;EACxC,cAAW;EACX,WAAW,EACT,oGAEA,KAAgB,YAClB;EACA,oBAAoB,EAAW,EAAI;EACnC,oBAAoB,EAAW,EAAK;EACpC,sBAAsB,EAAW,EAAI;EACrC,qBAAqB,EAAW,EAAK;EACrC,YAAY,EAAQ;YAdtB;GAiBG,KACC,kBAAC,OAAD,EAAK,WAAW,EAAG,qCAAqC,CAAW,EAAI,CAAA;GAIzE,kBAAC,OAAD;IAAK,WAAU;cAAf;KAEE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OAAiB,MAAK;iBACnB,IACC,IAAa,IACX,kBAAC,EAAO,KAAR;QAEE,SAAS;SAAE,SAAS;SAAG,OAAO;QAAI;QAClC,SAAS;SAAE,SAAS;SAAG,OAAO;QAAE;QAChC,MAAM;SAAE,SAAS;SAAG,OAAO;QAAI;QAC/B,YAAY,EAAQ;kBAEpB,kBAAC,GAAD;SAAM,MAAM;SAAiB,MAAK;SAAK,WAAU;QAA0B,CAAA;OACjE,GAPN,YAOM,IAEZ,kBAAC,EAAO,KAAR;QAEE,SAAS;SAAE,SAAS;SAAG,OAAO;QAAI;QAClC,SAAS;SAAE,SAAS;SAAG,OAAO;QAAE;QAChC,MAAM;SAAE,SAAS;SAAG,OAAO;QAAI;QAC/B,YAAY,EAAQ;kBAEpB,kBAAC,GAAD;SAAM,MAAM;SAAW,MAAK;SAAK,WAAU;QAA4B,CAAA;OAC7D,GAPN,cAOM,IAGd,kBAAC,EAAO,KAAR;QAEE,SAAS,EAAE,SAAS,EAAE;QACtB,SAAS,EAAE,SAAS,EAAE;QACtB,MAAM,EAAE,SAAS,EAAE;QACnB,YAAY,EAAO;kBAEnB,kBAAC,GAAD;SAAM,MAAM;SAAY,MAAK;SAAK,WAAU;QAAkC,CAAA;OACpE,GAPN,aAOM;MAEC,CAAA,GACjB,kBAAC,KAAD;OAAG,WAAU;iBACV,IACG,IAAa,IACX,GAAG,EAAc,MAAM,EAAM,OAAO,aACpC,GAAG,EAAM,OAAO,OAAO,EAAM,SAAS,IAAI,MAAM,GAAG,aACrD,aAAa,EAAM,OAAO,OAAO,EAAM,SAAS,IAAI,MAAM;MAC7D,CAAA,CACA;;KACJ,CAAC,KACA,kBAAC,KAAD;MAAG,WAAU;gBAAb;OACG;OAAc;OAAK,EAAM;OAAO;OAChC,IAAa,KAAK,MAAM,EAAW;MACnC;;KAIL,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD,EAAA,UACG,EAAM,KAAK,MACV,kBAAC,GAAD;OAEQ;OACG;OACC;MACX,GAJM,EAAK,EAIX,CACF,EACc,CAAA;KACd,CAAA;KAGL,kBAAC,OAAD;MAAK,WAAU;MAAU,aAAU;gBAAnC;OACG;OAAc;OAAK,EAAM;OAAO;OAChC,IAAa,KAAK,KAAK,EAAW;MAChC;;IACF;;GAGJ,KACC,kBAAC,GAAD;IACE,UAAU;IACV,MAAM;IACN,QAAQ;GACT,CAAA;EAEO;IACI,CAAA;AAEtB;AAMA,SAAS,EACP,GACA,GACe;CACf,OAAO;EACL,IAAI,GAAS;EACb,UAAU,GAAS,YAAY,KAAY;EAC3C,UAAU;EACV,YAAY,EAAE,OAAO,SAAS;CAChC;AACF;AAEA,SAAS,EACP,GACA,GACA,GACA;CACA,EAAqB;CACrB,IAAM,IAAW,GAAS,YAAY;CACtC,OAAO,EAAY,aAEf,kBAAC,GAAD;EACQ;EACN,OAAO;EACP,aAAa,GAAS;EACtB,QAAQ,GAAS;EACjB,QAAQ,GAAS;EACP;EACV,YAAY,GAAS;CACtB,CAAA,GAEH,EAAoB,GAAS,CAAQ,CACvC;AACF;AAeA,SAAS,EAAM,GAAmC,GAAwB;CACxE,OAAO,EAAiB,WAAW,GAAS,CAAO;AACrD;AAEA,EAAM,WACJ,GACA,MAEO,EAAiB,WAAW,GAAS,CAAO,GAGrD,EAAM,WACJ,GACA,MAEO,EAAiB,WAAW,GAAS,CAAO,GAGrD,EAAM,SACJ,GACA,MAEO,EAAiB,SAAS,GAAS,CAAO,GAGnD,EAAM,WACJ,GACA,MAEO,EAAiB,WAAW,GAAS,CAAO,GAGrD,EAAM,QACJ,GACA,MAEO,EAAiB,QAAQ,GAAS,CAAO,GAGlD,EAAM,WACJ,GACA,MAEO,EAAiB,WAAW,GAAS;CAC1C,GAAG;CACH,UAAU,GAAS,YAAY;AACjC,CAAC,GAGH,EAAM,WACJ,GACA,MAQG;CACH,IAAM,IAAK,EAAY,aAEnB,kBAAC,GAAD;EAAc,MAAK;EAAU,OAAO,EAAQ;EAAS,cAAc;CAAQ,CAAA,GAE7E;EAAE,UAAU;EAAU,UAAU;EAAM,YAAY,EAAE,OAAO,SAAS;CAAE,CACxE,GAEM,KAAe,GAAiB,MAA6B;EAIjE,EAAY,aAER,kBAAC,GAAD;GACQ;GACN,OAAO;GACP,UAAU;GACV,eAAe;EAChB,CAAA,GAEH;GAAE;GAAI,UAAU;GAAU,UAAU;GAAM,YAAY,EAAE,OAAO,SAAS;EAAE,CAC5E;CACF;CAkBA,OAhBA,EACG,MAAM,MAAS;EACd,IAAM,IACJ,OAAO,EAAQ,WAAY,aACvB,EAAQ,QAAQ,CAAI,IACpB,EAAQ;EACd,EAAY,WAAW,CAAO;CAChC,CAAC,CAAC,CACD,OAAO,MAAU;EAChB,IAAM,IACJ,OAAO,EAAQ,SAAU,aACrB,EAAQ,MAAM,CAAK,IACnB,EAAQ;EACd,EAAY,SAAS,CAAO;CAC9B,CAAC,GAEI;AACT,GAEA,EAAM,QAAQ,GAAmC,MAA8B;CAC7E,IAAM,IAAW,EAAQ,YAAY;CACrC,OAAO,EAAY,aAEf,kBAAC,GAAD;EACE,MAAK;EACL,OAAO;EACP,aAAa,EAAQ;EACrB,QAAQ;GAAE,OAAO;GAAQ,SAAS,EAAQ;EAAO;EACvC;CACX,CAAA,GAEH;EAAE;EAAU,UAAU;EAAM,YAAY,EAAE,OAAO,SAAS;CAAE,CAC9D;AACF,GAEA,EAAM,UAAU,MAAgC;CAE9C,IAAM,IAAU,EAAQ,MAAM,OAAO,KAAK,IAAI,CAAC;CAoB/C,OAlBA,EAAY,aAER,kBAAC,GAAD;EACE,OAAO,EAAQ;EACf,SAAS,EAAQ;EACjB,UAAU,EAAQ;EAClB,eAAe;EACf,YAAY,EAAQ;CACrB,CAAA,GAEH;EACE,IAAI;EACJ,UAAU;EACV,UAAU;EACV,YAAY,EAAE,OAAO,SAAS;CAChC,CACF,GAEO;AACT,GAEA,EAAM,UACJ,GACA,MAEO,EAAY,OAAO,GAAQ;CAChC,GAAG;CACH,UAAU;CACV,YAAY;EAAE,OAAO;EAAU,GAAI,GAAS;CAAsC;AACpF,CAAC,GAGH,EAAM,WAAW,MAAyB;CACxC,AAAI,MAAO,KAAA,IAGT,EAAY,QAAQ,IAFpB,EAAY,QAAQ,CAAE;AAI1B"}
|
|
1
|
+
{"version":3,"file":"toast.js","names":[],"sources":["../../src/ui/toast.tsx"],"sourcesContent":["'use client'\n\nimport {\n IconAlertCircle,\n IconAlertTriangle,\n IconCheck,\n IconCircleCheck,\n IconCircleX,\n IconFile,\n IconInfoCircle,\n IconPhoto,\n IconRefresh,\n IconUpload,\n IconX,\n} from '@tabler/icons-react'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\nimport { type ExternalToast,toast as sonnerToast } from 'sonner'\n\nimport { MotionPreference } from '../motion/motion-preference'\nimport { Icon } from './icon'\nimport { springs, tweens } from './lib/motion'\nimport { cn } from './lib/utils'\nimport { Progress } from './progress'\nimport { Spinner } from './spinner'\nimport { assertToasterMounted } from './toast-registry'\nimport type {\n ToastActionOptions,\n ToastOptions,\n ToastType,\n ToastUndoOptions,\n ToastUploadOptions,\n UploadFile,\n} from './toast-types'\n\n/* ---------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------ */\n\nconst DEFAULT_DURATION = 5000\nconst UNDO_DURATION = 8000\nconst UPLOAD_COMPLETE_DELAY = 3000\n\n/* ---------------------------------------------------------------------------\n * Accent bar & icon config per toast type\n * ------------------------------------------------------------------------ */\n\nconst TOAST_TYPE_CONFIG: Record<\n ToastType,\n {\n accentClass: string\n iconClass: string\n icon: React.ForwardRefExoticComponent<any> | null\n timerBarClass: string\n }\n> = {\n message: {\n accentClass: '',\n iconClass: '',\n icon: null,\n timerBarClass: 'bg-surface-border',\n },\n success: {\n accentClass: 'bg-success-9',\n iconClass: 'text-success-11',\n icon: IconCircleCheck,\n timerBarClass: 'bg-success-9',\n },\n error: {\n accentClass: 'bg-error-9',\n iconClass: 'text-error-11',\n icon: IconCircleX,\n timerBarClass: 'bg-error-9',\n },\n warning: {\n accentClass: 'bg-warning-9',\n iconClass: 'text-warning-11',\n icon: IconAlertTriangle,\n timerBarClass: 'bg-warning-9',\n },\n info: {\n accentClass: 'bg-info-9',\n iconClass: 'text-info-11',\n icon: IconInfoCircle,\n timerBarClass: 'bg-info-9',\n },\n loading: {\n accentClass: 'bg-accent-9',\n iconClass: 'text-accent-11',\n icon: null,\n timerBarClass: 'bg-accent-9',\n },\n}\n\n/* ---------------------------------------------------------------------------\n * Timer bar — shows auto-dismiss countdown\n * ------------------------------------------------------------------------ */\n\nfunction TimerBar({\n duration,\n type,\n paused,\n}: {\n duration: number\n type: ToastType\n paused: boolean\n}) {\n const config = TOAST_TYPE_CONFIG[type]\n return (\n /* The countdown now runs in a visible groove rather than over bare surface,\n so the bar reads as \"time remaining\" instead of a stray rule (design\n 2026-08-24). `surface-panel-hover` is the DS's designated track fill —\n a container value here would be a no-op in light, where overlay, panel\n and base are all the same white. */\n <div className=\"absolute bottom-0 left-0 right-0 h-ds-01 overflow-hidden bg-surface-panel-hover\">\n <div\n className={cn(\n 'h-full w-full origin-left opacity-30',\n config.timerBarClass,\n 'motion-safe:animate-timer-bar',\n )}\n style={{\n animationDuration: `${duration}ms`,\n animationPlayState: paused ? 'paused' : 'running',\n }}\n />\n </div>\n )\n}\n\n/* ---------------------------------------------------------------------------\n * Base toast UI — used by all typed toasts\n * ------------------------------------------------------------------------ */\n\nfunction ToastContent({\n type,\n title,\n description,\n action,\n cancel,\n duration = DEFAULT_DURATION,\n showTimerBar = true,\n showAccent = false,\n selfDismissId,\n}: {\n type: ToastType\n title?: React.ReactNode\n description?: React.ReactNode\n action?: ToastActionOptions\n cancel?: ToastActionOptions\n duration?: number\n showTimerBar?: boolean\n /** Render the colored left accent bar. Off by default (status = icon + timer bar). */\n showAccent?: boolean\n /** When set, this component manages its own dismiss timer (hover-aware). */\n selfDismissId?: string | number\n}) {\n const [hovered, setHovered] = React.useState(false)\n const [focused, setFocused] = React.useState(false)\n const paused = hovered || focused\n const config = TOAST_TYPE_CONFIG[type]\n const StatusIcon = config.icon\n\n // Self-managed dismiss timer that pauses on hover or keyboard focus\n const remainingRef = React.useRef(duration)\n const startTimeRef = React.useRef(0)\n\n React.useEffect(() => {\n if (selfDismissId == null || duration === Infinity) return\n if (paused) {\n // Pause: save remaining time\n const elapsed = Date.now() - startTimeRef.current\n remainingRef.current = Math.max(0, remainingRef.current - elapsed)\n return\n }\n // Start/resume timer\n startTimeRef.current = Date.now()\n const timer = setTimeout(() => {\n sonnerToast.dismiss(selfDismissId)\n }, remainingRef.current)\n return () => clearTimeout(timer)\n }, [selfDismissId, duration, paused])\n\n // Error toasts announce assertively so screen readers interrupt;\n // warnings stay polite but atomically re-read the message.\n const isUrgent = type === 'error'\n return (\n <MotionPreference>\n <motion.div\n layout=\"position\"\n role={isUrgent ? 'alert' : 'status'}\n aria-live={isUrgent ? 'assertive' : 'polite'}\n aria-atomic=\"true\"\n className={cn(\n 'group relative flex w-full overflow-hidden rounded-overlay-sm bg-surface-overlay shadow-floating',\n // Faint surface tint on error only — the one you must not miss.\n type === 'error' && 'bg-error-2',\n )}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n onFocusCapture={() => setFocused(true)}\n onBlurCapture={() => setFocused(false)}\n transition={springs.smooth}\n >\n {/* Left accent bar — opt-in via showAccent (status is carried by icon + timer bar) */}\n {showAccent && config.accentClass && (\n <div\n className={cn('w-1 shrink-0 rounded-l-overlay-sm', config.accentClass)}\n />\n )}\n\n {/* Content */}\n <div className=\"flex min-w-0 flex-1 items-start gap-ds-03 p-ds-04\">\n {/* Status icon — animates between spinner ↔ typed icon */}\n <AnimatePresence mode=\"wait\">\n {type === 'loading' ? (\n <motion.div\n key=\"loading\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={tweens.fade}\n className=\"mt-0.5 shrink-0\"\n >\n <Spinner size=\"sm\" className=\"h-4 w-4\" />\n </motion.div>\n ) : StatusIcon ? (\n <motion.div\n key={type}\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n className=\"mt-0.5 shrink-0\"\n >\n <Icon icon={StatusIcon} size=\"sm\" className={config.iconClass} />\n </motion.div>\n ) : null}\n </AnimatePresence>\n\n {/* Text */}\n <div className=\"min-w-0 flex-1\">\n {title && (\n <p className=\"text-body-md font-semibold text-surface-fg\">\n {title}\n </p>\n )}\n {description && (\n <p\n className={cn(\n 'text-body-sm text-surface-fg-muted',\n title && 'mt-0.5',\n )}\n >\n {description}\n </p>\n )}\n\n {/* Action / cancel buttons */}\n {(action || cancel) && (\n <div className=\"mt-ds-03 flex items-center gap-ds-03\">\n {action && (\n <button\n type=\"button\"\n onClick={action.onClick}\n className=\"text-body-sm font-medium text-accent-11 underline-offset-2 hover:underline hover:bg-surface-panel-hover rounded-control-inner px-ds-02 py-ds-01 transition-[color,background-color] duration-fast-01 ease-productive-standard focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:rounded-control-inner\"\n >\n {action.label}\n </button>\n )}\n {cancel && (\n <button\n type=\"button\"\n onClick={cancel.onClick}\n className=\"text-body-sm text-surface-fg-muted hover:text-surface-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:rounded-control-inner\"\n >\n {cancel.label}\n </button>\n )}\n </div>\n )}\n </div>\n </div>\n\n {/* Timer bar */}\n {showTimerBar && type !== 'loading' && (\n <TimerBar duration={duration} type={type} paused={paused} />\n )}\n </motion.div>\n </MotionPreference>\n )\n}\n\n/* ---------------------------------------------------------------------------\n * Upload toast UI\n * ------------------------------------------------------------------------ */\n\nconst IMAGE_EXTENSIONS = new Set([\n 'jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp', 'ico', 'avif',\n])\n\nfunction isImageFile(file: UploadFile): boolean {\n if (file.previewUrl) return true\n const ext = file.name.split('.').pop()?.toLowerCase() ?? ''\n return IMAGE_EXTENSIONS.has(ext)\n}\n\nfunction getProgressColor(\n status: UploadFile['status'],\n): 'accent' | 'success' | 'error' {\n switch (status) {\n case 'complete':\n return 'success'\n case 'error':\n return 'error'\n default:\n return 'accent'\n }\n}\n\nfunction getProgressValue(file: UploadFile): number | undefined {\n switch (file.status) {\n case 'pending':\n return 0\n case 'uploading':\n return file.progress ?? 0\n case 'processing':\n return undefined\n case 'complete':\n return 100\n case 'error':\n return file.progress ?? 0\n }\n}\n\nfunction formatFileSize(bytes: number): string {\n if (!bytes || bytes < 0 || !Number.isFinite(bytes)) return '0 B'\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n if (bytes < 1024 * 1024 * 1024)\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`\n}\n\nfunction UploadFileRow({\n file,\n onRetry,\n onRemove,\n}: {\n file: UploadFile\n onRetry?: (fileId: string) => void\n onRemove?: (fileId: string) => void\n}) {\n const isTerminal = file.status === 'complete' || file.status === 'error'\n const isUploading =\n file.status === 'uploading' || file.status === 'processing'\n\n return (\n <MotionPreference>\n <motion.div\n layout\n className=\"flex items-center gap-ds-02 py-1\"\n initial={false}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, x: -20, height: 0 }}\n transition={springs.snappy}\n >\n {/* File icon — animates between states */}\n <div className=\"flex h-5 w-5 shrink-0 items-center justify-center\">\n <AnimatePresence mode=\"wait\">\n {file.status === 'complete' ? (\n <motion.div\n key=\"complete\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n >\n <Icon icon={IconCheck} size=\"xs\" className=\"text-success-11\" />\n </motion.div>\n ) : file.status === 'error' ? (\n <motion.div\n key=\"error\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={tweens.fade}\n >\n <Icon icon={IconAlertCircle} size=\"xs\" className=\"text-error-11\" />\n </motion.div>\n ) : file.status === 'processing' ? (\n <motion.div\n key=\"processing\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Spinner size=\"sm\" className=\"h-3.5 w-3.5\" />\n </motion.div>\n ) : isImageFile(file) ? (\n <motion.div\n key=\"image\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Icon icon={IconPhoto} size=\"xs\" className=\"text-surface-fg-muted\" />\n </motion.div>\n ) : (\n <motion.div\n key=\"file\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Icon icon={IconFile} size=\"xs\" className=\"text-surface-fg-muted\" />\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n\n {/* Filename */}\n <span className=\"min-w-0 flex-1 truncate text-body-xs text-surface-fg\">\n {file.name}\n </span>\n\n {/* Progress bar or status */}\n {isUploading ? (\n <div className=\"flex w-16 items-center gap-1\">\n <Progress\n size=\"sm\"\n color={getProgressColor(file.status)}\n value={getProgressValue(file)}\n className=\"flex-1\"\n />\n {file.progress !== undefined && (\n <span className=\"shrink-0 text-caption tabular-nums text-surface-fg-muted\">\n {file.progress}%\n </span>\n )}\n </div>\n ) : file.status === 'complete' ? (\n <span className=\"text-body-xs text-success-11\">Done</span>\n ) : file.status === 'error' ? (\n <span className=\"max-w-[60px] truncate text-body-xs text-error-11\">\n {file.error || 'Failed'}\n </span>\n ) : (\n <span className=\"text-caption text-surface-fg-muted\">\n {formatFileSize(file.size)}\n </span>\n )}\n\n {/* Actions */}\n {file.status === 'error' && onRetry && (\n <button\n type=\"button\"\n onClick={() => onRetry(file.id)}\n className=\"touch-target flex min-h-6 min-w-6 items-center justify-center rounded-control-inner text-surface-fg-muted hover:text-surface-fg\"\n aria-label={`Retry ${file.name}`}\n title=\"Retry\"\n >\n <IconRefresh className=\"h-3.5 w-3.5\" />\n </button>\n )}\n {!isTerminal && onRemove && (\n <button\n type=\"button\"\n onClick={() => onRemove(file.id)}\n className=\"touch-target flex min-h-6 min-w-6 items-center justify-center rounded-control-inner text-surface-fg-muted hover:text-surface-fg\"\n aria-label={`Cancel ${file.name}`}\n title=\"Remove\"\n >\n <IconX className=\"h-3.5 w-3.5\" />\n </button>\n )}\n </motion.div>\n </MotionPreference>\n )\n}\n\nfunction UploadToastContent({\n files,\n onRetry,\n onRemove,\n selfDismissId,\n showAccent = false,\n}: {\n files: UploadFile[]\n onRetry?: (fileId: string) => void\n onRemove?: (fileId: string) => void\n selfDismissId?: string | number\n /** Render the colored left accent bar. Off by default. */\n showAccent?: boolean\n}) {\n const [hovered, setHovered] = React.useState(false)\n const [focused, setFocused] = React.useState(false)\n const paused = hovered || focused\n const completeCount = files.filter((f) => f.status === 'complete').length\n const errorCount = files.filter((f) => f.status === 'error').length\n const allTerminal = files.every(\n (f) => f.status === 'complete' || f.status === 'error',\n )\n\n // Self-managed dismiss timer that pauses on hover or keyboard focus\n const remainingRef = React.useRef(UPLOAD_COMPLETE_DELAY)\n const startTimeRef = React.useRef(0)\n\n React.useEffect(() => {\n if (selfDismissId == null || !allTerminal) return\n if (paused) {\n const elapsed = Date.now() - startTimeRef.current\n remainingRef.current = Math.max(0, remainingRef.current - elapsed)\n return\n }\n startTimeRef.current = Date.now()\n const timer = setTimeout(() => {\n sonnerToast.dismiss(selfDismissId)\n }, remainingRef.current)\n return () => clearTimeout(timer)\n }, [selfDismissId, allTerminal, paused])\n\n const accentClass = allTerminal\n ? errorCount > 0\n ? 'bg-error-7'\n : 'bg-success-7'\n : 'bg-accent-9'\n\n const timerBarType: ToastType = allTerminal\n ? errorCount > 0\n ? 'error'\n : 'success'\n : 'loading'\n\n const uploadUrgent = allTerminal && errorCount > 0\n return (\n <MotionPreference>\n <motion.div\n layout\n role={uploadUrgent ? 'alert' : 'status'}\n aria-live={uploadUrgent ? 'assertive' : 'polite'}\n aria-label=\"File uploads\"\n className={cn(\n 'group relative flex w-full overflow-hidden rounded-overlay-sm bg-surface-overlay shadow-floating',\n // Faint surface tint when the batch ended with failures.\n uploadUrgent && 'bg-error-2',\n )}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n onFocusCapture={() => setFocused(true)}\n onBlurCapture={() => setFocused(false)}\n transition={springs.smooth}\n >\n {/* Left accent bar — opt-in via showAccent */}\n {showAccent && (\n <div className={cn('w-1 shrink-0 rounded-l-overlay-sm', accentClass)} />\n )}\n\n {/* Content */}\n <div className=\"min-w-0 flex-1 p-ds-04\">\n {/* Header — icon morphs between upload ↔ result */}\n <div className=\"flex items-center gap-ds-02\">\n <AnimatePresence mode=\"wait\">\n {allTerminal ? (\n errorCount > 0 ? (\n <motion.div\n key=\"error-icon\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n >\n <Icon icon={IconAlertCircle} size=\"sm\" className=\"shrink-0 text-error-11\" />\n </motion.div>\n ) : (\n <motion.div\n key=\"success-icon\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n >\n <Icon icon={IconCheck} size=\"sm\" className=\"shrink-0 text-success-11\" />\n </motion.div>\n )\n ) : (\n <motion.div\n key=\"upload-icon\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Icon icon={IconUpload} size=\"sm\" className=\"shrink-0 text-surface-fg-muted\" />\n </motion.div>\n )}\n </AnimatePresence>\n <p className=\"text-body-md font-semibold text-surface-fg\">\n {allTerminal\n ? errorCount > 0\n ? `${completeCount} of ${files.length} uploaded`\n : `${files.length} file${files.length > 1 ? 's' : ''} uploaded`\n : `Uploading ${files.length} file${files.length > 1 ? 's' : ''}`}\n </p>\n </div>\n {!allTerminal && (\n <p className=\"mt-0.5 text-caption text-surface-fg-muted\">\n {completeCount} of {files.length} complete\n {errorCount > 0 && ` · ${errorCount} failed`}\n </p>\n )}\n\n {/* File list */}\n <div className=\"mt-ds-02 max-h-[140px] overflow-y-auto\">\n <AnimatePresence>\n {files.map((file) => (\n <UploadFileRow\n key={file.id}\n file={file}\n onRetry={onRetry}\n onRemove={onRemove}\n />\n ))}\n </AnimatePresence>\n </div>\n\n {/* Screen reader announcement */}\n <div className=\"sr-only\" aria-live=\"polite\">\n {completeCount} of {files.length} files uploaded\n {errorCount > 0 && `, ${errorCount} failed`}\n </div>\n </div>\n\n {/* Timer bar — only after all terminal */}\n {allTerminal && (\n <TimerBar\n duration={UPLOAD_COMPLETE_DELAY}\n type={timerBarType}\n paused={false}\n />\n )}\n </motion.div>\n </MotionPreference>\n )\n}\n\n/* ---------------------------------------------------------------------------\n * Toast API — wraps Sonner with our styled UI\n * ------------------------------------------------------------------------ */\n\nfunction createSonnerOptions(\n options?: ToastOptions,\n duration?: number,\n): ExternalToast {\n return {\n id: options?.id,\n duration: options?.duration ?? duration ?? DEFAULT_DURATION,\n unstyled: true,\n classNames: { toast: 'w-full' },\n }\n}\n\nfunction createTypedToast(\n type: ToastType,\n message: string | React.ReactNode,\n options?: ToastOptions,\n) {\n assertToasterMounted()\n const duration = options?.duration ?? DEFAULT_DURATION\n return sonnerToast.custom(\n () => (\n <ToastContent\n type={type}\n title={message}\n description={options?.description}\n action={options?.action}\n cancel={options?.cancel}\n duration={duration}\n showAccent={options?.showAccent}\n />\n ),\n createSonnerOptions(options, duration),\n )\n}\n\n/**\n * Toast API — imperative toast notifications with Sonner rendering engine.\n *\n * Mount `<Toaster />` once at your root layout. Then call `toast()` from anywhere.\n *\n * @example\n * toast('Something happened')\n * toast.success('Saved!')\n * toast.error('Failed to save')\n * toast.promise(saveFn(), { loading: 'Saving...', success: 'Done!', error: 'Failed' })\n * toast.undo('Deleted', { onUndo: () => restore() })\n * toast.upload({ files, onRetry, onRemove })\n */\nfunction toast(message: string | React.ReactNode, options?: ToastOptions) {\n return createTypedToast('message', message, options)\n}\n\ntoast.message = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('message', message, options)\n}\n\ntoast.success = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('success', message, options)\n}\n\ntoast.error = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('error', message, options)\n}\n\ntoast.warning = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('warning', message, options)\n}\n\ntoast.info = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('info', message, options)\n}\n\ntoast.loading = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('loading', message, {\n ...options,\n duration: options?.duration ?? Infinity,\n })\n}\n\ntoast.promise = <T,>(\n promise: Promise<T>,\n options: {\n loading: string | React.ReactNode\n success: string | React.ReactNode | ((data: T) => string | React.ReactNode)\n error:\n | string\n | React.ReactNode\n | ((error: unknown) => string | React.ReactNode)\n },\n) => {\n const id = sonnerToast.custom(\n () => (\n <ToastContent type=\"loading\" title={options.loading} showTimerBar={false} />\n ),\n { duration: Infinity, unstyled: true, classNames: { toast: 'w-full' } },\n )\n\n const replaceWith = (type: ToastType, message: React.ReactNode) => {\n // Sonner doesn't reset its auto-dismiss timer when updating a custom toast\n // in-place from duration: Infinity, so ToastContent manages its own\n // hover-aware dismiss timer via selfDismissId.\n sonnerToast.custom(\n () => (\n <ToastContent\n type={type}\n title={message}\n duration={DEFAULT_DURATION}\n selfDismissId={id}\n />\n ),\n { id, duration: Infinity, unstyled: true, classNames: { toast: 'w-full' } },\n )\n }\n\n promise\n .then((data) => {\n const message =\n typeof options.success === 'function'\n ? options.success(data)\n : options.success\n replaceWith('success', message)\n })\n .catch((error) => {\n const message =\n typeof options.error === 'function'\n ? options.error(error)\n : options.error\n replaceWith('error', message)\n })\n\n return id\n}\n\ntoast.undo = (message: string | React.ReactNode, options: ToastUndoOptions) => {\n const duration = options.duration ?? UNDO_DURATION\n return sonnerToast.custom(\n () => (\n <ToastContent\n type=\"message\"\n title={message}\n description={options.description}\n action={{ label: 'Undo', onClick: options.onUndo }}\n duration={duration}\n />\n ),\n { duration, unstyled: true, classNames: { toast: 'w-full' } },\n )\n}\n\ntoast.upload = (options: ToastUploadOptions) => {\n // Pre-determine the id so we can pass it to UploadToastContent for self-dismiss.\n const toastId = options.id ?? String(Date.now())\n\n sonnerToast.custom(\n () => (\n <UploadToastContent\n files={options.files}\n onRetry={options.onRetry}\n onRemove={options.onRemove}\n selfDismissId={toastId}\n showAccent={options.showAccent}\n />\n ),\n {\n id: toastId,\n duration: Infinity,\n unstyled: true,\n classNames: { toast: 'w-full' },\n },\n )\n\n return toastId\n}\n\ntoast.custom = (\n render: (id: string | number) => React.ReactElement,\n options?: ExternalToast,\n) => {\n return sonnerToast.custom(render, {\n ...options,\n unstyled: true,\n classNames: { toast: 'w-full', ...(options?.classNames as Record<string, string>) },\n })\n}\n\ntoast.dismiss = (id?: string | number) => {\n if (id !== undefined) {\n sonnerToast.dismiss(id)\n } else {\n sonnerToast.dismiss()\n }\n}\n\n/* ---------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------ */\n\nexport type ToastProps = ToastOptions\n\nexport {\n formatFileSize,\n TimerBar,\n toast,\n ToastContent,\n UploadFileRow,\n UploadToastContent,\n}\nexport type {\n ToastActionOptions,\n ToastOptions,\n ToastType,\n ToastUndoOptions,\n ToastUploadOptions,\n UploadFile,\n} from './toast-types'\n"],"mappings":"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"}
|