@parallelworks/ui 0.18.0 → 0.19.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 (161) hide show
  1. package/README.md +86 -6
  2. package/dist/ai/adapter/memoryStore.d.ts +8 -0
  3. package/dist/ai/adapter/mock/index.d.ts +2 -0
  4. package/dist/ai/adapter/mock/index.js +81 -0
  5. package/dist/ai/adapter/mock/mockAdapter.d.ts +4 -0
  6. package/dist/ai/adapter/openai/createOpenAIStream.d.ts +9 -0
  7. package/dist/ai/adapter/openai/errors.d.ts +3 -0
  8. package/dist/ai/adapter/openai/index.d.ts +6 -0
  9. package/dist/ai/adapter/openai/index.js +334 -0
  10. package/dist/ai/adapter/openai/openaiAdapter.d.ts +9 -0
  11. package/dist/ai/adapter/openai/responses.d.ts +4 -0
  12. package/dist/ai/adapter/openai/sse.d.ts +24 -0
  13. package/dist/ai/adapter/openai/wire.d.ts +40 -0
  14. package/dist/ai/adapter/types.d.ts +131 -0
  15. package/dist/ai/agent/classifier.d.ts +2 -0
  16. package/dist/ai/agent/diff.d.ts +35 -0
  17. package/dist/ai/agent/diffHighlight.d.ts +6 -0
  18. package/dist/ai/agent/index.d.ts +6 -0
  19. package/dist/ai/agent/permissions.d.ts +11 -0
  20. package/dist/ai/agent/toolfmt.d.ts +18 -0
  21. package/dist/ai/agent/transcript.d.ts +38 -0
  22. package/dist/ai/ai.css +325 -0
  23. package/dist/ai/components/AllocationSelector.d.ts +4 -0
  24. package/dist/ai/components/AttachmentManager.d.ts +1 -0
  25. package/dist/ai/components/AttachmentUpload.d.ts +24 -0
  26. package/dist/ai/components/BlockedGroupBanner.d.ts +1 -0
  27. package/dist/ai/components/BranchNavigator.d.ts +8 -0
  28. package/dist/ai/components/ChatEmptyState.d.ts +3 -0
  29. package/dist/ai/components/ChatInput.d.ts +45 -0
  30. package/dist/ai/components/ChatLayout.d.ts +5 -0
  31. package/dist/ai/components/ChatMessage.d.ts +24 -0
  32. package/dist/ai/components/ChatMessageList.d.ts +35 -0
  33. package/dist/ai/components/ChatSidebar.d.ts +5 -0
  34. package/dist/ai/components/ChatThread.d.ts +3 -0
  35. package/dist/ai/components/ComposerChrome.d.ts +18 -0
  36. package/dist/ai/components/ComposerFrame.d.ts +16 -0
  37. package/dist/ai/components/DragOverlay.d.ts +5 -0
  38. package/dist/ai/components/ModelSelector.d.ts +6 -0
  39. package/dist/ai/components/ShareDialog.d.ts +11 -0
  40. package/dist/ai/components/SlashMenu.d.ts +28 -0
  41. package/dist/ai/components/agent/ActivityLine.d.ts +21 -0
  42. package/dist/ai/components/agent/AgentMessageParts.d.ts +54 -0
  43. package/dist/ai/components/agent/PermissionPicker.d.ts +11 -0
  44. package/dist/ai/components/attachmentKeys.d.ts +4 -0
  45. package/dist/ai/core/ChatProvider.d.ts +66 -0
  46. package/dist/ai/core/KeyboardShortcutsProvider.d.ts +5 -0
  47. package/dist/ai/core/chatReducer.d.ts +114 -0
  48. package/dist/ai/core/config.d.ts +47 -0
  49. package/dist/ai/core/events.d.ts +1 -0
  50. package/dist/ai/core/exportConversation.d.ts +4 -0
  51. package/dist/ai/core/greeting.d.ts +3 -0
  52. package/dist/ai/core/parts.d.ts +11 -0
  53. package/dist/ai/core/useDragDrop.d.ts +14 -0
  54. package/dist/ai/core/useKeyboardShortcuts.d.ts +12 -0
  55. package/dist/ai/core/useSSEStream.d.ts +34 -0
  56. package/dist/ai/index.d.ts +40 -0
  57. package/dist/ai/index.js +6645 -0
  58. package/dist/ai/strings.d.ts +167 -0
  59. package/dist/ai/types.d.ts +236 -0
  60. package/dist/ai/ui/Avatar.d.ts +8 -0
  61. package/dist/ai/ui/Dropdown.d.ts +31 -0
  62. package/dist/ai/ui/Markdown.d.ts +8 -0
  63. package/dist/ai/ui/Notice.d.ts +21 -0
  64. package/dist/ai/ui/buttons.d.ts +1 -0
  65. package/dist/ai/ui/codeBreaks.d.ts +16 -0
  66. package/dist/ai/ui/prose.d.ts +1 -0
  67. package/dist/ai/ui/sidebar.d.ts +81 -0
  68. package/dist/ai/utils.d.ts +4 -0
  69. package/dist/assets/{editor.worker-CuBRIwi-.js → editor.worker-BmHUIVG4.js} +194 -163
  70. package/dist/assets/{json.worker-8ytS02vz.js → json.worker-D8bJ92nw.js} +384 -338
  71. package/dist/assets/{yaml.worker-DJC-VUcv.js → yaml.worker-C4gyGAko.js} +15657 -13823
  72. package/dist/chunks/{CodePreviewEditor-4lx3kE-w.js → CodePreviewEditor-YqQBz1YL.js} +1 -1
  73. package/dist/chunks/ConfirmModal-aOR4O1QE.js +79 -0
  74. package/dist/chunks/{FieldWrapper-Bef01EWO.js → FieldWrapper-Cszwg-4B.js} +3 -7
  75. package/dist/chunks/{CreateModal-BY6DLmur.js → Markdown-CM-FenNE.js} +19 -91
  76. package/dist/chunks/Monaco-DJ8MW-Dq.js +110 -0
  77. package/dist/chunks/{PageHeader-l49p1nM9.js → PageHeader-0kG_iypP.js} +67 -65
  78. package/dist/chunks/{Provider-Ghua9PkN.js → Provider-CDYurraE.js} +54 -20
  79. package/dist/chunks/{SwitchToggle-CHPP8uZQ.js → SwitchToggle-DShNdcao.js} +135 -132
  80. package/dist/chunks/{Table-4OHjdKdO.js → Table-DU2MW8xz.js} +5 -3
  81. package/dist/chunks/{Tooltip-Crxo_eU_.js → Tooltip-K5HJr4_p.js} +31 -4
  82. package/dist/chunks/duration-BOv8Phl8.js +17 -0
  83. package/dist/chunks/{editor-9_uVtUyy.js → editor-Dk1rbV75.js} +32 -18
  84. package/dist/chunks/focus-D_iZJTW1.js +7 -0
  85. package/dist/chunks/{list-qrojZZNv.js → list-CJm875m7.js} +377 -370
  86. package/dist/chunks/logviewer-BTKuQWzw.js +828 -0
  87. package/dist/chunks/memoryStore-CC91S5LP.js +152 -0
  88. package/dist/chunks/types-Yy0NrerF.js +11 -0
  89. package/dist/components/Avatar.d.ts +1 -1
  90. package/dist/components/Breadcrumbs.d.ts +1 -1
  91. package/dist/components/Dropdown.d.ts +1 -1
  92. package/dist/components/EmptyState.d.ts +1 -1
  93. package/dist/components/Indicator.d.ts +2 -2
  94. package/dist/components/Input.d.ts +1 -1
  95. package/dist/components/LetterBadge.d.ts +1 -1
  96. package/dist/components/MarkdownImpl.d.ts +1 -1
  97. package/dist/components/Provider.d.ts +16 -65
  98. package/dist/components/SettingRow.d.ts +1 -1
  99. package/dist/components/SettingsGroup.d.ts +1 -1
  100. package/dist/components/focus.d.ts +2 -0
  101. package/dist/components/keys.d.ts +12 -0
  102. package/dist/duration.d.ts +3 -0
  103. package/dist/editor/Monaco.d.ts +1 -20
  104. package/dist/editor/index.d.ts +2 -1
  105. package/dist/editor/index.js +2 -2
  106. package/dist/editor/yaml.d.ts +5 -0
  107. package/dist/engine.d.ts +98 -0
  108. package/dist/file-explorer/FileExplorer.d.ts +11 -2
  109. package/dist/file-explorer/FileExplorerContext.d.ts +1 -1
  110. package/dist/file-explorer/FilePreview/CodePreviewEditor.d.ts +1 -1
  111. package/dist/file-explorer/FilePreview/previewType.d.ts +1 -1
  112. package/dist/file-explorer/FileRow.d.ts +1 -1
  113. package/dist/file-explorer/helpers.d.ts +4 -4
  114. package/dist/file-explorer/index.d.ts +3 -3
  115. package/dist/file-explorer/index.js +613 -647
  116. package/dist/file-explorer/lib/types.d.ts +6 -1
  117. package/dist/file-explorer/lib/utils.d.ts +1 -3
  118. package/dist/file-explorer/useNearEnd.d.ts +1 -1
  119. package/dist/form/FieldWrapper.d.ts +2 -2
  120. package/dist/form/Fields/ResourceOptionIcon.d.ts +1 -1
  121. package/dist/form/Form.d.ts +4 -21
  122. package/dist/form/index.d.ts +11 -12
  123. package/dist/form/index.js +1488 -1611
  124. package/dist/form/lib.d.ts +6 -1
  125. package/dist/form/types/fieldComponentTypes.d.ts +3 -2
  126. package/dist/form/types/fieldTypes.d.ts +6 -20
  127. package/dist/form/useParsedOpts.d.ts +1 -1
  128. package/dist/form/utils/{slurmDuration.d.ts → duration.d.ts} +2 -2
  129. package/dist/{workflow → graph}/MatrixGroup.d.ts +2 -2
  130. package/dist/{workflow → graph}/index.d.ts +1 -1
  131. package/dist/{workflow → graph}/index.js +215 -180
  132. package/dist/{workflow → graph}/types.d.ts +3 -3
  133. package/dist/icons.d.ts +20 -21
  134. package/dist/index.d.ts +38 -40
  135. package/dist/index.js +347 -346
  136. package/dist/list/ListTable.d.ts +1 -1
  137. package/dist/list/ListViewControls.d.ts +2 -2
  138. package/dist/list/index.d.ts +9 -9
  139. package/dist/list/index.js +2 -2
  140. package/dist/logviewer/LogViewer.d.ts +1 -1
  141. package/dist/logviewer/index.d.ts +1 -0
  142. package/dist/logviewer/index.js +2 -742
  143. package/dist/logviewer/segments.d.ts +9 -0
  144. package/dist/styles/fonts.css +4 -4
  145. package/dist/styles/theme.css +6 -4
  146. package/dist/styles.css +1 -1
  147. package/dist/theme/index.js +2 -2
  148. package/package.json +38 -14
  149. package/LICENSE +0 -202
  150. package/dist/chunks/Monaco-C1POOdVv.js +0 -1046
  151. package/dist/editor/usesGitlab.d.ts +0 -97
  152. package/dist/form/Fields/BucketReadOnlyField.d.ts +0 -7
  153. package/dist/form/types/workflow-schema.d.ts +0 -1145
  154. package/dist/form/utils/pwResolver.d.ts +0 -18
  155. package/dist/chunks/{MarkdownImpl-Bq4QhrtA.js → MarkdownImpl-CyeTVvSt.js} +1 -1
  156. /package/dist/{workflow → graph}/AnnotationBanner.d.ts +0 -0
  157. /package/dist/{workflow → graph}/Collapse.d.ts +0 -0
  158. /package/dist/{workflow → graph}/DependencyGraph.d.ts +0 -0
  159. /package/dist/{workflow → graph}/JobSummary.d.ts +0 -0
  160. /package/dist/{workflow → graph}/Reveal.d.ts +0 -0
  161. /package/dist/{workflow → graph}/TreeView.d.ts +0 -0
