@spree/dashboard-ui 6.0.3 → 6.0.4
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/package.json +20 -19
- package/src/index.ts +5 -0
- package/src/lib/field-trigger.ts +10 -0
- package/src/lib/same-rich-text.ts +12 -3
- package/src/lib/utils.ts +14 -1
- package/src/spree/address-block.tsx +3 -3
- package/src/spree/address-book-row.tsx +88 -0
- package/src/spree/bulk-price-table.tsx +3 -3
- package/src/spree/claim-resolve-dialog.tsx +1 -1
- package/src/spree/collapsible-card.tsx +2 -2
- package/src/spree/color-picker.tsx +1 -1
- package/src/spree/currency-label.tsx +71 -0
- package/src/spree/data-grid/cells.tsx +2 -2
- package/src/spree/data-grid/data-grid.tsx +2 -2
- package/src/spree/delivery-form-dialog.tsx +1 -1
- package/src/spree/drag-handle.tsx +1 -1
- package/src/spree/editable-note-card.tsx +2 -2
- package/src/spree/form-actions.tsx +32 -10
- package/src/spree/fulfill-items-form.tsx +20 -22
- package/src/spree/fulfillment-edit-dialog.tsx +1 -1
- package/src/spree/fulfillment-item-list.tsx +87 -28
- package/src/spree/fulfillment-panel.tsx +2 -2
- package/src/spree/icons.ts +8 -0
- package/src/spree/json-preview-drawer.tsx +32 -7
- package/src/spree/json-value-view.tsx +14 -4
- package/src/spree/label-upload-dialog.tsx +1 -1
- package/src/spree/language-menu-items.tsx +1 -1
- package/src/spree/metadata/metadata-card.tsx +2 -2
- package/src/spree/order-cancel-dialog.tsx +3 -3
- package/src/spree/post-sale-create-dialogs.tsx +36 -33
- package/src/spree/post-sale-fields.tsx +30 -1
- package/src/spree/product-identity.tsx +74 -0
- package/src/spree/product-membership-list.tsx +79 -41
- package/src/spree/products/status-card.tsx +1 -1
- package/src/spree/quantity-tier-editor.tsx +1 -1
- package/src/spree/return-dialogs.tsx +2 -2
- package/src/spree/search-input.tsx +3 -1
- package/src/spree/secret-input.tsx +1 -1
- package/src/spree/section.tsx +55 -0
- package/src/spree/stat-item.tsx +27 -0
- package/src/spree/tax-identifiers-card.tsx +117 -76
- package/src/spree/theme-toggle.tsx +1 -1
- package/src/spree/wizard.tsx +2 -2
- package/src/styles.css +226 -120
- package/src/ui/alert.tsx +20 -17
- package/src/ui/attachment.tsx +1 -1
- package/src/ui/avatar.tsx +2 -2
- package/src/ui/badge.tsx +1 -1
- package/src/ui/breadcrumb.tsx +1 -1
- package/src/ui/button.tsx +14 -11
- package/src/ui/calendar.tsx +2 -2
- package/src/ui/card.tsx +14 -37
- package/src/ui/chart.tsx +1 -1
- package/src/ui/checkbox.tsx +2 -2
- package/src/ui/code-editor.tsx +6 -2
- package/src/ui/collapsible.tsx +21 -2
- package/src/ui/combobox.tsx +12 -16
- package/src/ui/command.tsx +8 -6
- package/src/ui/context-menu.tsx +1 -1
- package/src/ui/data-table.tsx +58 -15
- package/src/ui/date-picker.tsx +3 -3
- package/src/ui/date-range-panel.tsx +102 -0
- package/src/ui/date-range-picker.tsx +33 -72
- package/src/ui/dialog.tsx +3 -3
- package/src/ui/dropdown-menu.tsx +6 -6
- package/src/ui/empty.tsx +3 -3
- package/src/ui/field.tsx +33 -12
- package/src/ui/input-group.tsx +17 -6
- package/src/ui/input.tsx +148 -4
- package/src/ui/item.tsx +7 -4
- package/src/ui/label.tsx +1 -1
- package/src/ui/media-preview.tsx +40 -5
- package/src/ui/pagination.tsx +16 -4
- package/src/ui/progress.tsx +2 -2
- package/src/ui/radio-group.tsx +2 -2
- package/src/ui/rich-text-editor.tsx +3 -3
- package/src/ui/select.tsx +7 -16
- package/src/ui/sheet.tsx +13 -5
- package/src/ui/sidebar.tsx +5 -5
- package/src/ui/switch.tsx +8 -2
- package/src/ui/tabs.tsx +45 -9
- package/src/ui/textarea.tsx +1 -1
- package/src/ui/toast.tsx +2 -2
- package/src/ui/tooltip.tsx +1 -1
package/src/ui/alert.tsx
CHANGED
|
@@ -4,23 +4,22 @@ import * as React from 'react'
|
|
|
4
4
|
import { cn } from '../lib/utils'
|
|
5
5
|
|
|
6
6
|
const alertVariants = cva(
|
|
7
|
-
"group/alert relative grid w-full gap-0.5 rounded-
|
|
7
|
+
"group/alert relative grid w-full gap-0.5 rounded-card px-3.5 py-3 text-left text-base has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-20 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2.5 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4",
|
|
8
8
|
{
|
|
9
9
|
variants: {
|
|
10
10
|
variant: {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
warning:
|
|
23
|
-
'border-amber-500/20 bg-amber-500/10 text-amber-800 *:data-[slot=alert-description]:text-amber-800/90 dark:text-amber-300 dark:*:data-[slot=alert-description]:text-amber-300/90',
|
|
11
|
+
// Borderless: the fill alone sets an alert apart. A plain note takes a
|
|
12
|
+
// card's fill and lift, so it sits among the cards around it.
|
|
13
|
+
default: 'bg-card text-card-foreground shadow-card-lift',
|
|
14
|
+
// The status variants read off the badges' status ramps — their text
|
|
15
|
+
// step over the ramp's quieter `surface` fill, which suits a block of
|
|
16
|
+
// copy. Solid colours rather than one hue at an opacity, which
|
|
17
|
+
// takes on whatever sits behind it and washes out on a dark card. The
|
|
18
|
+
// description keeps the variant's colour, a little softer than the
|
|
19
|
+
// title.
|
|
20
|
+
destructive: 'bg-danger-surface text-danger *:data-[slot=alert-description]:text-danger/85',
|
|
21
|
+
info: 'bg-info-surface text-info *:data-[slot=alert-description]:text-info/85',
|
|
22
|
+
warning: 'bg-warning-surface text-warning *:data-[slot=alert-description]:text-warning/85',
|
|
24
23
|
},
|
|
25
24
|
},
|
|
26
25
|
defaultVariants: {
|
|
@@ -49,7 +48,7 @@ function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) {
|
|
|
49
48
|
<div
|
|
50
49
|
data-slot="alert-title"
|
|
51
50
|
className={cn(
|
|
52
|
-
'font-semibold group-has-[>svg]/alert:col-start-2 [&_a]:
|
|
51
|
+
'font-semibold group-has-[>svg]/alert:col-start-2 [&_a]:text-link [&_a]:transition-colors [&_a]:hover:text-link-hover',
|
|
53
52
|
className,
|
|
54
53
|
)}
|
|
55
54
|
{...props}
|
|
@@ -62,7 +61,7 @@ function AlertDescription({ className, ...props }: React.ComponentProps<'div'>)
|
|
|
62
61
|
<div
|
|
63
62
|
data-slot="alert-description"
|
|
64
63
|
className={cn(
|
|
65
|
-
'text-
|
|
64
|
+
'text-base text-balance text-muted-foreground md:text-pretty [&_a]:text-link [&_a]:transition-colors [&_a]:hover:text-link-hover [&_p:not(:last-child)]:mb-4',
|
|
66
65
|
className,
|
|
67
66
|
)}
|
|
68
67
|
{...props}
|
|
@@ -72,7 +71,11 @@ function AlertDescription({ className, ...props }: React.ComponentProps<'div'>)
|
|
|
72
71
|
|
|
73
72
|
function AlertAction({ className, ...props }: React.ComponentProps<'div'>) {
|
|
74
73
|
return (
|
|
75
|
-
<div
|
|
74
|
+
<div
|
|
75
|
+
data-slot="alert-action"
|
|
76
|
+
className={cn('absolute top-2.5 right-3', className)}
|
|
77
|
+
{...props}
|
|
78
|
+
/>
|
|
76
79
|
)
|
|
77
80
|
}
|
|
78
81
|
|
package/src/ui/attachment.tsx
CHANGED
|
@@ -96,7 +96,7 @@ function AttachmentTitle({ className, ...props }: React.ComponentProps<'span'>)
|
|
|
96
96
|
return (
|
|
97
97
|
<span
|
|
98
98
|
data-slot="attachment-title"
|
|
99
|
-
className={cn('block max-w-full min-w-0 truncate font-medium text-
|
|
99
|
+
className={cn('block max-w-full min-w-0 truncate font-medium text-base', className)}
|
|
100
100
|
{...props}
|
|
101
101
|
/>
|
|
102
102
|
)
|
package/src/ui/avatar.tsx
CHANGED
|
@@ -41,7 +41,7 @@ function AvatarFallback({
|
|
|
41
41
|
<AvatarPrimitive.Fallback
|
|
42
42
|
data-slot="avatar-fallback"
|
|
43
43
|
className={cn(
|
|
44
|
-
'flex size-full items-center justify-center rounded-full bg-muted text-
|
|
44
|
+
'flex size-full items-center justify-center rounded-full bg-muted text-base text-muted-foreground group-data-[size=sm]/avatar:text-xs uppercase',
|
|
45
45
|
className,
|
|
46
46
|
)}
|
|
47
47
|
{...props}
|
|
@@ -83,7 +83,7 @@ function AvatarGroupCount({ className, ...props }: React.ComponentProps<'div'>)
|
|
|
83
83
|
<div
|
|
84
84
|
data-slot="avatar-group-count"
|
|
85
85
|
className={cn(
|
|
86
|
-
'relative flex size-8 shrink-0 items-center justify-center rounded-lg bg-muted text-
|
|
86
|
+
'relative flex size-8 shrink-0 items-center justify-center rounded-lg bg-muted text-base text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3',
|
|
87
87
|
className,
|
|
88
88
|
)}
|
|
89
89
|
{...props}
|
package/src/ui/badge.tsx
CHANGED
|
@@ -272,7 +272,7 @@ function StatusBadge({
|
|
|
272
272
|
data-slot="status-badge"
|
|
273
273
|
data-status={status}
|
|
274
274
|
className={cn(
|
|
275
|
-
'inline-flex w-fit shrink-0 items-center gap-1.5 whitespace-nowrap text-
|
|
275
|
+
'inline-flex w-fit shrink-0 items-center gap-1.5 whitespace-nowrap text-base capitalize',
|
|
276
276
|
className,
|
|
277
277
|
)}
|
|
278
278
|
>
|
package/src/ui/breadcrumb.tsx
CHANGED
|
@@ -20,7 +20,7 @@ function BreadcrumbList({ className, ...props }: React.ComponentProps<'ol'>) {
|
|
|
20
20
|
<ol
|
|
21
21
|
data-slot="breadcrumb-list"
|
|
22
22
|
className={cn(
|
|
23
|
-
'flex flex-wrap items-center gap-1.5 text-
|
|
23
|
+
'flex flex-wrap items-center gap-1.5 text-base wrap-break-word text-muted-foreground',
|
|
24
24
|
className,
|
|
25
25
|
)}
|
|
26
26
|
{...props}
|
package/src/ui/button.tsx
CHANGED
|
@@ -4,23 +4,26 @@ import { cn } from '../lib/utils'
|
|
|
4
4
|
import { Slot } from './slot'
|
|
5
5
|
|
|
6
6
|
const buttonVariants = cva(
|
|
7
|
-
"group/button inline-flex shrink-0 items-center justify-center gap-2 rounded-
|
|
7
|
+
"group/button inline-flex shrink-0 items-center justify-center gap-2 rounded-xl border border-transparent text-base font-medium whitespace-nowrap cursor-pointer select-none no-underline transition-[color,background-color,border-color,box-shadow,transform,translate,scale] duration-100 ease-out outline-none active:scale-[0.98] motion-reduce:active:scale-100 focus-visible:border-blue-500 focus-visible:shadow-[0_0_0_3px_color-mix(in_srgb,var(--ring)_15%,transparent)] disabled:pointer-events-none disabled:opacity-70 disabled:cursor-not-allowed aria-invalid:border-destructive [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
8
8
|
{
|
|
9
9
|
variants: {
|
|
10
10
|
variant: {
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
|
|
11
|
+
// Buttons with chrome (primary, outline, destructive) take the field's
|
|
12
|
+
// `shadow-control`, so a button and the field beside it share one lift.
|
|
13
|
+
// Disabled drops the fill rather than fading it: a faded black (or, in
|
|
14
|
+
// dark mode, white) pill still reads as the page's live primary action.
|
|
15
|
+
default:
|
|
16
|
+
'bg-primary text-primary-foreground shadow-control hover:bg-primary/85 disabled:border-border disabled:bg-muted disabled:text-muted-foreground disabled:opacity-100 disabled:shadow-none',
|
|
15
17
|
outline:
|
|
16
|
-
'border-border/80 bg-card shadow-
|
|
18
|
+
'border-border/80 bg-card shadow-control text-foreground hover:bg-accent hover:text-foreground aria-expanded:bg-accent',
|
|
17
19
|
// `group-hover/row:` — inside a hovered table row the row already wears
|
|
18
20
|
// --accent, so repeating it here leaves the button at 1.03:1 against its
|
|
19
21
|
// own background: invisible exactly when the pointer is on it. On a
|
|
20
22
|
// hovered row it steps to --accent-strong instead.
|
|
21
23
|
ghost:
|
|
22
24
|
'hover:bg-accent hover:text-accent-foreground aria-expanded:bg-accent group-hover/row:hover:bg-accent-strong',
|
|
23
|
-
destructive:
|
|
25
|
+
destructive:
|
|
26
|
+
'text-destructive bg-card border-border shadow-control hover:bg-destructive/10',
|
|
24
27
|
// Ghost's chrome (none until hover) with destructive's colour. The
|
|
25
28
|
// inline remove buttons that sit inside a row or field want no box of
|
|
26
29
|
// their own, but still need the red tint on hover rather than ghost's
|
|
@@ -30,12 +33,12 @@ const buttonVariants = cva(
|
|
|
30
33
|
},
|
|
31
34
|
size: {
|
|
32
35
|
default: 'h-9 px-3 py-1.5',
|
|
33
|
-
xs: "h-6 gap-1 rounded-
|
|
34
|
-
sm: "h-7 gap-1 rounded-
|
|
36
|
+
xs: "h-6 gap-1 rounded-lg px-2 text-xs [&_svg:not([class*='size-'])]:size-3",
|
|
37
|
+
sm: "h-7 gap-1 rounded-lg px-2 py-1 text-base [&_svg:not([class*='size-'])]:size-3.5",
|
|
35
38
|
lg: 'h-10 px-3 py-2 text-base',
|
|
36
39
|
icon: 'size-9',
|
|
37
|
-
'icon-xs': "size-6 rounded-
|
|
38
|
-
'icon-sm': 'size-7 rounded-
|
|
40
|
+
'icon-xs': "size-6 rounded-lg [&_svg:not([class*='size-'])]:size-3",
|
|
41
|
+
'icon-sm': 'size-7 rounded-lg',
|
|
39
42
|
'icon-lg': 'size-10',
|
|
40
43
|
},
|
|
41
44
|
},
|
package/src/ui/calendar.tsx
CHANGED
|
@@ -22,7 +22,7 @@ function Calendar({
|
|
|
22
22
|
months: 'flex flex-col sm:flex-row gap-2 relative',
|
|
23
23
|
month: 'flex flex-col gap-4',
|
|
24
24
|
month_caption: 'flex justify-center pt-1 relative items-center w-full',
|
|
25
|
-
caption_label: 'text-
|
|
25
|
+
caption_label: 'text-base font-medium',
|
|
26
26
|
nav: 'absolute top-1 inset-x-0 flex items-center justify-between px-1 z-10',
|
|
27
27
|
button_previous:
|
|
28
28
|
'inline-flex size-7 items-center justify-center rounded-md border border-border bg-transparent p-0 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground',
|
|
@@ -32,7 +32,7 @@ function Calendar({
|
|
|
32
32
|
weekdays: 'flex',
|
|
33
33
|
weekday: 'text-muted-foreground rounded-md w-8 font-normal text-xs',
|
|
34
34
|
week: 'flex w-full mt-2',
|
|
35
|
-
day: 'relative p-0 text-center text-
|
|
35
|
+
day: 'relative p-0 text-center text-base focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-blue-50 [&:has([aria-selected].day-outside)]:bg-blue-50/50 [&:has([aria-selected].day-range-end)]:rounded-r-md',
|
|
36
36
|
day_button: cn(
|
|
37
37
|
'inline-flex size-8 items-center justify-center rounded-md p-0 font-normal transition-colors',
|
|
38
38
|
'hover:bg-accent hover:text-foreground',
|
package/src/ui/card.tsx
CHANGED
|
@@ -4,48 +4,29 @@ import { cn } from '../lib/utils'
|
|
|
4
4
|
|
|
5
5
|
function Card({
|
|
6
6
|
className,
|
|
7
|
-
variant
|
|
7
|
+
variant: _variant,
|
|
8
8
|
...props
|
|
9
9
|
}: React.ComponentProps<'div'> & {
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* horizontal padding so its sections divide edge to edge.
|
|
15
|
-
*
|
|
16
|
-
* The box is load-bearing once there is more than one of them: a fulfillment
|
|
17
|
-
* already divides its own rows, so grouping by rules alone would draw the
|
|
18
|
-
* boundary between two fulfillments as the same line at the same inset as
|
|
19
|
-
* the boundary between two items — and the panel's own actions would have no
|
|
20
|
-
* visible owner.
|
|
21
|
-
*
|
|
22
|
-
* `container` is the card those nested records sit in. Pair the two: the
|
|
23
|
-
* container recedes, the records stay raised. A `default` card wrapping
|
|
24
|
-
* `nested` ones puts the content in a well and the frame in front of it,
|
|
25
|
-
* which reads as the content being demoted.
|
|
11
|
+
* @deprecated Every card renders the same; the prop is accepted so plugin
|
|
12
|
+
* code that passes it keeps compiling. Group records under a `Section`
|
|
13
|
+
* heading, one plain card each, instead of nesting cards.
|
|
26
14
|
*/
|
|
27
15
|
variant?: 'default' | 'nested' | 'container'
|
|
28
16
|
}) {
|
|
29
17
|
return (
|
|
30
18
|
<div
|
|
31
19
|
data-slot="card"
|
|
32
|
-
data-variant={variant}
|
|
33
20
|
className={cn(
|
|
34
21
|
// `overflow-x-clip`: a wide child (a table sized to its columns) otherwise
|
|
35
22
|
// propagates its width up through every `overflow: visible` ancestor to
|
|
36
23
|
// the document, laying the whole page out wider than the viewport. `clip`
|
|
37
24
|
// rather than `hidden` so `overflow-y` stays `visible` and sticky
|
|
38
25
|
// descendants keep resolving against the page.
|
|
39
|
-
'group/card flex flex-col min-w-0 overflow-x-clip break-words text-card-foreground
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
// them be the raised, readable thing.
|
|
44
|
-
variant === 'container' && 'rounded-xl border border-border-card bg-card-container',
|
|
45
|
-
// `--border-card`, not `--border`: a card is a region of the sheet
|
|
46
|
-
// rather than a surface floating above it, so its edge is softer than
|
|
47
|
-
// the structural one the sheet, dialogs and dropdowns draw.
|
|
48
|
-
variant === 'default' && 'rounded-xl border border-border-card bg-card',
|
|
26
|
+
'group/card flex flex-col min-w-0 overflow-x-clip break-words text-card-foreground',
|
|
27
|
+
// A faint border and a wide, soft lift (`--card-shadow`; none in
|
|
28
|
+
// dark mode).
|
|
29
|
+
'rounded-card border border-card-edge bg-card shadow-card-lift',
|
|
49
30
|
className,
|
|
50
31
|
)}
|
|
51
32
|
{...props}
|
|
@@ -58,7 +39,7 @@ function CardHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
|
|
58
39
|
<div
|
|
59
40
|
data-slot="card-header"
|
|
60
41
|
className={cn(
|
|
61
|
-
'group/card-header @container/card-header grid auto-rows-min items-
|
|
42
|
+
'group/card-header @container/card-header grid auto-rows-min items-center gap-1 group-data-[size=sm]/card:px-3.5 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] mx-4 pt-3 pb-2 rounded-t-[calc(var(--radius-card)-1px)]',
|
|
62
43
|
className,
|
|
63
44
|
)}
|
|
64
45
|
{...props}
|
|
@@ -82,7 +63,7 @@ function CardDescription({ className, ...props }: React.ComponentProps<'div'>) {
|
|
|
82
63
|
return (
|
|
83
64
|
<div
|
|
84
65
|
data-slot="card-description"
|
|
85
|
-
className={cn('text-
|
|
66
|
+
className={cn('text-base text-muted-foreground text-left', className)}
|
|
86
67
|
{...props}
|
|
87
68
|
/>
|
|
88
69
|
)
|
|
@@ -93,13 +74,7 @@ function CardAction({ className, ...props }: React.ComponentProps<'div'>) {
|
|
|
93
74
|
}
|
|
94
75
|
|
|
95
76
|
function CardContent({ className, ...props }: React.ComponentProps<'div'>) {
|
|
96
|
-
return (
|
|
97
|
-
<div
|
|
98
|
-
data-slot="card-content"
|
|
99
|
-
className={cn('p-3 group-data-[variant=nested]/card:px-0', className)}
|
|
100
|
-
{...props}
|
|
101
|
-
/>
|
|
102
|
-
)
|
|
77
|
+
return <div data-slot="card-content" className={cn('p-4', className)} {...props} />
|
|
103
78
|
}
|
|
104
79
|
|
|
105
80
|
function CardFooter({ className, ...props }: React.ComponentProps<'div'>) {
|
|
@@ -107,7 +82,9 @@ function CardFooter({ className, ...props }: React.ComponentProps<'div'>) {
|
|
|
107
82
|
<div
|
|
108
83
|
data-slot="card-footer"
|
|
109
84
|
className={cn(
|
|
110
|
-
|
|
85
|
+
// Side margin, not padding, so the top rule stops short of the card's
|
|
86
|
+
// edges and reads as a divider inside it.
|
|
87
|
+
'mx-4 flex items-center py-4 bg-transparent border-t border-border-subtle',
|
|
111
88
|
className,
|
|
112
89
|
)}
|
|
113
90
|
{...props}
|
package/src/ui/chart.tsx
CHANGED
|
@@ -69,7 +69,7 @@ export function ChartTooltipContent({
|
|
|
69
69
|
const formattedLabel = labelFormatter ? labelFormatter(label ?? '') : label
|
|
70
70
|
|
|
71
71
|
return (
|
|
72
|
-
<div className={cn('rounded-lg border bg-background px-3 py-2 text-
|
|
72
|
+
<div className={cn('rounded-lg border bg-background px-3 py-2 text-base shadow-md', className)}>
|
|
73
73
|
<div className="mb-1 font-medium">{formattedLabel}</div>
|
|
74
74
|
<div className="flex flex-col gap-0.5">
|
|
75
75
|
{payload.map((item, i) => {
|
package/src/ui/checkbox.tsx
CHANGED
|
@@ -25,7 +25,7 @@ function Checkbox({
|
|
|
25
25
|
disabled={disabled}
|
|
26
26
|
name={name}
|
|
27
27
|
className={cn(
|
|
28
|
-
'peer relative inline-block size-4 shrink-0 cursor-pointer rounded-[4px] border border-border shadow-
|
|
28
|
+
'peer relative inline-block size-4 shrink-0 cursor-pointer rounded-[4px] border border-field-border shadow-control bg-card align-[-3px] outline-none transition-[color,background-color,border-color,box-shadow] duration-100 ease-out',
|
|
29
29
|
// The 16px box is the right *visual* size in a dense table, but on its
|
|
30
30
|
// own it is a 16px target — under the 24px WCAG 2.2 asks for, on the
|
|
31
31
|
// control every bulk action starts from. A pseudo-element extends the
|
|
@@ -34,7 +34,7 @@ function Checkbox({
|
|
|
34
34
|
// bridge, which would otherwise swallow presses in the padded margin
|
|
35
35
|
// and navigate instead of selecting.
|
|
36
36
|
"before:absolute before:inset-[-9px] before:z-1 before:content-['']",
|
|
37
|
-
'focus:border-blue-500 focus:shadow-
|
|
37
|
+
'focus:border-blue-500 focus:shadow-field-focus',
|
|
38
38
|
'disabled:cursor-not-allowed disabled:bg-muted disabled:text-muted-foreground',
|
|
39
39
|
'aria-invalid:border-destructive aria-invalid:ring-destructive/20',
|
|
40
40
|
'data-[checked]:border-blue-500 data-[checked]:bg-blue-500 data-[checked]:text-white',
|
package/src/ui/code-editor.tsx
CHANGED
|
@@ -71,7 +71,11 @@ const theme = EditorView.theme({
|
|
|
71
71
|
fontSize: '13px',
|
|
72
72
|
height: '100%',
|
|
73
73
|
},
|
|
74
|
-
'.cm-scroller': {
|
|
74
|
+
'.cm-scroller': {
|
|
75
|
+
fontFamily: 'var(--font-mono, ui-monospace, monospace)',
|
|
76
|
+
fontFeatureSettings: 'var(--default-mono-font-feature-settings, normal)',
|
|
77
|
+
lineHeight: '1.6',
|
|
78
|
+
},
|
|
75
79
|
'.cm-content': { caretColor: 'var(--foreground)' },
|
|
76
80
|
'.cm-gutters': {
|
|
77
81
|
backgroundColor: 'var(--background)',
|
|
@@ -238,7 +242,7 @@ function CodeEditor({
|
|
|
238
242
|
ref={containerRef}
|
|
239
243
|
data-slot="code-editor"
|
|
240
244
|
className={cn(
|
|
241
|
-
'min-h-64 overflow-hidden rounded-
|
|
245
|
+
'min-h-64 overflow-hidden rounded-xl border border-field-border bg-card shadow-control focus-within:border-blue-500 focus-within:shadow-field-focus',
|
|
242
246
|
className,
|
|
243
247
|
)}
|
|
244
248
|
/>
|
package/src/ui/collapsible.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Collapsible as CollapsiblePrimitive } from '@base-ui/react/collapsible'
|
|
2
2
|
import type * as React from 'react'
|
|
3
|
+
import { cn } from '../lib/utils'
|
|
3
4
|
|
|
4
5
|
function Collapsible({ ...props }: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
|
|
5
6
|
return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />
|
|
@@ -11,8 +12,26 @@ function CollapsibleTrigger({
|
|
|
11
12
|
return <CollapsiblePrimitive.Trigger data-slot="collapsible-trigger" {...props} />
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Opens and closes by sliding its height, which Base UI measures into
|
|
17
|
+
* `--collapsible-panel-height`, and fading so rows aren't sliced by the
|
|
18
|
+
* moving edge. Same timing as the chevrons that toggle it. Instant under
|
|
19
|
+
* reduced motion.
|
|
20
|
+
*/
|
|
21
|
+
function CollapsibleContent({
|
|
22
|
+
className,
|
|
23
|
+
...props
|
|
24
|
+
}: React.ComponentProps<typeof CollapsiblePrimitive.Panel>) {
|
|
25
|
+
return (
|
|
26
|
+
<CollapsiblePrimitive.Panel
|
|
27
|
+
data-slot="collapsible-content"
|
|
28
|
+
className={cn(
|
|
29
|
+
'h-[var(--collapsible-panel-height)] overflow-hidden transition-[height,opacity] duration-200 ease-out data-[ending-style]:h-0 data-[ending-style]:opacity-0 data-[starting-style]:h-0 data-[starting-style]:opacity-0 motion-reduce:transition-none',
|
|
30
|
+
className,
|
|
31
|
+
)}
|
|
32
|
+
{...props}
|
|
33
|
+
/>
|
|
34
|
+
)
|
|
16
35
|
}
|
|
17
36
|
|
|
18
37
|
export { Collapsible, CollapsibleContent, CollapsibleTrigger }
|
package/src/ui/combobox.tsx
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
import { Combobox as ComboboxPrimitive } from '@base-ui/react'
|
|
4
4
|
import i18n from 'i18next'
|
|
5
5
|
import * as React from 'react'
|
|
6
|
+
import { fieldTriggerClassName } from '../lib/field-trigger'
|
|
6
7
|
import { cn } from '../lib/utils'
|
|
7
|
-
import { CheckIcon,
|
|
8
|
+
import { CheckIcon, ChevronsUpDownIcon, XIcon } from '../spree/icons'
|
|
8
9
|
import { Button } from './button'
|
|
9
10
|
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from './input-group'
|
|
10
11
|
|
|
@@ -48,7 +49,7 @@ function ComboboxTrigger({ className, children, ...props }: ComboboxPrimitive.Tr
|
|
|
48
49
|
{...props}
|
|
49
50
|
>
|
|
50
51
|
{children}
|
|
51
|
-
<
|
|
52
|
+
<ChevronsUpDownIcon className="pointer-events-none size-4 text-muted-foreground" />
|
|
52
53
|
</ComboboxPrimitive.Trigger>
|
|
53
54
|
)
|
|
54
55
|
}
|
|
@@ -82,14 +83,11 @@ function ComboboxButtonTrigger({
|
|
|
82
83
|
<ComboboxPrimitive.Trigger
|
|
83
84
|
data-slot="combobox-button-trigger"
|
|
84
85
|
data-size={size}
|
|
85
|
-
className={cn(
|
|
86
|
-
"group/combobox-trigger flex w-full min-h-8 cursor-pointer items-center justify-between gap-1.5 rounded-md border border-border shadow-control bg-card py-1.5 pr-2 pl-2.5 text-base font-normal leading-normal text-foreground transition-[color,background-color,border-color,box-shadow] duration-100 ease-out outline-none select-none focus:border-blue-500 focus:shadow-[0_0_0_3px_color-mix(in_srgb,var(--ring)_15%,transparent)] disabled:cursor-not-allowed disabled:bg-muted disabled:text-muted-foreground aria-invalid:border-destructive data-[size=sm]:shadow-control data-[size=sm]:h-8 data-[size=sm]:min-h-8 data-[size=sm]:gap-2 data-[size=sm]:px-2 data-[size=sm]:py-1 data-[size=sm]:text-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
87
|
-
className,
|
|
88
|
-
)}
|
|
86
|
+
className={cn('group/combobox-trigger', fieldTriggerClassName, className)}
|
|
89
87
|
{...props}
|
|
90
88
|
>
|
|
91
89
|
<span className="flex min-w-0 items-center gap-2 truncate">{children}</span>
|
|
92
|
-
<
|
|
90
|
+
<ChevronsUpDownIcon className="pointer-events-none size-4 shrink-0 text-muted-foreground group-data-[size=sm]/combobox-trigger:size-3.5" />
|
|
93
91
|
</ComboboxPrimitive.Trigger>
|
|
94
92
|
)
|
|
95
93
|
}
|
|
@@ -218,7 +216,7 @@ function ComboboxContent({
|
|
|
218
216
|
// picker: the list re-renders as the user filters, and options
|
|
219
217
|
// arriving mid-transition sit at `opacity-0` long enough to be
|
|
220
218
|
// unclickable. The keyframes make the panel opaque immediately.
|
|
221
|
-
'group/combobox-content relative max-h-(--available-height) w-(--anchor-width) max-w-(--available-width) min-w-[calc(var(--anchor-width)+--spacing(7))] origin-(--transform-origin) overflow-hidden rounded-
|
|
219
|
+
'group/combobox-content relative max-h-(--available-height) w-(--anchor-width) max-w-(--available-width) min-w-[calc(var(--anchor-width)+--spacing(7))] origin-(--transform-origin) overflow-hidden rounded-xl bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[chips=true]:min-w-(--anchor-width) data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 *:data-[slot=input-group]:m-1 *:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:h-8 *:data-[slot=input-group]:border-input/30 *:data-[slot=input-group]:bg-input/30 *:data-[slot=input-group]:shadow-none data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',
|
|
222
220
|
className,
|
|
223
221
|
)}
|
|
224
222
|
{...props}
|
|
@@ -289,7 +287,7 @@ function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) {
|
|
|
289
287
|
<ComboboxPrimitive.Empty
|
|
290
288
|
data-slot="combobox-empty"
|
|
291
289
|
className={cn(
|
|
292
|
-
'hidden w-full justify-center py-2 text-center text-
|
|
290
|
+
'hidden w-full justify-center py-2 text-center text-base text-muted-foreground group-data-empty/combobox-content:flex',
|
|
293
291
|
className,
|
|
294
292
|
)}
|
|
295
293
|
{...props}
|
|
@@ -321,7 +319,7 @@ function ComboboxChips({
|
|
|
321
319
|
// resize lets the positioner follow the anchor smoothly; making it
|
|
322
320
|
// instant jumps the open popup out from under the pointer, so the next
|
|
323
321
|
// option click lands on nothing.
|
|
324
|
-
'flex flex-wrap items-center gap-1.5 rounded-
|
|
322
|
+
'flex flex-wrap items-center gap-1.5 rounded-xl border border-field-border shadow-control bg-card text-foreground px-2.5 py-1.5 text-base transition-all duration-100 ease-out not-focus-within:hover:bg-field-hover focus-within:border-blue-500 focus-within:shadow-field-focus has-aria-invalid:border-destructive has-data-[slot=combobox-chip]:p-1.5',
|
|
325
323
|
className,
|
|
326
324
|
)}
|
|
327
325
|
{...props}
|
|
@@ -345,11 +343,9 @@ function ComboboxChip({
|
|
|
345
343
|
// was shorter than the remove button inside it, and left the label with
|
|
346
344
|
// no vertical breathing room at any screen size.
|
|
347
345
|
//
|
|
348
|
-
//
|
|
349
|
-
//
|
|
350
|
-
|
|
351
|
-
// sits on, which left the chips barely visible.
|
|
352
|
-
'flex min-h-7 w-fit max-w-full items-center justify-center gap-1 rounded-sm bg-accent py-1 pl-2.5 pr-2 text-sm font-base text-foreground has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50 has-data-[slot=combobox-chip-remove]:pr-1',
|
|
346
|
+
// Outlined, so a chip keeps its edge while the field under it takes its
|
|
347
|
+
// hover fill; a fill alone matched that hover and the chip vanished.
|
|
348
|
+
'flex min-h-7 w-fit max-w-full items-center justify-center gap-1 rounded-sm bg-accent/50 border py-1 pl-2.5 pr-2 text-base font-base text-foreground has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50 has-data-[slot=combobox-chip-remove]:pr-1',
|
|
353
349
|
className,
|
|
354
350
|
)}
|
|
355
351
|
{...props}
|
|
@@ -358,7 +354,7 @@ function ComboboxChip({
|
|
|
358
354
|
{showRemove && (
|
|
359
355
|
<ComboboxPrimitive.ChipRemove
|
|
360
356
|
render={<Button variant="ghost" size="icon-sm" />}
|
|
361
|
-
className="size-6 shrink-0 opacity-50 hover:opacity-100"
|
|
357
|
+
className="size-6 shrink-0 opacity-50 hover:bg-transparent hover:opacity-100"
|
|
362
358
|
data-slot="combobox-chip-remove"
|
|
363
359
|
// Names the chip it clears ("Remove Wholesale") rather than a bare
|
|
364
360
|
// "Remove": several sit side by side, and a dialog's own submit
|
package/src/ui/command.tsx
CHANGED
|
@@ -91,10 +91,6 @@ function CommandDialog({
|
|
|
91
91
|
}) {
|
|
92
92
|
return (
|
|
93
93
|
<Dialog {...props}>
|
|
94
|
-
<DialogHeader className="sr-only">
|
|
95
|
-
<DialogTitle>{title}</DialogTitle>
|
|
96
|
-
<DialogDescription>{description}</DialogDescription>
|
|
97
|
-
</DialogHeader>
|
|
98
94
|
<DialogContent
|
|
99
95
|
// `-translate-x-1/2` must be restated alongside `translate-y-0`: in
|
|
100
96
|
// Tailwind v4 both compile to the standalone `translate` property, so
|
|
@@ -106,6 +102,12 @@ function CommandDialog({
|
|
|
106
102
|
)}
|
|
107
103
|
showCloseButton={showCloseButton}
|
|
108
104
|
>
|
|
105
|
+
{/* Inside the popup: the dialog is named by a title within it, and a
|
|
106
|
+
header beside it would stay in the page while the dialog is shut. */}
|
|
107
|
+
<DialogHeader className="sr-only">
|
|
108
|
+
<DialogTitle>{title}</DialogTitle>
|
|
109
|
+
<DialogDescription>{description}</DialogDescription>
|
|
110
|
+
</DialogHeader>
|
|
109
111
|
{children}
|
|
110
112
|
</DialogContent>
|
|
111
113
|
</Dialog>
|
|
@@ -199,7 +201,7 @@ function CommandEmpty({ className, ...props }: React.ComponentProps<'div'>) {
|
|
|
199
201
|
<div
|
|
200
202
|
data-slot="command-empty"
|
|
201
203
|
className={cn(
|
|
202
|
-
'py-10 text-center text-
|
|
204
|
+
'py-10 text-center text-base text-muted-foreground group-has-[[data-slot=command-item]]/command-list:hidden',
|
|
203
205
|
className,
|
|
204
206
|
)}
|
|
205
207
|
{...props}
|
|
@@ -278,7 +280,7 @@ function CommandItem({
|
|
|
278
280
|
value={value}
|
|
279
281
|
onClick={() => onSelect?.(text)}
|
|
280
282
|
className={cn(
|
|
281
|
-
"group/command-item relative flex cursor-pointer items-center gap-3 rounded-md px-3 py-2.5 text-
|
|
283
|
+
"group/command-item relative flex cursor-pointer items-center gap-3 rounded-md px-3 py-2.5 text-base outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-highlighted:bg-accent-strong data-highlighted:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-highlighted:*:[svg]:text-foreground",
|
|
282
284
|
className,
|
|
283
285
|
)}
|
|
284
286
|
{...props}
|
package/src/ui/context-menu.tsx
CHANGED
|
@@ -68,7 +68,7 @@ function ContextMenuItem({
|
|
|
68
68
|
data-inset={inset}
|
|
69
69
|
data-variant={variant}
|
|
70
70
|
className={cn(
|
|
71
|
-
"group/context-menu-item relative flex cursor-pointer items-center gap-3 rounded-lg px-2 py-1.5 text-
|
|
71
|
+
"group/context-menu-item relative flex cursor-pointer items-center gap-3 rounded-lg px-2 py-1.5 text-base outline-hidden select-none transition-colors duration-75 focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
72
72
|
className,
|
|
73
73
|
)}
|
|
74
74
|
{...props}
|