@ciridae/ui 1.0.3 → 1.0.4
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 +105 -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 +76 -39
- package/dist/components/chat.d.ts +90 -14
- package/dist/components/chat.js +402 -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 +12 -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 +289 -33
- package/dist/styled-system/styles.css +3115 -1044
- package/dist/styled-system/tokens/index.mjs +598 -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 +107 -32
- package/package.json +9 -1
|
@@ -6,10 +6,11 @@ import { Fragment } from "react";
|
|
|
6
6
|
import { css, cva, cx } from "#primitives/css";
|
|
7
7
|
import { CopyButton } from "./copyable-id.js";
|
|
8
8
|
import { Skeleton } from "./skeleton.js";
|
|
9
|
-
// The neutral avatar is
|
|
10
|
-
// organization
|
|
11
|
-
// fill, text, and border from one `--avatar-hue`, mixed toward the surface or
|
|
12
|
-
// holds in both themes. The hues are brighter than the accent tokens so
|
|
9
|
+
// The neutral avatar is a person or the agent on the raised glass gradient inside its gradient
|
|
10
|
+
// hairline, an organization the text-colored tile the workspace switcher shows. A colored one
|
|
11
|
+
// derives its fill, text, and border from one `--avatar-hue`, mixed toward the opaque surface or
|
|
12
|
+
// the text color so it holds in both themes. The hues are brighter than the accent tokens so
|
|
13
|
+
// avatars read apart.
|
|
13
14
|
const avatar = css({
|
|
14
15
|
display: "inline-grid",
|
|
15
16
|
placeItems: "center",
|
|
@@ -41,24 +42,25 @@ const avatar = css({
|
|
|
41
42
|
// A person, or the agent: a circle on the glass face, or a pale circle in its hue.
|
|
42
43
|
"&[data-kind=user], &[data-kind=agent]": {
|
|
43
44
|
borderRadius: "round",
|
|
44
|
-
background: "
|
|
45
|
+
background: "token(gradients.raised)",
|
|
45
46
|
color: "fg.primary",
|
|
46
47
|
},
|
|
48
|
+
"--edge": "token(gradients.border.raised)",
|
|
47
49
|
"&[data-kind=agent], &[data-icon]": { color: "fg.secondary" },
|
|
48
50
|
// An icon fills the same share of every size, so marks match whatever avatar they sit beside.
|
|
49
51
|
"&:is([data-kind=agent], [data-icon]) svg": { width: "58%", height: "58%" },
|
|
50
52
|
// An icon stands on its own, with no circle or ring, so it fills most of its box.
|
|
51
53
|
"&[data-icon]": { background: "transparent", "& svg": { width: "80%", height: "80%" } },
|
|
52
54
|
"&:is([data-kind=user], [data-kind=agent]):not([data-color=neutral])": {
|
|
53
|
-
borderColor: "color-mix(in srgb, var(--avatar-hue) 30%, token(colors.bg.
|
|
54
|
-
background: "color-mix(in srgb, var(--avatar-hue) 16%, token(colors.bg.
|
|
55
|
+
borderColor: "color-mix(in srgb, var(--avatar-hue) 30%, token(colors.bg.surface))",
|
|
56
|
+
background: "color-mix(in srgb, var(--avatar-hue) 16%, token(colors.bg.surface))",
|
|
55
57
|
color: "color-mix(in srgb, var(--avatar-hue) 70%, token(colors.fg.primary))",
|
|
56
58
|
},
|
|
57
59
|
// A project: a pale tile edged and lettered in the hue's strong shade.
|
|
58
60
|
"&[data-kind=project]": {
|
|
59
61
|
borderRadius: "20%",
|
|
60
|
-
borderColor: "color-mix(in srgb, var(--avatar-hue) 70%, token(colors.bg.
|
|
61
|
-
background: "color-mix(in srgb, var(--avatar-hue) 14%, token(colors.bg.
|
|
62
|
+
borderColor: "color-mix(in srgb, var(--avatar-hue) 70%, token(colors.bg.surface))",
|
|
63
|
+
background: "color-mix(in srgb, var(--avatar-hue) 14%, token(colors.bg.surface))",
|
|
62
64
|
color: "color-mix(in srgb, var(--avatar-hue) 75%, token(colors.fg.primary))",
|
|
63
65
|
fontWeight: "semibold",
|
|
64
66
|
},
|
|
@@ -81,12 +83,16 @@ const avatar = css({
|
|
|
81
83
|
"&[data-size=large]": {
|
|
82
84
|
width: "avatar.large",
|
|
83
85
|
height: "avatar.large",
|
|
84
|
-
fontSize: "
|
|
86
|
+
fontSize: "body",
|
|
85
87
|
},
|
|
86
88
|
// A list row's owner, and the workspace switcher's mark.
|
|
87
89
|
"&[data-size=row]": { width: "avatar.row", height: "avatar.row" },
|
|
90
|
+
// A record's owner, and the empty slot where one goes.
|
|
91
|
+
"&[data-size=owner]": { width: "avatar.owner", height: "avatar.owner" },
|
|
88
92
|
// A menu item's leading avatar.
|
|
89
93
|
"&[data-size=menu]": { width: "avatar.menu", height: "avatar.menu" },
|
|
94
|
+
// The agent standing on its own, the kit's Agent avatar.
|
|
95
|
+
"&[data-size=agent]": { width: "avatar.agent", height: "avatar.agent" },
|
|
90
96
|
"&[data-size=small]": { width: "avatar.small", height: "avatar.small", fontSize: "avatar.small" },
|
|
91
97
|
// The inline mark's size, so a project's tile lines up with icons and provider logos.
|
|
92
98
|
"&[data-size=mark]": { width: "mark.inline", height: "mark.inline", fontSize: "avatar.mark" },
|
|
@@ -125,7 +131,22 @@ const avatarBadge = css({
|
|
|
125
131
|
export function Avatar({ label, src, size = "medium", kind = "user", icon, badge, color = "neutral", className, ...props }) {
|
|
126
132
|
const initials = labelInitials(label, kind === "organization" ? 1 : 2);
|
|
127
133
|
const glass = !icon && color === "neutral" && (kind === "user" || kind === "agent");
|
|
128
|
-
return (_jsxs(BaseAvatar.Root, { ...props, "aria-label": label, "data-ciridae-ui": "", "data-size": size, "data-kind": kind, "data-color": color, "data-icon": icon ? "" : undefined, "data-edge": glass ? "
|
|
134
|
+
return (_jsxs(BaseAvatar.Root, { ...props, "aria-label": label, "data-ciridae-ui": "", "data-size": size, "data-kind": kind, "data-color": color, "data-icon": icon ? "" : undefined, "data-edge": glass ? "gradient" : undefined, className: cx(avatar, className), children: [src && _jsx(BaseAvatar.Image, { src: src, alt: "", className: avatarImage }), _jsx(BaseAvatar.Fallback, { className: avatarFallback, children: icon ?? (kind === "agent" ? _jsx(SparkleIcon, { weight: "fill" }) : initials) }), badge && _jsx("span", { className: avatarBadge, children: badge })] }));
|
|
135
|
+
}
|
|
136
|
+
// No owner yet: the avatar's circle as a dashed ring, empty.
|
|
137
|
+
const unassignedAvatar = css({
|
|
138
|
+
display: "inline-block",
|
|
139
|
+
flexShrink: "0",
|
|
140
|
+
width: "avatar.owner",
|
|
141
|
+
height: "avatar.owner",
|
|
142
|
+
border: "1px dashed token(colors.border.control)",
|
|
143
|
+
borderRadius: "round",
|
|
144
|
+
"&[data-size=small]": { width: "avatar.small", height: "avatar.small" },
|
|
145
|
+
"&[data-size=row]": { width: "avatar.row", height: "avatar.row" },
|
|
146
|
+
});
|
|
147
|
+
/** The slot where an owner's avatar goes, before anyone owns the record. */
|
|
148
|
+
export function UnassignedAvatar({ size = "owner", className, ...props }) {
|
|
149
|
+
return (_jsx("span", { ...props, className: cx(unassignedAvatar, className), "data-ciridae-ui": "", "data-size": size }));
|
|
129
150
|
}
|
|
130
151
|
/** Up to `count` capital initials from a name's first words. */
|
|
131
152
|
function labelInitials(label, count) {
|
|
@@ -141,13 +162,13 @@ const divider = cva({
|
|
|
141
162
|
variants: {
|
|
142
163
|
inset: {
|
|
143
164
|
none: {},
|
|
144
|
-
// Between a menu's or a popover's groups: inset to its rows'
|
|
145
|
-
// below.
|
|
165
|
+
// Between a menu's or a popover's groups, as the glass menu's divider: inset to its rows'
|
|
166
|
+
// text, a little room above and below.
|
|
146
167
|
menu: {
|
|
147
168
|
width: "auto",
|
|
148
169
|
marginBlock: "layout.tightGap",
|
|
149
170
|
marginInline: "menu.itemInline",
|
|
150
|
-
background: "border.
|
|
171
|
+
background: "border.divider",
|
|
151
172
|
},
|
|
152
173
|
},
|
|
153
174
|
},
|
|
@@ -155,31 +176,60 @@ const divider = cva({
|
|
|
155
176
|
export function Divider({ inset = "none", className, ...props }) {
|
|
156
177
|
return (_jsx(BaseSeparator, { ...props, "data-ciridae-ui": "", className: cx(divider({ inset }), className) }));
|
|
157
178
|
}
|
|
179
|
+
/*
|
|
180
|
+
* Labels are the muted grey, values the text color. `facts` sets each label against its value at
|
|
181
|
+
* the far edge, a hairline under every row but the last; `compact` is the key-value pair, its
|
|
182
|
+
* labels a 120px column of 12px type.
|
|
183
|
+
*/
|
|
158
184
|
const descriptionList = cva({
|
|
159
185
|
base: {
|
|
160
186
|
display: "grid",
|
|
161
187
|
gridTemplateColumns: "max-content minmax(0, 1fr)",
|
|
162
188
|
margin: "none",
|
|
163
|
-
"& dt": { color: "fg.
|
|
189
|
+
"& dt": { color: "fg.label" },
|
|
164
190
|
"& dd": { margin: "none", overflowWrap: "anywhere" },
|
|
165
191
|
"& a": { color: "inherit" },
|
|
166
192
|
},
|
|
167
193
|
variants: {
|
|
168
194
|
density: {
|
|
169
195
|
default: { rowGap: "layout.controlGap", columnGap: "layout.sectionGap" },
|
|
170
|
-
compact: {
|
|
196
|
+
compact: {
|
|
197
|
+
gridTemplateColumns: "120px minmax(0, 1fr)",
|
|
198
|
+
rowGap: "layout.clusterGap",
|
|
199
|
+
columnGap: "layout.contentGap",
|
|
200
|
+
"& dt": { fontSize: "code", fontWeight: "medium" },
|
|
201
|
+
"& dd": { fontSize: "body", fontWeight: "medium" },
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
layout: {
|
|
205
|
+
columns: {},
|
|
206
|
+
facts: {
|
|
207
|
+
gridTemplateColumns: "minmax(0, 1fr) auto",
|
|
208
|
+
alignItems: "center",
|
|
209
|
+
rowGap: "0",
|
|
210
|
+
columnGap: "layout.contentGap",
|
|
211
|
+
"& dt, & dd": {
|
|
212
|
+
paddingBlock: "panel.default",
|
|
213
|
+
borderBottom: "1px solid token(colors.border.divider)",
|
|
214
|
+
lineHeight: "tight",
|
|
215
|
+
},
|
|
216
|
+
"& > :nth-last-child(-n+2)": { borderBottom: "0" },
|
|
217
|
+
"& dt": { fontSize: "code", fontWeight: "medium" },
|
|
218
|
+
"& dd": { fontSize: "body", fontWeight: "medium", textAlign: "end" },
|
|
219
|
+
},
|
|
171
220
|
},
|
|
172
221
|
},
|
|
173
222
|
});
|
|
174
223
|
const placeholderValueWidths = [96, 120, 144];
|
|
175
224
|
/**
|
|
176
225
|
* Label-value pairs in two columns. Rows whose value is `null` or `undefined` are left out, so
|
|
177
|
-
* callers can list every field a record may carry. `compact`
|
|
226
|
+
* callers can list every field a record may carry. `compact` sets them as key-value pairs;
|
|
227
|
+
* `layout="facts"` as fact rows, each value at the far edge over a hairline.
|
|
178
228
|
* `loading` keeps every row's label and draws a placeholder for its value, so the list arrives at
|
|
179
229
|
* the size it loads to; build the rows from the record that may not have loaded yet.
|
|
180
230
|
*/
|
|
181
|
-
export function DescriptionList({ rows, density = "default", loading = false, className, ...props }) {
|
|
182
|
-
return (_jsx("dl", { ...props, "aria-busy": loading || undefined, "data-ciridae-ui": "", className: cx(descriptionList({ density }), className), children: rows.map(([label, value], index) => loading ? (_jsxs(Fragment, { children: [_jsx("dt", { children: label }), _jsx("dd", { children: _jsx(Skeleton, { shape: "text", style: { width: placeholderValueWidths[index % placeholderValueWidths.length] } }) })] }, label)) : value === null || value === undefined ? null : (_jsxs(Fragment, { children: [_jsx("dt", { children: label }), _jsx("dd", { children: value })] }, label))) }));
|
|
231
|
+
export function DescriptionList({ rows, density = "default", layout = "columns", loading = false, className, ...props }) {
|
|
232
|
+
return (_jsx("dl", { ...props, "aria-busy": loading || undefined, "data-ciridae-ui": "", className: cx(descriptionList({ density, layout }), className), children: rows.map(([label, value], index) => loading ? (_jsxs(Fragment, { children: [_jsx("dt", { children: label }), _jsx("dd", { children: _jsx(Skeleton, { shape: "text", style: { width: placeholderValueWidths[index % placeholderValueWidths.length] } }) })] }, label)) : value === null || value === undefined ? null : (_jsxs(Fragment, { children: [_jsx("dt", { children: label }), _jsx("dd", { children: value })] }, label))) }));
|
|
183
233
|
}
|
|
184
234
|
const codeBlock = css({
|
|
185
235
|
margin: "none",
|
|
@@ -219,3 +269,72 @@ export function CodeBlock({ className, copy, copyLabel = "text", ...props }) {
|
|
|
219
269
|
return block;
|
|
220
270
|
return (_jsxs("div", { className: codeBlockFrame, children: [block, _jsx("span", { className: codeBlockCopy, children: _jsx(CopyButton, { value: copy, label: copyLabel }) })] }));
|
|
221
271
|
}
|
|
272
|
+
/*
|
|
273
|
+
* Detail field: one field of a record as a row, the kit's Detail field. A 150px label column of
|
|
274
|
+
* 12px grey beside the 14px value and, under it, where the value came from and who vouches for it,
|
|
275
|
+
* or a description when a badge rides beside the value. `selected` is the open field; `inset`
|
|
276
|
+
* sets it in the selected glass between hairlines; `highlighted` is the warm band for a field that
|
|
277
|
+
* needs a look, such as two sources that disagree.
|
|
278
|
+
*/
|
|
279
|
+
const detailField = css({
|
|
280
|
+
display: "flex",
|
|
281
|
+
alignItems: "flex-start",
|
|
282
|
+
gap: "layout.contentGap",
|
|
283
|
+
paddingBlock: "panel.default",
|
|
284
|
+
paddingInline: "panel.comfortable",
|
|
285
|
+
fontWeight: "medium",
|
|
286
|
+
letterSpacing: "compact",
|
|
287
|
+
lineHeight: "tight",
|
|
288
|
+
"&[data-divider]": { borderBottom: "1px solid token(colors.border.divider)" },
|
|
289
|
+
"&[data-state=default]:hover": { background: "bg.rowHover" },
|
|
290
|
+
"&[data-state=selected]": { background: "token(gradients.selected)" },
|
|
291
|
+
"&:is([data-state=inset], [data-state=highlighted])": {
|
|
292
|
+
borderBlock: "1px solid token(colors.border.divider)",
|
|
293
|
+
},
|
|
294
|
+
"&[data-state=inset]": { background: "token(gradients.selected)" },
|
|
295
|
+
"&[data-state=highlighted]": { background: "bg.warningTint" },
|
|
296
|
+
});
|
|
297
|
+
// The label sits on the value's first line: 14px of text, or the badge's 24.
|
|
298
|
+
const detailFieldLabel = css({
|
|
299
|
+
display: "flex",
|
|
300
|
+
alignItems: "center",
|
|
301
|
+
flexShrink: "0",
|
|
302
|
+
width: "150px",
|
|
303
|
+
minHeight: "token(fontSizes.body)",
|
|
304
|
+
color: "fg.label",
|
|
305
|
+
fontSize: "code",
|
|
306
|
+
"[data-badge] > &": { minHeight: "control.shortcut" },
|
|
307
|
+
});
|
|
308
|
+
const detailFieldValue = css({ display: "grid", gap: "control.iconGap", flex: "1", minWidth: "0" });
|
|
309
|
+
// A badge that does not fit beside the value drops under it.
|
|
310
|
+
const detailFieldValueLine = css({
|
|
311
|
+
display: "flex",
|
|
312
|
+
alignItems: "center",
|
|
313
|
+
flexWrap: "wrap",
|
|
314
|
+
gap: "layout.controlGap",
|
|
315
|
+
minWidth: "0",
|
|
316
|
+
color: "fg.primary",
|
|
317
|
+
fontSize: "body",
|
|
318
|
+
"[data-badge] &": { minHeight: "control.shortcut" },
|
|
319
|
+
});
|
|
320
|
+
const detailFieldNote = css({
|
|
321
|
+
display: "flex",
|
|
322
|
+
alignItems: "center",
|
|
323
|
+
flexWrap: "wrap",
|
|
324
|
+
columnGap: "layout.contentGap",
|
|
325
|
+
rowGap: "layout.tightGap",
|
|
326
|
+
color: "fg.muted",
|
|
327
|
+
fontSize: "code",
|
|
328
|
+
_light: { color: "fg.secondary" },
|
|
329
|
+
});
|
|
330
|
+
const detailFieldNoteItem = css({
|
|
331
|
+
display: "inline-flex",
|
|
332
|
+
alignItems: "center",
|
|
333
|
+
gap: "layout.tightGap",
|
|
334
|
+
"& > svg": { flexShrink: "0", fontSize: "icon.small" },
|
|
335
|
+
});
|
|
336
|
+
const detailFieldAction = css({ flexShrink: "0", alignSelf: "center" });
|
|
337
|
+
export function DetailField({ label, value, source, assurance, badge, description, action, state = "default", divider = true, className, ...props }) {
|
|
338
|
+
const notes = [source, assurance].filter((note) => note !== undefined);
|
|
339
|
+
return (_jsxs("div", { ...props, className: cx(detailField, className), "data-ciridae-ui": "", "data-state": state, "data-divider": divider || undefined, "data-badge": badge !== undefined || undefined, children: [_jsx("span", { className: detailFieldLabel, children: label }), _jsxs("span", { className: detailFieldValue, children: [_jsxs("span", { className: detailFieldValueLine, children: [value, badge] }), notes.length > 0 && (_jsx("span", { className: detailFieldNote, children: notes.map((note, index) => (_jsxs("span", { className: detailFieldNoteItem, children: [note.icon, note.label] }, index))) })), description !== undefined && _jsx("span", { className: detailFieldNote, children: description })] }), action !== undefined && _jsx("span", { className: detailFieldAction, children: action })] }));
|
|
340
|
+
}
|
|
@@ -33,7 +33,11 @@ export type TableColumnMeta = {
|
|
|
33
33
|
locked?: boolean;
|
|
34
34
|
/** Holds the row's own link instead of the first column, when the first holds links of its own. */
|
|
35
35
|
rowLink?: boolean;
|
|
36
|
-
|
|
36
|
+
/**
|
|
37
|
+
* What the column draws while the list loads, shaped like its cells: `lines` a primary line over
|
|
38
|
+
* a smaller one, `pill` a badge, `person` an avatar and a name, or any node. A line by default.
|
|
39
|
+
*/
|
|
40
|
+
skeleton?: "lines" | "pill" | "person" | ReactNode;
|
|
37
41
|
/** Drops the column on phone-width screens, so the columns that identify a row keep their room. */
|
|
38
42
|
hideOnNarrow?: boolean;
|
|
39
43
|
};
|
|
@@ -87,6 +91,14 @@ export type DataGridProps<Row extends RowData> = Omit<ComponentProps<"div">, "ch
|
|
|
87
91
|
onSortingChange?: (sorting: SortingState) => void;
|
|
88
92
|
/** The rows arrive in the order `sorting` asks for, such as from a server. */
|
|
89
93
|
manualSorting?: boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Shows one page of the sorted rows, `number` counting from 1, for a list paged in the browser
|
|
96
|
+
* under a `Pagination`. Leave it out when `data` is already the page.
|
|
97
|
+
*/
|
|
98
|
+
page?: {
|
|
99
|
+
number: number;
|
|
100
|
+
rowsPerPage: number;
|
|
101
|
+
};
|
|
90
102
|
/** Adds a checkbox column; a function says which rows can be ticked. */
|
|
91
103
|
enableRowSelection?: boolean | ((row: Row) => boolean);
|
|
92
104
|
rowSelection?: RowSelectionState;
|
|
@@ -156,7 +168,7 @@ export type DataGridProps<Row extends RowData> = Omit<ComponentProps<"div">, "ch
|
|
|
156
168
|
* hover-revealed row actions, groups, loading, empty and no-results states. `role="table"`; Enter
|
|
157
169
|
* on a pressable row opens it.
|
|
158
170
|
*/
|
|
159
|
-
export declare function DataGrid<Row extends RowData>({ data, columns, getRowId, variant, density, sorting: sortingProp, defaultSorting, onSortingChange, manualSorting, enableRowSelection, rowSelection: rowSelectionProp, defaultRowSelection, onRowSelectionChange, stickySelectColumn, columnVisibility: columnVisibilityProp, defaultColumnVisibility, onColumnVisibilityChange, columnOrder: columnOrderProp, defaultColumnOrder, onColumnOrderChange, headerMenu, filters, onFiltersChange, getRowHref, renderRowLink, onRowClick, activeRowId, keyboardNavigation, virtualize, rowActions, renderRowDetail, groupBy, renderGroupRow, loading, empty, noResults, filtered, hideHeader, fill, narrowScreenLayout, footer, labels: ownLabels, className, frameClassName, style, ...props }: DataGridProps<Row>): import("react").JSX.Element;
|
|
171
|
+
export declare function DataGrid<Row extends RowData>({ data, columns, getRowId, variant, density, sorting: sortingProp, defaultSorting, onSortingChange, manualSorting, page, enableRowSelection, rowSelection: rowSelectionProp, defaultRowSelection, onRowSelectionChange, stickySelectColumn, columnVisibility: columnVisibilityProp, defaultColumnVisibility, onColumnVisibilityChange, columnOrder: columnOrderProp, defaultColumnOrder, onColumnOrderChange, headerMenu, filters, onFiltersChange, getRowHref, renderRowLink, onRowClick, activeRowId, keyboardNavigation, virtualize, rowActions, renderRowDetail, groupBy, renderGroupRow, loading, empty, noResults, filtered, hideHeader, fill, narrowScreenLayout, footer, labels: ownLabels, className, frameClassName, style, ...props }: DataGridProps<Row>): import("react").JSX.Element;
|
|
160
172
|
export type TableProps<Row extends RowData> = Omit<DataGridProps<Row>, "defaultSorting" | "empty"> & {
|
|
161
173
|
/** The sort the table starts with; `defaultSorting` on `DataGrid`. */
|
|
162
174
|
initialSorting?: SortingState;
|
|
@@ -61,7 +61,7 @@ function scrollingAncestor(element) {
|
|
|
61
61
|
* hover-revealed row actions, groups, loading, empty and no-results states. `role="table"`; Enter
|
|
62
62
|
* on a pressable row opens it.
|
|
63
63
|
*/
|
|
64
|
-
export function DataGrid({ data, columns, getRowId, variant = "framed", density = "default", sorting: sortingProp, defaultSorting = emptySorting, onSortingChange, manualSorting = false, enableRowSelection, rowSelection: rowSelectionProp, defaultRowSelection = emptySelection, onRowSelectionChange, stickySelectColumn = false, columnVisibility: columnVisibilityProp, defaultColumnVisibility = emptyVisibility, onColumnVisibilityChange, columnOrder: columnOrderProp, defaultColumnOrder = emptyOrder, onColumnOrderChange, headerMenu = false, filters, onFiltersChange, getRowHref, renderRowLink, onRowClick, activeRowId, keyboardNavigation = false, virtualize = false, rowActions, renderRowDetail, groupBy, renderGroupRow, loading = false, empty, noResults, filtered, hideHeader = false, fill = false, narrowScreenLayout = "columns", footer, labels: ownLabels, className, frameClassName, style, ...props }) {
|
|
64
|
+
export function DataGrid({ data, columns, getRowId, variant = "framed", density = "default", sorting: sortingProp, defaultSorting = emptySorting, onSortingChange, manualSorting = false, page, enableRowSelection, rowSelection: rowSelectionProp, defaultRowSelection = emptySelection, onRowSelectionChange, stickySelectColumn = false, columnVisibility: columnVisibilityProp, defaultColumnVisibility = emptyVisibility, onColumnVisibilityChange, columnOrder: columnOrderProp, defaultColumnOrder = emptyOrder, onColumnOrderChange, headerMenu = false, filters, onFiltersChange, getRowHref, renderRowLink, onRowClick, activeRowId, keyboardNavigation = false, virtualize = false, rowActions, renderRowDetail, groupBy, renderGroupRow, loading = false, empty, noResults, filtered, hideHeader = false, fill = false, narrowScreenLayout = "columns", footer, labels: ownLabels, className, frameClassName, style, ...props }) {
|
|
65
65
|
const { labels: localeLabels } = useCiridaeLocale();
|
|
66
66
|
const labels = { ...localeLabels.table, ...ownLabels };
|
|
67
67
|
const [sorting, setSorting] = useControllableState(sortingProp, defaultSorting, onSortingChange);
|
|
@@ -149,7 +149,10 @@ export function DataGrid({ data, columns, getRowId, variant = "framed", density
|
|
|
149
149
|
}
|
|
150
150
|
const selectable = enableRowSelection !== undefined && enableRowSelection !== false;
|
|
151
151
|
const canSelect = (row) => typeof enableRowSelection === "function" ? enableRowSelection(row) : selectable;
|
|
152
|
-
const
|
|
152
|
+
const sortedRows = table.getRowModel().rows;
|
|
153
|
+
const rows = page
|
|
154
|
+
? sortedRows.slice((page.number - 1) * page.rowsPerPage, page.number * page.rowsPerPage)
|
|
155
|
+
: sortedRows;
|
|
153
156
|
const selectableIds = rows.filter((row) => canSelect(row.original)).map((row) => row.id);
|
|
154
157
|
const setSelected = (ids, selected) => {
|
|
155
158
|
const next = Object.fromEntries(Object.entries(rowSelection).filter(([id]) => selected || !ids.includes(id)));
|
|
@@ -487,14 +490,12 @@ export function DataGrid({ data, columns, getRowId, variant = "framed", density
|
|
|
487
490
|
headRefs.current.set(column.id, element);
|
|
488
491
|
else
|
|
489
492
|
headRefs.current.delete(column.id);
|
|
490
|
-
}, children: menuItems.length > 0 ? (_jsx(Menu, { align: meta?.align === "end" ? "end" : "start", trigger: _jsxs("button", { type: "button", className: headButton, "data-sorted": sorted || undefined, children: [content, caret] }), items: menuItems })) : column.getCanSort() ? (_jsxs("button", { type: "button", className: headButton, "data-sorted": sorted || undefined,
|
|
493
|
+
}, children: menuItems.length > 0 ? (_jsx(Menu, { align: meta?.align === "end" ? "end" : "start", trigger: _jsxs("button", { type: "button", className: headButton, "data-sorted": sorted || undefined, children: [content, caret] }), items: menuItems })) : column.getCanSort() ? (_jsxs("button", { type: "button", className: headButton, "data-sorted": sorted || undefined, onClick: column.getToggleSortingHandler(), children: [content, caret] })) : (content) }, column.id));
|
|
491
494
|
};
|
|
492
495
|
const showEmpty = !loading && rows.length === 0;
|
|
493
496
|
const isFiltered = filtered ?? (filters !== undefined && filters.length > 0);
|
|
494
497
|
const emptyContent = isFiltered && noResults !== undefined ? noResults : (empty ?? labels.nothingToShow);
|
|
495
|
-
return (_jsxs("div", { ref: frameRef, className: cx(frame, frameClassName), "data-ciridae-ui": "", "data-variant": variant, "data-density": density, "data-fill": fill || undefined, "data-stacked": narrowScreenLayout === "stacked" || undefined, children: [_jsxs("div", { ...props, ref: gridRef, role: "table", "aria-busy": loading || undefined, className: cx(grid, className), style: { ...style, gridTemplateColumns: template }, children: [!hideHeader && (_jsxs("div", { role: "row", className: headRow, children: [selectable && (_jsx("div", { ref: selectHeadRef, role: "columnheader", className: cx(headCell, selectCell, stickySelectColumn && pinnedCell), children: _jsx(Checkbox, { "aria-label": labels.selectAll, checked: allSelected === "all" && selectableIds.length > 0, indeterminate: allSelected === "some", disabled: loading || selectableIds.length === 0, onCheckedChange: (checked) => setSelected(selectableIds, checked) }) })), shownColumns.map(headerCell), rowActions && (_jsx("div", { role: "columnheader", className: headCell, children: _jsx("span", { className: visuallyHidden, children: labels.actions }) }))] })), loading ? (skeletonRows.map((rowIndex) => (_jsxs("div", { role: "row", className: bodyRow, "data-skeleton": "", children: [selectable && _jsx("div", { role: "cell", className: cx(cell, selectCell) }), shownColumns.map((column, columnIndex) => (_jsx("div", { role: "cell", className: cellClass(column), "data-align": column.columnDef.meta?.align, children: column.columnDef.meta?.skeleton
|
|
496
|
-
width: skeletonWidths[(rowIndex + columnIndex) % skeletonWidths.length],
|
|
497
|
-
} })) }, column.id))), rowActions && _jsx("div", { role: "cell", className: cell })] }, rowIndex)))) : showEmpty ? (_jsx("div", { role: "row", className: emptyRow, children: _jsx("div", { role: "cell", "aria-colspan": columnCount, className: emptyCell, children: emptyContent }) })) : (drawBody())] }), footer, filterColumn && filterField && onFiltersChange && (_jsx(Popover, { open: true, onOpenChange: (open) => {
|
|
498
|
+
return (_jsxs("div", { ref: frameRef, className: cx(frame, frameClassName), "data-ciridae-ui": "", "data-variant": variant, "data-density": density, "data-fill": fill || undefined, "data-stacked": narrowScreenLayout === "stacked" || undefined, children: [_jsxs("div", { ...props, ref: gridRef, role: "table", "aria-busy": loading || undefined, className: cx(grid, className), style: { ...style, gridTemplateColumns: template }, children: [!hideHeader && (_jsxs("div", { role: "row", className: headRow, children: [selectable && (_jsx("div", { ref: selectHeadRef, role: "columnheader", className: cx(headCell, selectCell, stickySelectColumn && pinnedCell), children: _jsx(Checkbox, { "aria-label": labels.selectAll, checked: allSelected === "all" && selectableIds.length > 0, indeterminate: allSelected === "some", disabled: loading || selectableIds.length === 0, onCheckedChange: (checked) => setSelected(selectableIds, checked) }) })), shownColumns.map(headerCell), rowActions && (_jsx("div", { role: "columnheader", className: headCell, children: _jsx("span", { className: visuallyHidden, children: labels.actions }) }))] })), loading ? (skeletonRows.map((rowIndex) => (_jsxs("div", { role: "row", className: bodyRow, "data-skeleton": "", style: { opacity: 1 - rowIndex * skeletonRowFade }, children: [selectable && (_jsx("div", { role: "cell", className: cx(cell, selectCell), children: _jsx(Skeleton, { shape: "square", className: skeletonCheckbox }) })), shownColumns.map((column, columnIndex) => (_jsx("div", { role: "cell", className: cellClass(column), "data-align": column.columnDef.meta?.align, children: _jsx(SkeletonCell, { shape: column.columnDef.meta?.skeleton, width: skeletonWidths[(rowIndex + columnIndex) % skeletonWidths.length] }) }, column.id))), rowActions && _jsx("div", { role: "cell", className: cell })] }, rowIndex)))) : showEmpty ? (_jsx("div", { role: "row", className: emptyRow, children: _jsx("div", { role: "cell", "aria-colspan": columnCount, className: emptyCell, children: emptyContent }) })) : (drawBody())] }), footer, filterColumn && filterField && onFiltersChange && (_jsx(Popover, { open: true, onOpenChange: (open) => {
|
|
498
499
|
if (!open)
|
|
499
500
|
setFilterColumnId(null);
|
|
500
501
|
}, anchor: headRefs.current.get(filterColumn.id) ?? null, density: "compact", children: _jsx("div", { className: columnFilter, children: _jsx(FilterValueEditor, { field: filterField, value: filters?.find((filter) => filter.field === filterField.key), onChange: (filter) => onFiltersChange(replaceFilter(filters ?? [], filter)) }) }) }))] }));
|
|
@@ -511,6 +512,8 @@ const emptySelection = {};
|
|
|
511
512
|
const emptyVisibility = {};
|
|
512
513
|
const emptyOrder = [];
|
|
513
514
|
const skeletonRows = [0, 1, 2, 3, 4];
|
|
515
|
+
// Each loading row a little fainter than the one above, so the list reads as fading out.
|
|
516
|
+
const skeletonRowFade = 0.08;
|
|
514
517
|
const skeletonWidths = ["70%", "45%", "85%", "55%"];
|
|
515
518
|
/** What takes a click of its own inside a row, so pressing it leaves the row alone. */
|
|
516
519
|
const interactiveSelector = "a:not([data-row-link]), button, input, label, select, textarea, [role=button], [role=checkbox], [role=combobox], [role=menuitem], [contenteditable=true]";
|
|
@@ -568,7 +571,7 @@ function compareSortValues(left, right, columnId) {
|
|
|
568
571
|
// The surface a pinned cell rebuilds: the list's own surfaces are translucent, so a pinned cell
|
|
569
572
|
// redraws them on the solid canvas, or the columns scrolling under it would show through.
|
|
570
573
|
const frame = css({
|
|
571
|
-
"--row-gap-inline": "token(spacing.layout.
|
|
574
|
+
"--row-gap-inline": "token(spacing.layout.clusterGap)",
|
|
572
575
|
"--row-pad-inline": "token(spacing.panel.comfortable)",
|
|
573
576
|
"--row-pad-block": "token(spacing.panel.default)",
|
|
574
577
|
"--pinned-background": "token(colors.bg.canvas)",
|
|
@@ -583,9 +586,9 @@ const frame = css({
|
|
|
583
586
|
"&[data-variant=framed]": {
|
|
584
587
|
border: "1px solid token(colors.border.panel)",
|
|
585
588
|
borderRadius: "action",
|
|
586
|
-
background: "
|
|
589
|
+
background: "token(gradients.card)",
|
|
590
|
+
boxShadow: "card",
|
|
587
591
|
_light: {
|
|
588
|
-
background: "bg.card",
|
|
589
592
|
"--pinned-background": "token(colors.bg.card)",
|
|
590
593
|
"--pinned-layers": "none",
|
|
591
594
|
},
|
|
@@ -593,10 +596,11 @@ const frame = css({
|
|
|
593
596
|
"&[data-variant=framed][data-density=compact]": {
|
|
594
597
|
"--row-pad-block": "token(spacing.control.compactBlock)",
|
|
595
598
|
},
|
|
599
|
+
// A plain list has no frame to inset from: its cells pad themselves, half the column gap a side.
|
|
596
600
|
"&[data-variant=plain]": {
|
|
597
601
|
"--row-gap-inline": "0px",
|
|
598
602
|
"--row-pad-inline": "0px",
|
|
599
|
-
"--row-pad-block": "token(spacing.
|
|
603
|
+
"--row-pad-block": "token(spacing.panel.default)",
|
|
600
604
|
"--pinned-layers": "inset 0 0 0 999px token(colors.bg.panel)",
|
|
601
605
|
},
|
|
602
606
|
"&[data-variant=plain][data-density=compact]": {
|
|
@@ -615,6 +619,8 @@ const grid = css({
|
|
|
615
619
|
color: "fg.primary",
|
|
616
620
|
fontSize: "body",
|
|
617
621
|
letterSpacing: "compact",
|
|
622
|
+
// The kit's cells set their text at 100%; a plain list's lines may wrap, so they keep room.
|
|
623
|
+
lineHeight: "tight",
|
|
618
624
|
"[data-variant=plain] > &": { lineHeight: "compact" },
|
|
619
625
|
// On a phone a stacked list lays each row out as one block: the first cell takes the whole
|
|
620
626
|
// first line and the others wrap under it, an end-aligned one pushed to the far edge.
|
|
@@ -646,17 +652,13 @@ const rowBase = {
|
|
|
646
652
|
borderBottom: "1px solid token(colors.border.panel)",
|
|
647
653
|
"[data-variant=plain] &": { borderBottomColor: "border.subtle" },
|
|
648
654
|
};
|
|
655
|
+
// The kit's header row is 44px: its 12px labels centred, whatever the cells' block padding.
|
|
649
656
|
const headRow = css(rowBase, {
|
|
657
|
+
minHeight: "table.headRow",
|
|
650
658
|
color: "fg.primary",
|
|
651
659
|
fontSize: "code",
|
|
652
660
|
fontWeight: "medium",
|
|
653
661
|
lineHeight: "tight",
|
|
654
|
-
"[data-variant=plain] &": {
|
|
655
|
-
color: "fg.muted",
|
|
656
|
-
fontSize: "caption",
|
|
657
|
-
letterSpacing: "label",
|
|
658
|
-
textTransform: "uppercase",
|
|
659
|
-
},
|
|
660
662
|
"[data-fill] &": {
|
|
661
663
|
position: "sticky",
|
|
662
664
|
top: "0",
|
|
@@ -671,8 +673,9 @@ const bodyRow = css(rowBase, {
|
|
|
671
673
|
position: "relative",
|
|
672
674
|
outline: "none",
|
|
673
675
|
"&:last-child": { borderBottom: "0" },
|
|
676
|
+
// 56px: a 32px avatar or two-line cell inside the padding, the bottom hairline inside that.
|
|
674
677
|
"[data-variant=framed][data-density=default] &": {
|
|
675
|
-
minHeight: "calc(token(sizes.control.compact) + var(--row-pad-block) * 2
|
|
678
|
+
minHeight: "calc(token(sizes.control.compact) + var(--row-pad-block) * 2)",
|
|
676
679
|
},
|
|
677
680
|
// The row's wash is one layer over every cell, the pinned ones included, so they tint together.
|
|
678
681
|
_after: {
|
|
@@ -685,10 +688,11 @@ const bodyRow = css(rowBase, {
|
|
|
685
688
|
transitionProperty: "background",
|
|
686
689
|
transitionDuration: "fast",
|
|
687
690
|
},
|
|
691
|
+
// A ticked row shows it in its checkbox alone; the row open beside the list wears the selected
|
|
692
|
+
// glass, and keeps it under the pointer.
|
|
688
693
|
"&[data-pressable]": { cursor: "pointer" },
|
|
689
|
-
"&[data-pressable]:hover": { "--row-wash": "token(colors.bg.rowHover)" },
|
|
690
|
-
"
|
|
691
|
-
"&:is([data-selected], [data-active]):hover": { "--row-wash": "token(colors.bg.selectedHover)" },
|
|
694
|
+
"&[data-pressable]:not([data-active]):hover": { "--row-wash": "token(colors.bg.rowHover)" },
|
|
695
|
+
"&[data-active]": { "--row-wash": "token(gradients.selected)" },
|
|
692
696
|
_focusVisible: { focusRing: "inset", "--row-wash": "token(colors.bg.rowHover)" },
|
|
693
697
|
// Its anchor stands for the row and sits in a cell that may clip, so the row draws the ring.
|
|
694
698
|
"&:has(a[data-row-link]:focus-visible)": {
|
|
@@ -715,11 +719,13 @@ const emptyRow = css({ gridColumn: "1 / -1" });
|
|
|
715
719
|
const cellBase = {
|
|
716
720
|
minWidth: "0",
|
|
717
721
|
paddingBlock: "var(--row-pad-block)",
|
|
718
|
-
"[data-variant=plain] &": { paddingInline: "
|
|
722
|
+
"[data-variant=plain] &": { paddingInline: "panel.compact" },
|
|
719
723
|
"&[data-align=end]": { textAlign: "end", justifyContent: "flex-end" },
|
|
720
724
|
"&[data-align=end] > [data-shape]": { marginInlineStart: "auto" },
|
|
721
725
|
};
|
|
726
|
+
// The row's hairline sits inside its bottom padding, as the kit's 56px row draws it.
|
|
722
727
|
const cell = css(cellBase, {
|
|
728
|
+
paddingBottom: "calc(var(--row-pad-block) - token(sizes.border.hairline))",
|
|
723
729
|
fontWeight: "medium",
|
|
724
730
|
"[data-variant=plain] &": { fontWeight: "normal" },
|
|
725
731
|
"& a": { color: "inherit", textDecoration: "none" },
|
|
@@ -817,13 +823,42 @@ const headButton = css({
|
|
|
817
823
|
cursor: "pointer",
|
|
818
824
|
borderRadius: "control",
|
|
819
825
|
_focusVisible: { focusRing: "outer" },
|
|
820
|
-
|
|
826
|
+
// The sort caret shows only while the column is sorted, pointed at, focused or its menu open.
|
|
827
|
+
"& svg": { flexShrink: "0", color: "fg.muted", opacity: "0", transition: "opacity .15s" },
|
|
821
828
|
"&[data-sorted] svg": { color: "fg.primary" },
|
|
822
|
-
|
|
823
|
-
// A sort toggle shows its caret only while pointed at or sorted; a head menu's always shows.
|
|
824
|
-
"&[data-toggle] svg": { opacity: "0", transition: "opacity .15s" },
|
|
825
|
-
"&[data-toggle]:is(:hover, :focus-visible, [data-sorted]) svg": { opacity: "1" },
|
|
829
|
+
"&:is(:hover, :focus-visible, [data-sorted], [data-popup-open]) svg": { opacity: "1" },
|
|
826
830
|
"[data-align=end] > &": { marginInlineStart: "auto" },
|
|
827
831
|
});
|
|
832
|
+
// Past the skeleton's own shape sizes, which match on its `data-shape` too.
|
|
833
|
+
const skeletonCheckbox = css({
|
|
834
|
+
"&[data-shape][data-ciridae-ui]": { width: "control.checkbox", borderRadius: "control" },
|
|
835
|
+
});
|
|
836
|
+
// A loading cell in its column's shape: the kit's skeleton row draws two lines for the primary
|
|
837
|
+
// column, a 20px pill for a badge and a 24px circle beside a line for a person.
|
|
838
|
+
const skeletonLines = css({
|
|
839
|
+
display: "grid",
|
|
840
|
+
gap: "layout.controlGap",
|
|
841
|
+
width: "100%",
|
|
842
|
+
lineHeight: "tight",
|
|
843
|
+
});
|
|
844
|
+
const skeletonPill = css({
|
|
845
|
+
"&[data-shape][data-ciridae-ui]": { width: "80px", height: "control.mark", borderRadius: "pill" },
|
|
846
|
+
});
|
|
847
|
+
const skeletonPerson = css({
|
|
848
|
+
display: "flex",
|
|
849
|
+
alignItems: "center",
|
|
850
|
+
gap: "layout.controlGap",
|
|
851
|
+
width: "100%",
|
|
852
|
+
});
|
|
853
|
+
const skeletonAvatar = css({ "&[data-shape][data-ciridae-ui]": { width: "avatar.menu" } });
|
|
854
|
+
function SkeletonCell({ shape, width, }) {
|
|
855
|
+
if (shape === "lines")
|
|
856
|
+
return (_jsxs("span", { className: skeletonLines, children: [_jsx(Skeleton, { shape: "text", width: width }), _jsx(Skeleton, { shape: "text", height: "caption", width: "45%" })] }));
|
|
857
|
+
if (shape === "pill")
|
|
858
|
+
return _jsx(Skeleton, { className: skeletonPill });
|
|
859
|
+
if (shape === "person")
|
|
860
|
+
return (_jsxs("span", { className: skeletonPerson, children: [_jsx(Skeleton, { shape: "circle", className: skeletonAvatar }), _jsx(Skeleton, { shape: "text", width: width })] }));
|
|
861
|
+
return shape ?? _jsx(Skeleton, { shape: "text", width: width });
|
|
862
|
+
}
|
|
828
863
|
const columnFilter = css({ display: "flex", flexDirection: "column", width: "260px" });
|
|
829
864
|
const visuallyHidden = css({ srOnly: true });
|
|
@@ -9,7 +9,7 @@ import { useState } from "react";
|
|
|
9
9
|
import { css, cx } from "#primitives/css";
|
|
10
10
|
import { Calendar } from "./calendar.js";
|
|
11
11
|
import { useControllableState } from "./controllable-state.js";
|
|
12
|
-
import {
|
|
12
|
+
import { glassMenuDivider, glassMenuRow } from "./glass-menu.js";
|
|
13
13
|
import { icon } from "./icon.js";
|
|
14
14
|
import { useCiridaeLocale } from "./locale.js";
|
|
15
15
|
import { Popover } from "./overlays.js";
|
|
@@ -82,7 +82,7 @@ const trigger = css({
|
|
|
82
82
|
paddingInline: "control.compactInline",
|
|
83
83
|
border: "1px solid transparent",
|
|
84
84
|
borderRadius: "action",
|
|
85
|
-
background: "
|
|
85
|
+
background: "token(gradients.selected)",
|
|
86
86
|
color: "fg.primary",
|
|
87
87
|
fontFamily: "sans",
|
|
88
88
|
fontSize: "body",
|
|
@@ -90,31 +90,20 @@ const trigger = css({
|
|
|
90
90
|
letterSpacing: "compact",
|
|
91
91
|
whiteSpace: "nowrap",
|
|
92
92
|
cursor: "pointer",
|
|
93
|
-
_hover: { background: "
|
|
93
|
+
_hover: { background: "token(gradients.raisedHover)" },
|
|
94
94
|
"&[data-popup-open]": { borderColor: "fg.primary" },
|
|
95
95
|
_focusVisible: { focusRing: "outer" },
|
|
96
96
|
});
|
|
97
97
|
const options = css({ display: "grid", width: "222px" });
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
alignItems: "center",
|
|
101
|
-
gap: "layout.controlGap",
|
|
102
|
-
height: "control.compact",
|
|
103
|
-
paddingInline: "menu.itemInline",
|
|
98
|
+
// A menu row on the popover's glass sheet.
|
|
99
|
+
const option = css(glassMenuRow, {
|
|
104
100
|
border: "0",
|
|
105
|
-
borderRadius: "control",
|
|
106
101
|
background: "transparent",
|
|
107
|
-
|
|
108
|
-
font: "inherit",
|
|
109
|
-
fontSize: "body",
|
|
110
|
-
fontWeight: "medium",
|
|
111
|
-
letterSpacing: "compact",
|
|
102
|
+
fontFamily: "inherit",
|
|
112
103
|
textAlign: "start",
|
|
113
|
-
cursor: "pointer",
|
|
114
|
-
_hover: { background: "bg.glass" },
|
|
115
|
-
_focusVisible: { focusRing: "inset" },
|
|
116
104
|
});
|
|
117
|
-
const
|
|
105
|
+
const optionsDivider = css(glassMenuDivider);
|
|
106
|
+
const checkSlot = css({ display: "inline-flex", width: "mark.inline", flexShrink: "0" });
|
|
118
107
|
/**
|
|
119
108
|
* A select-shaped trigger opening the presets, and Pick dates, which swaps them for a calendar:
|
|
120
109
|
* the first day picked starts the range and the second ends it.
|
|
@@ -149,5 +138,5 @@ export function DateRangePicker({ value: ownValue, defaultValue = { kind: "prese
|
|
|
149
138
|
return (_jsx(Popover, { open: open, onOpenChange: changeOpen, density: "compact", trigger: _jsxs("button", { type: "button", className: cx(trigger, className), "aria-label": `${labels.label}: ${valueLabel}`, "data-ciridae-ui": "", "data-edge": "glass", children: [valueLabel, _jsx(CaretDownIcon, { className: icon({ size: "small" }) })] }), children: picking ? (_jsx(Calendar, { month: month, selected: start, onMonthChange: setMonth, onSelect: pickDay })) : (_jsxs("div", { className: options, children: [presets.map((preset) => (_jsxs("button", { type: "button", className: option, onClick: () => {
|
|
150
139
|
setValue({ kind: "preset", preset: preset.value });
|
|
151
140
|
changeOpen(false);
|
|
152
|
-
}, children: [_jsx("span", { className: checkSlot, children: value.kind === "preset" && value.preset === preset.value && (_jsx(CheckIcon, { className: icon({ size: "small" }), weight: "bold" })) }), preset.label] }, preset.value))), _jsx(
|
|
141
|
+
}, children: [_jsx("span", { className: checkSlot, children: value.kind === "preset" && value.preset === preset.value && (_jsx(CheckIcon, { className: icon({ size: "small" }), weight: "bold" })) }), preset.label] }, preset.value))), _jsx("div", { className: optionsDivider, role: "separator" }), _jsxs("button", { type: "button", className: option, onClick: () => setPicking(true), children: [_jsx("span", { className: checkSlot, children: value.kind === "custom" && (_jsx(CheckIcon, { className: icon({ size: "small" }), weight: "bold" })) }), labels.pickDates] })] })) }));
|
|
153
142
|
}
|
|
@@ -14,6 +14,11 @@ export type DrawerProps = Omit<ComponentProps<typeof BaseDrawer.Root>, "children
|
|
|
14
14
|
size?: "narrow" | "regular" | "large";
|
|
15
15
|
children: ReactNode;
|
|
16
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* A sheet docked at the window's right edge beside the page, which stays usable: it is not modal
|
|
19
|
+
* and a click outside leaves it open, so Escape or its close button closes it. Pass `modal` or
|
|
20
|
+
* `disablePointerDismissal` to change either.
|
|
21
|
+
*/
|
|
17
22
|
export declare function Drawer({ title, icon: titleIcon, eyebrow, subtitle, titleAccessory, actions, size, children, ...rootProps }: DrawerProps): import("react").JSX.Element;
|
|
18
23
|
export declare function DrawerBody({ flush, className, ...props }: ComponentProps<"div"> & {
|
|
19
24
|
flush?: boolean;
|