@parallelworks/ui 0.51.0 → 0.52.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 (227) hide show
  1. package/README.md +155 -14
  2. package/dist/ai/agent/toolStrings.d.ts +2 -0
  3. package/dist/ai/agent/toolStrings.js +20 -18
  4. package/dist/ai/ai.css +12 -0
  5. package/dist/ai/components/AllocationSelector.js +5 -1
  6. package/dist/ai/components/AttachmentManager.js +51 -42
  7. package/dist/ai/components/AttachmentUpload.d.ts +4 -2
  8. package/dist/ai/components/AttachmentUpload.js +9 -9
  9. package/dist/ai/components/BlockedGroupBanner.js +8 -14
  10. package/dist/ai/components/BranchNavigator.js +11 -12
  11. package/dist/ai/components/ChatEmptyState.js +12 -19
  12. package/dist/ai/components/ChatInput.d.ts +4 -3
  13. package/dist/ai/components/ChatInput.js +27 -13
  14. package/dist/ai/components/ChatMessage.js +45 -121
  15. package/dist/ai/components/ChatMessageList.d.ts +5 -2
  16. package/dist/ai/components/ChatMessageList.js +89 -51
  17. package/dist/ai/components/ChatSidebar.js +11 -7
  18. package/dist/ai/components/ChatThread.js +1 -13
  19. package/dist/ai/components/DragOverlay.js +3 -3
  20. package/dist/ai/components/MessageAttachments.d.ts +10 -0
  21. package/dist/ai/components/MessageAttachments.js +78 -0
  22. package/dist/ai/components/ModelSelector.js +3 -3
  23. package/dist/ai/components/PastedText.js +2 -2
  24. package/dist/ai/components/ProviderIssueBanner.js +19 -23
  25. package/dist/ai/components/ShareDialog.js +2 -2
  26. package/dist/ai/components/agent/ActivityLine.d.ts +0 -2
  27. package/dist/ai/components/agent/ActivityLine.js +8 -51
  28. package/dist/ai/components/agent/AgentMessageParts.js +114 -53
  29. package/dist/ai/core/ChatProvider.d.ts +16 -5
  30. package/dist/ai/core/ChatProvider.js +41 -8
  31. package/dist/ai/core/config.js +4 -3
  32. package/dist/ai/index.d.ts +1 -1
  33. package/dist/ai/index.js +3 -3
  34. package/dist/ai/strings.d.ts +29 -3
  35. package/dist/ai/strings.js +81 -26
  36. package/dist/ai/types.d.ts +4 -0
  37. package/dist/ai/ui/Notice.d.ts +2 -2
  38. package/dist/ai/ui/Notice.js +29 -62
  39. package/dist/ai/ui/sidebar.d.ts +5 -3
  40. package/dist/ai/ui/sidebar.js +21 -9
  41. package/dist/components/ActionMenu.d.ts +1 -0
  42. package/dist/components/ActionMenu.js +6 -1
  43. package/dist/components/AppLink.d.ts +26 -0
  44. package/dist/components/AppLink.js +51 -0
  45. package/dist/components/Avatar.d.ts +2 -1
  46. package/dist/components/Avatar.js +4 -2
  47. package/dist/components/Button.d.ts +15 -5
  48. package/dist/components/Button.js +47 -40
  49. package/dist/components/Callout.d.ts +31 -7
  50. package/dist/components/Callout.js +50 -19
  51. package/dist/components/Card.d.ts +11 -6
  52. package/dist/components/Card.js +14 -8
  53. package/dist/components/Checkbox.js +2 -1
  54. package/dist/components/ColorInput.js +5 -3
  55. package/dist/components/Combobox.js +49 -19
  56. package/dist/components/ConfirmModal.d.ts +19 -8
  57. package/dist/components/ConfirmModal.js +31 -53
  58. package/dist/components/CopyButton.d.ts +38 -0
  59. package/dist/components/CopyButton.js +68 -0
  60. package/dist/components/CreateModal.js +118 -111
  61. package/dist/components/DatePicker.js +12 -8
  62. package/dist/components/DescriptionList.d.ts +25 -5
  63. package/dist/components/DescriptionList.js +56 -22
  64. package/dist/components/Disclosure.js +1 -1
  65. package/dist/components/Drawer.js +10 -16
  66. package/dist/components/Field.d.ts +15 -5
  67. package/dist/components/Field.js +40 -8
  68. package/dist/components/IconButton.d.ts +14 -5
  69. package/dist/components/IconButton.js +25 -14
  70. package/dist/components/InfoTip.d.ts +4 -1
  71. package/dist/components/InfoTip.js +2 -1
  72. package/dist/components/Input.d.ts +14 -8
  73. package/dist/components/Input.js +46 -31
  74. package/dist/components/Loader.js +1 -1
  75. package/dist/components/MenuEntries.d.ts +4 -0
  76. package/dist/components/MenuEntries.js +11 -12
  77. package/dist/components/Meter.js +5 -4
  78. package/dist/components/Modal.d.ts +28 -4
  79. package/dist/components/Modal.js +37 -14
  80. package/dist/components/Overview.d.ts +20 -12
  81. package/dist/components/Overview.js +14 -9
  82. package/dist/components/PageHeader.d.ts +47 -65
  83. package/dist/components/PageHeader.js +275 -153
  84. package/dist/components/PageHeaderAction.d.ts +67 -0
  85. package/dist/components/PageHeaderAction.js +107 -0
  86. package/dist/components/Pagination.js +6 -16
  87. package/dist/components/Popover.js +3 -2
  88. package/dist/components/Provider.d.ts +77 -19
  89. package/dist/components/Provider.js +139 -130
  90. package/dist/components/Radio.js +2 -1
  91. package/dist/components/RefreshButton.js +7 -8
  92. package/dist/components/RequiredMark.js +1 -1
  93. package/dist/components/Section.js +1 -1
  94. package/dist/components/Select.d.ts +4 -9
  95. package/dist/components/Select.js +24 -11
  96. package/dist/components/SettingModeOption.d.ts +1 -1
  97. package/dist/components/SettingModeOption.js +7 -1
  98. package/dist/components/SettingsCard.d.ts +5 -3
  99. package/dist/components/SettingsCard.js +27 -20
  100. package/dist/components/Slider.js +1 -1
  101. package/dist/components/StatusBadge.js +2 -2
  102. package/dist/components/Switch.js +5 -4
  103. package/dist/components/Table.d.ts +30 -20
  104. package/dist/components/Table.js +50 -73
  105. package/dist/components/Tabs.d.ts +3 -7
  106. package/dist/components/Tabs.js +6 -10
  107. package/dist/components/ThemeEditor.js +18 -8
  108. package/dist/components/Toast.d.ts +11 -3
  109. package/dist/components/Toast.js +45 -20
  110. package/dist/components/buttonClasses.js +1 -1
  111. package/dist/components/comboboxItems.d.ts +4 -0
  112. package/dist/components/comboboxItems.js +6 -1
  113. package/dist/components/dismiss.d.ts +12 -0
  114. package/dist/components/dismiss.js +19 -0
  115. package/dist/components/fieldBox.d.ts +4 -0
  116. package/dist/components/fieldBox.js +7 -3
  117. package/dist/components/floating.d.ts +3 -3
  118. package/dist/components/floating.js +1 -1
  119. package/dist/components/markdownCodeBreaks.js +3 -4
  120. package/dist/components/pageHeaderStore.d.ts +41 -0
  121. package/dist/components/pageHeaderStore.js +67 -0
  122. package/dist/components/pathBreaks.d.ts +8 -0
  123. package/dist/components/pathBreaks.js +28 -0
  124. package/dist/components/segmented.js +1 -1
  125. package/dist/components/shortcutKey.d.ts +2 -1
  126. package/dist/components/shortcutKey.js +19 -2
  127. package/dist/components/splitter.js +1 -1
  128. package/dist/components/useCssIsDark.js +40 -18
  129. package/dist/components/useTheme.js +9 -10
  130. package/dist/dev.d.ts +9 -0
  131. package/dist/dev.js +14 -0
  132. package/dist/editor/Editor.d.ts +3 -2
  133. package/dist/editor/Editor.js +11 -2
  134. package/dist/editor/EditorField.d.ts +2 -1
  135. package/dist/editor/EditorField.js +11 -29
  136. package/dist/editor/FormCodePanel.d.ts +7 -3
  137. package/dist/editor/FormCodePanel.js +14 -7
  138. package/dist/editor/Monaco.d.ts +14 -5
  139. package/dist/editor/Monaco.js +163 -112
  140. package/dist/editor/index.d.ts +5 -5
  141. package/dist/editor/index.js +3 -3
  142. package/dist/editor/themes.d.ts +8 -5
  143. package/dist/editor/themes.js +58 -30
  144. package/dist/editor/yaml.d.ts +6 -2
  145. package/dist/editor/yaml.js +11 -2
  146. package/dist/file-explorer/FileExplorer.js +143 -124
  147. package/dist/file-explorer/FileExplorerContext.js +1 -1
  148. package/dist/file-explorer/FilePreview/StreamedBadge.js +3 -1
  149. package/dist/file-explorer/FilePreview/Viewer.js +2 -2
  150. package/dist/file-explorer/FilePreview/index.js +4 -4
  151. package/dist/file-explorer/FileRow.js +1 -1
  152. package/dist/file-explorer/FileRowsWindow.js +34 -3
  153. package/dist/file-explorer/FileTable.js +3 -1
  154. package/dist/file-explorer/TreeView.js +37 -14
  155. package/dist/file-explorer/helpers.d.ts +2 -1
  156. package/dist/file-explorer/helpers.js +21 -20
  157. package/dist/file-explorer/toasts.js +27 -19
  158. package/dist/file-explorer/treeRows.d.ts +6 -0
  159. package/dist/file-explorer/treeRows.js +19 -1
  160. package/dist/file-explorer/useNearEnd.js +3 -6
  161. package/dist/form/Fields/CheckboxGroupField.js +12 -5
  162. package/dist/form/Fields/DurationField.js +1 -1
  163. package/dist/form/Fields/LabelField.js +1 -1
  164. package/dist/form/Fields/ObjectField.d.ts +1 -1
  165. package/dist/form/Fields/ObjectField.js +3 -2
  166. package/dist/form/Fields/StringField.js +16 -29
  167. package/dist/form/Fields/TextAreaField.js +3 -4
  168. package/dist/form/Form.js +3 -8
  169. package/dist/form/Wizard/WizardNavigation.js +0 -3
  170. package/dist/form/Wizard/utils.js +1 -1
  171. package/dist/form/fieldFrame.d.ts +1 -1
  172. package/dist/form/fieldFrame.js +2 -2
  173. package/dist/form/types/fieldComponentTypes.d.ts +2 -1
  174. package/dist/graph/AnnotationBanner.d.ts +1 -2
  175. package/dist/graph/AnnotationBanner.js +30 -32
  176. package/dist/graph/Connectors.js +4 -1
  177. package/dist/graph/DependencyGraph.js +2 -2
  178. package/dist/graph/GraphEditor.js +11 -9
  179. package/dist/graph/GraphEditorDialogs.js +1 -1
  180. package/dist/graph/GraphNode.js +7 -4
  181. package/dist/graph/InputDialog.js +1 -1
  182. package/dist/graph/InputsEditor.js +14 -11
  183. package/dist/graph/MatrixGroup.js +9 -3
  184. package/dist/graph/Reveal.js +18 -10
  185. package/dist/graph/ShortcutsButton.d.ts +11 -2
  186. package/dist/graph/ShortcutsButton.js +14 -3
  187. package/dist/graph/TreeView.js +35 -18
  188. package/dist/graph/WorkflowSettingsDialog.js +1 -1
  189. package/dist/graph/editorFields.js +1 -1
  190. package/dist/graph/editorRows.js +16 -10
  191. package/dist/graph/editorStrings.d.ts +7 -0
  192. package/dist/graph/editorStrings.js +8 -1
  193. package/dist/graph/inputRefs.js +1 -1
  194. package/dist/graph/settingsViews.js +11 -18
  195. package/dist/graph/stepWith.js +1 -1
  196. package/dist/icons.d.ts +2 -0
  197. package/dist/icons.js +4 -2
  198. package/dist/index.d.ts +12 -12
  199. package/dist/index.js +16 -16
  200. package/dist/list/ActionToolbar.d.ts +42 -6
  201. package/dist/list/ActionToolbar.js +297 -101
  202. package/dist/list/ListPage.d.ts +15 -23
  203. package/dist/list/ListPage.js +42 -43
  204. package/dist/list/ListTable.d.ts +32 -7
  205. package/dist/list/ListTable.js +48 -12
  206. package/dist/list/ListViewControls.js +1 -1
  207. package/dist/list/UserHoverCard.js +7 -15
  208. package/dist/list/index.d.ts +2 -2
  209. package/dist/list/index.js +2 -2
  210. package/dist/list/listSearch.js +12 -10
  211. package/dist/list/listView.d.ts +10 -1
  212. package/dist/list/listView.js +15 -1
  213. package/dist/logviewer/LogViewer.d.ts +4 -2
  214. package/dist/logviewer/LogViewer.js +87 -69
  215. package/dist/logviewer/logviewer.css +3 -2
  216. package/dist/reducedMotion.d.ts +5 -0
  217. package/dist/reducedMotion.js +20 -0
  218. package/dist/safeUrl.d.ts +1 -1
  219. package/dist/safeUrl.js +4 -2
  220. package/dist/styles.css +1 -1
  221. package/package.json +1 -1
  222. package/dist/components/Breadcrumbs.d.ts +0 -37
  223. package/dist/components/Breadcrumbs.js +0 -90
  224. package/dist/components/CopyToClipboard.d.ts +0 -17
  225. package/dist/components/CopyToClipboard.js +0 -66
  226. package/dist/components/Sort.d.ts +0 -9
  227. package/dist/components/Sort.js +0 -42
