@ciridae/ui 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +110 -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 +74 -37
  26. package/dist/components/chat.d.ts +90 -14
  27. package/dist/components/chat.js +401 -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 +17 -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 +291 -33
  111. package/dist/styled-system/styles.css +3107 -1039
  112. package/dist/styled-system/tokens/index.mjs +602 -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 +108 -32
  124. package/package.json +9 -1
@@ -11,14 +11,16 @@ import { useCiridaeLocale } from "./locale.js";
11
11
  import { Popover } from "./overlays.js";
12
12
  const root = css({ display: "flex", flexDirection: "column", gap: "layout.sectionGap" });
13
13
  const bar = css({ display: "flex", gap: "layout.tightGap", height: "8px" });
14
+ // The chart's data marks, so a segment and its swatch match the series in a chart.
14
15
  const toneColor = cva({
15
16
  variants: {
16
17
  tone: {
17
- neutral: { background: "fg.subtle" },
18
- info: { background: "accent.blue" },
19
- success: { background: "accent.green" },
20
- warning: { background: "accent.yellow" },
21
- danger: { background: "accent.red" },
18
+ neutral: { background: "chart.neutral" },
19
+ info: { background: "chart.trend" },
20
+ success: { background: "chart.category2" },
21
+ warning: { background: "chart.category1" },
22
+ indigo: { background: "chart.category3" },
23
+ danger: { background: "chart.category4" },
22
24
  },
23
25
  },
24
26
  });
@@ -42,7 +44,15 @@ const legendRow = css({
42
44
  paddingTop: "panel.comfortable",
43
45
  borderTop: "1px solid token(colors.border.panel)",
44
46
  });
