@parallelworks/ui 0.19.0 → 0.19.1
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.
- package/dist/{chunks/memoryStore-CC91S5LP.js → ai/adapter/memoryStore.js} +2 -2
- package/dist/ai/adapter/mock/index.js +2 -80
- package/dist/ai/adapter/mock/mockAdapter.js +81 -0
- package/dist/ai/adapter/openai/createOpenAIStream.js +33 -0
- package/dist/ai/adapter/openai/errors.js +37 -0
- package/dist/ai/adapter/openai/index.js +5 -333
- package/dist/ai/adapter/openai/openaiAdapter.js +54 -0
- package/dist/ai/adapter/openai/responses.js +108 -0
- package/dist/ai/adapter/openai/sse.js +112 -0
- package/dist/{chunks/types-Yy0NrerF.js → ai/adapter/types.js} +1 -1
- package/dist/ai/agent/classifier.js +12 -0
- package/dist/ai/agent/diff.js +223 -0
- package/dist/ai/agent/diffHighlight.js +101 -0
- package/dist/ai/agent/permissions.js +29 -0
- package/dist/ai/agent/toolfmt.js +383 -0
- package/dist/ai/agent/transcript.js +127 -0
- package/dist/ai/components/AllocationSelector.js +56 -0
- package/dist/ai/components/AttachmentManager.js +144 -0
- package/dist/ai/components/AttachmentUpload.js +179 -0
- package/dist/ai/components/BlockedGroupBanner.js +29 -0
- package/dist/ai/components/BranchNavigator.js +57 -0
- package/dist/ai/components/ChatEmptyState.js +171 -0
- package/dist/ai/components/ChatInput.js +265 -0
- package/dist/ai/components/ChatLayout.js +15 -0
- package/dist/ai/components/ChatMessage.js +338 -0
- package/dist/ai/components/ChatMessageList.js +301 -0
- package/dist/ai/components/ChatSidebar.js +345 -0
- package/dist/ai/components/ChatThread.js +198 -0
- package/dist/ai/components/ComposerChrome.js +50 -0
- package/dist/ai/components/ComposerFrame.js +33 -0
- package/dist/ai/components/DragOverlay.js +20 -0
- package/dist/ai/components/ModelSelector.js +343 -0
- package/dist/ai/components/ShareDialog.js +260 -0
- package/dist/ai/components/SlashMenu.js +91 -0
- package/dist/ai/components/agent/ActivityLine.js +151 -0
- package/dist/ai/components/agent/AgentMessageParts.js +721 -0
- package/dist/ai/components/agent/PermissionPicker.js +38 -0
- package/dist/ai/components/attachmentKeys.js +10 -0
- package/dist/ai/core/ChatProvider.js +848 -0
- package/dist/ai/core/KeyboardShortcutsProvider.js +9 -0
- package/dist/ai/core/chatReducer.js +223 -0
- package/dist/ai/core/config.js +45 -0
- package/dist/ai/core/events.js +4 -0
- package/dist/ai/core/exportConversation.js +42 -0
- package/dist/ai/core/greeting.js +38 -0
- package/dist/ai/core/parts.js +84 -0
- package/dist/ai/core/useDragDrop.js +36 -0
- package/dist/ai/core/useKeyboardShortcuts.js +77 -0
- package/dist/ai/core/useSSEStream.js +98 -0
- package/dist/ai/index.js +43 -6644
- package/dist/ai/strings.js +175 -0
- package/dist/ai/ui/Avatar.js +35 -0
- package/dist/ai/ui/Dropdown.js +110 -0
- package/dist/ai/ui/Markdown.js +79 -0
- package/dist/ai/ui/Notice.js +77 -0
- package/dist/ai/ui/codeBreaks.js +36 -0
- package/dist/ai/ui/prose.js +13 -0
- package/dist/ai/ui/sidebar.js +148 -0
- package/dist/ai/utils.js +23 -0
- package/dist/components/AddMenu.js +46 -0
- package/dist/components/Avatar.js +46 -0
- package/dist/{chunks/BareModal-BtRiQezg.js → components/BareModal.js} +1 -1
- package/dist/components/Breadcrumbs.js +83 -0
- package/dist/components/Button.js +83 -0
- package/dist/components/Callout.js +23 -0
- package/dist/components/Card.js +31 -0
- package/dist/components/CollapsiblePanel.js +77 -0
- package/dist/{chunks/ConfirmModal-aOR4O1QE.js → components/ConfirmModal.js} +4 -9
- package/dist/components/CopyToClipboard.js +81 -0
- package/dist/{chunks/Markdown-CM-FenNE.js → components/CreateModal.js} +9 -116
- package/dist/components/DescriptionList.js +42 -0
- package/dist/components/Dropdown.js +349 -0
- package/dist/components/EmptyState.js +27 -0
- package/dist/components/Filter.js +32 -0
- package/dist/components/FilterPill.js +13 -0
- package/dist/components/IconButton.js +34 -0
- package/dist/{chunks/Indicator-hBSIxOPK.js → components/Indicator.js} +1 -1
- package/dist/components/Input.js +88 -0
- package/dist/components/LetterBadge.js +13 -0
- package/dist/components/Listbox.js +123 -0
- package/dist/{chunks/Loader-BXoXTZGc.js → components/Loader.js} +1 -1
- package/dist/components/Markdown.js +15 -0
- package/dist/components/MultiSelect.js +171 -0
- package/dist/components/Overview.js +39 -0
- package/dist/components/PageHeader.js +194 -0
- package/dist/components/Pagination.js +35 -0
- package/dist/{chunks/Provider-CDYurraE.js → components/Provider.js} +1 -1
- package/dist/{chunks/RequiredMark-DfLdYbYZ.js → components/RequiredMark.js} +1 -1
- package/dist/components/SectionHeader.js +11 -0
- package/dist/components/SettingModeOption.js +30 -0
- package/dist/components/SettingRow.js +35 -0
- package/dist/components/SettingsCard.js +31 -0
- package/dist/components/SettingsGroup.js +17 -0
- package/dist/components/Sort.js +42 -0
- package/dist/components/StatusBadge.js +63 -0
- package/dist/components/SwitchToggle.js +59 -0
- package/dist/{chunks/SwitchToggleSmall-FcsaYSTs.js → components/SwitchToggleSmall.js} +1 -1
- package/dist/{chunks/Table-DU2MW8xz.js → components/Table.js} +3 -3
- package/dist/components/Toggle.js +20 -0
- package/dist/{chunks/Tooltip-K5HJr4_p.js → components/Tooltip.js} +2 -27
- package/dist/{chunks/StatusBadge-C1Blx_cW.js → components/dropdownUtils.js} +2 -63
- package/dist/{chunks/focus-D_iZJTW1.js → components/focus.js} +1 -1
- package/dist/components/ghostButton.js +7 -0
- package/dist/components/inputClasses.js +4 -0
- package/dist/components/keys.js +27 -0
- package/dist/components/relativeTime.js +32 -0
- package/dist/{chunks/useCssIsDark-DHr05dG2.js → components/useCssIsDark.js} +1 -1
- package/dist/{chunks/duration-BOv8Phl8.js → duration.js} +1 -1
- package/dist/editor/Editor.js +13 -0
- package/dist/editor/EditorField.js +53 -0
- package/dist/editor/FormCodePanel.js +173 -0
- package/dist/{chunks/Monaco-DJ8MW-Dq.js → editor/Monaco.js} +4 -2
- package/dist/editor/index.js +6 -1
- package/dist/editor/themes.js +91 -0
- package/dist/editor/workers.js +29 -0
- package/dist/editor/yaml.js +15 -0
- package/dist/file-explorer/FileExplorer.js +1402 -0
- package/dist/file-explorer/FileExplorerContext.js +370 -0
- package/dist/{chunks/CodePreviewEditor-YqQBz1YL.js → file-explorer/FilePreview/CodePreviewEditor.js} +3 -2
- package/dist/file-explorer/FilePreview/Inline.js +45 -0
- package/dist/file-explorer/FilePreview/StreamedBadge.js +16 -0
- package/dist/file-explorer/FilePreview/Viewer.js +526 -0
- package/dist/file-explorer/FilePreview/index.js +266 -0
- package/dist/file-explorer/FilePreview/parse.js +63 -0
- package/dist/file-explorer/FilePreview/previewCache.js +98 -0
- package/dist/file-explorer/FilePreview/previewType.js +151 -0
- package/dist/file-explorer/FilePreview/readCappedBytes.js +196 -0
- package/dist/file-explorer/FilePreview/useObjectBlobUrl.js +95 -0
- package/dist/file-explorer/FilePreview/useObjectText.js +109 -0
- package/dist/file-explorer/FilePreview/usePresignedUrl.js +73 -0
- package/dist/file-explorer/FileRow.js +165 -0
- package/dist/file-explorer/FileRowsWindow.js +92 -0
- package/dist/file-explorer/FileTable.js +172 -0
- package/dist/file-explorer/FileTableHeaders.js +49 -0
- package/dist/file-explorer/LoadMoreRow.js +46 -0
- package/dist/file-explorer/TreeView.js +376 -0
- package/dist/file-explorer/helpers.js +537 -0
- package/dist/file-explorer/index.js +7 -5671
- package/dist/file-explorer/lib/batch.js +39 -0
- package/dist/file-explorer/lib/errors.js +9 -0
- package/dist/file-explorer/lib/paging.js +124 -0
- package/dist/file-explorer/lib/utils.js +322 -0
- package/dist/file-explorer/toasts.js +400 -0
- package/dist/file-explorer/treeRows.js +73 -0
- package/dist/file-explorer/useNearEnd.js +27 -0
- package/dist/form/FieldWrapper.js +54 -0
- package/dist/form/Fields/BooleanField.js +51 -0
- package/dist/form/Fields/CheckboxGroupField.js +72 -0
- package/dist/form/Fields/ColorField.js +33 -0
- package/dist/form/Fields/DropdownField.js +49 -0
- package/dist/form/Fields/DurationField.js +130 -0
- package/dist/form/Fields/LabelField.js +19 -0
- package/dist/form/Fields/MultiDropdownField.js +40 -0
- package/dist/form/Fields/NumberField.js +51 -0
- package/dist/form/Fields/ObjectField.js +54 -0
- package/dist/form/Fields/RadioField.js +49 -0
- package/dist/form/Fields/RangeField.js +58 -0
- package/dist/form/Fields/ResourceOptionIcon.js +21 -0
- package/dist/form/Fields/StepField.js +6 -0
- package/dist/form/Fields/StringField.js +37 -0
- package/dist/form/Fields/TextAreaField.js +31 -0
- package/dist/form/Fields/durationValidity.js +11 -0
- package/dist/form/Form.js +643 -0
- package/dist/form/SpecialTypes/DropdownWrapper.js +30 -0
- package/dist/form/SpecialTypes/FormikCustomDropdown.js +122 -0
- package/dist/form/SpecialTypes/FormikCustomInput.js +111 -0
- package/dist/form/SpecialTypes/FormikMultiSelectDropdown.js +31 -0
- package/dist/form/SpecialTypes/FormikNumericInput.js +60 -0
- package/dist/form/Wizard/WizardContainer.js +138 -0
- package/dist/form/Wizard/WizardNavigation.js +92 -0
- package/dist/form/Wizard/WizardStepContent.js +35 -0
- package/dist/form/Wizard/WizardStepIndicator.js +108 -0
- package/dist/form/Wizard/useWizardState.js +118 -0
- package/dist/form/Wizard/utils.js +34 -0
- package/dist/form/fieldContext.js +26 -0
- package/dist/form/fieldRegistry.js +50 -0
- package/dist/form/index.js +17 -2321
- package/dist/{chunks/FieldWrapper-Cszwg-4B.js → form/lib.js} +2 -97
- package/dist/form/useParsedOpts.js +62 -0
- package/dist/form/usePrevious.js +11 -0
- package/dist/form/utils/checkShowIfObject.js +14 -0
- package/dist/form/utils/duration.js +69 -0
- package/dist/form/utils/getParentValue.js +23 -0
- package/dist/form/utils/getPlaceholder.js +9 -0
- package/dist/form/utils/getValueUsingPath.js +23 -0
- package/dist/form/utils/secondaryField.js +22 -0
- package/dist/graph/AnnotationBanner.js +141 -0
- package/dist/graph/Collapse.js +41 -0
- package/dist/graph/DependencyGraph.js +1398 -0
- package/dist/graph/JobSummary.js +104 -0
- package/dist/graph/MatrixGroup.js +107 -0
- package/dist/graph/Reveal.js +57 -0
- package/dist/graph/TreeView.js +711 -0
- package/dist/graph/index.js +1 -2519
- package/dist/graph/types.js +6 -0
- package/dist/index.js +46 -599
- package/dist/list/ActionToolbar.js +184 -0
- package/dist/list/HoverCard.js +73 -0
- package/dist/list/ListPage.js +145 -0
- package/dist/list/ListPager.js +59 -0
- package/dist/list/ListTable.js +163 -0
- package/dist/list/ListViewControls.js +372 -0
- package/dist/list/RowContextMenu.js +350 -0
- package/dist/list/UserHoverCard.js +117 -0
- package/dist/list/index.js +11 -2
- package/dist/list/listSearch.js +137 -0
- package/dist/list/listView.js +210 -0
- package/dist/list/textEntry.js +10 -0
- package/dist/{chunks/logviewer-BTKuQWzw.js → logviewer/LogViewer.js} +4 -82
- package/dist/logviewer/index.js +2 -1
- package/dist/logviewer/segments.js +80 -0
- package/package.json +1 -1
- package/dist/chunks/PageHeader-0kG_iypP.js +0 -692
- package/dist/chunks/SwitchToggle-DShNdcao.js +0 -758
- package/dist/chunks/editor-Dk1rbV75.js +0 -361
- package/dist/chunks/list-CJm875m7.js +0 -1418
- /package/dist/{chunks/MarkdownImpl-CyeTVvSt.js → components/MarkdownImpl.js} +0 -0
|
@@ -1,1418 +0,0 @@
|
|
|
1
|
-
import { n as useLink, o as usePersistedState, r as useNavigation, u as useStrings } from "./Provider-CDYurraE.js";
|
|
2
|
-
import { CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, DisplayIcon, ErrorIcon, FilterIcon, MenuIcon, SearchIcon, XIcon } from "../icons.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";
|
|
7
|
-
import cx from "classnames";
|
|
8
|
-
import { Children, Fragment, cloneElement, createContext, isValidElement, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
9
|
-
import { useLocalStorage } from "usehooks-ts";
|
|
10
|
-
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
-
import { createPortal } from "react-dom";
|
|
12
|
-
//#region src/list/ActionToolbar.tsx
|
|
13
|
-
var OverflowMenuContext = createContext(false);
|
|
14
|
-
/** Omits data-tooltip-content when absent — react-tooltip treats an explicit undefined as an empty tooltip anchor. */
|
|
15
|
-
function tipProps(content, place) {
|
|
16
|
-
return {
|
|
17
|
-
"data-tooltip-id": TOOLTIP_ID,
|
|
18
|
-
"data-tooltip-place": place,
|
|
19
|
-
...content !== void 0 && { "data-tooltip-content": content }
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
function ToolbarItemView({ item }) {
|
|
23
|
-
if ("element" in item) return /* @__PURE__ */ jsx(Fragment$1, { children: item.element });
|
|
24
|
-
return /* @__PURE__ */ jsx(ToolbarButton, { action: item });
|
|
25
|
-
}
|
|
26
|
-
function ToolbarButton({ action }) {
|
|
27
|
-
const inOverflowMenu = useContext(OverflowMenuContext);
|
|
28
|
-
const Link = useLink();
|
|
29
|
-
if (inOverflowMenu) return /* @__PURE__ */ jsx(OverflowRow, { item: action });
|
|
30
|
-
const className = cx("inline-flex items-center gap-x-0.5 h-9 px-2 rounded-md text-[13.5px] uppercase font-semibold transition-colors whitespace-nowrap", "bg-(--theme-element) text-(--theme-element-text)", action.disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer hover:bg-(--theme-element-hover)");
|
|
31
|
-
const tooltipProps = tipProps(action.disabled ? action.disabledTooltip : action.enabledTooltip, "bottom");
|
|
32
|
-
const Icon = action.icon;
|
|
33
|
-
const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
|
|
34
|
-
className: "h-6 flex items-center",
|
|
35
|
-
children: /* @__PURE__ */ jsx(Icon, { className: cx("w-auto", action.iconClassName) })
|
|
36
|
-
}), action.label] });
|
|
37
|
-
if (action.href && !action.disabled) return /* @__PURE__ */ jsx(Link, {
|
|
38
|
-
to: action.href,
|
|
39
|
-
className,
|
|
40
|
-
...tooltipProps,
|
|
41
|
-
children: content
|
|
42
|
-
});
|
|
43
|
-
if (action.disabled) return /* @__PURE__ */ jsx("span", {
|
|
44
|
-
className: "inline-flex",
|
|
45
|
-
...tooltipProps,
|
|
46
|
-
children: /* @__PURE__ */ jsx("button", {
|
|
47
|
-
type: "button",
|
|
48
|
-
disabled: true,
|
|
49
|
-
className,
|
|
50
|
-
children: content
|
|
51
|
-
})
|
|
52
|
-
});
|
|
53
|
-
return /* @__PURE__ */ jsx("button", {
|
|
54
|
-
type: "button",
|
|
55
|
-
onClick: action.onClick,
|
|
56
|
-
className,
|
|
57
|
-
...tooltipProps,
|
|
58
|
-
children: content
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
/** A single overflowed item rendered as a full-width row inside the `…` menu. */
|
|
62
|
-
function OverflowRow({ item }) {
|
|
63
|
-
const Link = useLink();
|
|
64
|
-
const Icon = item.icon;
|
|
65
|
-
const className = cx("flex w-full items-center gap-x-1 px-4 py-2 text-[13px] uppercase font-semibold link hover:theme-hover", item.disabled && "opacity-50 cursor-not-allowed");
|
|
66
|
-
const tooltipProps = tipProps(item.disabled ? item.disabledTooltip : item.enabledTooltip, "left");
|
|
67
|
-
const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
|
|
68
|
-
className: "w-5 h-6 flex items-center",
|
|
69
|
-
children: /* @__PURE__ */ jsx(Icon, { className: cx(item.iconClassName) })
|
|
70
|
-
}), item.label] });
|
|
71
|
-
if (item.href && !item.disabled) return /* @__PURE__ */ jsx(MenuItem, {
|
|
72
|
-
as: Link,
|
|
73
|
-
to: item.href,
|
|
74
|
-
className,
|
|
75
|
-
...tooltipProps,
|
|
76
|
-
children: content
|
|
77
|
-
});
|
|
78
|
-
return /* @__PURE__ */ jsx(MenuItem, {
|
|
79
|
-
as: "button",
|
|
80
|
-
type: "button",
|
|
81
|
-
onClick: item.onClick,
|
|
82
|
-
disabled: item.disabled ?? false,
|
|
83
|
-
className,
|
|
84
|
-
...tooltipProps,
|
|
85
|
-
children: content
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
function OverflowMenu({ items }) {
|
|
89
|
-
const { list: t } = useStrings();
|
|
90
|
-
return /* @__PURE__ */ jsxs(Menu, {
|
|
91
|
-
as: "div",
|
|
92
|
-
className: "relative inline-block text-left",
|
|
93
|
-
children: [/* @__PURE__ */ jsx(MenuButton, {
|
|
94
|
-
"aria-label": t.moreActions,
|
|
95
|
-
className: "inline-flex items-center justify-center h-9 w-9 rounded-md border theme-border link hover:theme-hover transition-colors",
|
|
96
|
-
"data-tooltip-id": TOOLTIP_ID,
|
|
97
|
-
"data-tooltip-content": t.moreActions,
|
|
98
|
-
"data-testid": "action-toolbar-menu-button",
|
|
99
|
-
children: /* @__PURE__ */ jsx(MenuIcon, {})
|
|
100
|
-
}), /* @__PURE__ */ jsx(MenuItems, {
|
|
101
|
-
unmount: false,
|
|
102
|
-
anchor: {
|
|
103
|
-
to: "bottom end",
|
|
104
|
-
gap: 4
|
|
105
|
-
},
|
|
106
|
-
className: "z-50 min-w-56 rounded-md border theme-border bg-(--theme-app-bg) shadow-lg py-1 focus:outline-none",
|
|
107
|
-
children: /* @__PURE__ */ jsx(OverflowMenuContext.Provider, {
|
|
108
|
-
value: true,
|
|
109
|
-
children: items.map((item) => "element" in item ? /* @__PURE__ */ jsx(Fragment, { children: item.element }, item.key) : /* @__PURE__ */ jsx(OverflowRow, { item }, item.key))
|
|
110
|
-
})
|
|
111
|
-
})]
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
var MENU_BUTTON_WIDTH = 44;
|
|
115
|
-
var ITEM_GAP = 4;
|
|
116
|
-
function fitCount(widths, available, gap, menuWidth) {
|
|
117
|
-
if (widths.length === 0) return 0;
|
|
118
|
-
if (widths.reduce((sum, w) => sum + w, 0) + gap * (widths.length - 1) <= available) return widths.length;
|
|
119
|
-
let used = menuWidth;
|
|
120
|
-
let count = 0;
|
|
121
|
-
for (const w of widths) {
|
|
122
|
-
if (used + gap + w > available) break;
|
|
123
|
-
used += gap + w;
|
|
124
|
-
count++;
|
|
125
|
-
}
|
|
126
|
-
return count;
|
|
127
|
-
}
|
|
128
|
-
/** Measures in a layout effect (before paint) so the inline/overflow split never flashes. */
|
|
129
|
-
function CollapsibleRight({ items }) {
|
|
130
|
-
const wrapRef = useRef(null);
|
|
131
|
-
const itemRefs = useRef({});
|
|
132
|
-
const widthCache = useRef({});
|
|
133
|
-
const [available, setAvailable] = useState(Number.POSITIVE_INFINITY);
|
|
134
|
-
const [count, setCount] = useState(items.length);
|
|
135
|
-
useLayoutEffect(() => {
|
|
136
|
-
const el = wrapRef.current;
|
|
137
|
-
if (!el) return;
|
|
138
|
-
const update = () => setAvailable(el.clientWidth);
|
|
139
|
-
update();
|
|
140
|
-
const observer = new ResizeObserver(update);
|
|
141
|
-
observer.observe(el);
|
|
142
|
-
return () => observer.disconnect();
|
|
143
|
-
}, []);
|
|
144
|
-
useLayoutEffect(() => {
|
|
145
|
-
for (const key in itemRefs.current) {
|
|
146
|
-
const el = itemRefs.current[key];
|
|
147
|
-
if (el) widthCache.current[key] = el.offsetWidth;
|
|
148
|
-
}
|
|
149
|
-
const next = fitCount(items.map((item) => widthCache.current[item.key] ?? 0), available, ITEM_GAP, MENU_BUTTON_WIDTH);
|
|
150
|
-
setCount((prev) => prev === next ? prev : next);
|
|
151
|
-
});
|
|
152
|
-
const visible = items.slice(0, count);
|
|
153
|
-
const overflow = items.slice(count);
|
|
154
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
155
|
-
ref: wrapRef,
|
|
156
|
-
className: "flex-1 min-w-0 flex items-center justify-end gap-1",
|
|
157
|
-
children: [visible.map((item) => /* @__PURE__ */ jsx("span", {
|
|
158
|
-
ref: (el) => {
|
|
159
|
-
itemRefs.current[item.key] = el;
|
|
160
|
-
},
|
|
161
|
-
className: "inline-flex shrink-0",
|
|
162
|
-
children: /* @__PURE__ */ jsx(ToolbarItemView, { item })
|
|
163
|
-
}, item.key)), overflow.length > 0 && /* @__PURE__ */ jsx(OverflowMenu, { items: overflow })]
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
/** Pins a detail-page toolbar just below the sticky breadcrumb strip (top-10 matches the strip's 40px height). */
|
|
167
|
-
var stickyToolbarClasses = "sticky top-10 z-10 bg-(--theme-app-bg)";
|
|
168
|
-
/** Pins a detail-page tabs row under the pinned toolbar (top-22 = 40px strip + 48px toolbar block). */
|
|
169
|
-
var stickyTabsClasses = "sticky top-22 z-10 bg-(--theme-app-bg)";
|
|
170
|
-
/** Pins a detail-page tabs row directly under the breadcrumb strip when the page has no pinned toolbar (top-10 = the strip's height). */
|
|
171
|
-
var stickyTabsUnderHeaderClasses = "sticky top-10 z-10 bg-(--theme-app-bg)";
|
|
172
|
-
/** Action groups left, secondary actions right (collapsing into a `…` menu). */
|
|
173
|
-
function ActionToolbar({ groups, right = [], sticky = false }) {
|
|
174
|
-
const visibleGroups = groups.map((group) => group.filter((a) => !a.hidden)).filter((group) => group.length > 0);
|
|
175
|
-
const visibleRight = right.filter((a) => !a.hidden);
|
|
176
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
177
|
-
className: cx("flex items-center gap-2.5", sticky && cx("sticky top-10 z-10 bg-(--theme-app-bg)", "py-1.5")),
|
|
178
|
-
children: [visibleGroups.length > 0 && /* @__PURE__ */ jsx("div", {
|
|
179
|
-
className: "flex items-center gap-2.5 shrink-0",
|
|
180
|
-
children: visibleGroups.map((group, i) => /* @__PURE__ */ jsxs(Fragment, { children: [i > 0 && /* @__PURE__ */ jsx("div", { className: "w-px h-5 bg-(--theme-border)" }), /* @__PURE__ */ jsx("div", {
|
|
181
|
-
className: "flex items-center gap-1",
|
|
182
|
-
children: group.map((item) => /* @__PURE__ */ jsx(ToolbarItemView, { item }, item.key))
|
|
183
|
-
})] }, group[0]?.key ?? i))
|
|
184
|
-
}), visibleRight.length > 0 && /* @__PURE__ */ jsx(CollapsibleRight, { items: visibleRight })]
|
|
185
|
-
});
|
|
186
|
-
}
|
|
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
|
|
256
|
-
//#region src/list/ListTable.tsx
|
|
257
|
-
var NAME_TEXT = "font-medium text-[13px] leading-tight text-(--theme-app) truncate";
|
|
258
|
-
var SUBTITLE_TEXT = "text-xs leading-tight text-(--theme-muted-text-color) truncate";
|
|
259
|
-
/** table-auto + horizontal scroll; the name column has a min-width floor so it can't be crushed. */
|
|
260
|
-
var listTableProps = {
|
|
261
|
-
borderless: true,
|
|
262
|
-
panel: false,
|
|
263
|
-
rounded: false,
|
|
264
|
-
theadClassName: "text-(--theme-muted-text-color)",
|
|
265
|
-
tbodyClassName: "divide-y border-b border-(--theme-border) divide-(--theme-border)",
|
|
266
|
-
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"
|
|
267
|
-
};
|
|
268
|
-
/** Widths come only from the header row, so a windowed body cannot shift the
|
|
269
|
-
* columns as different content scrolls into view. */
|
|
270
|
-
var listTableFixedProps = {
|
|
271
|
-
...listTableProps,
|
|
272
|
-
className: listTableProps.className.replace("table-auto", "table-fixed")
|
|
273
|
-
};
|
|
274
|
-
function NameCell({ icon, name, subtitle, href, badge, title }) {
|
|
275
|
-
const Link = useLink();
|
|
276
|
-
const inner = /* @__PURE__ */ jsxs(Fragment$1, { children: [icon, /* @__PURE__ */ jsxs("div", {
|
|
277
|
-
className: "min-w-0",
|
|
278
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
279
|
-
className: "flex min-w-0 items-center gap-1.5",
|
|
280
|
-
children: [/* @__PURE__ */ jsx("div", {
|
|
281
|
-
className: cx(NAME_TEXT, href && "group-hover/namecell:text-(--theme-link)"),
|
|
282
|
-
title,
|
|
283
|
-
children: name
|
|
284
|
-
}), badge]
|
|
285
|
-
}), subtitle && /* @__PURE__ */ jsx("div", {
|
|
286
|
-
className: SUBTITLE_TEXT,
|
|
287
|
-
children: subtitle
|
|
288
|
-
})]
|
|
289
|
-
})] });
|
|
290
|
-
return /* @__PURE__ */ jsx(Table.Item, {
|
|
291
|
-
className: "py-2.5",
|
|
292
|
-
children: href ? /* @__PURE__ */ jsx(Link, {
|
|
293
|
-
to: href,
|
|
294
|
-
className: "group/namecell flex items-center gap-2.5 min-w-0 cursor-pointer",
|
|
295
|
-
children: inner
|
|
296
|
-
}) : /* @__PURE__ */ jsx("div", {
|
|
297
|
-
className: "flex items-center gap-2.5 min-w-0",
|
|
298
|
-
children: inner
|
|
299
|
-
})
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
function GroupRow({ label, count, colSpan }) {
|
|
303
|
-
return /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsxs("td", {
|
|
304
|
-
colSpan,
|
|
305
|
-
className: "bg-(--theme-muted-panel-bg) py-1.5 text-xs font-medium",
|
|
306
|
-
children: [
|
|
307
|
-
/* @__PURE__ */ jsx("span", {
|
|
308
|
-
className: "text-(--theme-app)",
|
|
309
|
-
children: label
|
|
310
|
-
}),
|
|
311
|
-
/* @__PURE__ */ jsx("span", {
|
|
312
|
-
className: "mx-1.5 opacity-50",
|
|
313
|
-
children: "·"
|
|
314
|
-
}),
|
|
315
|
-
/* @__PURE__ */ jsx("span", {
|
|
316
|
-
className: "text-(--theme-muted-text-color)",
|
|
317
|
-
children: count
|
|
318
|
-
})
|
|
319
|
-
]
|
|
320
|
-
}) });
|
|
321
|
-
}
|
|
322
|
-
function ListError({ error, noun = "data" }) {
|
|
323
|
-
const { list: t } = useStrings();
|
|
324
|
-
const message = error && typeof error === "object" && "message" in error ? String(error.message) : void 0;
|
|
325
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
326
|
-
className: "flex flex-col items-center justify-center gap-3 py-20 text-center",
|
|
327
|
-
children: [/* @__PURE__ */ jsx(ErrorIcon, { className: "h-10 w-10 text-red-400 opacity-80" }), /* @__PURE__ */ jsxs("div", {
|
|
328
|
-
className: "space-y-1",
|
|
329
|
-
children: [/* @__PURE__ */ jsx("p", {
|
|
330
|
-
className: "text-sm font-medium text-(--theme-app)",
|
|
331
|
-
children: t.couldNotLoad(noun)
|
|
332
|
-
}), message && /* @__PURE__ */ jsx("p", {
|
|
333
|
-
className: "max-w-md text-xs text-(--theme-muted-text-color)",
|
|
334
|
-
children: message
|
|
335
|
-
})]
|
|
336
|
-
})]
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
function ListSkeleton({ rows = 6 }) {
|
|
340
|
-
const { loading } = useStrings();
|
|
341
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
342
|
-
role: "status",
|
|
343
|
-
"aria-label": loading,
|
|
344
|
-
className: "divide-y border-b border-(--theme-border) divide-(--theme-border)",
|
|
345
|
-
children: [positionKeys(rows, "skeleton").map((key) => /* @__PURE__ */ jsxs("div", {
|
|
346
|
-
"aria-hidden": "true",
|
|
347
|
-
className: "flex items-center gap-3 px-4 py-2.5 animate-pulse",
|
|
348
|
-
children: [
|
|
349
|
-
/* @__PURE__ */ jsx("div", { className: "h-5 w-5 rounded bg-(--theme-muted-panel-bg)" }),
|
|
350
|
-
/* @__PURE__ */ jsxs("div", {
|
|
351
|
-
className: "space-y-1.5",
|
|
352
|
-
children: [/* @__PURE__ */ jsx("div", { className: "h-3 w-40 rounded bg-(--theme-muted-panel-bg)" }), /* @__PURE__ */ jsx("div", { className: "h-2.5 w-24 rounded bg-(--theme-muted-panel-bg)" })]
|
|
353
|
-
}),
|
|
354
|
-
/* @__PURE__ */ jsx("div", { className: "ml-auto h-3 w-20 rounded bg-(--theme-muted-panel-bg)" })
|
|
355
|
-
]
|
|
356
|
-
}, key)), /* @__PURE__ */ jsx("span", {
|
|
357
|
-
className: "sr-only",
|
|
358
|
-
children: loading
|
|
359
|
-
})]
|
|
360
|
-
});
|
|
361
|
-
}
|
|
362
|
-
/** Prevents the name column from being crushed under table-auto layout during horizontal overflow. */
|
|
363
|
-
var NAME_MIN_WIDTH_PX = 200;
|
|
364
|
-
/** Trailing cell geometry: icon-button size + gap + padding, sized to fit exactly its pinned buttons. */
|
|
365
|
-
var ACTION_BUTTON_PX = 24;
|
|
366
|
-
var ACTION_GAP_PX = 2;
|
|
367
|
-
var ACTIONS_CELL_PAD_PX = 28;
|
|
368
|
-
/** Width the trailing actions column reserves for `count` always-visible
|
|
369
|
-
* buttons plus the hover `⋯` trigger. */
|
|
370
|
-
function actionsColumnWidth(count) {
|
|
371
|
-
const buttons = count + 1;
|
|
372
|
-
return buttons * ACTION_BUTTON_PX + (buttons - 1) * ACTION_GAP_PX + ACTIONS_CELL_PAD_PX;
|
|
373
|
-
}
|
|
374
|
-
/** Shared leading selection checkbox for multi-select rows: stops the row-click
|
|
375
|
-
* and suppresses shift-click text selection. Wrap in <Table.Item> for `leading`. */
|
|
376
|
-
function RowSelectCheckbox({ checked, label, onToggle }) {
|
|
377
|
-
return /* @__PURE__ */ jsx("input", {
|
|
378
|
-
type: "checkbox",
|
|
379
|
-
"aria-label": label,
|
|
380
|
-
checked,
|
|
381
|
-
onChange: () => {},
|
|
382
|
-
onClick: (e) => {
|
|
383
|
-
e.stopPropagation();
|
|
384
|
-
onToggle(e);
|
|
385
|
-
},
|
|
386
|
-
onMouseDown: (e) => {
|
|
387
|
-
if (e.shiftKey) e.preventDefault();
|
|
388
|
-
},
|
|
389
|
-
className: "h-4 w-4 shrink-0 rounded border-(--theme-border) text-blue-500 focus:ring-blue-500 focus:ring-offset-0 cursor-pointer"
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
/** Sizes to fit pinned buttons + ⋯; below ~44rem only ⋯ remains. Pass a view's
|
|
393
|
-
* chrome (structurally: showActions + actionsWidth) or actionCount. */
|
|
394
|
-
function ListActionsHeader({ view, actionCount = 0, className }) {
|
|
395
|
-
if (view && !view.showActions) return null;
|
|
396
|
-
const width = view ? view.actionsWidth : actionsColumnWidth(actionCount);
|
|
397
|
-
return /* @__PURE__ */ jsx(Table.Header, {
|
|
398
|
-
caps: false,
|
|
399
|
-
style: {
|
|
400
|
-
width,
|
|
401
|
-
minWidth: width
|
|
402
|
-
},
|
|
403
|
-
className: cx("relative py-2 text-right @max-[43rem]:w-[52px]! @max-[43rem]:min-w-[52px]!", className),
|
|
404
|
-
children: /* @__PURE__ */ jsx("span", {
|
|
405
|
-
className: "sr-only",
|
|
406
|
-
children: "Actions"
|
|
407
|
-
})
|
|
408
|
-
});
|
|
409
|
-
}
|
|
410
|
-
ListActionsHeader.isTableHeader = true;
|
|
411
|
-
//#endregion
|
|
412
|
-
//#region src/list/ListPage.tsx
|
|
413
|
-
/** Negative offsets (admin/org) cancel the shell's pt-4 so rows don't peek through the gap above the pinned header. */
|
|
414
|
-
var SECTION_STICKY_TOP = {
|
|
415
|
-
main: "top-0",
|
|
416
|
-
kubernetes: "top-0",
|
|
417
|
-
admin: "-top-4",
|
|
418
|
-
org: "-top-4"
|
|
419
|
-
};
|
|
420
|
-
/** Whether the shell adds pt-4 padding that the edge-to-edge list must cancel with a negative margin. */
|
|
421
|
-
var SECTION_SHELL_PADDED = {
|
|
422
|
-
main: false,
|
|
423
|
-
kubernetes: false,
|
|
424
|
-
admin: true,
|
|
425
|
-
org: true
|
|
426
|
-
};
|
|
427
|
-
/** Defaults mean rows outside a ListView fully show actions (no view = show everything). */
|
|
428
|
-
var RowActionsContext = createContext({
|
|
429
|
-
show: true,
|
|
430
|
-
always: false,
|
|
431
|
-
columnClasses: []
|
|
432
|
-
});
|
|
433
|
-
function useRowActions(view) {
|
|
434
|
-
return useMemo(() => ({
|
|
435
|
-
show: view?.showActions ?? true,
|
|
436
|
-
always: view?.alwaysShowActions ?? false,
|
|
437
|
-
columnClasses: view?.columnClasses ?? []
|
|
438
|
-
}), [
|
|
439
|
-
view?.showActions,
|
|
440
|
-
view?.alwaysShowActions,
|
|
441
|
-
view?.columnClasses
|
|
442
|
-
]);
|
|
443
|
-
}
|
|
444
|
-
function ListRowActionsProvider({ view, children }) {
|
|
445
|
-
const rowActions = useRowActions(view);
|
|
446
|
-
return /* @__PURE__ */ jsx(RowActionsContext.Provider, {
|
|
447
|
-
value: rowActions,
|
|
448
|
-
children
|
|
449
|
-
});
|
|
450
|
-
}
|
|
451
|
-
function ListPage({ section = "main", shellPadded, embedded = false, breadcrumbs, title, titleAction, actions, toolbar, view, children }) {
|
|
452
|
-
const cancelShellPadding = shellPadded ?? SECTION_SHELL_PADDED[section];
|
|
453
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
454
|
-
className: cx("flex h-full min-h-0 flex-col", cancelShellPadding && "-m-4"),
|
|
455
|
-
children: [embedded ? toolbar && /* @__PURE__ */ jsx("div", {
|
|
456
|
-
className: "px-4 py-1.5",
|
|
457
|
-
children: toolbar
|
|
458
|
-
}) : /* @__PURE__ */ jsx(PageHeader, {
|
|
459
|
-
stickyTopClassName: SECTION_STICKY_TOP[section],
|
|
460
|
-
breadcrumbs,
|
|
461
|
-
title,
|
|
462
|
-
titleAction,
|
|
463
|
-
actions,
|
|
464
|
-
toolbar
|
|
465
|
-
}), /* @__PURE__ */ jsx("div", {
|
|
466
|
-
className: "relative @container min-h-0 flex-1 overflow-auto",
|
|
467
|
-
children: /* @__PURE__ */ jsx(ListRowActionsProvider, {
|
|
468
|
-
view,
|
|
469
|
-
children
|
|
470
|
-
})
|
|
471
|
-
})]
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
function ListRow({ href, onActivate, items, goTo, openMenu, pinnedActions, selected, selectionActive, onRowSelect, leading, className, children }) {
|
|
475
|
-
const { active: menuActive, openMenu: openRowMenu } = useRowMenuActive(openMenu);
|
|
476
|
-
const { show: showActions, always: alwaysShowActions, columnClasses } = useContext(RowActionsContext);
|
|
477
|
-
const styledCells = Children.toArray(children).map((child, i) => {
|
|
478
|
-
const cls = columnClasses[i];
|
|
479
|
-
if (!cls || !isValidElement(child)) return child;
|
|
480
|
-
const el = child;
|
|
481
|
-
return cloneElement(el, { className: cx(el.props.className, cls) });
|
|
482
|
-
});
|
|
483
|
-
const handleRowClick = (e) => {
|
|
484
|
-
if (e.target.closest("a,button,input,label")) return;
|
|
485
|
-
if (selectionActive && onRowSelect) {
|
|
486
|
-
onRowSelect(e);
|
|
487
|
-
return;
|
|
488
|
-
}
|
|
489
|
-
if (href) goTo(href);
|
|
490
|
-
else onActivate?.();
|
|
491
|
-
};
|
|
492
|
-
const pinnedLabels = new Set((pinnedActions ?? []).map((a) => a.label));
|
|
493
|
-
const hasOverflowItems = items.some((item) => item.kind !== "divider" && (item.kind === "submenu" || !pinnedLabels.has(item.label)));
|
|
494
|
-
return /* @__PURE__ */ jsxs("tr", {
|
|
495
|
-
className: cx("group", selected ? "bg-blue-500/5" : menuActive ? "theme-hover" : "hover:theme-hover", (href || onActivate || selectionActive) && "cursor-pointer", className),
|
|
496
|
-
onClick: handleRowClick,
|
|
497
|
-
...rowContextMenuProps(openRowMenu, items),
|
|
498
|
-
children: [
|
|
499
|
-
leading,
|
|
500
|
-
styledCells,
|
|
501
|
-
showActions && /* @__PURE__ */ jsx(Table.Item, {
|
|
502
|
-
className: "relative py-2.5",
|
|
503
|
-
children: /* @__PURE__ */ jsxs("div", {
|
|
504
|
-
className: "absolute inset-y-0 right-0 flex items-center gap-0.5 pr-4",
|
|
505
|
-
children: [pinnedActions && pinnedActions.length > 0 && /* @__PURE__ */ jsx("span", {
|
|
506
|
-
className: "hidden items-center gap-0.5 @[44rem]:flex",
|
|
507
|
-
children: pinnedActions.map((action) => /* @__PURE__ */ jsx("button", {
|
|
508
|
-
type: "button",
|
|
509
|
-
"aria-label": action.label,
|
|
510
|
-
"data-tooltip-id": "global-tooltip",
|
|
511
|
-
"data-tooltip-content": action.disabled && action.tooltip ? action.tooltip : action.label,
|
|
512
|
-
"data-tooltip-place": "bottom",
|
|
513
|
-
"aria-disabled": action.disabled,
|
|
514
|
-
onClick: (e) => {
|
|
515
|
-
e.stopPropagation();
|
|
516
|
-
if (action.disabled) return;
|
|
517
|
-
action.onSelect();
|
|
518
|
-
},
|
|
519
|
-
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", alwaysShowActions || menuActive ? "opacity-100" : "opacity-0 group-hover:opacity-100 focus-visible:opacity-100"),
|
|
520
|
-
children: action.icon
|
|
521
|
-
}, action.key))
|
|
522
|
-
}), /* @__PURE__ */ jsx(MoreButton, {
|
|
523
|
-
active: menuActive || alwaysShowActions,
|
|
524
|
-
onOpen: (x, y) => openRowMenu(x, y, items),
|
|
525
|
-
className: hasOverflowItems ? void 0 : "@[44rem]:hidden"
|
|
526
|
-
})]
|
|
527
|
-
})
|
|
528
|
-
})
|
|
529
|
-
]
|
|
530
|
-
});
|
|
531
|
-
}
|
|
532
|
-
/** Name column min-width is by role, not position, so a leading checkbox doesn't shift it. */
|
|
533
|
-
function ListColumns({ view }) {
|
|
534
|
-
return /* @__PURE__ */ jsxs(Fragment$1, { children: [view.visibleColumns.map((col, i) => /* @__PURE__ */ jsx(Table.Header, {
|
|
535
|
-
caps: false,
|
|
536
|
-
className: cx(col.headerClassName, view.columnClasses[i]),
|
|
537
|
-
style: col.alwaysVisible ? { minWidth: 200 } : void 0,
|
|
538
|
-
children: col.label
|
|
539
|
-
}, col.key)), /* @__PURE__ */ jsx(ListActionsHeader, { view })] });
|
|
540
|
-
}
|
|
541
|
-
ListColumns.isTableHeader = true;
|
|
542
|
-
/** Stable `goTo` for whole-row navigation (`rowNavProps`, `ListRow`). */
|
|
543
|
-
function useListNavigate() {
|
|
544
|
-
return useNavigation().goTo;
|
|
545
|
-
}
|
|
546
|
-
//#endregion
|
|
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\"]"));
|
|
554
|
-
}
|
|
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();
|
|
583
|
-
};
|
|
584
|
-
const onScroll = (e) => {
|
|
585
|
-
if (e.target instanceof Node && panelRef.current?.contains(e.target)) return;
|
|
586
|
-
close();
|
|
587
|
-
};
|
|
588
|
-
window.addEventListener("keydown", onKey);
|
|
589
|
-
window.addEventListener("scroll", onScroll, true);
|
|
590
|
-
window.addEventListener("resize", close);
|
|
591
|
-
return () => {
|
|
592
|
-
window.removeEventListener("keydown", onKey);
|
|
593
|
-
window.removeEventListener("scroll", onScroll, true);
|
|
594
|
-
window.removeEventListener("resize", close);
|
|
595
|
-
};
|
|
596
|
-
}, [pos, close]);
|
|
597
|
-
useEffect(() => {
|
|
598
|
-
if (!shortcut) return;
|
|
599
|
-
const onKey = (e) => {
|
|
600
|
-
if (e.metaKey || e.ctrlKey || e.altKey) return;
|
|
601
|
-
if (e.key.toLowerCase() !== shortcut.toLowerCase()) return;
|
|
602
|
-
if (isTextEntryContext(e.target)) return;
|
|
603
|
-
e.preventDefault();
|
|
604
|
-
open();
|
|
605
|
-
};
|
|
606
|
-
window.addEventListener("keydown", onKey);
|
|
607
|
-
return () => window.removeEventListener("keydown", onKey);
|
|
608
|
-
}, [shortcut, open]);
|
|
609
|
-
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("button", {
|
|
610
|
-
ref: btnRef,
|
|
611
|
-
type: "button",
|
|
612
|
-
onClick: () => pos ? close() : open(),
|
|
613
|
-
"aria-label": label,
|
|
614
|
-
"data-tooltip-id": TOOLTIP_ID,
|
|
615
|
-
"data-tooltip-content": label,
|
|
616
|
-
"data-tooltip-shortcut": shortcut ? shortcut.toUpperCase() : void 0,
|
|
617
|
-
className: cx(listControlButtonClasses, active && "border-(--theme-link) text-(--theme-app)"),
|
|
618
|
-
children: [icon, active && /* @__PURE__ */ jsx("span", { className: "absolute right-0 top-0 h-1.5 w-1.5 rounded-full bg-(--theme-link)" })]
|
|
619
|
-
}), pos && createPortal(/* @__PURE__ */ jsx("div", {
|
|
620
|
-
role: "none",
|
|
621
|
-
className: "fixed inset-0 z-50",
|
|
622
|
-
onMouseDown: close,
|
|
623
|
-
onContextMenu: (e) => {
|
|
624
|
-
e.preventDefault();
|
|
625
|
-
close();
|
|
626
|
-
},
|
|
627
|
-
children: /* @__PURE__ */ jsx("div", {
|
|
628
|
-
ref: panelRef,
|
|
629
|
-
role: "menu",
|
|
630
|
-
onMouseDown: (e) => e.stopPropagation(),
|
|
631
|
-
className: cx("absolute rounded-lg border border-(--theme-border) bg-(--theme-app-bg) shadow-lg", panelClassName),
|
|
632
|
-
style: {
|
|
633
|
-
right: pos.right,
|
|
634
|
-
top: pos.top
|
|
635
|
-
},
|
|
636
|
-
children
|
|
637
|
-
})
|
|
638
|
-
}), document.querySelector(".ds-root") ?? document.body)] });
|
|
639
|
-
}
|
|
640
|
-
/** Small on/off switch for the Display menu's boolean options. */
|
|
641
|
-
function Switch$1({ checked, onChange, label }) {
|
|
642
|
-
return /* @__PURE__ */ jsx("button", {
|
|
643
|
-
type: "button",
|
|
644
|
-
role: "switch",
|
|
645
|
-
"aria-checked": checked,
|
|
646
|
-
"aria-label": label,
|
|
647
|
-
onClick: onChange,
|
|
648
|
-
className: cx("relative inline-flex h-4 w-7 shrink-0 items-center rounded-full transition-colors", checked ? "bg-(--theme-link)" : "bg-(--theme-border)"),
|
|
649
|
-
children: /* @__PURE__ */ jsx("span", { className: cx("inline-block h-3 w-3 rounded-full bg-white transition-transform", checked ? "translate-x-3.5" : "translate-x-0.5") })
|
|
650
|
-
});
|
|
651
|
-
}
|
|
652
|
-
function ListDisplayMenu({ view, label }) {
|
|
653
|
-
const t = useStrings().list;
|
|
654
|
-
const toggleable = view.columns.filter((c) => !c.alwaysVisible);
|
|
655
|
-
const hasGrouping = view.groupBys.length > 0;
|
|
656
|
-
const orderDirLabel = view.orderDir === "asc" ? t.ascending : t.descending;
|
|
657
|
-
return /* @__PURE__ */ jsxs(AnchoredPopover, {
|
|
658
|
-
icon: /* @__PURE__ */ jsx(DisplayIcon, { className: "h-3.5 w-3.5" }),
|
|
659
|
-
label: label ?? t.displayOptions,
|
|
660
|
-
panelClassName: "w-72 p-3",
|
|
661
|
-
children: [
|
|
662
|
-
hasGrouping && /* @__PURE__ */ jsxs("div", {
|
|
663
|
-
className: "flex items-center justify-between gap-3",
|
|
664
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
665
|
-
className: "text-[13px] text-(--theme-app)",
|
|
666
|
-
children: t.grouping
|
|
667
|
-
}), /* @__PURE__ */ jsx(GroupingDropdown, { view })]
|
|
668
|
-
}),
|
|
669
|
-
view.orderBys.length > 0 && /* @__PURE__ */ jsxs("div", {
|
|
670
|
-
className: cx("flex items-center justify-between gap-3", hasGrouping && "mt-2.5"),
|
|
671
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
672
|
-
className: "text-[13px] text-(--theme-app)",
|
|
673
|
-
children: t.ordering
|
|
674
|
-
}), /* @__PURE__ */ jsxs("div", {
|
|
675
|
-
className: "flex items-center gap-1",
|
|
676
|
-
children: [/* @__PURE__ */ jsx("button", {
|
|
677
|
-
type: "button",
|
|
678
|
-
onClick: view.toggleOrderDir,
|
|
679
|
-
"aria-label": orderDirLabel,
|
|
680
|
-
title: orderDirLabel,
|
|
681
|
-
className: "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)",
|
|
682
|
-
children: view.orderDir === "asc" ? /* @__PURE__ */ jsx(ChevronUpIcon, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(ChevronDownIcon, { className: "h-3 w-3" })
|
|
683
|
-
}), /* @__PURE__ */ jsx(OrderingDropdown, { view })]
|
|
684
|
-
})]
|
|
685
|
-
}),
|
|
686
|
-
/* @__PURE__ */ jsxs("div", {
|
|
687
|
-
className: cx("flex items-center justify-between gap-3", (hasGrouping || view.orderBys.length > 0) && "mt-2.5"),
|
|
688
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
689
|
-
className: "text-[13px] text-(--theme-app)",
|
|
690
|
-
children: t.columnHeaders
|
|
691
|
-
}), /* @__PURE__ */ jsx(Switch$1, {
|
|
692
|
-
checked: view.showColumnHeaders,
|
|
693
|
-
onChange: view.toggleColumnHeaders,
|
|
694
|
-
label: t.showColumnHeaders
|
|
695
|
-
})]
|
|
696
|
-
}),
|
|
697
|
-
/* @__PURE__ */ jsxs("div", {
|
|
698
|
-
className: "mt-2.5 flex items-center justify-between gap-3",
|
|
699
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
700
|
-
className: "text-[13px] text-(--theme-app)",
|
|
701
|
-
children: t.showActions
|
|
702
|
-
}), /* @__PURE__ */ jsx(Switch$1, {
|
|
703
|
-
checked: view.showActions,
|
|
704
|
-
onChange: view.toggleShowActions,
|
|
705
|
-
label: t.showRowActions
|
|
706
|
-
})]
|
|
707
|
-
}),
|
|
708
|
-
view.showActions && /* @__PURE__ */ jsxs("div", {
|
|
709
|
-
className: "mt-2.5 flex items-center justify-between gap-3 pl-3",
|
|
710
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
711
|
-
className: "text-[13px] text-(--theme-muted-text-color)",
|
|
712
|
-
children: t.alwaysShowActions
|
|
713
|
-
}), /* @__PURE__ */ jsx(Switch$1, {
|
|
714
|
-
checked: view.alwaysShowActions,
|
|
715
|
-
onChange: view.toggleAlwaysShowActions,
|
|
716
|
-
label: t.alwaysShowRowActions
|
|
717
|
-
})]
|
|
718
|
-
}),
|
|
719
|
-
toggleable.length > 0 && /* @__PURE__ */ jsxs("div", {
|
|
720
|
-
className: "mt-3 border-t border-(--theme-border) pt-3",
|
|
721
|
-
children: [/* @__PURE__ */ jsx("div", {
|
|
722
|
-
className: SECTION_LABEL,
|
|
723
|
-
children: t.displayProperties
|
|
724
|
-
}), /* @__PURE__ */ jsx("div", {
|
|
725
|
-
className: "flex flex-wrap gap-1.5",
|
|
726
|
-
children: toggleable.map((c) => /* @__PURE__ */ jsx("button", {
|
|
727
|
-
type: "button",
|
|
728
|
-
onClick: () => view.toggleColumn(c.key),
|
|
729
|
-
className: pillClass(view.isVisible(c.key)),
|
|
730
|
-
children: c.label
|
|
731
|
-
}, c.key))
|
|
732
|
-
})]
|
|
733
|
-
}),
|
|
734
|
-
view.showActions && view.actions.length > 0 && /* @__PURE__ */ jsxs("div", {
|
|
735
|
-
className: "mt-3 border-t border-(--theme-border) pt-3",
|
|
736
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
737
|
-
className: cx(SECTION_LABEL, "flex items-center gap-1"),
|
|
738
|
-
children: [t.pinnedActions, /* @__PURE__ */ jsx(TooltipInfo, {
|
|
739
|
-
className: "inline-flex",
|
|
740
|
-
text: t.pinnedActionsHelp
|
|
741
|
-
})]
|
|
742
|
-
}), /* @__PURE__ */ jsx("div", {
|
|
743
|
-
className: "flex flex-wrap gap-1.5",
|
|
744
|
-
children: view.actions.map((a) => /* @__PURE__ */ jsx("button", {
|
|
745
|
-
type: "button",
|
|
746
|
-
onClick: () => view.togglePinned(a.key),
|
|
747
|
-
className: pillClass(view.isPinned(a.key)),
|
|
748
|
-
children: a.label
|
|
749
|
-
}, a.key))
|
|
750
|
-
})]
|
|
751
|
-
}),
|
|
752
|
-
!view.isDisplayDefault && /* @__PURE__ */ jsx("div", {
|
|
753
|
-
className: "mt-3 border-t border-(--theme-border) pt-2",
|
|
754
|
-
children: /* @__PURE__ */ jsx("button", {
|
|
755
|
-
type: "button",
|
|
756
|
-
onClick: view.resetDisplay,
|
|
757
|
-
className: "link text-[12px]",
|
|
758
|
-
children: t.reset
|
|
759
|
-
})
|
|
760
|
-
})
|
|
761
|
-
]
|
|
762
|
-
});
|
|
763
|
-
}
|
|
764
|
-
/** Capture phase: the popover panel stops mousedown propagation, so a
|
|
765
|
-
* bubble-phase listener never sees clicks inside it. */
|
|
766
|
-
function useCloseOnOutsideClick(open, close) {
|
|
767
|
-
const ref = useRef(null);
|
|
768
|
-
useEffect(() => {
|
|
769
|
-
if (!open) return;
|
|
770
|
-
const onDown = (e) => {
|
|
771
|
-
if (e.target instanceof Node && !ref.current?.contains(e.target)) close();
|
|
772
|
-
};
|
|
773
|
-
document.addEventListener("mousedown", onDown, true);
|
|
774
|
-
return () => document.removeEventListener("mousedown", onDown, true);
|
|
775
|
-
}, [open, close]);
|
|
776
|
-
return ref;
|
|
777
|
-
}
|
|
778
|
-
function GroupingDropdown({ view }) {
|
|
779
|
-
const t = useStrings().list;
|
|
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);
|
|
783
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
784
|
-
ref,
|
|
785
|
-
className: "relative",
|
|
786
|
-
children: [/* @__PURE__ */ jsxs("button", {
|
|
787
|
-
type: "button",
|
|
788
|
-
onClick: () => setOpen((o) => !o),
|
|
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)",
|
|
790
|
-
children: [current?.label ?? t.none, /* @__PURE__ */ jsx(ChevronDownIcon, { className: "h-2.5 w-2.5 opacity-60" })]
|
|
791
|
-
}), open && /* @__PURE__ */ jsx("div", {
|
|
792
|
-
className: cx("absolute right-0 top-full z-20 mt-1", FLYOUT),
|
|
793
|
-
children: view.groupBys.map((g) => /* @__PURE__ */ jsxs("button", {
|
|
794
|
-
type: "button",
|
|
795
|
-
onClick: () => {
|
|
796
|
-
view.setGroupBy(g.value);
|
|
797
|
-
setOpen(false);
|
|
798
|
-
},
|
|
799
|
-
className: cx("flex w-full items-center justify-between gap-3 px-3 py-1.5 text-left text-[13px] hover:bg-(--theme-muted-panel-bg)", view.groupBy === g.value ? "text-(--theme-app)" : "text-(--theme-muted-text-color)"),
|
|
800
|
-
children: [g.label, view.groupBy === g.value && /* @__PURE__ */ jsx(CheckIcon, { className: "h-3 w-3" })]
|
|
801
|
-
}, g.value))
|
|
802
|
-
})]
|
|
803
|
-
});
|
|
804
|
-
}
|
|
805
|
-
function OrderingDropdown({ view }) {
|
|
806
|
-
const t = useStrings().list;
|
|
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);
|
|
810
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
811
|
-
ref,
|
|
812
|
-
className: "relative",
|
|
813
|
-
children: [/* @__PURE__ */ jsxs("button", {
|
|
814
|
-
type: "button",
|
|
815
|
-
onClick: () => setOpen((o) => !o),
|
|
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)",
|
|
817
|
-
children: [current?.label ?? t.none, /* @__PURE__ */ jsx(ChevronDownIcon, { className: "h-2.5 w-2.5 opacity-60" })]
|
|
818
|
-
}), open && /* @__PURE__ */ jsx("div", {
|
|
819
|
-
className: cx("absolute right-0 top-full z-20 mt-1", FLYOUT),
|
|
820
|
-
children: view.orderBys.map((o) => /* @__PURE__ */ jsxs("button", {
|
|
821
|
-
type: "button",
|
|
822
|
-
onClick: () => {
|
|
823
|
-
view.setOrderBy(o.value);
|
|
824
|
-
setOpen(false);
|
|
825
|
-
},
|
|
826
|
-
className: cx("flex w-full items-center justify-between gap-3 px-3 py-1.5 text-left text-[13px] hover:bg-(--theme-muted-panel-bg)", view.orderBy === o.value ? "text-(--theme-app)" : "text-(--theme-muted-text-color)"),
|
|
827
|
-
children: [o.label, view.orderBy === o.value && /* @__PURE__ */ jsx(CheckIcon, { className: "h-3 w-3" })]
|
|
828
|
-
}, o.value))
|
|
829
|
-
})]
|
|
830
|
-
});
|
|
831
|
-
}
|
|
832
|
-
function ListFilterMenu({ view, label }) {
|
|
833
|
-
const t = useStrings().list;
|
|
834
|
-
if (view.facets.length === 0) return null;
|
|
835
|
-
return /* @__PURE__ */ jsx(AnchoredPopover, {
|
|
836
|
-
icon: /* @__PURE__ */ jsx(FilterIcon, { className: "h-3.5 w-3.5" }),
|
|
837
|
-
label: label ?? t.filter,
|
|
838
|
-
active: view.activeFilterCount > 0,
|
|
839
|
-
shortcut: "f",
|
|
840
|
-
panelClassName: "w-56 py-1",
|
|
841
|
-
children: /* @__PURE__ */ jsx(FilterMenuBody, { view })
|
|
842
|
-
});
|
|
843
|
-
}
|
|
844
|
-
/** Single-open submenu prevents overlapping; resets on panel remount so Filter opens clean. */
|
|
845
|
-
function FilterMenuBody({ view }) {
|
|
846
|
-
const t = useStrings().list;
|
|
847
|
-
const [openKey, setOpenKey] = useState(null);
|
|
848
|
-
const closeTimer = useRef(void 0);
|
|
849
|
-
const openFacet = (key) => {
|
|
850
|
-
clearTimeout(closeTimer.current);
|
|
851
|
-
setOpenKey(key);
|
|
852
|
-
};
|
|
853
|
-
const scheduleClose = () => {
|
|
854
|
-
closeTimer.current = setTimeout(() => setOpenKey(null), 160);
|
|
855
|
-
};
|
|
856
|
-
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
|
|
857
|
-
className: "flex items-center justify-between px-3 py-1",
|
|
858
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
859
|
-
className: "text-[11px] font-medium uppercase tracking-wide text-(--theme-muted-text-color)",
|
|
860
|
-
children: t.filter
|
|
861
|
-
}), view.activeFilterCount > 0 && /* @__PURE__ */ jsx("button", {
|
|
862
|
-
type: "button",
|
|
863
|
-
onClick: view.clearFilters,
|
|
864
|
-
className: "text-[12px] text-(--theme-muted-text-color) hover:text-(--theme-app)",
|
|
865
|
-
children: t.clear
|
|
866
|
-
})]
|
|
867
|
-
}), view.facets.map((facet) => /* @__PURE__ */ jsx(FilterFacetRow, {
|
|
868
|
-
view,
|
|
869
|
-
facet,
|
|
870
|
-
open: openKey === facet.key,
|
|
871
|
-
onEnter: () => openFacet(facet.key),
|
|
872
|
-
onLeave: scheduleClose
|
|
873
|
-
}, facet.key))] });
|
|
874
|
-
}
|
|
875
|
-
function FilterFacetRow({ view, facet, open, onEnter, onLeave }) {
|
|
876
|
-
const selected = view.filters[facet.key] ?? [];
|
|
877
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
878
|
-
className: "relative",
|
|
879
|
-
role: "none",
|
|
880
|
-
onMouseEnter: onEnter,
|
|
881
|
-
onMouseLeave: onLeave,
|
|
882
|
-
onFocus: onEnter,
|
|
883
|
-
onBlur: onLeave,
|
|
884
|
-
children: [/* @__PURE__ */ jsxs("button", {
|
|
885
|
-
type: "button",
|
|
886
|
-
"aria-expanded": open,
|
|
887
|
-
"aria-haspopup": "menu",
|
|
888
|
-
onClick: onEnter,
|
|
889
|
-
className: "flex w-full cursor-default items-center justify-between gap-3 px-3 py-1.5 text-[13px] text-(--theme-app) hover:bg-(--theme-muted-panel-bg)",
|
|
890
|
-
children: [/* @__PURE__ */ jsx("span", { children: facet.label }), /* @__PURE__ */ jsxs("span", {
|
|
891
|
-
className: "flex items-center gap-1.5",
|
|
892
|
-
children: [selected.length > 0 && /* @__PURE__ */ jsx("span", {
|
|
893
|
-
className: "text-[11px] text-(--theme-muted-text-color)",
|
|
894
|
-
children: selected.length
|
|
895
|
-
}), /* @__PURE__ */ jsx(ChevronRightIcon, { className: "h-3 w-3 opacity-60" })]
|
|
896
|
-
})]
|
|
897
|
-
}), open && /* @__PURE__ */ jsx("div", {
|
|
898
|
-
className: cx("absolute right-full top-0 z-10 max-h-[70vh] overflow-y-auto", FLYOUT),
|
|
899
|
-
children: facet.options.map((option) => {
|
|
900
|
-
const checked = selected.includes(option.value);
|
|
901
|
-
return /* @__PURE__ */ jsxs("button", {
|
|
902
|
-
type: "button",
|
|
903
|
-
onClick: () => view.setFacetValues(facet.key, checked ? selected.filter((v) => v !== option.value) : [...selected, option.value]),
|
|
904
|
-
className: "flex w-full items-center gap-2 px-3 py-1.5 text-left text-[13px] text-(--theme-app) hover:bg-(--theme-muted-panel-bg)",
|
|
905
|
-
children: [
|
|
906
|
-
/* @__PURE__ */ jsx("span", {
|
|
907
|
-
className: "flex h-3.5 w-3.5 shrink-0 items-center justify-center text-(--theme-link)",
|
|
908
|
-
children: checked && /* @__PURE__ */ jsx(CheckIcon, { className: "h-3 w-3" })
|
|
909
|
-
}),
|
|
910
|
-
option.icon,
|
|
911
|
-
option.label
|
|
912
|
-
]
|
|
913
|
-
}, option.value);
|
|
914
|
-
})
|
|
915
|
-
})]
|
|
916
|
-
});
|
|
917
|
-
}
|
|
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
|
|
972
|
-
//#region src/list/listSearch.tsx
|
|
973
|
-
var NO_TERMS = [];
|
|
974
|
-
/** Pass `enabled: false` to suspend the global ⌘F/`/` hijack (e.g. when search isn't shown and browser Find should stay active). */
|
|
975
|
-
function useListSearch(enabled = true, storageKey) {
|
|
976
|
-
const [filter, setFilter] = useState("");
|
|
977
|
-
const [searchOpen, setSearchOpen] = useState(false);
|
|
978
|
-
const [storedTerms, setStoredTerms] = useLocalStorage(`${storageKey ?? "list"}:searchTerms`, NO_TERMS);
|
|
979
|
-
const terms = storageKey ? storedTerms : NO_TERMS;
|
|
980
|
-
const inputRef = useRef(null);
|
|
981
|
-
useEffect(() => {
|
|
982
|
-
if (searchOpen) inputRef.current?.focus();
|
|
983
|
-
}, [searchOpen]);
|
|
984
|
-
useEffect(() => {
|
|
985
|
-
if (!enabled) return;
|
|
986
|
-
const onKey = (e) => {
|
|
987
|
-
if (e.key === "Escape" && searchOpen) {
|
|
988
|
-
setSearchOpen(false);
|
|
989
|
-
setFilter("");
|
|
990
|
-
return;
|
|
991
|
-
}
|
|
992
|
-
if (isTextEntryContext(e.target)) return;
|
|
993
|
-
if ((e.key === "f" || e.key === "F") && (e.metaKey || e.ctrlKey)) {
|
|
994
|
-
e.preventDefault();
|
|
995
|
-
setSearchOpen(true);
|
|
996
|
-
} else if (e.key === "/") {
|
|
997
|
-
e.preventDefault();
|
|
998
|
-
setSearchOpen(true);
|
|
999
|
-
}
|
|
1000
|
-
};
|
|
1001
|
-
window.addEventListener("keydown", onKey);
|
|
1002
|
-
return () => window.removeEventListener("keydown", onKey);
|
|
1003
|
-
}, [searchOpen, enabled]);
|
|
1004
|
-
const openSearch = useCallback(() => setSearchOpen(true), []);
|
|
1005
|
-
const closeSearch = useCallback(() => {
|
|
1006
|
-
setSearchOpen(false);
|
|
1007
|
-
setFilter("");
|
|
1008
|
-
}, []);
|
|
1009
|
-
const addTerm = useCallback((term) => {
|
|
1010
|
-
const trimmed = term.trim();
|
|
1011
|
-
if (!storageKey || !trimmed) return;
|
|
1012
|
-
setStoredTerms((prev) => prev.some((t) => t.toLowerCase() === trimmed.toLowerCase()) ? prev : [...prev, trimmed]);
|
|
1013
|
-
setFilter("");
|
|
1014
|
-
}, [storageKey, setStoredTerms]);
|
|
1015
|
-
const removeTerm = useCallback((term) => setStoredTerms((prev) => prev.filter((t) => t !== term)), [setStoredTerms]);
|
|
1016
|
-
const clearSearch = useCallback(() => {
|
|
1017
|
-
if (storageKey) setStoredTerms(NO_TERMS);
|
|
1018
|
-
setSearchOpen(false);
|
|
1019
|
-
setFilter("");
|
|
1020
|
-
}, [storageKey, setStoredTerms]);
|
|
1021
|
-
const trimmedFilter = filter.trim();
|
|
1022
|
-
return {
|
|
1023
|
-
filter,
|
|
1024
|
-
setFilter,
|
|
1025
|
-
searchOpen,
|
|
1026
|
-
openSearch,
|
|
1027
|
-
closeSearch,
|
|
1028
|
-
clearSearch,
|
|
1029
|
-
terms,
|
|
1030
|
-
addTerm,
|
|
1031
|
-
removeTerm,
|
|
1032
|
-
queries: trimmedFilter ? [...terms, trimmedFilter] : terms,
|
|
1033
|
-
inputRef
|
|
1034
|
-
};
|
|
1035
|
-
}
|
|
1036
|
-
function ListSearchControl({ search, placeholder, label = "Search" }) {
|
|
1037
|
-
if (search.searchOpen || search.terms.length > 0) return /* @__PURE__ */ jsxs("div", {
|
|
1038
|
-
className: "flex min-h-7 w-72 max-w-[55vw] flex-wrap items-center gap-1.5 rounded-md border border-(--theme-border) bg-(--theme-muted-panel-bg) px-2 py-0.5",
|
|
1039
|
-
children: [
|
|
1040
|
-
/* @__PURE__ */ jsx(SearchIcon, { className: "w-3.5 h-3.5 shrink-0 text-(--theme-muted-text-color)" }),
|
|
1041
|
-
search.terms.map((term) => /* @__PURE__ */ jsxs("span", {
|
|
1042
|
-
className: "flex max-w-40 items-center gap-1 rounded border border-(--theme-border) bg-(--theme-app-bg) px-1.5 py-px text-[12px] text-(--theme-app)",
|
|
1043
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
1044
|
-
className: "truncate",
|
|
1045
|
-
children: term
|
|
1046
|
-
}), /* @__PURE__ */ jsx("button", {
|
|
1047
|
-
type: "button",
|
|
1048
|
-
onClick: () => search.removeTerm(term),
|
|
1049
|
-
"aria-label": `Remove filter ${term}`,
|
|
1050
|
-
className: "shrink-0 rounded p-0.5 text-(--theme-muted-text-color) hover:text-(--theme-app) transition-colors",
|
|
1051
|
-
children: /* @__PURE__ */ jsx(XIcon, { className: "w-2.5 h-2.5" })
|
|
1052
|
-
})]
|
|
1053
|
-
}, term)),
|
|
1054
|
-
/* @__PURE__ */ jsx("input", {
|
|
1055
|
-
ref: search.inputRef,
|
|
1056
|
-
value: search.filter,
|
|
1057
|
-
onChange: (e) => search.setFilter(e.target.value),
|
|
1058
|
-
onFocus: search.openSearch,
|
|
1059
|
-
onKeyDown: (e) => {
|
|
1060
|
-
if (e.key === "Enter") {
|
|
1061
|
-
e.preventDefault();
|
|
1062
|
-
search.addTerm(search.filter);
|
|
1063
|
-
} else if (e.key === "Backspace" && !search.filter) {
|
|
1064
|
-
const lastTerm = search.terms.at(-1);
|
|
1065
|
-
if (lastTerm) search.removeTerm(lastTerm);
|
|
1066
|
-
}
|
|
1067
|
-
},
|
|
1068
|
-
placeholder,
|
|
1069
|
-
className: "min-w-24 flex-1 bg-transparent text-[13px] text-(--theme-app) placeholder:text-(--theme-muted-text-color) focus:outline-none"
|
|
1070
|
-
}),
|
|
1071
|
-
/* @__PURE__ */ jsx("button", {
|
|
1072
|
-
type: "button",
|
|
1073
|
-
onClick: search.clearSearch,
|
|
1074
|
-
"aria-label": "Clear search",
|
|
1075
|
-
className: "shrink-0 rounded p-0.5 text-(--theme-muted-text-color) hover:text-(--theme-app) transition-colors",
|
|
1076
|
-
children: /* @__PURE__ */ jsx(XIcon, { className: "w-3 h-3" })
|
|
1077
|
-
})
|
|
1078
|
-
]
|
|
1079
|
-
});
|
|
1080
|
-
return /* @__PURE__ */ jsx("button", {
|
|
1081
|
-
type: "button",
|
|
1082
|
-
onClick: search.openSearch,
|
|
1083
|
-
"aria-label": label,
|
|
1084
|
-
"data-tooltip-id": TOOLTIP_ID,
|
|
1085
|
-
"data-tooltip-content": label,
|
|
1086
|
-
"data-tooltip-shortcut": "⌘+F",
|
|
1087
|
-
className: listControlButtonClasses,
|
|
1088
|
-
children: /* @__PURE__ */ jsx(SearchIcon, { className: "w-3.5 h-3.5" })
|
|
1089
|
-
});
|
|
1090
|
-
}
|
|
1091
|
-
function ListCount({ total, matches, filterActive, noun, nounPlural }) {
|
|
1092
|
-
return /* @__PURE__ */ jsx("div", {
|
|
1093
|
-
className: "flex items-center h-9",
|
|
1094
|
-
children: /* @__PURE__ */ jsx("span", {
|
|
1095
|
-
className: "text-[13px] text-(--theme-muted-text-color)",
|
|
1096
|
-
children: filterActive ? `${matches} of ${total} ${nounPlural}` : `${total} ${total === 1 ? noun : nounPlural}`
|
|
1097
|
-
})
|
|
1098
|
-
});
|
|
1099
|
-
}
|
|
1100
|
-
//#endregion
|
|
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] : "";
|
|
1108
|
-
}
|
|
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;
|
|
1201
|
-
});
|
|
1202
|
-
}, [
|
|
1203
|
-
setLocalFilters,
|
|
1204
|
-
setSharedFilters,
|
|
1205
|
-
sharedKeys
|
|
1206
|
-
]);
|
|
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
|
-
};
|
|
1305
|
-
}
|
|
1306
|
-
//#endregion
|
|
1307
|
-
//#region src/list/UserHoverCard.tsx
|
|
1308
|
-
/** Opens `card` after a short hover or on focus of `children`. */
|
|
1309
|
-
function HoverCardTrigger({ className, children, card }) {
|
|
1310
|
-
const { triggerRef, coords, scheduleOpen, scheduleClose, keepOpen } = useHoverCard();
|
|
1311
|
-
return /* @__PURE__ */ jsxs("span", {
|
|
1312
|
-
ref: triggerRef,
|
|
1313
|
-
role: "none",
|
|
1314
|
-
className,
|
|
1315
|
-
onMouseEnter: scheduleOpen,
|
|
1316
|
-
onMouseLeave: scheduleClose,
|
|
1317
|
-
onFocus: scheduleOpen,
|
|
1318
|
-
onBlur: scheduleClose,
|
|
1319
|
-
children: [children, coords && card({
|
|
1320
|
-
x: coords.x,
|
|
1321
|
-
y: coords.y,
|
|
1322
|
-
onKeepOpen: keepOpen,
|
|
1323
|
-
onLeave: scheduleClose
|
|
1324
|
-
})]
|
|
1325
|
-
});
|
|
1326
|
-
}
|
|
1327
|
-
function UserHoverCard({ x, y, onKeepOpen, onLeave, username, name, avatarSrc, badge, href, linkComponent, loading = false, relayoutKey, children }) {
|
|
1328
|
-
const strings = useStrings();
|
|
1329
|
-
const SlotLink = useLink();
|
|
1330
|
-
const Link = linkComponent ?? SlotLink;
|
|
1331
|
-
const displayName = name || username;
|
|
1332
|
-
const header = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Avatar, {
|
|
1333
|
-
src: avatarSrc,
|
|
1334
|
-
name: displayName,
|
|
1335
|
-
size: "lg",
|
|
1336
|
-
className: "shrink-0"
|
|
1337
|
-
}), /* @__PURE__ */ jsxs("div", {
|
|
1338
|
-
className: "min-w-0",
|
|
1339
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
1340
|
-
className: "flex items-center gap-1.5",
|
|
1341
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
1342
|
-
className: "truncate text-sm font-semibold text-(--theme-app)",
|
|
1343
|
-
children: displayName
|
|
1344
|
-
}), badge && /* @__PURE__ */ jsx("span", {
|
|
1345
|
-
className: "shrink-0 rounded-full bg-(--theme-muted-panel-bg) px-1.5 py-0.5 text-[10px] font-medium leading-none text-(--theme-muted-text-color)",
|
|
1346
|
-
children: badge
|
|
1347
|
-
})]
|
|
1348
|
-
}), /* @__PURE__ */ jsx("div", {
|
|
1349
|
-
className: "truncate text-xs text-(--theme-muted-text-color)",
|
|
1350
|
-
children: username
|
|
1351
|
-
})]
|
|
1352
|
-
})] });
|
|
1353
|
-
return /* @__PURE__ */ jsxs(HoverCardSurface, {
|
|
1354
|
-
x,
|
|
1355
|
-
y,
|
|
1356
|
-
widthClassName: "w-64",
|
|
1357
|
-
relayoutKey,
|
|
1358
|
-
onKeepOpen,
|
|
1359
|
-
onLeave,
|
|
1360
|
-
children: [href ? /* @__PURE__ */ jsx(Link, {
|
|
1361
|
-
to: href,
|
|
1362
|
-
className: "flex items-center gap-3 transition-opacity hover:opacity-80",
|
|
1363
|
-
children: header
|
|
1364
|
-
}) : /* @__PURE__ */ jsx("div", {
|
|
1365
|
-
className: "flex items-center gap-3",
|
|
1366
|
-
children: header
|
|
1367
|
-
}), loading ? /* @__PURE__ */ jsxs("div", {
|
|
1368
|
-
role: "status",
|
|
1369
|
-
"aria-label": strings.loading,
|
|
1370
|
-
className: "mt-3 space-y-2 border-t border-(--theme-border) pt-3",
|
|
1371
|
-
children: [
|
|
1372
|
-
/* @__PURE__ */ jsx("div", {
|
|
1373
|
-
"aria-hidden": "true",
|
|
1374
|
-
className: "h-3 w-24 animate-pulse rounded theme-muted-panel"
|
|
1375
|
-
}),
|
|
1376
|
-
/* @__PURE__ */ jsx("div", {
|
|
1377
|
-
"aria-hidden": "true",
|
|
1378
|
-
className: "h-3 w-36 animate-pulse rounded theme-muted-panel"
|
|
1379
|
-
}),
|
|
1380
|
-
/* @__PURE__ */ jsx("span", {
|
|
1381
|
-
className: "sr-only",
|
|
1382
|
-
children: strings.loading
|
|
1383
|
-
})
|
|
1384
|
-
]
|
|
1385
|
-
}) : children ? /* @__PURE__ */ jsx("div", {
|
|
1386
|
-
className: "mt-3 space-y-2 border-t border-(--theme-border) pt-3 text-[13px]",
|
|
1387
|
-
children
|
|
1388
|
-
}) : null]
|
|
1389
|
-
});
|
|
1390
|
-
}
|
|
1391
|
-
var rowClasses = "flex gap-2 text-(--theme-muted-text-color)";
|
|
1392
|
-
/** One detail line of a hover card: a leading icon and its content. With
|
|
1393
|
-
* `onClick` the line is a button. */
|
|
1394
|
-
function HoverCardRow({ icon, children, title, align = "center", truncate = false, onClick }) {
|
|
1395
|
-
const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
1396
|
-
"aria-hidden": "true",
|
|
1397
|
-
className: cx("flex shrink-0 items-center [&>svg]:h-3.5 [&>svg]:w-3.5", align === "start" && "mt-0.5"),
|
|
1398
|
-
children: icon
|
|
1399
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
1400
|
-
className: cx("min-w-0", truncate && "truncate"),
|
|
1401
|
-
children
|
|
1402
|
-
})] });
|
|
1403
|
-
const alignClass = align === "start" ? "items-start" : "items-center";
|
|
1404
|
-
if (onClick) return /* @__PURE__ */ jsx("button", {
|
|
1405
|
-
type: "button",
|
|
1406
|
-
title,
|
|
1407
|
-
onClick,
|
|
1408
|
-
className: cx(rowClasses, alignClass, "-mx-1 max-w-full rounded px-1 py-0.5 text-left transition-colors hover:bg-(--theme-muted-panel-bg) hover:text-(--theme-app)"),
|
|
1409
|
-
children: content
|
|
1410
|
-
});
|
|
1411
|
-
return /* @__PURE__ */ jsx("div", {
|
|
1412
|
-
title,
|
|
1413
|
-
className: cx(rowClasses, alignClass),
|
|
1414
|
-
children: content
|
|
1415
|
-
});
|
|
1416
|
-
}
|
|
1417
|
-
//#endregion
|
|
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 };
|