@calliopelabs/ui 0.4.0 → 0.6.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/dist/appearance/AppearancePicker.d.ts +19 -0
- package/dist/appearance/AppearancePicker.js +9 -2
- package/dist/data/DataTable.js +2 -2
- package/dist/index.d.ts +4 -3
- package/dist/index.js +4 -3
- package/dist/nav/model.d.ts +6 -1
- package/dist/nav/model.js +10 -2
- package/dist/overlays/AlertDialog.d.ts +22 -0
- package/dist/overlays/AlertDialog.js +41 -0
- package/dist/overlays/ConfirmDialog.d.ts +9 -2
- package/dist/overlays/ConfirmDialog.js +9 -5
- package/dist/overlays/Dialog.d.ts +1 -1
- package/dist/overlays/Dialog.js +4 -4
- package/dist/overlays/Drawer.d.ts +3 -3
- package/dist/overlays/Drawer.js +6 -5
- package/dist/page/PageHeader.d.ts +7 -1
- package/dist/page/PageHeader.js +2 -2
- package/dist/primitives/Button.js +4 -4
- package/dist/primitives/Checkbox.js +1 -1
- package/dist/primitives/DropdownMenu.d.ts +3 -0
- package/dist/primitives/DropdownMenu.js +12 -1
- package/dist/primitives/Input.js +1 -1
- package/dist/primitives/Popover.d.ts +2 -0
- package/dist/primitives/Popover.js +6 -0
- package/dist/provider/messages.d.ts +6 -0
- package/dist/provider/messages.js +3 -0
- package/dist/shell/RailTree.d.ts +2 -0
- package/dist/shell/RailTree.js +1 -1
- package/dist/states/EmptyState.js +1 -1
- package/dist/styles/preset.css +38 -1
- package/package.json +1 -1
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
import { type Ground } from "../theme/tokens.js";
|
|
3
|
+
import { type CustomAccent } from "./appearance.js";
|
|
2
4
|
/**
|
|
3
5
|
* The person's appearance for the product in context: theme presets, accent
|
|
4
6
|
* (including any colour that stands 3:1 off the ground), ground, density and
|
|
@@ -8,3 +10,20 @@ import * as React from "react";
|
|
|
8
10
|
export declare function AppearancePicker({ className }: {
|
|
9
11
|
className?: string;
|
|
10
12
|
}): React.JSX.Element;
|
|
13
|
+
export interface CustomAccentFieldProps {
|
|
14
|
+
/** The text field's id; the help text takes `${id}-help`. */
|
|
15
|
+
id: string;
|
|
16
|
+
/** The custom accent in use, or null while a named accent is. */
|
|
17
|
+
value: CustomAccent | null;
|
|
18
|
+
/** The ground the colour must stand 3:1 off. */
|
|
19
|
+
ground: Ground;
|
|
20
|
+
disabled: boolean;
|
|
21
|
+
/** Called only with a colour that passes the contrast check. */
|
|
22
|
+
onChange: (accent: CustomAccent) => void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Any colour as the accent (Astrolift palette option A). It applies only once
|
|
26
|
+
* it stands 3:1 off the ground (WCAG 2.2 §1.4.11); until then the draft stays
|
|
27
|
+
* in the field with the reason.
|
|
28
|
+
*/
|
|
29
|
+
export declare function CustomAccentField({ id, value, ground, disabled, onChange, }: CustomAccentFieldProps): React.JSX.Element;
|
|
@@ -44,17 +44,24 @@ export function AppearancePicker({ className }) {
|
|
|
44
44
|
const id = React.useId();
|
|
45
45
|
const accents = product.accents;
|
|
46
46
|
const activeTheme = product.themes.find((th) => th.ground === appearance.ground && th.accent === appearance.accent);
|
|
47
|
-
return (_jsxs("div", { "data-slot": "appearance-picker", className: cn("flex min-w-0 flex-col gap-6", className), children: [locked && (_jsxs("p", { className: "flex items-center gap-2 rounded-lg border border-info-border bg-info-bg px-3 py-2 text-sm text-info-fg", children: [_jsx(LockIcon, { className: "size-4 shrink-0", "aria-hidden": true }), t.locked] })), product.themes.length > 0 && (_jsx(Group, { legend: t.themes, children: _jsx("div", { className: "grid grid-cols-[repeat(auto-fill,minmax(9rem,1fr))] gap-2", children: product.themes.map((theme) => (_jsxs(Choice, { name: `${id}-theme`, checked: activeTheme?.id === theme.id, disabled: locked, onSelect: () => setAppearance({ ground: theme.ground, accent: theme.accent }), className: "flex-col items-stretch gap-2 p-2", children: [_jsx(ThemePreview, { product: product, ground: theme.ground, accent: accents.find((a) => a.key === theme.accent) }), _jsxs("span", { children: [_jsx("span", { className: "block text-sm font-semibold", children: theme
|
|
47
|
+
return (_jsxs("div", { "data-slot": "appearance-picker", className: cn("flex min-w-0 flex-col gap-6", className), children: [locked && (_jsxs("p", { className: "flex items-center gap-2 rounded-lg border border-info-border bg-info-bg px-3 py-2 text-sm text-info-fg", children: [_jsx(LockIcon, { className: "size-4 shrink-0", "aria-hidden": true }), t.locked] })), product.themes.length > 0 && (_jsx(Group, { legend: t.themes, children: _jsx("div", { className: "grid grid-cols-[repeat(auto-fill,minmax(9rem,1fr))] gap-2", children: product.themes.map((theme) => (_jsxs(Choice, { name: `${id}-theme`, checked: activeTheme?.id === theme.id, disabled: locked, onSelect: () => setAppearance({ ground: theme.ground, accent: theme.accent }), className: "flex-col items-stretch gap-2 p-2", children: [_jsx(ThemePreview, { product: product, ground: theme.ground, accent: accents.find((a) => a.key === theme.accent) }), _jsxs("span", { children: [_jsx("span", { className: "block text-sm font-semibold", children: t.themeName(theme) }), _jsx("span", { className: "block text-xs text-muted-foreground", children: t.themeHint(theme) })] })] }, theme.id))) }) })), _jsxs(Group, { legend: t.accent, children: [_jsx("div", { className: "flex flex-wrap gap-2", children: accents.map((accent) => (_jsxs(Choice, { name: `${id}-accent`, checked: appearance.accent === accent.key, disabled: locked, onSelect: () => setAppearance({ accent: accent.key }), children: [_jsx("span", { "aria-hidden": true, className: "size-3.5 rounded-full", style: { background: accentOn(accent, appearance.ground) } }), t.accentLabel(accent.key, accent.label)] }, accent.key))) }), _jsx(CustomAccentField, { id: `${id}-custom`, value: isCustomAccent(appearance.accent) ? appearance.accent : null, ground: appearance.ground, disabled: locked, onChange: (accent) => setAppearance({ accent }) })] }), _jsx(Group, { legend: t.ground, children: _jsx("div", { className: "flex flex-wrap gap-2", children: product.grounds.map((ground) => (_jsxs(Choice, { name: `${id}-ground`, checked: appearance.ground === ground, disabled: locked, onSelect: () => setAppearance({ ground }), children: [_jsx("span", { "aria-hidden": true, className: "size-3.5 rounded-full border", style: { background: SURFACES[ground].background } }), t.groundLabel(ground, GROUNDS[ground].label), _jsx("span", { className: "text-xs text-muted-foreground", children: t.mode(GROUNDS[ground].mode) })] }, ground))) }) }), _jsx(Group, { legend: t.density, children: _jsx("div", { className: "grid gap-2 sm:grid-cols-2", children: DENSITY_KEYS.map((density) => (_jsxs(Choice, { name: `${id}-density`, checked: appearance.density === density, disabled: locked, onSelect: () => setAppearance({ density }), className: "flex-col items-start gap-0.5 px-3 py-2", children: [_jsx("span", { className: "text-sm font-semibold", children: t.densityLabel(density) }), _jsx("span", { className: "text-xs text-muted-foreground", children: t.densityHint(density) })] }, density))) }) }), _jsx(Group, { legend: t.corners, children: _jsx("div", { className: "flex flex-wrap gap-2", children: CORNERS.map((corners) => (_jsxs(Choice, { name: `${id}-corners`, checked: appearance.corners === corners, disabled: locked, onSelect: () => setAppearance({ corners }), children: [_jsx("span", { "aria-hidden": true, className: "size-3.5 border-t-2 border-l-2 border-current", style: { borderTopLeftRadius: corners } }), t.cornersLabel(corners)] }, corners))) }) }), !locked && (_jsx("div", { children: _jsx(Button, { variant: "outline", size: "sm", onClick: reset, children: t.reset }) }))] }));
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
50
50
|
* Any colour as the accent (Astrolift palette option A). It applies only once
|
|
51
51
|
* it stands 3:1 off the ground (WCAG 2.2 §1.4.11); until then the draft stays
|
|
52
52
|
* in the field with the reason.
|
|
53
53
|
*/
|
|
54
|
-
function
|
|
54
|
+
export function CustomAccentField({ id, value, ground, disabled, onChange, }) {
|
|
55
55
|
const { messages } = useUI();
|
|
56
56
|
const t = messages.appearance;
|
|
57
57
|
const [draft, setDraft] = React.useState(value ?? "");
|
|
58
|
+
// Controlled by `value`: a new value from outside (a server load, a reset)
|
|
59
|
+
// replaces the draft. Adjusted during render, not in an effect.
|
|
60
|
+
const [shownValue, setShownValue] = React.useState(value);
|
|
61
|
+
if (value !== shownValue) {
|
|
62
|
+
setShownValue(value);
|
|
63
|
+
setDraft(value ?? "");
|
|
64
|
+
}
|
|
58
65
|
const parsed = parseCustomAccent(draft);
|
|
59
66
|
const check = parsed ? accentContrast(parsed, ground) : null;
|
|
60
67
|
const helpId = `${id}-help`;
|
package/dist/data/DataTable.js
CHANGED
|
@@ -31,7 +31,7 @@ export function DataTable({ label, controller, columns, getRowId, empty, emptyFi
|
|
|
31
31
|
const t = messages.table;
|
|
32
32
|
const contextDensity = useDensity();
|
|
33
33
|
const density = densityProp ?? contextDensity;
|
|
34
|
-
const { rows, state, error, retry, pageSize, clearFilters } = controller;
|
|
34
|
+
const { rows, state, error, retry, pageSize, isFiltered, clearFilters } = controller;
|
|
35
35
|
const cards = density === "cards" && Boolean(renderCard);
|
|
36
36
|
const pageIds = React.useMemo(() => rows.map(getRowId), [rows, getRowId]);
|
|
37
37
|
const visibleColumns = rowActions
|
|
@@ -98,7 +98,7 @@ export function DataTable({ label, controller, columns, getRowId, empty, emptyFi
|
|
|
98
98
|
return (_jsxs("div", { className: "relative", "aria-busy": controller.isStale || undefined, children: [controller.isStale && _jsx(StaleBar, { className: "-top-1.5" }), _jsx("ul", { "aria-label": label, className: grid, children: rows.map((row) => (_jsx("li", { className: cn(box, rowClassName?.(row)), children: rowHref ? (_jsx(Link, { href: rowHref(row), className: "block h-full rounded-lg outline-none after:absolute after:inset-0 focus-visible:after:ring-2 focus-visible:after:ring-ring", children: renderCard?.(row) })) : onRowActivate ? (_jsx("button", { type: "button", "aria-label": rowLabel?.(row), onClick: () => onRowActivate(row), className: "block h-full w-full text-left outline-none after:absolute after:inset-0 focus-visible:after:ring-2 focus-visible:after:ring-ring", children: renderCard?.(row) })) : (renderCard?.(row)) }, getRowId(row)))) })] }));
|
|
99
99
|
}
|
|
100
100
|
})();
|
|
101
|
-
return (_jsxs("div", { "data-slot": "data-table", "data-density": densityProp, className: cn("flex min-w-0 flex-col gap-3", className), children: [chrome === "full" && (_jsx(Toolbar, { controller: controller, placeholder: searchPlaceholder, children: toolbar })), selection && selection.selectedCount > 0 && bulkActions && !cards && (_jsxs("div", { className: "flex flex-wrap items-center gap-3 rounded-lg border bg-muted/50 px-3 py-2", children: [_jsx("span", { className: "text-sm font-medium tabular-nums", children: t.selected(selection.selectedCount) }), _jsx(Button, { variant: "ghost", size: "sm", onClick: selection.clear, children: t.clearSelection }), _jsx("div", { className: "ml-auto flex flex-wrap items-center gap-2", children: bulkActions(selection) })] })), cards ? cardGrid : table, chrome === "full" && _jsx(Pagination, { controller: controller }), state === "loading" && (_jsx("p", { role: "status", className: "sr-only", children: t.loading }))] }));
|
|
101
|
+
return (_jsxs("div", { "data-slot": "data-table", "data-density": densityProp, className: cn("flex min-w-0 flex-col gap-3", className), children: [chrome === "full" && (_jsx(Toolbar, { controller: controller, placeholder: searchPlaceholder, children: toolbar })), selection && selection.selectedCount > 0 && bulkActions && !cards && (_jsxs("div", { className: "flex flex-wrap items-center gap-3 rounded-lg border bg-muted/50 px-3 py-2", children: [_jsx("span", { className: "text-sm font-medium tabular-nums", children: t.selected(selection.selectedCount) }), _jsx(Button, { variant: "ghost", size: "sm", onClick: selection.clear, children: t.clearSelection }), _jsx("div", { className: "ml-auto flex flex-wrap items-center gap-2", children: bulkActions(selection) })] })), cards ? cardGrid : table, chrome === "full" && _jsx(Pagination, { controller: controller }), state === "loading" && (_jsx("p", { role: "status", className: "sr-only", children: t.loading })), isFiltered && state === "ready" && (_jsx("p", { "aria-live": "polite", className: "sr-only", children: t.filteredResults }))] }));
|
|
102
102
|
}
|
|
103
103
|
/**
|
|
104
104
|
* The rows on screen answer the previous question while the next answer is
|
package/dist/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export { accentContrast, appearanceFromServer, applyAppearance, clearAppearance,
|
|
|
13
13
|
export { appearanceScript, productStyles } from "./appearance/boot.js";
|
|
14
14
|
export { AppearanceScript, type AppearanceScriptProps } from "./appearance/AppearanceScript.js";
|
|
15
15
|
export { AppearanceProvider, useAppearance, useDensity, useProduct, type AppearanceContextValue, type AppearanceProviderProps, } from "./appearance/AppearanceProvider.js";
|
|
16
|
-
export { AppearancePicker } from "./appearance/AppearancePicker.js";
|
|
16
|
+
export { AppearancePicker, CustomAccentField, type CustomAccentFieldProps, } from "./appearance/AppearancePicker.js";
|
|
17
17
|
export { activeFor, areaSwitcher, localizeNav, visibleNav, type CrumbSwitchOption, type NavActive, type NavArea, type NavFunction, type NavGroup, type NavIcon, type NavModel, } from "./nav/model.js";
|
|
18
18
|
export { Nav, type NavProps } from "./nav/Nav.js";
|
|
19
19
|
export { AppShell, type AppShellProps } from "./shell/AppShell.js";
|
|
@@ -40,11 +40,12 @@ export { ErrorState, type ErrorStateProps } from "./states/ErrorState.js";
|
|
|
40
40
|
export { LoadingState, type LoadingStateProps } from "./states/LoadingState.js";
|
|
41
41
|
export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "./overlays/Dialog.js";
|
|
42
42
|
export { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger, } from "./overlays/Drawer.js";
|
|
43
|
+
export { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "./overlays/AlertDialog.js";
|
|
43
44
|
export { ConfirmDialog, type ConfirmDialogProps, type ConfirmReason, } from "./overlays/ConfirmDialog.js";
|
|
44
45
|
export { Button, buttonVariants, type ButtonProps } from "./primitives/Button.js";
|
|
45
46
|
export { Input } from "./primitives/Input.js";
|
|
46
47
|
export { Checkbox } from "./primitives/Checkbox.js";
|
|
47
|
-
export { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "./primitives/DropdownMenu.js";
|
|
48
|
-
export { Popover, PopoverContent, PopoverTrigger } from "./primitives/Popover.js";
|
|
48
|
+
export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "./primitives/DropdownMenu.js";
|
|
49
|
+
export { Popover, PopoverAnchor, PopoverContent, PopoverHeader, PopoverTrigger, } from "./primitives/Popover.js";
|
|
49
50
|
export { Tabs, TabsContent, TabsList, TabsTrigger } from "./primitives/Tabs.js";
|
|
50
51
|
export { Skeleton } from "./primitives/Skeleton.js";
|
package/dist/index.js
CHANGED
|
@@ -16,7 +16,7 @@ export { accentContrast, appearanceFromServer, applyAppearance, clearAppearance,
|
|
|
16
16
|
export { appearanceScript, productStyles } from "./appearance/boot.js";
|
|
17
17
|
export { AppearanceScript } from "./appearance/AppearanceScript.js";
|
|
18
18
|
export { AppearanceProvider, useAppearance, useDensity, useProduct, } from "./appearance/AppearanceProvider.js";
|
|
19
|
-
export { AppearancePicker } from "./appearance/AppearancePicker.js";
|
|
19
|
+
export { AppearancePicker, CustomAccentField, } from "./appearance/AppearancePicker.js";
|
|
20
20
|
// Navigation
|
|
21
21
|
export { activeFor, areaSwitcher, localizeNav, visibleNav, } from "./nav/model.js";
|
|
22
22
|
export { Nav } from "./nav/Nav.js";
|
|
@@ -50,12 +50,13 @@ export { LoadingState } from "./states/LoadingState.js";
|
|
|
50
50
|
// Overlays
|
|
51
51
|
export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "./overlays/Dialog.js";
|
|
52
52
|
export { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger, } from "./overlays/Drawer.js";
|
|
53
|
+
export { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "./overlays/AlertDialog.js";
|
|
53
54
|
export { ConfirmDialog, } from "./overlays/ConfirmDialog.js";
|
|
54
55
|
// Primitives
|
|
55
56
|
export { Button, buttonVariants } from "./primitives/Button.js";
|
|
56
57
|
export { Input } from "./primitives/Input.js";
|
|
57
58
|
export { Checkbox } from "./primitives/Checkbox.js";
|
|
58
|
-
export { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "./primitives/DropdownMenu.js";
|
|
59
|
-
export { Popover, PopoverContent, PopoverTrigger } from "./primitives/Popover.js";
|
|
59
|
+
export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "./primitives/DropdownMenu.js";
|
|
60
|
+
export { Popover, PopoverAnchor, PopoverContent, PopoverHeader, PopoverTrigger, } from "./primitives/Popover.js";
|
|
60
61
|
export { Tabs, TabsContent, TabsList, TabsTrigger } from "./primitives/Tabs.js";
|
|
61
62
|
export { Skeleton } from "./primitives/Skeleton.js";
|
package/dist/nav/model.d.ts
CHANGED
|
@@ -59,14 +59,19 @@ export interface CrumbSwitchOption {
|
|
|
59
59
|
label: string;
|
|
60
60
|
href: string;
|
|
61
61
|
active?: boolean;
|
|
62
|
+
/** The function's key, so an app can translate the label from its catalog. */
|
|
63
|
+
navFunction?: string;
|
|
62
64
|
}
|
|
63
65
|
/**
|
|
64
66
|
* The first crumb of a page inside an area: the area's name, switching
|
|
65
67
|
* between its functions with the current one checked. Read from the model,
|
|
66
|
-
* so the sidebar and the breadcrumb always agree.
|
|
68
|
+
* so the sidebar and the breadcrumb always agree. `navArea` and each
|
|
69
|
+
* option's `navFunction` carry the model's keys, so an app can translate the
|
|
70
|
+
* labels from its catalog.
|
|
67
71
|
*/
|
|
68
72
|
export declare function areaSwitcher(nav: NavModel, area: string, activeFn: string): {
|
|
69
73
|
label: string;
|
|
74
|
+
navArea: string;
|
|
70
75
|
switcher: CrumbSwitchOption[];
|
|
71
76
|
};
|
|
72
77
|
/**
|
package/dist/nav/model.js
CHANGED
|
@@ -40,13 +40,21 @@ export function activeFor(nav, pathname) {
|
|
|
40
40
|
/**
|
|
41
41
|
* The first crumb of a page inside an area: the area's name, switching
|
|
42
42
|
* between its functions with the current one checked. Read from the model,
|
|
43
|
-
* so the sidebar and the breadcrumb always agree.
|
|
43
|
+
* so the sidebar and the breadcrumb always agree. `navArea` and each
|
|
44
|
+
* option's `navFunction` carry the model's keys, so an app can translate the
|
|
45
|
+
* labels from its catalog.
|
|
44
46
|
*/
|
|
45
47
|
export function areaSwitcher(nav, area, activeFn) {
|
|
46
48
|
const found = nav.find((a) => a.key === area);
|
|
47
49
|
return {
|
|
48
50
|
label: found?.label ?? area,
|
|
49
|
-
|
|
51
|
+
navArea: area,
|
|
52
|
+
switcher: (found?.groups ?? []).flatMap((group) => group.functions.map((fn) => ({
|
|
53
|
+
label: fn.label,
|
|
54
|
+
href: fn.href,
|
|
55
|
+
active: fn.key === activeFn,
|
|
56
|
+
navFunction: fn.key,
|
|
57
|
+
}))),
|
|
50
58
|
};
|
|
51
59
|
}
|
|
52
60
|
/**
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { AlertDialog as AlertDialogPrimitive } from "radix-ui";
|
|
2
|
+
import type * as React from "react";
|
|
3
|
+
import { Button } from "../primitives/Button.js";
|
|
4
|
+
/**
|
|
5
|
+
* The parts of a confirmation that needs more than ConfirmDialog holds: a
|
|
6
|
+
* preview of what the action touches, a typed check, a choice. Prefer
|
|
7
|
+
* ConfirmDialog when a title, a description and an optional reason will do;
|
|
8
|
+
* it brings the pending state and the error feedback. The scrim does not
|
|
9
|
+
* close an alert dialog; Escape and Cancel do.
|
|
10
|
+
*/
|
|
11
|
+
export declare function AlertDialog(props: React.ComponentProps<typeof AlertDialogPrimitive.Root>): React.JSX.Element;
|
|
12
|
+
export declare function AlertDialogTrigger(props: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>): React.JSX.Element;
|
|
13
|
+
export declare function AlertDialogContent({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Content>): React.JSX.Element;
|
|
14
|
+
export declare function AlertDialogHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
15
|
+
export declare function AlertDialogFooter({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
16
|
+
export declare function AlertDialogTitle({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Title>): React.JSX.Element;
|
|
17
|
+
export declare function AlertDialogDescription({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Description>): React.JSX.Element;
|
|
18
|
+
type ButtonLook = Pick<React.ComponentProps<typeof Button>, "variant" | "size">;
|
|
19
|
+
/** Closes the dialog when clicked; call `event.preventDefault()` to hold it open. */
|
|
20
|
+
export declare function AlertDialogAction({ className, variant, size, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Action> & ButtonLook): React.JSX.Element;
|
|
21
|
+
export declare function AlertDialogCancel({ className, variant, size, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Cancel> & ButtonLook): React.JSX.Element;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { AlertDialog as AlertDialogPrimitive } from "radix-ui";
|
|
4
|
+
import { cn } from "../lib/cn.js";
|
|
5
|
+
import { Button } from "../primitives/Button.js";
|
|
6
|
+
import { overlayClassName } from "./Dialog.js";
|
|
7
|
+
/**
|
|
8
|
+
* The parts of a confirmation that needs more than ConfirmDialog holds: a
|
|
9
|
+
* preview of what the action touches, a typed check, a choice. Prefer
|
|
10
|
+
* ConfirmDialog when a title, a description and an optional reason will do;
|
|
11
|
+
* it brings the pending state and the error feedback. The scrim does not
|
|
12
|
+
* close an alert dialog; Escape and Cancel do.
|
|
13
|
+
*/
|
|
14
|
+
export function AlertDialog(props) {
|
|
15
|
+
return _jsx(AlertDialogPrimitive.Root, { "data-slot": "alert-dialog", ...props });
|
|
16
|
+
}
|
|
17
|
+
export function AlertDialogTrigger(props) {
|
|
18
|
+
return _jsx(AlertDialogPrimitive.Trigger, { "data-slot": "alert-dialog-trigger", ...props });
|
|
19
|
+
}
|
|
20
|
+
export function AlertDialogContent({ className, ...props }) {
|
|
21
|
+
return (_jsxs(AlertDialogPrimitive.Portal, { children: [_jsx(AlertDialogPrimitive.Overlay, { "data-slot": "alert-dialog-overlay", className: overlayClassName("bg-black/10") }), _jsx(AlertDialogPrimitive.Content, { "data-slot": "alert-dialog-content", className: cn("fixed top-1/2 left-1/2 z-50 grid max-h-[calc(100dvh-2rem)] w-[calc(100%-2rem)] max-w-xs -translate-x-1/2 -translate-y-1/2 grid-cols-[minmax(0,1fr)] gap-4 overflow-y-auto rounded-xl bg-background p-4 text-foreground shadow-lg ring-1 ring-foreground/10 outline-none data-[state=closed]:animate-cui-pop-out data-[state=open]:animate-cui-pop-in sm:max-w-sm", className), ...props })] }));
|
|
22
|
+
}
|
|
23
|
+
export function AlertDialogHeader({ className, ...props }) {
|
|
24
|
+
return (_jsx("div", { "data-slot": "alert-dialog-header", className: cn("grid place-items-center gap-1.5 text-center sm:place-items-start sm:text-left", className), ...props }));
|
|
25
|
+
}
|
|
26
|
+
export function AlertDialogFooter({ className, ...props }) {
|
|
27
|
+
return (_jsx("div", { "data-slot": "alert-dialog-footer", className: cn("-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 sm:flex-row sm:justify-end", className), ...props }));
|
|
28
|
+
}
|
|
29
|
+
export function AlertDialogTitle({ className, ...props }) {
|
|
30
|
+
return (_jsx(AlertDialogPrimitive.Title, { "data-slot": "alert-dialog-title", className: cn("font-head text-base font-semibold [overflow-wrap:anywhere]", className), ...props }));
|
|
31
|
+
}
|
|
32
|
+
export function AlertDialogDescription({ className, ...props }) {
|
|
33
|
+
return (_jsx(AlertDialogPrimitive.Description, { "data-slot": "alert-dialog-description", className: cn("text-sm [overflow-wrap:anywhere] text-muted-foreground", className), ...props }));
|
|
34
|
+
}
|
|
35
|
+
/** Closes the dialog when clicked; call `event.preventDefault()` to hold it open. */
|
|
36
|
+
export function AlertDialogAction({ className, variant = "default", size = "default", ...props }) {
|
|
37
|
+
return (_jsx(Button, { variant: variant, size: size, asChild: true, children: _jsx(AlertDialogPrimitive.Action, { "data-slot": "alert-dialog-action", className: className, ...props }) }));
|
|
38
|
+
}
|
|
39
|
+
export function AlertDialogCancel({ className, variant = "outline", size = "default", ...props }) {
|
|
40
|
+
return (_jsx(Button, { variant: variant, size: size, asChild: true, children: _jsx(AlertDialogPrimitive.Cancel, { "data-slot": "alert-dialog-cancel", className: className, ...props }) }));
|
|
41
|
+
}
|
|
@@ -23,9 +23,16 @@ export interface ConfirmDialogProps {
|
|
|
23
23
|
* Awaited on confirm, with the trimmed reason ("" without one). While it is
|
|
24
24
|
* pending both buttons are disabled. Resolving `false` keeps the dialog
|
|
25
25
|
* open (the caller reported the failure); any other result closes it. A
|
|
26
|
-
* rejection keeps it open and shows the error's message in the dialog
|
|
26
|
+
* rejection keeps it open and shows the error's message in the dialog,
|
|
27
|
+
* or hands it to `onError` when one is given.
|
|
27
28
|
*/
|
|
28
29
|
onConfirm: (reason: string) => Promise<unknown> | unknown;
|
|
30
|
+
/**
|
|
31
|
+
* Report a rejected `onConfirm` yourself (a toast, say) instead of the
|
|
32
|
+
* inline error. `message` is the line the dialog would have shown. The
|
|
33
|
+
* dialog still stays open. A missing reason is always shown inline.
|
|
34
|
+
*/
|
|
35
|
+
onError?: (error: unknown, message: string) => void;
|
|
29
36
|
reason?: ConfirmReason;
|
|
30
37
|
}
|
|
31
38
|
/**
|
|
@@ -34,4 +41,4 @@ export interface ConfirmDialogProps {
|
|
|
34
41
|
* place of `window.confirm`, which blocks the event loop and can say nothing
|
|
35
42
|
* when the action fails.
|
|
36
43
|
*/
|
|
37
|
-
export declare function ConfirmDialog({ open, onOpenChange, title, description, confirmLabel, cancelLabel, destructive, confirmDisabled, onConfirm, reason, }: ConfirmDialogProps): React.JSX.Element;
|
|
44
|
+
export declare function ConfirmDialog({ open, onOpenChange, title, description, confirmLabel, cancelLabel, destructive, confirmDisabled, onConfirm, onError, reason, }: ConfirmDialogProps): React.JSX.Element;
|
|
@@ -12,7 +12,7 @@ import { overlayClassName } from "./Dialog.js";
|
|
|
12
12
|
* place of `window.confirm`, which blocks the event loop and can say nothing
|
|
13
13
|
* when the action fails.
|
|
14
14
|
*/
|
|
15
|
-
export function ConfirmDialog({ open, onOpenChange, title, description, confirmLabel, cancelLabel, destructive = false, confirmDisabled = false, onConfirm, reason, }) {
|
|
15
|
+
export function ConfirmDialog({ open, onOpenChange, title, description, confirmLabel, cancelLabel, destructive = false, confirmDisabled = false, onConfirm, onError, reason, }) {
|
|
16
16
|
const { messages } = useUI();
|
|
17
17
|
const t = messages.confirm;
|
|
18
18
|
const [pending, setPending] = React.useState(false);
|
|
@@ -44,11 +44,15 @@ export function ConfirmDialog({ open, onOpenChange, title, description, confirmL
|
|
|
44
44
|
close();
|
|
45
45
|
}
|
|
46
46
|
catch (err) {
|
|
47
|
-
|
|
47
|
+
const message = err instanceof Error && err.message
|
|
48
48
|
? err.message
|
|
49
49
|
: typeof err === "string" && err
|
|
50
50
|
? err
|
|
51
|
-
: t.failed
|
|
51
|
+
: t.failed;
|
|
52
|
+
if (onError)
|
|
53
|
+
onError(err, message);
|
|
54
|
+
else
|
|
55
|
+
setError(message);
|
|
52
56
|
}
|
|
53
57
|
finally {
|
|
54
58
|
setPending(false);
|
|
@@ -62,9 +66,9 @@ export function ConfirmDialog({ open, onOpenChange, title, description, confirmL
|
|
|
62
66
|
onOpenChange(true);
|
|
63
67
|
else
|
|
64
68
|
close();
|
|
65
|
-
}, children: _jsxs(AlertDialogPrimitive.Portal, { children: [_jsx(AlertDialogPrimitive.Overlay, { "data-slot": "confirm-dialog-overlay", className: overlayClassName() }), _jsxs(AlertDialogPrimitive.Content, { "data-slot": "confirm-dialog", "aria-busy": pending || undefined, ...(description == null ? { "aria-describedby": undefined } : {}), className: "fixed top-1/2 left-1/2 z-50 grid w-[calc(100%-2rem)] max-w-
|
|
69
|
+
}, children: _jsxs(AlertDialogPrimitive.Portal, { children: [_jsx(AlertDialogPrimitive.Overlay, { "data-slot": "confirm-dialog-overlay", className: overlayClassName("bg-black/10") }), _jsxs(AlertDialogPrimitive.Content, { "data-slot": "confirm-dialog", "aria-busy": pending || undefined, ...(description == null ? { "aria-describedby": undefined } : {}), className: "fixed top-1/2 left-1/2 z-50 grid w-[calc(100%-2rem)] max-w-xs -translate-x-1/2 -translate-y-1/2 grid-cols-[minmax(0,1fr)] gap-4 rounded-xl bg-background p-4 text-foreground shadow-lg ring-1 ring-foreground/10 outline-none data-[state=closed]:animate-cui-pop-out data-[state=open]:animate-cui-pop-in sm:max-w-sm", children: [_jsxs("div", { "data-slot": "confirm-dialog-header", className: "grid place-items-center gap-1.5 text-center sm:place-items-start sm:text-left", children: [_jsx(AlertDialogPrimitive.Title, { className: "font-head text-base font-semibold [overflow-wrap:anywhere]", children: title }), description != null && (_jsx(AlertDialogPrimitive.Description, { className: "text-sm [overflow-wrap:anywhere] text-muted-foreground", children: description }))] }), reason && (_jsxs("div", { className: "grid min-w-0 gap-1.5", children: [_jsx("label", { htmlFor: reasonId, className: "text-sm font-medium", children: reason.label }), _jsx("textarea", { id: reasonId, value: draft, rows: 4, disabled: pending, placeholder: reason.placeholder, "aria-invalid": error != null || undefined, "aria-describedby": error ? errorId : undefined, onChange: (event) => {
|
|
66
70
|
setDraft(event.target.value);
|
|
67
71
|
if (error)
|
|
68
72
|
setError(null);
|
|
69
|
-
}, className: "min-h-20 w-full rounded-lg border border-input bg-transparent px-2.5 py-2 text-sm outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:opacity-50 aria-invalid:border-destructive" })] })), error && (_jsx("p", { id: errorId, role: "alert", className: "rounded-lg border border-danger-border bg-danger-bg px-3 py-2 text-sm [overflow-wrap:anywhere] text-danger-fg", children: error })), _jsxs("div", { className: "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", children: [_jsx(AlertDialogPrimitive.Cancel, { asChild: true, children: _jsx(Button, { variant: "outline", disabled: pending, children: cancelLabel ?? t.cancel }) }), _jsx(AlertDialogPrimitive.Action, { asChild: true, children: _jsx(Button, { variant: destructive ? "destructive" : "default", disabled: pending || confirmDisabled, onClick: confirm, className: cn(pending && "cursor-progress"), children: pending ? t.working : (confirmLabel ?? t.confirm) }) })] })] })] }) }));
|
|
73
|
+
}, className: "min-h-20 w-full rounded-lg border border-input bg-transparent px-2.5 py-2 text-sm outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:opacity-50 aria-invalid:border-destructive" })] })), error && (_jsx("p", { id: errorId, role: "alert", className: "rounded-lg border border-danger-border bg-danger-bg px-3 py-2 text-sm [overflow-wrap:anywhere] text-danger-fg", children: error })), _jsxs("div", { "data-slot": "confirm-dialog-footer", className: "-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 sm:flex-row sm:justify-end", children: [_jsx(AlertDialogPrimitive.Cancel, { asChild: true, children: _jsx(Button, { variant: "outline", disabled: pending, children: cancelLabel ?? t.cancel }) }), _jsx(AlertDialogPrimitive.Action, { asChild: true, children: _jsx(Button, { variant: destructive ? "destructive" : "default", disabled: pending || confirmDisabled, onClick: confirm, className: cn(pending && "cursor-progress"), children: pending ? t.working : (confirmLabel ?? t.confirm) }) })] })] })] }) }));
|
|
70
74
|
}
|
|
@@ -3,7 +3,7 @@ import type * as React from "react";
|
|
|
3
3
|
export declare function Dialog(props: React.ComponentProps<typeof DialogPrimitive.Root>): React.JSX.Element;
|
|
4
4
|
export declare function DialogTrigger(props: React.ComponentProps<typeof DialogPrimitive.Trigger>): React.JSX.Element;
|
|
5
5
|
export declare function DialogClose(props: React.ComponentProps<typeof DialogPrimitive.Close>): React.JSX.Element;
|
|
6
|
-
/** The scrim behind a modal; shared with Drawer. */
|
|
6
|
+
/** The scrim behind a modal; shared with Drawer and ConfirmDialog, which lighten it. */
|
|
7
7
|
export declare function overlayClassName(className?: string): string;
|
|
8
8
|
/**
|
|
9
9
|
* A centred modal on the raised surface. Focus moves in on open and back to
|
package/dist/overlays/Dialog.js
CHANGED
|
@@ -13,9 +13,9 @@ export function DialogTrigger(props) {
|
|
|
13
13
|
export function DialogClose(props) {
|
|
14
14
|
return _jsx(DialogPrimitive.Close, { "data-slot": "dialog-close", ...props });
|
|
15
15
|
}
|
|
16
|
-
/** The scrim behind a modal; shared with Drawer. */
|
|
16
|
+
/** The scrim behind a modal; shared with Drawer and ConfirmDialog, which lighten it. */
|
|
17
17
|
export function overlayClassName(className) {
|
|
18
|
-
return cn("fixed inset-0 z-50 bg-black/
|
|
18
|
+
return cn("fixed inset-0 z-50 bg-black/30 data-[state=closed]:animate-cui-fade-out data-[state=open]:animate-cui-fade-in supports-backdrop-filter:backdrop-blur-xs", className);
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
21
|
* A centred modal on the raised surface. Focus moves in on open and back to
|
|
@@ -23,10 +23,10 @@ export function overlayClassName(className) {
|
|
|
23
23
|
*/
|
|
24
24
|
export function DialogContent({ className, children, hideCloseButton = false, ...props }) {
|
|
25
25
|
const { messages } = useUI();
|
|
26
|
-
return (_jsxs(DialogPrimitive.Portal, { children: [_jsx(DialogPrimitive.Overlay, { "data-slot": "dialog-overlay", className: overlayClassName() }), _jsxs(DialogPrimitive.Content, { "data-slot": "dialog-content", className: cn("fixed top-1/2 left-1/2 z-50 grid max-h-[calc(100dvh-2rem)] w-[calc(100%-2rem)] max-w-lg -translate-x-1/2 -translate-y-1/2 grid-cols-[minmax(0,1fr)] gap-4 overflow-y-auto rounded-
|
|
26
|
+
return (_jsxs(DialogPrimitive.Portal, { children: [_jsx(DialogPrimitive.Overlay, { "data-slot": "dialog-overlay", className: overlayClassName() }), _jsxs(DialogPrimitive.Content, { "data-slot": "dialog-content", className: cn("fixed top-1/2 left-1/2 z-50 grid max-h-[calc(100dvh-2rem)] w-[calc(100%-2rem)] max-w-lg -translate-x-1/2 -translate-y-1/2 grid-cols-[minmax(0,1fr)] gap-4 overflow-y-auto rounded-xl bg-background p-6 text-foreground shadow-lg ring-1 ring-foreground/10 outline-none data-[state=closed]:animate-cui-pop-out data-[state=open]:animate-cui-pop-in sm:max-w-xl", className), ...props, children: [children, !hideCloseButton && (_jsx(DialogPrimitive.Close, { "data-slot": "dialog-close-button", "aria-label": messages.dialog.close, className: "absolute top-3 right-3 rounded-lg p-1 text-muted-foreground transition-colors hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none", children: _jsx(XIcon, { className: "size-4", "aria-hidden": true }) }))] })] }));
|
|
27
27
|
}
|
|
28
28
|
export function DialogHeader({ className, ...props }) {
|
|
29
|
-
return (_jsx("div", { "data-slot": "dialog-header", className: cn("flex flex-col gap-1.5 pr-6", className), ...props }));
|
|
29
|
+
return (_jsx("div", { "data-slot": "dialog-header", className: cn("flex flex-col gap-1.5 pr-6 text-left", className), ...props }));
|
|
30
30
|
}
|
|
31
31
|
export function DialogFooter({ className, ...props }) {
|
|
32
32
|
return (_jsx("div", { "data-slot": "dialog-footer", className: cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className), ...props }));
|
|
@@ -4,9 +4,9 @@ export declare function Drawer(props: React.ComponentProps<typeof DrawerPrimitiv
|
|
|
4
4
|
export declare function DrawerTrigger(props: React.ComponentProps<typeof DrawerPrimitive.Trigger>): React.JSX.Element;
|
|
5
5
|
export declare function DrawerClose(props: React.ComponentProps<typeof DrawerPrimitive.Close>): React.JSX.Element;
|
|
6
6
|
declare const SIDE: {
|
|
7
|
-
readonly right: "inset-y-0 right-0 h-full w-3/4 border-l data-[state=open]:animate-cui-slide-in-right sm:max-w-sm";
|
|
8
|
-
readonly left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=open]:animate-cui-slide-in-left sm:max-w-sm";
|
|
9
|
-
readonly bottom: "inset-x-0 bottom-0 max-h-[85dvh] border-t data-[state=open]:animate-cui-slide-in-bottom";
|
|
7
|
+
readonly right: "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:animate-cui-slide-out-right data-[state=open]:animate-cui-slide-in-right sm:max-w-sm";
|
|
8
|
+
readonly left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:animate-cui-slide-out-left data-[state=open]:animate-cui-slide-in-left sm:max-w-sm";
|
|
9
|
+
readonly bottom: "inset-x-0 bottom-0 max-h-[85dvh] border-t data-[state=closed]:animate-cui-slide-out-bottom data-[state=open]:animate-cui-slide-in-bottom";
|
|
10
10
|
};
|
|
11
11
|
/**
|
|
12
12
|
* A modal panel from an edge: secondary content that should not cost a page
|
package/dist/overlays/Drawer.js
CHANGED
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { XIcon } from "lucide-react";
|
|
4
4
|
import { Dialog as DrawerPrimitive } from "radix-ui";
|
|
5
5
|
import { cn } from "../lib/cn.js";
|
|
6
|
+
import { Button } from "../primitives/Button.js";
|
|
6
7
|
import { useUI } from "../provider/UIProvider.js";
|
|
7
8
|
import { overlayClassName } from "./Dialog.js";
|
|
8
9
|
export function Drawer(props) {
|
|
@@ -15,9 +16,9 @@ export function DrawerClose(props) {
|
|
|
15
16
|
return _jsx(DrawerPrimitive.Close, { "data-slot": "drawer-close", ...props });
|
|
16
17
|
}
|
|
17
18
|
const SIDE = {
|
|
18
|
-
right: "inset-y-0 right-0 h-full w-3/4 border-l data-[state=open]:animate-cui-slide-in-right sm:max-w-sm",
|
|
19
|
-
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=open]:animate-cui-slide-in-left sm:max-w-sm",
|
|
20
|
-
bottom: "inset-x-0 bottom-0 max-h-[85dvh] border-t data-[state=open]:animate-cui-slide-in-bottom",
|
|
19
|
+
right: "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:animate-cui-slide-out-right data-[state=open]:animate-cui-slide-in-right sm:max-w-sm",
|
|
20
|
+
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:animate-cui-slide-out-left data-[state=open]:animate-cui-slide-in-left sm:max-w-sm",
|
|
21
|
+
bottom: "inset-x-0 bottom-0 max-h-[85dvh] border-t data-[state=closed]:animate-cui-slide-out-bottom data-[state=open]:animate-cui-slide-in-bottom",
|
|
21
22
|
};
|
|
22
23
|
/**
|
|
23
24
|
* A modal panel from an edge: secondary content that should not cost a page
|
|
@@ -26,7 +27,7 @@ const SIDE = {
|
|
|
26
27
|
*/
|
|
27
28
|
export function DrawerContent({ className, children, side = "right", hideCloseButton = false, ...props }) {
|
|
28
29
|
const { messages } = useUI();
|
|
29
|
-
return (_jsxs(DrawerPrimitive.Portal, { children: [_jsx(DrawerPrimitive.Overlay, { "data-slot": "drawer-overlay", className: overlayClassName() }), _jsxs(DrawerPrimitive.Content, { "data-slot": "drawer-content", "data-side": side, className: cn("fixed z-50 flex flex-col gap-4 overflow-y-auto border-border bg-background text-sm text-foreground shadow-lg outline-none", SIDE[side], className), ...props, children: [children, !hideCloseButton && (_jsx(DrawerPrimitive.Close, { "data-slot": "drawer-close-button", "aria-label": messages.dialog.close, className: "absolute top-3 right-3
|
|
30
|
+
return (_jsxs(DrawerPrimitive.Portal, { children: [_jsx(DrawerPrimitive.Overlay, { "data-slot": "drawer-overlay", className: overlayClassName("bg-black/10") }), _jsxs(DrawerPrimitive.Content, { "data-slot": "drawer-content", "data-side": side, className: cn("fixed z-50 flex flex-col gap-4 overflow-y-auto border-border bg-background text-sm text-foreground shadow-lg outline-none", SIDE[side], className), ...props, children: [children, !hideCloseButton && (_jsx(DrawerPrimitive.Close, { asChild: true, children: _jsx(Button, { "data-slot": "drawer-close-button", variant: "ghost", size: "icon-sm", "aria-label": messages.dialog.close, className: "absolute top-3 right-3", children: _jsx(XIcon, { "aria-hidden": true }) }) }))] })] }));
|
|
30
31
|
}
|
|
31
32
|
export function DrawerHeader({ className, ...props }) {
|
|
32
33
|
return (_jsx("div", { "data-slot": "drawer-header", className: cn("flex flex-col gap-1 p-4 pr-10", className), ...props }));
|
|
@@ -35,7 +36,7 @@ export function DrawerFooter({ className, ...props }) {
|
|
|
35
36
|
return (_jsx("div", { "data-slot": "drawer-footer", className: cn("mt-auto flex flex-col gap-2 p-4", className), ...props }));
|
|
36
37
|
}
|
|
37
38
|
export function DrawerTitle({ className, ...props }) {
|
|
38
|
-
return (_jsx(DrawerPrimitive.Title, { "data-slot": "drawer-title", className: cn("font-head text-base font-
|
|
39
|
+
return (_jsx(DrawerPrimitive.Title, { "data-slot": "drawer-title", className: cn("font-head text-base font-medium text-foreground", className), ...props }));
|
|
39
40
|
}
|
|
40
41
|
export function DrawerDescription({ className, ...props }) {
|
|
41
42
|
return (_jsx(DrawerPrimitive.Description, { "data-slot": "drawer-description", className: cn("text-sm text-muted-foreground", className), ...props }));
|
|
@@ -18,6 +18,12 @@ export interface PageHeaderProps {
|
|
|
18
18
|
tabs?: readonly TabNavItem[];
|
|
19
19
|
/** Names the tab row. Defaults to "Page". */
|
|
20
20
|
tabsLabel?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Classes for the tab row, merged over its default gutter
|
|
23
|
+
* (`-mx-3 md:-mx-6`). Match the page's padding: a page padded `p-6` at
|
|
24
|
+
* every width passes `-mx-6 px-6`.
|
|
25
|
+
*/
|
|
26
|
+
tabsClassName?: string;
|
|
21
27
|
className?: string;
|
|
22
28
|
}
|
|
23
29
|
/**
|
|
@@ -25,4 +31,4 @@ export interface PageHeaderProps {
|
|
|
25
31
|
* breadcrumb, the title row with its status, context and actions, and at
|
|
26
32
|
* most one row of tabs. Pure.
|
|
27
33
|
*/
|
|
28
|
-
export declare function PageHeader({ crumbs, title, status, context, description, primaryAction, menu, tabs, tabsLabel, className, }: PageHeaderProps): React.JSX.Element;
|
|
34
|
+
export declare function PageHeader({ crumbs, title, status, context, description, primaryAction, menu, tabs, tabsLabel, tabsClassName, className, }: PageHeaderProps): React.JSX.Element;
|
package/dist/page/PageHeader.js
CHANGED
|
@@ -8,6 +8,6 @@ import { TabNav } from "./TabNav.js";
|
|
|
8
8
|
* breadcrumb, the title row with its status, context and actions, and at
|
|
9
9
|
* most one row of tabs. Pure.
|
|
10
10
|
*/
|
|
11
|
-
export function PageHeader({ crumbs, title, status, context, description, primaryAction, menu, tabs, tabsLabel = "Page", className, }) {
|
|
12
|
-
return (_jsxs("header", { "data-slot": "page-header", className: cn("flex min-w-0 flex-col gap-3", className), children: [crumbs && crumbs.length > 0 && _jsx(Breadcrumbs, { crumbs: crumbs }), _jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-x-3 gap-y-2", children: [_jsx("h1", { className: "min-w-0 truncate font-head text-xl font-semibold", children: title }), status, context && (_jsx("span", { className: "min-w-0 truncate text-sm text-muted-foreground", children: context })), (primaryAction || menu) && (_jsxs("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: [primaryAction, menu] }))] }), description && (_jsx("div", { className: "max-w-2xl text-sm [overflow-wrap:anywhere] text-muted-foreground", children: description })), tabs && tabs.length > 0 && (_jsx(TabNav, { tabs: tabs, label: tabsLabel, className: "-mx-3 md:-mx-6" }))] }));
|
|
11
|
+
export function PageHeader({ crumbs, title, status, context, description, primaryAction, menu, tabs, tabsLabel = "Page", tabsClassName, className, }) {
|
|
12
|
+
return (_jsxs("header", { "data-slot": "page-header", className: cn("flex min-w-0 flex-col gap-3", className), children: [crumbs && crumbs.length > 0 && _jsx(Breadcrumbs, { crumbs: crumbs }), _jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-x-3 gap-y-2", children: [_jsx("h1", { className: "min-w-0 truncate font-head text-xl font-semibold", children: title }), status, context && (_jsx("span", { className: "min-w-0 truncate text-sm text-muted-foreground", children: context })), (primaryAction || menu) && (_jsxs("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: [primaryAction, menu] }))] }), description && (_jsx("div", { className: "max-w-2xl text-sm [overflow-wrap:anywhere] text-muted-foreground", children: description })), tabs && tabs.length > 0 && (_jsx(TabNav, { tabs: tabs, label: tabsLabel, className: cn("-mx-3 md:-mx-6", tabsClassName) }))] }));
|
|
13
13
|
}
|
|
@@ -7,13 +7,13 @@ import { cn } from "../lib/cn.js";
|
|
|
7
7
|
* corners setting (`rounded-lg` is `--radius`), so a 0px or 10px choice
|
|
8
8
|
* reaches buttons as it reaches panels.
|
|
9
9
|
*/
|
|
10
|
-
export const buttonVariants = cva("group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-colors outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
|
|
10
|
+
export const buttonVariants = cva("group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-colors outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
|
|
11
11
|
variants: {
|
|
12
12
|
variant: {
|
|
13
13
|
default: "bg-primary text-primary-foreground hover:bg-primary/85",
|
|
14
|
-
outline: "border-border bg-
|
|
14
|
+
outline: "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
|
|
15
15
|
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary",
|
|
16
|
-
ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground",
|
|
16
|
+
ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
|
|
17
17
|
destructive: "bg-danger/10 text-danger-fg hover:bg-danger/15 focus-visible:border-danger-border focus-visible:ring-danger/20",
|
|
18
18
|
link: "text-primary underline-offset-4 hover:underline",
|
|
19
19
|
},
|
|
@@ -32,5 +32,5 @@ export const buttonVariants = cva("group/button inline-flex shrink-0 items-cente
|
|
|
32
32
|
});
|
|
33
33
|
export function Button({ className, variant, size, asChild = false, ...props }) {
|
|
34
34
|
const Component = asChild ? Slot.Root : "button";
|
|
35
|
-
return (_jsx(Component, { "data-slot": "button", "data-variant": variant ?? "default", className: cn(buttonVariants({ variant, size }), className), ...props }));
|
|
35
|
+
return (_jsx(Component, { "data-slot": "button", "data-variant": variant ?? "default", "data-size": size ?? "default", className: cn(buttonVariants({ variant, size }), className), ...props }));
|
|
36
36
|
}
|
|
@@ -9,5 +9,5 @@ import { cn } from "../lib/cn.js";
|
|
|
9
9
|
* state cannot be expressed in JSX on a native box.
|
|
10
10
|
*/
|
|
11
11
|
export function Checkbox({ className, ...props }) {
|
|
12
|
-
return (_jsx(CheckboxPrimitive.Root, { "data-slot": "checkbox", className: cn("peer size-4 shrink-0 rounded-sm border border-input transition-shadow outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground", className), ...props, children: _jsx(CheckboxPrimitive.Indicator, { "data-slot": "checkbox-indicator", className: "flex items-center justify-center text-current", children: props.checked === "indeterminate" ? (_jsx(MinusIcon, { className: "size-3.5", "aria-hidden": true })) : (_jsx(CheckIcon, { className: "size-3.5", "aria-hidden": true })) }) }));
|
|
12
|
+
return (_jsx(CheckboxPrimitive.Root, { "data-slot": "checkbox", className: cn("peer size-4 shrink-0 rounded-sm border border-input shadow-xs transition-shadow outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground dark:bg-input/30", className), ...props, children: _jsx(CheckboxPrimitive.Indicator, { "data-slot": "checkbox-indicator", className: "flex items-center justify-center text-current transition-none", children: props.checked === "indeterminate" ? (_jsx(MinusIcon, { className: "size-3.5", "aria-hidden": true })) : (_jsx(CheckIcon, { className: "size-3.5", "aria-hidden": true })) }) }));
|
|
13
13
|
}
|
|
@@ -7,5 +7,8 @@ export declare function DropdownMenuGroup(props: React.ComponentProps<typeof Dro
|
|
|
7
7
|
export declare function DropdownMenuItem({ className, variant, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
|
|
8
8
|
variant?: "default" | "destructive";
|
|
9
9
|
}): React.JSX.Element;
|
|
10
|
+
export declare function DropdownMenuCheckboxItem({ className, children, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>): React.JSX.Element;
|
|
11
|
+
export declare function DropdownMenuRadioGroup(props: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>): React.JSX.Element;
|
|
12
|
+
export declare function DropdownMenuRadioItem({ className, children, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>): React.JSX.Element;
|
|
10
13
|
export declare function DropdownMenuLabel({ className, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Label>): React.JSX.Element;
|
|
11
14
|
export declare function DropdownMenuSeparator({ className, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>): React.JSX.Element;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { CheckIcon } from "lucide-react";
|
|
3
4
|
import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui";
|
|
4
5
|
import { cn } from "../lib/cn.js";
|
|
5
6
|
export function DropdownMenu(props) {
|
|
@@ -17,6 +18,16 @@ export function DropdownMenuGroup(props) {
|
|
|
17
18
|
export function DropdownMenuItem({ className, variant = "default", ...props }) {
|
|
18
19
|
return (_jsx(DropdownMenuPrimitive.Item, { "data-slot": "dropdown-menu-item", "data-variant": variant, className: cn("relative flex cursor-default items-center gap-1.5 rounded-lg px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 data-[variant=destructive]:text-danger-fg data-[variant=destructive]:focus:bg-danger/10 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className), ...props }));
|
|
19
20
|
}
|
|
21
|
+
const selectableItemClass = "relative flex cursor-default items-center gap-1.5 rounded-lg py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4";
|
|
22
|
+
export function DropdownMenuCheckboxItem({ className, children, ...props }) {
|
|
23
|
+
return (_jsxs(DropdownMenuPrimitive.CheckboxItem, { "data-slot": "dropdown-menu-checkbox-item", className: cn(selectableItemClass, className), ...props, children: [_jsx("span", { "data-slot": "dropdown-menu-checkbox-item-indicator", className: "pointer-events-none absolute right-2 flex items-center justify-center", children: _jsx(DropdownMenuPrimitive.ItemIndicator, { children: _jsx(CheckIcon, { "aria-hidden": true }) }) }), children] }));
|
|
24
|
+
}
|
|
25
|
+
export function DropdownMenuRadioGroup(props) {
|
|
26
|
+
return _jsx(DropdownMenuPrimitive.RadioGroup, { "data-slot": "dropdown-menu-radio-group", ...props });
|
|
27
|
+
}
|
|
28
|
+
export function DropdownMenuRadioItem({ className, children, ...props }) {
|
|
29
|
+
return (_jsxs(DropdownMenuPrimitive.RadioItem, { "data-slot": "dropdown-menu-radio-item", className: cn(selectableItemClass, className), ...props, children: [_jsx("span", { "data-slot": "dropdown-menu-radio-item-indicator", className: "pointer-events-none absolute right-2 flex items-center justify-center", children: _jsx(DropdownMenuPrimitive.ItemIndicator, { children: _jsx(CheckIcon, { "aria-hidden": true }) }) }), children] }));
|
|
30
|
+
}
|
|
20
31
|
export function DropdownMenuLabel({ className, ...props }) {
|
|
21
32
|
return (_jsx(DropdownMenuPrimitive.Label, { "data-slot": "dropdown-menu-label", className: cn("px-1.5 py-1 text-xs font-medium text-muted-foreground", className), ...props }));
|
|
22
33
|
}
|
package/dist/primitives/Input.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from "../lib/cn.js";
|
|
3
3
|
export function Input({ className, type, ...props }) {
|
|
4
|
-
return (_jsx("input", { type: type, "data-slot": "input", className: cn("h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm", className), ...props }));
|
|
4
|
+
return (_jsx("input", { type: type, "data-slot": "input", className: cn("h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", className), ...props }));
|
|
5
5
|
}
|
|
@@ -3,3 +3,5 @@ import type * as React from "react";
|
|
|
3
3
|
export declare function Popover(props: React.ComponentProps<typeof PopoverPrimitive.Root>): React.JSX.Element;
|
|
4
4
|
export declare function PopoverTrigger(props: React.ComponentProps<typeof PopoverPrimitive.Trigger>): React.JSX.Element;
|
|
5
5
|
export declare function PopoverContent({ className, align, sideOffset, ...props }: React.ComponentProps<typeof PopoverPrimitive.Content>): React.JSX.Element;
|
|
6
|
+
export declare function PopoverAnchor(props: React.ComponentProps<typeof PopoverPrimitive.Anchor>): React.JSX.Element;
|
|
7
|
+
export declare function PopoverHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
@@ -11,3 +11,9 @@ export function PopoverTrigger(props) {
|
|
|
11
11
|
export function PopoverContent({ className, align = "center", sideOffset = 4, ...props }) {
|
|
12
12
|
return (_jsx(PopoverPrimitive.Portal, { children: _jsx(PopoverPrimitive.Content, { "data-slot": "popover-content", align: align, sideOffset: sideOffset, className: cn("z-50 flex w-72 origin-(--radix-popover-content-transform-origin) flex-col gap-2 rounded-lg bg-popover p-2.5 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden data-[state=open]:animate-cui-fade-in", className), ...props }) }));
|
|
13
13
|
}
|
|
14
|
+
export function PopoverAnchor(props) {
|
|
15
|
+
return _jsx(PopoverPrimitive.Anchor, { "data-slot": "popover-anchor", ...props });
|
|
16
|
+
}
|
|
17
|
+
export function PopoverHeader({ className, ...props }) {
|
|
18
|
+
return (_jsx("div", { "data-slot": "popover-header", className: cn("flex flex-col gap-0.5 text-sm", className), ...props }));
|
|
19
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ProductTheme } from "../appearance/product.js";
|
|
1
2
|
/**
|
|
2
3
|
* Every string the package renders, in English. An app with a message
|
|
3
4
|
* catalog overrides any of them through `UIProvider messages={...}`; a
|
|
@@ -34,6 +35,7 @@ export interface Messages {
|
|
|
34
35
|
search: string;
|
|
35
36
|
clearSearch: string;
|
|
36
37
|
results: (count: number) => string;
|
|
38
|
+
filteredResults: string;
|
|
37
39
|
loading: string;
|
|
38
40
|
loadFailed: (label: string) => string;
|
|
39
41
|
requestFailed: string;
|
|
@@ -83,6 +85,10 @@ export interface Messages {
|
|
|
83
85
|
density: string;
|
|
84
86
|
corners: string;
|
|
85
87
|
reset: string;
|
|
88
|
+
/** A named theme's title; the default is the product's own English name. */
|
|
89
|
+
themeName: (theme: ProductTheme) => string;
|
|
90
|
+
/** A named theme's one-line hint; the default is the product's own English hint. */
|
|
91
|
+
themeHint: (theme: ProductTheme) => string;
|
|
86
92
|
mode: (mode: "dark" | "light") => string;
|
|
87
93
|
groundLabel: (ground: string, label: string) => string;
|
|
88
94
|
accentLabel: (accent: string, label: string) => string;
|
|
@@ -28,6 +28,7 @@ export const defaultMessages = {
|
|
|
28
28
|
search: "Search",
|
|
29
29
|
clearSearch: "Clear search",
|
|
30
30
|
results: (n) => (n === 1 ? "1 result" : `${count(n)} results`),
|
|
31
|
+
filteredResults: "Filtered results shown.",
|
|
31
32
|
loading: "Loading",
|
|
32
33
|
loadFailed: (label) => `Could not load ${lower(label)}`,
|
|
33
34
|
requestFailed: "The request failed.",
|
|
@@ -76,6 +77,8 @@ export const defaultMessages = {
|
|
|
76
77
|
density: "Density",
|
|
77
78
|
corners: "Corners",
|
|
78
79
|
reset: "Use the default",
|
|
80
|
+
themeName: (theme) => theme.name,
|
|
81
|
+
themeHint: (theme) => theme.hint,
|
|
79
82
|
mode: (mode) => mode,
|
|
80
83
|
groundLabel: (_ground, label) => label,
|
|
81
84
|
accentLabel: (_accent, label) => label,
|
package/dist/shell/RailTree.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export interface RailTreeItem {
|
|
|
9
9
|
status?: StatusTone;
|
|
10
10
|
/** Read with the status dot, which carries no text of its own ("failing"). */
|
|
11
11
|
statusLabel?: string;
|
|
12
|
+
/** In flight (pending, provisioning): the status dot pulses, unless the viewer prefers reduced motion. */
|
|
13
|
+
statusPulse?: boolean;
|
|
12
14
|
}
|
|
13
15
|
export interface RailTreeGroup {
|
|
14
16
|
key: string;
|
package/dist/shell/RailTree.js
CHANGED
|
@@ -27,6 +27,6 @@ function Branch({ group, activeHref }) {
|
|
|
27
27
|
const isActive = item.href === activeHref;
|
|
28
28
|
return (_jsx("li", { children: _jsxs(Link, { href: item.href, "aria-current": isActive ? "page" : undefined, className: cn("flex h-7 min-w-0 items-center gap-2 rounded-lg px-1.5 text-sm focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none", isActive
|
|
29
29
|
? "bg-primary/15 font-medium text-sidebar-foreground"
|
|
30
|
-
: "text-sidebar-foreground/80 hover:bg-sidebar-accent/60"), children: [Icon && _jsx(Icon, { className: "size-3.5 shrink-0 opacity-70", "aria-hidden": true }), _jsx("span", { className: "truncate", title: item.label, children: item.label }), " ", item.status && (_jsx(StatusDot, { tone: item.status, label: item.statusLabel, className: "ml-auto" }))] }) }, item.key));
|
|
30
|
+
: "text-sidebar-foreground/80 hover:bg-sidebar-accent/60"), children: [Icon && _jsx(Icon, { className: "size-3.5 shrink-0 opacity-70", "aria-hidden": true }), _jsx("span", { className: "truncate", title: item.label, children: item.label }), " ", item.status && (_jsx(StatusDot, { tone: item.status, pulse: item.statusPulse, label: item.statusLabel, className: "ml-auto" }))] }) }, item.key));
|
|
31
31
|
}) })] }));
|
|
32
32
|
}
|
|
@@ -10,6 +10,6 @@ export function EmptyState({ icon, title, description, actionHref, actionLabel,
|
|
|
10
10
|
const learnMoreText = learnMoreLabel ?? messages.states.learnMore;
|
|
11
11
|
const external = learnMoreHref ? /^https?:\/\//.test(learnMoreHref) : false;
|
|
12
12
|
const learnMoreClass = "mt-2 inline-flex items-center gap-1 text-xs text-muted-foreground underline-offset-2 hover:text-foreground hover:underline";
|
|
13
|
-
return (_jsxs("div", { "data-slot": "empty-state", className: cn("flex flex-col items-center justify-center gap-3 rounded-lg border border-dashed px-6 py-12 text-center whitespace-normal", className), children: [icon && (_jsx("div", { "aria-hidden": true, className: "rounded-lg bg-muted p-2 text-muted-foreground [&_svg]:size-5", children: icon })), _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "font-medium [overflow-wrap:anywhere]", children: title }), description && (_jsx("p", { className: "mx-auto mt-1 max-w-sm text-sm leading-relaxed text-muted-foreground", children: description })), learnMoreHref &&
|
|
13
|
+
return (_jsxs("div", { "data-slot": "empty-state", className: cn("flex flex-col items-center justify-center gap-3 rounded-lg border border-dashed px-6 py-12 text-center whitespace-normal", className), children: [icon && (_jsx("div", { "aria-hidden": true, className: "rounded-lg bg-muted p-2 text-muted-foreground [&_svg:not([class*='size-'])]:size-5", children: icon })), _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "font-medium [overflow-wrap:anywhere]", children: title }), description && (_jsx("p", { className: "mx-auto mt-1 max-w-sm text-sm leading-relaxed text-muted-foreground", children: description })), learnMoreHref &&
|
|
14
14
|
(external ? (_jsxs("a", { href: learnMoreHref, target: "_blank", rel: "noreferrer", className: learnMoreClass, children: [learnMoreText, _jsx(ExternalLinkIcon, { className: "size-3", "aria-hidden": true })] })) : (_jsx(Link, { href: learnMoreHref, className: learnMoreClass, children: learnMoreText })))] }), actionHref && actionLabel && (_jsx(Button, { asChild: true, size: "sm", children: _jsx(Link, { href: actionHref, children: actionLabel }) })), secondary] }));
|
|
15
15
|
}
|
package/dist/styles/preset.css
CHANGED
|
@@ -94,12 +94,20 @@
|
|
|
94
94
|
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
|
95
95
|
--ease-emphasized: cubic-bezier(0.3, 0, 0, 1);
|
|
96
96
|
|
|
97
|
-
/* Overlays arrive calmly and leave
|
|
97
|
+
/* Overlays arrive calmly and leave quickly. */
|
|
98
98
|
--animate-cui-fade-in: cui-fade-in var(--motion-fast) var(--ease-standard);
|
|
99
99
|
--animate-cui-pop-in: cui-pop-in var(--motion-base) var(--ease-emphasized);
|
|
100
100
|
--animate-cui-slide-in-right: cui-slide-in-right var(--motion-base) var(--ease-emphasized);
|
|
101
101
|
--animate-cui-slide-in-left: cui-slide-in-left var(--motion-base) var(--ease-emphasized);
|
|
102
102
|
--animate-cui-slide-in-bottom: cui-slide-in-bottom var(--motion-base) var(--ease-emphasized);
|
|
103
|
+
/* Exits hold their last frame so nothing flashes back before Radix unmounts. */
|
|
104
|
+
--animate-cui-fade-out: cui-fade-out var(--motion-fast) var(--ease-standard) forwards;
|
|
105
|
+
--animate-cui-pop-out: cui-pop-out var(--motion-fast) var(--ease-standard) forwards;
|
|
106
|
+
--animate-cui-slide-out-right: cui-slide-out-right var(--motion-fast) var(--ease-standard)
|
|
107
|
+
forwards;
|
|
108
|
+
--animate-cui-slide-out-left: cui-slide-out-left var(--motion-fast) var(--ease-standard) forwards;
|
|
109
|
+
--animate-cui-slide-out-bottom: cui-slide-out-bottom var(--motion-fast) var(--ease-standard)
|
|
110
|
+
forwards;
|
|
103
111
|
/* A table's rows being replaced: a short bar travelling along the top edge. */
|
|
104
112
|
--animate-cui-progress: cui-progress 1.4s var(--ease-standard) infinite;
|
|
105
113
|
|
|
@@ -141,6 +149,35 @@
|
|
|
141
149
|
opacity: 0;
|
|
142
150
|
}
|
|
143
151
|
}
|
|
152
|
+
@keyframes cui-fade-out {
|
|
153
|
+
to {
|
|
154
|
+
opacity: 0;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
@keyframes cui-pop-out {
|
|
158
|
+
to {
|
|
159
|
+
opacity: 0;
|
|
160
|
+
transform: scale(0.98);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
@keyframes cui-slide-out-right {
|
|
164
|
+
to {
|
|
165
|
+
transform: translateX(1.5rem);
|
|
166
|
+
opacity: 0;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
@keyframes cui-slide-out-left {
|
|
170
|
+
to {
|
|
171
|
+
transform: translateX(-1.5rem);
|
|
172
|
+
opacity: 0;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
@keyframes cui-slide-out-bottom {
|
|
176
|
+
to {
|
|
177
|
+
transform: translateY(1.5rem);
|
|
178
|
+
opacity: 0;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
144
181
|
}
|
|
145
182
|
|
|
146
183
|
@layer base {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@calliopelabs/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Calliope AI shared operator UI: app shell, navigation, page frame, data table and list patterns, status colours, design tokens and appearance for Astrolift AI and Zentinelle AI.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"calliope-ai",
|