@vendure-io/ui 1.2.0 → 2.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/package.json +9 -4
- package/src/components/{ui → atoms}/alert-dialog.tsx +4 -4
- package/src/components/{ui → atoms}/badge.tsx +6 -3
- package/src/components/{ui → atoms}/button-group.tsx +1 -2
- package/src/components/{ui → atoms}/button.tsx +8 -2
- package/src/components/{ui → atoms}/calendar.tsx +1 -1
- package/src/components/{ui → atoms}/card.tsx +2 -3
- package/src/components/{ui → atoms}/carousel.tsx +1 -1
- package/src/components/atoms/checkbox.tsx +29 -0
- package/src/components/{ui → atoms}/combobox.tsx +3 -4
- package/src/components/{ui → atoms}/command.tsx +2 -2
- package/src/components/{ui → atoms}/context-menu.tsx +1 -1
- package/src/components/{ui → atoms}/dialog.tsx +4 -4
- package/src/components/{ui → atoms}/drawer.tsx +3 -3
- package/src/components/{ui → atoms}/dropdown-menu.tsx +3 -2
- package/src/components/{ui → atoms}/empty.tsx +1 -1
- package/src/components/{ui → atoms}/field.tsx +2 -2
- package/src/components/{ui → atoms}/hover-card.tsx +1 -1
- package/src/components/{ui → atoms}/input-group.tsx +3 -4
- package/src/components/{ui → atoms}/input-otp.tsx +0 -1
- package/src/components/{ui → atoms}/input.tsx +0 -1
- package/src/components/{ui → atoms}/item.tsx +2 -2
- package/src/components/{ui → atoms}/menubar.tsx +3 -4
- package/src/components/{ui → atoms}/native-select.tsx +0 -1
- package/src/components/{ui → atoms}/navigation-menu.tsx +7 -4
- package/src/components/{ui → atoms}/pagination.tsx +1 -1
- package/src/components/{ui → atoms}/popover.tsx +2 -2
- package/src/components/{ui → atoms}/select.tsx +22 -4
- package/src/components/{ui → atoms}/sheet.tsx +4 -4
- package/src/components/{ui → atoms}/sidebar.tsx +6 -6
- package/src/components/atoms/skeleton.tsx +16 -0
- package/src/components/{ui → atoms}/slider.tsx +0 -1
- package/src/components/{ui → atoms}/sonner.tsx +1 -1
- package/src/components/{ui → atoms}/switch.tsx +0 -1
- package/src/components/{ui → atoms}/tabs.tsx +6 -4
- package/src/components/{ui → atoms}/textarea.tsx +0 -1
- package/src/components/{ui → atoms}/toggle-group.tsx +3 -3
- package/src/components/{ui → atoms}/toggle.tsx +0 -1
- package/src/components/{ui → atoms}/tooltip.tsx +2 -1
- package/src/components/molecules/anonymized-token.tsx +170 -0
- package/src/components/molecules/chip.tsx +85 -0
- package/src/components/molecules/combobox-free-text.tsx +160 -0
- package/src/components/molecules/confirm-dialog.tsx +108 -0
- package/src/components/molecules/copyable-text.tsx +102 -0
- package/src/components/molecules/data-table/data-table-bulk-actions.tsx +69 -0
- package/src/components/molecules/data-table/data-table-column-header.tsx +61 -0
- package/src/components/molecules/data-table/data-table-filters.tsx +198 -0
- package/src/components/molecules/data-table/data-table-helpers.tsx +73 -0
- package/src/components/molecules/data-table/data-table-types.tsx +241 -0
- package/src/components/molecules/data-table/data-table-view-options.tsx +73 -0
- package/src/components/molecules/data-table/data-table.tsx +500 -0
- package/src/components/molecules/data-table/list-header.tsx +53 -0
- package/src/components/molecules/data-table/table-pagination.tsx +263 -0
- package/src/components/molecules/date-time.tsx +73 -0
- package/src/components/molecules/description-list.tsx +120 -0
- package/src/components/molecules/format-provider.tsx +46 -0
- package/src/components/molecules/id-chip.tsx +79 -0
- package/src/components/molecules/illustrations/access-denied.tsx +55 -0
- package/src/components/molecules/illustrations/empty-collection.tsx +66 -0
- package/src/components/molecules/illustrations/empty-database.tsx +44 -0
- package/src/components/molecules/illustrations/empty-media.tsx +68 -0
- package/src/components/molecules/illustrations/error.tsx +64 -0
- package/src/components/molecules/illustrations/first-run.tsx +55 -0
- package/src/components/molecules/illustrations/illustration-types.tsx +9 -0
- package/src/components/molecules/illustrations/no-activity.tsx +49 -0
- package/src/components/molecules/illustrations/no-deployments.tsx +49 -0
- package/src/components/molecules/illustrations/no-documents.tsx +67 -0
- package/src/components/molecules/illustrations/no-keys.tsx +50 -0
- package/src/components/molecules/illustrations/no-logs.tsx +53 -0
- package/src/components/molecules/illustrations/no-members.tsx +56 -0
- package/src/components/molecules/illustrations/no-notifications.tsx +52 -0
- package/src/components/molecules/illustrations/no-orders.tsx +48 -0
- package/src/components/molecules/illustrations/no-plugins.tsx +72 -0
- package/src/components/molecules/illustrations/no-results.tsx +69 -0
- package/src/components/molecules/illustrations/not-found.tsx +48 -0
- package/src/components/molecules/illustrations/offline.tsx +43 -0
- package/src/components/molecules/illustrations/pending-approval.tsx +56 -0
- package/src/components/molecules/illustrations/upload-dropzone.tsx +59 -0
- package/src/components/molecules/money.tsx +91 -0
- package/src/components/{custom → molecules}/multi-select.tsx +2 -2
- package/src/components/molecules/page-header.tsx +80 -0
- package/src/components/molecules/password-input.tsx +47 -0
- package/src/components/molecules/relative-time.tsx +81 -0
- package/src/components/molecules/stat-card.tsx +134 -0
- package/src/components/molecules/state-views/empty-state.tsx +75 -0
- package/src/components/molecules/state-views/error-state.tsx +91 -0
- package/src/components/molecules/state-views/loading-state.tsx +80 -0
- package/src/components/molecules/status-badge.tsx +100 -0
- package/src/hooks/use-copy.ts +51 -0
- package/src/lib/base-ui.ts +2 -1
- package/src/lib/format-relative.ts +41 -0
- package/src/lib/state-dictionary.ts +137 -0
- package/src/components/ui/checkbox.tsx +0 -30
- package/src/components/ui/skeleton.tsx +0 -13
- /package/src/components/{ui → atoms}/accordion.tsx +0 -0
- /package/src/components/{ui → atoms}/alert.tsx +0 -0
- /package/src/components/{ui → atoms}/aspect-ratio.tsx +0 -0
- /package/src/components/{ui → atoms}/avatar.tsx +0 -0
- /package/src/components/{ui → atoms}/breadcrumb.tsx +0 -0
- /package/src/components/{ui → atoms}/chart.tsx +0 -0
- /package/src/components/{ui → atoms}/collapsible.tsx +0 -0
- /package/src/components/{ui → atoms}/direction.tsx +0 -0
- /package/src/components/{ui → atoms}/kbd.tsx +0 -0
- /package/src/components/{ui → atoms}/label.tsx +0 -0
- /package/src/components/{ui → atoms}/progress.tsx +0 -0
- /package/src/components/{ui → atoms}/radio-group.tsx +0 -0
- /package/src/components/{ui → atoms}/resizable.tsx +0 -0
- /package/src/components/{ui → atoms}/scroll-area.tsx +0 -0
- /package/src/components/{ui → atoms}/separator.tsx +0 -0
- /package/src/components/{ui → atoms}/spinner.tsx +0 -0
- /package/src/components/{ui → atoms}/table.tsx +0 -0
|
@@ -41,7 +41,7 @@ function PopoverContent({
|
|
|
41
41
|
<PopoverPrimitive.Popup
|
|
42
42
|
data-slot="popover-content"
|
|
43
43
|
className={cn(
|
|
44
|
-
"bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-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 ring-foreground/10 data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 z-50 flex w-72 origin-(--transform-origin) flex-col gap-4 rounded-md p-4 text-sm shadow-md ring-1 outline-hidden duration-
|
|
44
|
+
"bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-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 ring-foreground/10 data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 z-50 flex w-72 origin-(--transform-origin) flex-col gap-4 rounded-md p-4 text-sm shadow-md ring-1 outline-hidden motion-reduce:animate-none data-open:duration-(--transition-duration-normal) data-open:ease-(--ease-out) data-closed:duration-(--transition-duration-fast) data-closed:ease-(--ease-in)",
|
|
45
45
|
className
|
|
46
46
|
)}
|
|
47
47
|
{...props}
|
|
@@ -65,7 +65,7 @@ function PopoverTitle({ className, ...props }: PopoverPrimitive.Title.Props) {
|
|
|
65
65
|
return (
|
|
66
66
|
<PopoverPrimitive.Title
|
|
67
67
|
data-slot="popover-title"
|
|
68
|
-
className={cn("
|
|
68
|
+
className={cn("text-style-card-title", className)}
|
|
69
69
|
{...props}
|
|
70
70
|
/>
|
|
71
71
|
)
|
|
@@ -28,7 +28,6 @@ function SelectValue({ className, ...props }: SelectPrimitive.Value.Props) {
|
|
|
28
28
|
)
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
// [vendure] shadow removed for flat look — restore if overwritten by shadcn upgrade
|
|
32
31
|
function SelectTrigger({
|
|
33
32
|
className,
|
|
34
33
|
size = "default",
|
|
@@ -57,7 +56,7 @@ function SelectTrigger({
|
|
|
57
56
|
)
|
|
58
57
|
}
|
|
59
58
|
|
|
60
|
-
//
|
|
59
|
+
// min-w-(--anchor-width) rather than w-(--anchor-width) so the popup can expand beyond the trigger to fit content
|
|
61
60
|
function SelectContent({
|
|
62
61
|
className,
|
|
63
62
|
children,
|
|
@@ -72,8 +71,27 @@ function SelectContent({
|
|
|
72
71
|
SelectPrimitive.Positioner.Props,
|
|
73
72
|
"align" | "alignOffset" | "side" | "sideOffset" | "alignItemWithTrigger"
|
|
74
73
|
>) {
|
|
74
|
+
// Force the popup to remount when the set of items changes. Base UI's Select
|
|
75
|
+
// keeps an internal item registry that doesn't refresh when items change
|
|
76
|
+
// while the controlled value stays the same (e.g. a value="" fire-and-forget
|
|
77
|
+
// pattern used for state transitions), leaving stale items selectable.
|
|
78
|
+
// Re-keying on the children's values remounts the popup so items re-register.
|
|
79
|
+
// Only inspects top-level children — items nested in a SelectGroup or
|
|
80
|
+
// fragment won't contribute to the key.
|
|
81
|
+
const childrenKey = React.useMemo(
|
|
82
|
+
() =>
|
|
83
|
+
React.Children.toArray(children)
|
|
84
|
+
.map((child) =>
|
|
85
|
+
React.isValidElement(child)
|
|
86
|
+
? String((child.props as { value?: unknown }).value ?? child.key)
|
|
87
|
+
: ""
|
|
88
|
+
)
|
|
89
|
+
.join("|"),
|
|
90
|
+
[children]
|
|
91
|
+
)
|
|
92
|
+
|
|
75
93
|
return (
|
|
76
|
-
<SelectPrimitive.Portal>
|
|
94
|
+
<SelectPrimitive.Portal key={childrenKey}>
|
|
77
95
|
<SelectPrimitive.Positioner
|
|
78
96
|
side={side}
|
|
79
97
|
sideOffset={sideOffset}
|
|
@@ -85,7 +103,7 @@ function SelectContent({
|
|
|
85
103
|
<SelectPrimitive.Popup
|
|
86
104
|
data-slot="select-content"
|
|
87
105
|
data-align-trigger={alignItemWithTrigger}
|
|
88
|
-
className={cn("bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-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 ring-foreground/10 data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 relative isolate z-50 max-h-(--available-height) min-w-(--anchor-width) origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-md shadow-md ring-1 duration-
|
|
106
|
+
className={cn("bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-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 ring-foreground/10 data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 relative isolate z-50 max-h-(--available-height) min-w-(--anchor-width) origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-md shadow-md ring-1 motion-reduce:animate-none data-open:duration-(--transition-duration-normal) data-open:ease-(--ease-out) data-closed:duration-(--transition-duration-fast) data-closed:ease-(--ease-in) data-[align-trigger=true]:animate-none", className )}
|
|
89
107
|
{...props}
|
|
90
108
|
>
|
|
91
109
|
<SelectScrollUpButton />
|
|
@@ -4,7 +4,7 @@ import * as React from "react"
|
|
|
4
4
|
import { Dialog as SheetPrimitive } from "@base-ui/react/dialog"
|
|
5
5
|
|
|
6
6
|
import { cn } from "@vendure-io/ui/lib/utils"
|
|
7
|
-
import { Button } from "@vendure-io/ui/components/
|
|
7
|
+
import { Button } from "@vendure-io/ui/components/atoms/button"
|
|
8
8
|
import { XIcon } from "lucide-react"
|
|
9
9
|
|
|
10
10
|
function Sheet({ ...props }: SheetPrimitive.Root.Props) {
|
|
@@ -28,7 +28,7 @@ function SheetOverlay({ className, ...props }: SheetPrimitive.Backdrop.Props) {
|
|
|
28
28
|
<SheetPrimitive.Backdrop
|
|
29
29
|
data-slot="sheet-overlay"
|
|
30
30
|
className={cn(
|
|
31
|
-
"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 fixed inset-0 z-50 bg-black/10 duration-
|
|
31
|
+
"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 fixed inset-0 z-50 bg-black/10 motion-reduce:animate-none data-open:duration-(--transition-duration-normal) data-open:ease-(--ease-out) data-closed:duration-(--transition-duration-fast) data-closed:ease-(--ease-in) data-ending-style:opacity-0 data-starting-style:opacity-0 supports-backdrop-filter:backdrop-blur-xs",
|
|
32
32
|
className
|
|
33
33
|
)}
|
|
34
34
|
{...props}
|
|
@@ -53,7 +53,7 @@ function SheetContent({
|
|
|
53
53
|
data-slot="sheet-content"
|
|
54
54
|
data-side={side}
|
|
55
55
|
className={cn(
|
|
56
|
-
"bg-
|
|
56
|
+
"bg-overlay data-open:animate-in data-closed:animate-out data-[side=right]:data-closed:slide-out-to-right-10 data-[side=right]:data-open:slide-in-from-right-10 data-[side=left]:data-closed:slide-out-to-left-10 data-[side=left]:data-open:slide-in-from-left-10 data-[side=top]:data-closed:slide-out-to-top-10 data-[side=top]:data-open:slide-in-from-top-10 data-closed:fade-out-0 data-open:fade-in-0 data-[side=bottom]:data-closed:slide-out-to-bottom-10 data-[side=bottom]:data-open:slide-in-from-bottom-10 fixed z-50 flex flex-col gap-4 bg-clip-padding text-sm shadow-lg transition motion-reduce:animate-none motion-reduce:transition-none data-open:duration-(--transition-duration-normal) data-open:ease-(--ease-out) data-closed:duration-(--transition-duration-fast) data-closed:ease-(--ease-in) data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=left]:sm:max-w-lg data-[side=right]:sm:max-w-lg",
|
|
57
57
|
className
|
|
58
58
|
)}
|
|
59
59
|
{...props}
|
|
@@ -104,7 +104,7 @@ function SheetTitle({ className, ...props }: SheetPrimitive.Title.Props) {
|
|
|
104
104
|
return (
|
|
105
105
|
<SheetPrimitive.Title
|
|
106
106
|
data-slot="sheet-title"
|
|
107
|
-
className={cn("text-foreground
|
|
107
|
+
className={cn("text-foreground text-style-section-title", className)}
|
|
108
108
|
{...props}
|
|
109
109
|
/>
|
|
110
110
|
)
|
|
@@ -7,22 +7,22 @@ import { cva, type VariantProps } from "class-variance-authority"
|
|
|
7
7
|
|
|
8
8
|
import { useIsMobile } from "@vendure-io/ui/hooks/use-mobile"
|
|
9
9
|
import { cn } from "@vendure-io/ui/lib/utils"
|
|
10
|
-
import { Button } from "@vendure-io/ui/components/
|
|
11
|
-
import { Input } from "@vendure-io/ui/components/
|
|
12
|
-
import { Separator } from "@vendure-io/ui/components/
|
|
10
|
+
import { Button } from "@vendure-io/ui/components/atoms/button"
|
|
11
|
+
import { Input } from "@vendure-io/ui/components/atoms/input"
|
|
12
|
+
import { Separator } from "@vendure-io/ui/components/atoms/separator"
|
|
13
13
|
import {
|
|
14
14
|
Sheet,
|
|
15
15
|
SheetContent,
|
|
16
16
|
SheetDescription,
|
|
17
17
|
SheetHeader,
|
|
18
18
|
SheetTitle,
|
|
19
|
-
} from "@vendure-io/ui/components/
|
|
20
|
-
import { Skeleton } from "@vendure-io/ui/components/
|
|
19
|
+
} from "@vendure-io/ui/components/atoms/sheet"
|
|
20
|
+
import { Skeleton } from "@vendure-io/ui/components/atoms/skeleton"
|
|
21
21
|
import {
|
|
22
22
|
Tooltip,
|
|
23
23
|
TooltipContent,
|
|
24
24
|
TooltipTrigger,
|
|
25
|
-
} from "@vendure-io/ui/components/
|
|
25
|
+
} from "@vendure-io/ui/components/atoms/tooltip"
|
|
26
26
|
import { PanelLeftIcon } from "lucide-react"
|
|
27
27
|
|
|
28
28
|
const SIDEBAR_COOKIE_NAME = "sidebar_state"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { cn } from "@vendure-io/ui/lib/utils"
|
|
2
|
+
|
|
3
|
+
function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
|
|
4
|
+
return (
|
|
5
|
+
<div
|
|
6
|
+
data-slot="skeleton"
|
|
7
|
+
className={cn(
|
|
8
|
+
"animate-shimmer motion-reduce:animate-none motion-reduce:bg-none motion-reduce:bg-muted rounded-md bg-[length:200%_100%] bg-[linear-gradient(90deg,var(--color-muted)_25%,var(--color-accent)_50%,var(--color-muted)_75%)]",
|
|
9
|
+
className
|
|
10
|
+
)}
|
|
11
|
+
{...props}
|
|
12
|
+
/>
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export { Skeleton }
|
|
@@ -44,7 +44,6 @@ function Slider({
|
|
|
44
44
|
className="bg-primary select-none data-horizontal:h-full data-vertical:w-full"
|
|
45
45
|
/>
|
|
46
46
|
</SliderPrimitive.Track>
|
|
47
|
-
{/* [vendure] shadow removed for flat look — restore if overwritten by shadcn upgrade */}
|
|
48
47
|
{Array.from({ length: _values.length }, (_, index) => (
|
|
49
48
|
<SliderPrimitive.Thumb
|
|
50
49
|
data-slot="slider-thumb"
|
|
@@ -40,7 +40,7 @@ const Toaster = ({ ...props }: ToasterProps) => {
|
|
|
40
40
|
toastOptions={{
|
|
41
41
|
classNames: {
|
|
42
42
|
toast: "cn-toast",
|
|
43
|
-
//
|
|
43
|
+
// Override Sonner's hardcoded #3f3f3f description colour
|
|
44
44
|
// with our semantic muted-foreground token for consistent contrast.
|
|
45
45
|
// See: https://github.com/vendurehq/design/issues/5
|
|
46
46
|
description: "!text-muted-foreground",
|
|
@@ -28,7 +28,7 @@ const tabsListVariants = cva(
|
|
|
28
28
|
{
|
|
29
29
|
variants: {
|
|
30
30
|
variant: {
|
|
31
|
-
default: "bg-
|
|
31
|
+
default: "bg-inset",
|
|
32
32
|
line: "gap-1 bg-transparent",
|
|
33
33
|
},
|
|
34
34
|
},
|
|
@@ -53,15 +53,17 @@ function TabsList({
|
|
|
53
53
|
)
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
// [vendure] shadow removed for flat look — restore if overwritten by shadcn upgrade
|
|
57
56
|
function TabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props) {
|
|
58
57
|
return (
|
|
59
58
|
<TabsPrimitive.Tab
|
|
60
59
|
data-slot="tabs-trigger"
|
|
61
60
|
className={cn(
|
|
62
61
|
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring text-foreground/60 hover:text-foreground dark:text-muted-foreground dark:hover:text-foreground relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
63
|
-
"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent
|
|
64
|
-
|
|
62
|
+
"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent",
|
|
63
|
+
// ADR 0001: the active trigger lifts to surface-raised out of the inset
|
|
64
|
+
// track in both themes. Dark keeps a hairline — lightness alone can't
|
|
65
|
+
// separate a 0.21 thumb near the black end of the ramp.
|
|
66
|
+
"data-active:bg-surface-raised dark:data-active:border-input dark:data-active:text-foreground data-active:text-foreground",
|
|
65
67
|
"after:bg-foreground after:absolute after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100",
|
|
66
68
|
className
|
|
67
69
|
)}
|
|
@@ -2,7 +2,6 @@ import * as React from "react"
|
|
|
2
2
|
|
|
3
3
|
import { cn } from "@vendure-io/ui/lib/utils"
|
|
4
4
|
|
|
5
|
-
// [vendure] shadow removed for flat look — restore if overwritten by shadcn upgrade
|
|
6
5
|
function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
|
|
7
6
|
return (
|
|
8
7
|
<textarea
|
|
@@ -6,7 +6,7 @@ import { ToggleGroup as ToggleGroupPrimitive } from "@base-ui/react/toggle-group
|
|
|
6
6
|
import { type VariantProps } from "class-variance-authority"
|
|
7
7
|
|
|
8
8
|
import { cn } from "@vendure-io/ui/lib/utils"
|
|
9
|
-
import { toggleVariants } from "@vendure-io/ui/components/
|
|
9
|
+
import { toggleVariants } from "@vendure-io/ui/components/atoms/toggle"
|
|
10
10
|
|
|
11
11
|
const ToggleGroupContext = React.createContext<
|
|
12
12
|
VariantProps<typeof toggleVariants> & {
|
|
@@ -20,7 +20,6 @@ const ToggleGroupContext = React.createContext<
|
|
|
20
20
|
orientation: "horizontal",
|
|
21
21
|
})
|
|
22
22
|
|
|
23
|
-
// [vendure] shadow removed for flat look — restore if overwritten by shadcn upgrade
|
|
24
23
|
function ToggleGroup({
|
|
25
24
|
className,
|
|
26
25
|
variant,
|
|
@@ -73,7 +72,8 @@ function ToggleGroupItem({
|
|
|
73
72
|
data-size={context.size || size}
|
|
74
73
|
data-spacing={context.spacing}
|
|
75
74
|
className={cn(
|
|
76
|
-
|
|
75
|
+
// Base UI's Toggle emits data-pressed, never Radix's data-[state=on] — the pressed style comes from toggleVariants via its aria-pressed rule
|
|
76
|
+
"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 group-data-[spacing=0]/toggle-group:shadow-none focus:z-10 focus-visible:z-10 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-md group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-md group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-md group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-md group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t",
|
|
77
77
|
toggleVariants({
|
|
78
78
|
variant: context.variant || variant,
|
|
79
79
|
size: context.size || size,
|
|
@@ -5,7 +5,6 @@ import { cva, type VariantProps } from "class-variance-authority"
|
|
|
5
5
|
|
|
6
6
|
import { cn } from "@vendure-io/ui/lib/utils"
|
|
7
7
|
|
|
8
|
-
// [vendure] shadow removed for flat look — restore if overwritten by shadcn upgrade
|
|
9
8
|
const toggleVariants = cva(
|
|
10
9
|
"hover:text-foreground aria-pressed:bg-muted focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive gap-1 rounded-md text-sm font-medium transition-[color,box-shadow] [&_svg:not([class*='size-'])]:size-4 group/toggle hover:bg-muted inline-flex items-center justify-center whitespace-nowrap outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
11
10
|
{
|
|
@@ -47,10 +47,11 @@ function TooltipContent({
|
|
|
47
47
|
sideOffset={sideOffset}
|
|
48
48
|
className="isolate z-50"
|
|
49
49
|
>
|
|
50
|
+
{/* Base UI never emits Radix-style data-state attributes — data-[state=delayed-open]:* selectors would be dead here; data-open:* covers the open animation */}
|
|
50
51
|
<TooltipPrimitive.Popup
|
|
51
52
|
data-slot="tooltip-content"
|
|
52
53
|
className={cn(
|
|
53
|
-
"data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-
|
|
54
|
+
"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 data-[side=bottom]:slide-in-from-top-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-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 bg-foreground text-background z-50 w-fit max-w-xs origin-(--transform-origin) rounded-md px-3 py-1.5 text-xs motion-reduce:animate-none data-open:duration-(--transition-duration-normal) data-open:ease-(--ease-out) data-closed:duration-(--transition-duration-fast) data-closed:ease-(--ease-in)",
|
|
54
55
|
className
|
|
55
56
|
)}
|
|
56
57
|
{...props}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
CopyButton,
|
|
5
|
+
type CopyButtonProps,
|
|
6
|
+
} from '@vendure-io/ui/components/molecules/copyable-text';
|
|
7
|
+
import { chipChromeClass } from '@vendure-io/ui/components/molecules/id-chip';
|
|
8
|
+
import { cn } from '@vendure-io/ui/lib/utils';
|
|
9
|
+
import type * as React from 'react';
|
|
10
|
+
import { useState } from 'react';
|
|
11
|
+
|
|
12
|
+
function clampLength(value: number): number {
|
|
13
|
+
return Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function getTokenPreview(
|
|
17
|
+
value: string,
|
|
18
|
+
prefixLength: number,
|
|
19
|
+
suffixLength: number,
|
|
20
|
+
separator: string,
|
|
21
|
+
): string {
|
|
22
|
+
const prefix = clampLength(prefixLength);
|
|
23
|
+
const suffix = clampLength(suffixLength);
|
|
24
|
+
|
|
25
|
+
if (prefix === 0 && suffix === 0) return separator;
|
|
26
|
+
|
|
27
|
+
if (value.length <= prefix + suffix) {
|
|
28
|
+
const visibleLength = Math.max(1, Math.min(prefix || suffix, Math.floor(value.length / 2)));
|
|
29
|
+
return `${value.slice(0, visibleLength)}${separator}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return `${value.slice(0, prefix)}${separator}${suffix > 0 ? value.slice(-suffix) : ''}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface AnonymizedTokenProps extends Omit<React.ComponentProps<'span'>, 'children'> {
|
|
36
|
+
/** Full token copied by the button. Never rendered in full by the component. */
|
|
37
|
+
value: string | null | undefined;
|
|
38
|
+
/** Remove the blur from the preview on hover/focus. @default true */
|
|
39
|
+
revealOnHover?: boolean;
|
|
40
|
+
/** Characters shown at the start of the preview. @default 8 */
|
|
41
|
+
previewPrefixLength?: number;
|
|
42
|
+
/** Characters shown at the end of the preview. @default 4 */
|
|
43
|
+
previewSuffixLength?: number;
|
|
44
|
+
/** Text placed between visible preview segments. @default "..." */
|
|
45
|
+
previewSeparator?: string;
|
|
46
|
+
/** Accessible name while the preview is blurred. @default "Partially obscured token" */
|
|
47
|
+
obscuredLabel?: string;
|
|
48
|
+
/** Accessible name while the preview is sharp. @default "Partially visible token" */
|
|
49
|
+
previewLabel?: string;
|
|
50
|
+
/** Rendered for nullish or empty values. @default "-" */
|
|
51
|
+
fallback?: React.ReactNode;
|
|
52
|
+
/** Render the copy affordance. @default true */
|
|
53
|
+
copyable?: boolean;
|
|
54
|
+
/** How long the check-mark feedback stays visible, in ms. */
|
|
55
|
+
timeout?: CopyButtonProps['timeout'];
|
|
56
|
+
/** Called after a successful copy. Wire your toast here. */
|
|
57
|
+
onCopied?: CopyButtonProps['onCopied'];
|
|
58
|
+
/** Called when the clipboard write fails. */
|
|
59
|
+
onCopyError?: CopyButtonProps['onCopyError'];
|
|
60
|
+
/** Accessible label before copying. @default "Copy token" */
|
|
61
|
+
copyLabel?: CopyButtonProps['copyLabel'];
|
|
62
|
+
/** Accessible label while the copied state is active. @default "Token copied" */
|
|
63
|
+
copiedLabel?: CopyButtonProps['copiedLabel'];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Displays a blurred, truncated token preview while copying the original value
|
|
68
|
+
* in full.
|
|
69
|
+
*
|
|
70
|
+
* This is presentation, not a security boundary: the full token still reaches
|
|
71
|
+
* the client so the explicit copy action can write it to the clipboard.
|
|
72
|
+
*/
|
|
73
|
+
function AnonymizedToken({
|
|
74
|
+
value,
|
|
75
|
+
revealOnHover = true,
|
|
76
|
+
previewPrefixLength = 8,
|
|
77
|
+
previewSuffixLength = 4,
|
|
78
|
+
previewSeparator = '...',
|
|
79
|
+
obscuredLabel = 'Partially obscured token',
|
|
80
|
+
previewLabel = 'Partially visible token',
|
|
81
|
+
fallback = '-',
|
|
82
|
+
copyable = true,
|
|
83
|
+
timeout,
|
|
84
|
+
onCopied,
|
|
85
|
+
onCopyError,
|
|
86
|
+
copyLabel = 'Copy token',
|
|
87
|
+
copiedLabel = 'Token copied',
|
|
88
|
+
className,
|
|
89
|
+
onMouseEnter,
|
|
90
|
+
onMouseLeave,
|
|
91
|
+
onFocus,
|
|
92
|
+
onBlur,
|
|
93
|
+
...props
|
|
94
|
+
}: AnonymizedTokenProps) {
|
|
95
|
+
const [previewRevealed, setPreviewRevealed] = useState(false);
|
|
96
|
+
|
|
97
|
+
if (!value) {
|
|
98
|
+
return (
|
|
99
|
+
<span
|
|
100
|
+
data-slot="anonymized-token"
|
|
101
|
+
className={cn('text-muted-foreground text-sm', className)}
|
|
102
|
+
{...props}
|
|
103
|
+
>
|
|
104
|
+
{fallback}
|
|
105
|
+
</span>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const preview = getTokenPreview(
|
|
110
|
+
value,
|
|
111
|
+
previewPrefixLength,
|
|
112
|
+
previewSuffixLength,
|
|
113
|
+
previewSeparator,
|
|
114
|
+
);
|
|
115
|
+
const showRevealed = revealOnHover && previewRevealed;
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<span
|
|
119
|
+
data-slot="anonymized-token"
|
|
120
|
+
role="group"
|
|
121
|
+
className={chipChromeClass(copyable, className)}
|
|
122
|
+
onMouseEnter={(event) => {
|
|
123
|
+
onMouseEnter?.(event);
|
|
124
|
+
if (revealOnHover) setPreviewRevealed(true);
|
|
125
|
+
}}
|
|
126
|
+
onMouseLeave={(event) => {
|
|
127
|
+
onMouseLeave?.(event);
|
|
128
|
+
if (revealOnHover) setPreviewRevealed(false);
|
|
129
|
+
}}
|
|
130
|
+
onFocus={(event) => {
|
|
131
|
+
onFocus?.(event);
|
|
132
|
+
if (revealOnHover) setPreviewRevealed(true);
|
|
133
|
+
}}
|
|
134
|
+
onBlur={(event) => {
|
|
135
|
+
onBlur?.(event);
|
|
136
|
+
if (revealOnHover && !event.currentTarget.contains(event.relatedTarget as Node | null)) {
|
|
137
|
+
setPreviewRevealed(false);
|
|
138
|
+
}
|
|
139
|
+
}}
|
|
140
|
+
{...props}
|
|
141
|
+
>
|
|
142
|
+
{/* Blur is the only thing that sets a token apart from a plain `IdChip`;
|
|
143
|
+
the pill chrome is shared. No `overflow-hidden` here — it would clip
|
|
144
|
+
the blur halo — and the full value is never surfaced via `title`. */}
|
|
145
|
+
<code
|
|
146
|
+
data-slot="anonymized-token-value"
|
|
147
|
+
aria-hidden="true"
|
|
148
|
+
data-revealed={showRevealed || undefined}
|
|
149
|
+
className="blur-[1px] transition-[filter] data-[revealed]:blur-none"
|
|
150
|
+
>
|
|
151
|
+
{preview}
|
|
152
|
+
</code>
|
|
153
|
+
<span className="sr-only">{showRevealed ? previewLabel : obscuredLabel}</span>
|
|
154
|
+
{copyable ? (
|
|
155
|
+
<CopyButton
|
|
156
|
+
value={value}
|
|
157
|
+
timeout={timeout}
|
|
158
|
+
onCopied={onCopied}
|
|
159
|
+
onCopyError={onCopyError}
|
|
160
|
+
copyLabel={copyLabel}
|
|
161
|
+
copiedLabel={copiedLabel}
|
|
162
|
+
className="relative size-4 after:absolute after:-inset-1"
|
|
163
|
+
/>
|
|
164
|
+
) : null}
|
|
165
|
+
</span>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export { AnonymizedToken };
|
|
170
|
+
export type { AnonymizedTokenProps };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Badge, type badgeVariants } from '@vendure-io/ui/components/atoms/badge';
|
|
4
|
+
import { cn } from '@vendure-io/ui/lib/utils';
|
|
5
|
+
import type { VariantProps } from 'class-variance-authority';
|
|
6
|
+
import { XIcon } from 'lucide-react';
|
|
7
|
+
import type * as React from 'react';
|
|
8
|
+
|
|
9
|
+
interface ChipProps extends React.ComponentProps<'span'>, VariantProps<typeof badgeVariants> {
|
|
10
|
+
/** Leading icon slot. */
|
|
11
|
+
icon?: React.ReactNode;
|
|
12
|
+
/**
|
|
13
|
+
* Presence of this handler renders the remove (×) button; the caller closes
|
|
14
|
+
* over its own id (`onRemove={() => remove(item.id)}`). Absent → static chip.
|
|
15
|
+
*/
|
|
16
|
+
onRemove?: () => void;
|
|
17
|
+
/**
|
|
18
|
+
* Accessible label for the remove button (e.g. `Remove ${name}`). Required
|
|
19
|
+
* whenever `onRemove` is set — the × has no text otherwise.
|
|
20
|
+
*/
|
|
21
|
+
removeLabel?: string;
|
|
22
|
+
/** Dims the chip and disables removal (e.g. a pending un-assign mutation). */
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A compact, optionally removable tag built on the `Badge` atom. Generalizes the
|
|
28
|
+
* hand-rolled entity chips (facet-value / channel / customer-group) — the label
|
|
29
|
+
* is free `children`, and secondary text is a composition:
|
|
30
|
+
*
|
|
31
|
+
* ```tsx
|
|
32
|
+
* <Chip onRemove={() => remove(fv.id)} removeLabel={`Remove ${fv.name}`}>
|
|
33
|
+
* {fv.name} <span className="text-muted-foreground">in {facet.name}</span>
|
|
34
|
+
* </Chip>
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
function Chip({
|
|
38
|
+
className,
|
|
39
|
+
variant,
|
|
40
|
+
icon,
|
|
41
|
+
onRemove,
|
|
42
|
+
removeLabel,
|
|
43
|
+
disabled,
|
|
44
|
+
children,
|
|
45
|
+
...props
|
|
46
|
+
}: ChipProps) {
|
|
47
|
+
if (process.env.NODE_ENV !== 'production' && onRemove && !removeLabel) {
|
|
48
|
+
console.warn(
|
|
49
|
+
'Chip: `removeLabel` is required when `onRemove` is set — the remove button has no accessible name otherwise. Pass e.g. removeLabel="Remove <name>".',
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<Badge
|
|
55
|
+
data-slot="chip"
|
|
56
|
+
variant={variant}
|
|
57
|
+
data-disabled={disabled ? '' : undefined}
|
|
58
|
+
className={cn(
|
|
59
|
+
'max-w-full gap-1',
|
|
60
|
+
onRemove && 'pr-1',
|
|
61
|
+
disabled && 'pointer-events-none opacity-60',
|
|
62
|
+
className,
|
|
63
|
+
)}
|
|
64
|
+
{...props}
|
|
65
|
+
>
|
|
66
|
+
{icon}
|
|
67
|
+
<span className="truncate">{children}</span>
|
|
68
|
+
{onRemove ? (
|
|
69
|
+
<button
|
|
70
|
+
type="button"
|
|
71
|
+
data-slot="chip-remove"
|
|
72
|
+
disabled={disabled}
|
|
73
|
+
aria-label={removeLabel}
|
|
74
|
+
onClick={onRemove}
|
|
75
|
+
className="-mr-0.5 ml-0.5 inline-flex size-4 shrink-0 items-center justify-center rounded-full text-current/70 hover:bg-black/10 hover:text-current disabled:pointer-events-none dark:hover:bg-white/10"
|
|
76
|
+
>
|
|
77
|
+
<XIcon className="size-3" />
|
|
78
|
+
</button>
|
|
79
|
+
) : null}
|
|
80
|
+
</Badge>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export { Chip };
|
|
85
|
+
export type { ChipProps };
|