@vendure-io/ui 2.0.0-beta.0 → 2.0.0-beta.10
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 +6 -3
- package/package.json +7 -7
- package/src/components/atoms/button.tsx +5 -1
- package/src/components/atoms/card.tsx +1 -1
- package/src/components/atoms/scroll-area.tsx +4 -1
- package/src/components/atoms/sidebar.tsx +26 -11
- package/src/components/atoms/table.tsx +1 -1
- package/src/components/molecules/anonymized-token.tsx +2 -2
- package/src/components/molecules/app-shell.tsx +110 -0
- package/src/components/molecules/code-block.tsx +996 -0
- package/src/components/molecules/copy-feedback-provider.tsx +33 -0
- package/src/components/molecules/copyable-text.tsx +30 -7
- package/src/components/molecules/data-table/data-table.tsx +87 -85
- package/src/components/molecules/date-picker.tsx +117 -0
- package/src/components/molecules/date-range-picker.tsx +151 -0
- package/src/components/molecules/date-time-picker.tsx +131 -0
- package/src/components/molecules/file-dropzone.tsx +261 -0
- package/src/components/molecules/id-chip.tsx +1 -1
- package/src/components/molecules/skip-link.tsx +36 -0
- package/src/components/molecules/state-views/loading-state.tsx +7 -3
- package/src/lib/date-value.ts +48 -0
- package/src/lib/highlight.ts +141 -0
package/README.md
CHANGED
|
@@ -4,8 +4,8 @@ React component library for Vendure, built on [shadcn/ui](https://ui.shadcn.com/
|
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
|
-
```
|
|
8
|
-
|
|
7
|
+
```sh
|
|
8
|
+
bun add @vendure-io/ui
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
### Peer dependencies
|
|
@@ -13,7 +13,7 @@ npm install @vendure-io/ui
|
|
|
13
13
|
- `react` >= 19
|
|
14
14
|
- `react-dom` >= 19
|
|
15
15
|
|
|
16
|
-
Optional peers: `next`, `next-themes
|
|
16
|
+
Optional peers: `next`, `next-themes`
|
|
17
17
|
|
|
18
18
|
## Usage
|
|
19
19
|
|
|
@@ -32,6 +32,9 @@ import { cn } from "@vendure-io/ui/lib/utils";
|
|
|
32
32
|
| `@vendure-io/ui/lib/base-ui` | `@base-ui/react` primitive namespaces (see below) |
|
|
33
33
|
| `@vendure-io/ui/hooks/*` | Shared React hooks |
|
|
34
34
|
|
|
35
|
+
See the repository's [component guide](../../docs/ui-components.md) for the full
|
|
36
|
+
atom and molecule inventory.
|
|
37
|
+
|
|
35
38
|
## Customizing wrapper components
|
|
36
39
|
|
|
37
40
|
The components under `components/atoms/*` are thin wrappers around [`@base-ui/react`](https://base-ui.com/) primitives. To override a single subcomponent (e.g. swap `DialogTitle` for one with a different font) while re-using the rest, import the underlying primitive namespace from `@vendure-io/ui/lib/base-ui` rather than from `@base-ui/react` directly:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vendure-io/ui",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.10",
|
|
4
4
|
"description": "React component library for Vendure, built on shadcn/ui and Tailwind v4",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"homepage": "https://github.com/vendurehq/design/tree/main/packages/ui",
|
|
@@ -33,8 +33,7 @@
|
|
|
33
33
|
"react": ">=19",
|
|
34
34
|
"react-dom": ">=19",
|
|
35
35
|
"next": ">=14",
|
|
36
|
-
"next-themes": "^0.4.6"
|
|
37
|
-
"react-hook-form": ">=7"
|
|
36
|
+
"next-themes": "^0.4.6"
|
|
38
37
|
},
|
|
39
38
|
"peerDependenciesMeta": {
|
|
40
39
|
"next": {
|
|
@@ -42,15 +41,15 @@
|
|
|
42
41
|
},
|
|
43
42
|
"next-themes": {
|
|
44
43
|
"optional": true
|
|
45
|
-
},
|
|
46
|
-
"react-hook-form": {
|
|
47
|
-
"optional": true
|
|
48
44
|
}
|
|
49
45
|
},
|
|
50
46
|
"dependencies": {
|
|
51
47
|
"@base-ui/react": "^1.2.0",
|
|
48
|
+
"@shikijs/langs": "^3.23.0",
|
|
49
|
+
"@shikijs/themes": "^3.23.0",
|
|
50
|
+
"@shikijs/transformers": "^3.22.0",
|
|
52
51
|
"@tanstack/react-table": "^8.21.3",
|
|
53
|
-
"@vendure-io/design-tokens": "^
|
|
52
|
+
"@vendure-io/design-tokens": "^2.0.0-beta.7",
|
|
54
53
|
"class-variance-authority": "^0.7.1",
|
|
55
54
|
"clsx": "^2.1.1",
|
|
56
55
|
"cmdk": "^1.1.1",
|
|
@@ -62,6 +61,7 @@
|
|
|
62
61
|
"react-day-picker": "^9.14.0",
|
|
63
62
|
"react-resizable-panels": "^4.7.0",
|
|
64
63
|
"recharts": "2.15.4",
|
|
64
|
+
"shiki": "^3.22.0",
|
|
65
65
|
"sonner": "^2.0.7",
|
|
66
66
|
"tailwind-merge": "^3.5.0",
|
|
67
67
|
"vaul": "^1.1.2"
|
|
@@ -6,11 +6,15 @@ import { cva, type VariantProps } from "class-variance-authority"
|
|
|
6
6
|
import { cn } from "@vendure-io/ui/lib/utils"
|
|
7
7
|
|
|
8
8
|
const buttonVariants = cva(
|
|
9
|
-
"focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-md border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-3 aria-invalid:ring-3 [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all duration-(--transition-duration-fast) ease-(--ease-default) data-pressed:brightness-95 dark:data-pressed:brightness-110 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none",
|
|
9
|
+
"focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-md border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-3 aria-invalid:ring-3 [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all duration-(--transition-duration-fast) ease-(--ease-default) data-pressed:brightness-95 dark:data-pressed:brightness-110 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none cursor-pointer",
|
|
10
10
|
{
|
|
11
11
|
variants: {
|
|
12
12
|
variant: {
|
|
13
13
|
default: "bg-primary text-primary-foreground hover:bg-primary/80",
|
|
14
|
+
// Identity/marketing CTA only ("Start free trial", "Upgrade to Cloud"):
|
|
15
|
+
// a deliberate brand moment, not a louder `default` primary. Brand is a
|
|
16
|
+
// signature, not a wallpaper; see Button guidance before reaching for it.
|
|
17
|
+
brand: "bg-brand text-brand-foreground hover:bg-brand/80",
|
|
14
18
|
outline:
|
|
15
19
|
"border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground",
|
|
16
20
|
secondary:
|
|
@@ -12,7 +12,7 @@ function Card({
|
|
|
12
12
|
data-slot="card"
|
|
13
13
|
data-size={size}
|
|
14
14
|
className={cn(
|
|
15
|
-
"bg-card text-card-foreground group/card flex flex-col gap-6 overflow-hidden rounded-xl py-6 text-sm has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
|
|
15
|
+
"bg-card text-card-foreground group/card flex flex-col gap-6 overflow-hidden rounded-xl border border-border/60 py-6 text-sm has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
|
|
16
16
|
className
|
|
17
17
|
)}
|
|
18
18
|
{...props}
|
|
@@ -39,7 +39,10 @@ function ScrollBar({
|
|
|
39
39
|
data-orientation={orientation}
|
|
40
40
|
orientation={orientation}
|
|
41
41
|
className={cn(
|
|
42
|
-
|
|
42
|
+
// Overlay scrollbars: invisible at rest, fade in while the pointer is over
|
|
43
|
+
// the scroll area (data-hovering) or during active scrolling (data-scrolling).
|
|
44
|
+
// Base UI omits data-scrolling on programmatic scrolls, so route changes don't flash.
|
|
45
|
+
"flex touch-none p-px select-none opacity-0 transition-opacity data-hovering:opacity-100 data-scrolling:opacity-100 data-horizontal:h-2.5 data-horizontal:flex-col data-horizontal:border-t data-horizontal:border-t-transparent data-vertical:h-full data-vertical:w-2.5 data-vertical:border-l data-vertical:border-l-transparent",
|
|
43
46
|
className
|
|
44
47
|
)}
|
|
45
48
|
{...props}
|
|
@@ -302,17 +302,32 @@ function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
|
|
|
302
302
|
)
|
|
303
303
|
}
|
|
304
304
|
|
|
305
|
-
function SidebarInset({
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
305
|
+
function SidebarInset({
|
|
306
|
+
className,
|
|
307
|
+
render,
|
|
308
|
+
...props
|
|
309
|
+
}: useRender.ComponentProps<"main">) {
|
|
310
|
+
// The inset variant is our recessed content pane, built on the surface ramp: it
|
|
311
|
+
// steps up to --surface while the wrapper stays on --background, so the shell
|
|
312
|
+
// recedes and cards (--surface-raised) lift off the pane. A border carries the
|
|
313
|
+
// tier in light mode where the ramp collapses to white; flat by design, no drop
|
|
314
|
+
// shadow (see AGENTS.md "flat look"). The default variant stays flush on canvas.
|
|
315
|
+
return useRender({
|
|
316
|
+
defaultTagName: "main",
|
|
317
|
+
props: mergeProps<"main">(
|
|
318
|
+
{
|
|
319
|
+
className: cn(
|
|
320
|
+
"bg-background relative flex w-full flex-1 flex-col md:peer-data-[variant=inset]:bg-surface md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:border md:peer-data-[variant=inset]:border-border/60 md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2",
|
|
321
|
+
className
|
|
322
|
+
),
|
|
323
|
+
},
|
|
324
|
+
props
|
|
325
|
+
),
|
|
326
|
+
render,
|
|
327
|
+
state: {
|
|
328
|
+
slot: "sidebar-inset",
|
|
329
|
+
},
|
|
330
|
+
})
|
|
316
331
|
}
|
|
317
332
|
|
|
318
333
|
function SidebarInput({
|
|
@@ -57,7 +57,7 @@ function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
|
|
|
57
57
|
<tr
|
|
58
58
|
data-slot="table-row"
|
|
59
59
|
className={cn(
|
|
60
|
-
"hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",
|
|
60
|
+
"hover:bg-muted/50 data-[state=selected]:bg-muted border-b border-border/50 transition-colors",
|
|
61
61
|
className
|
|
62
62
|
)}
|
|
63
63
|
{...props}
|
|
@@ -53,9 +53,9 @@ interface AnonymizedTokenProps extends Omit<React.ComponentProps<'span'>, 'child
|
|
|
53
53
|
copyable?: boolean;
|
|
54
54
|
/** How long the check-mark feedback stays visible, in ms. */
|
|
55
55
|
timeout?: CopyButtonProps['timeout'];
|
|
56
|
-
/** Called after a successful copy. Wire your toast here. */
|
|
56
|
+
/** Called after a successful copy. Wire your toast here. Falls back to `CopyFeedbackProvider`. */
|
|
57
57
|
onCopied?: CopyButtonProps['onCopied'];
|
|
58
|
-
/** Called when the clipboard write fails. */
|
|
58
|
+
/** Called when the clipboard write fails. Falls back to `CopyFeedbackProvider`. */
|
|
59
59
|
onCopyError?: CopyButtonProps['onCopyError'];
|
|
60
60
|
/** Accessible label before copying. @default "Copy token" */
|
|
61
61
|
copyLabel?: CopyButtonProps['copyLabel'];
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { cn } from '@vendure-io/ui/lib/utils';
|
|
2
|
+
import type * as React from 'react';
|
|
3
|
+
|
|
4
|
+
// AppShell — the recessed-surface app layout, built on the surface ramp.
|
|
5
|
+
//
|
|
6
|
+
// Nests three surface tiers so the chrome reads as "deep" the way Linear's does:
|
|
7
|
+
//
|
|
8
|
+
// AppShell → --background the canvas the whole window sits on
|
|
9
|
+
// AppShellSidebar → --sidebar nav rail; equals the canvas, so it recedes
|
|
10
|
+
// AppShellContent → --surface the content pane, stepped forward + inset
|
|
11
|
+
//
|
|
12
|
+
// Cards dropped into the content pane resolve to --surface-raised (via the `card`
|
|
13
|
+
// token), completing the third step. In dark mode each tier is a lightness step;
|
|
14
|
+
// in light mode the ramp collapses to white, so the pane's border + shadow carry
|
|
15
|
+
// the step instead. That asymmetry is intentional, not a gap.
|
|
16
|
+
//
|
|
17
|
+
// The sidebar slot is deliberately unopinionated: drop the full `Sidebar` atom in
|
|
18
|
+
// for a real app, or use `AppShellSidebar` for a lightweight rail. Content owns a
|
|
19
|
+
// header + main anatomy so every consumer gets the same scroll boundary and skip
|
|
20
|
+
// link target without coupling the design system to a router or auth provider. The
|
|
21
|
+
// shell is viewport-bound so the canvas inset remains visible while main scrolls.
|
|
22
|
+
|
|
23
|
+
function AppShell({ className, ...props }: React.ComponentProps<'div'>) {
|
|
24
|
+
return (
|
|
25
|
+
<div
|
|
26
|
+
data-slot="app-shell"
|
|
27
|
+
className={cn('bg-background text-foreground flex h-svh w-full overflow-hidden', className)}
|
|
28
|
+
{...props}
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function AppShellSidebar({ className, ...props }: React.ComponentProps<'aside'>) {
|
|
34
|
+
return (
|
|
35
|
+
<aside
|
|
36
|
+
data-slot="app-shell-sidebar"
|
|
37
|
+
className={cn(
|
|
38
|
+
'bg-sidebar text-sidebar-foreground flex w-64 shrink-0 flex-col gap-1 p-3',
|
|
39
|
+
className,
|
|
40
|
+
)}
|
|
41
|
+
{...props}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function AppShellContent({ className, ...props }: React.ComponentProps<'div'>) {
|
|
47
|
+
return (
|
|
48
|
+
<div
|
|
49
|
+
data-slot="app-shell-content"
|
|
50
|
+
className={cn(
|
|
51
|
+
// The missing tier: step up to --surface and inset from the canvas so the
|
|
52
|
+
// shell frames the pane on all sides, including a gutter between the
|
|
53
|
+
// sidebar and the pane. Flat by design: the border carries the tier in
|
|
54
|
+
// light mode (see AGENTS.md "flat look"), so no drop shadow.
|
|
55
|
+
'bg-surface m-3 flex min-w-0 flex-1 flex-col overflow-hidden rounded-xl border border-border/60',
|
|
56
|
+
className,
|
|
57
|
+
)}
|
|
58
|
+
{...props}
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function AppShellHeader({ className, ...props }: React.ComponentProps<'header'>) {
|
|
64
|
+
return (
|
|
65
|
+
<header
|
|
66
|
+
data-slot="app-shell-header"
|
|
67
|
+
className={cn(
|
|
68
|
+
'flex h-12 shrink-0 items-center gap-2 border-b border-border/60 px-4',
|
|
69
|
+
className,
|
|
70
|
+
)}
|
|
71
|
+
{...props}
|
|
72
|
+
/>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface AppShellMainProps extends React.ComponentProps<'main'> {
|
|
77
|
+
/** The element responsible for scrolling the main content region. */
|
|
78
|
+
scrollOwner?: 'main' | 'container';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function AppShellMain({
|
|
82
|
+
className,
|
|
83
|
+
id = 'main-content',
|
|
84
|
+
scrollOwner = 'main',
|
|
85
|
+
...props
|
|
86
|
+
}: AppShellMainProps) {
|
|
87
|
+
return (
|
|
88
|
+
<main
|
|
89
|
+
id={id}
|
|
90
|
+
tabIndex={-1}
|
|
91
|
+
data-slot="app-shell-main"
|
|
92
|
+
className={cn(
|
|
93
|
+
scrollOwner === 'main'
|
|
94
|
+
? 'min-h-0 min-w-0 flex-1 overflow-auto outline-none'
|
|
95
|
+
: 'min-w-0 flex-1 outline-none',
|
|
96
|
+
className,
|
|
97
|
+
)}
|
|
98
|
+
{...props}
|
|
99
|
+
/>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export {
|
|
104
|
+
AppShell,
|
|
105
|
+
AppShellSidebar,
|
|
106
|
+
AppShellContent,
|
|
107
|
+
AppShellHeader,
|
|
108
|
+
AppShellMain,
|
|
109
|
+
type AppShellMainProps,
|
|
110
|
+
};
|