@ciridae/ui 1.0.3 → 1.0.5
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/chat/conversation-source.d.ts +15 -1
- package/dist/chat/describe-tool-call.d.ts +3 -3
- package/dist/chat/describe-tool-call.js +3 -3
- package/dist/chat/message-text.js +2 -2
- package/dist/ciridae.css +16 -4
- package/dist/components/activity-feed.d.ts +1 -0
- package/dist/components/activity-feed.js +1 -0
- package/dist/components/app-frame.d.ts +13 -1
- package/dist/components/app-frame.js +45 -3
- package/dist/components/app-navigation.d.ts +28 -1
- package/dist/components/app-navigation.js +110 -14
- package/dist/components/badge.d.ts +29 -4
- package/dist/components/badge.js +134 -15
- package/dist/components/bulk-action-bar.js +3 -3
- package/dist/components/button.d.ts +10 -3
- package/dist/components/button.js +77 -44
- package/dist/components/calendar.js +44 -17
- package/dist/components/centered-page.js +2 -2
- package/dist/components/chart.d.ts +1 -1
- package/dist/components/chart.js +16 -11
- package/dist/components/chat-client-source.js +47 -6
- package/dist/components/chat-sheet-controller.d.ts +1 -0
- package/dist/components/chat-sheet-controller.js +1 -0
- package/dist/components/chat-sheet.d.ts +10 -2
- package/dist/components/chat-sheet.js +74 -37
- package/dist/components/chat.d.ts +90 -14
- package/dist/components/chat.js +401 -101
- package/dist/components/checkbox-item.d.ts +5 -2
- package/dist/components/checkbox-item.js +9 -5
- package/dist/components/chip.d.ts +5 -0
- package/dist/components/chip.js +27 -1
- package/dist/components/column-settings.js +1 -1
- package/dist/components/command-menu.d.ts +5 -1
- package/dist/components/command-menu.js +98 -32
- package/dist/components/command-palette.d.ts +6 -2
- package/dist/components/command-palette.js +20 -10
- package/dist/components/data-display.d.ts +35 -4
- package/dist/components/data-display.js +138 -19
- package/dist/components/data-grid.d.ts +14 -2
- package/dist/components/data-grid.js +61 -26
- package/dist/components/date-range-picker.js +9 -20
- package/dist/components/drawer.d.ts +5 -0
- package/dist/components/drawer.js +91 -54
- package/dist/components/editable-field.d.ts +17 -3
- package/dist/components/editable-field.js +57 -27
- package/dist/components/email-input.js +6 -5
- package/dist/components/environment-frame.d.ts +1 -1
- package/dist/components/environment-frame.js +37 -6
- package/dist/components/filter-bar.js +39 -28
- package/dist/components/form-controls.d.ts +9 -0
- package/dist/components/form-controls.js +46 -22
- package/dist/components/form-layout.d.ts +7 -3
- package/dist/components/form-layout.js +68 -23
- package/dist/components/glass-menu.d.ts +3 -0
- package/dist/components/glass-menu.js +63 -0
- package/dist/components/gradient-edge.d.ts +9 -0
- package/dist/components/gradient-edge.js +31 -0
- package/dist/components/input.d.ts +4 -2
- package/dist/components/input.js +26 -15
- package/dist/components/keycap.d.ts +9 -5
- package/dist/components/keycap.js +57 -19
- package/dist/components/lightbox.js +2 -1
- package/dist/components/locale.d.ts +10 -0
- package/dist/components/locale.js +18 -2
- package/dist/components/log-entry.d.ts +40 -0
- package/dist/components/log-entry.js +146 -0
- package/dist/components/mention-input.js +4 -3
- package/dist/components/menu.d.ts +6 -2
- package/dist/components/menu.js +34 -37
- package/dist/components/meter.d.ts +9 -1
- package/dist/components/meter.js +64 -6
- package/dist/components/navigation.d.ts +21 -4
- package/dist/components/navigation.js +84 -19
- package/dist/components/notification-tray.js +5 -4
- package/dist/components/overlays.d.ts +3 -1
- package/dist/components/overlays.js +69 -40
- package/dist/components/page-frame.js +1 -1
- package/dist/components/panel.d.ts +1 -1
- package/dist/components/panel.js +18 -12
- package/dist/components/radio-card.js +10 -9
- package/dist/components/record.d.ts +25 -1
- package/dist/components/record.js +147 -18
- package/dist/components/segmented-bar.d.ts +3 -1
- package/dist/components/segmented-bar.js +16 -6
- package/dist/components/select.d.ts +5 -2
- package/dist/components/select.js +103 -127
- package/dist/components/shell-skeleton.js +1 -1
- package/dist/components/shell.d.ts +2 -2
- package/dist/components/shell.js +43 -45
- package/dist/components/sidebar.d.ts +7 -2
- package/dist/components/sidebar.js +55 -27
- package/dist/components/single-line-text.d.ts +2 -0
- package/dist/components/single-line-text.js +16 -0
- package/dist/components/site-banner.d.ts +5 -2
- package/dist/components/site-banner.js +17 -6
- package/dist/components/skeleton.d.ts +7 -6
- package/dist/components/skeleton.js +28 -16
- package/dist/components/skeletons.d.ts +12 -0
- package/dist/components/skeletons.js +71 -8
- package/dist/components/stages.d.ts +78 -6
- package/dist/components/stages.js +512 -156
- package/dist/components/stat-card.d.ts +10 -2
- package/dist/components/stat-card.js +92 -37
- package/dist/components/status-banner.js +1 -1
- package/dist/components/tabs.js +26 -13
- package/dist/components/theme-toggle.d.ts +4 -0
- package/dist/components/theme-toggle.js +67 -0
- package/dist/components/toast.d.ts +9 -2
- package/dist/components/toast.js +78 -29
- package/dist/preset.js +291 -33
- package/dist/styled-system/styles.css +3107 -1039
- package/dist/styled-system/tokens/index.mjs +602 -22
- package/dist/styled-system/tokens/tokens.d.ts +11 -11
- package/dist/styled-system/types/prop-type.d.ts +2 -2
- package/dist/styles.css +16 -4
- package/dist/theme/color.d.ts +3 -0
- package/dist/theme/color.js +5 -0
- package/dist/theme/derive.d.ts +9 -2
- package/dist/theme/derive.js +72 -12
- package/dist/theme/palettes.d.ts +49 -6
- package/dist/theme/palettes.js +84 -15
- package/dist/theme/tokens.d.ts +3 -3
- package/dist/theme/tokens.js +108 -32
- package/package.json +9 -1
package/dist/components/menu.js
CHANGED
|
@@ -3,21 +3,14 @@ import { Menu as BaseMenu } from "@base-ui/react/menu";
|
|
|
3
3
|
import { CaretRightIcon, CheckIcon, DotsThreeIcon } from "@phosphor-icons/react";
|
|
4
4
|
import { css, cx } from "#primitives/css";
|
|
5
5
|
import { IconButton } from "./button.js";
|
|
6
|
+
import { glassMenuDivider, glassMenuRow, glassMenuSurface } from "./glass-menu.js";
|
|
6
7
|
import { HotkeyLayer } from "./hotkeys.js";
|
|
7
8
|
import { directional, icon, Spinner } from "./icon.js";
|
|
8
9
|
import { ShortcutHint } from "./keycap.js";
|
|
9
|
-
|
|
10
|
-
const popup = css({
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
overflow: "hidden",
|
|
14
|
-
border: "1px solid token(colors.border.control)",
|
|
15
|
-
borderRadius: "action",
|
|
16
|
-
background: "bg.card",
|
|
17
|
-
color: "fg.primary",
|
|
18
|
-
textStyle: "body",
|
|
19
|
-
boxShadow: "card",
|
|
20
|
-
});
|
|
10
|
+
import { singleLineTextClass } from "./single-line-text.js";
|
|
11
|
+
const popup = css(glassMenuSurface, { minWidth: "overlay.menu" });
|
|
12
|
+
// As wide as its trigger, such as a switcher whose menu hangs under a full-width button.
|
|
13
|
+
const triggerWidthPopup = css(glassMenuSurface, { width: "var(--anchor-width)" });
|
|
21
14
|
// A submenu joins its parent: the corner it opens from goes square, so the two read as one panel.
|
|
22
15
|
const submenuPopup = css({
|
|
23
16
|
"&:is([data-side=right], [data-side=inline-end])": { borderStartStartRadius: "0" },
|
|
@@ -25,28 +18,34 @@ const submenuPopup = css({
|
|
|
25
18
|
});
|
|
26
19
|
/** The positioner is the positioned element, so it carries the stacking order. */
|
|
27
20
|
const positioner = css({ zIndex: "popover" });
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
lineHeight: "body",
|
|
36
|
-
cursor: "pointer",
|
|
37
|
-
outline: "none",
|
|
38
|
-
"&[data-highlighted], &[data-popup-open]": { background: "bg.rowHover" },
|
|
39
|
-
"&[data-disabled]": { opacity: "disabled", cursor: "not-allowed" },
|
|
21
|
+
// A submenu's row stays lit while its submenu is open. A label stays on the row's one line, as the
|
|
22
|
+
// design's 32px rows set it, and ends in an ellipsis in a menu held to its trigger's width.
|
|
23
|
+
const itemStyle = css.raw(glassMenuRow, {
|
|
24
|
+
"&[data-popup-open]": {
|
|
25
|
+
background: "bg.hover",
|
|
26
|
+
boxShadow: "inset 0 0 0 1px token(colors.border.menuHover)",
|
|
27
|
+
},
|
|
40
28
|
"& > [data-label]": { flex: "1", minWidth: "0" },
|
|
41
29
|
"& > [data-trailing]": { color: "fg.muted" },
|
|
42
30
|
});
|
|
43
|
-
const
|
|
31
|
+
const item = css(itemStyle);
|
|
32
|
+
const dangerItem = css(itemStyle, { color: "accent.red" });
|
|
33
|
+
// Every icon sits in one 14px slot, so labels line up whatever the icon's own size. A wider mark,
|
|
34
|
+
// such as an avatar, widens its slot instead of spilling into the gap before the label.
|
|
35
|
+
const iconSlot = css({
|
|
36
|
+
display: "inline-grid",
|
|
37
|
+
placeItems: "center",
|
|
38
|
+
flexShrink: "0",
|
|
39
|
+
minWidth: "mark.inline",
|
|
40
|
+
minHeight: "mark.inline",
|
|
41
|
+
fontSize: "icon.small",
|
|
42
|
+
});
|
|
44
43
|
/* Not an item: a row that names what the menu belongs to, such as the signed-in account. */
|
|
45
44
|
const header = css({
|
|
46
45
|
display: "flex",
|
|
47
46
|
alignItems: "center",
|
|
48
47
|
gap: "layout.controlGap",
|
|
49
|
-
paddingInline: "
|
|
48
|
+
paddingInline: "menu.itemInline",
|
|
50
49
|
paddingBlock: "control.compactBlock",
|
|
51
50
|
fontSize: "caption",
|
|
52
51
|
lineHeight: "compact",
|
|
@@ -64,7 +63,7 @@ const groupHeading = css({
|
|
|
64
63
|
display: "flex",
|
|
65
64
|
alignItems: "center",
|
|
66
65
|
minHeight: "menu.item",
|
|
67
|
-
paddingInline: "
|
|
66
|
+
paddingInline: "menu.itemInline",
|
|
68
67
|
color: "fg.muted",
|
|
69
68
|
fontSize: "code",
|
|
70
69
|
fontWeight: "medium",
|
|
@@ -72,18 +71,16 @@ const groupHeading = css({
|
|
|
72
71
|
cursor: "default",
|
|
73
72
|
});
|
|
74
73
|
const itemMeta = css({
|
|
74
|
+
flexShrink: "1",
|
|
75
75
|
minWidth: "0",
|
|
76
76
|
overflow: "hidden",
|
|
77
77
|
textOverflow: "ellipsis",
|
|
78
78
|
whiteSpace: "nowrap",
|
|
79
|
-
|
|
80
|
-
});
|
|
81
|
-
const separator = css({
|
|
82
|
-
height: "border.hairline",
|
|
83
|
-
background: "border.subtle",
|
|
79
|
+
fontSize: "caption",
|
|
84
80
|
});
|
|
85
|
-
|
|
86
|
-
|
|
81
|
+
const separator = css(glassMenuDivider);
|
|
82
|
+
export function Menu({ trigger, items, align = "start", sideOffset = 4, matchTriggerWidth = false, ...rootProps }) {
|
|
83
|
+
return (_jsxs(BaseMenu.Root, { ...rootProps, children: [_jsx(BaseMenu.Trigger, { render: trigger }), _jsx(BaseMenu.Portal, { children: _jsx(HotkeyLayer, { children: _jsx(BaseMenu.Positioner, { className: positioner, sideOffset: sideOffset, align: align, children: _jsx(BaseMenu.Popup, { className: matchTriggerWidth ? triggerWidthPopup : popup, "data-ciridae-ui": "", children: _jsx(MenuItems, { items: items }) }) }) }) })] }));
|
|
87
84
|
}
|
|
88
85
|
/** The "more" button: an `IconButton` with three dots that opens a `Menu` of `items`. */
|
|
89
86
|
export function OverflowMenu({ label, size = "medium", variant = "raised", align = "end", disabled, ref, ...props }) {
|
|
@@ -100,10 +97,10 @@ function MenuItems({ items }) {
|
|
|
100
97
|
return (_jsxs("div", { className: header, role: "presentation", children: [menuItem.icon, _jsxs("span", { className: headerCopy, children: [_jsx("span", { children: menuItem.label }), menuItem.detail && _jsx("span", { children: menuItem.detail })] })] }, index));
|
|
101
98
|
}
|
|
102
99
|
if (menuItem.items) {
|
|
103
|
-
return (_jsxs(BaseMenu.SubmenuRoot, { children: [_jsxs(BaseMenu.SubmenuTrigger, { className: item, disabled: menuItem.disabled, children: [menuItem.icon, _jsx("span", { "data-label": "", children: menuItem.label }), _jsx(CaretRightIcon, { "data-trailing": "", className: cx(icon({ size: "
|
|
100
|
+
return (_jsxs(BaseMenu.SubmenuRoot, { children: [_jsxs(BaseMenu.SubmenuTrigger, { className: item, disabled: menuItem.disabled, children: [menuItem.icon && _jsx("span", { className: iconSlot, children: menuItem.icon }), _jsx("span", { "data-label": "", className: singleLineTextClass, children: menuItem.label }), _jsx(CaretRightIcon, { "data-trailing": "", className: cx(icon({ size: "small" }), directional) })] }), _jsx(BaseMenu.Portal, { children: _jsx(BaseMenu.Positioner, { className: positioner, sideOffset: 0, align: "start", alignOffset: -5, children: _jsx(BaseMenu.Popup, { className: cx(popup, submenuPopup), "data-ciridae-ui": "", children: _jsx(MenuItems, { items: menuItem.items }) }) }) })] }, menuItem.label));
|
|
104
101
|
}
|
|
105
|
-
const content = (_jsxs(_Fragment, { children: [menuItem.loading ? _jsx(Spinner, { "aria-hidden": "true" }) : menuItem.icon,
|
|
106
|
-
const className =
|
|
102
|
+
const content = (_jsxs(_Fragment, { children: [(menuItem.loading || menuItem.icon) && (_jsx("span", { className: iconSlot, children: menuItem.loading ? _jsx(Spinner, { "aria-hidden": "true" }) : menuItem.icon })), _jsx("span", { "data-label": "", className: singleLineTextClass, children: menuItem.label }), menuItem.meta !== undefined && (_jsx("span", { "data-trailing": "", className: itemMeta, children: menuItem.meta })), menuItem.shortcut && _jsx(ShortcutHint, { "data-trailing": "", boxed: true, hotkey: menuItem.shortcut }), menuItem.checked && _jsx(CheckIcon, { "data-trailing": "", className: icon({ size: "small" }) })] }));
|
|
103
|
+
const className = menuItem.variant === "danger" ? dangerItem : item;
|
|
107
104
|
if (menuItem.href !== undefined || menuItem.render !== undefined) {
|
|
108
105
|
return (_jsx(BaseMenu.LinkItem, { className: className, href: menuItem.href, render: menuItem.render, closeOnClick: true, onClick: menuItem.onSelect, children: content }, menuItem.label));
|
|
109
106
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Tone } from "./badge.js";
|
|
2
|
-
import type { ComponentProps, ReactNode } from "react";
|
|
2
|
+
import type { ComponentProps, HTMLAttributes, ReactNode } from "react";
|
|
3
3
|
export type MeterProps = Omit<ComponentProps<"span">, "children"> & {
|
|
4
4
|
value: number;
|
|
5
5
|
/** 1 by default, so a share can be passed as is. */
|
|
@@ -12,3 +12,11 @@ export type MeterProps = Omit<ComponentProps<"span">, "children"> & {
|
|
|
12
12
|
valueLabel?: ReactNode;
|
|
13
13
|
};
|
|
14
14
|
export declare function Meter({ value, max, tone, label, valueLabel, className, ...props }: MeterProps): import("react").JSX.Element;
|
|
15
|
+
/** A button with `onClick`, otherwise a plain row; either way it takes the caller's attributes. */
|
|
16
|
+
export type BudgetRowProps = Omit<HTMLAttributes<HTMLElement>, "children"> & Pick<MeterProps, "value" | "max" | "tone" | "valueLabel"> & {
|
|
17
|
+
icon: ReactNode;
|
|
18
|
+
name: string;
|
|
19
|
+
/** Under the name, such as "$184K of $200K of approved budget". */
|
|
20
|
+
detail: ReactNode;
|
|
21
|
+
};
|
|
22
|
+
export declare function BudgetRow({ icon, name, detail, value, max, tone, valueLabel, className, ...props }: BudgetRowProps): import("react").JSX.Element;
|
package/dist/components/meter.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
/*
|
|
3
3
|
* A measured amount against its limit, such as a budget spent: a thin track filled to
|
|
4
4
|
* `value / max`, with the figure above it at the end. Past the limit the track stays full. It is
|
|
5
|
-
* phrasing content, so it can sit inside a row's button.
|
|
5
|
+
* phrasing content, so it can sit inside a row's button. On track (`info`) and at risk (`warning`)
|
|
6
|
+
* fill as the chart's progress bar does, a gradient over the hue's track.
|
|
6
7
|
*/
|
|
7
8
|
import { css, cva, cx } from "#primitives/css";
|
|
8
9
|
const meter = css({
|
|
@@ -19,9 +20,9 @@ const track = cva({
|
|
|
19
20
|
variants: {
|
|
20
21
|
tone: {
|
|
21
22
|
neutral: { background: "bg.hover" },
|
|
22
|
-
info: { background: "
|
|
23
|
+
info: { background: "chart.progressTrack" },
|
|
23
24
|
success: { background: "bg.successSubtle" },
|
|
24
|
-
warning: { background: "
|
|
25
|
+
warning: { background: "chart.atRiskTrack" },
|
|
25
26
|
danger: { background: "bg.errorSubtle" },
|
|
26
27
|
},
|
|
27
28
|
},
|
|
@@ -31,9 +32,9 @@ const fill = cva({
|
|
|
31
32
|
variants: {
|
|
32
33
|
tone: {
|
|
33
34
|
neutral: { background: "fg.subtle" },
|
|
34
|
-
info: { background: "
|
|
35
|
+
info: { background: "token(gradients.chart.progress)" },
|
|
35
36
|
success: { background: "accent.green" },
|
|
36
|
-
warning: { background: "
|
|
37
|
+
warning: { background: "token(gradients.chart.atRisk)" },
|
|
37
38
|
danger: { background: "accent.red" },
|
|
38
39
|
},
|
|
39
40
|
},
|
|
@@ -42,3 +43,60 @@ export function Meter({ value, max = 1, tone = "info", label, valueLabel, classN
|
|
|
42
43
|
const share = max > 0 ? Math.min(Math.max(value / max, 0), 1) : 0;
|
|
43
44
|
return (_jsxs("span", { role: "meter", "aria-label": label, "aria-valuenow": value, "aria-valuemin": 0, "aria-valuemax": max, "aria-valuetext": typeof valueLabel === "string" ? valueLabel : undefined, ...props, className: cx(meter, className), "data-ciridae-ui": "", children: [valueLabel, _jsx("span", { className: track({ tone }), children: _jsx("span", { className: fill({ tone }), style: { width: `${share * 100}%` } }) })] }));
|
|
44
45
|
}
|
|
46
|
+
/*
|
|
47
|
+
* Budget row: a job's spend against its budget as a row, the kit's Budget row: the icon on the
|
|
48
|
+
* raised tile, the name over what is spent of what, and a 170px meter at the end. A row with
|
|
49
|
+
* `onClick` is a button that opens the job. Rows in a list part with a hairline above each.
|
|
50
|
+
*/
|
|
51
|
+
const budgetRow = css({
|
|
52
|
+
display: "flex",
|
|
53
|
+
alignItems: "center",
|
|
54
|
+
justifyContent: "space-between",
|
|
55
|
+
gap: "meter.rowGap",
|
|
56
|
+
width: "100%",
|
|
57
|
+
paddingBlock: "panel.default",
|
|
58
|
+
paddingInline: "panel.comfortable",
|
|
59
|
+
border: "0",
|
|
60
|
+
borderTop: "1px solid token(colors.border.divider)",
|
|
61
|
+
background: "transparent",
|
|
62
|
+
color: "fg.primary",
|
|
63
|
+
font: "inherit",
|
|
64
|
+
fontWeight: "medium",
|
|
65
|
+
letterSpacing: "compact",
|
|
66
|
+
lineHeight: "tight",
|
|
67
|
+
textAlign: "start",
|
|
68
|
+
_first: { borderTop: "0" },
|
|
69
|
+
"&:is(button)": {
|
|
70
|
+
cursor: "pointer",
|
|
71
|
+
_hover: { background: "bg.rowHover" },
|
|
72
|
+
_focusVisible: { focusRing: "inset" },
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
const budgetLeading = css({
|
|
76
|
+
display: "flex",
|
|
77
|
+
alignItems: "center",
|
|
78
|
+
gap: "layout.contentGap",
|
|
79
|
+
flex: "1",
|
|
80
|
+
minWidth: "0",
|
|
81
|
+
});
|
|
82
|
+
const budgetTile = css({
|
|
83
|
+
display: "inline-grid",
|
|
84
|
+
placeItems: "center",
|
|
85
|
+
flexShrink: "0",
|
|
86
|
+
width: "tile.icon",
|
|
87
|
+
height: "tile.icon",
|
|
88
|
+
border: "1px solid transparent",
|
|
89
|
+
borderRadius: "action",
|
|
90
|
+
background: "token(gradients.raised)",
|
|
91
|
+
fontSize: "icon.medium",
|
|
92
|
+
"--edge": "token(gradients.border.raised)",
|
|
93
|
+
});
|
|
94
|
+
const budgetCopy = css({ display: "grid", gap: "layout.tightGap", minWidth: "0" });
|
|
95
|
+
const budgetName = css({ fontSize: "body" });
|
|
96
|
+
const budgetDetail = css({ color: "fg.secondary", fontSize: "code" });
|
|
97
|
+
// Fluid under 170px, so the job's name keeps a readable line on a phone.
|
|
98
|
+
const budgetMeter = css({ width: "min(170px, 40%)", flexShrink: "0" });
|
|
99
|
+
export function BudgetRow({ icon, name, detail, value, max, tone, valueLabel, className, ...props }) {
|
|
100
|
+
const content = (_jsxs(_Fragment, { children: [_jsxs("span", { className: budgetLeading, children: [_jsx("span", { className: budgetTile, "data-ciridae-ui": "", "data-edge": "gradient", children: icon }), _jsxs("span", { className: budgetCopy, children: [_jsx("span", { className: budgetName, children: name }), _jsx("span", { className: budgetDetail, children: detail })] })] }), _jsx(Meter, { className: budgetMeter, value: value, max: max, tone: tone, label: name, valueLabel: valueLabel })] }));
|
|
101
|
+
return props.onClick ? (_jsx("button", { type: "button", ...props, className: cx(budgetRow, className), "data-ciridae-ui": "", children: content })) : (_jsx("div", { ...props, className: cx(budgetRow, className), "data-ciridae-ui": "", children: content }));
|
|
102
|
+
}
|
|
@@ -4,18 +4,35 @@ export type PaginationProps = Omit<ComponentProps<"nav">, "children"> & {
|
|
|
4
4
|
page: number;
|
|
5
5
|
pages: number;
|
|
6
6
|
onPageChange: (page: number) => void;
|
|
7
|
+
/** At the bar's start, such as a `Select` of how many rows a page shows. */
|
|
8
|
+
pageSizeSelect?: ReactNode;
|
|
9
|
+
/** Before the pager, such as "Showing 1 to 25 of 1,284". */
|
|
10
|
+
summary?: ReactNode;
|
|
11
|
+
/** A button per page between Previous and Next, for a short run of pages. */
|
|
12
|
+
numbered?: boolean;
|
|
7
13
|
labels?: Partial<CiridaeLabels["pagination"]>;
|
|
8
14
|
};
|
|
9
|
-
export declare function Pagination({ page, pages, onPageChange, labels: ownLabels, className, ...props }: PaginationProps): import("react").JSX.Element;
|
|
15
|
+
export declare function Pagination({ page, pages, onPageChange, pageSizeSelect, summary, numbered, labels: ownLabels, className, ...props }: PaginationProps): import("react").JSX.Element;
|
|
10
16
|
export type EmptyStateProps = Omit<ComponentProps<"section">, "title"> & {
|
|
11
17
|
icon?: ReactNode;
|
|
12
18
|
title: ReactNode;
|
|
13
19
|
description?: ReactNode;
|
|
20
|
+
/** The main way forward: a primary medium button on a page, a secondary small one in a card. */
|
|
14
21
|
action?: ReactNode;
|
|
22
|
+
/** A second way forward before `action`, such as a secondary medium button on a page. */
|
|
23
|
+
secondaryAction?: ReactNode;
|
|
15
24
|
/**
|
|
16
|
-
* `block`
|
|
17
|
-
*
|
|
25
|
+
* `block` is a page's or panel's empty state; `bare` the same, tighter, for a table or card
|
|
26
|
+
* that frames it; `inline` is one muted line in a list.
|
|
18
27
|
*/
|
|
19
28
|
variant?: "block" | "bare" | "inline";
|
|
20
29
|
};
|
|
21
|
-
export declare function EmptyState({ icon, title, description, action, variant, className, ...props }: EmptyStateProps): import("react").JSX.Element;
|
|
30
|
+
export declare function EmptyState({ icon, title, description, action, secondaryAction, variant, className, ...props }: EmptyStateProps): import("react").JSX.Element;
|
|
31
|
+
export type EmptyTileProps = Omit<ComponentProps<"div">, "children"> & {
|
|
32
|
+
/** What the tile would count, as its stat tile names it. */
|
|
33
|
+
label: ReactNode;
|
|
34
|
+
/** Why there is no figure, such as "No invoices this period". */
|
|
35
|
+
note?: ReactNode;
|
|
36
|
+
};
|
|
37
|
+
/** A stat tile with no figure: its label, a dash in the figure's place, and why. */
|
|
38
|
+
export declare function EmptyTile({ label, note, className, ...props }: EmptyTileProps): import("react").JSX.Element;
|
|
@@ -1,30 +1,67 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { CaretLeftIcon, CaretRightIcon } from "@phosphor-icons/react";
|
|
2
3
|
import { css, cva, cx } from "#primitives/css";
|
|
3
|
-
import { Button } from "./button.js";
|
|
4
|
+
import { Button, IconButton } from "./button.js";
|
|
5
|
+
import { icon } from "./icon.js";
|
|
4
6
|
import { Heading } from "./layout.js";
|
|
5
7
|
import { useCiridaeLocale } from "./locale.js";
|
|
6
|
-
|
|
8
|
+
import { cardSurface } from "./panel.js";
|
|
9
|
+
/*
|
|
10
|
+
* The bar under a table, the kit's Pagination. Full: the page size at the start, then the summary
|
|
11
|
+
* and the pager at the end, inset 12px. Simple, with no page size: the summary at the start, the
|
|
12
|
+
* pager at the end, flush.
|
|
13
|
+
*/
|
|
14
|
+
const pagination = css({
|
|
15
|
+
display: "flex",
|
|
16
|
+
alignItems: "center",
|
|
17
|
+
justifyContent: "space-between",
|
|
18
|
+
gap: "layout.controlGap",
|
|
19
|
+
minHeight: "control.row",
|
|
20
|
+
paddingInline: "panel.default",
|
|
21
|
+
lineHeight: "tight",
|
|
22
|
+
"&[data-type=simple]": { paddingInline: "0" },
|
|
23
|
+
});
|
|
24
|
+
const paginationEnd = css({
|
|
25
|
+
display: "flex",
|
|
26
|
+
alignItems: "center",
|
|
27
|
+
gap: "layout.clusterGap",
|
|
28
|
+
marginInlineStart: "auto",
|
|
29
|
+
});
|
|
30
|
+
const paginationSummary = css({
|
|
31
|
+
color: "fg.secondary",
|
|
32
|
+
fontSize: "code",
|
|
33
|
+
fontWeight: "medium",
|
|
34
|
+
letterSpacing: "compact",
|
|
35
|
+
fontVariantNumeric: "tabular-nums",
|
|
36
|
+
});
|
|
37
|
+
// Simple: the summary and the pager are the bar's own ends.
|
|
38
|
+
const summaryAndPagerAsBarEnds = css({ display: "contents" });
|
|
39
|
+
const pager = css({ display: "flex", alignItems: "center", gap: "layout.tightGap" });
|
|
40
|
+
const pagerAlone = css({ marginInlineStart: "auto" });
|
|
41
|
+
export function Pagination({ page, pages, onPageChange, pageSizeSelect, summary, numbered = false, labels: ownLabels, className, ...props }) {
|
|
7
42
|
const locale = useCiridaeLocale();
|
|
8
43
|
const labels = { ...locale.labels.pagination, ...ownLabels };
|
|
9
|
-
const
|
|
10
|
-
|
|
44
|
+
const Previous = locale.dir === "rtl" ? CaretRightIcon : CaretLeftIcon;
|
|
45
|
+
const Next = locale.dir === "rtl" ? CaretLeftIcon : CaretRightIcon;
|
|
46
|
+
return (_jsxs("nav", { "aria-label": labels.label, ...props, className: cx(pagination, className), "data-ciridae-ui": "", "data-type": pageSizeSelect ? "full" : "simple", children: [pageSizeSelect, _jsxs("div", { className: pageSizeSelect ? paginationEnd : summaryAndPagerAsBarEnds, children: [summary && _jsx("span", { className: paginationSummary, children: summary }), _jsxs("div", { className: cx(pager, !summary && pagerAlone), children: [_jsx(IconButton, { "aria-label": labels.previous, size: "medium", disabled: pages === 0 || page <= 1, onClick: () => onPageChange(Math.max(1, page - 1)), children: _jsx(Previous, { className: icon({ size: "small" }) }) }), numbered &&
|
|
47
|
+
Array.from({ length: pages }, (_, index) => index + 1).map((value) => (_jsx(Button, { variant: value === page ? "primary" : "ghost", "aria-current": value === page ? "page" : undefined, onClick: () => onPageChange(value), children: locale.formatNumber(value) }, value))), _jsx(IconButton, { "aria-label": labels.next, size: "medium", disabled: pages === 0 || page >= pages, onClick: () => onPageChange(Math.min(pages, page + 1)), children: _jsx(Next, { className: icon({ size: "small" }) }) })] })] })] }));
|
|
11
48
|
}
|
|
12
49
|
const emptyState = cva({
|
|
13
50
|
base: { display: "grid", placeItems: "center", textAlign: "center" },
|
|
14
51
|
variants: {
|
|
15
52
|
variant: {
|
|
16
|
-
// A page or panel with nothing in it yet:
|
|
53
|
+
// A page or panel with nothing in it yet: no frame, with room around it.
|
|
17
54
|
block: {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
55
|
+
justifyItems: "center",
|
|
56
|
+
gap: "layout.clusterGap",
|
|
57
|
+
paddingBlock: "emptyState.pageBlock",
|
|
58
|
+
paddingInline: "panel.roomy",
|
|
22
59
|
},
|
|
23
|
-
// Inside a table or a card that frames it already:
|
|
60
|
+
// Inside a table or a card that frames it already: tighter, its copy wrapping to the card.
|
|
24
61
|
bare: {
|
|
25
62
|
justifyItems: "center",
|
|
26
|
-
gap: "layout.
|
|
27
|
-
paddingBlock: "
|
|
63
|
+
gap: "layout.contentGap",
|
|
64
|
+
paddingBlock: "emptyState.compactBlock",
|
|
28
65
|
paddingInline: "panel.comfortable",
|
|
29
66
|
},
|
|
30
67
|
// A list or section with nothing to show: one muted line, no frame.
|
|
@@ -45,31 +82,59 @@ const emptyRing = css({
|
|
|
45
82
|
height: "brand.logo",
|
|
46
83
|
border: "1px solid token(colors.border.control)",
|
|
47
84
|
borderRadius: "round",
|
|
48
|
-
|
|
85
|
+
// The field's grey well in light; in dark the deep grey a disabled field wears, not the white
|
|
86
|
+
// wash of a dark field, so the ring sits below the page rather than lit on it.
|
|
87
|
+
background: "bg.fieldDisabled",
|
|
88
|
+
_light: { background: "bg.field" },
|
|
49
89
|
color: "fg.muted",
|
|
50
90
|
fontSize: "icon.small",
|
|
51
91
|
});
|
|
52
92
|
const emptyCopy = css({ display: "grid", gap: "control.iconGap", maxWidth: "card.centered" });
|
|
93
|
+
// The title in the primary ink, whatever the table or card round it sets as its text colour.
|
|
94
|
+
const emptyTitle = css({ color: "fg.primary", lineHeight: "tight" });
|
|
53
95
|
const emptyDescription = css({
|
|
54
96
|
margin: "none",
|
|
55
97
|
color: "fg.muted",
|
|
98
|
+
_light: { color: "fg.secondary" },
|
|
56
99
|
fontSize: "code",
|
|
57
100
|
fontWeight: "medium",
|
|
58
101
|
letterSpacing: "compact",
|
|
59
|
-
|
|
102
|
+
// A page's one line of help sits on the title's tight line; a card's wraps.
|
|
103
|
+
lineHeight: "tight",
|
|
104
|
+
"[data-variant=bare] &": { lineHeight: "body" },
|
|
60
105
|
});
|
|
61
106
|
const emptyActions = css({
|
|
62
107
|
display: "flex",
|
|
63
108
|
flexWrap: "wrap",
|
|
64
109
|
justifyContent: "center",
|
|
65
110
|
gap: "layout.controlGap",
|
|
111
|
+
paddingBlock: "layout.tightGap",
|
|
112
|
+
"[data-variant=bare] > &": { paddingBlock: "none" },
|
|
66
113
|
});
|
|
67
|
-
export function EmptyState({ icon, title, description, action, variant = "block", className, ...props }) {
|
|
114
|
+
export function EmptyState({ icon, title, description, action, secondaryAction, variant = "block", className, ...props }) {
|
|
68
115
|
if (variant === "inline") {
|
|
69
116
|
return (_jsxs("section", { ...props, "data-ciridae-ui": "", className: cx(emptyState({ variant }), className), children: [_jsxs("span", { className: css({ display: "inline-flex", alignItems: "center", gap: "control.iconGap" }), children: [icon, title] }), description && _jsx("span", { children: description }), action] }));
|
|
70
117
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
118
|
+
return (_jsxs("section", { ...props, "data-ciridae-ui": "", "data-variant": variant, className: cx(emptyState({ variant }), className), children: [icon && (_jsx("span", { className: emptyRing, "aria-hidden": "true", children: icon })), _jsxs("div", { className: emptyCopy, children: [_jsx(Heading, { level: 2, size: "small", className: emptyTitle, children: title }), description && _jsx("p", { className: emptyDescription, children: description })] }), (action || secondaryAction) && (_jsxs("div", { className: emptyActions, children: [secondaryAction, action] }))] }));
|
|
119
|
+
}
|
|
120
|
+
// A dashboard tile with nothing to count, worn like the stat tile it stands in for.
|
|
121
|
+
const emptyTile = css(cardSurface, {
|
|
122
|
+
display: "grid",
|
|
123
|
+
alignContent: "start",
|
|
124
|
+
justifyItems: "start",
|
|
125
|
+
gap: "layout.controlGap",
|
|
126
|
+
minHeight: "tile.minimum",
|
|
127
|
+
paddingBlock: "tile.block",
|
|
128
|
+
paddingInline: "tile.inline",
|
|
129
|
+
fontSize: "code",
|
|
130
|
+
fontWeight: "medium",
|
|
131
|
+
letterSpacing: "compact",
|
|
132
|
+
lineHeight: "tight",
|
|
133
|
+
});
|
|
134
|
+
const emptyTileLabel = css({ color: "fg.label" });
|
|
135
|
+
const emptyTileValue = css({ color: "fg.primary", fontSize: "body" });
|
|
136
|
+
const emptyTileNote = css({ color: "fg.subtle" });
|
|
137
|
+
/** A stat tile with no figure: its label, a dash in the figure's place, and why. */
|
|
138
|
+
export function EmptyTile({ label, note, className, ...props }) {
|
|
139
|
+
return (_jsxs("div", { ...props, className: cx(emptyTile, className), "data-ciridae-ui": "", children: [_jsx("span", { className: emptyTileLabel, children: label }), _jsx("span", { className: emptyTileValue, children: "\u2014" }), note && _jsx("span", { className: emptyTileNote, children: note })] }));
|
|
75
140
|
}
|
|
@@ -45,12 +45,13 @@ const popup = css({
|
|
|
45
45
|
maxWidth: "calc(100vw - token(spacing.layout.pageGap) * 2)",
|
|
46
46
|
padding: "none",
|
|
47
47
|
overflow: "hidden",
|
|
48
|
-
border: "1px solid token(colors.border.
|
|
48
|
+
border: "1px solid token(colors.border.divider)",
|
|
49
49
|
borderRadius: "action",
|
|
50
|
-
background: "bg.
|
|
50
|
+
background: "bg.popover",
|
|
51
|
+
backdropFilter: "blur(token(blurs.popover))",
|
|
51
52
|
color: "fg.primary",
|
|
52
53
|
textStyle: "body",
|
|
53
|
-
boxShadow: "
|
|
54
|
+
boxShadow: "soft",
|
|
54
55
|
outline: "none",
|
|
55
56
|
});
|
|
56
57
|
const header = css({
|
|
@@ -66,7 +67,7 @@ const caughtUp = css({ color: "fg.muted", fontSize: "caption", fontWeight: "medi
|
|
|
66
67
|
const filters = css({
|
|
67
68
|
paddingInline: "panel.comfortable",
|
|
68
69
|
paddingBottom: "layout.tightGap",
|
|
69
|
-
borderBottom: "1px solid token(colors.border.
|
|
70
|
+
borderBottom: "1px solid token(colors.border.divider)",
|
|
70
71
|
});
|
|
71
72
|
const list = css({
|
|
72
73
|
display: "grid",
|
|
@@ -103,5 +103,7 @@ export declare function Popover({ trigger, anchor, children, density, width, ali
|
|
|
103
103
|
export type TooltipProps = Omit<ComponentProps<typeof BaseTooltip.Root>, "children"> & {
|
|
104
104
|
children: ReactElement;
|
|
105
105
|
content: ReactNode;
|
|
106
|
+
/** A count or shortcut after the content, quieter than it. */
|
|
107
|
+
meta?: ReactNode;
|
|
106
108
|
};
|
|
107
|
-
export declare function Tooltip({ children, content, ...rootProps }: TooltipProps): import("react").JSX.Element;
|
|
109
|
+
export declare function Tooltip({ children, content, meta, ...rootProps }: TooltipProps): import("react").JSX.Element;
|