@parallelworks/ui 0.77.0 → 0.78.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) hide show
  1. package/README.md +30 -5
  2. package/dist/access/AccessDrawer.d.ts +28 -7
  3. package/dist/access/AccessDrawer.js +216 -35
  4. package/dist/access/AccessList.d.ts +19 -1
  5. package/dist/access/AccessList.js +220 -9
  6. package/dist/access/GrantView.d.ts +3 -1
  7. package/dist/access/GrantView.js +17 -4
  8. package/dist/access/PrincipalPicker.js +5 -5
  9. package/dist/access/focus.js +1 -1
  10. package/dist/access/index.d.ts +2 -2
  11. package/dist/access/index.js +2 -2
  12. package/dist/access/strings.d.ts +18 -0
  13. package/dist/access/strings.js +20 -2
  14. package/dist/access/types.d.ts +26 -0
  15. package/dist/ai/components/AllocationSelector.js +1 -1
  16. package/dist/ai/components/AttachmentManager.js +5 -5
  17. package/dist/ai/components/AttachmentUpload.js +2 -2
  18. package/dist/ai/components/BranchNavigator.js +1 -1
  19. package/dist/ai/components/ChatActivity.js +1 -1
  20. package/dist/ai/components/ChatEmptyState.js +3 -3
  21. package/dist/ai/components/ChatInput.js +3 -5
  22. package/dist/ai/components/ChatLayout.js +1 -1
  23. package/dist/ai/components/ChatMessage.js +1 -1
  24. package/dist/ai/components/ChatMessageList.js +2 -2
  25. package/dist/ai/components/ChatSidebar.js +3 -3
  26. package/dist/ai/components/ChatThread.js +4 -4
  27. package/dist/ai/components/ComposerAddMenu.js +9 -20
  28. package/dist/ai/components/ModelSelector.js +3 -3
  29. package/dist/ai/components/PastedText.js +5 -5
  30. package/dist/ai/components/SessionView.js +1 -1
  31. package/dist/ai/components/ShareDialog.js +3 -3
  32. package/dist/ai/components/SlashMenu.d.ts +31 -4
  33. package/dist/ai/components/SlashMenu.js +36 -63
  34. package/dist/ai/components/agent/ActivityLine.js +1 -1
  35. package/dist/ai/components/agent/AgentMessageParts.js +13 -13
  36. package/dist/ai/components/conversationActions.js +3 -3
  37. package/dist/ai/core/ChatProvider.js +1 -1
  38. package/dist/ai/core/config.js +1 -1
  39. package/dist/ai/ui/ConversationSidebar.js +3 -3
  40. package/dist/ai/ui/sidebar.js +3 -3
  41. package/dist/assets/yaml.worker-DOBV2lND.js +884 -0
  42. package/dist/components/ActionMenu.d.ts +38 -9
  43. package/dist/components/ActionMenu.js +48 -22
  44. package/dist/components/Avatar.d.ts +66 -6
  45. package/dist/components/Avatar.js +130 -18
  46. package/dist/components/Breadcrumbs.d.ts +4 -0
  47. package/dist/components/Breadcrumbs.js +26 -10
  48. package/dist/components/Button.d.ts +3 -2
  49. package/dist/components/Button.js +11 -1
  50. package/dist/components/Callout.d.ts +3 -2
  51. package/dist/components/Callout.js +3 -2
  52. package/dist/components/Card.d.ts +33 -9
  53. package/dist/components/Card.js +24 -8
  54. package/dist/components/Checkbox.d.ts +3 -1
  55. package/dist/components/Checkbox.js +4 -2
  56. package/dist/components/ColorInput.js +1 -1
  57. package/dist/components/Combobox.d.ts +23 -0
  58. package/dist/components/Combobox.js +252 -86
  59. package/dist/components/ConfirmModal.d.ts +26 -2
  60. package/dist/components/ConfirmModal.js +81 -15
  61. package/dist/components/ContextMenu.d.ts +29 -2
  62. package/dist/components/ContextMenu.js +60 -8
  63. package/dist/components/CopyButton.d.ts +21 -8
  64. package/dist/components/CopyButton.js +29 -9
  65. package/dist/components/CreateModal.d.ts +16 -4
  66. package/dist/components/CreateModal.js +27 -16
  67. package/dist/components/DatePicker.d.ts +31 -1
  68. package/dist/components/DatePicker.js +178 -47
  69. package/dist/components/DescriptionList.d.ts +13 -3
  70. package/dist/components/DescriptionList.js +43 -11
  71. package/dist/components/Disclosure.d.ts +10 -2
  72. package/dist/components/Disclosure.js +18 -7
  73. package/dist/components/Drawer.js +5 -5
  74. package/dist/components/EmptyState.d.ts +12 -4
  75. package/dist/components/EmptyState.js +18 -10
  76. package/dist/components/ErrorPage.d.ts +40 -0
  77. package/dist/components/ErrorPage.js +101 -0
  78. package/dist/components/ExternalLink.d.ts +4 -1
  79. package/dist/components/ExternalLink.js +4 -5
  80. package/dist/components/Field.d.ts +21 -6
  81. package/dist/components/Field.js +32 -16
  82. package/dist/components/FileButton.d.ts +19 -0
  83. package/dist/components/FileButton.js +32 -0
  84. package/dist/components/HoverCard.d.ts +26 -6
  85. package/dist/components/HoverCard.js +11 -5
  86. package/dist/components/IconButton.js +4 -2
  87. package/dist/components/InfoTip.js +3 -3
  88. package/dist/components/Input.d.ts +33 -3
  89. package/dist/components/Input.js +149 -14
  90. package/dist/components/Kbd.d.ts +40 -0
  91. package/dist/components/Kbd.js +168 -0
  92. package/dist/components/Listbox.d.ts +103 -0
  93. package/dist/components/Listbox.js +190 -0
  94. package/dist/components/Loader.d.ts +8 -2
  95. package/dist/components/Loader.js +16 -1
  96. package/dist/components/Markdown.js +1 -1
  97. package/dist/components/MarkdownImpl.js +1 -1
  98. package/dist/components/Menu.d.ts +20 -2
  99. package/dist/components/Menu.js +48 -19
  100. package/dist/components/MenuEntries.d.ts +38 -2
  101. package/dist/components/MenuEntries.js +266 -39
  102. package/dist/components/Meter.d.ts +50 -11
  103. package/dist/components/Meter.js +71 -21
  104. package/dist/components/Modal.d.ts +9 -3
  105. package/dist/components/Modal.js +68 -29
  106. package/dist/components/OneTimeCodeInput.js +1 -1
  107. package/dist/components/PageHeader.js +111 -7
  108. package/dist/components/PageHeaderAction.d.ts +8 -3
  109. package/dist/components/PageHeaderAction.js +122 -34
  110. package/dist/components/Pagination.d.ts +13 -1
  111. package/dist/components/Pagination.js +90 -19
  112. package/dist/components/Popover.d.ts +13 -2
  113. package/dist/components/Popover.js +20 -21
  114. package/dist/components/Provider.d.ts +140 -3
  115. package/dist/components/Provider.js +142 -12
  116. package/dist/components/RefreshButton.d.ts +13 -6
  117. package/dist/components/RefreshButton.js +17 -9
  118. package/dist/components/RunStatusIcon.d.ts +16 -7
  119. package/dist/components/RunStatusIcon.js +21 -8
  120. package/dist/components/Section.d.ts +11 -4
  121. package/dist/components/Section.js +33 -22
  122. package/dist/components/SegmentedControl.d.ts +16 -3
  123. package/dist/components/SegmentedControl.js +7 -3
  124. package/dist/components/Select.d.ts +19 -3
  125. package/dist/components/Select.js +114 -68
  126. package/dist/components/SelectionCopy.d.ts +10 -0
  127. package/dist/components/SelectionCopy.js +105 -0
  128. package/dist/components/SettingModeOption.js +1 -1
  129. package/dist/components/SettingsCard.js +1 -1
  130. package/dist/components/Skeleton.js +1 -1
  131. package/dist/components/StatusBadge.d.ts +55 -10
  132. package/dist/components/StatusBadge.js +91 -15
  133. package/dist/components/StickyBar.js +1 -1
  134. package/dist/components/Table.d.ts +22 -4
  135. package/dist/components/Table.js +46 -24
  136. package/dist/components/Tabs.d.ts +9 -4
  137. package/dist/components/Tabs.js +9 -4
  138. package/dist/components/Tag.d.ts +49 -0
  139. package/dist/components/Tag.js +199 -0
  140. package/dist/components/ThemeEditor.js +1 -1
  141. package/dist/components/Toast.js +1 -1
  142. package/dist/components/ToggleGroup.d.ts +4 -1
  143. package/dist/components/ToggleGroup.js +30 -13
  144. package/dist/components/Tooltip.d.ts +9 -3
  145. package/dist/components/Tooltip.js +55 -39
  146. package/dist/components/buttonClasses.d.ts +5 -2
  147. package/dist/components/buttonClasses.js +11 -10
  148. package/dist/components/chart.d.ts +69 -0
  149. package/dist/components/chart.js +145 -0
  150. package/dist/components/comboboxItems.d.ts +7 -1
  151. package/dist/components/comboboxItems.js +6 -2
  152. package/dist/components/dismiss.d.ts +5 -1
  153. package/dist/components/dismiss.js +6 -2
  154. package/dist/components/floating.d.ts +4 -4
  155. package/dist/components/floating.js +5 -5
  156. package/dist/components/headerActionsFit.d.ts +37 -0
  157. package/dist/components/headerActionsFit.js +81 -0
  158. package/dist/components/pageHeaderStore.d.ts +3 -0
  159. package/dist/components/pageHeaderStore.js +3 -0
  160. package/dist/components/pathBreaks.js +2 -2
  161. package/dist/components/popupContainer.d.ts +17 -0
  162. package/dist/components/popupContainer.js +69 -0
  163. package/dist/components/relativeTime.d.ts +21 -10
  164. package/dist/components/relativeTime.js +50 -22
  165. package/dist/components/segmented.d.ts +18 -4
  166. package/dist/components/segmented.js +48 -6
  167. package/dist/components/typedDate.d.ts +5 -0
  168. package/dist/components/typedDate.js +50 -0
  169. package/dist/components/useClosingValue.d.ts +4 -0
  170. package/dist/components/useClosingValue.js +12 -0
  171. package/dist/components/useCopy.d.ts +6 -0
  172. package/dist/components/useCopy.js +3 -3
  173. package/dist/components/useNarrow.d.ts +2 -0
  174. package/dist/components/useNarrow.js +16 -0
  175. package/dist/editor/Editor.js +1 -1
  176. package/dist/editor/FormCodePanel.js +1 -1
  177. package/dist/editor/Monaco.js +1 -1
  178. package/dist/editor/workers.js +1 -1
  179. package/dist/file-explorer/FileExplorer.js +16 -15
  180. package/dist/file-explorer/FileExplorerContext.js +2 -2
  181. package/dist/file-explorer/FilePreview/Viewer.js +3 -3
  182. package/dist/file-explorer/FilePreview/index.js +2 -2
  183. package/dist/file-explorer/FileRowsWindow.js +3 -3
  184. package/dist/file-explorer/FileTable.js +1 -1
  185. package/dist/file-explorer/LoadMoreRow.js +1 -1
  186. package/dist/file-explorer/TreeView.js +5 -5
  187. package/dist/file-explorer/helpers.js +2 -2
  188. package/dist/file-explorer/toasts.js +2 -2
  189. package/dist/form/Fields/CheckboxGroupField.js +1 -1
  190. package/dist/form/Fields/DropdownField.js +1 -1
  191. package/dist/form/Fields/DurationField.js +2 -2
  192. package/dist/form/Fields/MultiDropdownField.js +1 -1
  193. package/dist/form/Fields/NumberField.js +1 -1
  194. package/dist/form/Fields/ObjectField.js +1 -1
  195. package/dist/form/Fields/RangeField.js +1 -1
  196. package/dist/form/Fields/StringField.js +1 -1
  197. package/dist/form/Form.js +3 -3
  198. package/dist/form/FormLayout.js +1 -1
  199. package/dist/form/Wizard/WizardContainer.js +1 -1
  200. package/dist/form/Wizard/WizardNavigation.js +1 -1
  201. package/dist/form/Wizard/WizardStepContent.js +1 -1
  202. package/dist/form/fieldRegistry.js +1 -1
  203. package/dist/form/formEditing.js +1 -1
  204. package/dist/format.d.ts +17 -7
  205. package/dist/format.js +39 -16
  206. package/dist/graph/AnnotationBanner.js +2 -2
  207. package/dist/graph/Connectors.js +5 -5
  208. package/dist/graph/DependencyGraph.js +10 -9
  209. package/dist/graph/GraphEditor.js +4 -4
  210. package/dist/graph/GraphEditorDialogs.js +13 -12
  211. package/dist/graph/GraphNode.js +1 -1
  212. package/dist/graph/InputDialog.js +5 -5
  213. package/dist/graph/InputsEditor.js +7 -7
  214. package/dist/graph/MatrixGroup.js +2 -2
  215. package/dist/graph/ProblemsButton.js +3 -3
  216. package/dist/graph/Reveal.js +1 -1
  217. package/dist/graph/RunWarningsList.js +1 -1
  218. package/dist/graph/ShortcutsButton.js +25 -8
  219. package/dist/graph/StepSummaries.js +1 -1
  220. package/dist/graph/TreeView.js +2 -2
  221. package/dist/graph/WorkflowSettingsDialog.js +3 -3
  222. package/dist/graph/editorFields.js +3 -3
  223. package/dist/graph/editorPrimitives.js +1 -1
  224. package/dist/graph/editorRows.js +5 -5
  225. package/dist/graph/fieldProblems.js +2 -2
  226. package/dist/graph/inputRefs.js +7 -6
  227. package/dist/graph/stepWith.js +2 -2
  228. package/dist/index.d.ts +30 -21
  229. package/dist/index.js +20 -12
  230. package/dist/list/ActionToolbar.d.ts +4 -0
  231. package/dist/list/ActionToolbar.js +35 -12
  232. package/dist/list/ListFilterBar.d.ts +42 -0
  233. package/dist/list/ListFilterBar.js +789 -0
  234. package/dist/list/ListPage.d.ts +42 -4
  235. package/dist/list/ListPage.js +56 -22
  236. package/dist/list/ListTable.d.ts +27 -3
  237. package/dist/list/ListTable.js +48 -23
  238. package/dist/list/ListToolbar.d.ts +31 -0
  239. package/dist/list/ListToolbar.js +43 -0
  240. package/dist/list/ListViewControls.d.ts +8 -4
  241. package/dist/list/ListViewControls.js +22 -14
  242. package/dist/list/UserHoverCard.d.ts +8 -2
  243. package/dist/list/UserHoverCard.js +26 -13
  244. package/dist/list/filterModel.d.ts +122 -0
  245. package/dist/list/filterModel.js +308 -0
  246. package/dist/list/index.d.ts +6 -2
  247. package/dist/list/index.js +6 -2
  248. package/dist/list/listSearch.d.ts +1 -1
  249. package/dist/list/listSearch.js +8 -4
  250. package/dist/list/listView.d.ts +24 -2
  251. package/dist/list/listView.js +47 -45
  252. package/dist/list/rowMenu.d.ts +10 -5
  253. package/dist/list/rowMenu.js +17 -7
  254. package/dist/list/useListFilters.d.ts +68 -0
  255. package/dist/list/useListFilters.js +252 -0
  256. package/dist/logviewer/LogViewer.js +4 -4
  257. package/dist/styles/theme.css +94 -0
  258. package/dist/styles.css +1 -1
  259. package/package.json +11 -11
  260. package/dist/assets/yaml.worker-k9AdOqzf.js +0 -1441
  261. package/dist/components/fullscreen.d.ts +0 -4
  262. package/dist/components/fullscreen.js +0 -18