45
- const legendItem = css({ display: "inline-flex", alignItems: "center", gap: "layout.contentGap" });
47
+ const legendItem = css({
48
+ display: "inline-flex",
49
+ alignItems: "center",
50
+ gap: "layout.contentGap",
51
+ color: "fg.primary",
52
+ fontSize: "body",
53
+ fontWeight: "medium",
54
+ lineHeight: "tight",
55
+ });
46
56
  const legendKey = css({
47
57
  display: "inline-flex",
48
58
  alignItems: "center",
@@ -18,8 +18,11 @@ export type SelectProps = {
18
18
  label?: string;
19
19
  /** `medium` is the Round 2 field height; `small` is the pill beside other filters. */
20
20
  size?: ControlSize;
21
- /** Defaults to the flat `field` at medium and the glass `raised` face when small. */
22
- variant?: "field" | "raised";
21
+ /**
22
+ * Defaults to the flat `field` at medium and the glass `raised` face when small. `underline` is
23
+ * a 34px row control at the body size, such as a table footer's rows per page, whatever `size`.
24
+ */
25
+ variant?: "field" | "raised" | "underline";
23
26
  /** Submits the value with an enclosing form under this name. */
24
27
  name?: string;
25
28
  /** Blocks the enclosing form's submission until a value is chosen. */
@@ -1,51 +1,24 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Combobox as BaseCombobox } from "@base-ui/react/combobox";
3
3
  import { Select as BaseSelect } from "@base-ui/react/select";
4
- import { CaretDownIcon, CaretUpIcon, CheckIcon, FunnelSimpleIcon } from "@phosphor-icons/react";
4
+ import { CaretDownIcon, CheckIcon, FunnelSimpleIcon } from "@phosphor-icons/react";
5
5
  import { useDebouncedCallback } from "@tanstack/react-pacer";
6
6
  import { useVirtualizer } from "@tanstack/react-virtual";
7
- import { useCallback, useEffect, useRef, useState } from "react";
7
+ import { useEffect, useRef, useState } from "react";
8
8
  import { css, cva, cx } from "#primitives/css";
9
9
  import { Button } from "./button.js";
10
10
  import { useFieldControl, useInField } from "./field-context.js";
11
+ import { glassMenuDivider, glassMenuRow, glassMenuSurface } from "./glass-menu.js";
12
+ import { faceEdge } from "./gradient-edge.js";
11
13
  import { HotkeyLayer } from "./hotkeys.js";
12
14
  import { icon, Spinner } from "./icon.js";
13
15
  import { useCiridaeLocale } from "./locale.js";
14
16
  import { Popover } from "./overlays.js";
15
- /*
16
- * Open, a trigger and its menu read as one control: the menu sits flush on the trigger's edge, and
17
- * the corners and border where they meet go square and quiet, on whichever side the menu opened.
18
- */
19
- const attachedTrigger = {
20
- "&[data-popup-open][data-popup-side=bottom]": {
21
- borderBottomLeftRadius: "0",
22
- borderBottomRightRadius: "0",
23
- borderBottomColor: "border.subtle",
24
- },
25
- "&[data-popup-open][data-popup-side=top]": {
26
- borderTopLeftRadius: "0",
27
- borderTopRightRadius: "0",
28
- borderTopColor: "border.subtle",
29
- },
30
- };
31
- /** Which side of its trigger the open popup sits on, read from the popup's own `data-side`. */
32
- function usePopupSide() {
33
- const [side, setSide] = useState(null);
34
- const popupRef = useCallback((popup) => {
35
- if (!popup)
36
- return;
37
- const read = () => setSide(popup.getAttribute("data-side"));
38
- read();
39
- const observer = new MutationObserver(read);
40
- observer.observe(popup, { attributes: true, attributeFilter: ["data-side"] });
41
- return () => observer.disconnect();
42
- }, []);
43
- return { side: side ?? undefined, popupRef };
44
- }
45
17
  /*
46
18
  * Round 2 draws three select triggers. `field` is the input's flat well, for a form. `raised` is
47
- * the glass face of a toolbar control; at the compact size it is a pill. Open, every one takes the
48
- * control border its menu continues.
19
+ * the glass face of a toolbar control; at the compact size it is a pill. `underline` has no face,
20
+ * only a bottom hairline, for a control set in a row such as a table footer. Open, every one draws
21
+ * its edge in the text color, and its menu floats just below it.
49
22
  */
50
23
  const trigger = cva({
51
24
  base: {
@@ -64,19 +37,40 @@ const trigger = cva({
64
37
  cursor: "pointer",
65
38
  transitionProperty: "background, border-color",
66
39
  transitionDuration: "fast",
67
- "&[data-popup-open]": { borderColor: "border.control" },
68
- ...attachedTrigger,
40
+ "&[data-popup-open]": { borderColor: "fg.primary" },
69
41
  _focusVisible: { focusRing: "outer" },
70
42
  "&:disabled, &[data-disabled]": { opacity: "disabled", cursor: "not-allowed" },
71
43
  },
72
44
  variants: {
73
45
  variant: {
74
- field: { background: "bg.field", borderColor: "border.control" },
75
- // The gradient edge is `data-edge="toolbar"` on the trigger; the border stays clear for it.
46
+ // Hovered, the well darkens by the control hover layer, in light only.
47
+ field: {
48
+ background: "bg.formField",
49
+ borderColor: "border.control",
50
+ _hover: {
51
+ background: "linear-gradient(token(colors.bg.controlHover), token(colors.bg.controlHover)), token(colors.bg.formField)",
52
+ },
53
+ // Open, the well rests under the pointer too; only the edge changes.
54
+ "&[data-popup-open]:hover": { background: "bg.formField" },
55
+ },
56
+ /*
57
+ * The hairline is `toolbarEdge` on the trigger; the border stays clear for it. The face is
58
+ * the selected tint, and hovers to the raised face.
59
+ */
76
60
  raised: {
77
- background: "bg.glass",
61
+ background: "token(gradients.selected)",
78
62
  borderColor: "transparent",
79
- _hover: { background: "bg.glassHover" },
63
+ _hover: { background: "token(gradients.raisedHover)" },
64
+ // Open, the face rests under the pointer too; only the edge changes.
65
+ "&[data-popup-open]:hover": { background: "token(gradients.selected)" },
66
+ },
67
+ // The caret takes the secondary ink beside the value.
68
+ underline: {
69
+ borderWidth: "0 0 1px",
70
+ borderColor: "border.control",
71
+ borderRadius: "0",
72
+ background: "transparent",
73
+ "& svg": { color: "fg.muted" },
80
74
  },
81
75
  },
82
76
  size: {
@@ -90,83 +84,69 @@ const trigger = cva({
90
84
  },
91
85
  },
92
86
  compoundVariants: [
93
- { variant: "raised", size: "compact", css: { borderRadius: "action" } },
87
+ { variant: "raised", size: "compact", css: { borderRadius: "pill" } },
94
88
  { variant: "raised", size: "regular", css: { gap: "layout.controlGap" } },
89
+ // A row's height at the body size, sized to its value.
90
+ {
91
+ variant: "underline",
92
+ size: "regular",
93
+ css: {
94
+ minWidth: "auto",
95
+ height: "control.row",
96
+ paddingInline: "layout.tightGap",
97
+ gap: "layout.controlGap",
98
+ },
99
+ },
95
100
  ],
96
101
  defaultVariants: { variant: "field", size: "regular" },
97
102
  });
103
+ const toolbarEdge = faceEdge("toolbar");
98
104
  /** The positioner is the positioned element, so it carries the stacking order. */
99
105
  const positioner = css({ zIndex: "popover" });
100
- /* Flush: the menu is the trigger's width and continues its open border, its rows run edge to edge,
101
- and their text lines up under the trigger's own. The trigger's edge is the seam between them. A
106
+ /** How far a menu floats from its trigger, in px. */
107
+ const menuGap = 4;
108
+ /* The menu floats on the glass sheet just below its trigger, at least the trigger's width. A
109
+ compact trigger is sized to its value, so its menu may run wider to keep every option whole. A
102
110
  long list stops at `overlay.menuList`, or the room left in the viewport, and scrolls inside. */
103
- const popup = cva({
104
- base: {
105
- display: "flex",
106
- flexDirection: "column",
107
- width: "var(--anchor-width)",
108
- maxHeight: "min(token(sizes.overlay.menuList), var(--available-height))",
109
- padding: "none",
110
- overflow: "hidden",
111
- border: "1px solid token(colors.border.control)",
112
- background: "bg.card",
113
- boxShadow: "card",
114
- "&[data-side=bottom]": { borderTop: "0", borderTopLeftRadius: "0", borderTopRightRadius: "0" },
115
- "&[data-side=top]": {
116
- borderBottom: "0",
117
- borderBottomLeftRadius: "0",
118
- borderBottomRightRadius: "0",
119
- },
120
- },
121
- // The trigger's own corners: a field's near-square ones, a compact pill's rounder ones. A compact
122
- // trigger is sized to its value, so its menu may run wider to keep every option whole.
123
- variants: {
124
- size: {
125
- regular: { borderRadius: "control" },
126
- compact: { borderRadius: "action", width: "max-content", minWidth: "var(--anchor-width)" },
127
- },
128
- },
129
- defaultVariants: { size: "regular" },
111
+ const popupStyle = css.raw(glassMenuSurface, {
112
+ display: "flex",
113
+ flexDirection: "column",
114
+ width: "var(--anchor-width)",
115
+ maxHeight: "min(token(sizes.overlay.menuList), var(--available-height))",
130
116
  });
131
- const item = cva({
132
- base: {
133
- display: "flex",
134
- alignItems: "center",
135
- justifyContent: "space-between",
136
- gap: "layout.contentGap",
137
- height: "menu.item",
138
- paddingInline: "control.compactInline",
139
- color: "fg.muted",
140
- fontSize: "caption",
141
- cursor: "pointer",
142
- _highlighted: { background: "bg.rowHover", color: "fg.primary" },
143
- "&[data-selected]": { color: "fg.primary" },
144
- // The menu is only as wide as its trigger, so a long option truncates rather than widening it.
145
- "& > :first-child": { minWidth: "0", overflow: "hidden", textOverflow: "ellipsis" },
146
- whiteSpace: "nowrap",
147
- },
148
- // A compact trigger's menu reads at the trigger's own text size.
149
- variants: {
150
- size: { regular: {}, compact: { height: "select.compactItem" } },
151
- },
152
- defaultVariants: { size: "compact" },
117
+ const compactPopupStyle = css.raw({ width: "max-content", minWidth: "var(--anchor-width)" });
118
+ const popup = {
119
+ regular: css(popupStyle),
120
+ compact: css(popupStyle, compactPopupStyle),
121
+ };
122
+ const itemStyle = css.raw(glassMenuRow, {
123
+ justifyContent: "space-between",
124
+ // The menu is only as wide as its trigger, so a long option truncates rather than widening it.
125
+ "& > :first-child": { minWidth: "0", overflow: "hidden", textOverflow: "ellipsis" },
126
+ whiteSpace: "nowrap",
153
127
  });
128
+ // A compact trigger's menu reads at the trigger's own text size.
129
+ const item = {
130
+ regular: css(itemStyle),
131
+ compact: css(itemStyle, { fontSize: "caption" }),
132
+ };
154
133
  const itemIndicator = css({ display: "inline-flex", flexShrink: "0" });
155
134
  /** Scrolls inside the capped popup; opening scrolls the chosen option into view. */
156
- const list = css({ overflowY: "auto", overscrollBehavior: "contain" });
135
+ const list = css({ overflowY: "auto", overscrollBehavior: "contain", ringRoom: true });
157
136
  function density(size) {
158
137
  return size === "small" ? "compact" : "regular";
159
138
  }
160
- function SelectMenu({ options, size, popupRef, }) {
161
- return (_jsx(BaseSelect.Portal, { children: _jsx(HotkeyLayer, { children: _jsx(BaseSelect.Positioner, { className: positioner, sideOffset: 0, align: "start", alignItemWithTrigger: false, children: _jsx(BaseSelect.Popup, { ref: popupRef, className: popup({ size }), "data-ciridae-ui": "", children: _jsx(BaseSelect.List, { className: list, children: options.map((option) => (_jsxs(BaseSelect.Item, { className: item({ size }), value: option.value, children: [_jsx(BaseSelect.ItemText, { children: option.label }), _jsx(BaseSelect.ItemIndicator, { className: itemIndicator, children: _jsx(CheckIcon, { className: icon({ size: "small" }) }) })] }, option.value))) }) }) }) }) }));
139
+ function SelectMenu({ options, popupClassName, itemClassName, }) {
140
+ return (_jsx(BaseSelect.Portal, { children: _jsx(HotkeyLayer, { children: _jsx(BaseSelect.Positioner, { className: positioner, sideOffset: menuGap, align: "start", alignItemWithTrigger: false, children: _jsx(BaseSelect.Popup, { className: popupClassName, "data-ciridae-ui": "", children: _jsx(BaseSelect.List, { className: list, children: options.map((option) => (_jsxs(BaseSelect.Item, { className: itemClassName, value: option.value, children: [_jsx(BaseSelect.ItemText, { children: option.label }), _jsx(BaseSelect.ItemIndicator, { className: itemIndicator, children: _jsx(CheckIcon, { className: icon({ size: "small" }) }) })] }, option.value))) }) }) }) }) }));
162
141
  }
163
142
  export function Select({ options, value, onValueChange, placeholder, label, size: controlSize = "small", variant = controlSize === "small" ? "raised" : "field", name, required, disabled, }) {
164
- const { side, popupRef } = usePopupSide();
165
143
  const { labels } = useCiridaeLocale();
166
144
  const fieldIds = useFieldControl({});
167
145
  const inField = useInField();
168
- const size = density(controlSize);
169
- return (_jsxs(BaseSelect.Root, { items: options, value: value, onValueChange: onValueChange, name: name, required: required, disabled: disabled, children: [_jsxs(BaseSelect.Trigger, { ...fieldIds, className: trigger({ variant, size }), "aria-label": label ?? (inField ? undefined : labels.select.label), "data-ciridae-ui": "", "data-edge": variant === "raised" ? "toolbar" : undefined, "data-popup-side": side, children: [_jsx(BaseSelect.Value, { placeholder: placeholder ?? labels.select.placeholder }), _jsx(BaseSelect.Icon, { children: _jsx(CaretUpIcon, { className: icon({ size: "compact" }) }) })] }), _jsx(SelectMenu, { options: options, size: size, popupRef: popupRef })] }));
146
+ const underline = variant === "underline";
147
+ // An underline trigger is sized to its value, so its menu may run wider, at its body text.
148
+ const size = underline ? "regular" : density(controlSize);
149
+ return (_jsxs(BaseSelect.Root, { items: options, value: value, onValueChange: onValueChange, name: name, required: required, disabled: disabled, children: [_jsxs(BaseSelect.Trigger, { ...fieldIds, className: cx(trigger({ variant, size }), variant === "raised" && toolbarEdge.className), "aria-label": label ?? (inField ? undefined : labels.select.label), "data-ciridae-ui": "", "data-edge": variant === "raised" ? toolbarEdge["data-edge"] : undefined, children: [_jsx(BaseSelect.Value, { placeholder: placeholder ?? labels.select.placeholder }), _jsx(BaseSelect.Icon, { children: _jsx(CaretDownIcon, { className: icon({ size: "small" }) }) })] }), _jsx(SelectMenu, { options: options, popupClassName: underline ? popup.compact : popup[size], itemClassName: item[size] })] }));
170
150
  }
171
151
  /*
172
152
  * A slicer is a filter's pill: the dimension it cuts by, then the value chosen, then the caret.
@@ -174,11 +154,10 @@ export function Select({ options, value, onValueChange, placeholder, label, size
174
154
  */
175
155
  const slicerTrigger = css({
176
156
  height: "control.compact",
177
- borderRadius: "action",
178
157
  fontSize: "caption",
179
158
  justifyContent: "flex-start",
180
159
  });
181
- const slicerLabel = css({ color: "fg.muted" });
160
+ const slicerLabel = css({ color: "fg.muted", _light: { color: "fg.secondary" } });
182
161
  const slicerValue = css({ flex: "1", textAlign: "start" });
183
162
  /** A combobox role takes no name from its content, so a slicer names its dimension and value. */
184
163
  function slicerName(label, options, value) {
@@ -186,12 +165,11 @@ function slicerName(label, options, value) {
186
165
  return chosen ? `${label}: ${chosen.label}` : label;
187
166
  }
188
167
  export function Slicer({ options, value, onValueChange, label, name, required, disabled, }) {
189
- const { side, popupRef } = usePopupSide();
190
- return (_jsxs(BaseSelect.Root, { items: options, value: value, onValueChange: onValueChange, name: name, required: required, disabled: disabled, children: [_jsxs(BaseSelect.Trigger, { className: cx(trigger({ variant: "raised", size: "compact" }), slicerTrigger), "aria-label": slicerName(label, options, value), "data-ciridae-ui": "", "data-edge": "toolbar", "data-popup-side": side, children: [_jsx("span", { className: slicerLabel, "data-slicer-label": "", children: label }), _jsx(BaseSelect.Value, { className: slicerValue }), _jsx(BaseSelect.Icon, { children: _jsx(CaretDownIcon, { className: icon({ size: "small" }) }) })] }), _jsx(SelectMenu, { options: options, size: "compact", popupRef: popupRef })] }));
168
+ return (_jsxs(BaseSelect.Root, { items: options, value: value, onValueChange: onValueChange, name: name, required: required, disabled: disabled, children: [_jsxs(BaseSelect.Trigger, { className: cx(trigger({ variant: "raised", size: "compact" }), slicerTrigger, toolbarEdge.className), "aria-label": slicerName(label, options, value), "data-ciridae-ui": "", "data-edge": toolbarEdge["data-edge"], children: [_jsx("span", { className: slicerLabel, "data-slicer-label": "", children: label }), _jsx(BaseSelect.Value, { className: slicerValue }), _jsx(BaseSelect.Icon, { children: _jsx(CaretDownIcon, { className: icon({ size: "small" }) }) })] }), _jsx(SelectMenu, { options: options, popupClassName: popup.compact, itemClassName: item.compact })] }));
191
169
  }
192
- /* A regular combobox is a field: the well holds the typed query and a caret, and open, it joins
193
- its menu exactly as a `Select` does. The ring shows only while closed, since open the menu is
194
- the focus. */
170
+ /* A regular combobox is a field: the well holds the typed query and a caret, and open, its edge
171
+ goes to the text color as a `Select`'s does. The ring shows only while closed, since open the
172
+ menu is the focus. */
195
173
  const comboboxField = css({
196
174
  display: "flex",
197
175
  alignItems: "center",
@@ -200,8 +178,8 @@ const comboboxField = css({
200
178
  height: "control.default",
201
179
  border: "1px solid token(colors.border.control)",
202
180
  borderRadius: "control",
203
- background: "bg.field",
204
- ...attachedTrigger,
181
+ background: "bg.formField",
182
+ "&[data-popup-open]": { borderColor: "fg.primary" },
205
183
  "&:has(input:focus-visible):not([data-popup-open])": { focusRing: "outer" },
206
184
  "&[data-disabled]": { opacity: "disabled", cursor: "not-allowed" },
207
185
  });
@@ -234,22 +212,19 @@ const comboboxCaret = css({
234
212
  "&:disabled": { cursor: "not-allowed" },
235
213
  });
236
214
  /* A compact combobox is a slicer that searches: the same pill, with the query typed in a row at
237
- the top of its menu. The menu keeps one width while typing narrows it. */
238
- const comboboxPopup = cva({
239
- variants: {
240
- size: {
241
- regular: {},
242
- compact: { width: "max(var(--anchor-width), token(sizes.overlay.menu))" },
243
- },
244
- },
245
- });
215
+ the top of its menu over a divider. The menu keeps one width while typing narrows it. */
216
+ const comboboxPopup = {
217
+ regular: popup.regular,
218
+ compact: css(popupStyle, compactPopupStyle, {
219
+ width: "max(var(--anchor-width), token(sizes.overlay.menu))",
220
+ }),
221
+ };
246
222
  const comboboxSearch = css({
247
223
  flexShrink: "0",
248
224
  width: "100%",
249
- height: "select.compactItem",
250
- paddingInline: "control.compactInline",
225
+ height: "control.compact",
226
+ paddingInline: "search.inline",
251
227
  border: "0",
252
- borderBottom: "1px solid token(colors.border.subtle)",
253
228
  background: "transparent",
254
229
  color: "fg.primary",
255
230
  fontFamily: "sans",
@@ -268,9 +243,10 @@ const optionContent = css({
268
243
  overflow: "hidden",
269
244
  textOverflow: "ellipsis",
270
245
  });
246
+ const searchDivider = css(glassMenuDivider);
271
247
  const emptyMessageStyle = css({
272
248
  paddingBlock: "control.compactBlock",
273
- paddingInline: "control.compactInline",
249
+ paddingInline: "menu.itemInline",
274
250
  color: "fg.muted",
275
251
  fontSize: "caption",
276
252
  // Base UI keeps the empty slot mounted while options are listed.
@@ -313,12 +289,12 @@ export function Combobox({ options, value, onValueChange, placeholder: ownPlaceh
313
289
  if (details.reason === "input-change" || details.reason === "input-clear")
314
290
  search(input);
315
291
  },
316
- }), children: [size === "compact" ? (_jsxs(BaseCombobox.Trigger, { className: cx(trigger({ variant: "raised", size: "compact" }), slicerTrigger), "aria-label": slicerName(label, options, value), "data-ciridae-ui": "", "data-edge": "toolbar", children: [_jsx("span", { className: slicerLabel, "data-slicer-label": "", children: label }), _jsx("span", { className: slicerValue, children: selected?.label }), _jsx(CaretDownIcon, { className: icon({ size: "small" }) })] })) : (_jsxs(BaseCombobox.InputGroup, { className: comboboxField, "data-ciridae-ui": "", children: [_jsx(BaseCombobox.Input, { ...fieldIds, className: comboboxInput, "aria-label": inputLabel, placeholder: placeholder, autoFocus: autoFocus }), _jsx(BaseCombobox.Trigger, { className: comboboxCaret, "aria-label": label, tabIndex: -1, children: _jsx(CaretUpIcon, { className: icon({ size: "compact" }) }) })] })), _jsx(BaseCombobox.Portal, { children: _jsx(HotkeyLayer, { children: _jsx(BaseCombobox.Positioner, { className: positioner, sideOffset: 0, align: "start", children: _jsxs(BaseCombobox.Popup, { className: css(popup.raw({ size }), comboboxPopup.raw({ size })), "data-ciridae-ui": "", children: [size === "compact" && (_jsx(BaseCombobox.Input, { className: comboboxSearch, "aria-label": label, placeholder: placeholder })), loading ? (_jsx("div", { className: emptyMessageStyle, children: _jsx(Spinner, { size: "compact" }) })) : (_jsx(BaseCombobox.Empty, { className: emptyMessageStyle, children: emptyMessage ?? labels.common.noResults })), virtualized ? (_jsx(VirtualOptions, { size: size, renderOption: renderOption, scrollToOption: scrollToOption })) : (_jsx(BaseCombobox.List, { className: list, children: (option) => (_jsxs(BaseCombobox.Item, { className: item({ size }), value: option, children: [_jsx("span", { className: optionContent, children: renderOption ? renderOption(option) : option.label }), _jsx(BaseCombobox.ItemIndicator, { className: itemIndicator, children: _jsx(CheckIcon, { className: icon({ size: "small" }) }) })] }, option.value)) }))] }) }) }) })] }));
292
+ }), children: [size === "compact" ? (_jsxs(BaseCombobox.Trigger, { className: cx(trigger({ variant: "raised", size: "compact" }), slicerTrigger, toolbarEdge.className), "aria-label": slicerName(label, options, value), "data-ciridae-ui": "", "data-edge": toolbarEdge["data-edge"], children: [_jsx("span", { className: slicerLabel, "data-slicer-label": "", children: label }), _jsx("span", { className: slicerValue, children: selected?.label }), _jsx(CaretDownIcon, { className: icon({ size: "small" }) })] })) : (_jsxs(BaseCombobox.InputGroup, { className: comboboxField, "data-ciridae-ui": "", children: [_jsx(BaseCombobox.Input, { ...fieldIds, className: comboboxInput, "aria-label": inputLabel, placeholder: placeholder, autoFocus: autoFocus }), _jsx(BaseCombobox.Trigger, { className: comboboxCaret, "aria-label": label, tabIndex: -1, children: _jsx(CaretDownIcon, { className: icon({ size: "small" }) }) })] })), _jsx(BaseCombobox.Portal, { children: _jsx(HotkeyLayer, { children: _jsx(BaseCombobox.Positioner, { className: positioner, sideOffset: menuGap, align: "start", children: _jsxs(BaseCombobox.Popup, { className: comboboxPopup[size], "data-ciridae-ui": "", children: [size === "compact" && (_jsxs(_Fragment, { children: [_jsx(BaseCombobox.Input, { className: comboboxSearch, "aria-label": label, placeholder: placeholder }), _jsx("div", { className: searchDivider })] })), loading ? (_jsx("div", { className: emptyMessageStyle, children: _jsx(Spinner, { size: "compact" }) })) : (_jsx(BaseCombobox.Empty, { className: emptyMessageStyle, children: emptyMessage ?? labels.common.noResults })), virtualized ? (_jsx(VirtualOptions, { size: size, renderOption: renderOption, scrollToOption: scrollToOption })) : (_jsx(BaseCombobox.List, { className: list, children: (option) => (_jsxs(BaseCombobox.Item, { className: item[size], value: option, children: [_jsx("span", { className: optionContent, children: renderOption ? renderOption(option) : option.label }), _jsx(BaseCombobox.ItemIndicator, { className: itemIndicator, children: _jsx(CheckIcon, { className: icon({ size: "small" }) }) })] }, option.value)) }))] }) }) }) })] }));
317
293
  }
318
294
  /** Past this many options a combobox draws only those near the view. */
319
295
  const virtualizeOptionsPast = 50;
320
- /** An option row's height before it is measured. */
321
- const optionHeights = { regular: 38, compact: 30 };
296
+ /** An option row's height before it is measured, in px. */
297
+ const optionHeight = 32;
322
298
  // The options lie over a box as tall as all of them, each moved down to its place.
323
299
  const virtualOptions = css({
324
300
  position: "relative",
@@ -331,7 +307,7 @@ function VirtualOptions({ size, renderOption, scrollToOption, }) {
331
307
  const virtualizer = useVirtualizer({
332
308
  count: shown.length,
333
309
  getScrollElement: () => listElement,
334
- estimateSize: () => optionHeights[size],
310
+ estimateSize: () => optionHeight,
335
311
  getItemKey: (index) => shown[index]?.value ?? index,
336
312
  overscan: 8,
337
313
  });
@@ -343,7 +319,7 @@ function VirtualOptions({ size, renderOption, scrollToOption, }) {
343
319
  }, [scrollToOption, virtualizer]);
344
320
  return (_jsx(BaseCombobox.List, { ref: setListElement, className: list, children: _jsx("div", { className: virtualOptions, style: { height: virtualizer.getTotalSize() }, children: virtualizer.getVirtualItems().map((virtualItem) => {
345
321
  const option = shown[virtualItem.index];
346
- return (option && (_jsxs(BaseCombobox.Item, { ref: virtualizer.measureElement, "data-index": virtualItem.index, index: virtualItem.index, value: option, className: item({ size }), style: { transform: `translateY(${virtualItem.start}px)` }, children: [_jsx("span", { className: optionContent, children: renderOption ? renderOption(option) : option.label }), _jsx(BaseCombobox.ItemIndicator, { className: itemIndicator, children: _jsx(CheckIcon, { className: icon({ size: "small" }) }) })] }, option.value)));
322
+ return (option && (_jsxs(BaseCombobox.Item, { ref: virtualizer.measureElement, "data-index": virtualItem.index, index: virtualItem.index, value: option, className: item[size], style: { transform: `translateY(${virtualItem.start}px)` }, children: [_jsx("span", { className: optionContent, children: renderOption ? renderOption(option) : option.label }), _jsx(BaseCombobox.ItemIndicator, { className: itemIndicator, children: _jsx(CheckIcon, { className: icon({ size: "small" }) }) })] }, option.value)));
347
323
  }) }) }));
348
324
  }
349
325
  /*
@@ -370,5 +346,5 @@ const filterStack = css({
370
346
  });
371
347
  export function FilterPopover({ label, count = 0, open, onOpenChange, onClear, children, }) {
372
348
  const { labels } = useCiridaeLocale();
373
- return (_jsx(Popover, { open: open, onOpenChange: onOpenChange && ((next) => onOpenChange(next)), density: "compact", trigger: _jsxs("button", { type: "button", className: cx(trigger({ variant: "raised", size: "compact" }), slicerTrigger), "data-ciridae-ui": "", "data-edge": "toolbar", children: [_jsx(FunnelSimpleIcon, { className: icon({ size: "small" }) }), label ?? labels.select.filters, count > 0 && _jsx("span", { className: filterCount, children: count })] }), children: _jsxs("div", { className: filterStack, children: [children, onClear && count > 0 && (_jsx(Button, { variant: "ghost", className: css({ justifySelf: "start" }), onClick: onClear, children: labels.filters.clearAll }))] }) }));
349
+ return (_jsx(Popover, { open: open, onOpenChange: onOpenChange && ((next) => onOpenChange(next)), density: "compact", trigger: _jsxs("button", { type: "button", className: cx(trigger({ variant: "raised", size: "compact" }), slicerTrigger, toolbarEdge.className), "data-ciridae-ui": "", "data-edge": toolbarEdge["data-edge"], children: [_jsx(FunnelSimpleIcon, { className: icon({ size: "small" }) }), label ?? labels.select.filters, count > 0 && _jsx("span", { className: filterCount, children: count })] }), children: _jsxs("div", { className: filterStack, children: [children, onClear && count > 0 && (_jsx(Button, { variant: "ghost", className: css({ justifySelf: "start" }), onClick: onClear, children: labels.filters.clearAll }))] }) }));
374
350
  }
@@ -11,5 +11,5 @@ import { PanelSkeleton, SidebarRowsSkeleton } from "./skeletons.js";
11
11
  /** Names itself for assistive tech with the locale's "Loading" unless given an `aria-label`. */
12
12
  export function ShellSkeleton({ children, ...props }) {
13
13
  const { labels } = useCiridaeLocale();
14
- return (_jsxs(AppShell, { "aria-busy": "true", "aria-label": labels.common.loading, ...props, children: [_jsxs(AppSidebar, { children: [_jsxs(AppSidebarGroup, { children: [_jsx(Skeleton, { height: "control" }), _jsx(Skeleton, { height: "control" })] }), _jsxs(AppSidebarRail, { children: [_jsx(Skeleton, { shape: "circle" }), _jsx(Skeleton, { shape: "circle" })] }), _jsx(AppSidebarNav, { children: _jsx(SidebarRowsSkeleton, {}) }), _jsx(AppSidebarFooter, { children: _jsx(Skeleton, { shape: "text", width: "70%" }) })] }), _jsxs(AppMain, { children: [_jsx(AppHeader, { children: _jsx(Skeleton, { shape: "text", width: "medium" }) }), _jsx(AppContent, { children: children ?? _jsx(PanelSkeleton, { rows: 6 }) })] })] }));
14
+ return (_jsxs(AppShell, { "aria-busy": "true", "aria-label": labels.common.loading, ...props, children: [_jsxs(AppSidebar, { children: [_jsxs(AppSidebarGroup, { children: [_jsx(Skeleton, { height: "control" }), _jsx(Skeleton, { height: "control" })] }), _jsxs(AppSidebarRail, { children: [_jsx(Skeleton, { shape: "circle", style: { width: 32 } }), _jsx(Skeleton, { shape: "circle", style: { width: 32 } })] }), _jsx(AppSidebarNav, { children: _jsx(SidebarRowsSkeleton, {}) }), _jsx(AppSidebarFooter, { children: _jsx(Skeleton, { shape: "text", width: "70%" }) })] }), _jsxs(AppMain, { children: [_jsx(AppHeader, { children: _jsx(Skeleton, { shape: "text", width: "medium" }) }), _jsx(AppContent, { children: children ?? _jsx(PanelSkeleton, { rows: 6 }) })] })] }));
15
15
  }
@@ -50,8 +50,8 @@ export type AppBreadcrumbButtonProps = useRender.ComponentProps<"button"> & {
50
50
  */
51
51
  export declare function AppBreadcrumbButton({ className, render, href, ...props }: AppBreadcrumbButtonProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
52
52
  /**
53
- * The signed-in account as a menu trigger: avatar, name, and a detail line, or with `compact` the
54
- * avatar alone.
53
+ * The signed-in account as a menu trigger: avatar, name, and a detail line if given, or with
54
+ * `compact` the avatar alone.
55
55
  */
56
56
  export declare function AppAccountButton({ avatar, name, detail, trailing, compact, className, ...props }: ComponentProps<"button"> & {
57
57
  avatar: ReactNode;