@@ -1,40 +1,29 @@
1
1
  import type { MouseEvent as ReactMouseEvent, ReactNode } from 'react';
2
2
  import type { OpenMenu } from '../components/ContextMenu.js';
3
3
  import { type MenuEntry } from '../components/MenuEntries.js';
4
- import { type PageHeaderBreadcrumb } from '../components/PageHeader.js';
5
4
  import type { ListView, ListViewChrome } from './listView.js';
6
5
  import { type RowAction } from './rowMenu.js';
7
- /** Negative offsets (admin/org) cancel the shell's pt-4 so rows don't peek through the gap above the pinned header. */
8
- declare const SECTION_STICKY_TOP: {
9
- readonly main: 'top-0';
10
- readonly kubernetes: 'top-0';
11
- readonly admin: '-top-4';
12
- readonly org: '-top-4';
13
- };
14
- export type ListSection = keyof typeof SECTION_STICKY_TOP;
15
6
  export declare function ListRowActionsProvider({ view, children, }: {
16
7
  view?: ListViewChrome | undefined;
17
8
  children: ReactNode;
18
9
  }): import("react").JSX.Element;
19
10
  export interface ListPageProps {
20
- section?: ListSection;
21
- /** Force edge-to-edge negative margin for a main-shell page nested under a padded route layout. */
22
- shellPadded?: boolean;
23
- /** Rendered inside a route layout that already owns the page header (e.g. a
24
- * detail page's tab body). Skips this page's own header so the global
25
- * breadcrumb strip stays visible, keeping only the toolbar. */
26
- embedded?: boolean;
27
- breadcrumbs?: PageHeaderBreadcrumb[] | undefined;
28
- title?: ReactNode;
29
- titleAction?: ReactNode;
30
- actions?: ReactNode;
11
+ /** Reaches past the host's content padding (`p-4`) to the container's
12
+ * edges, for a host whose page container is padded. A list runs edge to
13
+ * edge so its rows' hover and dividers span the page. */
14
+ bleed?: boolean | undefined;
15
+ /** The list's search, filters and view controls, above the rows. The page's
16
+ * trail and actions go in its `PageHeader`. */
31
17
  toolbar?: ReactNode;
32
18
  /** Omit on pages without a list view — rows show all actions by default. */
33
19
  view?: ListViewChrome | undefined;
34
20
  children: ReactNode;
35
21
  }