@@ -1,19 +1,23 @@
1
1
  import { cx } from "../cx.js";
2
- import { MenuCheckboxItem, MenuItem, MenuLinkItem, MenuPopup, MenuSeparator, MenuSubmenuTrigger } from "./Menu.js";
2
+ import { EmptyState } from "./EmptyState.js";
3
+ import { CheckIcon, ChevronLeftIcon, ChevronRightIcon, ExternalLinkIcon } from "../icons.js";
4
+ import { MenuCheckboxItem, MenuGroupLabel, MenuItem, MenuLinkItem, MenuPopup, MenuSeparator, MenuSubmenuTrigger } from "./Menu.js";
3
5
  import { isDev } from "../dev.js";
4
- import { keyedByContent } from "./keys.js";
5
6
  import { Tooltip } from "./Tooltip.js";
7
+ import { useStrings } from "./Provider.js";
6
8
  import { AppLink } from "./AppLink.js";
7
- import { EmptyState } from "./EmptyState.js";
9
+ import { keyedByContent } from "./keys.js";
8
10
  import { useQueryMatch } from "./search.js";
9
11
  import { useCopy } from "./useCopy.js";
12
+ import { useNarrow } from "./useNarrow.js";
10
13
  import { Menu } from "@base-ui/react/menu";
