@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.
Files changed (124) hide show
  1. package/dist/chat/conversation-source.d.ts +15 -1
  2. package/dist/chat/describe-tool-call.d.ts +3 -3
  3. package/dist/chat/describe-tool-call.js +3 -3
  4. package/dist/chat/message-text.js +2 -2
  5. package/dist/ciridae.css +16 -4
  6. package/dist/components/activity-feed.d.ts +1 -0
  7. package/dist/components/activity-feed.js +1 -0
  8. package/dist/components/app-frame.d.ts +13 -1
  9. package/dist/components/app-frame.js +45 -3
  10. package/dist/components/app-navigation.d.ts +28 -1
  11. package/dist/components/app-navigation.js +105 -14
  12. package/dist/components/badge.d.ts +29 -4
  13. package/dist/components/badge.js +134 -15
  14. package/dist/components/bulk-action-bar.js +3 -3
  15. package/dist/components/button.d.ts +10 -3
  16. package/dist/components/button.js +77 -44
  17. package/dist/components/calendar.js +44 -17
  18. package/dist/components/centered-page.js +2 -2
  19. package/dist/components/chart.d.ts +1 -1
  20. package/dist/components/chart.js +16 -11
  21. package/dist/components/chat-client-source.js +47 -6
  22. package/dist/components/chat-sheet-controller.d.ts +1 -0
  23. package/dist/components/chat-sheet-controller.js +1 -0
  24. package/dist/components/chat-sheet.d.ts +10 -2
  25. package/dist/components/chat-sheet.js +76 -39
  26. package/dist/components/chat.d.ts +90 -14
  27. package/dist/components/chat.js +402 -101
  28. package/dist/components/checkbox-item.d.ts +5 -2
  29. package/dist/components/checkbox-item.js +9 -5
  30. package/dist/components/chip.d.ts +5 -0
  31. package/dist/components/chip.js +27 -1
  32. package/dist/components/column-settings.js +1 -1
  33. package/dist/components/command-menu.d.ts +5 -1
  34. package/dist/components/command-menu.js +98 -32
  35. package/dist/components/command-palette.d.ts +6 -2
  36. package/dist/components/command-palette.js +20 -10
  37. package/dist/components/data-display.d.ts +35 -4
  38. package/dist/components/data-display.js +138 -19
  39. package/dist/components/data-grid.d.ts +14 -2
  40. package/dist/components/data-grid.js +61 -26
  41. package/dist/components/date-range-picker.js +9 -20
  42. package/dist/components/drawer.d.ts +5 -0
  43. package/dist/components/drawer.js +91 -54
  44. package/dist/components/editable-field.d.ts +12 -3
  45. package/dist/components/editable-field.js +57 -27
  46. package/dist/components/email-input.js +6 -5
  47. package/dist/components/environment-frame.d.ts +1 -1
  48. package/dist/components/environment-frame.js +37 -6
  49. package/dist/components/filter-bar.js +39 -28
  50. package/dist/components/form-controls.d.ts +9 -0
  51. package/dist/components/form-controls.js +46 -22
  52. package/dist/components/form-layout.d.ts +7 -3
  53. package/dist/components/form-layout.js +68 -23
  54. package/dist/components/glass-menu.d.ts +3 -0
  55. package/dist/components/glass-menu.js +63 -0
  56. package/dist/components/gradient-edge.d.ts +9 -0
  57. package/dist/components/gradient-edge.js +31 -0
  58. package/dist/components/input.d.ts +4 -2
  59. package/dist/components/input.js +26 -15
  60. package/dist/components/keycap.d.ts +9 -5
  61. package/dist/components/keycap.js +57 -19
  62. package/dist/components/lightbox.js +2 -1
  63. package/dist/components/locale.d.ts +10 -0
  64. package/dist/components/locale.js +18 -2
  65. package/dist/components/log-entry.d.ts +40 -0
  66. package/dist/components/log-entry.js +146 -0
  67. package/dist/components/mention-input.js +4 -3
  68. package/dist/components/menu.d.ts +6 -2
  69. package/dist/components/menu.js +34 -37
  70. package/dist/components/meter.d.ts +9 -1
  71. package/dist/components/meter.js +64 -6
  72. package/dist/components/navigation.d.ts +21 -4
  73. package/dist/components/navigation.js +84 -19
  74. package/dist/components/notification-tray.js +5 -4
  75. package/dist/components/overlays.d.ts +3 -1
  76. package/dist/components/overlays.js +69 -40
  77. package/dist/components/page-frame.js +1 -1
  78. package/dist/components/panel.d.ts +1 -1
  79. package/dist/components/panel.js +18 -12
  80. package/dist/components/radio-card.js +10 -9
  81. package/dist/components/record.d.ts +25 -1
  82. package/dist/components/record.js +147 -18
  83. package/dist/components/segmented-bar.d.ts +3 -1
  84. package/dist/components/segmented-bar.js +16 -6
  85. package/dist/components/select.d.ts +5 -2
  86. package/dist/components/select.js +103 -127
  87. package/dist/components/shell-skeleton.js +1 -1
  88. package/dist/components/shell.d.ts +2 -2
  89. package/dist/components/shell.js +43 -45
  90. package/dist/components/sidebar.d.ts +7 -2
  91. package/dist/components/sidebar.js +55 -27
  92. package/dist/components/single-line-text.d.ts +2 -0
  93. package/dist/components/single-line-text.js +16 -0
  94. package/dist/components/site-banner.d.ts +5 -2
  95. package/dist/components/site-banner.js +17 -6
  96. package/dist/components/skeleton.d.ts +7 -6
  97. package/dist/components/skeleton.js +28 -16
  98. package/dist/components/skeletons.d.ts +12 -0
  99. package/dist/components/skeletons.js +71 -8
  100. package/dist/components/stages.d.ts +78 -6
  101. package/dist/components/stages.js +512 -156
  102. package/dist/components/stat-card.d.ts +10 -2
  103. package/dist/components/stat-card.js +92 -37
  104. package/dist/components/status-banner.js +1 -1
  105. package/dist/components/tabs.js +26 -13
  106. package/dist/components/theme-toggle.d.ts +4 -0
  107. package/dist/components/theme-toggle.js +67 -0
  108. package/dist/components/toast.d.ts +9 -2
  109. package/dist/components/toast.js +78 -29
  110. package/dist/preset.js +289 -33
  111. package/dist/styled-system/styles.css +3115 -1044
  112. package/dist/styled-system/tokens/index.mjs +598 -22
  113. package/dist/styled-system/tokens/tokens.d.ts +11 -11
  114. package/dist/styled-system/types/prop-type.d.ts +2 -2
  115. package/dist/styles.css +16 -4
  116. package/dist/theme/color.d.ts +3 -0
  117. package/dist/theme/color.js +5 -0
  118. package/dist/theme/derive.d.ts +9 -2
  119. package/dist/theme/derive.js +72 -12
  120. package/dist/theme/palettes.d.ts +49 -6
  121. package/dist/theme/palettes.js +84 -15
  122. package/dist/theme/tokens.d.ts +3 -3
  123. package/dist/theme/tokens.js +107 -32
  124. 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 the Round 2 one: a person or the agent on the raised glass face, an