@@ -1,14 +1,14 @@
1
- import { a as usePersistedState, n as useLink, r as useNavigation, u as useStrings } from "./Provider-Ghua9PkN.js";
2
- import { T as Avatar, _ as useRowMenuActive, c as PageHeader, f as rowContextMenuProps, u as MoreButton } from "./PageHeader-l49p1nM9.js";
1
+ import { n as useLink, o as usePersistedState, r as useNavigation, u as useStrings } from "./Provider-CDYurraE.js";
3
2
  import { CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, DisplayIcon, ErrorIcon, FilterIcon, MenuIcon, SearchIcon, XIcon } from "../icons.js";
4
- import { n as TOOLTIP_ID, r as TooltipInfo } from "./Tooltip-Crxo_eU_.js";
5
- import { n as Table } from "./Table-4OHjdKdO.js";
3
+ import { n as TOOLTIP_ID, r as TooltipInfo, s as positionKeys } from "./Tooltip-K5HJr4_p.js";
4
+ import { _ as useRowMenuActive, c as PageHeader, f as rowContextMenuProps, u as MoreButton, w as Avatar } from "./PageHeader-0kG_iypP.js";
5
+ import { n as Table } from "./Table-DU2MW8xz.js";
6
+ import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react";
6
7
  import cx from "classnames";
7
8
  import { Children, Fragment, cloneElement, createContext, isValidElement, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
8
9
  import { useLocalStorage } from "usehooks-ts";
9
10
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
10
11
  import { createPortal } from "react-dom";
11
- import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react";
12
12
  //#region src/list/ActionToolbar.tsx
13
13
  var OverflowMenuContext = createContext(false);
14
14
  /** Omits data-tooltip-content when absent — react-tooltip treats an explicit undefined as an empty tooltip anchor. */
@@ -185,6 +185,74 @@ function ActionToolbar({ groups, right = [], sticky = false }) {
185
185
  });
186
186
  }
187
187
  //#endregion
188
+ //#region src/list/HoverCard.tsx
189
+ function useHoverCard() {
190
+ const triggerRef = useRef(null);
191
+ const [coords, setCoords] = useState(null);
192
+ const openTimer = useRef(void 0);
193
+ const closeTimer = useRef(void 0);
194
+ useEffect(() => () => {
195
+ clearTimeout(openTimer.current);
196
+ clearTimeout(closeTimer.current);
197
+ }, []);
198
+ const scheduleOpen = () => {
199
+ clearTimeout(closeTimer.current);
200
+ openTimer.current = setTimeout(() => {
201
+ const rect = triggerRef.current?.getBoundingClientRect();
202
+ if (rect) setCoords({
203
+ x: rect.left,
204
+ y: rect.bottom
205
+ });
206
+ }, 350);
207
+ };
208
+ const scheduleClose = () => {
209
+ clearTimeout(openTimer.current);
210
+ closeTimer.current = setTimeout(() => setCoords(null), 150);
211
+ };
212
+ const keepOpen = () => clearTimeout(closeTimer.current);
213
+ return {
214
+ triggerRef,
215
+ coords,
216
+ scheduleOpen,
217
+ scheduleClose,
218
+ keepOpen
219
+ };
220
+ }
221
+ function HoverCardSurface({ x, y, widthClassName, padded = true, relayoutKey, onKeepOpen, onLeave, children }) {
222
+ const ref = useRef(null);
223
+ const [pos, setPos] = useState(null);
224
+ useLayoutEffect(() => {
225
+ if (!ref.current) return;
226
+ const { width, height } = ref.current.getBoundingClientRect();
227
+ const pad = 8;
228
+ const left = Math.max(pad, Math.min(x, window.innerWidth - width - pad));
229
+ const top = y + 8 + height + pad > window.innerHeight ? Math.max(pad, y - 8 - height) : y + 8;
230
+ setPos({
231
+ left,
232
+ top
233
+ });
234
+ }, [
235
+ x,
236
+ y,
237
+ relayoutKey
238
+ ]);
239
+ return createPortal(/* @__PURE__ */ jsx("div", {
240
+ ref,
241
+ role: "none",
242
+ onMouseEnter: onKeepOpen,
243
+ onMouseLeave: onLeave,
244
+ onFocus: onKeepOpen,
245
+ onBlur: onLeave,
246
+ className: cx("fixed z-[9999] overflow-hidden rounded-xl border border-(--theme-border) bg-(--theme-panel-bg) shadow-xl backdrop-blur-xl", padded && "p-3", widthClassName),
247
+ style: {
248
+ left: pos?.left ?? x,
249
+ top: pos?.top ?? y + 8,
250
+ visibility: pos ? "visible" : "hidden"
251
+ },
252
+ children
253
+ }), document.querySelector(".ds-root") ?? document.body);
254
+ }
255
+ //#endregion
188
256
  //#region src/list/ListTable.tsx