11
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
12
- import { useCallback, useState } from "react";
14
+ import { createContext, use, useCallback, useLayoutEffect, useRef, useState } from "react";
15
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
13
16
  //#region src/components/MenuEntries.tsx
14
17
  var menuDivider = { divider: true };
15
18
  var isMenuDivider = (entry) => "divider" in entry;
16
- var isMenuSubmenu = (entry) => "items" in entry;
19
+ var isMenuGroup = (entry) => "group" in entry;
20
+ var isMenuSubmenu = (entry) => "items" in entry && !("group" in entry);
17
21
  /** Gives a menu root its search's filter; `children` renders the root with the query. */
18
22
  function MenuFilter({ search, children }) {
19
23
  const [query, setQuery] = useState("");
@@ -26,13 +30,99 @@ function MenuFilter({ search, children }) {
26
30
  children: children(query)
27
31
  });
28
32
  }
33
+ var DrillContext = createContext(null);
29
34
  /** A menu's rows, with its search field when it has one. */
30
35
  function MenuEntries({ entries, search, query = "" }) {
36
+ const narrow = useNarrow();
37
+ const [drilled, setDrilled] = useState();
38
+ const [returnTo, setReturnTo] = useState();
39
+ useLayoutEffect(() => {
40
+ if (!returnTo) return;
41
+ const { label, popup } = returnTo;
42
+ const frame = requestAnimationFrame(() => {
43
+ for (const row of popup?.querySelectorAll("[data-drill]") ?? []) if (row.getAttribute("data-drill") === label) row.focus();
44
+ setReturnTo(void 0);
45
+ });
46
+ return () => cancelAnimationFrame(frame);
47
+ }, [returnTo]);
48
+ if (narrow && drilled) {
49
+ const back = () => {
50
+ setReturnTo({
51
+ label: drilled.submenu.label,
52
+ popup: drilled.popup
53
+ });
54
+ setDrilled(void 0);
55
+ };
56
+ return /* @__PURE__ */ jsx(DrilledSubmenu, {
57
+ submenu: drilled.submenu,
58
+ onBack: back
59
+ });
60
+ }
61
+ return /* @__PURE__ */ jsx(DrillContext, {
62
+ value: narrow ? (submenu, row) => setDrilled({
63
+ submenu,
64
+ popup: row.closest("[role=\"menu\"]")
65
+ }) : null,
66
+ children: /* @__PURE__ */ jsx(SearchedEntries, {
67
+ entries,
68
+ search,
69
+ query
70
+ })
71
+ });
72
+ }
73
+ /** A submenu opened in its parent's place: a row back to the parent, then its rows. */
74
+ function DrilledSubmenu({ submenu, onBack }) {
75
+ const t = useStrings().common;
76
+ const backRef = useRef(null);
77
+ useLayoutEffect(() => {
78
+ const frame = requestAnimationFrame(() => backRef.current?.focus());
79
+ return () => cancelAnimationFrame(frame);
80
+ }, []);
81
+ const onKeyDown = (e) => {
82
+ if (e.key === "ArrowLeft" && !(e.target instanceof HTMLInputElement)) {
83
+ e.preventDefault();
84
+ onBack();
85
+ }
86
+ };
87
+ return /* @__PURE__ */ jsxs("div", {
88
+ role: "none",
89
+ className: "contents",
90
+ onKeyDown,
91
+ children: [
92
+ /* @__PURE__ */ jsx(MenuItem, {
93
+ ref: backRef,
94
+ closeOnClick: false,
95
+ onClick: onBack,
96
+ className: "font-medium",
97
+ children: /* @__PURE__ */ jsx(RowContent, {
98
+ icon: /* @__PURE__ */ jsx(ChevronLeftIcon, {
99
+ "aria-hidden": "true",
100
+ className: "size-3 opacity-60"
101
+ }),
102
+ label: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
103
+ className: "sr-only",
104
+ children: [t.back, ": "]
105
+ }), submenu.label] })
106
+ })
107
+ }),
108
+ /* @__PURE__ */ jsx(MenuSeparator, {}),
109
+ /* @__PURE__ */ jsx(MenuFilter, {
110
+ search: submenu.search,
111
+ children: (query) => /* @__PURE__ */ jsx(MenuEntries, {
112
+ entries: submenu.items,
113
+ search: submenu.search,
114
+ query
115
+ })
116
+ })
117
+ ]
118
+ });
119
+ }
120
+ function SearchedEntries({ entries, search, query }) {
31
121
  const matches = useQueryMatch();
32
122
  if (!search) return /* @__PURE__ */ jsx(EntryRows, { entries });
33
123
  const needle = query.trim();
34
- const shown = needle ? searchMatches(entries, (a) => matches(needle, a.label, a.keywords)) : entries;
35
- return /* @__PURE__ */ jsxs(Fragment, { children: [
124
+ const shown = needle ? searchMatches(entries, (a) => matches(needle, a.label, a.description, a.keywords), (submenu) => matches(needle, submenu.label, submenu.keywords)) : entries;
125
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
36
126
  /* @__PURE__ */ jsx("div", {
37
127
  className: "sticky top-0 z-[1] bg-(--theme-app-bg) px-1 pt-0.5 pb-1",
38
128
  children: /* @__PURE__ */ jsx(Menu.Input, {
@@ -48,29 +138,131 @@ function MenuEntries({ entries, search, query = "" }) {
48
138
  })
49
139
  ] });
50
140
  }
51
- function searchMatches(entries, match) {
141
+ function searchMatches(entries, match, matchSubmenu) {
52
142
  const found = /* @__PURE__ */ new Map();
53
- const walk = (list) => {
54
- for (const entry of list) if (isMenuSubmenu(entry)) walk(entry.items);
55
- else if (!isMenuDivider(entry) && !found.has(entry.label) && match(entry)) found.set(entry.label, entry);
143
+ const walk = (list, all) => {
144
+ for (const entry of list) {
145
+ if (isMenuDivider(entry)) continue;
146
+ if (isMenuGroup(entry)) walk(entry.items, all);
147
+ else if (isMenuSubmenu(entry)) {
148
+ if (!entry.disabled) walk(entry.items, all || matchSubmenu(entry));
149
+ } else if (!found.has(entry.label) && (all || match(entry))) found.set(entry.label, entry);
150
+ }
56
151
  };
57
- walk(entries);
152
+ walk(entries, false);
58
153
  return [...found.values()];
59
154
  }
155
+ var entryKey = (entry) => isMenuDivider(entry) ? "divider" : isMenuGroup(entry) ? `group:${entry.group}` : entry.label;
60
156
  function EntryRows({ entries }) {
61
- return keyedByContent(entries, (entry) => isMenuDivider(entry) ? "divider" : entry.label).map(({ key, item }) => isMenuDivider(item) ? /* @__PURE__ */ jsx(MenuSeparator, {}, key) : isMenuSubmenu(item) ? /* @__PURE__ */ jsx(SubmenuRow, { submenu: item }, key) : /* @__PURE__ */ jsx(ActionRow, { action: item }, key));
157
+ return keyedByContent(entries, entryKey).map(({ key, item }) => isMenuDivider(item) ? /* @__PURE__ */ jsx(MenuSeparator, {}, key) : isMenuGroup(item) ? /* @__PURE__ */ jsxs(Menu.Group, { children: [/* @__PURE__ */ jsx(MenuGroupLabel, { children: item.group }), /* @__PURE__ */ jsx(EntryRows, { entries: item.items })] }, key) : isMenuSubmenu(item) ? /* @__PURE__ */ jsx(SubmenuRow, { submenu: item }, key) : /* @__PURE__ */ jsx(ActionRow, { action: item }, key));
158
+ }
159
+ /** A key as `aria-keyshortcuts` spells it: `⌘K` reads Meta+K, `⇧P` Shift+P. */
160
+ function ariaKeyShortcut(shortcut) {
161
+ if (!shortcut) return;
162
+ const names = {
163
+ "⌘": "Meta",
164
+ "⌃": "Control",
165
+ "⌥": "Alt",
166
+ "⇧": "Shift"
167
+ };
168
+ const parts = [];
169
+ let rest = shortcut.replace(/\s+/g, "");
170
+ while (rest.length > 1 && names[rest[0] ?? ""]) {
171
+ parts.push(names[rest[0] ?? ""] ?? "");
172
+ rest = rest.slice(1);
173
+ }
174
+ for (const part of rest.split("+").filter(Boolean)) parts.push(part.length === 1 ? part.toUpperCase() : part);
175
+ return parts.join("+");
176
+ }
177
+ /** A detail at the row's end, held to the first line's height so it sits beside the label
178
+ * even when a description adds a second. */
179
+ function RowEnd({ children, className }) {
180
+ return /* @__PURE__ */ jsx("span", {
181
+ "aria-hidden": "true",
182
+ className: cx("flex h-5 shrink-0 items-center in-data-[size=sm]:h-4", className),
183
+ children
184
+ });
185
+ }
186
+ var shortcutClassName = "ps-3 text-xs text-(--theme-muted-text-color) tabular-nums in-data-[size=sm]:text-[11px]";
187
+ /** A row's icon, label, second line and end details, laid out alike in every kind of row. */
188
+ function RowContent({ icon, label, description, end }) {
189
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
190
+ icon !== void 0 && icon !== null && /* @__PURE__ */ jsx("span", {
191
+ className: "flex h-5 shrink-0 items-center in-data-[size=sm]:h-4",
192
+ children: icon
193
+ }),
194
+ /* @__PURE__ */ jsxs("span", {
195
+ className: "flex min-w-0 flex-1 flex-col",
196
+ children: [/* @__PURE__ */ jsx("span", {
197
+ className: "wrap-break-word",
198
+ children: label
199
+ }), description && /* @__PURE__ */ jsx("span", {
200
+ className: "text-xs text-(--theme-muted-text-color) in-data-[size=sm]:text-[11px]",
201
+ children: description
202
+ })]
203
+ }),
204
+ end
205
+ ] });
62
206
  }
63
207
  function SubmenuRow({ submenu }) {
208
+ const reason = submenu.disabled ? submenu.disabledReason : void 0;
209
+ const drill = use(DrillContext);
210
+ if (drill) return /* @__PURE__ */ jsx(MenuItem, {
211
+ closeOnClick: false,
212
+ disabled: submenu.disabled,
213
+ "data-drill": submenu.label,
214
+ "aria-haspopup": "menu",
215
+ "aria-keyshortcuts": ariaKeyShortcut(submenu.shortcut),
216
+ onClick: (e) => drill(submenu, e.currentTarget),
217
+ onKeyDown: (e) => {
218
+ if (e.key === "ArrowRight") {
219
+ e.preventDefault();
220
+ drill(submenu, e.currentTarget);
221
+ }
222
+ },
223
+ children: /* @__PURE__ */ jsx(RowContent, {
224
+ icon: submenu.icon,
225
+ label: /* @__PURE__ */ jsxs(Fragment$1, { children: [submenu.label, reason && /* @__PURE__ */ jsxs("span", {
226
+ className: "sr-only",
227
+ children: [". ", reason]
228
+ })] }),
229
+ end: /* @__PURE__ */ jsxs(Fragment$1, { children: [submenu.shortcut && /* @__PURE__ */ jsx(RowEnd, {
230
+ className: shortcutClassName,
231
+ children: submenu.shortcut
232
+ }), /* @__PURE__ */ jsx(RowEnd, { children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: "size-3 opacity-60" }) })] })
233
+ })
234
+ });
64
235
  return /* @__PURE__ */ jsx(MenuFilter, {
65
236
  search: submenu.search,
66
- children: (query) => /* @__PURE__ */ jsxs(Menu.SubmenuRoot, { children: [/* @__PURE__ */ jsxs(MenuSubmenuTrigger, { children: [submenu.icon, submenu.label] }), /* @__PURE__ */ jsx(MenuPopup, {
67
- side: "inline-end",
68
- children: /* @__PURE__ */ jsx(MenuEntries, {
69
- entries: submenu.items,
70
- search: submenu.search,
71
- query
72
- })
73
- })] })
237
+ children: (query) => /* @__PURE__ */ jsxs(Menu.SubmenuRoot, {
238
+ disabled: submenu.disabled,
239
+ children: [/* @__PURE__ */ jsx(Tooltip, {
240
+ content: reason,
241
+ side: "right",
242
+ children: /* @__PURE__ */ jsx(MenuSubmenuTrigger, {
243
+ disabled: submenu.disabled,
244
+ "aria-keyshortcuts": ariaKeyShortcut(submenu.shortcut),
245
+ children: /* @__PURE__ */ jsx(RowContent, {
246
+ icon: submenu.icon,
247
+ label: /* @__PURE__ */ jsxs(Fragment$1, { children: [submenu.label, reason && /* @__PURE__ */ jsxs("span", {
248
+ className: "sr-only",
249
+ children: [". ", reason]
250
+ })] }),
251
+ end: submenu.shortcut && /* @__PURE__ */ jsx(RowEnd, {
252
+ className: shortcutClassName,
253
+ children: submenu.shortcut
254
+ })
255
+ })
256
+ })
257
+ }), /* @__PURE__ */ jsx(MenuPopup, {
258
+ side: "inline-end",
259
+ children: /* @__PURE__ */ jsx(MenuEntries, {
260
+ entries: submenu.items,
261
+ search: submenu.search,
262
+ query
263
+ })
264
+ })]
265
+ })
74
266
  });
75
267
  }