36
- export declare function ListPage({ section, shellPadded, embedded, breadcrumbs, title, titleAction, actions, toolbar, view, children, }: ListPageProps): import("react").JSX.Element;
22
+ /** A list filling the page: its toolbar stays put while only the rows scroll. */
23
+ export declare function ListPage({ bleed, toolbar, view, children }: ListPageProps): import("react").JSX.Element;
37
24
  export declare function ListRow({ href, onActivate, items, goTo, openMenu, pinnedActions, selected, selectionActive, onRowSelect, leading, className, children, }: {
25
+ /** Where the row goes. Its `NameCell` with the same href becomes a link
26
+ * covering the row; a click the link doesn't catch still goes there. */
38
27
  href: string | null;
39
28
  /** Plain-click handler for rows that open something in place instead of navigating. */
40
29
  onActivate?: (() => void) | undefined;
@@ -53,7 +42,11 @@ export declare function ListRow({ href, onActivate, items, goTo, openMenu, pinne
53
42
  leading?: ReactNode;
54
43
  children: ReactNode;
55
44
  }): import("react").JSX.Element;
56
- /** Name column min-width is by role, not position, so a leading checkbox doesn't shift it. */
45
+ /** Name column min-width is by role, not position, so a leading checkbox
46
+ * doesn't shift it. A column with an `orderBy` the view offers sorts by it.
47
+ * While the view's column headers are off, the header row collapses: its
48
+ * cells still size the columns, but nobody sees or hears them, as with any
49
+ * other display option that is off. */
57
50
  export declare function ListColumns<T>({ view }: {
58
51
  view: ListView<T>;
59
52
  }): import("react").JSX.Element;
@@ -62,4 +55,3 @@ export declare namespace ListColumns {
62
55
  }
63
56
  /** Stable `goTo` for whole-row navigation (`rowNavProps`, `ListRow`). */
64
57
  export declare function useListNavigate(): (to: string) => void;
65
- export {};
@@ -1,28 +1,14 @@
1
1
  import { cx } from "../cx.js";
2
2
  import { Tooltip } from "../components/Tooltip.js";
3
- import { useLink, useNavigation } from "../components/Provider.js";
3
+ import { useNavigation } from "../components/Provider.js";
4
+ import { AppLink, isPlainClick } from "../components/AppLink.js";
4
5
  import { isMenuDivider, isMenuSubmenu, useRunAction, warnIfInert } from "../components/MenuEntries.js";
5
- import { PageHeader } from "../components/PageHeader.js";
6
6
  import { Table } from "../components/Table.js";
7
- import { ListActionsHeader } from "./ListTable.js";
7
+ import { ListActionsHeader, StretchRowLinkContext } from "./ListTable.js";
8
8
  import { MoreButton, rowContextMenuProps, useRowMenuActive } from "./rowMenu.js";
9
9
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
10
  import { Children, cloneElement, createContext, isValidElement, useContext, useMemo } from "react";
11
11
  //#region src/list/ListPage.tsx
12
- /** Negative offsets (admin/org) cancel the shell's pt-4 so rows don't peek through the gap above the pinned header. */
13
- var SECTION_STICKY_TOP = {
14
- main: "top-0",
15
- kubernetes: "top-0",
16
- admin: "-top-4",
17
- org: "-top-4"
18
- };
19
- /** Whether the shell adds pt-4 padding that the edge-to-edge list must cancel with a negative margin. */
20
- var SECTION_SHELL_PADDED = {
21
- main: false,
22
- kubernetes: false,
23
- admin: true,
24
- org: true
25
- };
26
12
  /** Defaults mean rows outside a ListView fully show actions (no view = show everything). */
27
13
  var RowActionsContext = createContext({
28
14
  show: true,
@@ -50,20 +36,13 @@ function ListRowActionsProvider({ view, children }) {
50
36
  children
51
37
  });
52
38
  }
53
- function ListPage({ section = "main", shellPadded, embedded = false, breadcrumbs, title, titleAction, actions, toolbar, view, children }) {
54
- const cancelShellPadding = shellPadded ?? SECTION_SHELL_PADDED[section];
39
+ /** A list filling the page: its toolbar stays put while only the rows scroll. */
40
+ function ListPage({ bleed = false, toolbar, view, children }) {
55
41
  return /* @__PURE__ */ jsxs("div", {
56
- className: cx("flex h-full min-h-0 flex-col", cancelShellPadding && "-m-4"),
57
- children: [embedded ? toolbar && /* @__PURE__ */ jsx("div", {
58
- className: "px-4 py-1.5",
42
+ className: cx("flex h-full min-h-0 flex-col", bleed && "-m-4"),
43
+ children: [toolbar && /* @__PURE__ */ jsx("div", {
44
+ className: "shrink-0 px-4",
59
45
  children: toolbar
60
- }) : /* @__PURE__ */ jsx(PageHeader, {
61
- stickyTopClassName: SECTION_STICKY_TOP[section],
62
- breadcrumbs,
63
- title,
64
- titleAction,
65
- actions,
66
- toolbar
67
46
  }), /* @__PURE__ */ jsx("div", {
68
47
  className: "relative @container min-h-0 flex-1 overflow-auto",
69
48
  children: /* @__PURE__ */ jsx(ListRowActionsProvider, {
@@ -96,24 +75,29 @@ function ListRow({ href, onActivate, items, goTo, openMenu, pinnedActions, selec
96
75
  });
97
76
  return cloneElement(el, { className: cx(el.props.className, cls) });
98
77
  });
78
+ const stretch = !!href && !selectionActive;
99
79
  const handleRowClick = (e) => {
100
80
  if (e.target.closest("a,button,input,label")) return;
101
81
  if (selectionActive && onRowSelect) {
102
82
  onRowSelect(e);
103
83
  return;
104
84
  }
105
- if (href) goTo(href);
106
- else onActivate?.();
85
+ if (href) {
86
+ if (isPlainClick(e)) goTo(href);
87
+ } else onActivate?.();
107
88
  };
108
89
  const pinnedLabels = new Set((pinnedActions ?? []).map((a) => a.label));
109
90
  const hasOverflowItems = items.some((item) => !isMenuDivider(item) && (isMenuSubmenu(item) || !pinnedLabels.has(item.label)));
110
91
  return /* @__PURE__ */ jsxs("tr", {
111
- className: cx("group", selected ? "bg-blue-500/5" : menuActive ? "theme-hover" : "hover:theme-hover", (href || onActivate || selectionActive) && "cursor-pointer", className),
92
+ className: cx("group", selected ? "bg-(--theme-accent-subtle)/60" : menuActive ? "theme-hover" : "hover:theme-hover", (href || onActivate || selectionActive) && "cursor-pointer", stretch && "relative [&_:is(a,button,input,label):not([data-row-link])]:relative [&_:is(a,button,input,label):not([data-row-link])]:z-[1]", className),
112
93
  onClick: handleRowClick,
113
94
  ...rowContextMenuProps(openRowMenu, items),
114
95
  children: [
115
96
  leading,
116
- styledCells,
97
+ /* @__PURE__ */ jsx(StretchRowLinkContext.Provider, {
98
+ value: stretch,
99
+ children: styledCells
100
+ }),
117
101
  showActions && /* @__PURE__ */ jsx(Table.Item, {
118
102
  className: "relative py-2.5",
119
103
  children: /* @__PURE__ */ jsxs("div", {
@@ -137,18 +121,20 @@ function ListRow({ href, onActivate, items, goTo, openMenu, pinnedActions, selec
137
121
  }
138
122
  function PinnedActionButton({ action, visible }) {
139
123
  warnIfInert(action, "ListPage pinned action");
140
- const Link = useLink();
141
124
  const runAction = useRunAction();
142
125
  const reason = action.disabled ? action.disabledReason : void 0;
143
126
  const props = {
144
127
  "aria-label": reason ? `${action.label}. ${reason}` : action.label,
145
- className: cx("rounded p-1 cursor-pointer text-(--theme-muted-text-color) transition-colors hover:bg-(--theme-muted-panel-bg) hover:text-(--theme-app) aria-disabled:cursor-not-allowed aria-disabled:opacity-20 aria-disabled:hover:bg-transparent aria-disabled:hover:text-(--theme-muted-text-color) [&>svg]:h-4 [&>svg]:w-4", visible ? "opacity-100" : "opacity-0 group-hover:opacity-100 focus-visible:opacity-100")
128
+ className: cx("rounded p-1 cursor-pointer text-(--theme-muted-text-color) transition-colors hover:bg-(--theme-muted-panel-bg) hover:text-(--theme-app) focus-visible:outline-2 focus-visible:outline-(--theme-link) aria-disabled:cursor-not-allowed aria-disabled:opacity-20 aria-disabled:hover:bg-transparent aria-disabled:hover:text-(--theme-muted-text-color) [&>svg]:h-4 [&>svg]:w-4", visible ? "opacity-100" : "opacity-0 group-hover:opacity-100 focus-visible:opacity-100")
146
129
  };
147
130
  return /* @__PURE__ */ jsx(Tooltip, {
148
131
  content: reason ?? action.label,
149
132
  side: "bottom",
150
- children: action.href && !action.disabled ? /* @__PURE__ */ jsx(Link, {
133
+ children: action.href ? /* @__PURE__ */ jsx(AppLink, {
151
134
  href: action.href,
135
+ reloadDocument: action.reloadDocument,
136
+ target: action.target,
137
+ disabled: action.disabled,
152
138
  ...props,
153
139
  children: action.icon
154
140
  }) : /* @__PURE__ */ jsx("button", {
@@ -163,14 +149,27 @@ function PinnedActionButton({ action, visible }) {
163
149
  })
164
150
  });
165
151
  }
166
- /** Name column min-width is by role, not position, so a leading checkbox doesn't shift it. */
152
+ function columnSort(view, orderBy) {
153
+ if (view.orderBy !== orderBy) return "none";
154
+ return view.orderDir === "asc" ? "ascending" : "descending";
155
+ }
156
+ /** Name column min-width is by role, not position, so a leading checkbox
157
+ * doesn't shift it. A column with an `orderBy` the view offers sorts by it.
158
+ * While the view's column headers are off, the header row collapses: its
159
+ * cells still size the columns, but nobody sees or hears them, as with any
160
+ * other display option that is off. */
167
161
  function ListColumns({ view }) {
168
- return /* @__PURE__ */ jsxs(Fragment, { children: [view.visibleColumns.map((col, i) => /* @__PURE__ */ jsx(Table.Header, {
169
- caps: false,
170
- className: cx(col.headerClassName, view.columnClasses[i]),
171
- style: col.alwaysVisible ? { minWidth: 200 } : void 0,
172
- children: col.label
173
- }, col.key)), /* @__PURE__ */ jsx(ListActionsHeader, { view })] });
162
+ return /* @__PURE__ */ jsxs(Fragment, { children: [view.visibleColumns.map((col, i) => {
163
+ const orderBy = view.orderBys.some((o) => o.value === col.orderBy) ? col.orderBy : void 0;
164
+ return /* @__PURE__ */ jsx(Table.Header, {
165
+ caps: false,
166
+ className: cx(col.headerClassName, view.columnClasses[i], !view.showColumnHeaders && "list-headers-off"),
167
+ style: col.alwaysVisible ? { minWidth: 200 } : void 0,
168
+ sort: orderBy === void 0 ? void 0 : columnSort(view, orderBy),
169
+ onSort: orderBy === void 0 ? void 0 : () => view.sortBy(orderBy),
170
+ children: col.label
171
+ }, col.key);
172
+ }), /* @__PURE__ */ jsx(ListActionsHeader, { view })] });
174
173
  }
175
174
  ListColumns.isTableHeader = true;
176
175
  /** Stable `goTo` for whole-row navigation (`rowNavProps`, `ListRow`). */
@@ -1,27 +1,39 @@
1
- import type { ReactNode } from 'react';
2
- /** table-auto + horizontal scroll; the name column has a min-width floor so it can't be crushed. */
1
+ import { type ReactNode } from 'react';
2
+ /** Marks a header cell of a list whose column headers are off; see `listTableProps`. */
3
+ export declare const HEADERS_OFF = "list-headers-off";
4
+ /** table-auto; the name column has a min-width floor so it can't be crushed.
5
+ * The header row sticks as the rows scroll, so the list's scrolling box
6
+ * (`ListPage`'s body) scrolls it sideways too. */
3
7
  export declare const listTableProps: {
4
8
  readonly borderless: true;
9
+ readonly stickyHeader: true;
5
10
  readonly panel: false;
6
11
  readonly rounded: false;
7
- readonly theadClassName: 'text-(--theme-muted-text-color)';
12
+ readonly theadClassName: 'bg-(--theme-app-bg) text-(--theme-muted-text-color) has-[.list-headers-off]:collapse';
8
13
  readonly tbodyClassName: 'divide-y border-b border-(--theme-border) divide-(--theme-border)';
9
14
  readonly className: 'table-auto w-full [&_td:first-child]:pl-4 [&_th:first-child]:pl-4 [&_td:last-child]:pr-4 [&_th:last-child]:pr-4';
10
15
  };
11
16
  /** Widths come only from the header row, so a windowed body cannot shift the
12
- * columns as different content scrolls into view. */
17
+ * columns as different content scrolls into view. The windowed table scrolls
18
+ * in its own box, so its header doesn't stick. */
13
19
  export declare const listTableFixedProps: {
14
20
  readonly borderless: true;
15
21
  readonly panel: false;
16
22
  readonly rounded: false;
17
- readonly theadClassName: 'text-(--theme-muted-text-color)';
23
+ readonly theadClassName: 'bg-(--theme-app-bg) text-(--theme-muted-text-color) has-[.list-headers-off]:collapse';
18
24
  readonly tbodyClassName: 'divide-y border-b border-(--theme-border) divide-(--theme-border)';
25
+ readonly stickyHeader: false;
19
26
  readonly className: string;
20
27
  };
28
+ /** True inside a `ListRow` whose name link should take the whole row's
29
+ * clicks: it navigates, and no selection claims plain clicks. */
30
+ export declare const StretchRowLinkContext: import("react").Context<boolean>;
21
31
  export declare function NameCell({ icon, name, subtitle, href, badge, title, children, }: {
22
32
  icon: ReactNode;
23
33
  name: string;
24
34
  subtitle?: string | undefined;
35
+ /** The name's link. Inside a `ListRow` with an `href`, the whole row is
36
+ * this link, so give both the same URL. */
25
37
  href: string | null;
26
38
  badge?: ReactNode;
27
39
  title?: string | undefined;
@@ -34,9 +46,10 @@ export declare function GroupRow({ label, count, colSpan, }: {
34
46
  count: number;
35
47
  colSpan: number;
36
48
  }): import("react").JSX.Element;
37
- export declare function ListError({ error, noun }: {
49
+ export declare function ListError({ error, noun, }: {
38
50
  error: unknown;
39
- noun?: string;
51
+ /** What failed to load, as the heading names it: "Couldn't load clusters". */
52
+ noun?: string | undefined;
40
53
  }): import("react").JSX.Element;
41
54
  export declare function ListSkeleton({ rows }: {
42
55
  rows?: number;
@@ -54,6 +67,18 @@ export declare function RowSelectCheckbox({ checked, label, onToggle, }: {
54
67
  /** Gets the press, a click or a key, so a host can extend a range on Shift. */
55
68
  onToggle: (event: Event) => void;
56
69
  }): import("react").JSX.Element;
70
+ /** The selection column's header: a checkbox that ticks every row, mixed while
71
+ * only some are. `onToggle` selects them all, or clears them once all are. */
72
+ export declare function ListSelectAllHeader({ selected, total, onToggle, }: {
73
+ /** How many rows are selected. */
74
+ selected: number;
75
+ /** How many rows there are to select. */
76
+ total: number;
77
+ onToggle: () => void;
78
+ }): import("react").JSX.Element;
79
+ export declare namespace ListSelectAllHeader {
80
+ var isTableHeader: boolean;
81
+ }
57
82
  /** Sizes to fit pinned buttons + ⋯; below ~44rem only ⋯ remains. Pass a view's
58
83
  * chrome (structurally: showActions + actionsWidth) or actionCount. */
59
84
  export declare function ListActionsHeader({ view, actionCount, className, }: {
@@ -1,31 +1,44 @@
1
1
  import { cx } from "../cx.js";
2
2
  import { ErrorIcon } from "../icons.js";
3
3
  import { positionKeys } from "../components/keys.js";
4
- import { useLink, useStrings } from "../components/Provider.js";
4
+ import { useStrings } from "../components/Provider.js";
5
+ import { AppLink } from "../components/AppLink.js";
5
6
  import { Checkbox } from "../components/Checkbox.js";
6
7
  import { LoadingRegion, Skeleton } from "../components/Skeleton.js";
7
8
  import { Table } from "../components/Table.js";
8
9
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
+ import { createContext, useContext } from "react";
9
11
  //#region src/list/ListTable.tsx
10
12
  var NAME_TEXT = "font-medium text-[13px] leading-tight text-(--theme-app) truncate";
11
13
  var SUBTITLE_TEXT = "text-xs leading-tight text-(--theme-muted-text-color) truncate";
12
- /** table-auto + horizontal scroll; the name column has a min-width floor so it can't be crushed. */
14
+ /** Marks a header cell of a list whose column headers are off; see `listTableProps`. */
15
+ var HEADERS_OFF = "list-headers-off";
16
+ /** table-auto; the name column has a min-width floor so it can't be crushed.
17
+ * The header row sticks as the rows scroll, so the list's scrolling box
18
+ * (`ListPage`'s body) scrolls it sideways too. */
13
19
  var listTableProps = {
14
20
  borderless: true,
21
+ stickyHeader: true,
15
22
  panel: false,
16
23
  rounded: false,
17
- theadClassName: "text-(--theme-muted-text-color)",
24
+ theadClassName: "bg-(--theme-app-bg) text-(--theme-muted-text-color) has-[.list-headers-off]:collapse",
18
25
  tbodyClassName: "divide-y border-b border-(--theme-border) divide-(--theme-border)",
19
26
  className: "table-auto w-full [&_td:first-child]:pl-4 [&_th:first-child]:pl-4 [&_td:last-child]:pr-4 [&_th:last-child]:pr-4"
20
27
  };
21
28
  /** Widths come only from the header row, so a windowed body cannot shift the
22
- * columns as different content scrolls into view. */
29
+ * columns as different content scrolls into view. The windowed table scrolls
30
+ * in its own box, so its header doesn't stick. */
23
31
  var listTableFixedProps = {
24
32
  ...listTableProps,
33
+ stickyHeader: false,
25
34
  className: listTableProps.className.replace("table-auto", "table-fixed")
26
35
  };
36
+ /** True inside a `ListRow` whose name link should take the whole row's
37
+ * clicks: it navigates, and no selection claims plain clicks. */
38
+ var StretchRowLinkContext = createContext(false);
39
+ var STRETCHED_LINK = "after:absolute after:inset-0 after:content-[''] focus-visible:outline-none focus-visible:after:outline-2 focus-visible:after:-outline-offset-2 focus-visible:after:outline-(--theme-link)";
27
40
  function NameCell({ icon, name, subtitle, href, badge, title, children }) {
28
- const Link = useLink();
41
+ const stretch = useContext(StretchRowLinkContext);
29
42
  const inner = /* @__PURE__ */ jsxs(Fragment, { children: [icon, /* @__PURE__ */ jsxs("div", {
30
43
  className: "min-w-0",
31
44
  children: [/* @__PURE__ */ jsxs("div", {
@@ -42,9 +55,10 @@ function NameCell({ icon, name, subtitle, href, badge, title, children }) {
42
55
  })] });
43
56
  return /* @__PURE__ */ jsxs(Table.Item, {
44
57
  className: "py-2.5",
45
- children: [href ? /* @__PURE__ */ jsx(Link, {
58
+ children: [href ? /* @__PURE__ */ jsx(AppLink, {
46
59
  href,
47
- className: "group/namecell flex items-center gap-2.5 min-w-0 cursor-pointer",
60
+ "data-row-link": stretch || void 0,
61
+ className: cx("group/namecell flex items-center gap-2.5 min-w-0 cursor-pointer", stretch && STRETCHED_LINK),
48
62
  children: inner
49
63
  }) : /* @__PURE__ */ jsx("div", {
50
64
  className: "flex items-center gap-2.5 min-w-0",
@@ -75,16 +89,20 @@ function GroupRow({ label, count, colSpan }) {
75
89
  ]
76
90
  }) });
77
91
  }
78
- function ListError({ error, noun = "data" }) {
92
+ function ListError({ error, noun }) {
79
93
  const { list: t } = useStrings();
80
94
  const message = error && typeof error === "object" && "message" in error ? String(error.message) : void 0;
81
95
  return /* @__PURE__ */ jsxs("div", {
96
+ role: "alert",
82
97
  className: "flex flex-col items-center justify-center gap-3 py-20 text-center",
83
- children: [/* @__PURE__ */ jsx(ErrorIcon, { className: "h-10 w-10 text-red-400 opacity-80" }), /* @__PURE__ */ jsxs("div", {
98
+ children: [/* @__PURE__ */ jsx(ErrorIcon, {
99
+ "aria-hidden": "true",
100
+ className: "h-10 w-10 text-(--theme-error)"
101
+ }), /* @__PURE__ */ jsxs("div", {
84
102
  className: "space-y-1",
85
103
  children: [/* @__PURE__ */ jsx("p", {
86
104
  className: "text-sm font-medium text-(--theme-app)",
87
- children: t.couldNotLoad(noun)
105
+ children: t.couldNotLoad(noun ?? t.data)
88
106
  }), message && /* @__PURE__ */ jsx("p", {
89
107
  className: "max-w-md text-xs text-(--theme-muted-text-color)",
90
108
  children: message
@@ -146,6 +164,24 @@ function RowSelectCheckbox({ checked, label, onToggle }) {
146
164
  }
147
165
  });
148
166
  }
167
+ /** The selection column's header: a checkbox that ticks every row, mixed while
168
+ * only some are. `onToggle` selects them all, or clears them once all are. */
169
+ function ListSelectAllHeader({ selected, total, onToggle }) {
170
+ const t = useStrings().list;
171
+ const all = total > 0 && selected >= total;
172
+ return /* @__PURE__ */ jsx(Table.Header, {
173
+ caps: false,
174
+ className: "w-10",
175
+ children: /* @__PURE__ */ jsx(Checkbox, {
176
+ "aria-label": t.selectAll,
177
+ checked: all,
178
+ indeterminate: selected > 0 && !all,
179
+ disabled: total === 0,
180
+ onCheckedChange: () => onToggle()
181
+ })
182
+ });
183
+ }
184
+ ListSelectAllHeader.isTableHeader = true;
149
185
  /** Sizes to fit pinned buttons + ⋯; below ~44rem only ⋯ remains. Pass a view's
150
186
  * chrome (structurally: showActions + actionsWidth) or actionCount. */
151
187
  function ListActionsHeader({ view, actionCount = 0, className }) {
@@ -158,7 +194,7 @@ function ListActionsHeader({ view, actionCount = 0, className }) {
158
194
  width,
159
195
  minWidth: width
160
196
  },
161
- className: cx("relative py-2 text-right @max-[43rem]:w-[52px]! @max-[43rem]:min-w-[52px]!", className),
197
+ className: cx("relative py-2 text-right @max-[44rem]:w-[52px]! @max-[44rem]:min-w-[52px]!", className),
162
198
  children: /* @__PURE__ */ jsx("span", {
163
199
  className: "sr-only",
164
200
  children: common.actions
@@ -167,4 +203,4 @@ function ListActionsHeader({ view, actionCount = 0, className }) {
167
203
  }
168
204
  ListActionsHeader.isTableHeader = true;
169
205
  //#endregion
170
- export { GroupRow, ListActionsHeader, ListError, ListSkeleton, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, actionsColumnWidth, listTableFixedProps, listTableProps };
206
+ export { GroupRow, HEADERS_OFF, ListActionsHeader, ListError, ListSelectAllHeader, ListSkeleton, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, StretchRowLinkContext, actionsColumnWidth, listTableFixedProps, listTableProps };
@@ -4,10 +4,10 @@ import { MenuCheckboxItem, MenuPopup, MenuSubmenuTrigger } from "../components/M
4
4
  import { Tooltip } from "../components/Tooltip.js";
5
5
  import { useStrings } from "../components/Provider.js";
6
6
  import { useQueryMatch } from "../components/search.js";
7
- import { Switch } from "../components/Switch.js";
8
7
  import { IconButton } from "../components/IconButton.js";
9
8
  import { Popover } from "../components/Popover.js";
10
9
  import { InfoTip } from "../components/InfoTip.js";
10
+ import { Switch } from "../components/Switch.js";
11
11
  import { Select } from "../components/Select.js";
12
12
  import { Menu } from "@base-ui/react/menu";
13
13
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -1,15 +1,12 @@
1
1
  import { cx } from "../cx.js";
2
- import { CopyIcon } from "../icons.js";
3
- import { Tooltip } from "../components/Tooltip.js";
4
2
  import { useLink, useStrings } from "../components/Provider.js";
5
- import { useCopy } from "../components/useCopy.js";
6
3
  import { Avatar } from "../components/Avatar.js";
4
+ import { CopyButton } from "../components/CopyButton.js";
7
5
  import { LoadingRegion, Skeleton } from "../components/Skeleton.js";
8
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
7
  //#region src/list/UserHoverCard.tsx
10
8
  function UserHoverCard({ username, name, avatarSrc, badge, href, linkComponent, loading = false, children }) {
11
- const { common, list: t } = useStrings();
12
- const { copy } = useCopy();
9
+ const { list: t } = useStrings();
13
10
  const SlotLink = useLink();
14
11
  const Link = linkComponent ?? SlotLink;
15
12
  const displayName = name || username;
@@ -36,7 +33,6 @@ function UserHoverCard({ username, name, avatarSrc, badge, href, linkComponent,
36
33
  })]
37
34
  })] });
38
35
  const copyLabel = EMAIL.test(username) ? t.labelEmail : t.labelUsername;
39
- const onCopy = () => void copy(username, { label: copyLabel });
40
36
  return /* @__PURE__ */ jsxs("div", {
41
37
  className: "w-64 p-3",
42
38
  children: [/* @__PURE__ */ jsxs("div", {
@@ -48,15 +44,11 @@ function UserHoverCard({ username, name, avatarSrc, badge, href, linkComponent,
48
44
  }) : /* @__PURE__ */ jsx("div", {
49
45
  className: "flex min-w-0 flex-1 items-start gap-3",
50
46
  children: header
51
- }), username && /* @__PURE__ */ jsx(Tooltip, {
52
- content: common.copyThing(copyLabel),
53
- children: /* @__PURE__ */ jsx("button", {
54
- type: "button",
55
- "aria-label": common.copyThing(copyLabel),
56
- onClick: onCopy,
57
- className: cx("shrink-0 cursor-pointer rounded p-0.5 text-(--theme-muted-text-color) opacity-0 transition hover:bg-(--theme-muted-panel-bg) hover:text-(--theme-app) [&>svg]:h-3.5 [&>svg]:w-3.5", "hover:opacity-100 group-has-[[data-copy-value]:hover]/copy:opacity-100 focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--theme-link) pointer-coarse:opacity-100"),
58
- children: /* @__PURE__ */ jsx(CopyIcon, { "aria-hidden": "true" })
59
- })
47
+ }), username && /* @__PURE__ */ jsx(CopyButton, {
48
+ text: username,
49
+ label: copyLabel,
50
+ size: "xs",
51
+ className: cx("-mr-1 opacity-0", "hover:opacity-100 group-has-[[data-copy-value]:hover]/copy:opacity-100 focus-visible:opacity-100 pointer-coarse:opacity-100")
60
52
  })]
61
53
  }), loading ? /* @__PURE__ */ jsxs(LoadingRegion, {
62
54
  className: "mt-3 space-y-2 border-t border-(--theme-border) pt-3",
@@ -1,6 +1,6 @@
1
1
  export { ActionToolbar, stickyToolbarClasses, type ToolbarAction, ToolbarButton, type ToolbarItem, } from './ActionToolbar.js';
2
- export { ListColumns, ListPage, type ListPageProps, ListRow, ListRowActionsProvider, type ListSection, useListNavigate, } from './ListPage.js';
3
- export { actionsColumnWidth, GroupRow, ListActionsHeader, ListError, ListSkeleton, listTableFixedProps, listTableProps, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, } from './ListTable.js';
2
+ export { ListColumns, ListPage, type ListPageProps, ListRow, ListRowActionsProvider, useListNavigate, } from './ListPage.js';
3
+ export { actionsColumnWidth, GroupRow, ListActionsHeader, ListError, ListSelectAllHeader, ListSkeleton, listTableFixedProps, listTableProps, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, } from './ListTable.js';
4
4
  export { ListDisplayMenu, ListFilterMenu } from './ListViewControls.js';
5
5
  export { ListCount, type ListSearch, ListSearchControl, useListSearch, } from './listSearch.js';
6
6
  export { type ActionDef, type ColumnDef, DEFAULT_ORDER, FAVORITE_ORDER, type FilterFacet, type ListView, type ListViewChrome, type ListViewConfig, type OrderByOption, useListView, } from './listView.js';
@@ -1,9 +1,9 @@
1
1
  import { ActionToolbar, ToolbarButton, stickyToolbarClasses } from "./ActionToolbar.js";
2
- import { GroupRow, ListActionsHeader, ListError, ListSkeleton, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, actionsColumnWidth, listTableFixedProps, listTableProps } from "./ListTable.js";
2
+ import { GroupRow, ListActionsHeader, ListError, ListSelectAllHeader, ListSkeleton, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, actionsColumnWidth, listTableFixedProps, listTableProps } from "./ListTable.js";
3
3
  import { MoreButton, assembleRowMenu, rowContextMenuProps, useCopySubmenu, useRowMenuActive } from "./rowMenu.js";
4
4
  import { ListColumns, ListPage, ListRow, ListRowActionsProvider, useListNavigate } from "./ListPage.js";
5
5
  import { ListDisplayMenu, ListFilterMenu } from "./ListViewControls.js";
6
6
  import { ListCount, ListSearchControl, useListSearch } from "./listSearch.js";
7
7
  import { DEFAULT_ORDER, FAVORITE_ORDER, useListView } from "./listView.js";
8
8
  import { HoverCardRow, UserHoverCard } from "./UserHoverCard.js";
9
- export { ActionToolbar, DEFAULT_ORDER, FAVORITE_ORDER, GroupRow, HoverCardRow, ListActionsHeader, ListColumns, ListCount, ListDisplayMenu, ListError, ListFilterMenu, ListPage, ListRow, ListRowActionsProvider, ListSearchControl, ListSkeleton, MoreButton, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, ToolbarButton, UserHoverCard, actionsColumnWidth, assembleRowMenu, listTableFixedProps, listTableProps, rowContextMenuProps, stickyToolbarClasses, useCopySubmenu, useListNavigate, useListSearch, useListView, useRowMenuActive };
9
+ export { ActionToolbar, DEFAULT_ORDER, FAVORITE_ORDER, GroupRow, HoverCardRow, ListActionsHeader, ListColumns, ListCount, ListDisplayMenu, ListError, ListFilterMenu, ListPage, ListRow, ListRowActionsProvider, ListSearchControl, ListSelectAllHeader, ListSkeleton, MoreButton, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, ToolbarButton, UserHoverCard, actionsColumnWidth, assembleRowMenu, listTableFixedProps, listTableProps, rowContextMenuProps, stickyToolbarClasses, useCopySubmenu, useListNavigate, useListSearch, useListView, useRowMenuActive };
@@ -1,5 +1,5 @@
1
- import { CloseIcon, RemoveIcon, SearchIcon } from "../icons.js";
2
1
  import { buttonClasses } from "../components/buttonClasses.js";
2
+ import { CloseIcon, RemoveIcon, SearchIcon } from "../icons.js";
3
3
  import { Tooltip } from "../components/Tooltip.js";
4
4
  import { usePersistedState, useStrings } from "../components/Provider.js";
5
5
  import { isTextEntryContext } from "../components/shortcutKey.js";
@@ -7,6 +7,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
7
7
  import { useCallback, useEffect, useRef, useState } from "react";
8
8
  //#region src/list/listSearch.tsx
9
9
  var NO_TERMS = [];
10
+ var COUNT = new Intl.NumberFormat();
10
11
  /** Pass `enabled: false` to suspend the global ⌘F/`/` hijack (e.g. when search isn't shown and browser Find should stay active). */
11
12
  function useListSearch(enabled = true, storageKey) {
12
13
  const [filter, setFilter] = useState("");
@@ -20,11 +21,6 @@ function useListSearch(enabled = true, storageKey) {
20
21
  useEffect(() => {
21
22
  if (!enabled) return;
22
23
  const onKey = (e) => {
23
- if (e.key === "Escape" && searchOpen) {
24
- setSearchOpen(false);
25
- setFilter("");
26
- return;
27
- }
28
24
  if (isTextEntryContext(e.target)) return;
29
25
  if ((e.key === "f" || e.key === "F") && (e.metaKey || e.ctrlKey)) {
30
26
  e.preventDefault();
@@ -36,7 +32,7 @@ function useListSearch(enabled = true, storageKey) {
36
32
  };
37
33
  window.addEventListener("keydown", onKey);
38
34
  return () => window.removeEventListener("keydown", onKey);
39
- }, [searchOpen, enabled]);
35
+ }, [enabled]);
40
36
  const openSearch = useCallback(() => setSearchOpen(true), []);
41
37
  const closeSearch = useCallback(() => {
42
38
  setSearchOpen(false);
@@ -94,8 +90,12 @@ function ListSearchControl({ search, placeholder, label: labelProp }) {
94
90
  value: search.filter,
95
91
  onChange: (e) => search.setFilter(e.target.value),
96
92
  onFocus: search.openSearch,
93
+ "aria-label": label,
97
94
  onKeyDown: (e) => {
98
- if (e.key === "Enter") {
95
+ if (e.key === "Escape" && search.searchOpen) {
96
+ e.preventDefault();
97
+ search.closeSearch();
98
+ } else if (e.key === "Enter") {
99
99
  e.preventDefault();
100
100
  search.addTerm(search.filter);
101
101
  } else if (e.key === "Backspace" && !search.filter) {
@@ -117,7 +117,7 @@ function ListSearchControl({ search, placeholder, label: labelProp }) {
117
117
  });
118
118
  return /* @__PURE__ */ jsx(Tooltip, {
119
119
  content: label,
120
- shortcut: "⌘+F",
120
+ shortcut: "/",
121
121
  children: /* @__PURE__ */ jsx("button", {
122
122
  type: "button",
123
123
  onClick: search.openSearch,
@@ -132,11 +132,13 @@ function ListSearchControl({ search, placeholder, label: labelProp }) {
132
132
  });
133
133
  }
134
134
  function ListCount({ total, matches, filterActive, noun, nounPlural }) {
135
+ const { list: t } = useStrings();
135
136
  return /* @__PURE__ */ jsx("div", {
136
137
  className: "flex items-center h-9",
137
138
  children: /* @__PURE__ */ jsx("span", {
139
+ role: "status",
138
140
  className: "text-[13px] text-(--theme-muted-text-color)",
139
- children: filterActive ? `${matches} of ${total} ${nounPlural}` : `${total} ${total === 1 ? noun : nounPlural}`
141
+ children: filterActive ? t.matchCount(COUNT.format(matches), COUNT.format(total), nounPlural) : t.count(COUNT.format(total), total === 1 ? noun : nounPlural)
140
142
  })
141
143
  });
142
144
  }
@@ -19,6 +19,10 @@ export interface ColumnDef {
19
19
  * column there: for something short that compares best lined up, like a
20
20
  * total. The first column never stacks; it is what the others stack under. */
21
21
  stack?: boolean;
22
+ /** The `value` of the {@link OrderByOption} this column sorts by. Its header
23
+ * then sorts the list: it shows, and sets, the same order as Display
24
+ * options' Ordering. */
25
+ orderBy?: string;
22
26
  }
23
27
  /** Column responsive importance; `low` drops before `medium`. See {@link ColumnDef.priority}. */
24
28
  type ColumnPriority = 'low' | 'medium';
@@ -65,6 +69,8 @@ export interface ListViewConfig<T> {
65
69
  actions?: ActionDef[];
66
70
  /** Explicit pinned-button count when not using actions+isPinned; sizes the actions column. */
67
71
  actionCount?: number;
72
+ /** Whether the column-header row shows before the user changes Display
73
+ * options (default true). */
68
74
  defaultShowColumnHeaders?: boolean;
69
75
  /** Whether row action affordances show before the user changes Display options. */
70
76
  defaultShowActions?: boolean;
@@ -111,6 +117,9 @@ export interface ListView<T> extends ListViewChrome {
111
117
  setOrderBy: (value: string) => void;
112
118
  orderDir: OrderDir;
113
119
  toggleOrderDir: () => void;
120
+ /** Orders by `value` ascending, or flips the direction when the list
121
+ * already orders by it: a sortable column header's click. */
122
+ sortBy: (value: string) => void;
114
123
  applyOrder: (rows: T[]) => T[];
115
124
  facets: FilterFacet<T>[];
116
125
  filters: Record<string, string[]>;
@@ -121,7 +130,7 @@ export interface ListView<T> extends ListViewChrome {
121
130
  actions: ActionDef[];
122
131
  isPinned: (key: string) => boolean;
123
132
  togglePinned: (key: string) => void;
124
- /** Whether the table renders its column-header row (default off). */
133
+ /** Whether the table renders its column-header row (default on). */
125
134
  showColumnHeaders: boolean;
126
135
  toggleColumnHeaders: () => void;
127
136
  /** Toggle whether rows expose action affordances. */