@vendure-io/ui 2.0.0-beta.17 → 2.0.0-beta.19
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
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.19",
|
|
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",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"@shikijs/themes": "^3.23.0",
|
|
51
51
|
"@shikijs/transformers": "^3.22.0",
|
|
52
52
|
"@tanstack/react-table": "^8.21.3",
|
|
53
|
-
"@vendure-io/design-tokens": "^2.0.0-beta.
|
|
53
|
+
"@vendure-io/design-tokens": "^2.0.0-beta.9",
|
|
54
54
|
"class-variance-authority": "^0.7.1",
|
|
55
55
|
"clsx": "^2.1.1",
|
|
56
56
|
"cmdk": "^1.1.1",
|
|
@@ -19,15 +19,26 @@ function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) {
|
|
|
19
19
|
return <ComboboxPrimitive.Value data-slot="combobox-value" {...props} />
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
// Field styling matches SelectTrigger, so a select-like combobox (the trigger
|
|
23
|
+
// shows the value, the search input sits in the popup) looks like the other
|
|
24
|
+
// form fields. ComboboxValue renders no element, so there is no value slot to
|
|
25
|
+
// style.
|
|
22
26
|
function ComboboxTrigger({
|
|
23
27
|
className,
|
|
28
|
+
size = "default",
|
|
24
29
|
children,
|
|
25
30
|
...props
|
|
26
|
-
}: ComboboxPrimitive.Trigger.Props
|
|
31
|
+
}: ComboboxPrimitive.Trigger.Props & {
|
|
32
|
+
size?: "sm" | "default"
|
|
33
|
+
}) {
|
|
27
34
|
return (
|
|
28
35
|
<ComboboxPrimitive.Trigger
|
|
29
36
|
data-slot="combobox-trigger"
|
|
30
|
-
|
|
37
|
+
data-size={size}
|
|
38
|
+
className={cn(
|
|
39
|
+
"border-input data-placeholder:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 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 flex w-fit items-center justify-between gap-1.5 rounded-md border bg-transparent py-2 pr-2 pl-2.5 text-sm whitespace-nowrap transition-[color,box-shadow] outline-none focus-visible:ring-3 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-3 data-[size=default]:h-9 data-[size=sm]:h-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
40
|
+
className
|
|
41
|
+
)}
|
|
31
42
|
{...props}
|
|
32
43
|
>
|
|
33
44
|
{children}
|
|
@@ -68,14 +79,18 @@ function ComboboxInput({
|
|
|
68
79
|
/>
|
|
69
80
|
<InputGroupAddon align="inline-end">
|
|
70
81
|
{showTrigger && (
|
|
82
|
+
// The bare primitive, not ComboboxTrigger: the icon button inside
|
|
83
|
+
// the input group must not take the field styling.
|
|
71
84
|
<InputGroupButton
|
|
72
85
|
size="icon-xs"
|
|
73
86
|
variant="ghost"
|
|
74
|
-
render={<
|
|
87
|
+
render={<ComboboxPrimitive.Trigger />}
|
|
75
88
|
data-slot="input-group-button"
|
|
76
89
|
className="group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent"
|
|
77
90
|
disabled={disabled}
|
|
78
|
-
|
|
91
|
+
>
|
|
92
|
+
<ChevronDownIcon className="text-muted-foreground pointer-events-none size-4" />
|
|
93
|
+
</InputGroupButton>
|
|
79
94
|
)}
|
|
80
95
|
{showClear && <ComboboxClear disabled={disabled} />}
|
|
81
96
|
</InputGroupAddon>
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
|
+
import { mergeProps } from "@base-ui/react/merge-props"
|
|
3
4
|
import { Tabs as TabsPrimitive } from "@base-ui/react/tabs"
|
|
5
|
+
import { useRender } from "@base-ui/react/use-render"
|
|
4
6
|
import { cva, type VariantProps } from "class-variance-authority"
|
|
5
7
|
|
|
6
8
|
import { cn } from "@vendure-io/ui/lib/utils"
|
|
@@ -82,4 +84,65 @@ function TabsContent({ className, ...props }: TabsPrimitive.Panel.Props) {
|
|
|
82
84
|
)
|
|
83
85
|
}
|
|
84
86
|
|
|
85
|
-
|
|
87
|
+
// Link tabs: section navigation where each tab is a route. They look like the
|
|
88
|
+
// line variant, but they are a <nav> of links with aria-current="page", not a
|
|
89
|
+
// tablist. Base UI Tabs would put role="tab" on a link and expect a panel for
|
|
90
|
+
// each tab, and the router already owns which section shows.
|
|
91
|
+
function TabsNav({ className, ...props }: React.ComponentProps<"nav">) {
|
|
92
|
+
return (
|
|
93
|
+
<nav
|
|
94
|
+
data-slot="tabs-nav"
|
|
95
|
+
className={cn(
|
|
96
|
+
// The padding keeps the focus ring and the underline inside the
|
|
97
|
+
// scroll box, which clips everything outside it.
|
|
98
|
+
"text-muted-foreground flex h-9 max-w-full items-center gap-1 overflow-x-auto p-[3px]",
|
|
99
|
+
className
|
|
100
|
+
)}
|
|
101
|
+
{...props}
|
|
102
|
+
/>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// `active` sets aria-current="page". The style reads aria-current, so a router
|
|
107
|
+
// link that sets it on the active route (TanStack Router, React Router's
|
|
108
|
+
// NavLink) works through `render` without `active`.
|
|
109
|
+
function TabsLink({
|
|
110
|
+
className,
|
|
111
|
+
active,
|
|
112
|
+
render,
|
|
113
|
+
...props
|
|
114
|
+
}: useRender.ComponentProps<"a"> & { active?: boolean }) {
|
|
115
|
+
return useRender({
|
|
116
|
+
defaultTagName: "a",
|
|
117
|
+
render,
|
|
118
|
+
props: mergeProps<"a">(
|
|
119
|
+
{
|
|
120
|
+
// Leave the attribute to the router link unless `active` is set.
|
|
121
|
+
...(active !== undefined && {
|
|
122
|
+
"aria-current": active ? ("page" as const) : undefined,
|
|
123
|
+
}),
|
|
124
|
+
className: cn(
|
|
125
|
+
"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-full shrink-0 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-all focus-visible:ring-[3px] focus-visible:outline-1 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
126
|
+
"aria-[current=page]:text-foreground dark:aria-[current=page]:text-foreground",
|
|
127
|
+
// The underline sits on the bottom edge of the nav, like the line variant.
|
|
128
|
+
"after:bg-foreground after:absolute after:inset-x-0 after:bottom-[-4px] after:h-0.5 after:opacity-0 after:transition-opacity aria-[current=page]:after:opacity-100",
|
|
129
|
+
className
|
|
130
|
+
),
|
|
131
|
+
},
|
|
132
|
+
props
|
|
133
|
+
),
|
|
134
|
+
state: {
|
|
135
|
+
slot: "tabs-link",
|
|
136
|
+
},
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export {
|
|
141
|
+
Tabs,
|
|
142
|
+
TabsList,
|
|
143
|
+
TabsTrigger,
|
|
144
|
+
TabsContent,
|
|
145
|
+
TabsNav,
|
|
146
|
+
TabsLink,
|
|
147
|
+
tabsListVariants,
|
|
148
|
+
}
|
|
@@ -6,12 +6,16 @@ import type * as React from 'react';
|
|
|
6
6
|
// header and lets variance in the wild (back links, badge rows beside the title,
|
|
7
7
|
// mono subtitles) live as free children rather than as an ever-growing prop set.
|
|
8
8
|
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* The header row: title/description on the left, actions on the right. The row
|
|
11
|
+
* wraps, so on a narrow screen the actions move below the title instead of
|
|
12
|
+
* squeezing it.
|
|
13
|
+
*/
|
|
10
14
|
function PageHeader({ className, ...props }: React.ComponentProps<'header'>) {
|
|
11
15
|
return (
|
|
12
16
|
<header
|
|
13
17
|
data-slot="page-header"
|
|
14
|
-
className={cn('flex items-start justify-between gap-4', className)}
|
|
18
|
+
className={cn('flex flex-wrap items-start justify-between gap-4', className)}
|
|
15
19
|
{...props}
|
|
16
20
|
/>
|
|
17
21
|
);
|
|
@@ -48,7 +52,7 @@ function PageHeaderDescription({ className, ...props }: React.ComponentProps<'p'
|
|
|
48
52
|
);
|
|
49
53
|
}
|
|
50
54
|
|
|
51
|
-
/** Right-hand action slot (buttons, menus).
|
|
55
|
+
/** Right-hand action slot (buttons, menus). Moves below the title when the row has no room. */
|
|
52
56
|
function PageHeaderActions({ className, ...props }: React.ComponentProps<'div'>) {
|
|
53
57
|
return (
|
|
54
58
|
<div
|
|
@@ -25,6 +25,12 @@ export interface EmptyStateProps extends Omit<React.ComponentProps<typeof Empty>
|
|
|
25
25
|
description?: React.ReactNode;
|
|
26
26
|
/** Actions/CTA — rendered in the content slot below the header. */
|
|
27
27
|
children?: React.ReactNode;
|
|
28
|
+
/**
|
|
29
|
+
* Draw the dashed box. Defaults to `true`. Set `false` when the empty state
|
|
30
|
+
* fills a region that already has a frame, such as a table body: the table
|
|
31
|
+
* owns its perimeter.
|
|
32
|
+
*/
|
|
33
|
+
bordered?: boolean;
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
/**
|
|
@@ -42,6 +48,7 @@ function EmptyState({
|
|
|
42
48
|
title,
|
|
43
49
|
description,
|
|
44
50
|
children,
|
|
51
|
+
bordered = true,
|
|
45
52
|
className,
|
|
46
53
|
...props
|
|
47
54
|
}: EmptyStateProps) {
|
|
@@ -57,7 +64,11 @@ function EmptyState({
|
|
|
57
64
|
) : null;
|
|
58
65
|
|
|
59
66
|
return (
|
|
60
|
-
<Empty
|
|
67
|
+
<Empty
|
|
68
|
+
data-slot="empty-state"
|
|
69
|
+
className={cn(bordered ? 'border' : 'rounded-none', className)}
|
|
70
|
+
{...props}
|
|
71
|
+
>
|
|
61
72
|
<EmptyHeader>
|
|
62
73
|
{resolvedIllustration ? (
|
|
63
74
|
<EmptyMedia>{resolvedIllustration}</EmptyMedia>
|