10
- // organization as the text-colored tile the workspace switcher shows. A colored one derives its
11
- // fill, text, and border from one `--avatar-hue`, mixed toward the surface or the text color so it
12
- // holds in both themes. The hues are brighter than the accent tokens so avatars read apart.
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: "bg.raised",
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.card))",
54
- background: "color-mix(in srgb, var(--avatar-hue) 16%, token(colors.bg.card))",
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.card))",
61
- background: "color-mix(in srgb, var(--avatar-hue) 14%, token(colors.bg.card))",
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: "heading",
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 ? "glass" : 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 })] }));
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' text, a little room above and
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.panel",
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.muted" },
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: { rowGap: "control.trackInset", columnGap: "layout.clusterGap" },
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` tightens the rows for dense output.
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
- skeleton?: ReactNode;
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 rows = table.getRowModel().rows;
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, "data-toggle": "", onClick: column.getToggleSortingHandler(), children: [content, caret] })) : (content) }, column.id));
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 ?? (_jsx(Skeleton, { shape: "text", style: {
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.pageGap)",
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: "bg.hover",
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.table.rowBlock)",
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 + 1px)",
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
- "&:is([data-selected], [data-active])": { "--row-wash": "token(colors.bg.selected)" },
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: "table.cell" },
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
- "& svg": { flexShrink: "0", color: "fg.muted" },
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
- // A plain list's sort toggle shows its caret only while pointed at or sorted.
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 { Divider } from "./data-display.js";
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: "bg.glass",
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: "bg.glassHover" },
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
- const option = css({
99
- display: "flex",
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
- color: "fg.primary",
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 checkSlot = css({ display: "inline-flex", width: "14px", flexShrink: "0" });
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(Divider, { inset: "menu" }), _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] })] })) }));
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;