@epilot/spark-ui 0.20.1 → 0.20.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/accordion.cjs +5 -5
- package/dist/components/accordion.cjs.map +1 -1
- package/dist/components/alert-dialog.cjs +9 -9
- package/dist/components/alert-dialog.cjs.map +1 -1
- package/dist/components/attachment.cjs +10 -10
- package/dist/components/attachment.cjs.map +1 -1
- package/dist/components/attachment.d.cts +2 -2
- package/dist/components/attachment.d.ts +2 -2
- package/dist/components/autocomplete.cjs +12 -12
- package/dist/components/autocomplete.cjs.map +1 -1
- package/dist/components/avatar.cjs +2 -2
- package/dist/components/avatar.cjs.map +1 -1
- package/dist/components/avatar.js +1 -1
- package/dist/components/badge.cjs +2 -2
- package/dist/components/badge.cjs.map +1 -1
- package/dist/components/badge.js +2 -2
- package/dist/components/badge.js.map +1 -1
- package/dist/components/bar-chart.cjs +3 -3
- package/dist/components/bar-chart.cjs.map +1 -1
- package/dist/components/bar-chart.js.map +1 -1
- package/dist/components/box.cjs +2 -2
- package/dist/components/box.cjs.map +1 -1
- package/dist/components/box.js +1 -1
- package/dist/components/breadcrumbs.cjs +7 -7
- package/dist/components/breadcrumbs.cjs.map +1 -1
- package/dist/components/bubble.cjs +5 -5
- package/dist/components/bubble.cjs.map +1 -1
- package/dist/components/bubble.d.cts +1 -1
- package/dist/components/bubble.d.ts +1 -1
- package/dist/components/button-group.cjs +5 -5
- package/dist/components/button-group.cjs.map +1 -1
- package/dist/components/button-group.js +1 -1
- package/dist/components/button.cjs +2 -2
- package/dist/components/button.cjs.map +1 -1
- package/dist/components/button.d.cts +3 -3
- package/dist/components/button.d.ts +3 -3
- package/dist/components/button.js +1 -1
- package/dist/components/button.js.map +1 -1
- package/dist/components/calendar.cjs +11 -10
- package/dist/components/calendar.cjs.map +1 -1
- package/dist/components/calendar.js +4 -3
- package/dist/components/calendar.js.map +1 -1
- package/dist/components/callout.cjs +7 -7
- package/dist/components/callout.cjs.map +1 -1
- package/dist/components/callout.d.cts +1 -1
- package/dist/components/callout.d.ts +1 -1
- package/dist/components/card.cjs +16 -16
- package/dist/components/card.cjs.map +1 -1
- package/dist/components/card.js +2 -2
- package/dist/components/card.js.map +1 -1
- package/dist/components/chart.cjs +2 -2
- package/dist/components/chart.cjs.map +1 -1
- package/dist/components/checkbox.cjs +2 -2
- package/dist/components/checkbox.cjs.map +1 -1
- package/dist/components/checkbox.js +1 -1
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/circular-gauge.cjs +3 -3
- package/dist/components/circular-gauge.cjs.map +1 -1
- package/dist/components/circular-gauge.js +1 -1
- package/dist/components/circular-gauge.js.map +1 -1
- package/dist/components/day-of-week-picker.cjs +4 -4
- package/dist/components/day-of-week-picker.cjs.map +1 -1
- package/dist/components/dialog.cjs +8 -8
- package/dist/components/dialog.cjs.map +1 -1
- package/dist/components/donut-chart.cjs.map +1 -1
- package/dist/components/donut-chart.js.map +1 -1
- package/dist/components/drawer.cjs +8 -8
- package/dist/components/drawer.cjs.map +1 -1
- package/dist/components/drawer.js +2 -2
- package/dist/components/drawer.js.map +1 -1
- package/dist/components/dropdown-menu.cjs +10 -10
- package/dist/components/dropdown-menu.cjs.map +1 -1
- package/dist/components/energy-breakdown.cjs +1 -1
- package/dist/components/energy-breakdown.cjs.map +1 -1
- package/dist/components/energy-breakdown.js +1 -1
- package/dist/components/energy-breakdown.js.map +1 -1
- package/dist/components/entity-card.cjs +11 -11
- package/dist/components/entity-card.cjs.map +1 -1
- package/dist/components/field.cjs +16 -16
- package/dist/components/field.cjs.map +1 -1
- package/dist/components/field.js +1 -1
- package/dist/components/field.js.map +1 -1
- package/dist/components/hero-metric.cjs +4 -4
- package/dist/components/hero-metric.cjs.map +1 -1
- package/dist/components/icon.cjs +6 -6
- package/dist/components/icon.cjs.map +1 -1
- package/dist/components/icon.js +5 -5
- package/dist/components/icon.js.map +1 -1
- package/dist/components/info-block.cjs +9 -9
- package/dist/components/info-block.cjs.map +1 -1
- package/dist/components/info-block.d.cts +1 -1
- package/dist/components/info-block.d.ts +1 -1
- package/dist/components/input-date.cjs +4 -3
- package/dist/components/input-date.cjs.map +1 -1
- package/dist/components/input-date.d.cts +3 -1
- package/dist/components/input-date.d.ts +3 -1
- package/dist/components/input-date.js +2 -1
- package/dist/components/input-date.js.map +1 -1
- package/dist/components/input-group.cjs +5 -5
- package/dist/components/input-group.cjs.map +1 -1
- package/dist/components/input-group.d.cts +1 -1
- package/dist/components/input-group.d.ts +1 -1
- package/dist/components/input-otp.cjs +3 -3
- package/dist/components/input-otp.cjs.map +1 -1
- package/dist/components/input-otp.js +1 -1
- package/dist/components/input.cjs +3 -3
- package/dist/components/input.cjs.map +1 -1
- package/dist/components/input.js +1 -1
- package/dist/components/label.cjs +2 -2
- package/dist/components/label.cjs.map +1 -1
- package/dist/components/layout.cjs +2 -2
- package/dist/components/layout.cjs.map +1 -1
- package/dist/components/line-chart.cjs +6 -6
- package/dist/components/line-chart.cjs.map +1 -1
- package/dist/components/line-chart.js +3 -3
- package/dist/components/line-chart.js.map +1 -1
- package/dist/components/list.cjs +11 -11
- package/dist/components/list.cjs.map +1 -1
- package/dist/components/list.d.cts +7 -2
- package/dist/components/list.d.ts +7 -2
- package/dist/components/list.js +2 -2
- package/dist/components/list.js.map +1 -1
- package/dist/components/marker.cjs +4 -4
- package/dist/components/marker.cjs.map +1 -1
- package/dist/components/marker.d.cts +1 -1
- package/dist/components/marker.d.ts +1 -1
- package/dist/components/message-scroller.cjs +7 -7
- package/dist/components/message-scroller.cjs.map +1 -1
- package/dist/components/message-scroller.js +2 -2
- package/dist/components/message-scroller.js.map +1 -1
- package/dist/components/message.cjs +7 -7
- package/dist/components/message.cjs.map +1 -1
- package/dist/components/page-header.cjs +5 -5
- package/dist/components/page-header.cjs.map +1 -1
- package/dist/components/period-tabs.cjs +6 -6
- package/dist/components/period-tabs.cjs.map +1 -1
- package/dist/components/pill.cjs +3 -3
- package/dist/components/pill.cjs.map +1 -1
- package/dist/components/pill.js +3 -3
- package/dist/components/pill.js.map +1 -1
- package/dist/components/popover.cjs +2 -2
- package/dist/components/popover.cjs.map +1 -1
- package/dist/components/power-history-chart.cjs +3 -3
- package/dist/components/power-history-chart.cjs.map +1 -1
- package/dist/components/power-history-chart.js +1 -1
- package/dist/components/power-history-chart.js.map +1 -1
- package/dist/components/power-network.cjs +9 -9
- package/dist/components/power-network.cjs.map +1 -1
- package/dist/components/progress.cjs +3 -3
- package/dist/components/progress.cjs.map +1 -1
- package/dist/components/progress.js +1 -1
- package/dist/components/progress.js.map +1 -1
- package/dist/components/radio-card-group.cjs +3 -3
- package/dist/components/radio-card-group.cjs.map +1 -1
- package/dist/components/radio.cjs +3 -3
- package/dist/components/radio.cjs.map +1 -1
- package/dist/components/radio.js +1 -1
- package/dist/components/sankey-chart.cjs +4 -4
- package/dist/components/sankey-chart.cjs.map +1 -1
- package/dist/components/sankey-chart.js +2 -2
- package/dist/components/sankey-chart.js.map +1 -1
- package/dist/components/schedule-editor.cjs +9 -9
- package/dist/components/schedule-editor.cjs.map +1 -1
- package/dist/components/schedule-list.cjs +10 -10
- package/dist/components/schedule-list.cjs.map +1 -1
- package/dist/components/scroll-area.cjs +3 -3
- package/dist/components/scroll-area.cjs.map +1 -1
- package/dist/components/segmented-control.cjs +8 -8
- package/dist/components/segmented-control.cjs.map +1 -1
- package/dist/components/segmented-control.d.cts +2 -16
- package/dist/components/segmented-control.d.ts +2 -16
- package/dist/components/segmented-control.js +5 -5
- package/dist/components/segmented-control.js.map +1 -1
- package/dist/components/select.cjs +12 -12
- package/dist/components/select.cjs.map +1 -1
- package/dist/components/select.js +1 -1
- package/dist/components/separator.cjs +3 -3
- package/dist/components/separator.cjs.map +1 -1
- package/dist/components/series-legend.cjs +4 -4
- package/dist/components/series-legend.cjs.map +1 -1
- package/dist/components/sheet.cjs +8 -8
- package/dist/components/sheet.cjs.map +1 -1
- package/dist/components/skeleton.cjs +3 -3
- package/dist/components/skeleton.cjs.map +1 -1
- package/dist/components/slider.cjs +11 -11
- package/dist/components/slider.cjs.map +1 -1
- package/dist/components/slider.js.map +1 -1
- package/dist/components/solar-forecast-chart.cjs +2 -2
- package/dist/components/solar-forecast-chart.cjs.map +1 -1
- package/dist/components/sortable-list.cjs +3 -3
- package/dist/components/sortable-list.cjs.map +1 -1
- package/dist/components/spinner.cjs +2 -2
- package/dist/components/spinner.cjs.map +1 -1
- package/dist/components/stat-card.cjs +8 -8
- package/dist/components/stat-card.cjs.map +1 -1
- package/dist/components/stepper.cjs +2 -2
- package/dist/components/stepper.cjs.map +1 -1
- package/dist/components/switch.cjs +3 -3
- package/dist/components/switch.cjs.map +1 -1
- package/dist/components/switch.js +2 -2
- package/dist/components/switch.js.map +1 -1
- package/dist/components/table.cjs +8 -8
- package/dist/components/table.cjs.map +1 -1
- package/dist/components/tabs.cjs +9 -9
- package/dist/components/tabs.cjs.map +1 -1
- package/dist/components/tabs.d.cts +1 -8
- package/dist/components/tabs.d.ts +1 -8
- package/dist/components/tabs.js +2 -2
- package/dist/components/tabs.js.map +1 -1
- package/dist/components/textarea.cjs +3 -3
- package/dist/components/textarea.cjs.map +1 -1
- package/dist/components/textarea.js +1 -1
- package/dist/components/theme.cjs +2 -2
- package/dist/components/theme.cjs.map +1 -1
- package/dist/components/theme.js +1 -1
- package/dist/components/thumbnail.cjs +3 -3
- package/dist/components/thumbnail.cjs.map +1 -1
- package/dist/components/time-picker.cjs +3 -3
- package/dist/components/time-picker.cjs.map +1 -1
- package/dist/components/toast.cjs +39 -14
- package/dist/components/toast.cjs.map +1 -1
- package/dist/components/toast.d.cts +15 -2
- package/dist/components/toast.d.ts +15 -2
- package/dist/components/toast.js +32 -7
- package/dist/components/toast.js.map +1 -1
- package/dist/components/toggle-group.cjs +40 -0
- package/dist/components/toggle-group.cjs.map +1 -0
- package/dist/components/toggle-group.d.cts +28 -0
- package/dist/components/toggle-group.d.ts +28 -0
- package/dist/components/toggle-group.js +37 -0
- package/dist/components/toggle-group.js.map +1 -0
- package/dist/components/toggle.cjs +69 -0
- package/dist/components/toggle.cjs.map +1 -0
- package/dist/components/toggle.d.cts +23 -0
- package/dist/components/toggle.d.ts +23 -0
- package/dist/components/toggle.js +66 -0
- package/dist/components/toggle.js.map +1 -0
- package/dist/components/tooltip.cjs +3 -3
- package/dist/components/tooltip.cjs.map +1 -1
- package/dist/components/tooltip.js +1 -1
- package/dist/components/tooltip.js.map +1 -1
- package/dist/components/typography.cjs +6 -6
- package/dist/components/typography.cjs.map +1 -1
- package/dist/components/wizard-layout.cjs +8 -8
- package/dist/components/wizard-layout.cjs.map +1 -1
- package/dist/lib/generate-radix-colors.cjs +1 -1
- package/dist/lib/generate-radix-colors.cjs.map +1 -1
- package/dist/lib/generate-radix-colors.js +1 -1
- package/dist/lib/generate-radix-colors.js.map +1 -1
- package/dist/lib/input-box-variants.cjs +9 -9
- package/dist/lib/input-box-variants.cjs.map +1 -1
- package/dist/lib/toggle-group-context.cjs +12 -0
- package/dist/lib/toggle-group-context.cjs.map +1 -0
- package/dist/lib/toggle-group-context.d.cts +17 -0
- package/dist/lib/toggle-group-context.d.ts +17 -0
- package/dist/lib/toggle-group-context.js +9 -0
- package/dist/lib/toggle-group-context.js.map +1 -0
- package/dist/styles/spark-default.css +11 -0
- package/dist/styles/theme.css +15 -4
- package/dist/utils/cn.cjs +2 -2
- package/dist/utils/cn.d.cts +1 -1
- package/dist/utils/cn.d.ts +1 -1
- package/dist/utils/cn.js +1 -1
- package/dist/utils/format.cjs +1 -0
- package/dist/utils/format.cjs.map +1 -1
- package/dist/utils/format.d.cts +2 -1
- package/dist/utils/format.d.ts +2 -1
- package/dist/utils/format.js +1 -1
- package/dist/utils/format.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toast.cjs","names":["CheckCircle","Info","Warning","Cancel","ToastPrimitive","Spinner","buttonVariants","Close"],"sources":["../../src/components/toast.tsx"],"sourcesContent":["'use client'\n\nimport { Toast as ToastPrimitive } from '@base-ui/react/toast'\nimport {\n Cancel,\n CheckCircle,\n Close,\n Info,\n Warning,\n type SparkIcon,\n} from '@epilot/spark-icons'\nimport * as React from 'react'\nimport { cn } from '../utils/cn'\nimport { buttonVariants } from './button'\nimport { Spinner } from './spinner'\n\ntype ToastType = 'success' | 'info' | 'warning' | 'error' | 'loading'\n\ntype ToastPosition =\n | 'top-left'\n | 'top-center'\n | 'top-right'\n | 'bottom-left'\n | 'bottom-center'\n | 'bottom-right'\n\n// Per-toast overrides Base UI carries through untouched as opaque `data`.\ntype ToastData = { closeButton?: boolean }\n\n// `label` is the button's content; everything else a `<button>` takes passes\n// straight through, so a caller can set `className`, `disabled`, `type`, and\n// the rest without a new prop for each one.\ntype ToastAction = Omit<\n React.ComponentPropsWithoutRef<'button'>,\n 'children'\n> & {\n label: React.ReactNode\n}\n\ntype ToastOptions = {\n description?: React.ReactNode\n /** Auto-dismiss time in ms. `0` keeps the toast until dismissed. */\n duration?: number\n /** Reuse an id to update a toast in place instead of stacking a new one. */\n id?: string\n closeButton?: boolean\n action?: ToastAction\n onDismiss?: () => void\n /** `high` announces urgently (assertive); the default is polite. */\n priority?: 'low' | 'high'\n}\n\ntype ToastPromiseMessages<Value> = {\n loading: string\n success: string | ((value: Value) => string)\n error: string | ((error: unknown) => string)\n}\n\nconst TOAST_ICONS: Record<\n Exclude<ToastType, 'loading'>,\n { icon: SparkIcon; className: string }\n> = {\n success: { icon: CheckCircle, className: 'text-success-light' },\n info: { icon: Info, className: 'text-info-light' },\n warning: { icon: Warning, className: 'text-warning-light' },\n error: { icon: Cancel, className: 'text-error-light' },\n}\n\n// Opaque radix steps (bg 2, border 6, text 11) so a floating toast never lets\n// page content bleed through the way the alpha `-soft` tokens would. The border\n// carries the tone too, otherwise a tinted toast keeps a grey outline.\nconst TONE_CLASSES: Record<Exclude<ToastType, 'loading'>, string> = {\n success: 'border-green-6 bg-green-2 text-green-11',\n info: 'border-blue-6 bg-blue-2 text-blue-11',\n warning: 'border-orange-6 bg-orange-2 text-orange-11',\n error: 'border-red-6 bg-red-2 text-red-11',\n}\n\n// Mobile-first: every position drops to a full-width stack at the top, which\n// is what the end-customer portal ships. `position` only takes effect from the\n// `sm` breakpoint up.\nconst POSITION_CLASSES: Record<ToastPosition, string> = {\n 'top-left': 'sm:top-4 sm:right-auto sm:bottom-auto sm:left-4',\n 'top-center':\n 'sm:top-4 sm:right-auto sm:bottom-auto sm:left-1/2 sm:-translate-x-1/2',\n 'top-right': 'sm:top-4 sm:right-4 sm:bottom-auto sm:left-auto',\n 'bottom-left': 'sm:top-auto sm:right-auto sm:bottom-4 sm:left-4',\n 'bottom-center':\n 'sm:top-auto sm:right-auto sm:bottom-4 sm:left-1/2 sm:-translate-x-1/2',\n 'bottom-right': 'sm:top-auto sm:right-4 sm:bottom-4 sm:left-auto',\n}\n\nconst manager = ToastPrimitive.createToastManager<ToastData>()\n\nlet sequence = 0\n\n// `label` names the content and `onClick` is wrapped, so neither reaches the\n// DOM button as-is; everything else does.\nfunction actionRest({\n label: _label,\n onClick: _onClick,\n ...rest\n}: ToastAction) {\n return rest\n}\n\nfunction toManagerOptions(\n id: string,\n title: React.ReactNode,\n type: ToastType | undefined,\n options: ToastOptions | undefined,\n) {\n const action = options?.action\n return {\n id,\n title,\n type,\n description: options?.description,\n timeout: options?.duration,\n priority: options?.priority,\n onClose: options?.onDismiss,\n // Base UI leaves the toast open after an action; dismissing matches what\n // callers expect from an \"Undo\" button.\n actionProps: action && {\n ...actionRest(action),\n children: action.label,\n onClick: (event: React.MouseEvent<HTMLButtonElement>) => {\n action.onClick?.(event)\n manager.close(id)\n },\n },\n data: { closeButton: options?.closeButton },\n }\n}\n\nconst emit =\n (type?: ToastType) =>\n (message: React.ReactNode, options?: ToastOptions): string => {\n // an id up front lets the action handler close the toast it belongs to\n const id = options?.id ?? `spark-toast-${(sequence += 1)}`\n return manager.add(toManagerOptions(id, message, type, options))\n }\n\n// A string phase becomes a titled toast of that type; an object phase keeps\n// whatever the caller set and only fills in the type.\nconst phase =\n <Value,>(\n value: ToastPromiseMessages<Value>[keyof ToastPromiseMessages<Value>],\n type: ToastType,\n ) =>\n (result: Value) => {\n const resolved = typeof value === 'function' ? value(result) : value\n return { title: resolved, type }\n }\n\nconst toast = Object.assign(emit(), {\n success: emit('success'),\n info: emit('info'),\n warning: emit('warning'),\n error: emit('error'),\n loading: emit('loading'),\n /** Dismiss one toast, or every toast when called without an id. */\n dismiss: (id?: string) => manager.close(id),\n promise: <Value,>(\n promiseValue: Promise<Value>,\n messages: ToastPromiseMessages<Value>,\n ) =>\n manager.promise(promiseValue, {\n loading: { title: messages.loading, type: 'loading' as const },\n success: phase(messages.success, 'success'),\n error: phase(messages.error as never, 'error'),\n }),\n})\n\nfunction ToastIcon({ type }: { type?: string }) {\n const icon = TOAST_ICONS[type as Exclude<ToastType, 'loading'>]\n if (type !== 'loading' && !icon) return null\n\n const Glyph = icon?.icon\n\n // Fixed box matching the title's line height, so the glyph and the spinner\n // both centre on the first line whether or not a description follows.\n return (\n <span\n aria-hidden=\"true\"\n className=\"flex h-5 w-5 shrink-0 items-center justify-center\"\n >\n {type === 'loading' ? (\n // Decorative: the toast already announces the loading message, so the\n // spinner must not add a second status live region.\n <Spinner\n className=\"text-gray-11\"\n label=\"\"\n role=\"presentation\"\n size=\"sm\"\n />\n ) : Glyph ? (\n <Glyph className={cn('[--spark-icon-size:20px]', icon.className)} />\n ) : null}\n </span>\n )\n}\n\nfunction ToastItem({\n closeButton,\n richColors,\n toast: toastObject,\n}: {\n closeButton: boolean\n richColors: boolean\n toast: ToastPrimitive.Root.ToastObject<ToastData>\n}) {\n const tone = TONE_CLASSES[toastObject.type as Exclude<ToastType, 'loading'>]\n const showClose = toastObject.data?.closeButton ?? closeButton\n\n return (\n <ToastPrimitive.Root\n data-slot=\"toast\"\n className={cn(\n // Skin only. All geometry (position, stacking, transitions, the\n // pointer bridge) lives in styles/index.css, because Tailwind's\n // `utilities` layer outranks `components` and a stray `bottom-0` here\n // would beat the stylesheet's `bottom: auto` on top-anchored stacks.\n // The border is what makes the cards behind the front one legible.\n 'flex items-start gap-2 rounded-lg border border-gray-a5 bg-gray-1 p-3 text-gray shadow-md select-none',\n richColors && tone,\n )}\n // the stack is top-anchored on mobile and bottom-anchored above `sm`, so\n // both vertical directions have to dismiss\n swipeDirection={['up', 'down', 'right']}\n toast={toastObject}\n >\n <ToastIcon type={toastObject.type} />\n <ToastPrimitive.Content className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <ToastPrimitive.Title\n className=\"text-sm font-medium\"\n data-slot=\"toast-title\"\n />\n <ToastPrimitive.Description\n className={cn(\n 'text-sm',\n richColors && tone ? 'text-inherit' : 'text-gray-11',\n )}\n data-slot=\"toast-description\"\n />\n </ToastPrimitive.Content>\n {toastObject.actionProps ? (\n <ToastPrimitive.Action\n // -my-2 keeps the 36px control from stretching a single-line row past\n // the 20px text; self-center then lands it on the title when there is\n // no description, and mid-block when there is one\n className={cn(\n buttonVariants({ size: 'small', variant: 'primary' }),\n '-my-2 shrink-0 self-center rounded-full',\n )}\n data-slot=\"toast-action\"\n />\n ) : null}\n {showClose ? (\n <ToastPrimitive.Close\n aria-label=\"Close\"\n className={cn(\n buttonVariants({ size: 'icon-small', variant: 'tertiary' }),\n // -my-1: same reason as the action, for a 28px control\n '-my-1 -mr-1 size-7 shrink-0 self-center text-gray-11',\n )}\n data-slot=\"toast-close\"\n >\n <Close aria-hidden=\"true\" className=\"[--spark-icon-size:18px]\" />\n </ToastPrimitive.Close>\n ) : null}\n </ToastPrimitive.Root>\n )\n}\n\nfunction ToastList({\n closeButton,\n richColors,\n}: {\n closeButton: boolean\n richColors: boolean\n}) {\n const { toasts } = ToastPrimitive.useToastManager<ToastData>()\n\n return toasts.map((toastObject) => (\n <ToastItem\n closeButton={closeButton}\n key={toastObject.id}\n richColors={richColors}\n toast={toastObject}\n />\n ))\n}\n\ntype ToasterProps = {\n className?: string\n /** Corner or edge the toasts stack in. */\n position?: ToastPosition\n /** Close button on every toast; a per-toast `closeButton` wins. */\n closeButton?: boolean\n /** Tinted surfaces for success/info/warning/error toasts. */\n richColors?: boolean\n /** Default auto-dismiss time in ms. `0` disables auto-dismiss. */\n duration?: number\n /** Toasts visible at once before older ones are marked `data-limited`. */\n limit?: number\n /**\n * Vertical edge below the `sm` breakpoint, where the stack is always\n * full-width and `position` does not apply. Defaults to `top`, matching the\n * end-customer portal.\n */\n mobilePosition?: 'top' | 'bottom'\n}\n\nfunction Toaster({\n className,\n closeButton = true,\n duration = 4000,\n limit,\n mobilePosition = 'top',\n position = 'bottom-right',\n richColors = true,\n}: ToasterProps) {\n return (\n <ToastPrimitive.Provider\n limit={limit}\n timeout={duration}\n toastManager={manager}\n >\n <ToastPrimitive.Portal>\n <ToastPrimitive.Viewport\n // block + zero height: the toasts are absolutely positioned against\n // this box's bottom edge, the way Base UI's stacked example works\n className={cn(\n 'fixed inset-x-4 z-50 block h-0 outline-none',\n mobilePosition === 'top' ? 'top-4' : 'bottom-4',\n 'sm:inset-x-auto sm:w-96 sm:max-w-[calc(100vw-2rem)]',\n POSITION_CLASSES[position],\n className,\n )}\n data-mobile-side={mobilePosition}\n data-side={position.startsWith('top') ? 'top' : 'bottom'}\n data-slot=\"toaster\"\n >\n <ToastList closeButton={closeButton} richColors={richColors} />\n </ToastPrimitive.Viewport>\n </ToastPrimitive.Portal>\n </ToastPrimitive.Provider>\n )\n}\n\nexport { Toaster, toast }\nexport type {\n ToastAction,\n ToasterProps,\n ToastOptions,\n ToastPosition,\n ToastPromiseMessages,\n ToastType,\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"toast.cjs","names":["CheckCircle","Info","Warning","Cancel","ToastPrimitive","Spinner","buttonVariants","Close"],"sources":["../../src/components/toast.tsx"],"sourcesContent":["'use client'\n\nimport { Toast as ToastPrimitive } from '@base-ui/react/toast'\nimport type { BaseUIEvent } from '@base-ui/react/types'\nimport {\n Cancel,\n CheckCircle,\n Close,\n Info,\n Warning,\n type SparkIcon,\n} from '@epilot/spark-icons'\nimport * as React from 'react'\nimport { cn } from '../utils/cn'\nimport { buttonVariants } from './button'\nimport { Spinner } from './spinner'\n\ntype ToastType = 'success' | 'info' | 'warning' | 'error' | 'loading'\n\ntype ToastPosition =\n | 'top-left'\n | 'top-center'\n | 'top-right'\n | 'bottom-left'\n | 'bottom-center'\n | 'bottom-right'\n\n// Per-toast overrides Base UI carries through untouched as opaque `data`.\ntype ToastData = {\n closeButton?: boolean\n closeLabel?: string\n icon?: React.ReactNode\n onCloseClick?: (event: React.MouseEvent<HTMLButtonElement>) => void\n}\n\n// `label` is the button's content; everything else a `<button>` takes passes\n// straight through, so a caller can set `className`, `disabled`, `type`, and\n// the rest without a new prop for each one.\ntype ToastAction = Omit<\n React.ComponentPropsWithoutRef<'button'>,\n 'children'\n> & {\n label: React.ReactNode\n}\n\ntype ToastOptions = {\n description?: React.ReactNode\n /** Auto-dismiss time in ms. `0` keeps the toast until dismissed. */\n duration?: number\n /** Reuse an id to update a toast in place instead of stacking a new one. */\n id?: string\n closeButton?: boolean\n /** Accessible name of the close button; overrides the Toaster's `closeLabel`. */\n closeLabel?: string\n /**\n * Replaces the type icon. `null` renders no icon. The icon is decorative,\n * so the title has to carry the meaning on its own.\n */\n icon?: React.ReactNode\n /**\n * Runs when the close button is clicked. Call `event.preventDefault()` to\n * keep the toast open, for example to cancel a running download first.\n */\n onCloseClick?: (event: React.MouseEvent<HTMLButtonElement>) => void\n action?: ToastAction\n onDismiss?: () => void\n /** `high` announces urgently (assertive); the default is polite. */\n priority?: 'low' | 'high'\n}\n\ntype ToastPromiseMessages<Value> = {\n loading: string\n success: string | ((value: Value) => string)\n error: string | ((error: unknown) => string)\n}\n\nconst TOAST_ICONS: Record<\n Exclude<ToastType, 'loading'>,\n { icon: SparkIcon; className: string }\n> = {\n success: { icon: CheckCircle, className: 'text-success-light' },\n info: { icon: Info, className: 'text-info-light' },\n warning: { icon: Warning, className: 'text-warning-light' },\n error: { icon: Cancel, className: 'text-error-light' },\n}\n\n// Opaque radix steps (bg 2, border 6, text 11) so a floating toast never lets\n// page content bleed through the way the alpha `-soft` tokens would. The border\n// carries the tone too, otherwise a tinted toast keeps a grey outline.\nconst TONE_CLASSES: Record<Exclude<ToastType, 'loading'>, string> = {\n success: 'border-green-6 bg-green-2 text-green-11',\n info: 'border-blue-6 bg-blue-2 text-blue-11',\n warning: 'border-orange-6 bg-orange-2 text-orange-11',\n error: 'border-red-6 bg-red-2 text-red-11',\n}\n\n// Mobile-first: every position drops to a full-width stack at the top, which\n// is what the end-customer portal ships. `position` only takes effect from the\n// `sm` breakpoint up.\nconst POSITION_CLASSES: Record<ToastPosition, string> = {\n 'top-left': 'sm:top-4 sm:right-auto sm:bottom-auto sm:left-4',\n 'top-center':\n 'sm:top-4 sm:right-auto sm:bottom-auto sm:left-1/2 sm:-translate-x-1/2',\n 'top-right': 'sm:top-4 sm:right-4 sm:bottom-auto sm:left-auto',\n 'bottom-left': 'sm:top-auto sm:right-auto sm:bottom-4 sm:left-4',\n 'bottom-center':\n 'sm:top-auto sm:right-auto sm:bottom-4 sm:left-1/2 sm:-translate-x-1/2',\n 'bottom-right': 'sm:top-auto sm:right-4 sm:bottom-4 sm:left-auto',\n}\n\nconst manager = ToastPrimitive.createToastManager<ToastData>()\n\nlet sequence = 0\n\n// `label` names the content and `onClick` is wrapped, so neither reaches the\n// DOM button as-is; everything else does.\nfunction actionRest({\n label: _label,\n onClick: _onClick,\n ...rest\n}: ToastAction) {\n return rest\n}\n\nfunction toManagerOptions(\n id: string,\n title: React.ReactNode,\n type: ToastType | undefined,\n options: ToastOptions | undefined,\n) {\n const action = options?.action\n return {\n id,\n title,\n type,\n description: options?.description,\n timeout: options?.duration,\n priority: options?.priority,\n onClose: options?.onDismiss,\n // Base UI leaves the toast open after an action; dismissing matches what\n // callers expect from an \"Undo\" button.\n actionProps: action && {\n ...actionRest(action),\n children: action.label,\n onClick: (event: React.MouseEvent<HTMLButtonElement>) => {\n action.onClick?.(event)\n manager.close(id)\n },\n },\n data: {\n closeButton: options?.closeButton,\n closeLabel: options?.closeLabel,\n icon: options?.icon,\n onCloseClick: options?.onCloseClick,\n },\n }\n}\n\nconst emit =\n (type?: ToastType) =>\n (message: React.ReactNode, options?: ToastOptions): string => {\n // an id up front lets the action handler close the toast it belongs to\n const id = options?.id ?? `spark-toast-${(sequence += 1)}`\n return manager.add(toManagerOptions(id, message, type, options))\n }\n\n// A string phase becomes a titled toast of that type; an object phase keeps\n// whatever the caller set and only fills in the type.\nconst phase =\n <Value,>(\n value: ToastPromiseMessages<Value>[keyof ToastPromiseMessages<Value>],\n type: ToastType,\n ) =>\n (result: Value) => {\n const resolved = typeof value === 'function' ? value(result) : value\n return { title: resolved, type }\n }\n\nconst toast = Object.assign(emit(), {\n success: emit('success'),\n info: emit('info'),\n warning: emit('warning'),\n error: emit('error'),\n loading: emit('loading'),\n /** Dismiss one toast, or every toast when called without an id. */\n dismiss: (id?: string) => manager.close(id),\n promise: <Value,>(\n promiseValue: Promise<Value>,\n messages: ToastPromiseMessages<Value>,\n ) =>\n manager.promise(promiseValue, {\n loading: { title: messages.loading, type: 'loading' as const },\n success: phase(messages.success, 'success'),\n error: phase(messages.error as never, 'error'),\n }),\n})\n\nfunction ToastIcon({\n custom,\n type,\n}: {\n custom: React.ReactNode | undefined\n type?: string\n}) {\n const icon = TOAST_ICONS[type as Exclude<ToastType, 'loading'>]\n if (custom !== undefined) {\n if (custom === null || custom === false) return null\n return (\n <span\n aria-hidden=\"true\"\n className=\"flex h-5 w-5 shrink-0 items-center justify-center [--spark-icon-size:20px]\"\n data-slot=\"toast-icon\"\n >\n {custom}\n </span>\n )\n }\n if (type !== 'loading' && !icon) return null\n\n const Glyph = icon?.icon\n\n // Fixed box matching the title's line height, so the glyph and the spinner\n // both centre on the first line whether or not a description follows.\n return (\n <span\n aria-hidden=\"true\"\n className=\"flex h-5 w-5 shrink-0 items-center justify-center\"\n data-slot=\"toast-icon\"\n >\n {type === 'loading' ? (\n // Decorative: the toast already announces the loading message, so the\n // spinner must not add a second status live region.\n <Spinner\n className=\"text-gray-11\"\n label=\"\"\n role=\"presentation\"\n size=\"sm\"\n />\n ) : Glyph ? (\n <Glyph className={cn('[--spark-icon-size:20px]', icon.className)} />\n ) : null}\n </span>\n )\n}\n\nfunction ToastItem({\n closeButton,\n closeLabel,\n richColors,\n toast: toastObject,\n}: {\n closeButton: boolean\n closeLabel: string\n richColors: boolean\n toast: ToastPrimitive.Root.ToastObject<ToastData>\n}) {\n const tone = TONE_CLASSES[toastObject.type as Exclude<ToastType, 'loading'>]\n const showClose = toastObject.data?.closeButton ?? closeButton\n const onCloseClick = toastObject.data?.onCloseClick\n\n return (\n <ToastPrimitive.Root\n data-slot=\"toast\"\n className={cn(\n // Skin only. All geometry (position, stacking, transitions, the\n // pointer bridge) lives in styles/index.css, because Tailwind's\n // `utilities` layer outranks `components` and a stray `bottom-0` here\n // would beat the stylesheet's `bottom: auto` on top-anchored stacks.\n // The border is what makes the cards behind the front one legible.\n 'flex items-start gap-2 rounded-lg border border-gray-a5 bg-gray-1 p-3 text-gray shadow-md select-none',\n richColors && tone,\n )}\n // the stack is top-anchored on mobile and bottom-anchored above `sm`, so\n // both vertical directions have to dismiss\n swipeDirection={['up', 'down', 'right']}\n toast={toastObject}\n >\n <ToastIcon custom={toastObject.data?.icon} type={toastObject.type} />\n <ToastPrimitive.Content className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <ToastPrimitive.Title\n className=\"text-sm font-medium\"\n data-slot=\"toast-title\"\n />\n <ToastPrimitive.Description\n className={cn(\n 'text-sm',\n richColors && tone ? 'text-inherit' : 'text-gray-11',\n )}\n data-slot=\"toast-description\"\n />\n </ToastPrimitive.Content>\n {toastObject.actionProps ? (\n <ToastPrimitive.Action\n // -my-2 keeps the 36px control from stretching a single-line row past\n // the 20px text; self-center then lands it on the title when there is\n // no description, and mid-block when there is one\n className={cn(\n buttonVariants({ size: 'small', variant: 'primary' }),\n '-my-2 shrink-0 self-center rounded-full',\n )}\n data-slot=\"toast-action\"\n />\n ) : null}\n {showClose ? (\n <ToastPrimitive.Close\n aria-label={toastObject.data?.closeLabel ?? closeLabel}\n className={cn(\n buttonVariants({ size: 'icon-small', variant: 'tertiary' }),\n // -my-1: same reason as the action, for a 28px control\n '-my-1 -mr-1 size-7 shrink-0 self-center text-gray-11',\n )}\n data-slot=\"toast-close\"\n onClick={\n onCloseClick &&\n ((event: BaseUIEvent<React.MouseEvent<HTMLButtonElement>>) => {\n onCloseClick(event)\n // Base UI's own close runs after this handler unless prevented.\n if (event.defaultPrevented) event.preventBaseUIHandler()\n })\n }\n >\n <Close aria-hidden=\"true\" className=\"[--spark-icon-size:18px]\" />\n </ToastPrimitive.Close>\n ) : null}\n </ToastPrimitive.Root>\n )\n}\n\nfunction ToastList({\n closeButton,\n closeLabel,\n richColors,\n}: {\n closeButton: boolean\n closeLabel: string\n richColors: boolean\n}) {\n const { toasts } = ToastPrimitive.useToastManager<ToastData>()\n\n return toasts.map((toastObject) => (\n <ToastItem\n closeButton={closeButton}\n closeLabel={closeLabel}\n key={toastObject.id}\n richColors={richColors}\n toast={toastObject}\n />\n ))\n}\n\ntype ToasterProps = {\n className?: string\n /** Corner or edge the toasts stack in. */\n position?: ToastPosition\n /** Close button on every toast; a per-toast `closeButton` wins. */\n closeButton?: boolean\n /** Accessible name of every close button; a per-toast `closeLabel` wins. */\n closeLabel?: string\n /** Tinted surfaces for success/info/warning/error toasts. */\n richColors?: boolean\n /** Default auto-dismiss time in ms. `0` disables auto-dismiss. */\n duration?: number\n /** Toasts visible at once before older ones are marked `data-limited`. */\n limit?: number\n /**\n * Vertical edge below the `sm` breakpoint, where the stack is always\n * full-width and `position` does not apply. Defaults to `top`, matching the\n * end-customer portal.\n */\n mobilePosition?: 'top' | 'bottom'\n}\n\nfunction Toaster({\n className,\n closeButton = true,\n closeLabel = 'Close',\n duration = 4000,\n limit,\n mobilePosition = 'top',\n position = 'bottom-right',\n richColors = true,\n}: ToasterProps) {\n return (\n <ToastPrimitive.Provider\n limit={limit}\n timeout={duration}\n toastManager={manager}\n >\n <ToastPrimitive.Portal>\n <ToastPrimitive.Viewport\n // block + zero height: the toasts are absolutely positioned against\n // this box's bottom edge, the way Base UI's stacked example works\n className={cn(\n 'fixed inset-x-4 z-50 block h-0 outline-none',\n mobilePosition === 'top' ? 'top-4' : 'bottom-4',\n 'sm:inset-x-auto sm:w-96 sm:max-w-[calc(100vw-2rem)]',\n POSITION_CLASSES[position],\n className,\n )}\n data-mobile-side={mobilePosition}\n data-side={position.startsWith('top') ? 'top' : 'bottom'}\n data-slot=\"toaster\"\n >\n <ToastList\n closeButton={closeButton}\n closeLabel={closeLabel}\n richColors={richColors}\n />\n </ToastPrimitive.Viewport>\n </ToastPrimitive.Portal>\n </ToastPrimitive.Provider>\n )\n}\n\nexport { Toaster, toast }\nexport type {\n ToastAction,\n ToasterProps,\n ToastOptions,\n ToastPosition,\n ToastPromiseMessages,\n ToastType,\n}\n"],"mappings":"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"}
|
|
@@ -12,7 +12,18 @@ type ToastOptions = {
|
|
|
12
12
|
description?: React$1.ReactNode; /** Auto-dismiss time in ms. `0` keeps the toast until dismissed. */
|
|
13
13
|
duration?: number; /** Reuse an id to update a toast in place instead of stacking a new one. */
|
|
14
14
|
id?: string;
|
|
15
|
-
closeButton?: boolean;
|
|
15
|
+
closeButton?: boolean; /** Accessible name of the close button; overrides the Toaster's `closeLabel`. */
|
|
16
|
+
closeLabel?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Replaces the type icon. `null` renders no icon. The icon is decorative,
|
|
19
|
+
* so the title has to carry the meaning on its own.
|
|
20
|
+
*/
|
|
21
|
+
icon?: React$1.ReactNode;
|
|
22
|
+
/**
|
|
23
|
+
* Runs when the close button is clicked. Call `event.preventDefault()` to
|
|
24
|
+
* keep the toast open, for example to cancel a running download first.
|
|
25
|
+
*/
|
|
26
|
+
onCloseClick?: (event: React$1.MouseEvent<HTMLButtonElement>) => void;
|
|
16
27
|
action?: ToastAction;
|
|
17
28
|
onDismiss?: () => void; /** `high` announces urgently (assertive); the default is polite. */
|
|
18
29
|
priority?: 'low' | 'high';
|
|
@@ -34,7 +45,8 @@ declare const toast: ((message: React$1.ReactNode, options?: ToastOptions) => st
|
|
|
34
45
|
type ToasterProps = {
|
|
35
46
|
className?: string; /** Corner or edge the toasts stack in. */
|
|
36
47
|
position?: ToastPosition; /** Close button on every toast; a per-toast `closeButton` wins. */
|
|
37
|
-
closeButton?: boolean; /**
|
|
48
|
+
closeButton?: boolean; /** Accessible name of every close button; a per-toast `closeLabel` wins. */
|
|
49
|
+
closeLabel?: string; /** Tinted surfaces for success/info/warning/error toasts. */
|
|
38
50
|
richColors?: boolean; /** Default auto-dismiss time in ms. `0` disables auto-dismiss. */
|
|
39
51
|
duration?: number; /** Toasts visible at once before older ones are marked `data-limited`. */
|
|
40
52
|
limit?: number;
|
|
@@ -48,6 +60,7 @@ type ToasterProps = {
|
|
|
48
60
|
declare function Toaster({
|
|
49
61
|
className,
|
|
50
62
|
closeButton,
|
|
63
|
+
closeLabel,
|
|
51
64
|
duration,
|
|
52
65
|
limit,
|
|
53
66
|
mobilePosition,
|
|
@@ -12,7 +12,18 @@ type ToastOptions = {
|
|
|
12
12
|
description?: React$1.ReactNode; /** Auto-dismiss time in ms. `0` keeps the toast until dismissed. */
|
|
13
13
|
duration?: number; /** Reuse an id to update a toast in place instead of stacking a new one. */
|
|
14
14
|
id?: string;
|
|
15
|
-
closeButton?: boolean;
|
|
15
|
+
closeButton?: boolean; /** Accessible name of the close button; overrides the Toaster's `closeLabel`. */
|
|
16
|
+
closeLabel?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Replaces the type icon. `null` renders no icon. The icon is decorative,
|
|
19
|
+
* so the title has to carry the meaning on its own.
|
|
20
|
+
*/
|
|
21
|
+
icon?: React$1.ReactNode;
|
|
22
|
+
/**
|
|
23
|
+
* Runs when the close button is clicked. Call `event.preventDefault()` to
|
|
24
|
+
* keep the toast open, for example to cancel a running download first.
|
|
25
|
+
*/
|
|
26
|
+
onCloseClick?: (event: React$1.MouseEvent<HTMLButtonElement>) => void;
|
|
16
27
|
action?: ToastAction;
|
|
17
28
|
onDismiss?: () => void; /** `high` announces urgently (assertive); the default is polite. */
|
|
18
29
|
priority?: 'low' | 'high';
|
|
@@ -34,7 +45,8 @@ declare const toast: ((message: React$1.ReactNode, options?: ToastOptions) => st
|
|
|
34
45
|
type ToasterProps = {
|
|
35
46
|
className?: string; /** Corner or edge the toasts stack in. */
|
|
36
47
|
position?: ToastPosition; /** Close button on every toast; a per-toast `closeButton` wins. */
|
|
37
|
-
closeButton?: boolean; /**
|
|
48
|
+
closeButton?: boolean; /** Accessible name of every close button; a per-toast `closeLabel` wins. */
|
|
49
|
+
closeLabel?: string; /** Tinted surfaces for success/info/warning/error toasts. */
|
|
38
50
|
richColors?: boolean; /** Default auto-dismiss time in ms. `0` disables auto-dismiss. */
|
|
39
51
|
duration?: number; /** Toasts visible at once before older ones are marked `data-limited`. */
|
|
40
52
|
limit?: number;
|
|
@@ -48,6 +60,7 @@ type ToasterProps = {
|
|
|
48
60
|
declare function Toaster({
|
|
49
61
|
className,
|
|
50
62
|
closeButton,
|
|
63
|
+
closeLabel,
|
|
51
64
|
duration,
|
|
52
65
|
limit,
|
|
53
66
|
mobilePosition,
|
package/dist/components/toast.js
CHANGED
|
@@ -63,7 +63,12 @@ function toManagerOptions(id, title, type, options) {
|
|
|
63
63
|
manager.close(id);
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
|
-
data: {
|
|
66
|
+
data: {
|
|
67
|
+
closeButton: options?.closeButton,
|
|
68
|
+
closeLabel: options?.closeLabel,
|
|
69
|
+
icon: options?.icon,
|
|
70
|
+
onCloseClick: options?.onCloseClick
|
|
71
|
+
}
|
|
67
72
|
};
|
|
68
73
|
}
|
|
69
74
|
const emit = (type) => (message, options) => {
|
|
@@ -93,13 +98,23 @@ const toast = Object.assign(emit(), {
|
|
|
93
98
|
error: phase(messages.error, "error")
|
|
94
99
|
})
|
|
95
100
|
});
|
|
96
|
-
function ToastIcon({ type }) {
|
|
101
|
+
function ToastIcon({ custom, type }) {
|
|
97
102
|
const icon = TOAST_ICONS[type];
|
|
103
|
+
if (custom !== void 0) {
|
|
104
|
+
if (custom === null || custom === false) return null;
|
|
105
|
+
return /* @__PURE__ */ jsx("span", {
|
|
106
|
+
"aria-hidden": "true",
|
|
107
|
+
className: "flex h-5 w-5 shrink-0 items-center justify-center [--spark-icon-size:20px]",
|
|
108
|
+
"data-slot": "toast-icon",
|
|
109
|
+
children: custom
|
|
110
|
+
});
|
|
111
|
+
}
|
|
98
112
|
if (type !== "loading" && !icon) return null;
|
|
99
113
|
const Glyph = icon?.icon;
|
|
100
114
|
return /* @__PURE__ */ jsx("span", {
|
|
101
115
|
"aria-hidden": "true",
|
|
102
116
|
className: "flex h-5 w-5 shrink-0 items-center justify-center",
|
|
117
|
+
"data-slot": "toast-icon",
|
|
103
118
|
children: type === "loading" ? /* @__PURE__ */ jsx(Spinner, {
|
|
104
119
|
className: "text-gray-11",
|
|
105
120
|
label: "",
|
|
@@ -108,9 +123,10 @@ function ToastIcon({ type }) {
|
|
|
108
123
|
}) : Glyph ? /* @__PURE__ */ jsx(Glyph, { className: cn("[--spark-icon-size:20px]", icon.className) }) : null
|
|
109
124
|
});
|
|
110
125
|
}
|
|
111
|
-
function ToastItem({ closeButton, richColors, toast: toastObject }) {
|
|
126
|
+
function ToastItem({ closeButton, closeLabel, richColors, toast: toastObject }) {
|
|
112
127
|
const tone = TONE_CLASSES[toastObject.type];
|
|
113
128
|
const showClose = toastObject.data?.closeButton ?? closeButton;
|
|
129
|
+
const onCloseClick = toastObject.data?.onCloseClick;
|
|
114
130
|
return /* @__PURE__ */ jsxs(Toast.Root, {
|
|
115
131
|
"data-slot": "toast",
|
|
116
132
|
className: cn("flex items-start gap-2 rounded-lg border border-gray-a5 bg-gray-1 p-3 text-gray shadow-md select-none", richColors && tone),
|
|
@@ -121,7 +137,10 @@ function ToastItem({ closeButton, richColors, toast: toastObject }) {
|
|
|
121
137
|
],
|
|
122
138
|
toast: toastObject,
|
|
123
139
|
children: [
|
|
124
|
-
/* @__PURE__ */ jsx(ToastIcon, {
|
|
140
|
+
/* @__PURE__ */ jsx(ToastIcon, {
|
|
141
|
+
custom: toastObject.data?.icon,
|
|
142
|
+
type: toastObject.type
|
|
143
|
+
}),
|
|
125
144
|
/* @__PURE__ */ jsxs(Toast.Content, {
|
|
126
145
|
className: "flex min-w-0 flex-1 flex-col gap-0.5",
|
|
127
146
|
children: [/* @__PURE__ */ jsx(Toast.Title, {
|
|
@@ -140,12 +159,16 @@ function ToastItem({ closeButton, richColors, toast: toastObject }) {
|
|
|
140
159
|
"data-slot": "toast-action"
|
|
141
160
|
}) : null,
|
|
142
161
|
showClose ? /* @__PURE__ */ jsx(Toast.Close, {
|
|
143
|
-
"aria-label":
|
|
162
|
+
"aria-label": toastObject.data?.closeLabel ?? closeLabel,
|
|
144
163
|
className: cn(buttonVariants({
|
|
145
164
|
size: "icon-small",
|
|
146
165
|
variant: "tertiary"
|
|
147
166
|
}), "-my-1 -mr-1 size-7 shrink-0 self-center text-gray-11"),
|
|
148
167
|
"data-slot": "toast-close",
|
|
168
|
+
onClick: onCloseClick && ((event) => {
|
|
169
|
+
onCloseClick(event);
|
|
170
|
+
if (event.defaultPrevented) event.preventBaseUIHandler();
|
|
171
|
+
}),
|
|
149
172
|
children: /* @__PURE__ */ jsx(Close, {
|
|
150
173
|
"aria-hidden": "true",
|
|
151
174
|
className: "[--spark-icon-size:18px]"
|
|
@@ -154,15 +177,16 @@ function ToastItem({ closeButton, richColors, toast: toastObject }) {
|
|
|
154
177
|
]
|
|
155
178
|
});
|
|
156
179
|
}
|
|
157
|
-
function ToastList({ closeButton, richColors }) {
|
|
180
|
+
function ToastList({ closeButton, closeLabel, richColors }) {
|
|
158
181
|
const { toasts } = Toast.useToastManager();
|
|
159
182
|
return toasts.map((toastObject) => /* @__PURE__ */ jsx(ToastItem, {
|
|
160
183
|
closeButton,
|
|
184
|
+
closeLabel,
|
|
161
185
|
richColors,
|
|
162
186
|
toast: toastObject
|
|
163
187
|
}, toastObject.id));
|
|
164
188
|
}
|
|
165
|
-
function Toaster({ className, closeButton = true, duration = 4e3, limit, mobilePosition = "top", position = "bottom-right", richColors = true }) {
|
|
189
|
+
function Toaster({ className, closeButton = true, closeLabel = "Close", duration = 4e3, limit, mobilePosition = "top", position = "bottom-right", richColors = true }) {
|
|
166
190
|
return /* @__PURE__ */ jsx(Toast.Provider, {
|
|
167
191
|
limit,
|
|
168
192
|
timeout: duration,
|
|
@@ -174,6 +198,7 @@ function Toaster({ className, closeButton = true, duration = 4e3, limit, mobileP
|
|
|
174
198
|
"data-slot": "toaster",
|
|
175
199
|
children: /* @__PURE__ */ jsx(ToastList, {
|
|
176
200
|
closeButton,
|
|
201
|
+
closeLabel,
|
|
177
202
|
richColors
|
|
178
203
|
})
|
|
179
204
|
}) })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toast.js","names":["ToastPrimitive"],"sources":["../../src/components/toast.tsx"],"sourcesContent":["'use client'\n\nimport { Toast as ToastPrimitive } from '@base-ui/react/toast'\nimport {\n Cancel,\n CheckCircle,\n Close,\n Info,\n Warning,\n type SparkIcon,\n} from '@epilot/spark-icons'\nimport * as React from 'react'\nimport { cn } from '../utils/cn'\nimport { buttonVariants } from './button'\nimport { Spinner } from './spinner'\n\ntype ToastType = 'success' | 'info' | 'warning' | 'error' | 'loading'\n\ntype ToastPosition =\n | 'top-left'\n | 'top-center'\n | 'top-right'\n | 'bottom-left'\n | 'bottom-center'\n | 'bottom-right'\n\n// Per-toast overrides Base UI carries through untouched as opaque `data`.\ntype ToastData = { closeButton?: boolean }\n\n// `label` is the button's content; everything else a `<button>` takes passes\n// straight through, so a caller can set `className`, `disabled`, `type`, and\n// the rest without a new prop for each one.\ntype ToastAction = Omit<\n React.ComponentPropsWithoutRef<'button'>,\n 'children'\n> & {\n label: React.ReactNode\n}\n\ntype ToastOptions = {\n description?: React.ReactNode\n /** Auto-dismiss time in ms. `0` keeps the toast until dismissed. */\n duration?: number\n /** Reuse an id to update a toast in place instead of stacking a new one. */\n id?: string\n closeButton?: boolean\n action?: ToastAction\n onDismiss?: () => void\n /** `high` announces urgently (assertive); the default is polite. */\n priority?: 'low' | 'high'\n}\n\ntype ToastPromiseMessages<Value> = {\n loading: string\n success: string | ((value: Value) => string)\n error: string | ((error: unknown) => string)\n}\n\nconst TOAST_ICONS: Record<\n Exclude<ToastType, 'loading'>,\n { icon: SparkIcon; className: string }\n> = {\n success: { icon: CheckCircle, className: 'text-success-light' },\n info: { icon: Info, className: 'text-info-light' },\n warning: { icon: Warning, className: 'text-warning-light' },\n error: { icon: Cancel, className: 'text-error-light' },\n}\n\n// Opaque radix steps (bg 2, border 6, text 11) so a floating toast never lets\n// page content bleed through the way the alpha `-soft` tokens would. The border\n// carries the tone too, otherwise a tinted toast keeps a grey outline.\nconst TONE_CLASSES: Record<Exclude<ToastType, 'loading'>, string> = {\n success: 'border-green-6 bg-green-2 text-green-11',\n info: 'border-blue-6 bg-blue-2 text-blue-11',\n warning: 'border-orange-6 bg-orange-2 text-orange-11',\n error: 'border-red-6 bg-red-2 text-red-11',\n}\n\n// Mobile-first: every position drops to a full-width stack at the top, which\n// is what the end-customer portal ships. `position` only takes effect from the\n// `sm` breakpoint up.\nconst POSITION_CLASSES: Record<ToastPosition, string> = {\n 'top-left': 'sm:top-4 sm:right-auto sm:bottom-auto sm:left-4',\n 'top-center':\n 'sm:top-4 sm:right-auto sm:bottom-auto sm:left-1/2 sm:-translate-x-1/2',\n 'top-right': 'sm:top-4 sm:right-4 sm:bottom-auto sm:left-auto',\n 'bottom-left': 'sm:top-auto sm:right-auto sm:bottom-4 sm:left-4',\n 'bottom-center':\n 'sm:top-auto sm:right-auto sm:bottom-4 sm:left-1/2 sm:-translate-x-1/2',\n 'bottom-right': 'sm:top-auto sm:right-4 sm:bottom-4 sm:left-auto',\n}\n\nconst manager = ToastPrimitive.createToastManager<ToastData>()\n\nlet sequence = 0\n\n// `label` names the content and `onClick` is wrapped, so neither reaches the\n// DOM button as-is; everything else does.\nfunction actionRest({\n label: _label,\n onClick: _onClick,\n ...rest\n}: ToastAction) {\n return rest\n}\n\nfunction toManagerOptions(\n id: string,\n title: React.ReactNode,\n type: ToastType | undefined,\n options: ToastOptions | undefined,\n) {\n const action = options?.action\n return {\n id,\n title,\n type,\n description: options?.description,\n timeout: options?.duration,\n priority: options?.priority,\n onClose: options?.onDismiss,\n // Base UI leaves the toast open after an action; dismissing matches what\n // callers expect from an \"Undo\" button.\n actionProps: action && {\n ...actionRest(action),\n children: action.label,\n onClick: (event: React.MouseEvent<HTMLButtonElement>) => {\n action.onClick?.(event)\n manager.close(id)\n },\n },\n data: { closeButton: options?.closeButton },\n }\n}\n\nconst emit =\n (type?: ToastType) =>\n (message: React.ReactNode, options?: ToastOptions): string => {\n // an id up front lets the action handler close the toast it belongs to\n const id = options?.id ?? `spark-toast-${(sequence += 1)}`\n return manager.add(toManagerOptions(id, message, type, options))\n }\n\n// A string phase becomes a titled toast of that type; an object phase keeps\n// whatever the caller set and only fills in the type.\nconst phase =\n <Value,>(\n value: ToastPromiseMessages<Value>[keyof ToastPromiseMessages<Value>],\n type: ToastType,\n ) =>\n (result: Value) => {\n const resolved = typeof value === 'function' ? value(result) : value\n return { title: resolved, type }\n }\n\nconst toast = Object.assign(emit(), {\n success: emit('success'),\n info: emit('info'),\n warning: emit('warning'),\n error: emit('error'),\n loading: emit('loading'),\n /** Dismiss one toast, or every toast when called without an id. */\n dismiss: (id?: string) => manager.close(id),\n promise: <Value,>(\n promiseValue: Promise<Value>,\n messages: ToastPromiseMessages<Value>,\n ) =>\n manager.promise(promiseValue, {\n loading: { title: messages.loading, type: 'loading' as const },\n success: phase(messages.success, 'success'),\n error: phase(messages.error as never, 'error'),\n }),\n})\n\nfunction ToastIcon({ type }: { type?: string }) {\n const icon = TOAST_ICONS[type as Exclude<ToastType, 'loading'>]\n if (type !== 'loading' && !icon) return null\n\n const Glyph = icon?.icon\n\n // Fixed box matching the title's line height, so the glyph and the spinner\n // both centre on the first line whether or not a description follows.\n return (\n <span\n aria-hidden=\"true\"\n className=\"flex h-5 w-5 shrink-0 items-center justify-center\"\n >\n {type === 'loading' ? (\n // Decorative: the toast already announces the loading message, so the\n // spinner must not add a second status live region.\n <Spinner\n className=\"text-gray-11\"\n label=\"\"\n role=\"presentation\"\n size=\"sm\"\n />\n ) : Glyph ? (\n <Glyph className={cn('[--spark-icon-size:20px]', icon.className)} />\n ) : null}\n </span>\n )\n}\n\nfunction ToastItem({\n closeButton,\n richColors,\n toast: toastObject,\n}: {\n closeButton: boolean\n richColors: boolean\n toast: ToastPrimitive.Root.ToastObject<ToastData>\n}) {\n const tone = TONE_CLASSES[toastObject.type as Exclude<ToastType, 'loading'>]\n const showClose = toastObject.data?.closeButton ?? closeButton\n\n return (\n <ToastPrimitive.Root\n data-slot=\"toast\"\n className={cn(\n // Skin only. All geometry (position, stacking, transitions, the\n // pointer bridge) lives in styles/index.css, because Tailwind's\n // `utilities` layer outranks `components` and a stray `bottom-0` here\n // would beat the stylesheet's `bottom: auto` on top-anchored stacks.\n // The border is what makes the cards behind the front one legible.\n 'flex items-start gap-2 rounded-lg border border-gray-a5 bg-gray-1 p-3 text-gray shadow-md select-none',\n richColors && tone,\n )}\n // the stack is top-anchored on mobile and bottom-anchored above `sm`, so\n // both vertical directions have to dismiss\n swipeDirection={['up', 'down', 'right']}\n toast={toastObject}\n >\n <ToastIcon type={toastObject.type} />\n <ToastPrimitive.Content className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <ToastPrimitive.Title\n className=\"text-sm font-medium\"\n data-slot=\"toast-title\"\n />\n <ToastPrimitive.Description\n className={cn(\n 'text-sm',\n richColors && tone ? 'text-inherit' : 'text-gray-11',\n )}\n data-slot=\"toast-description\"\n />\n </ToastPrimitive.Content>\n {toastObject.actionProps ? (\n <ToastPrimitive.Action\n // -my-2 keeps the 36px control from stretching a single-line row past\n // the 20px text; self-center then lands it on the title when there is\n // no description, and mid-block when there is one\n className={cn(\n buttonVariants({ size: 'small', variant: 'primary' }),\n '-my-2 shrink-0 self-center rounded-full',\n )}\n data-slot=\"toast-action\"\n />\n ) : null}\n {showClose ? (\n <ToastPrimitive.Close\n aria-label=\"Close\"\n className={cn(\n buttonVariants({ size: 'icon-small', variant: 'tertiary' }),\n // -my-1: same reason as the action, for a 28px control\n '-my-1 -mr-1 size-7 shrink-0 self-center text-gray-11',\n )}\n data-slot=\"toast-close\"\n >\n <Close aria-hidden=\"true\" className=\"[--spark-icon-size:18px]\" />\n </ToastPrimitive.Close>\n ) : null}\n </ToastPrimitive.Root>\n )\n}\n\nfunction ToastList({\n closeButton,\n richColors,\n}: {\n closeButton: boolean\n richColors: boolean\n}) {\n const { toasts } = ToastPrimitive.useToastManager<ToastData>()\n\n return toasts.map((toastObject) => (\n <ToastItem\n closeButton={closeButton}\n key={toastObject.id}\n richColors={richColors}\n toast={toastObject}\n />\n ))\n}\n\ntype ToasterProps = {\n className?: string\n /** Corner or edge the toasts stack in. */\n position?: ToastPosition\n /** Close button on every toast; a per-toast `closeButton` wins. */\n closeButton?: boolean\n /** Tinted surfaces for success/info/warning/error toasts. */\n richColors?: boolean\n /** Default auto-dismiss time in ms. `0` disables auto-dismiss. */\n duration?: number\n /** Toasts visible at once before older ones are marked `data-limited`. */\n limit?: number\n /**\n * Vertical edge below the `sm` breakpoint, where the stack is always\n * full-width and `position` does not apply. Defaults to `top`, matching the\n * end-customer portal.\n */\n mobilePosition?: 'top' | 'bottom'\n}\n\nfunction Toaster({\n className,\n closeButton = true,\n duration = 4000,\n limit,\n mobilePosition = 'top',\n position = 'bottom-right',\n richColors = true,\n}: ToasterProps) {\n return (\n <ToastPrimitive.Provider\n limit={limit}\n timeout={duration}\n toastManager={manager}\n >\n <ToastPrimitive.Portal>\n <ToastPrimitive.Viewport\n // block + zero height: the toasts are absolutely positioned against\n // this box's bottom edge, the way Base UI's stacked example works\n className={cn(\n 'fixed inset-x-4 z-50 block h-0 outline-none',\n mobilePosition === 'top' ? 'top-4' : 'bottom-4',\n 'sm:inset-x-auto sm:w-96 sm:max-w-[calc(100vw-2rem)]',\n POSITION_CLASSES[position],\n className,\n )}\n data-mobile-side={mobilePosition}\n data-side={position.startsWith('top') ? 'top' : 'bottom'}\n data-slot=\"toaster\"\n >\n <ToastList closeButton={closeButton} richColors={richColors} />\n </ToastPrimitive.Viewport>\n </ToastPrimitive.Portal>\n </ToastPrimitive.Provider>\n )\n}\n\nexport { Toaster, toast }\nexport type {\n ToastAction,\n ToasterProps,\n ToastOptions,\n ToastPosition,\n ToastPromiseMessages,\n ToastType,\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"toast.js","names":["ToastPrimitive"],"sources":["../../src/components/toast.tsx"],"sourcesContent":["'use client'\n\nimport { Toast as ToastPrimitive } from '@base-ui/react/toast'\nimport type { BaseUIEvent } from '@base-ui/react/types'\nimport {\n Cancel,\n CheckCircle,\n Close,\n Info,\n Warning,\n type SparkIcon,\n} from '@epilot/spark-icons'\nimport * as React from 'react'\nimport { cn } from '../utils/cn'\nimport { buttonVariants } from './button'\nimport { Spinner } from './spinner'\n\ntype ToastType = 'success' | 'info' | 'warning' | 'error' | 'loading'\n\ntype ToastPosition =\n | 'top-left'\n | 'top-center'\n | 'top-right'\n | 'bottom-left'\n | 'bottom-center'\n | 'bottom-right'\n\n// Per-toast overrides Base UI carries through untouched as opaque `data`.\ntype ToastData = {\n closeButton?: boolean\n closeLabel?: string\n icon?: React.ReactNode\n onCloseClick?: (event: React.MouseEvent<HTMLButtonElement>) => void\n}\n\n// `label` is the button's content; everything else a `<button>` takes passes\n// straight through, so a caller can set `className`, `disabled`, `type`, and\n// the rest without a new prop for each one.\ntype ToastAction = Omit<\n React.ComponentPropsWithoutRef<'button'>,\n 'children'\n> & {\n label: React.ReactNode\n}\n\ntype ToastOptions = {\n description?: React.ReactNode\n /** Auto-dismiss time in ms. `0` keeps the toast until dismissed. */\n duration?: number\n /** Reuse an id to update a toast in place instead of stacking a new one. */\n id?: string\n closeButton?: boolean\n /** Accessible name of the close button; overrides the Toaster's `closeLabel`. */\n closeLabel?: string\n /**\n * Replaces the type icon. `null` renders no icon. The icon is decorative,\n * so the title has to carry the meaning on its own.\n */\n icon?: React.ReactNode\n /**\n * Runs when the close button is clicked. Call `event.preventDefault()` to\n * keep the toast open, for example to cancel a running download first.\n */\n onCloseClick?: (event: React.MouseEvent<HTMLButtonElement>) => void\n action?: ToastAction\n onDismiss?: () => void\n /** `high` announces urgently (assertive); the default is polite. */\n priority?: 'low' | 'high'\n}\n\ntype ToastPromiseMessages<Value> = {\n loading: string\n success: string | ((value: Value) => string)\n error: string | ((error: unknown) => string)\n}\n\nconst TOAST_ICONS: Record<\n Exclude<ToastType, 'loading'>,\n { icon: SparkIcon; className: string }\n> = {\n success: { icon: CheckCircle, className: 'text-success-light' },\n info: { icon: Info, className: 'text-info-light' },\n warning: { icon: Warning, className: 'text-warning-light' },\n error: { icon: Cancel, className: 'text-error-light' },\n}\n\n// Opaque radix steps (bg 2, border 6, text 11) so a floating toast never lets\n// page content bleed through the way the alpha `-soft` tokens would. The border\n// carries the tone too, otherwise a tinted toast keeps a grey outline.\nconst TONE_CLASSES: Record<Exclude<ToastType, 'loading'>, string> = {\n success: 'border-green-6 bg-green-2 text-green-11',\n info: 'border-blue-6 bg-blue-2 text-blue-11',\n warning: 'border-orange-6 bg-orange-2 text-orange-11',\n error: 'border-red-6 bg-red-2 text-red-11',\n}\n\n// Mobile-first: every position drops to a full-width stack at the top, which\n// is what the end-customer portal ships. `position` only takes effect from the\n// `sm` breakpoint up.\nconst POSITION_CLASSES: Record<ToastPosition, string> = {\n 'top-left': 'sm:top-4 sm:right-auto sm:bottom-auto sm:left-4',\n 'top-center':\n 'sm:top-4 sm:right-auto sm:bottom-auto sm:left-1/2 sm:-translate-x-1/2',\n 'top-right': 'sm:top-4 sm:right-4 sm:bottom-auto sm:left-auto',\n 'bottom-left': 'sm:top-auto sm:right-auto sm:bottom-4 sm:left-4',\n 'bottom-center':\n 'sm:top-auto sm:right-auto sm:bottom-4 sm:left-1/2 sm:-translate-x-1/2',\n 'bottom-right': 'sm:top-auto sm:right-4 sm:bottom-4 sm:left-auto',\n}\n\nconst manager = ToastPrimitive.createToastManager<ToastData>()\n\nlet sequence = 0\n\n// `label` names the content and `onClick` is wrapped, so neither reaches the\n// DOM button as-is; everything else does.\nfunction actionRest({\n label: _label,\n onClick: _onClick,\n ...rest\n}: ToastAction) {\n return rest\n}\n\nfunction toManagerOptions(\n id: string,\n title: React.ReactNode,\n type: ToastType | undefined,\n options: ToastOptions | undefined,\n) {\n const action = options?.action\n return {\n id,\n title,\n type,\n description: options?.description,\n timeout: options?.duration,\n priority: options?.priority,\n onClose: options?.onDismiss,\n // Base UI leaves the toast open after an action; dismissing matches what\n // callers expect from an \"Undo\" button.\n actionProps: action && {\n ...actionRest(action),\n children: action.label,\n onClick: (event: React.MouseEvent<HTMLButtonElement>) => {\n action.onClick?.(event)\n manager.close(id)\n },\n },\n data: {\n closeButton: options?.closeButton,\n closeLabel: options?.closeLabel,\n icon: options?.icon,\n onCloseClick: options?.onCloseClick,\n },\n }\n}\n\nconst emit =\n (type?: ToastType) =>\n (message: React.ReactNode, options?: ToastOptions): string => {\n // an id up front lets the action handler close the toast it belongs to\n const id = options?.id ?? `spark-toast-${(sequence += 1)}`\n return manager.add(toManagerOptions(id, message, type, options))\n }\n\n// A string phase becomes a titled toast of that type; an object phase keeps\n// whatever the caller set and only fills in the type.\nconst phase =\n <Value,>(\n value: ToastPromiseMessages<Value>[keyof ToastPromiseMessages<Value>],\n type: ToastType,\n ) =>\n (result: Value) => {\n const resolved = typeof value === 'function' ? value(result) : value\n return { title: resolved, type }\n }\n\nconst toast = Object.assign(emit(), {\n success: emit('success'),\n info: emit('info'),\n warning: emit('warning'),\n error: emit('error'),\n loading: emit('loading'),\n /** Dismiss one toast, or every toast when called without an id. */\n dismiss: (id?: string) => manager.close(id),\n promise: <Value,>(\n promiseValue: Promise<Value>,\n messages: ToastPromiseMessages<Value>,\n ) =>\n manager.promise(promiseValue, {\n loading: { title: messages.loading, type: 'loading' as const },\n success: phase(messages.success, 'success'),\n error: phase(messages.error as never, 'error'),\n }),\n})\n\nfunction ToastIcon({\n custom,\n type,\n}: {\n custom: React.ReactNode | undefined\n type?: string\n}) {\n const icon = TOAST_ICONS[type as Exclude<ToastType, 'loading'>]\n if (custom !== undefined) {\n if (custom === null || custom === false) return null\n return (\n <span\n aria-hidden=\"true\"\n className=\"flex h-5 w-5 shrink-0 items-center justify-center [--spark-icon-size:20px]\"\n data-slot=\"toast-icon\"\n >\n {custom}\n </span>\n )\n }\n if (type !== 'loading' && !icon) return null\n\n const Glyph = icon?.icon\n\n // Fixed box matching the title's line height, so the glyph and the spinner\n // both centre on the first line whether or not a description follows.\n return (\n <span\n aria-hidden=\"true\"\n className=\"flex h-5 w-5 shrink-0 items-center justify-center\"\n data-slot=\"toast-icon\"\n >\n {type === 'loading' ? (\n // Decorative: the toast already announces the loading message, so the\n // spinner must not add a second status live region.\n <Spinner\n className=\"text-gray-11\"\n label=\"\"\n role=\"presentation\"\n size=\"sm\"\n />\n ) : Glyph ? (\n <Glyph className={cn('[--spark-icon-size:20px]', icon.className)} />\n ) : null}\n </span>\n )\n}\n\nfunction ToastItem({\n closeButton,\n closeLabel,\n richColors,\n toast: toastObject,\n}: {\n closeButton: boolean\n closeLabel: string\n richColors: boolean\n toast: ToastPrimitive.Root.ToastObject<ToastData>\n}) {\n const tone = TONE_CLASSES[toastObject.type as Exclude<ToastType, 'loading'>]\n const showClose = toastObject.data?.closeButton ?? closeButton\n const onCloseClick = toastObject.data?.onCloseClick\n\n return (\n <ToastPrimitive.Root\n data-slot=\"toast\"\n className={cn(\n // Skin only. All geometry (position, stacking, transitions, the\n // pointer bridge) lives in styles/index.css, because Tailwind's\n // `utilities` layer outranks `components` and a stray `bottom-0` here\n // would beat the stylesheet's `bottom: auto` on top-anchored stacks.\n // The border is what makes the cards behind the front one legible.\n 'flex items-start gap-2 rounded-lg border border-gray-a5 bg-gray-1 p-3 text-gray shadow-md select-none',\n richColors && tone,\n )}\n // the stack is top-anchored on mobile and bottom-anchored above `sm`, so\n // both vertical directions have to dismiss\n swipeDirection={['up', 'down', 'right']}\n toast={toastObject}\n >\n <ToastIcon custom={toastObject.data?.icon} type={toastObject.type} />\n <ToastPrimitive.Content className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <ToastPrimitive.Title\n className=\"text-sm font-medium\"\n data-slot=\"toast-title\"\n />\n <ToastPrimitive.Description\n className={cn(\n 'text-sm',\n richColors && tone ? 'text-inherit' : 'text-gray-11',\n )}\n data-slot=\"toast-description\"\n />\n </ToastPrimitive.Content>\n {toastObject.actionProps ? (\n <ToastPrimitive.Action\n // -my-2 keeps the 36px control from stretching a single-line row past\n // the 20px text; self-center then lands it on the title when there is\n // no description, and mid-block when there is one\n className={cn(\n buttonVariants({ size: 'small', variant: 'primary' }),\n '-my-2 shrink-0 self-center rounded-full',\n )}\n data-slot=\"toast-action\"\n />\n ) : null}\n {showClose ? (\n <ToastPrimitive.Close\n aria-label={toastObject.data?.closeLabel ?? closeLabel}\n className={cn(\n buttonVariants({ size: 'icon-small', variant: 'tertiary' }),\n // -my-1: same reason as the action, for a 28px control\n '-my-1 -mr-1 size-7 shrink-0 self-center text-gray-11',\n )}\n data-slot=\"toast-close\"\n onClick={\n onCloseClick &&\n ((event: BaseUIEvent<React.MouseEvent<HTMLButtonElement>>) => {\n onCloseClick(event)\n // Base UI's own close runs after this handler unless prevented.\n if (event.defaultPrevented) event.preventBaseUIHandler()\n })\n }\n >\n <Close aria-hidden=\"true\" className=\"[--spark-icon-size:18px]\" />\n </ToastPrimitive.Close>\n ) : null}\n </ToastPrimitive.Root>\n )\n}\n\nfunction ToastList({\n closeButton,\n closeLabel,\n richColors,\n}: {\n closeButton: boolean\n closeLabel: string\n richColors: boolean\n}) {\n const { toasts } = ToastPrimitive.useToastManager<ToastData>()\n\n return toasts.map((toastObject) => (\n <ToastItem\n closeButton={closeButton}\n closeLabel={closeLabel}\n key={toastObject.id}\n richColors={richColors}\n toast={toastObject}\n />\n ))\n}\n\ntype ToasterProps = {\n className?: string\n /** Corner or edge the toasts stack in. */\n position?: ToastPosition\n /** Close button on every toast; a per-toast `closeButton` wins. */\n closeButton?: boolean\n /** Accessible name of every close button; a per-toast `closeLabel` wins. */\n closeLabel?: string\n /** Tinted surfaces for success/info/warning/error toasts. */\n richColors?: boolean\n /** Default auto-dismiss time in ms. `0` disables auto-dismiss. */\n duration?: number\n /** Toasts visible at once before older ones are marked `data-limited`. */\n limit?: number\n /**\n * Vertical edge below the `sm` breakpoint, where the stack is always\n * full-width and `position` does not apply. Defaults to `top`, matching the\n * end-customer portal.\n */\n mobilePosition?: 'top' | 'bottom'\n}\n\nfunction Toaster({\n className,\n closeButton = true,\n closeLabel = 'Close',\n duration = 4000,\n limit,\n mobilePosition = 'top',\n position = 'bottom-right',\n richColors = true,\n}: ToasterProps) {\n return (\n <ToastPrimitive.Provider\n limit={limit}\n timeout={duration}\n toastManager={manager}\n >\n <ToastPrimitive.Portal>\n <ToastPrimitive.Viewport\n // block + zero height: the toasts are absolutely positioned against\n // this box's bottom edge, the way Base UI's stacked example works\n className={cn(\n 'fixed inset-x-4 z-50 block h-0 outline-none',\n mobilePosition === 'top' ? 'top-4' : 'bottom-4',\n 'sm:inset-x-auto sm:w-96 sm:max-w-[calc(100vw-2rem)]',\n POSITION_CLASSES[position],\n className,\n )}\n data-mobile-side={mobilePosition}\n data-side={position.startsWith('top') ? 'top' : 'bottom'}\n data-slot=\"toaster\"\n >\n <ToastList\n closeButton={closeButton}\n closeLabel={closeLabel}\n richColors={richColors}\n />\n </ToastPrimitive.Viewport>\n </ToastPrimitive.Portal>\n </ToastPrimitive.Provider>\n )\n}\n\nexport { Toaster, toast }\nexport type {\n ToastAction,\n ToasterProps,\n ToastOptions,\n ToastPosition,\n ToastPromiseMessages,\n ToastType,\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
|
+
const require_chunk = require("../chunk-DY84GLCh.cjs");
|
|
5
|
+
const require_lib_accent_colors = require("../lib/accent-colors.cjs");
|
|
6
|
+
const require_lib_toggle_group_context = require("../lib/toggle-group-context.cjs");
|
|
7
|
+
let react = require("react");
|
|
8
|
+
react = require_chunk.__toESM(react);
|
|
9
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
|
+
let _base_ui_react_toggle_group = require("@base-ui/react/toggle-group");
|
|
11
|
+
let cn = require("cn");
|
|
12
|
+
//#region src/components/toggle-group.tsx
|
|
13
|
+
/**
|
|
14
|
+
* Children are `Toggle`s with a `value`. Arrow keys move focus between them
|
|
15
|
+
* (roving tabindex); pass `multiple` to let more than one stay pressed.
|
|
16
|
+
*/
|
|
17
|
+
const ToggleGroup = react.forwardRef(function ToggleGroup({ className, styleVariant, color, size, showCheck, ...props }, ref) {
|
|
18
|
+
const look = styleVariant !== void 0 || color !== void 0 ? styleVariant ?? "solid" : void 0;
|
|
19
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_lib_toggle_group_context.ToggleGroupContext.Provider, {
|
|
20
|
+
value: {
|
|
21
|
+
styleVariant,
|
|
22
|
+
color,
|
|
23
|
+
size,
|
|
24
|
+
showCheck
|
|
25
|
+
},
|
|
26
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_base_ui_react_toggle_group.ToggleGroup, {
|
|
27
|
+
"data-slot": "toggle-group",
|
|
28
|
+
"data-style": look,
|
|
29
|
+
"data-accent-color": color ? require_lib_accent_colors.accentByColor[color] : void 0,
|
|
30
|
+
className: (0, cn.cn)("isolate inline-flex *:focus-visible:z-10", "data-[orientation=horizontal]:*:not-first:rounded-l-none data-[orientation=horizontal]:*:not-last:rounded-r-none", "data-[orientation=vertical]:flex-col data-[orientation=vertical]:*:not-first:rounded-t-none data-[orientation=vertical]:*:not-last:rounded-b-none", look === "soft" ? "gap-[calc(var(--spark-layout-gap)*0.5)]" : "data-[orientation=horizontal]:*:not-first:-ml-px data-[orientation=vertical]:*:not-first:-mt-px", className),
|
|
31
|
+
ref,
|
|
32
|
+
...props
|
|
33
|
+
})
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
ToggleGroup.displayName = "ToggleGroup";
|
|
37
|
+
//#endregion
|
|
38
|
+
exports.ToggleGroup = ToggleGroup;
|
|
39
|
+
|
|
40
|
+
//# sourceMappingURL=toggle-group.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toggle-group.cjs","names":["React","ToggleGroupContext","ToggleGroupPrimitive","accentByColor"],"sources":["../../src/components/toggle-group.tsx"],"sourcesContent":["'use client'\n\nimport { ToggleGroup as ToggleGroupPrimitive } from '@base-ui/react/toggle-group'\nimport * as React from 'react'\nimport { accentByColor } from '../lib/accent-colors'\nimport { ToggleGroupContext } from '../lib/toggle-group-context'\nimport { cn } from '../utils/cn'\nimport type { SparkButtonColor } from './button'\nimport type { ToggleProps, ToggleStyle } from './toggle'\n\nexport interface ToggleGroupProps extends Omit<\n React.ComponentProps<typeof ToggleGroupPrimitive>,\n 'ref'\n> {\n /**\n * Pressed look for every item -- unlike ButtonGroup, unpressed items keep\n * the neutral outline, or a neutral fill in a soft group. Each of the three\n * is overridden per item by that item's own prop.\n */\n styleVariant?: ToggleStyle\n color?: SparkButtonColor\n size?: ToggleProps['size']\n /** Default `showCheck` for every item. */\n showCheck?: boolean\n}\n\n/**\n * Children are `Toggle`s with a `value`. Arrow keys move focus between them\n * (roving tabindex); pass `multiple` to let more than one stay pressed.\n */\nconst ToggleGroup = React.forwardRef<HTMLDivElement, ToggleGroupProps>(\n function ToggleGroup(\n { className, styleVariant, color, size, showCheck, ...props },\n ref,\n ) {\n const hasLook = styleVariant !== undefined || color !== undefined\n const look = hasLook ? (styleVariant ?? 'solid') : undefined\n\n return (\n <ToggleGroupContext.Provider\n value={{ styleVariant, color, size, showCheck }}\n >\n <ToggleGroupPrimitive\n data-slot=\"toggle-group\"\n data-style={look}\n data-accent-color={color ? accentByColor[color] : undefined}\n className={cn(\n 'isolate inline-flex *:focus-visible:z-10',\n 'data-[orientation=horizontal]:*:not-first:rounded-l-none data-[orientation=horizontal]:*:not-last:rounded-r-none',\n 'data-[orientation=vertical]:flex-col data-[orientation=vertical]:*:not-first:rounded-t-none data-[orientation=vertical]:*:not-last:rounded-b-none',\n // Soft fills are translucent, so glued neighbours' outlines would\n // frame a pressed item; List's soft rows space out the same way.\n look === 'soft'\n ? 'gap-[calc(var(--spark-layout-gap)*0.5)]'\n : 'data-[orientation=horizontal]:*:not-first:-ml-px data-[orientation=vertical]:*:not-first:-mt-px',\n className,\n )}\n ref={ref}\n {...props}\n />\n </ToggleGroupContext.Provider>\n )\n },\n)\n\nToggleGroup.displayName = 'ToggleGroup'\n\nexport { ToggleGroup }\n"],"mappings":";;;;;;;;;;;;;;;;AA8BA,MAAM,cAAcA,MAAM,WACxB,SAAS,YACP,EAAE,WAAW,cAAc,OAAO,MAAM,WAAW,GAAG,SACtD,KACA;CAEA,MAAM,OADU,iBAAiB,KAAA,KAAa,UAAU,KAAA,IAChC,gBAAgB,UAAW,KAAA;CAEnD,OACE,iBAAA,GAAA,kBAAA,KAACC,iCAAAA,mBAAmB,UAApB;EACE,OAAO;GAAE;GAAc;GAAO;GAAM;EAAU;YAE9C,iBAAA,GAAA,kBAAA,KAACC,4BAAAA,aAAD;GACE,aAAU;GACV,cAAY;GACZ,qBAAmB,QAAQC,0BAAAA,cAAc,SAAS,KAAA;GAClD,YAAA,GAAA,GAAA,IACE,4CACA,oHACA,qJAGA,SAAS,SACL,4CACA,mGACJ,SACF;GACK;GACL,GAAI;EACL,CAAA;CAC0B,CAAA;AAEjC,CACF;AAEA,YAAY,cAAc"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { SparkButtonColor } from "./button.cjs";
|
|
4
|
+
import { ToggleProps, ToggleStyle } from "./toggle.cjs";
|
|
5
|
+
import * as React$1 from "react";
|
|
6
|
+
import { ToggleGroup as ToggleGroup$1 } from "@base-ui/react/toggle-group";
|
|
7
|
+
|
|
8
|
+
//#region src/components/toggle-group.d.ts
|
|
9
|
+
interface ToggleGroupProps extends Omit<React$1.ComponentProps<typeof ToggleGroup$1>, 'ref'> {
|
|
10
|
+
/**
|
|
11
|
+
* Pressed look for every item -- unlike ButtonGroup, unpressed items keep
|
|
12
|
+
* the neutral outline, or a neutral fill in a soft group. Each of the three
|
|
13
|
+
* is overridden per item by that item's own prop.
|
|
14
|
+
*/
|
|
15
|
+
styleVariant?: ToggleStyle;
|
|
16
|
+
color?: SparkButtonColor;
|
|
17
|
+
size?: ToggleProps['size'];
|
|
18
|
+
/** Default `showCheck` for every item. */
|
|
19
|
+
showCheck?: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Children are `Toggle`s with a `value`. Arrow keys move focus between them
|
|
23
|
+
* (roving tabindex); pass `multiple` to let more than one stay pressed.
|
|
24
|
+
*/
|
|
25
|
+
declare const ToggleGroup: React$1.ForwardRefExoticComponent<ToggleGroupProps & React$1.RefAttributes<HTMLDivElement>>;
|
|
26
|
+
//#endregion
|
|
27
|
+
export { ToggleGroup, ToggleGroupProps };
|
|
28
|
+
//# sourceMappingURL=toggle-group.d.cts.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { SparkButtonColor } from "./button.js";
|
|
4
|
+
import { ToggleProps, ToggleStyle } from "./toggle.js";
|
|
5
|
+
import * as React$1 from "react";
|
|
6
|
+
import { ToggleGroup as ToggleGroup$1 } from "@base-ui/react/toggle-group";
|
|
7
|
+
|
|
8
|
+
//#region src/components/toggle-group.d.ts
|
|
9
|
+
interface ToggleGroupProps extends Omit<React$1.ComponentProps<typeof ToggleGroup$1>, 'ref'> {
|
|
10
|
+
/**
|
|
11
|
+
* Pressed look for every item -- unlike ButtonGroup, unpressed items keep
|
|
12
|
+
* the neutral outline, or a neutral fill in a soft group. Each of the three
|
|
13
|
+
* is overridden per item by that item's own prop.
|
|
14
|
+
*/
|
|
15
|
+
styleVariant?: ToggleStyle;
|
|
16
|
+
color?: SparkButtonColor;
|
|
17
|
+
size?: ToggleProps['size'];
|
|
18
|
+
/** Default `showCheck` for every item. */
|
|
19
|
+
showCheck?: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Children are `Toggle`s with a `value`. Arrow keys move focus between them
|
|
23
|
+
* (roving tabindex); pass `multiple` to let more than one stay pressed.
|
|
24
|
+
*/
|
|
25
|
+
declare const ToggleGroup: React$1.ForwardRefExoticComponent<ToggleGroupProps & React$1.RefAttributes<HTMLDivElement>>;
|
|
26
|
+
//#endregion
|
|
27
|
+
export { ToggleGroup, ToggleGroupProps };
|
|
28
|
+
//# sourceMappingURL=toggle-group.d.ts.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
import { accentByColor } from "../lib/accent-colors.js";
|
|
4
|
+
import { cn } from "../utils/cn.js";
|
|
5
|
+
import { ToggleGroupContext } from "../lib/toggle-group-context.js";
|
|
6
|
+
import * as React$1 from "react";
|
|
7
|
+
import { jsx } from "react/jsx-runtime";
|
|
8
|
+
import { ToggleGroup as ToggleGroup$1 } from "@base-ui/react/toggle-group";
|
|
9
|
+
//#region src/components/toggle-group.tsx
|
|
10
|
+
/**
|
|
11
|
+
* Children are `Toggle`s with a `value`. Arrow keys move focus between them
|
|
12
|
+
* (roving tabindex); pass `multiple` to let more than one stay pressed.
|
|
13
|
+
*/
|
|
14
|
+
const ToggleGroup = React$1.forwardRef(function ToggleGroup({ className, styleVariant, color, size, showCheck, ...props }, ref) {
|
|
15
|
+
const look = styleVariant !== void 0 || color !== void 0 ? styleVariant ?? "solid" : void 0;
|
|
16
|
+
return /* @__PURE__ */ jsx(ToggleGroupContext.Provider, {
|
|
17
|
+
value: {
|
|
18
|
+
styleVariant,
|
|
19
|
+
color,
|
|
20
|
+
size,
|
|
21
|
+
showCheck
|
|
22
|
+
},
|
|
23
|
+
children: /* @__PURE__ */ jsx(ToggleGroup$1, {
|
|
24
|
+
"data-slot": "toggle-group",
|
|
25
|
+
"data-style": look,
|
|
26
|
+
"data-accent-color": color ? accentByColor[color] : void 0,
|
|
27
|
+
className: cn("isolate inline-flex *:focus-visible:z-10", "data-[orientation=horizontal]:*:not-first:rounded-l-none data-[orientation=horizontal]:*:not-last:rounded-r-none", "data-[orientation=vertical]:flex-col data-[orientation=vertical]:*:not-first:rounded-t-none data-[orientation=vertical]:*:not-last:rounded-b-none", look === "soft" ? "gap-[calc(var(--spark-layout-gap)*0.5)]" : "data-[orientation=horizontal]:*:not-first:-ml-px data-[orientation=vertical]:*:not-first:-mt-px", className),
|
|
28
|
+
ref,
|
|
29
|
+
...props
|
|
30
|
+
})
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
ToggleGroup.displayName = "ToggleGroup";
|
|
34
|
+
//#endregion
|
|
35
|
+
export { ToggleGroup };
|
|
36
|
+
|
|
37
|
+
//# sourceMappingURL=toggle-group.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toggle-group.js","names":["React","ToggleGroupPrimitive"],"sources":["../../src/components/toggle-group.tsx"],"sourcesContent":["'use client'\n\nimport { ToggleGroup as ToggleGroupPrimitive } from '@base-ui/react/toggle-group'\nimport * as React from 'react'\nimport { accentByColor } from '../lib/accent-colors'\nimport { ToggleGroupContext } from '../lib/toggle-group-context'\nimport { cn } from '../utils/cn'\nimport type { SparkButtonColor } from './button'\nimport type { ToggleProps, ToggleStyle } from './toggle'\n\nexport interface ToggleGroupProps extends Omit<\n React.ComponentProps<typeof ToggleGroupPrimitive>,\n 'ref'\n> {\n /**\n * Pressed look for every item -- unlike ButtonGroup, unpressed items keep\n * the neutral outline, or a neutral fill in a soft group. Each of the three\n * is overridden per item by that item's own prop.\n */\n styleVariant?: ToggleStyle\n color?: SparkButtonColor\n size?: ToggleProps['size']\n /** Default `showCheck` for every item. */\n showCheck?: boolean\n}\n\n/**\n * Children are `Toggle`s with a `value`. Arrow keys move focus between them\n * (roving tabindex); pass `multiple` to let more than one stay pressed.\n */\nconst ToggleGroup = React.forwardRef<HTMLDivElement, ToggleGroupProps>(\n function ToggleGroup(\n { className, styleVariant, color, size, showCheck, ...props },\n ref,\n ) {\n const hasLook = styleVariant !== undefined || color !== undefined\n const look = hasLook ? (styleVariant ?? 'solid') : undefined\n\n return (\n <ToggleGroupContext.Provider\n value={{ styleVariant, color, size, showCheck }}\n >\n <ToggleGroupPrimitive\n data-slot=\"toggle-group\"\n data-style={look}\n data-accent-color={color ? accentByColor[color] : undefined}\n className={cn(\n 'isolate inline-flex *:focus-visible:z-10',\n 'data-[orientation=horizontal]:*:not-first:rounded-l-none data-[orientation=horizontal]:*:not-last:rounded-r-none',\n 'data-[orientation=vertical]:flex-col data-[orientation=vertical]:*:not-first:rounded-t-none data-[orientation=vertical]:*:not-last:rounded-b-none',\n // Soft fills are translucent, so glued neighbours' outlines would\n // frame a pressed item; List's soft rows space out the same way.\n look === 'soft'\n ? 'gap-[calc(var(--spark-layout-gap)*0.5)]'\n : 'data-[orientation=horizontal]:*:not-first:-ml-px data-[orientation=vertical]:*:not-first:-mt-px',\n className,\n )}\n ref={ref}\n {...props}\n />\n </ToggleGroupContext.Provider>\n )\n },\n)\n\nToggleGroup.displayName = 'ToggleGroup'\n\nexport { ToggleGroup }\n"],"mappings":";;;;;;;;;;;;;AA8BA,MAAM,cAAcA,QAAM,WACxB,SAAS,YACP,EAAE,WAAW,cAAc,OAAO,MAAM,WAAW,GAAG,SACtD,KACA;CAEA,MAAM,OADU,iBAAiB,KAAA,KAAa,UAAU,KAAA,IAChC,gBAAgB,UAAW,KAAA;CAEnD,OACE,oBAAC,mBAAmB,UAApB;EACE,OAAO;GAAE;GAAc;GAAO;GAAM;EAAU;YAE9C,oBAACC,eAAD;GACE,aAAU;GACV,cAAY;GACZ,qBAAmB,QAAQ,cAAc,SAAS,KAAA;GAClD,WAAW,GACT,4CACA,oHACA,qJAGA,SAAS,SACL,4CACA,mGACJ,SACF;GACK;GACL,GAAI;EACL,CAAA;CAC0B,CAAA;AAEjC,CACF;AAEA,YAAY,cAAc"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
|
+
const require_chunk = require("../chunk-DY84GLCh.cjs");
|
|
5
|
+
const require_lib_accent_colors = require("../lib/accent-colors.cjs");
|
|
6
|
+
const require_lib_toggle_group_context = require("../lib/toggle-group-context.cjs");
|
|
7
|
+
const require_components_button = require("./button.cjs");
|
|
8
|
+
let react = require("react");
|
|
9
|
+
react = require_chunk.__toESM(react);
|
|
10
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
11
|
+
let _base_ui_react_toggle = require("@base-ui/react/toggle");
|
|
12
|
+
let cn = require("cn");
|
|
13
|
+
//#region src/components/toggle.tsx
|
|
14
|
+
function DrawnCheck() {
|
|
15
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
16
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
17
|
+
viewBox: "0 0 24 24",
|
|
18
|
+
fill: "none",
|
|
19
|
+
stroke: "currentColor",
|
|
20
|
+
strokeWidth: 2.5,
|
|
21
|
+
strokeLinecap: "round",
|
|
22
|
+
strokeLinejoin: "round",
|
|
23
|
+
"aria-hidden": "true",
|
|
24
|
+
"data-slot": "toggle-check",
|
|
25
|
+
className: "absolute start-0 top-1/2 size-(--spark-icon-size) -translate-y-1/2 [opacity:var(--check-drawn,0)] transition-[stroke-dashoffset] [transition-duration:calc(var(--check-drawn,0)*260ms)] ease-out [stroke-dashoffset:calc(1_-_var(--check-drawn,0))] motion-reduce:transition-none",
|
|
26
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
27
|
+
d: "M5 12.5l4.5 4.5L19 7.5",
|
|
28
|
+
pathLength: 1,
|
|
29
|
+
strokeDasharray: 1
|
|
30
|
+
})
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
const spacerClasses = "min-w-0 shrink-[1000] w-[calc(var(--check-room)/2)] transition-[width] duration-200 ease-out motion-reduce:transition-none";
|
|
34
|
+
const Toggle = react.forwardRef(function Toggle({ className, styleVariant, color, size, showCheck, children, ...props }, ref) {
|
|
35
|
+
const group = react.useContext(require_lib_toggle_group_context.ToggleGroupContext);
|
|
36
|
+
const resolvedSize = size ?? group.size ?? "default";
|
|
37
|
+
const resolvedColor = color ?? group.color;
|
|
38
|
+
const hasOwnLook = styleVariant !== void 0 || color !== void 0;
|
|
39
|
+
const look = styleVariant ?? group.styleVariant ?? "solid";
|
|
40
|
+
const withCheck = (showCheck ?? group.showCheck ?? true) && !resolvedSize.startsWith("icon");
|
|
41
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_base_ui_react_toggle.Toggle, {
|
|
42
|
+
"data-slot": "toggle",
|
|
43
|
+
"data-style": hasOwnLook ? look : void 0,
|
|
44
|
+
"data-accent-color": color ? require_lib_accent_colors.accentByColor[color] : void 0,
|
|
45
|
+
className: (0, cn.cn)(require_components_button.buttonVariants({
|
|
46
|
+
variant: "secondary",
|
|
47
|
+
size: resolvedSize,
|
|
48
|
+
color: resolvedColor
|
|
49
|
+
}), "group/toggle [--check-drawn:0] data-disabled:pointer-events-none data-disabled:opacity-50", "data-pressed:bg-(--spark-style-bg,var(--color-primary)) data-pressed:text-(--spark-style-fg,var(--color-primary-foreground)) data-pressed:shadow-[inset_0_0_0_var(--spark-style-border-width,1px)_var(--spark-style-border,transparent)] data-pressed:[--check-drawn:1] data-pressed:hover:bg-(--spark-style-bg-hover,var(--color-primary-hover))", look === "solid" && "[[data-slot=toggle-group][data-orientation=horizontal]>[data-pressed]+&]:data-pressed:shadow-[inset_1px_0_0_0_color-mix(in_oklab,currentColor_25%,transparent)] [[data-slot=toggle-group][data-orientation=vertical]>[data-pressed]+&]:data-pressed:shadow-[inset_0_1px_0_0_color-mix(in_oklab,currentColor_25%,transparent)]", group.styleVariant === "soft" && "not-data-pressed:bg-gray-a2 not-data-pressed:shadow-none not-data-pressed:hover:bg-gray-a3", withCheck && "gap-0 [--check-room:calc(var(--spark-icon-size)_+_0.5rem)]", className),
|
|
50
|
+
ref,
|
|
51
|
+
...props,
|
|
52
|
+
children: withCheck ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
53
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
54
|
+
className: (0, cn.cn)(spacerClasses, "relative self-stretch group-data-pressed/toggle:w-(--check-room) group-data-pressed/toggle:shrink-0"),
|
|
55
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DrawnCheck, {})
|
|
56
|
+
}),
|
|
57
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
58
|
+
className: "inline-flex min-w-0 items-center gap-2",
|
|
59
|
+
children
|
|
60
|
+
}),
|
|
61
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: (0, cn.cn)(spacerClasses, "group-data-pressed/toggle:w-0") })
|
|
62
|
+
] }) : children
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
Toggle.displayName = "Toggle";
|
|
66
|
+
//#endregion
|
|
67
|
+
exports.Toggle = Toggle;
|
|
68
|
+
|
|
69
|
+
//# sourceMappingURL=toggle.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toggle.cjs","names":["React","ToggleGroupContext","TogglePrimitive","accentByColor","buttonVariants"],"sources":["../../src/components/toggle.tsx"],"sourcesContent":["'use client'\n\nimport { Toggle as TogglePrimitive } from '@base-ui/react/toggle'\nimport type { VariantProps } from 'class-variance-authority'\nimport * as React from 'react'\nimport { accentByColor } from '../lib/accent-colors'\nimport type { SparkStyle } from '../lib/theme-types'\nimport { ToggleGroupContext } from '../lib/toggle-group-context'\nimport { cn } from '../utils/cn'\nimport { buttonVariants, type SparkButtonColor } from './button'\n\ntype ToggleSize = NonNullable<VariantProps<typeof buttonVariants>['size']>\n\n// ghost and outline are left out: pressed, they read weaker than the unpressed outline.\nexport type ToggleStyle = Extract<SparkStyle, 'solid' | 'soft' | 'surface'>\n\nexport interface ToggleProps extends Omit<\n React.ComponentProps<typeof TogglePrimitive>,\n 'ref'\n> {\n /** The pressed look; unpressed is the neutral outline, or a neutral fill in a soft group. */\n styleVariant?: ToggleStyle\n color?: SparkButtonColor\n size?: ToggleSize\n /** Draws a check on press. Icon sizes never show one. Defaults to `true`. */\n showCheck?: boolean\n}\n\n// Ported from volt-ui-experimental `lib/icons.tsx` DrawnCheck. Undrawing is\n// instant: the duration scales with --check-drawn.\nfunction DrawnCheck() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2.5}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n data-slot=\"toggle-check\"\n className=\"absolute start-0 top-1/2 size-(--spark-icon-size) -translate-y-1/2 [opacity:var(--check-drawn,0)] transition-[stroke-dashoffset] [transition-duration:calc(var(--check-drawn,0)*260ms)] ease-out [stroke-dashoffset:calc(1_-_var(--check-drawn,0))] motion-reduce:transition-none\"\n >\n <path d=\"M5 12.5l4.5 4.5L19 7.5\" pathLength={1} strokeDasharray={1} />\n </svg>\n )\n}\n\n// volt SegmentedControl's split spacers: unpressed, the check's room is split\n// either side of the label; pressed, it moves left under the check. The label\n// slides, and the Toggle never resizes or shifts its group siblings.\nconst spacerClasses =\n 'min-w-0 shrink-[1000] w-[calc(var(--check-room)/2)] transition-[width] duration-200 ease-out motion-reduce:transition-none'\n\n// forwardRef keeps refs working for React 18 consumers (ref-as-prop is 19-only)\nconst Toggle = React.forwardRef<HTMLButtonElement, ToggleProps>(function Toggle(\n { className, styleVariant, color, size, showCheck, children, ...props },\n ref,\n) {\n const group = React.useContext(ToggleGroupContext)\n const resolvedSize = size ?? group.size ?? 'default'\n const resolvedColor = color ?? group.color\n const hasOwnLook = styleVariant !== undefined || color !== undefined\n const look = styleVariant ?? group.styleVariant ?? 'solid'\n const withCheck =\n (showCheck ?? group.showCheck ?? true) && !resolvedSize.startsWith('icon')\n\n return (\n <TogglePrimitive\n data-slot=\"toggle\"\n data-style={hasOwnLook ? look : undefined}\n data-accent-color={color ? accentByColor[color] : undefined}\n className={cn(\n buttonVariants({\n variant: 'secondary',\n size: resolvedSize,\n color: resolvedColor,\n }),\n 'group/toggle [--check-drawn:0] data-disabled:pointer-events-none data-disabled:opacity-50',\n 'data-pressed:bg-(--spark-style-bg,var(--color-primary)) data-pressed:text-(--spark-style-fg,var(--color-primary-foreground)) data-pressed:shadow-[inset_0_0_0_var(--spark-style-border-width,1px)_var(--spark-style-border,transparent)] data-pressed:[--check-drawn:1] data-pressed:hover:bg-(--spark-style-bg-hover,var(--color-primary-hover))',\n // Solid has no pressed border, so two glued pressed items would merge;\n // the line matches ButtonGroupSeparator's.\n look === 'solid' &&\n '[[data-slot=toggle-group][data-orientation=horizontal]>[data-pressed]+&]:data-pressed:shadow-[inset_1px_0_0_0_color-mix(in_oklab,currentColor_25%,transparent)] [[data-slot=toggle-group][data-orientation=vertical]>[data-pressed]+&]:data-pressed:shadow-[inset_0_1px_0_0_color-mix(in_oklab,currentColor_25%,transparent)]',\n // In a soft group the outline would frame the spaced items. The fill sits\n // a step under soft's a3 so a pressed neutral item still stands out.\n group.styleVariant === 'soft' &&\n 'not-data-pressed:bg-gray-a2 not-data-pressed:shadow-none not-data-pressed:hover:bg-gray-a3',\n withCheck &&\n 'gap-0 [--check-room:calc(var(--spark-icon-size)_+_0.5rem)]',\n className,\n )}\n ref={ref}\n {...props}\n >\n {withCheck ? (\n <>\n <span\n className={cn(\n spacerClasses,\n 'relative self-stretch group-data-pressed/toggle:w-(--check-room) group-data-pressed/toggle:shrink-0',\n )}\n >\n <DrawnCheck />\n </span>\n <span className=\"inline-flex min-w-0 items-center gap-2\">\n {children}\n </span>\n <span\n className={cn(spacerClasses, 'group-data-pressed/toggle:w-0')}\n />\n </>\n ) : (\n children\n )}\n </TogglePrimitive>\n )\n})\n\nToggle.displayName = 'Toggle'\n\nexport { Toggle }\n"],"mappings":";;;;;;;;;;;;;AA8BA,SAAS,aAAa;CACpB,OACE,iBAAA,GAAA,kBAAA,KAAC,OAAD;EACE,OAAM;EACN,SAAQ;EACR,MAAK;EACL,QAAO;EACP,aAAa;EACb,eAAc;EACd,gBAAe;EACf,eAAY;EACZ,aAAU;EACV,WAAU;YAEV,iBAAA,GAAA,kBAAA,KAAC,QAAD;GAAM,GAAE;GAAyB,YAAY;GAAG,iBAAiB;EAAI,CAAA;CAClE,CAAA;AAET;AAKA,MAAM,gBACJ;AAGF,MAAM,SAASA,MAAM,WAA2C,SAAS,OACvE,EAAE,WAAW,cAAc,OAAO,MAAM,WAAW,UAAU,GAAG,SAChE,KACA;CACA,MAAM,QAAQA,MAAM,WAAWC,iCAAAA,kBAAkB;CACjD,MAAM,eAAe,QAAQ,MAAM,QAAQ;CAC3C,MAAM,gBAAgB,SAAS,MAAM;CACrC,MAAM,aAAa,iBAAiB,KAAA,KAAa,UAAU,KAAA;CAC3D,MAAM,OAAO,gBAAgB,MAAM,gBAAgB;CACnD,MAAM,aACH,aAAa,MAAM,aAAa,SAAS,CAAC,aAAa,WAAW,MAAM;CAE3E,OACE,iBAAA,GAAA,kBAAA,KAACC,sBAAAA,QAAD;EACE,aAAU;EACV,cAAY,aAAa,OAAO,KAAA;EAChC,qBAAmB,QAAQC,0BAAAA,cAAc,SAAS,KAAA;EAClD,YAAA,GAAA,GAAA,IACEC,0BAAAA,eAAe;GACb,SAAS;GACT,MAAM;GACN,OAAO;EACT,CAAC,GACD,6FACA,qVAGA,SAAS,WACP,iUAGF,MAAM,iBAAiB,UACrB,8FACF,aACE,8DACF,SACF;EACK;EACL,GAAI;YAEH,YACC,iBAAA,GAAA,kBAAA,MAAA,kBAAA,UAAA,EAAA,UAAA;GACE,iBAAA,GAAA,kBAAA,KAAC,QAAD;IACE,YAAA,GAAA,GAAA,IACE,eACA,qGACF;cAEA,iBAAA,GAAA,kBAAA,KAAC,YAAD,CAAa,CAAA;GACT,CAAA;GACN,iBAAA,GAAA,kBAAA,KAAC,QAAD;IAAM,WAAU;IACb;GACG,CAAA;GACN,iBAAA,GAAA,kBAAA,KAAC,QAAD,EACE,YAAA,GAAA,GAAA,IAAc,eAAe,+BAA+B,EAC7D,CAAA;EACD,EAAA,CAAA,IAEF;CAEa,CAAA;AAErB,CAAC;AAED,OAAO,cAAc"}
|