@spree/dashboard-ui 0.13.1 → 1.0.0-beta.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/README.md +1 -1
- package/package.json +13 -9
- package/src/hooks/use-prefers-reduced-motion.ts +26 -0
- package/src/hooks/use-scrolled.ts +34 -14
- package/src/index.ts +38 -1
- package/src/lib/date-range-presets.ts +79 -0
- package/src/lib/same-rich-text.ts +145 -0
- package/src/spree/address-block.tsx +5 -4
- package/src/spree/address-book-row.tsx +96 -0
- package/src/spree/back-button.tsx +14 -9
- package/src/spree/bulk-dialog.tsx +2 -2
- package/src/spree/bulk-price-table.tsx +145 -14
- package/src/spree/bulk-variants-table.tsx +434 -0
- package/src/spree/calculator-summary.tsx +5 -1
- package/src/spree/claim-resolve-dialog.tsx +219 -0
- package/src/spree/confirm-dialog.tsx +1 -1
- package/src/spree/copy-to-clipboard-button.tsx +1 -1
- package/src/spree/data-grid/cells.tsx +199 -25
- package/src/spree/data-grid/context.ts +6 -0
- package/src/spree/data-grid/data-grid.tsx +149 -7
- package/src/spree/data-grid/helpers.ts +6 -2
- package/src/spree/data-grid/index.ts +9 -1
- package/src/spree/data-grid/use-data-grid-keyboard.ts +4 -2
- package/src/spree/delivery-form-dialog.tsx +166 -0
- package/src/spree/drag-handle.tsx +2 -2
- package/src/spree/editable-note-card.tsx +103 -0
- package/src/spree/external-link.tsx +56 -0
- package/src/spree/file-type-icon.tsx +68 -0
- package/src/spree/form-actions.tsx +2 -3
- package/src/spree/form-section.tsx +46 -0
- package/src/spree/fulfill-items-form.tsx +282 -0
- package/src/spree/fulfillment-edit-dialog.tsx +211 -0
- package/src/spree/fulfillment-item-list.tsx +61 -0
- package/src/spree/fulfillment-panel.tsx +53 -0
- package/src/spree/icons.ts +167 -0
- package/src/spree/json-preview-drawer.tsx +4 -4
- package/src/spree/label-upload-dialog.tsx +195 -0
- package/src/spree/language-menu-items.tsx +37 -30
- package/src/spree/metadata/metadata-card.tsx +1 -1
- package/src/spree/order-cancel-dialog.tsx +149 -0
- package/src/spree/post-sale-create-dialogs.tsx +253 -0
- package/src/spree/post-sale-fields.tsx +133 -0
- package/src/spree/product-membership-list.tsx +453 -0
- package/src/spree/products/status-card.tsx +79 -0
- package/src/spree/quantity-tier-editor.tsx +162 -0
- package/src/spree/resource-combobox.tsx +12 -1
- package/src/spree/resource-name-cell.tsx +21 -4
- package/src/spree/return-dialogs.tsx +238 -0
- package/src/spree/route-error-boundary.tsx +1 -1
- package/src/spree/row-actions.tsx +2 -2
- package/src/spree/search-input.tsx +76 -0
- package/src/spree/secret-input.tsx +26 -40
- package/src/spree/tax-identifiers-card.tsx +418 -0
- package/src/spree/theme-toggle.tsx +36 -23
- package/src/spree/wizard.tsx +254 -0
- package/src/styles.css +750 -75
- package/src/ui/accordion.tsx +83 -0
- package/src/ui/alert.tsx +79 -0
- package/src/ui/attachment.tsx +1 -1
- package/src/ui/badge.tsx +198 -56
- package/src/ui/breadcrumb.tsx +1 -1
- package/src/ui/button.tsx +22 -10
- package/src/ui/calendar.tsx +1 -1
- package/src/ui/card.tsx +52 -6
- package/src/ui/checkbox.tsx +13 -5
- package/src/ui/combobox.tsx +125 -6
- package/src/ui/command.tsx +149 -23
- package/src/ui/context-menu.tsx +99 -0
- package/src/ui/data-table.tsx +448 -9
- package/src/ui/date-picker.tsx +18 -3
- package/src/ui/date-range-picker.tsx +47 -60
- package/src/ui/dialog.tsx +6 -6
- package/src/ui/dropdown-menu.tsx +18 -11
- package/src/ui/field.tsx +10 -2
- package/src/ui/input-group.tsx +1 -1
- package/src/ui/input.tsx +1 -1
- package/src/ui/media-preview.tsx +105 -0
- package/src/ui/pagination.tsx +19 -11
- package/src/ui/popover.tsx +4 -2
- package/src/ui/progress.tsx +5 -2
- package/src/ui/radio-group.tsx +4 -4
- package/src/ui/rich-text-editor.tsx +214 -88
- package/src/ui/scroll-area.tsx +65 -0
- package/src/ui/select.tsx +17 -10
- package/src/ui/separator.tsx +1 -1
- package/src/ui/sheet.tsx +49 -7
- package/src/ui/sidebar.tsx +138 -37
- package/src/ui/skeleton.tsx +3 -1
- package/src/ui/tabs.tsx +84 -0
- package/src/ui/textarea.tsx +1 -1
- package/src/ui/thumbnail.tsx +80 -0
- package/src/ui/toast.tsx +112 -0
- package/src/ui/tooltip.tsx +3 -1
- package/src/ui/sonner.tsx +0 -47
package/src/ui/toast.tsx
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Toast as ToastPrimitive } from '@base-ui/react/toast'
|
|
4
|
+
import i18n from 'i18next'
|
|
5
|
+
import type * as React from 'react'
|
|
6
|
+
import { cn } from '../lib/utils'
|
|
7
|
+
import {
|
|
8
|
+
CircleCheckIcon,
|
|
9
|
+
InfoIcon,
|
|
10
|
+
Loader2Icon,
|
|
11
|
+
OctagonXIcon,
|
|
12
|
+
TriangleAlertIcon,
|
|
13
|
+
XIcon,
|
|
14
|
+
} from '../spree/icons'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* App-wide toast manager. Created outside React so a toast can be raised from
|
|
18
|
+
* anywhere — a mutation hook, an event subscriber, a plain async function —
|
|
19
|
+
* without threading a hook through the call stack.
|
|
20
|
+
*
|
|
21
|
+
* Raise one with `toastManager.add({ type: 'success', title: '…' })`. Passing
|
|
22
|
+
* an `id` that already exists updates that toast in place and restarts its
|
|
23
|
+
* timer, which is how a long-running job reports progress through a single
|
|
24
|
+
* toast rather than stacking one per step.
|
|
25
|
+
*/
|
|
26
|
+
export const toastManager = ToastPrimitive.createToastManager()
|
|
27
|
+
|
|
28
|
+
const ICONS: Record<string, React.ReactNode> = {
|
|
29
|
+
success: <CircleCheckIcon className="size-4 text-emerald-600 dark:text-emerald-400" />,
|
|
30
|
+
error: <OctagonXIcon className="size-4 text-destructive" />,
|
|
31
|
+
warning: <TriangleAlertIcon className="size-4 text-amber-600 dark:text-amber-400" />,
|
|
32
|
+
info: <InfoIcon className="size-4 text-muted-foreground" />,
|
|
33
|
+
loading: <Loader2Icon className="size-4 animate-spin text-muted-foreground" />,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function ToastList() {
|
|
37
|
+
const { toasts } = ToastPrimitive.useToastManager()
|
|
38
|
+
|
|
39
|
+
return toasts.map((toast) => (
|
|
40
|
+
<ToastPrimitive.Root
|
|
41
|
+
key={toast.id}
|
|
42
|
+
toast={toast}
|
|
43
|
+
// Base UI types a toast as `dialog`, which puts every transient
|
|
44
|
+
// notification in the same bucket as real modals — a `getByRole('dialog')`
|
|
45
|
+
// lookup, ours or a screen reader user's, matches whatever toast happens
|
|
46
|
+
// to be on screen. `status` is what a passive notification should expose.
|
|
47
|
+
role={toast.type === 'error' ? 'alert' : 'status'}
|
|
48
|
+
// `--toast-index` / `--toast-offset-y` drive the stacked-card effect:
|
|
49
|
+
// toasts behind the front one scale down and sit slightly higher.
|
|
50
|
+
className={cn(
|
|
51
|
+
// Normal flow inside a bottom-anchored column, not absolute
|
|
52
|
+
// positioning: a zero-height viewport gives an absolutely-positioned
|
|
53
|
+
// child nothing to sit on, and it renders below the fold.
|
|
54
|
+
'pointer-events-auto w-full',
|
|
55
|
+
'rounded-xl border border-border bg-popover p-4 text-popover-foreground shadow-lg',
|
|
56
|
+
'transition-all duration-200 ease-out',
|
|
57
|
+
'data-[starting-style]:translate-y-2 data-[starting-style]:opacity-0',
|
|
58
|
+
'data-[ending-style]:translate-y-2 data-[ending-style]:opacity-0',
|
|
59
|
+
)}
|
|
60
|
+
>
|
|
61
|
+
<div className="flex items-start gap-3">
|
|
62
|
+
{toast.type && ICONS[toast.type] && (
|
|
63
|
+
<span className="mt-0.5 shrink-0">{ICONS[toast.type]}</span>
|
|
64
|
+
)}
|
|
65
|
+
<div className="flex min-w-0 flex-1 flex-col gap-1">
|
|
66
|
+
<ToastPrimitive.Title className="font-medium text-sm leading-snug" />
|
|
67
|
+
<ToastPrimitive.Description className="text-muted-foreground text-sm leading-snug" />
|
|
68
|
+
</div>
|
|
69
|
+
<ToastPrimitive.Close
|
|
70
|
+
// 44px tap target on touch, compact for a pointer — the same rule the
|
|
71
|
+
// rest of the app follows for icon-only controls.
|
|
72
|
+
className="-mt-1 -mr-1 inline-flex size-7 shrink-0 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
|
|
73
|
+
aria-label={i18n.t('admin.actions.close')}
|
|
74
|
+
>
|
|
75
|
+
<XIcon className="size-4" />
|
|
76
|
+
</ToastPrimitive.Close>
|
|
77
|
+
</div>
|
|
78
|
+
</ToastPrimitive.Root>
|
|
79
|
+
))
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Mount once at the app root, inside the provider that owns the queue.
|
|
84
|
+
*
|
|
85
|
+
* Replaces the Sonner toaster: shadcn dropped that integration in favour of
|
|
86
|
+
* this Base UI primitive, which is also what every other overlay in this app
|
|
87
|
+
* (Sheet, Dialog, Popover, Select) is already built on.
|
|
88
|
+
*/
|
|
89
|
+
export function Toaster({ children }: { children?: React.ReactNode }) {
|
|
90
|
+
return (
|
|
91
|
+
<ToastPrimitive.Provider toastManager={toastManager}>
|
|
92
|
+
{children}
|
|
93
|
+
<ToastPrimitive.Portal>
|
|
94
|
+
<ToastPrimitive.Viewport
|
|
95
|
+
className={cn(
|
|
96
|
+
// Below the overlay layer (`z-50`) rather than above everything:
|
|
97
|
+
// the stack sits bottom-right, which is exactly where a side
|
|
98
|
+
// sheet's footer buttons are, and a toast drifting over Save is
|
|
99
|
+
// both unclickable and a click the merchant did not intend.
|
|
100
|
+
'fixed right-4 z-40 flex w-[min(22rem,calc(100vw-2rem))] flex-col gap-2',
|
|
101
|
+
// `pointer-events-none` on the column so the empty space above the
|
|
102
|
+
// stack never blocks the page; each toast takes them back.
|
|
103
|
+
'pointer-events-none',
|
|
104
|
+
'bottom-[max(1rem,env(safe-area-inset-bottom))]',
|
|
105
|
+
)}
|
|
106
|
+
>
|
|
107
|
+
<ToastList />
|
|
108
|
+
</ToastPrimitive.Viewport>
|
|
109
|
+
</ToastPrimitive.Portal>
|
|
110
|
+
</ToastPrimitive.Provider>
|
|
111
|
+
)
|
|
112
|
+
}
|
package/src/ui/tooltip.tsx
CHANGED
|
@@ -85,7 +85,9 @@ function TooltipContent({
|
|
|
85
85
|
<TooltipPrimitive.Popup
|
|
86
86
|
data-slot="tooltip-content"
|
|
87
87
|
className={cn(
|
|
88
|
-
|
|
88
|
+
// `origin-(--transform-origin)` so it grows out of the element it
|
|
89
|
+
// describes rather than its own centre.
|
|
90
|
+
'z-50 inline-flex w-fit max-w-[200px] items-center gap-1.5 rounded-lg border border-border bg-popover px-2 py-1 text-sm font-normal text-popover-foreground shadow-md origin-(--transform-origin) data-[starting-style]:opacity-0 data-[starting-style]:scale-95 data-[ending-style]:opacity-0 data-[ending-style]:scale-95 transition-[opacity,transform,translate,scale] duration-100 ease-out',
|
|
89
91
|
className,
|
|
90
92
|
)}
|
|
91
93
|
{...props}
|
package/src/ui/sonner.tsx
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CircleCheckIcon,
|
|
3
|
-
InfoIcon,
|
|
4
|
-
Loader2Icon,
|
|
5
|
-
OctagonXIcon,
|
|
6
|
-
TriangleAlertIcon,
|
|
7
|
-
} from 'lucide-react'
|
|
8
|
-
import type { CSSProperties } from 'react'
|
|
9
|
-
import { Toaster as Sonner, type ToasterProps } from 'sonner'
|
|
10
|
-
import { useTheme } from '../spree/theme-provider'
|
|
11
|
-
|
|
12
|
-
const Toaster = ({ ...props }: ToasterProps) => {
|
|
13
|
-
// The shadcn-default Sonner template assumes `next-themes`. This app has its
|
|
14
|
-
// own ThemeProvider that exposes a `mode` (the user's selection: light/dark/system)
|
|
15
|
-
// — Sonner's `theme` prop accepts the same three values, so we forward it.
|
|
16
|
-
const { mode } = useTheme()
|
|
17
|
-
|
|
18
|
-
return (
|
|
19
|
-
<Sonner
|
|
20
|
-
theme={mode as ToasterProps['theme']}
|
|
21
|
-
className="toaster group"
|
|
22
|
-
icons={{
|
|
23
|
-
success: <CircleCheckIcon className="size-4" />,
|
|
24
|
-
info: <InfoIcon className="size-4" />,
|
|
25
|
-
warning: <TriangleAlertIcon className="size-4" />,
|
|
26
|
-
error: <OctagonXIcon className="size-4" />,
|
|
27
|
-
loading: <Loader2Icon className="size-4 animate-spin" />,
|
|
28
|
-
}}
|
|
29
|
-
style={
|
|
30
|
-
{
|
|
31
|
-
'--normal-bg': 'var(--popover)',
|
|
32
|
-
'--normal-text': 'var(--popover-foreground)',
|
|
33
|
-
'--normal-border': 'var(--border)',
|
|
34
|
-
'--border-radius': 'var(--radius)',
|
|
35
|
-
} as CSSProperties
|
|
36
|
-
}
|
|
37
|
-
toastOptions={{
|
|
38
|
-
classNames: {
|
|
39
|
-
toast: 'cn-toast',
|
|
40
|
-
},
|
|
41
|
-
}}
|
|
42
|
-
{...props}
|
|
43
|
-
/>
|
|
44
|
-
)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export { Toaster }
|