76
268
  var destructiveClassName = "text-(--theme-error-text) data-highlighted:text-(--theme-error-text)";
@@ -92,42 +284,77 @@ function useRunAction() {
92
284
  else action.onClick?.();
93
285
  }, [copy]);
94
286
  }
287
+ /** One action as a menu row: a link, an on/off choice, or a plain item. */
95
288
  function ActionRow({ action }) {
96
289
  warnIfInert(action, "MenuEntries");
97
290
  const runAction = useRunAction();
98
- const className = cx(action.destructive && destructiveClassName);
99
- const content = /* @__PURE__ */ jsxs(Fragment, { children: [action.icon, action.label] });
100
- if (action.href && !action.disabled) return /* @__PURE__ */ jsx(MenuLinkItem, {
101
- render: /* @__PURE__ */ jsx(AppLink, {
102
- href: action.href,
103
- reloadDocument: action.reloadDocument,
104
- target: action.target
291
+ const className = cx(action.description && "items-start", action.destructive && destructiveClassName, action.current && "font-medium");
292
+ const keyshortcuts = ariaKeyShortcut(action.shortcut);
293
+ const end = (trailing) => /* @__PURE__ */ jsxs(Fragment$1, { children: [
294
+ action.badge !== void 0 && action.badge !== null && action.badge !== false && /* @__PURE__ */ jsx(RowEnd, {
295
+ className: "ps-2",
296
+ children: action.badge
105
297
  }),
106
- className,
107
- children: content
108
- });
298
+ action.shortcut && /* @__PURE__ */ jsx(RowEnd, {
299
+ className: shortcutClassName,
300
+ children: action.shortcut
301
+ }),
302
+ trailing
303
+ ] });
304
+ if (action.href && !action.disabled) {
305
+ const newTab = action.target === "_blank";
306
+ return /* @__PURE__ */ jsx(MenuLinkItem, {
307
+ render: /* @__PURE__ */ jsx(AppLink, {
308
+ href: action.href,
309
+ reloadDocument: action.reloadDocument,
310
+ target: action.target,
311
+ "aria-current": action.current ? "page" : void 0
312
+ }),
313
+ "aria-keyshortcuts": keyshortcuts,
314
+ className,
315
+ children: /* @__PURE__ */ jsx(RowContent, {
316
+ icon: action.icon,
317
+ label: action.label,
318
+ description: action.description,
319
+ end: end(action.current ? /* @__PURE__ */ jsx(RowEnd, {
320
+ className: "ps-2",
321
+ children: /* @__PURE__ */ jsx(CheckIcon, { className: "size-3.5 opacity-80" })
322
+ }) : newTab ? /* @__PURE__ */ jsx(RowEnd, {
323
+ className: "ps-2 text-(--theme-muted-text-color)",
324
+ children: /* @__PURE__ */ jsx(ExternalLinkIcon, { className: "size-3" })
325
+ }) : void 0)
326
+ })
327
+ });
328
+ }
109
329
  const run = () => runAction(action);
110
330
  const reason = action.disabled ? action.disabledReason : void 0;
111
- const label = /* @__PURE__ */ jsxs(Fragment, { children: [content, reason && /* @__PURE__ */ jsxs("span", {
112
- className: "sr-only",
113
- children: [". ", reason]
114
- })] });
331
+ const content = (trailing) => /* @__PURE__ */ jsx(RowContent, {
332
+ icon: action.icon,
333
+ label: /* @__PURE__ */ jsxs(Fragment$1, { children: [action.label, reason && /* @__PURE__ */ jsxs("span", {
334
+ className: "sr-only",
335
+ children: [". ", reason]
336
+ })] }),
337
+ description: action.description,
338
+ end: end(trailing)
339
+ });
115
340
  return /* @__PURE__ */ jsx(Tooltip, {
116
341
  content: reason,
117
342
  side: "right",
118
343
  children: action.checked === void 0 ? /* @__PURE__ */ jsx(MenuItem, {
119
344
  disabled: action.disabled,
120
345
  onClick: run,
346
+ "aria-keyshortcuts": keyshortcuts,
121
347
  className,
122
- children: label
348
+ children: content()
123
349
  }) : /* @__PURE__ */ jsx(MenuCheckboxItem, {
124
350
  checked: action.checked,
125
351
  onCheckedChange: run,
126
352
  disabled: action.disabled,
353
+ "aria-keyshortcuts": keyshortcuts,
127
354
  className,
128
- children: label
355
+ children: content()
129
356
  })
130
357
  });
131
358
  }
132
359
  //#endregion
133
- export { MenuEntries, MenuFilter, isMenuDivider, isMenuSubmenu, menuDivider, useRunAction, warnIfInert };
360
+ export { ActionRow, MenuEntries, MenuFilter, ariaKeyShortcut, isMenuDivider, isMenuGroup, isMenuSubmenu, menuDivider, useRunAction, warnIfInert };
@@ -1,12 +1,16 @@
1
1
  import type { ReactNode } from 'react';
2
2
  /** A bar's color: the theme's accent, a status, or muted for something stopped. */
3
- export type BarTone = 'accent' | 'success' | 'warning' | 'error' | 'muted';
4
- declare const trackClassName: {
3
+ export type BarTone = 'accent' | 'info' | 'success' | 'warning' | 'error' | 'muted';
4
+ declare const trackHeight: {
5
5
  readonly sm: 'h-1';
6
6
  readonly md: 'h-1.5';
7
7
  readonly lg: 'h-2';
8
8
  };
9
- export type BarSize = keyof typeof trackClassName;
9
+ export type BarSize = keyof typeof trackHeight;
10
+ /** Where a bar's value shows: `beside` its label, over the bar on `tooltip` when
11
+ * room is short, at the bar's `end` on the same row (so a column of bars lines up),
12
+ * or `none`. */
13
+ export type BarValuePlacement = 'beside' | 'tooltip' | 'end' | 'none';
10
14
  interface BarProps {
11
15
  /** Shown above the bar, which it names. */
12
16
  label?: ReactNode;
@@ -15,15 +19,20 @@ interface BarProps {
15
19
  /** Shown under the bar, such as the amounts behind it. */
16
20
  detail?: ReactNode;
17
21
  size?: BarSize;
22
+ /** On the track, such as a fixed `w-24` so bars in a column line up whatever
23
+ * the value beside them reads. */
24
+ trackClassName?: string | undefined;
25
+ /** The locale the value is written in; the browser's own without it. */
26
+ locale?: string | undefined;
18
27
  className?: string;
19
28
  }
20
29
  /** The tone for `value` against a warning and an error level, for a measure
21
- * that's worse the higher it reads. */
22
- export declare function levelTone(value: number, warnAt: number, errorAt: number): BarTone;
30
+ * that's worse the higher it reads; `okTone` below the warning. */
31
+ export declare function levelTone(value: number, warnAt: number, errorAt: number, okTone?: BarTone): BarTone;
23
32
  /** How full something is, such as storage used or a quota spent: a reading,
24
- * not a task under way (that's `ProgressBar`). The bar turns amber, then red,
25
- * as it passes `warnAt` and `errorAt`; `tone` overrides that. */
26
- export declare function Meter({ value, min, max, warnAt, errorAt, tone, format, showValue, label, 'aria-label': ariaLabel, detail, size, className, }: BarProps & {
33
+ * not a task under way (that's `ProgressBar`). The bar is blue, then turns
34
+ * amber, then red, as it passes `warnAt` and `errorAt`; `tone` overrides that. */
35
+ export declare function Meter({ value, min, max, warnAt, errorAt, tone, okTone, format, showValue, valueText, getAriaValueText, minFill, locale, label, 'aria-label': ariaLabel, detail, size, trackClassName, className, }: BarProps & {
27
36
  value: number;
28
37
  min?: number;
29
38
  max?: number;
@@ -31,18 +40,48 @@ export declare function Meter({ value, min, max, warnAt, errorAt, tone, format,
31
40
  warnAt?: number;
32
41
  errorAt?: number;
33
42
  tone?: BarTone;
43
+ /** The tone below `warnAt`. Info by default, so the bar reads the same in every
44
+ * host whatever its accent. */
45
+ okTone?: BarTone | undefined;
34
46
  /** How the value itself reads; without it, as a percentage of the range. */
35
47
  format?: Intl.NumberFormatOptions;
36
- /** Beside the label, or over the bar on hover when room is short. */
37
- showValue?: 'beside' | 'tooltip';
48
+ showValue?: BarValuePlacement;
49
+ /** The value as shown, from its formatted text, such as "45% used". */
50
+ valueText?: ((formatted: string, value: number) => ReactNode) | undefined;
51
+ /** The value as read by assistive technology, such as "82%, near limit", for a
52
+ * level the bar's color alone shows. */
53
+ getAriaValueText?: ((formatted: string, value: number) => string) | undefined;
54
+ /** The narrowest the fill draws for a value above `min`, as a percent of the
55
+ * track, so a small reading still shows. */
56
+ minFill?: number | undefined;
38
57
  }): import("react").JSX.Element;
58
+ /** A further stretch of a progress bar, drawn after its value, such as work
59
+ * started but not yet done. */
60
+ export interface ProgressSegment {
61
+ /** In the same units as the bar's `max`. */
62
+ value: number;
63
+ tone?: BarTone | undefined;
64
+ /** At 60% strength, for a stretch that's less settled than the one before it. */
65
+ subtle?: boolean | undefined;
66
+ }
39
67
  /** How far a task has come, such as an upload, out of `max`; `null` while
40
68
  * that's not known. Name it with `label` or `aria-label`. */
41
- export declare function ProgressBar({ value, max, tone, status, label, 'aria-label': ariaLabel, detail, size, className, }: BarProps & {
69
+ export declare function ProgressBar({ value, max, tone, segments, status, format, showValue, valueText, getAriaValueText, locale, label, 'aria-label': ariaLabel, detail, size, trackClassName, className, }: BarProps & {
42
70
  value: number | null;
43
71
  max?: number;
44
72
  tone?: BarTone;
73
+ /** Further stretches after `value`, in one bar, such as started work after done
74
+ * work. `value` alone is what the bar reads as to assistive technology. */
75
+ segments?: ReadonlyArray<ProgressSegment> | undefined;
45
76
  /** Beside the label in place of the percentage, such as "Completed". */
46
77
  status?: ReactNode;
78
+ /** How the value itself reads; without it, as a percentage of `max`. */
79
+ format?: Intl.NumberFormatOptions | undefined;
80
+ /** `beside` the label, at the bar's `end` on the same row, or `none`. */
81
+ showValue?: Exclude<BarValuePlacement, 'tooltip'> | undefined;
82
+ /** The value as shown, from its formatted text, such as "3 of 8 done, 38%". */
83
+ valueText?: ((formatted: string | null, value: number | null) => ReactNode) | undefined;
84
+ /** The value as read by assistive technology. */
85
+ getAriaValueText?: ((formatted: string | null, value: number | null) => string) | undefined;
47
86
  }): import("react").JSX.Element;
48
87
  export {};
@@ -7,39 +7,56 @@ import { Progress } from "@base-ui/react/progress";
7
7
  //#region src/components/Meter.tsx
8
8
  var fillClassName = {
9
9
  accent: "bg-(--theme-accent)",
10
+ info: toneFill.info,
10
11
  success: toneFill.success,
11
12
  warning: toneFill.warning,
12
13
  error: toneFill.error,
13
14
  muted: toneFill.muted
14
15
  };
15
- var trackClassName = {
16
+ var trackHeight = {
16
17
  sm: "h-1",
17
18
  md: "h-1.5",
18
19
  lg: "h-2"
19
20
  };
20
21
  var labelRowClassName = "mb-1 flex items-baseline justify-between gap-2 text-[13px]";
21
22
  var detailClassName = "mt-1 text-[11px] tabular-nums text-(--theme-muted-text-color)";
23
+ var endRowClassName = "flex items-center gap-2";
24
+ var endValueClassName = "shrink-0 text-xs tabular-nums text-(--theme-muted-text-color)";
25
+ var trackBaseClassName = "w-full overflow-hidden rounded-full bg-(--theme-border)";
26
+ function trackClasses(size, end, own) {
27
+ return cx(trackBaseClassName, trackHeight[size], end && (own ? "shrink-0" : "min-w-0 flex-1"), own);
28
+ }
22
29
  /** The tone for `value` against a warning and an error level, for a measure
23
- * that's worse the higher it reads. */
24
- function levelTone(value, warnAt, errorAt) {
25
- return value >= errorAt ? "error" : value >= warnAt ? "warning" : "accent";
30
+ * that's worse the higher it reads; `okTone` below the warning. */
31
+ function levelTone(value, warnAt, errorAt, okTone = "info") {
32
+ return value >= errorAt ? "error" : value >= warnAt ? "warning" : okTone;
26
33
  }
27
34
  /** How full something is, such as storage used or a quota spent: a reading,
28
- * not a task under way (that's `ProgressBar`). The bar turns amber, then red,
29
- * as it passes `warnAt` and `errorAt`; `tone` overrides that. */
30
- function Meter$1({ value, min = 0, max = 100, warnAt = 80, errorAt = 90, tone, format, showValue = "beside", label, "aria-label": ariaLabel, detail, size = "md", className }) {
35
+ * not a task under way (that's `ProgressBar`). The bar is blue, then turns
36
+ * amber, then red, as it passes `warnAt` and `errorAt`; `tone` overrides that. */
37
+ function Meter$1({ value, min = 0, max = 100, warnAt = 80, errorAt = 90, tone, okTone = "info", format, showValue = "beside", valueText, getAriaValueText, minFill = 0, locale, label, "aria-label": ariaLabel, detail, size = "md", trackClassName, className }) {
31
38
  const clamped = Math.min(Math.max(value, min), max);
32
- const fill = fillClassName[tone ?? levelTone(clamped, warnAt, errorAt)];
33
- const shownValue = /* @__PURE__ */ jsx(Meter.Value, { className: "shrink-0 font-medium tabular-nums theme-text" });
39
+ const fill = fillClassName[tone ?? levelTone(clamped, warnAt, errorAt, okTone)];
40
+ const end = showValue === "end";
41
+ const shownValue = /* @__PURE__ */ jsx(Meter.Value, {
42
+ className: end ? endValueClassName : "shrink-0 font-medium tabular-nums theme-text",
43
+ children: valueText
44
+ });
34
45
  const track = /* @__PURE__ */ jsx(Meter.Track, {
35
- className: cx("w-full overflow-hidden rounded-full bg-(--theme-border)", trackClassName[size]),
36
- children: /* @__PURE__ */ jsx(Meter.Indicator, { className: cx("h-full rounded-full transition-[width] duration-300 motion-reduce:transition-none", fill) })
46
+ className: trackClasses(size, end, trackClassName),
47
+ children: /* @__PURE__ */ jsx(Meter.Indicator, {
48
+ className: cx("h-full rounded-full transition-[width] duration-300 motion-reduce:transition-none", fill),
49
+ style: minFill > 0 && clamped > min ? { minWidth: `${minFill}%` } : void 0
50
+ })
37
51
  });
52
+ const share = formatShare(clamped, min, max, format, locale);
38
53
  return /* @__PURE__ */ jsxs(Meter.Root, {
39
54
  value: clamped,
40
55
  min,
41
56
  max,
42
57
  format,
58
+ locale,
59
+ getAriaValueText,
43
60
  "aria-label": ariaLabel,
44
61
  className: cx("min-w-0", className),
45
62
  children: [
@@ -51,8 +68,11 @@ function Meter$1({ value, min = 0, max = 100, warnAt = 80, errorAt = 90, tone, f
51
68
  }), showValue === "beside" && shownValue]
52
69
  }),
53
70
  showValue === "tooltip" ? /* @__PURE__ */ jsx(Tooltip, {
54
- content: formatShare(clamped, min, max, format),
71
+ content: valueText ? valueText(share, clamped) : share,
55
72
  children: track
73
+ }) : end ? /* @__PURE__ */ jsxs("div", {
74
+ className: endRowClassName,
75
+ children: [track, shownValue]
56
76
  }) : track,
57
77
  detail !== void 0 && /* @__PURE__ */ jsx("div", {
58
78
  className: detailClassName,
@@ -61,29 +81,46 @@ function Meter$1({ value, min = 0, max = 100, warnAt = 80, errorAt = 90, tone, f
61
81
  ]
62
82
  });
63
83
  }
64
- function formatShare(value, min, max, format) {
65
- return format ? new Intl.NumberFormat(void 0, format).format(value) : new Intl.NumberFormat(void 0, { style: "percent" }).format((value - min) / (max - min || 1));
84
+ function formatShare(value, min, max, format, locale) {
85
+ return format ? new Intl.NumberFormat(locale, format).format(value) : new Intl.NumberFormat(locale, { style: "percent" }).format((value - min) / (max - min || 1));
66
86
  }
67
87
  /** How far a task has come, such as an upload, out of `max`; `null` while
68
88
  * that's not known. Name it with `label` or `aria-label`. */
69
- function ProgressBar({ value, max = 100, tone = "accent", status, label, "aria-label": ariaLabel, detail, size = "md", className }) {
89
+ function ProgressBar({ value, max = 100, tone = "accent", segments, status, format, showValue = "beside", valueText, getAriaValueText, locale, label, "aria-label": ariaLabel, detail, size = "md", trackClassName, className }) {
90
+ const end = showValue === "end";
91
+ const shownValue = status ?? (showValue === "none" ? null : /* @__PURE__ */ jsx(Progress.Value, {
92
+ className: end ? endValueClassName : "shrink-0 tabular-nums theme-muted-text",
93
+ children: valueText
94
+ }));
95
+ const stacked = value !== null && segments !== void 0 && segments.length > 0;
96
+ const track = /* @__PURE__ */ jsxs(Progress.Track, {
97
+ className: cx(trackClasses(size, end, trackClassName), stacked && "flex"),
98
+ children: [/* @__PURE__ */ jsx(Progress.Indicator, { className: cx("h-full transition-[width] duration-300 motion-reduce:transition-none", "data-indeterminate:w-1/3 data-indeterminate:animate-pulse", stacked ? "shrink-0" : "rounded-full", fillClassName[tone]) }), stacked && /* @__PURE__ */ jsx(Segments, {
99
+ segments,
100
+ from: value,
101
+ max
102
+ })]
103
+ });
70
104
  return /* @__PURE__ */ jsxs(Progress.Root, {
71
105
  value,
72
106
  max,
107
+ format,
108
+ locale,
109
+ getAriaValueText,
73
110
  "aria-label": ariaLabel,
74
111
  className: cx("min-w-0", className),
75
112
  children: [
76
- (label !== void 0 || status !== void 0) && /* @__PURE__ */ jsxs("div", {
113
+ (label !== void 0 || !end && status !== void 0) && /* @__PURE__ */ jsxs("div", {
77
114
  className: labelRowClassName,
78
115
  children: [/* @__PURE__ */ jsx(Progress.Label, {
79
116
  className: "min-w-0 truncate theme-muted-text",
80
117
  children: label
81
- }), status ?? /* @__PURE__ */ jsx(Progress.Value, { className: "shrink-0 tabular-nums theme-muted-text" })]
82
- }),
83
- /* @__PURE__ */ jsx(Progress.Track, {
84
- className: cx("w-full overflow-hidden rounded-full bg-(--theme-border)", trackClassName[size]),
85
- children: /* @__PURE__ */ jsx(Progress.Indicator, { className: cx("h-full rounded-full transition-[width] duration-300 motion-reduce:transition-none", "data-indeterminate:w-1/3 data-indeterminate:animate-pulse", fillClassName[tone]) })
118
+ }), !end && shownValue]
86
119
  }),
120
+ end ? /* @__PURE__ */ jsxs("div", {
121
+ className: endRowClassName,
122
+ children: [track, shownValue]
123
+ }) : track,
87
124
  detail !== void 0 && /* @__PURE__ */ jsx("div", {
88
125
  className: detailClassName,
89
126
  children: detail
@@ -91,5 +128,18 @@ function ProgressBar({ value, max = 100, tone = "accent", status, label, "aria-l
91
128
  ]
92
129
  });
93
130
  }
131
+ function Segments({ segments, from, max }) {
132
+ let used = Math.min(Math.max(from, 0), max);
133
+ return segments.map((segment, index) => {
134
+ const width = Math.min(Math.max(segment.value, 0), max - used);
135
+ used += width;
136
+ return /* @__PURE__ */ jsx("div", {
137
+ "aria-hidden": "true",
138
+ "data-segment": "",
139
+ className: cx("h-full shrink-0 transition-[width] duration-300 motion-reduce:transition-none", fillClassName[segment.tone ?? "accent"], segment.subtle && "opacity-60"),
140
+ style: { width: `${width / (max || 1) * 100}%` }
141
+ }, index);
142
+ });
143
+ }
94
144
  //#endregion
95
145
  export { Meter$1 as Meter, ProgressBar, levelTone };
@@ -1,5 +1,5 @@
1
1
  import { Dialog } from '@base-ui/react/dialog';
2
- import type { ReactNode } from 'react';
2
+ import { type FormEvent, type ReactNode } from 'react';
3
3
  /** The panel a modal draws by default; pass `panel={false}` to paint your own. */
4
4
  export declare const modalPanelClassName = "rounded-xl border border-(--theme-border) bg-(--theme-app-bg) text-(--theme-app) shadow-2xl";
5
5
  /** Names the modal for assistive technology; renders an `<h2>`. */
@@ -13,7 +13,7 @@ declare const sizeClasses: {
13
13
  readonly xl: 'w-[672px]';
14
14
  };
15
15
  export type ModalSize = keyof typeof sizeClasses;
16
- export interface ModalProps extends Omit<Dialog.Popup.Props, 'className' | 'children' | 'render' | 'title'> {
16
+ export interface ModalProps extends Omit<Dialog.Popup.Props, 'className' | 'children' | 'render' | 'title' | 'onSubmit'> {
17
17
  open: boolean;
18
18
  onClose: () => void;
19
19
  /** The header's heading, which names the modal. Without one, name it with
@@ -39,6 +39,12 @@ export interface ModalProps extends Omit<Dialog.Popup.Props, 'className' | 'chil
39
39
  panel?: boolean;
40
40
  /** The backdrop's dimming, for a modal that needs a darker or blurred stage. */
41
41
  backdropClassName?: string;
42
+ /** Adds a close button at the header's end, for a modal whose footer doesn't close it,
43
+ * such as one that only shows something. It needs no title. */
44
+ closeButton?: boolean | undefined;
45
+ /** Makes the header, body and footer one form, so a `type="submit"` button in the footer
46
+ * submits it, as Enter in a field does. The page never reloads: its default is prevented. */
47
+ onSubmit?: ((event: FormEvent<HTMLFormElement>) => void) | undefined;
42
48
  /** On the panel. */
43
49
  className?: string;
44
50
  /** The body. With a title or a footer it sits padded between them and scrolls
@@ -49,5 +55,5 @@ export interface ModalProps extends Omit<Dialog.Popup.Props, 'className' | 'chil
49
55
  * and focus returns to where it was on close. Give it a `title`, a `description`
50
56
  * and a `footer` for the standard layout, as a Drawer has, or lay out the whole
51
57
  * panel with the children. */
52
- export declare function Modal({ open, onClose, title, description, footer, size, alert, dismissible, align, panel, backdropClassName, className, children, ...popupProps }: ModalProps): import("react").JSX.Element;
58
+ export declare function Modal({ open, onClose, title, description, footer, size, alert, dismissible, align, panel, backdropClassName, closeButton, onSubmit, className, children, initialFocus, ...popupProps }: ModalProps): import("react").JSX.Element;
53
59
  export {};