189
257
  var NAME_TEXT = "font-medium text-[13px] leading-tight text-(--theme-app) truncate";
190
258
  var SUBTITLE_TEXT = "text-xs leading-tight text-(--theme-muted-text-color) truncate";
@@ -274,7 +342,7 @@ function ListSkeleton({ rows = 6 }) {
274
342
  role: "status",
275
343
  "aria-label": loading,
276
344
  className: "divide-y border-b border-(--theme-border) divide-(--theme-border)",
277
- children: [Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsxs("div", {
345
+ children: [positionKeys(rows, "skeleton").map((key) => /* @__PURE__ */ jsxs("div", {
278
346
  "aria-hidden": "true",
279
347
  className: "flex items-center gap-3 px-4 py-2.5 animate-pulse",
280
348
  children: [
@@ -285,7 +353,7 @@ function ListSkeleton({ rows = 6 }) {
285
353
  }),
286
354
  /* @__PURE__ */ jsx("div", { className: "ml-auto h-3 w-20 rounded bg-(--theme-muted-panel-bg)" })
287
355
  ]
288
- }, i)), /* @__PURE__ */ jsx("span", {
356
+ }, key)), /* @__PURE__ */ jsx("span", {
289
357
  className: "sr-only",
290
358
  children: loading
291
359
  })]
@@ -381,8 +449,9 @@ function ListRowActionsProvider({ view, children }) {
381
449
  });
382
450
  }
383
451
  function ListPage({ section = "main", shellPadded, embedded = false, breadcrumbs, title, titleAction, actions, toolbar, view, children }) {
452
+ const cancelShellPadding = shellPadded ?? SECTION_SHELL_PADDED[section];
384
453
  return /* @__PURE__ */ jsxs("div", {
385
- className: cx("flex h-full min-h-0 flex-col", (shellPadded ?? SECTION_SHELL_PADDED[section]) && "-m-4"),
454
+ className: cx("flex h-full min-h-0 flex-col", cancelShellPadding && "-m-4"),
386
455
  children: [embedded ? toolbar && /* @__PURE__ */ jsx("div", {
387
456
  className: "px-4 py-1.5",
388
457
  children: toolbar
@@ -475,252 +544,46 @@ function useListNavigate() {
475
544
  return useNavigation().goTo;
476
545
  }
477
546
  //#endregion
478
- //#region src/list/listView.tsx
479
- var PRIORITY_SHOW_CLASS = {
480
- low: "hidden @[52rem]:table-cell",
481
- medium: "hidden @[36rem]:table-cell"
482
- };
483
- function columnResponsiveClass(col) {
484
- return col.priority ? PRIORITY_SHOW_CLASS[col.priority] : "";
547
+ //#region src/list/textEntry.ts
548
+ /** Guards list-page shortcuts from firing inside inputs, terminals, editors, and dialogs. */
549
+ function isTextEntryContext(target) {
550
+ const el = target;
551
+ if (!el) return false;
552
+ if (el.tagName === "INPUT" || el.tagName === "TEXTAREA" || el.isContentEditable) return true;
553
+ return Boolean(el.closest?.(".xterm, .monaco-editor, .cm-editor, [role=\"dialog\"]"));
485
554
  }
486
- var DEFAULT_ORDER = "__default";
487
- var FAVORITE_ORDER = "__favorite";
488
- /** Persisted grouping + column-visibility + field-filter state for a list page,
489
- * driving the shared Display/Filter menus. */
490
- function useListView({ storageKey, sharedFiltersKey, columns, groupBys = [], defaultGroupBy, orderBys = [], defaultOrderBy, facets = [], actions = [], actionCount, defaultShowColumnHeaders = false, defaultShowActions = true, isFavorite, isOn, createdAt }) {
491
- const t = useStrings().list;
492
- const tiebreak = orderBys[0]?.compare;
493
- const effectiveOrderBys = useMemo(() => {
494
- if (!isFavorite) return orderBys;
495
- const favoriteRank = (row) => isFavorite(row) ? 0 : 1;
496
- const onRank = (row) => isOn?.(row) ? 0 : 1;
497
- const createdMs = (row) => {
498
- const v = createdAt?.(row);
499
- return v === null || v === void 0 || Number.isNaN(v) ? 0 : v;
555
+ //#endregion
556
+ //#region src/list/ListViewControls.tsx
557
+ /** Shared chrome for the toolbar control buttons (Filter, Display, Search) so
558
+ * they read as a consistent button cluster — matched by ListSearchControl. */
559
+ var listControlButtonClasses = "relative flex h-7 w-7 items-center justify-center rounded-md border border-(--theme-border) text-(--theme-muted-text-color) hover:bg-(--theme-muted-panel-bg) hover:text-(--theme-app) transition-colors";
560
+ var FLYOUT = "min-w-44 rounded-lg border border-(--theme-border) bg-(--theme-app-bg) py-1 shadow-lg";
561
+ /** Toggle pill shared by the Display menu's column + pinned-action pickers. */
562
+ function pillClass(on) {
563
+ return cx("whitespace-nowrap rounded-full border px-2.5 py-1 text-[12px] transition-colors", on ? "border-transparent bg-(--theme-muted-panel-bg) text-(--theme-app)" : "border-(--theme-border) text-(--theme-muted-text-color) hover:bg-(--theme-muted-panel-bg) hover:text-(--theme-app)");
564
+ }
565
+ var SECTION_LABEL = "mb-2 text-[11px] font-medium uppercase tracking-wide text-(--theme-muted-text-color)";
566
+ /** Portals into .ds-root so the panel escapes the toolbar's overflow clipping. */
567
+ function AnchoredPopover({ icon, label, active, shortcut, panelClassName, children }) {
568
+ const btnRef = useRef(null);
569
+ const panelRef = useRef(null);
570
+ const [pos, setPos] = useState(null);
571
+ const close = useCallback(() => setPos(null), []);
572
+ const open = useCallback(() => {
573
+ const r = btnRef.current?.getBoundingClientRect();
574
+ if (r) setPos({
575
+ right: Math.max(8, Math.round(window.innerWidth - r.right)),
576
+ top: Math.round(r.bottom) + 4
577
+ });
578
+ }, []);
579
+ useEffect(() => {
580
+ if (!pos) return;
581
+ const onKey = (e) => {
582
+ if (e.key === "Escape") close();
500
583
  };
501
- const newestFirst = (a, b) => createdMs(b) - createdMs(a);
502
- return [...[{
503
- value: DEFAULT_ORDER,
504
- label: t.orderDefault,
505
- compare: (a, b) => onRank(a) - onRank(b) || favoriteRank(a) - favoriteRank(b) || newestFirst(a, b) || (tiebreak?.(a, b) ?? 0)
506
- }, {
507
- value: FAVORITE_ORDER,
508
- label: t.orderFavorites,
509
- compare: (a, b) => favoriteRank(a) - favoriteRank(b) || (tiebreak?.(a, b) ?? 0)
510
- }], ...orderBys];
511
- }, [
512
- orderBys,
513
- isFavorite,
514
- isOn,
515
- createdAt,
516
- tiebreak,
517
- t
518
- ]);
519
- const defaultGroupByValue = defaultGroupBy ?? groupBys[0]?.value ?? "";
520
- const defaultOrderByValue = isFavorite ? DEFAULT_ORDER : defaultOrderBy ?? orderBys[0]?.value ?? "";
521
- const defaultHiddenKeys = useMemo(() => columns.filter((c) => c.defaultHidden && !c.alwaysVisible).map((c) => c.key), [columns]);
522
- const defaultPinnedKeys = useMemo(() => actions.filter((a) => a.defaultPinned).map((a) => a.key), [actions]);
523
- const [hiddenKeys, setHiddenKeys] = usePersistedState(`${storageKey}:hiddenColumns`, defaultHiddenKeys);
524
- const [pinnedKeys, setPinnedKeys] = usePersistedState(`${storageKey}:pinnedActions`, defaultPinnedKeys);
525
- const [groupBy, setGroupBy] = usePersistedState(`${storageKey}:groupBy`, defaultGroupByValue);
526
- const [showColumnHeaders, setShowColumnHeaders] = usePersistedState(`${storageKey}:showColumnHeaders`, defaultShowColumnHeaders);
527
- const [showActions, setShowActions] = usePersistedState(`${storageKey}:showActions`, defaultShowActions);
528
- const [alwaysShowActions, setAlwaysShowActions] = usePersistedState(`${storageKey}:alwaysShowActions`, false);
529
- const [orderBy, setOrderBy] = usePersistedState(`${storageKey}:orderBy`, defaultOrderByValue);
530
- const [orderDir, setOrderDir] = usePersistedState(`${storageKey}:orderDir`, "asc");
531
- const [localFilters, setLocalFilters] = usePersistedState(`${storageKey}:filters`, {});
532
- const [sharedFilters, setSharedFilters] = usePersistedState(`${sharedFiltersKey ?? storageKey}:sharedFilters`, {});
533
- const sharedKeys = useMemo(() => new Set(facets.filter((f) => f.shared).map((f) => f.key)), [facets]);
534
- const filters = useMemo(() => {
535
- if (sharedKeys.size === 0) return localFilters;
536
- const merged = {};
537
- for (const [key, values] of Object.entries(localFilters)) if (!sharedKeys.has(key)) merged[key] = values;
538
- for (const key of sharedKeys) {
539
- const values = sharedFilters[key];
540
- if (values?.length) merged[key] = values;
541
- }
542
- return merged;
543
- }, [
544
- localFilters,
545
- sharedFilters,
546
- sharedKeys
547
- ]);
548
- const pinnedCount = actionCount ?? pinnedKeys.filter((key) => actions.some((action) => action.key === key)).length;
549
- const effectiveShowActions = showActions || alwaysShowActions;
550
- const actionsWidth = effectiveShowActions ? actionsColumnWidth(pinnedCount) : 0;
551
- const isVisible = useCallback((key) => {
552
- const col = columns.find((c) => c.key === key);
553
- return Boolean(col?.alwaysVisible) || !hiddenKeys.includes(key);
554
- }, [columns, hiddenKeys]);
555
- const visibleColumns = useMemo(() => columns.filter((c) => !c.inline && (c.alwaysVisible || !hiddenKeys.includes(c.key))), [columns, hiddenKeys]);
556
- const columnClasses = useMemo(() => visibleColumns.map(columnResponsiveClass), [visibleColumns]);
557
- const toggleColumn = useCallback((key) => setHiddenKeys((prev) => prev.includes(key) ? prev.filter((k) => k !== key) : [...prev, key]), [setHiddenKeys]);
558
- const setFacetValues = useCallback((key, values) => {
559
- const update = (prev) => {
560
- const next = { ...prev };
561
- if (values.length) next[key] = values;
562
- else delete next[key];
563
- return next;
564
- };
565
- if (sharedKeys.has(key)) setSharedFilters(update);
566
- else setLocalFilters(update);
567
- }, [
568
- setLocalFilters,
569
- setSharedFilters,
570
- sharedKeys
571
- ]);
572
- const clearFilters = useCallback(() => {
573
- setLocalFilters({});
574
- if (sharedKeys.size > 0) setSharedFilters((prev) => {
575
- const next = { ...prev };
576
- for (const key of sharedKeys) delete next[key];
577
- return next;
578
- });
579
- }, [
580
- setLocalFilters,
581
- setSharedFilters,
582
- sharedKeys
583
- ]);
584
- const isPinned = useCallback((key) => pinnedKeys.includes(key), [pinnedKeys]);
585
- const togglePinned = useCallback((key) => setPinnedKeys((prev) => prev.includes(key) ? prev.filter((k) => k !== key) : [...prev, key]), [setPinnedKeys]);
586
- const toggleColumnHeaders = useCallback(() => setShowColumnHeaders((prev) => !prev), [setShowColumnHeaders]);
587
- const toggleShowActions = useCallback(() => {
588
- if (effectiveShowActions) {
589
- setShowActions(false);
590
- setAlwaysShowActions(false);
591
- } else setShowActions(true);
592
- }, [
593
- effectiveShowActions,
594
- setShowActions,
595
- setAlwaysShowActions
596
- ]);
597
- const toggleAlwaysShowActions = useCallback(() => {
598
- const next = !alwaysShowActions;
599
- setAlwaysShowActions(next);
600
- if (next) setShowActions(true);
601
- }, [
602
- alwaysShowActions,
603
- setAlwaysShowActions,
604
- setShowActions
605
- ]);
606
- const toggleOrderDir = useCallback(() => setOrderDir((d) => d === "asc" ? "desc" : "asc"), [setOrderDir]);
607
- const applyOrder = useCallback((rows) => {
608
- const opt = effectiveOrderBys.find((o) => o.value === orderBy);
609
- if (!opt) return rows;
610
- const sign = orderDir === "asc" ? 1 : -1;
611
- return [...rows].sort((a, b) => opt.compare(a, b) * sign);
612
- }, [
613
- effectiveOrderBys,
614
- orderBy,
615
- orderDir
616
- ]);
617
- const sameKeys = (a, b) => a.length === b.length && a.every((k) => b.includes(k));
618
- const isDisplayDefault = groupBy === defaultGroupByValue && orderBy === defaultOrderByValue && orderDir === "asc" && showColumnHeaders === defaultShowColumnHeaders && showActions === defaultShowActions && alwaysShowActions === false && sameKeys(hiddenKeys, defaultHiddenKeys) && sameKeys(pinnedKeys, defaultPinnedKeys);
619
- const resetDisplay = useCallback(() => {
620
- setGroupBy(defaultGroupByValue);
621
- setOrderBy(defaultOrderByValue);
622
- setOrderDir("asc");
623
- setShowColumnHeaders(defaultShowColumnHeaders);
624
- setShowActions(defaultShowActions);
625
- setAlwaysShowActions(false);
626
- setHiddenKeys(defaultHiddenKeys);
627
- setPinnedKeys(defaultPinnedKeys);
628
- }, [
629
- setGroupBy,
630
- setOrderBy,
631
- setOrderDir,
632
- defaultOrderByValue,
633
- setShowColumnHeaders,
634
- setShowActions,
635
- setAlwaysShowActions,
636
- setHiddenKeys,
637
- setPinnedKeys,
638
- defaultGroupByValue,
639
- defaultHiddenKeys,
640
- defaultPinnedKeys,
641
- defaultShowColumnHeaders,
642
- defaultShowActions
643
- ]);
644
- return {
645
- columns,
646
- visibleColumns,
647
- columnClasses,
648
- isVisible,
649
- pinnedCount,
650
- actionsWidth,
651
- toggleColumn,
652
- groupBys,
653
- groupBy,
654
- setGroupBy,
655
- orderBys: effectiveOrderBys,
656
- orderBy,
657
- setOrderBy,
658
- orderDir,
659
- toggleOrderDir,
660
- applyOrder,
661
- facets,
662
- filters,
663
- setFacetValues,
664
- clearFilters,
665
- activeFilterCount: useMemo(() => Object.values(filters).filter((v) => v && v.length > 0).length, [filters]),
666
- applyFilters: useCallback((rows) => rows.filter((row) => facets.every((f) => {
667
- const selected = filters[f.key];
668
- return !selected || selected.length === 0 ? true : f.matches(row, selected);
669
- })), [facets, filters]),
670
- actions,
671
- isPinned,
672
- togglePinned,
673
- showColumnHeaders,
674
- toggleColumnHeaders,
675
- showActions: effectiveShowActions,
676
- toggleShowActions,
677
- alwaysShowActions,
678
- toggleAlwaysShowActions,
679
- isDisplayDefault,
680
- resetDisplay
681
- };
682
- }
683
- //#endregion
684
- //#region src/list/textEntry.ts
685
- /** Guards list-page shortcuts from firing inside inputs, terminals, editors, and dialogs. */
686
- function isTextEntryContext(target) {
687
- const el = target;
688
- if (!el) return false;
689
- if (el.tagName === "INPUT" || el.tagName === "TEXTAREA" || el.isContentEditable) return true;
690
- return Boolean(el.closest?.(".xterm, .monaco-editor, .cm-editor, [role=\"dialog\"]"));
691
- }
692
- //#endregion
693
- //#region src/list/ListViewControls.tsx
694
- /** Shared chrome for the toolbar control buttons (Filter, Display, Search) so
695
- * they read as a consistent button cluster — matched by ListSearchControl. */
696
- var listControlButtonClasses = "relative flex h-7 w-7 items-center justify-center rounded-md border border-(--theme-border) text-(--theme-muted-text-color) hover:bg-(--theme-muted-panel-bg) hover:text-(--theme-app) transition-colors";
697
- var FLYOUT = "min-w-44 rounded-lg border border-(--theme-border) bg-(--theme-app-bg) py-1 shadow-lg";
698
- /** Toggle pill shared by the Display menu's column + pinned-action pickers. */
699
- function pillClass(on) {
700
- return cx("whitespace-nowrap rounded-full border px-2.5 py-1 text-[12px] transition-colors", on ? "border-transparent bg-(--theme-muted-panel-bg) text-(--theme-app)" : "border-(--theme-border) text-(--theme-muted-text-color) hover:bg-(--theme-muted-panel-bg) hover:text-(--theme-app)");
701
- }
702
- var SECTION_LABEL = "mb-2 text-[11px] font-medium uppercase tracking-wide text-(--theme-muted-text-color)";
703
- /** Portals into .ds-root so the panel escapes the toolbar's overflow clipping. */
704
- function AnchoredPopover({ icon, label, active, shortcut, panelClassName, children }) {
705
- const btnRef = useRef(null);
706
- const panelRef = useRef(null);
707
- const [pos, setPos] = useState(null);
708
- const close = useCallback(() => setPos(null), []);
709
- const open = useCallback(() => {
710
- const r = btnRef.current?.getBoundingClientRect();
711
- if (r) setPos({
712
- right: Math.max(8, Math.round(window.innerWidth - r.right)),
713
- top: Math.round(r.bottom) + 4
714
- });
715
- }, []);
716
- useEffect(() => {
717
- if (!pos) return;
718
- const onKey = (e) => {
719
- if (e.key === "Escape") close();
720
- };
721
- const onScroll = (e) => {
722
- if (e.target instanceof Node && panelRef.current?.contains(e.target)) return;
723
- close();
584
+ const onScroll = (e) => {
585
+ if (e.target instanceof Node && panelRef.current?.contains(e.target)) return;
586
+ close();
724
587
  };
725
588
  window.addEventListener("keydown", onKey);
726
589
  window.addEventListener("scroll", onScroll, true);
@@ -915,14 +778,16 @@ function useCloseOnOutsideClick(open, close) {
915
778
  function GroupingDropdown({ view }) {
916
779
  const t = useStrings().list;
917
780
  const [open, setOpen] = useState(false);
781
+ const ref = useCloseOnOutsideClick(open, useCallback(() => setOpen(false), []));
782
+ const current = view.groupBys.find((g) => g.value === view.groupBy);
918
783
  return /* @__PURE__ */ jsxs("div", {
919
- ref: useCloseOnOutsideClick(open, useCallback(() => setOpen(false), [])),
784
+ ref,
920
785
  className: "relative",
921
786
  children: [/* @__PURE__ */ jsxs("button", {
922
787
  type: "button",
923
788
  onClick: () => setOpen((o) => !o),
924
789
  className: "flex items-center gap-1.5 rounded-md border border-(--theme-border) px-2 py-1 text-[12px] text-(--theme-app) hover:bg-(--theme-muted-panel-bg)",
925
- children: [view.groupBys.find((g) => g.value === view.groupBy)?.label ?? t.none, /* @__PURE__ */ jsx(ChevronDownIcon, { className: "h-2.5 w-2.5 opacity-60" })]
790
+ children: [current?.label ?? t.none, /* @__PURE__ */ jsx(ChevronDownIcon, { className: "h-2.5 w-2.5 opacity-60" })]
926
791
  }), open && /* @__PURE__ */ jsx("div", {
927
792
  className: cx("absolute right-0 top-full z-20 mt-1", FLYOUT),
928
793
  children: view.groupBys.map((g) => /* @__PURE__ */ jsxs("button", {
@@ -940,14 +805,16 @@ function GroupingDropdown({ view }) {
940
805
  function OrderingDropdown({ view }) {
941
806
  const t = useStrings().list;
942
807
  const [open, setOpen] = useState(false);
808
+ const ref = useCloseOnOutsideClick(open, useCallback(() => setOpen(false), []));
809
+ const current = view.orderBys.find((o) => o.value === view.orderBy);
943
810
  return /* @__PURE__ */ jsxs("div", {
944
- ref: useCloseOnOutsideClick(open, useCallback(() => setOpen(false), [])),
811
+ ref,
945
812
  className: "relative",
946
813
  children: [/* @__PURE__ */ jsxs("button", {
947
814
  type: "button",
948
815
  onClick: () => setOpen((o) => !o),
949
816
  className: "flex items-center gap-1.5 rounded-md border border-(--theme-border) px-2 py-1 text-[12px] text-(--theme-app) hover:bg-(--theme-muted-panel-bg)",
950
- children: [view.orderBys.find((o) => o.value === view.orderBy)?.label ?? t.none, /* @__PURE__ */ jsx(ChevronDownIcon, { className: "h-2.5 w-2.5 opacity-60" })]
817
+ children: [current?.label ?? t.none, /* @__PURE__ */ jsx(ChevronDownIcon, { className: "h-2.5 w-2.5 opacity-60" })]
951
818
  }), open && /* @__PURE__ */ jsx("div", {
952
819
  className: cx("absolute right-0 top-full z-20 mt-1", FLYOUT),
953
820
  children: view.orderBys.map((o) => /* @__PURE__ */ jsxs("button", {
@@ -1049,6 +916,59 @@ function FilterFacetRow({ view, facet, open, onEnter, onLeave }) {
1049
916
  });
1050
917
  }
1051
918
  //#endregion
919
+ //#region src/list/ListPager.tsx
920
+ /** Page sizes offered by the pager's per-page select; first entry is the default. */
921
+ var LIST_PAGE_SIZES = [
922
+ 50,
923
+ 100,
924
+ 200
925
+ ];
926
+ /** Footer pager bar for paginated new-nav lists: "{start}–{end} of {total}" with prev/next. Renders nothing for a single page. */
927
+ function ListPager({ page, pageSize, total, onPageChange, pageSizes = LIST_PAGE_SIZES, onPageSizeChange, hasNext }) {
928
+ const t = useStrings().list;
929
+ const pageCount = Math.max(1, Math.ceil(total / pageSize));
930
+ const start = page * pageSize + 1;
931
+ const end = Math.min(total, (page + 1) * pageSize);
932
+ const smallest = Math.min(...pageSizes);
933
+ const showSizeSelect = !!onPageSizeChange && (total > smallest || pageSize > smallest);
934
+ if (pageCount <= 1 && !showSizeSelect) return null;
935
+ return /* @__PURE__ */ jsxs("div", {
936
+ className: "flex items-center justify-between px-2.5 h-10 shrink-0 border-t theme-border",
937
+ children: [/* @__PURE__ */ jsxs("div", {
938
+ className: "flex items-center gap-3 text-xs text-(--theme-muted-text-color)",
939
+ children: [/* @__PURE__ */ jsx("span", { children: t.pager(start, end, total) }), showSizeSelect && /* @__PURE__ */ jsxs("label", {
940
+ className: "flex items-center gap-1",
941
+ children: [t.perPage, /* @__PURE__ */ jsx("select", {
942
+ value: pageSize,
943
+ onChange: (e) => onPageSizeChange(Number(e.target.value)),
944
+ className: "theme-input rounded px-1.5 py-0.5 text-xs",
945
+ children: pageSizes.map((size) => /* @__PURE__ */ jsx("option", {
946
+ value: size,
947
+ children: size
948
+ }, size))
949
+ })]
950
+ })]
951
+ }), /* @__PURE__ */ jsxs("div", {
952
+ className: "flex items-center gap-1",
953
+ children: [/* @__PURE__ */ jsx("button", {
954
+ type: "button",
955
+ "aria-label": t.paginationPrevious,
956
+ disabled: page === 0,
957
+ onClick: () => onPageChange(page - 1),
958
+ className: cx(listControlButtonClasses, "disabled:opacity-40 disabled:pointer-events-none"),
959
+ children: /* @__PURE__ */ jsx(ChevronLeftIcon, { className: "w-3.5 h-3.5" })
960
+ }), /* @__PURE__ */ jsx("button", {
961
+ type: "button",
962
+ "aria-label": t.paginationNext,
963
+ disabled: hasNext === void 0 ? page >= pageCount - 1 : !hasNext,
964
+ onClick: () => onPageChange(page + 1),
965
+ className: cx(listControlButtonClasses, "disabled:opacity-40 disabled:pointer-events-none"),
966
+ children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: "w-3.5 h-3.5" })
967
+ })]
968
+ })]
969
+ });
970
+ }
971
+ //#endregion
1052
972
  //#region src/list/listSearch.tsx
1053
973
  var NO_TERMS = [];
1054
974
  /** Pass `enabled: false` to suspend the global ⌘F/`/` hijack (e.g. when search isn't shown and browser Find should stay active). */
@@ -1178,123 +1098,210 @@ function ListCount({ total, matches, filterActive, noun, nounPlural }) {
1178
1098
  });
1179
1099
  }
1180
1100
  //#endregion
1181
- //#region src/list/ListPager.tsx
1182
- /** Page sizes offered by the pager's per-page select; first entry is the default. */
1183
- var LIST_PAGE_SIZES = [
1184
- 50,
1185
- 100,
1186
- 200
1187
- ];
1188
- /** Footer pager bar for paginated new-nav lists: "{start}–{end} of {total}" with prev/next. Renders nothing for a single page. */
1189
- function ListPager({ page, pageSize, total, onPageChange, pageSizes = LIST_PAGE_SIZES, onPageSizeChange, hasNext }) {
1190
- const t = useStrings().list;
1191
- const pageCount = Math.max(1, Math.ceil(total / pageSize));
1192
- const start = page * pageSize + 1;
1193
- const end = Math.min(total, (page + 1) * pageSize);
1194
- const smallest = Math.min(...pageSizes);
1195
- const showSizeSelect = !!onPageSizeChange && (total > smallest || pageSize > smallest);
1196
- if (pageCount <= 1 && !showSizeSelect) return null;
1197
- return /* @__PURE__ */ jsxs("div", {
1198
- className: "flex items-center justify-between px-2.5 h-10 shrink-0 border-t theme-border",
1199
- children: [/* @__PURE__ */ jsxs("div", {
1200
- className: "flex items-center gap-3 text-xs text-(--theme-muted-text-color)",
1201
- children: [/* @__PURE__ */ jsx("span", { children: t.pager(start, end, total) }), showSizeSelect && /* @__PURE__ */ jsxs("label", {
1202
- className: "flex items-center gap-1",
1203
- children: [t.perPage, /* @__PURE__ */ jsx("select", {
1204
- value: pageSize,
1205
- onChange: (e) => onPageSizeChange(Number(e.target.value)),
1206
- className: "theme-input rounded px-1.5 py-0.5 text-xs",
1207
- children: pageSizes.map((size) => /* @__PURE__ */ jsx("option", {
1208
- value: size,
1209
- children: size
1210
- }, size))
1211
- })]
1212
- })]
1213
- }), /* @__PURE__ */ jsxs("div", {
1214
- className: "flex items-center gap-1",
1215
- children: [/* @__PURE__ */ jsx("button", {
1216
- type: "button",
1217
- "aria-label": t.paginationPrevious,
1218
- disabled: page === 0,
1219
- onClick: () => onPageChange(page - 1),
1220
- className: cx(listControlButtonClasses, "disabled:opacity-40 disabled:pointer-events-none"),
1221
- children: /* @__PURE__ */ jsx(ChevronLeftIcon, { className: "w-3.5 h-3.5" })
1222
- }), /* @__PURE__ */ jsx("button", {
1223
- type: "button",
1224
- "aria-label": t.paginationNext,
1225
- disabled: hasNext === void 0 ? page >= pageCount - 1 : !hasNext,
1226
- onClick: () => onPageChange(page + 1),
1227
- className: cx(listControlButtonClasses, "disabled:opacity-40 disabled:pointer-events-none"),
1228
- children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: "w-3.5 h-3.5" })
1229
- })]
1230
- })]
1231
- });
1232
- }
1233
- //#endregion
1234
- //#region src/list/HoverCard.tsx
1235
- function useHoverCard() {
1236
- const triggerRef = useRef(null);
1237
- const [coords, setCoords] = useState(null);
1238
- const openTimer = useRef(void 0);
1239
- const closeTimer = useRef(void 0);
1240
- useEffect(() => () => {
1241
- clearTimeout(openTimer.current);
1242
- clearTimeout(closeTimer.current);
1243
- }, []);
1244
- const scheduleOpen = () => {
1245
- clearTimeout(closeTimer.current);
1246
- openTimer.current = setTimeout(() => {
1247
- const rect = triggerRef.current?.getBoundingClientRect();
1248
- if (rect) setCoords({
1249
- x: rect.left,
1250
- y: rect.bottom
1251
- });
1252
- }, 350);
1253
- };
1254
- const scheduleClose = () => {
1255
- clearTimeout(openTimer.current);
1256
- closeTimer.current = setTimeout(() => setCoords(null), 150);
1257
- };
1258
- const keepOpen = () => clearTimeout(closeTimer.current);
1259
- return {
1260
- triggerRef,
1261
- coords,
1262
- scheduleOpen,
1263
- scheduleClose,
1264
- keepOpen
1265
- };
1101
+ //#region src/list/listView.tsx
1102
+ var PRIORITY_SHOW_CLASS = {
1103
+ low: "hidden @[52rem]:table-cell",
1104
+ medium: "hidden @[36rem]:table-cell"
1105
+ };
1106
+ function columnResponsiveClass(col) {
1107
+ return col.priority ? PRIORITY_SHOW_CLASS[col.priority] : "";
1266
1108
  }
1267
- function HoverCardSurface({ x, y, widthClassName, padded = true, relayoutKey, onKeepOpen, onLeave, children }) {
1268
- const ref = useRef(null);
1269
- const [pos, setPos] = useState(null);
1270
- useLayoutEffect(() => {
1271
- if (!ref.current) return;
1272
- const { width, height } = ref.current.getBoundingClientRect();
1273
- const pad = 8;
1274
- setPos({
1275
- left: Math.max(pad, Math.min(x, window.innerWidth - width - pad)),
1276
- top: y + 8 + height + pad > window.innerHeight ? Math.max(pad, y - 8 - height) : y + 8
1109
+ var DEFAULT_ORDER = "__default";
1110
+ var FAVORITE_ORDER = "__favorite";
1111
+ /** Persisted grouping + column-visibility + field-filter state for a list page,
1112
+ * driving the shared Display/Filter menus. */
1113
+ function useListView({ storageKey, sharedFiltersKey, columns, groupBys = [], defaultGroupBy, orderBys = [], defaultOrderBy, facets = [], actions = [], actionCount, defaultShowColumnHeaders = false, defaultShowActions = true, isFavorite, isOn, createdAt }) {
1114
+ const t = useStrings().list;
1115
+ const tiebreak = orderBys[0]?.compare;
1116
+ const effectiveOrderBys = useMemo(() => {
1117
+ if (!isFavorite) return orderBys;
1118
+ const favoriteRank = (row) => isFavorite(row) ? 0 : 1;
1119
+ const onRank = (row) => isOn?.(row) ? 0 : 1;
1120
+ const createdMs = (row) => {
1121
+ const v = createdAt?.(row);
1122
+ return v === null || v === void 0 || Number.isNaN(v) ? 0 : v;
1123
+ };
1124
+ const newestFirst = (a, b) => createdMs(b) - createdMs(a);
1125
+ return [...[{
1126
+ value: DEFAULT_ORDER,
1127
+ label: t.orderDefault,
1128
+ compare: (a, b) => onRank(a) - onRank(b) || favoriteRank(a) - favoriteRank(b) || newestFirst(a, b) || (tiebreak?.(a, b) ?? 0)
1129
+ }, {
1130
+ value: FAVORITE_ORDER,
1131
+ label: t.orderFavorites,
1132
+ compare: (a, b) => favoriteRank(a) - favoriteRank(b) || (tiebreak?.(a, b) ?? 0)
1133
+ }], ...orderBys];
1134
+ }, [
1135
+ orderBys,
1136
+ isFavorite,
1137
+ isOn,
1138
+ createdAt,
1139
+ tiebreak,
1140
+ t
1141
+ ]);
1142
+ const defaultGroupByValue = defaultGroupBy ?? groupBys[0]?.value ?? "";
1143
+ const defaultOrderByValue = isFavorite ? DEFAULT_ORDER : defaultOrderBy ?? orderBys[0]?.value ?? "";
1144
+ const defaultHiddenKeys = useMemo(() => columns.filter((c) => c.defaultHidden && !c.alwaysVisible).map((c) => c.key), [columns]);
1145
+ const defaultPinnedKeys = useMemo(() => actions.filter((a) => a.defaultPinned).map((a) => a.key), [actions]);
1146
+ const [hiddenKeys, setHiddenKeys] = usePersistedState(`${storageKey}:hiddenColumns`, defaultHiddenKeys);
1147
+ const [pinnedKeys, setPinnedKeys] = usePersistedState(`${storageKey}:pinnedActions`, defaultPinnedKeys);
1148
+ const [groupBy, setGroupBy] = usePersistedState(`${storageKey}:groupBy`, defaultGroupByValue);
1149
+ const [showColumnHeaders, setShowColumnHeaders] = usePersistedState(`${storageKey}:showColumnHeaders`, defaultShowColumnHeaders);
1150
+ const [showActions, setShowActions] = usePersistedState(`${storageKey}:showActions`, defaultShowActions);
1151
+ const [alwaysShowActions, setAlwaysShowActions] = usePersistedState(`${storageKey}:alwaysShowActions`, false);
1152
+ const [orderBy, setOrderBy] = usePersistedState(`${storageKey}:orderBy`, defaultOrderByValue);
1153
+ const [orderDir, setOrderDir] = usePersistedState(`${storageKey}:orderDir`, "asc");
1154
+ const [localFilters, setLocalFilters] = usePersistedState(`${storageKey}:filters`, {});
1155
+ const [sharedFilters, setSharedFilters] = usePersistedState(`${sharedFiltersKey ?? storageKey}:sharedFilters`, {});
1156
+ const sharedKeys = useMemo(() => new Set(facets.filter((f) => f.shared).map((f) => f.key)), [facets]);
1157
+ const filters = useMemo(() => {
1158
+ if (sharedKeys.size === 0) return localFilters;
1159
+ const merged = {};
1160
+ for (const [key, values] of Object.entries(localFilters)) if (!sharedKeys.has(key)) merged[key] = values;
1161
+ for (const key of sharedKeys) {
1162
+ const values = sharedFilters[key];
1163
+ if (values?.length) merged[key] = values;
1164
+ }
1165
+ return merged;
1166
+ }, [
1167
+ localFilters,
1168
+ sharedFilters,
1169
+ sharedKeys
1170
+ ]);
1171
+ const pinnedCount = actionCount ?? pinnedKeys.filter((key) => actions.some((action) => action.key === key)).length;
1172
+ const effectiveShowActions = showActions || alwaysShowActions;
1173
+ const actionsWidth = effectiveShowActions ? actionsColumnWidth(pinnedCount) : 0;
1174
+ const isVisible = useCallback((key) => {
1175
+ const col = columns.find((c) => c.key === key);
1176
+ return Boolean(col?.alwaysVisible) || !hiddenKeys.includes(key);
1177
+ }, [columns, hiddenKeys]);
1178
+ const visibleColumns = useMemo(() => columns.filter((c) => !c.inline && (c.alwaysVisible || !hiddenKeys.includes(c.key))), [columns, hiddenKeys]);
1179
+ const columnClasses = useMemo(() => visibleColumns.map(columnResponsiveClass), [visibleColumns]);
1180
+ const toggleColumn = useCallback((key) => setHiddenKeys((prev) => prev.includes(key) ? prev.filter((k) => k !== key) : [...prev, key]), [setHiddenKeys]);
1181
+ const setFacetValues = useCallback((key, values) => {
1182
+ const update = (prev) => {
1183
+ const next = { ...prev };
1184
+ if (values.length) next[key] = values;
1185
+ else delete next[key];
1186
+ return next;
1187
+ };
1188
+ if (sharedKeys.has(key)) setSharedFilters(update);
1189
+ else setLocalFilters(update);
1190
+ }, [
1191
+ setLocalFilters,
1192
+ setSharedFilters,
1193
+ sharedKeys
1194
+ ]);
1195
+ const clearFilters = useCallback(() => {
1196
+ setLocalFilters({});
1197
+ if (sharedKeys.size > 0) setSharedFilters((prev) => {
1198
+ const next = { ...prev };
1199
+ for (const key of sharedKeys) delete next[key];
1200
+ return next;
1277
1201
  });
1278
1202
  }, [
1279
- x,
1280
- y,
1281
- relayoutKey
1203
+ setLocalFilters,
1204
+ setSharedFilters,
1205
+ sharedKeys
1282
1206
  ]);
1283
- return createPortal(/* @__PURE__ */ jsx("div", {
1284
- ref,
1285
- role: "none",
1286
- onMouseEnter: onKeepOpen,
1287
- onMouseLeave: onLeave,
1288
- onFocus: onKeepOpen,
1289
- onBlur: onLeave,
1290
- className: cx("fixed z-[9999] overflow-hidden rounded-xl border border-(--theme-border) bg-(--theme-panel-bg) shadow-xl backdrop-blur-xl", padded && "p-3", widthClassName),
1291
- style: {
1292
- left: pos?.left ?? x,
1293
- top: pos?.top ?? y + 8,
1294
- visibility: pos ? "visible" : "hidden"
1295
- },
1296
- children
1297
- }), document.querySelector(".ds-root") ?? document.body);
1207
+ const isPinned = useCallback((key) => pinnedKeys.includes(key), [pinnedKeys]);
1208
+ const togglePinned = useCallback((key) => setPinnedKeys((prev) => prev.includes(key) ? prev.filter((k) => k !== key) : [...prev, key]), [setPinnedKeys]);
1209
+ const toggleColumnHeaders = useCallback(() => setShowColumnHeaders((prev) => !prev), [setShowColumnHeaders]);
1210
+ const toggleShowActions = useCallback(() => {
1211
+ if (effectiveShowActions) {
1212
+ setShowActions(false);
1213
+ setAlwaysShowActions(false);
1214
+ } else setShowActions(true);
1215
+ }, [
1216
+ effectiveShowActions,
1217
+ setShowActions,
1218
+ setAlwaysShowActions
1219
+ ]);
1220
+ const toggleAlwaysShowActions = useCallback(() => {
1221
+ const next = !alwaysShowActions;
1222
+ setAlwaysShowActions(next);
1223
+ if (next) setShowActions(true);
1224
+ }, [
1225
+ alwaysShowActions,
1226
+ setAlwaysShowActions,
1227
+ setShowActions
1228
+ ]);
1229
+ const toggleOrderDir = useCallback(() => setOrderDir((d) => d === "asc" ? "desc" : "asc"), [setOrderDir]);
1230
+ const applyOrder = useCallback((rows) => {
1231
+ const opt = effectiveOrderBys.find((o) => o.value === orderBy);
1232
+ if (!opt) return rows;
1233
+ const sign = orderDir === "asc" ? 1 : -1;
1234
+ return [...rows].sort((a, b) => opt.compare(a, b) * sign);
1235
+ }, [
1236
+ effectiveOrderBys,
1237
+ orderBy,
1238
+ orderDir
1239
+ ]);
1240
+ const sameKeys = (a, b) => a.length === b.length && a.every((k) => b.includes(k));
1241
+ const isDisplayDefault = groupBy === defaultGroupByValue && orderBy === defaultOrderByValue && orderDir === "asc" && showColumnHeaders === defaultShowColumnHeaders && showActions === defaultShowActions && alwaysShowActions === false && sameKeys(hiddenKeys, defaultHiddenKeys) && sameKeys(pinnedKeys, defaultPinnedKeys);
1242
+ const resetDisplay = useCallback(() => {
1243
+ setGroupBy(defaultGroupByValue);
1244
+ setOrderBy(defaultOrderByValue);
1245
+ setOrderDir("asc");
1246
+ setShowColumnHeaders(defaultShowColumnHeaders);
1247
+ setShowActions(defaultShowActions);
1248
+ setAlwaysShowActions(false);
1249
+ setHiddenKeys(defaultHiddenKeys);
1250
+ setPinnedKeys(defaultPinnedKeys);
1251
+ }, [
1252
+ setGroupBy,
1253
+ setOrderBy,
1254
+ setOrderDir,
1255
+ defaultOrderByValue,
1256
+ setShowColumnHeaders,
1257
+ setShowActions,
1258
+ setAlwaysShowActions,
1259
+ setHiddenKeys,
1260
+ setPinnedKeys,
1261
+ defaultGroupByValue,
1262
+ defaultHiddenKeys,
1263
+ defaultPinnedKeys,
1264
+ defaultShowColumnHeaders,
1265
+ defaultShowActions
1266
+ ]);
1267
+ return {
1268
+ columns,
1269
+ visibleColumns,
1270
+ columnClasses,
1271
+ isVisible,
1272
+ pinnedCount,
1273
+ actionsWidth,
1274
+ toggleColumn,
1275
+ groupBys,
1276
+ groupBy,
1277
+ setGroupBy,
1278
+ orderBys: effectiveOrderBys,
1279
+ orderBy,
1280
+ setOrderBy,
1281
+ orderDir,
1282
+ toggleOrderDir,
1283
+ applyOrder,
1284
+ facets,
1285
+ filters,
1286
+ setFacetValues,
1287
+ clearFilters,
1288
+ activeFilterCount: useMemo(() => Object.values(filters).filter((v) => v && v.length > 0).length, [filters]),
1289
+ applyFilters: useCallback((rows) => rows.filter((row) => facets.every((f) => {
1290
+ const selected = filters[f.key];
1291
+ return !selected || selected.length === 0 ? true : f.matches(row, selected);
1292
+ })), [facets, filters]),
1293
+ actions,
1294
+ isPinned,
1295
+ togglePinned,
1296
+ showColumnHeaders,
1297
+ toggleColumnHeaders,
1298
+ showActions: effectiveShowActions,
1299
+ toggleShowActions,
1300
+ alwaysShowActions,
1301
+ toggleAlwaysShowActions,
1302
+ isDisplayDefault,
1303
+ resetDisplay
1304
+ };
1298
1305
  }
1299
1306
  //#endregion
1300
1307
  //#region src/list/UserHoverCard.tsx
@@ -1408,4 +1415,4 @@ function HoverCardRow({ icon, children, title, align = "center", truncate = fals
1408
1415
  });
1409
1416
  }
1410
1417
  //#endregion
1411
- export { actionsColumnWidth as A, GroupRow as C, NAME_MIN_WIDTH_PX as D, ListSkeleton as E, stickyTabsClasses as F, stickyTabsUnderHeaderClasses as I, stickyToolbarClasses as L, listTableProps as M, ActionToolbar as N, NameCell as O, ToolbarButton as P, useListNavigate as S, ListError as T, useListView as _, useHoverCard as a, ListRow as b, ListCount as c, ListDisplayMenu as d, ListFilterMenu as f, FAVORITE_ORDER as g, DEFAULT_ORDER as h, HoverCardSurface as i, listTableFixedProps as j, RowSelectCheckbox as k, ListSearchControl as l, isTextEntryContext as m, HoverCardTrigger as n, LIST_PAGE_SIZES as o, listControlButtonClasses as p, UserHoverCard as r, ListPager as s, HoverCardRow as t, useListSearch as u, ListColumns as v, ListActionsHeader as w, ListRowActionsProvider as x, ListPage as y };
1418
+ export { listTableProps as A, ListError as C, RowSelectCheckbox as D, NameCell as E, stickyTabsClasses as F, stickyTabsUnderHeaderClasses as I, stickyToolbarClasses as L, useHoverCard as M, ActionToolbar as N, actionsColumnWidth as O, ToolbarButton as P, ListActionsHeader as S, NAME_MIN_WIDTH_PX as T, ListPage as _, FAVORITE_ORDER as a, useListNavigate as b, ListSearchControl as c, ListPager as d, ListDisplayMenu as f, ListColumns as g, isTextEntryContext as h, DEFAULT_ORDER as i, HoverCardSurface as j, listTableFixedProps as k, useListSearch as l, listControlButtonClasses as m, HoverCardTrigger as n, useListView as o, ListFilterMenu as p, UserHoverCard as r, ListCount as s, HoverCardRow as t, LIST_PAGE_SIZES as u, ListRow as v, ListSkeleton as w, GroupRow as x, ListRowActionsProvider as y };