@parallelworks/ui 0.5.0 → 0.5.2

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 (32) hide show
  1. package/README.md +0 -6
  2. package/dist/chunks/{CodePreviewEditor-CBtYRZi6.js → CodePreviewEditor-Uij_Npp4.js} +1 -1
  3. package/dist/chunks/{CreateModal-6zZZHxCI.js → CreateModal-DEvWkdFl.js} +4 -29
  4. package/dist/chunks/{Monaco-CvFXoW3r.js → Monaco-H1M5hQbJ.js} +2 -2
  5. package/dist/chunks/PageHeader-CSo3LVId.js +571 -0
  6. package/dist/chunks/{Provider-jQCAN3NN.js → Provider-BIL5347a.js} +5 -1
  7. package/dist/chunks/{SwitchToggle-D0ud8cd5.js → SwitchToggle-DiBXEP12.js} +1 -1
  8. package/dist/chunks/{editor-CEXg6C8X.js → editor-4ZuZruLz.js} +2 -2
  9. package/dist/chunks/{list-CvDWt0TC.js → list-CgzqVlao.js} +296 -302
  10. package/dist/components/Breadcrumbs.d.ts +28 -0
  11. package/dist/components/PageHeader.d.ts +69 -0
  12. package/dist/components/Provider.d.ts +13 -1
  13. package/dist/components/Table.d.ts +1 -1
  14. package/dist/editor/index.js +1 -1
  15. package/dist/file-explorer/FileExplorer.d.ts +1 -3
  16. package/dist/file-explorer/helpers.d.ts +1 -3
  17. package/dist/file-explorer/index.js +15 -22
  18. package/dist/form/index.js +2 -2
  19. package/dist/index.d.ts +2 -0
  20. package/dist/index.js +6 -5
  21. package/dist/list/ActionToolbar.d.ts +38 -0
  22. package/dist/list/ListPage.d.ts +63 -0
  23. package/dist/list/RowContextMenu.d.ts +1 -1
  24. package/dist/list/index.d.ts +2 -0
  25. package/dist/list/index.js +3 -2
  26. package/dist/logviewer/index.js +1 -1
  27. package/dist/styles/theme.css +2 -0
  28. package/dist/styles.css +1 -1
  29. package/dist/theme/index.d.ts +1 -1
  30. package/dist/theme/index.js +9 -0
  31. package/dist/workflow/index.js +1 -1
  32. package/package.json +1 -1
@@ -1,321 +1,186 @@
1
- import { CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, ClipboardIcon, CopyIcon, DisplayIcon, ErrorIcon, FilterIcon, IdCardIcon, KeyIcon, LinkIcon, MailIcon, MoreIcon, SearchIcon, UserIcon, XIcon } from "../icons.js";
2
- import { c as useStrings, i as useNotify, n as useLink } from "./Provider-jQCAN3NN.js";
1
+ import { CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, DisplayIcon, ErrorIcon, FilterIcon, MenuIcon, SearchIcon, XIcon } from "../icons.js";
2
+ import { c as useStrings, n as useLink, r as useNavigation } from "./Provider-BIL5347a.js";
3
3
  import { n as TOOLTIP_ID, r as TooltipInfo } from "./Tooltip-WgbiC_nd.js";
4
+ import { c as PageHeader, f as rowContextMenuProps, g as useRowMenuActive, l as MoreButton } from "./PageHeader-CSo3LVId.js";
4
5
  import { n as Table } from "./Table-ByziK93x.js";
5
6
  import cx from "classnames";
6
7
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
- import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
8
+ import { Children, Fragment as Fragment$1, cloneElement, createContext, isValidElement, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
8
9
  import { createPortal } from "react-dom";
10
+ import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react";
9
11
  import { useLocalStorage } from "usehooks-ts";
10
- //#region src/list/RowContextMenu.tsx
11
- /** Converts a single row action to a menu item: a `to` makes it a link, otherwise an action. */
12
- function toRowMenuItem(action) {
13
- if (action.to) return {
14
- kind: "link",
15
- label: action.label,
16
- icon: action.icon,
17
- to: action.to,
18
- destructive: action.destructive
19
- };
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) {
20
16
  return {
21
- kind: "action",
22
- label: action.label,
23
- icon: action.icon,
24
- onSelect: action.onSelect ?? (() => {}),
25
- disabled: action.disabled,
26
- destructive: action.destructive
17
+ "data-tooltip-id": TOOLTIP_ID,
18
+ "data-tooltip-place": place,
19
+ ...content !== void 0 && { "data-tooltip-content": content }
27
20
  };
28
21
  }
29
- /** Bridges a toolbar action (icon component, `onClick`/`href`) onto the shared
30
- * `RowAction` shape. Typed structurally so any toolbar action shape matching
31
- * these fields is assignable. */
32
- function toolbarActionToRowAction(action) {
22
+ function ToolbarItemView({ item }) {
23
+ if ("element" in item) return /* @__PURE__ */ jsx(Fragment, { 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");
33
32
  const Icon = action.icon;
34
- return {
35
- key: action.key,
36
- label: action.label,
37
- icon: /* @__PURE__ */ jsx(Icon, {}),
38
- onSelect: action.onClick,
33
+ const content = /* @__PURE__ */ jsxs(Fragment, { 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, {
39
38
  to: action.href,
40
- disabled: action.disabled,
41
- destructive: action.key === "delete" || action.key === "destroy"
42
- };
43
- }
44
- /** Assembles a row's context menu in the shared order: leading link(s) → middle
45
- * actions → extra items → Copy submenu → trailing (destructive) actions last. */
46
- function assembleRowMenu({ lead = [], actions, exclude, order, trailing = ["delete"], extra = [], copy }) {
47
- const trailingSet = new Set(trailing);
48
- const excludeSet = new Set(exclude);
49
- let middle = actions.filter((a) => !trailingSet.has(a.key) && !excludeSet.has(a.key));
50
- if (order) {
51
- const byKey = new Map(middle.map((a) => [a.key, a]));
52
- middle = order.map((key) => byKey.get(key)).filter((a) => a !== void 0);
53
- }
54
- const trailingItems = trailing.map((key) => actions.find((a) => a.key === key)).filter((a) => a !== void 0);
55
- return [
56
- ...lead,
57
- ...middle.map(toRowMenuItem),
58
- ...extra,
59
- ...copy ? [copy] : [],
60
- ...trailingItems.map(toRowMenuItem)
61
- ];
62
- }
63
- function useRowMenu() {
64
- const [menu, setMenu] = useState(null);
65
- const onCloseRef = useRef(void 0);
66
- return {
67
- openMenu: useCallback((x, y, items, onClose) => {
68
- if (items.length > 0) {
69
- onCloseRef.current?.();
70
- onCloseRef.current = onClose;
71
- setMenu({
72
- x,
73
- y,
74
- items
75
- });
76
- }
77
- }, []),
78
- contextMenu: /* @__PURE__ */ jsx(RowContextMenu, {
79
- state: menu,
80
- onClose: useCallback(() => {
81
- onCloseRef.current?.();
82
- onCloseRef.current = void 0;
83
- setMenu(null);
84
- }, [])
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
85
51
  })
86
- };
87
- }
88
- /** The full-screen menu overlay drops CSS :hover on the triggering row; wrap openMenu here to substitute a JS-driven `active` highlight. */
89
- function useRowMenuActive(openMenu) {
90
- const [active, setActive] = useState(false);
91
- return {
92
- active,
93
- openMenu: useCallback((x, y, items) => {
94
- setActive(true);
95
- openMenu(x, y, items, () => setActive(false));
96
- }, [openMenu])
97
- };
52
+ });
53
+ return /* @__PURE__ */ jsx("button", {
54
+ type: "button",
55
+ onClick: action.onClick,
56
+ className,
57
+ ...tooltipProps,
58
+ children: content
59
+ });
98
60
  }
99
- function rowContextMenuProps(openMenu, items) {
100
- return { onContextMenu: (e) => {
101
- e.preventDefault();
102
- if (items.length > 0) e.stopPropagation();
103
- openMenu(e.clientX, e.clientY, items);
104
- } };
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, { 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
+ });
105
87
  }
106
- function RowContextMenu({ state, onClose }) {
107
- const ref = useRef(null);
108
- const [pos, setPos] = useState(null);
109
- useLayoutEffect(() => {
110
- if (!state || !ref.current) {
111
- setPos(null);
112
- return;
113
- }
114
- const { width, height } = ref.current.getBoundingClientRect();
115
- const pad = 8;
116
- const top = height > window.innerHeight - pad - state.y && state.y - height - pad >= 0 ? state.y - height : Math.min(state.y, window.innerHeight - height - pad);
117
- setPos({
118
- left: Math.max(pad, Math.min(state.x, window.innerWidth - width - pad)),
119
- top: Math.max(pad, top)
120
- });
121
- }, [state]);
122
- useEffect(() => {
123
- if (!state) return;
124
- const close = () => onClose();
125
- const onKey = (e) => {
126
- if (e.key === "Escape") onClose();
127
- };
128
- window.addEventListener("scroll", close, true);
129
- window.addEventListener("resize", close);
130
- window.addEventListener("keydown", onKey);
131
- return () => {
132
- window.removeEventListener("scroll", close, true);
133
- window.removeEventListener("resize", close);
134
- window.removeEventListener("keydown", onKey);
135
- };
136
- }, [state, onClose]);
137
- if (!state) return null;
138
- const side = (pos?.left ?? state.x) > window.innerWidth * .6 ? "left" : "right";
139
- return createPortal(/* @__PURE__ */ jsx("div", {
140
- className: "fixed inset-0 z-50",
141
- onClick: onClose,
142
- onContextMenu: (e) => {
143
- e.preventDefault();
144
- if (ref.current?.contains(e.target)) {
145
- onClose();
146
- return;
147
- }
148
- const { clientX, clientY } = e;
149
- const overlay = e.currentTarget;
150
- overlay.style.pointerEvents = "none";
151
- const under = document.elementFromPoint(clientX, clientY);
152
- overlay.style.pointerEvents = "";
153
- onClose();
154
- under?.dispatchEvent(new MouseEvent("contextmenu", {
155
- bubbles: true,
156
- cancelable: true,
157
- clientX,
158
- clientY
159
- }));
160
- },
161
- children: /* @__PURE__ */ jsx("div", {
162
- ref,
163
- role: "menu",
164
- className: "absolute min-w-44 rounded-lg border border-(--theme-border) bg-(--theme-panel-bg) py-1 shadow-lg backdrop-blur-xl",
165
- style: {
166
- left: pos?.left ?? state.x,
167
- top: pos?.top ?? state.y,
168
- visibility: pos ? "visible" : "hidden"
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
+ className: "inline-flex items-center justify-center h-9 w-9 rounded-md border theme-border link hover:theme-hover transition-colors",
95
+ "data-tooltip-id": TOOLTIP_ID,
96
+ "data-tooltip-content": t.moreActions,
97
+ "data-testid": "action-toolbar-menu-button",
98
+ children: /* @__PURE__ */ jsx(MenuIcon, {})
99
+ }), /* @__PURE__ */ jsx(MenuItems, {
100
+ unmount: false,
101
+ anchor: {
102
+ to: "bottom end",
103
+ gap: 4
169
104
  },
170
- onClick: (e) => e.stopPropagation(),
171
- children: /* @__PURE__ */ jsx(MenuItemList, {
172
- items: state.items,
173
- onClose,
174
- side
105
+ className: "z-50 min-w-56 rounded-md border theme-border bg-(--theme-app-bg) shadow-lg py-1 focus:outline-none",
106
+ children: /* @__PURE__ */ jsx(OverflowMenuContext.Provider, {
107
+ value: true,
108
+ children: items.map((item) => "element" in item ? /* @__PURE__ */ jsx(Fragment$1, { children: item.element }, item.key) : /* @__PURE__ */ jsx(OverflowRow, { item }, item.key))
175
109
  })
176
- })
177
- }), document.body);
178
- }
179
- var MENU_ITEM_CLASSES = "flex w-full items-center gap-2.5 px-3 py-1.5 text-left text-[13px] hover:bg-(--theme-muted-panel-bg) transition-colors cursor-pointer [&>svg]:h-4 [&>svg]:w-4 [&>svg]:shrink-0";
180
- function MenuItemList({ items, onClose, side }) {
181
- return items.map((item, i) => item.kind === "divider" ? /* @__PURE__ */ jsx("div", {
182
- "aria-hidden": "true",
183
- className: "my-1 h-px bg-(--theme-border)"
184
- }, `divider-${i}`) : /* @__PURE__ */ jsx(MenuRow, {
185
- item,
186
- onClose,
187
- side
188
- }, `${item.label}-${i}`));
110
+ })]
111
+ });
189
112
  }
190
- function MenuRow({ item, onClose, side }) {
191
- const Link = useLink();
192
- const notify = useNotify();
193
- const { list: t } = useStrings();
194
- const [openSub, setOpenSub] = useState(false);
195
- const closeTimer = useRef(void 0);
196
- if (item.kind === "submenu") {
197
- const open = () => {
198
- clearTimeout(closeTimer.current);
199
- setOpenSub(true);
200
- };
201
- const close = () => {
202
- closeTimer.current = setTimeout(() => setOpenSub(false), 160);
203
- };
204
- return /* @__PURE__ */ jsxs("div", {
205
- className: "relative",
206
- onMouseEnter: open,
207
- onMouseLeave: close,
208
- children: [/* @__PURE__ */ jsxs("div", {
209
- className: cx(MENU_ITEM_CLASSES, "justify-between text-(--theme-app)"),
210
- children: [/* @__PURE__ */ jsxs("span", {
211
- className: "flex items-center gap-2.5",
212
- children: [item.icon, item.label]
213
- }), /* @__PURE__ */ jsx(ChevronRightIcon, { className: "h-3 w-3 shrink-0 opacity-60" })]
214
- }), openSub && /* @__PURE__ */ jsx("div", {
215
- className: cx("absolute -top-1 z-10 min-w-44 rounded-lg border border-(--theme-border) bg-(--theme-panel-bg) py-1 shadow-lg", side === "left" ? "right-full" : "left-full"),
216
- children: /* @__PURE__ */ jsx(MenuItemList, {
217
- items: item.items,
218
- onClose,
219
- side
220
- })
221
- })]
222
- });
113
+ var MENU_BUTTON_WIDTH = 44;
114
+ var ITEM_GAP = 4;
115
+ function fitCount(widths, available, gap, menuWidth) {
116
+ if (widths.length === 0) return 0;
117
+ if (widths.reduce((sum, w) => sum + w, 0) + gap * (widths.length - 1) <= available) return widths.length;
118
+ let used = menuWidth;
119
+ let count = 0;
120
+ for (const w of widths) {
121
+ if (used + gap + w > available) break;
122
+ used += gap + w;
123
+ count++;
223
124
  }
224
- if (item.kind === "link") return /* @__PURE__ */ jsxs(Link, {
225
- to: item.to,
226
- onClick: onClose,
227
- className: cx(MENU_ITEM_CLASSES, item.destructive ? "text-red-500" : "text-(--theme-app)"),
228
- children: [item.icon, item.label]
229
- });
230
- return /* @__PURE__ */ jsxs("button", {
231
- type: "button",
232
- disabled: item.disabled,
233
- onClick: () => {
234
- if (item.copy) {
235
- const { text, label } = item.copy;
236
- navigator.clipboard?.writeText(text).then(() => notify.success(t.copied(label)), () => notify.error(t.couldntCopy(label)));
237
- }
238
- item.onSelect?.();
239
- onClose();
240
- },
241
- className: cx(MENU_ITEM_CLASSES, item.destructive ? "text-red-500" : "text-(--theme-app)", item.disabled && "opacity-40 cursor-not-allowed hover:bg-transparent"),
242
- children: [
243
- item.icon,
244
- item.label,
245
- item.selected && /* @__PURE__ */ jsx(CheckIcon, { className: "ml-auto opacity-80" })
246
- ]
247
- });
125
+ return count;
248
126
  }
249
- /** Builds the Copy submenu entries for whichever fields a row has, in stable
250
- * order. The menu itself performs the copy and notifies; `t` is an optional
251
- * host translator for the field labels (falls back to the provider defaults'
252
- * English). */
253
- function copySubmenu({ name, nameLabel, email, username, uid, id, url, extra, t }) {
254
- const items = [
255
- {
256
- label: nameLabel ?? (t ? t("rowContextMenu.labelName") : "name"),
257
- value: name,
258
- icon: /* @__PURE__ */ jsx(CopyIcon, {})
259
- },
260
- {
261
- label: t ? t("rowContextMenu.labelEmail") : "email",
262
- value: email,
263
- icon: /* @__PURE__ */ jsx(MailIcon, {})
264
- },
265
- {
266
- label: t ? t("rowContextMenu.labelUsername") : "username",
267
- value: username,
268
- icon: /* @__PURE__ */ jsx(UserIcon, {})
269
- },
270
- {
271
- label: t ? t("rowContextMenu.labelUid") : "UID",
272
- value: uid === null || uid === void 0 ? null : String(uid),
273
- icon: /* @__PURE__ */ jsx(KeyIcon, {})
274
- },
275
- {
276
- label: t ? t("rowContextMenu.labelId") : "ID",
277
- value: id,
278
- icon: /* @__PURE__ */ jsx(IdCardIcon, {})
279
- },
280
- {
281
- label: t ? t("rowContextMenu.labelUrl") : "URL",
282
- value: url,
283
- icon: /* @__PURE__ */ jsx(LinkIcon, {})
284
- },
285
- ...(extra ?? []).map((e) => ({
286
- label: e.label,
287
- value: e.value,
288
- icon: e.icon ?? /* @__PURE__ */ jsx(CopyIcon, {})
289
- }))
290
- ].filter((f) => f.value).map((f) => ({
291
- kind: "action",
292
- label: t ? t("rowContextMenu.copy", { label: f.label }) : `Copy ${f.label}`,
293
- icon: f.icon,
294
- copy: {
295
- text: f.value,
296
- label: f.label
127
+ /** Measures in a layout effect (before paint) so the inline/overflow split never flashes. */
128
+ function CollapsibleRight({ items }) {
129
+ const wrapRef = useRef(null);
130
+ const itemRefs = useRef({});
131
+ const widthCache = useRef({});
132
+ const [available, setAvailable] = useState(Number.POSITIVE_INFINITY);
133
+ const [count, setCount] = useState(items.length);
134
+ useLayoutEffect(() => {
135
+ const el = wrapRef.current;
136
+ if (!el) return;
137
+ const update = () => setAvailable(el.clientWidth);
138
+ update();
139
+ const observer = new ResizeObserver(update);
140
+ observer.observe(el);
141
+ return () => observer.disconnect();
142
+ }, []);
143
+ useLayoutEffect(() => {
144
+ for (const key in itemRefs.current) {
145
+ const el = itemRefs.current[key];
146
+ if (el) widthCache.current[key] = el.offsetWidth;
297
147
  }
298
- }));
299
- return {
300
- kind: "submenu",
301
- label: t ? t("common.copy") : "Copy",
302
- icon: /* @__PURE__ */ jsx(ClipboardIcon, {}),
303
- items
304
- };
148
+ const next = fitCount(items.map((item) => widthCache.current[item.key] ?? 0), available, ITEM_GAP, MENU_BUTTON_WIDTH);
149
+ setCount((prev) => prev === next ? prev : next);
150
+ });
151
+ const visible = items.slice(0, count);
152
+ const overflow = items.slice(count);
153
+ return /* @__PURE__ */ jsxs("div", {
154
+ ref: wrapRef,
155
+ className: "flex-1 min-w-0 flex items-center justify-end gap-1",
156
+ children: [visible.map((item) => /* @__PURE__ */ jsx("span", {
157
+ ref: (el) => {
158
+ itemRefs.current[item.key] = el;
159
+ },
160
+ className: "inline-flex shrink-0",
161
+ children: /* @__PURE__ */ jsx(ToolbarItemView, { item })
162
+ }, item.key)), overflow.length > 0 && /* @__PURE__ */ jsx(OverflowMenu, { items: overflow })]
163
+ });
305
164
  }
306
- /** Stays visible while active (menu open) so it doesn't vanish behind the overlay. */
307
- function MoreButton({ onOpen, active, className }) {
308
- const { list: t } = useStrings();
309
- return /* @__PURE__ */ jsx("button", {
310
- type: "button",
311
- "aria-label": t.moreActions,
312
- onClick: (e) => {
313
- e.stopPropagation();
314
- const r = e.currentTarget.getBoundingClientRect();
315
- onOpen(r.right, r.bottom);
316
- },
317
- className: cx("rounded p-1 cursor-pointer text-(--theme-muted-text-color) transition-colors hover:bg-(--theme-muted-panel-bg) hover:text-(--theme-app) focus-visible:opacity-100", active ? "opacity-100" : "opacity-0 group-hover:opacity-100", className),
318
- children: /* @__PURE__ */ jsx(MoreIcon, { className: "h-4 w-4" })
165
+ /** Pins a detail-page toolbar just below the sticky breadcrumb strip (top-10 matches the strip's 40px height). */
166
+ var stickyToolbarClasses = "sticky top-10 z-10 bg-(--theme-app-bg)";
167
+ /** Pins a detail-page tabs row under the pinned toolbar (top-22 = 40px strip + 48px toolbar block). */
168
+ var stickyTabsClasses = "sticky top-22 z-10 bg-(--theme-app-bg)";
169
+ /** Pins a detail-page tabs row directly under the breadcrumb strip when the page has no pinned toolbar (top-10 = the strip's height). */
170
+ var stickyTabsUnderHeaderClasses = "sticky top-10 z-10 bg-(--theme-app-bg)";
171
+ /** Action groups left, secondary actions right (collapsing into a `…` menu). */
172
+ function ActionToolbar({ groups, right = [], sticky = false }) {
173
+ const visibleGroups = groups.map((group) => group.filter((a) => !a.hidden)).filter((group) => group.length > 0);
174
+ const visibleRight = right.filter((a) => !a.hidden);
175
+ return /* @__PURE__ */ jsxs("div", {
176
+ className: cx("flex items-center gap-2.5", sticky && cx("sticky top-10 z-10 bg-(--theme-app-bg)", "py-1.5")),
177
+ children: [visibleGroups.length > 0 && /* @__PURE__ */ jsx("div", {
178
+ className: "flex items-center gap-2.5 shrink-0",
179
+ children: visibleGroups.map((group, i) => /* @__PURE__ */ jsxs(Fragment$1, { children: [i > 0 && /* @__PURE__ */ jsx("div", { className: "w-px h-5 bg-(--theme-border)" }), /* @__PURE__ */ jsx("div", {
180
+ className: "flex items-center gap-1",
181
+ children: group.map((item) => /* @__PURE__ */ jsx(ToolbarItemView, { item }, item.key))
182
+ })] }, group[0]?.key ?? i))
183
+ }), visibleRight.length > 0 && /* @__PURE__ */ jsx(CollapsibleRight, { items: visibleRight })]
319
184
  });
320
185
  }
321
186
  //#endregion
@@ -464,6 +329,135 @@ function ListActionsHeader({ view, actionCount = 0, className }) {
464
329
  }
465
330
  ListActionsHeader.isTableHeader = true;
466
331
  //#endregion
332
+ //#region src/list/ListPage.tsx
333
+ /** Negative offsets (admin/org) cancel the shell's pt-4 so rows don't peek through the gap above the pinned header. */
334
+ var SECTION_STICKY_TOP = {
335
+ main: "top-0",
336
+ kubernetes: "top-0",
337
+ admin: "-top-4",
338
+ org: "-top-4"
339
+ };
340
+ /** Whether the shell adds pt-4 padding that the edge-to-edge list must cancel with a negative margin. */
341
+ var SECTION_SHELL_PADDED = {
342
+ main: false,
343
+ kubernetes: false,
344
+ admin: true,
345
+ org: true
346
+ };
347
+ /** Defaults mean rows outside a ListView fully show actions (no view = show everything). */
348
+ var RowActionsContext = createContext({
349
+ show: true,
350
+ always: false,
351
+ columnClasses: []
352
+ });
353
+ function useRowActions(view) {
354
+ return useMemo(() => ({
355
+ show: view?.showActions ?? true,
356
+ always: view?.alwaysShowActions ?? false,
357
+ columnClasses: view?.columnClasses ?? []
358
+ }), [
359
+ view?.showActions,
360
+ view?.alwaysShowActions,
361
+ view?.columnClasses
362
+ ]);
363
+ }
364
+ function ListRowActionsProvider({ view, children }) {
365
+ const rowActions = useRowActions(view);
366
+ return /* @__PURE__ */ jsx(RowActionsContext.Provider, {
367
+ value: rowActions,
368
+ children
369
+ });
370
+ }
371
+ function ListPage({ section = "main", shellPadded, breadcrumbs, title, titleAction, actions, toolbar, view, children }) {
372
+ return /* @__PURE__ */ jsxs("div", {
373
+ className: cx("flex h-full min-h-0 flex-col", (shellPadded ?? SECTION_SHELL_PADDED[section]) && "-m-4"),
374
+ children: [/* @__PURE__ */ jsx(PageHeader, {
375
+ stickyTopClassName: SECTION_STICKY_TOP[section],
376
+ breadcrumbs,
377
+ title,
378
+ titleAction,
379
+ actions,
380
+ toolbar
381
+ }), /* @__PURE__ */ jsx("div", {
382
+ className: "@container min-h-0 flex-1 overflow-auto",
383
+ children: /* @__PURE__ */ jsx(ListRowActionsProvider, {
384
+ view,
385
+ children
386
+ })
387
+ })]
388
+ });
389
+ }
390
+ function ListRow({ href, items, goTo, openMenu, pinnedActions, selected, selectionActive, onRowSelect, leading, className, children }) {
391
+ const { active: menuActive, openMenu: openRowMenu } = useRowMenuActive(openMenu);
392
+ const { show: showActions, always: alwaysShowActions, columnClasses } = useContext(RowActionsContext);
393
+ const styledCells = Children.toArray(children).map((child, i) => {
394
+ const cls = columnClasses[i];
395
+ if (!cls || !isValidElement(child)) return child;
396
+ const el = child;
397
+ return cloneElement(el, { className: cx(el.props.className, cls) });
398
+ });
399
+ const handleRowClick = (e) => {
400
+ if (e.target.closest("a,button,input,label")) return;
401
+ if (selectionActive && onRowSelect) {
402
+ onRowSelect(e);
403
+ return;
404
+ }
405
+ if (href) goTo(href);
406
+ };
407
+ const pinnedLabels = new Set((pinnedActions ?? []).map((a) => a.label));
408
+ const hasOverflowItems = items.some((item) => item.kind !== "divider" && (item.kind === "submenu" || !pinnedLabels.has(item.label)));
409
+ return /* @__PURE__ */ jsxs("tr", {
410
+ className: cx("group", selected ? "bg-blue-500/5" : menuActive ? "theme-hover" : "hover:theme-hover", (href || selectionActive) && "cursor-pointer", className),
411
+ onClick: handleRowClick,
412
+ ...rowContextMenuProps(openRowMenu, items),
413
+ children: [
414
+ leading,
415
+ styledCells,
416
+ showActions && /* @__PURE__ */ jsx(Table.Item, {
417
+ className: "relative py-2.5",
418
+ children: /* @__PURE__ */ jsxs("div", {
419
+ className: "absolute inset-y-0 right-0 flex items-center gap-0.5 pr-4",
420
+ children: [pinnedActions && pinnedActions.length > 0 && /* @__PURE__ */ jsx("span", {
421
+ className: "hidden items-center gap-0.5 @[44rem]:flex",
422
+ children: pinnedActions.map((action) => /* @__PURE__ */ jsx("button", {
423
+ type: "button",
424
+ "aria-label": action.label,
425
+ "data-tooltip-id": "global-tooltip",
426
+ "data-tooltip-content": action.label,
427
+ "data-tooltip-place": "bottom",
428
+ disabled: action.disabled,
429
+ onClick: (e) => {
430
+ e.stopPropagation();
431
+ action.onSelect();
432
+ },
433
+ className: cx("rounded p-1 cursor-pointer text-(--theme-muted-text-color) transition-colors hover:bg-(--theme-muted-panel-bg) hover:text-(--theme-app) disabled:cursor-not-allowed disabled:opacity-20 [&>svg]:h-4 [&>svg]:w-4", alwaysShowActions || menuActive ? "opacity-100" : "opacity-0 group-hover:opacity-100 focus-visible:opacity-100"),
434
+ children: action.icon
435
+ }, action.key))
436
+ }), /* @__PURE__ */ jsx(MoreButton, {
437
+ active: menuActive || alwaysShowActions,
438
+ onOpen: (x, y) => openRowMenu(x, y, items),
439
+ className: hasOverflowItems ? void 0 : "@[44rem]:hidden"
440
+ })]
441
+ })
442
+ })
443
+ ]
444
+ });
445
+ }
446
+ /** Name column min-width is by role, not position, so a leading checkbox doesn't shift it. */
447
+ function ListColumns({ view }) {
448
+ return /* @__PURE__ */ jsxs(Fragment, { children: [view.visibleColumns.map((col, i) => /* @__PURE__ */ jsx(Table.Header, {
449
+ caps: false,
450
+ className: cx(col.headerClassName, view.columnClasses[i]),
451
+ style: col.alwaysVisible ? { minWidth: 200 } : void 0,
452
+ children: col.label
453
+ }, col.key)), /* @__PURE__ */ jsx(ListActionsHeader, { view })] });
454
+ }
455
+ ListColumns.isTableHeader = true;
456
+ /** Stable `goTo` for whole-row navigation (`rowNavProps`, `ListRow`). */
457
+ function useListNavigate() {
458
+ return useNavigation().goTo;
459
+ }
460
+ //#endregion
467
461
  //#region src/list/listView.tsx
468
462
  var PRIORITY_SHOW_CLASS = {
469
463
  low: "hidden @[52rem]:table-cell",
@@ -763,7 +757,7 @@ function AnchoredPopover({ icon, label, active, shortcut, panelClassName, childr
763
757
  }), document.querySelector(".ds-root") ?? document.body)] });
764
758
  }
765
759
  /** Small on/off switch for the Display menu's boolean options. */
766
- function Switch({ checked, onChange, label }) {
760
+ function Switch$1({ checked, onChange, label }) {
767
761
  return /* @__PURE__ */ jsx("button", {
768
762
  type: "button",
769
763
  role: "switch",
@@ -813,7 +807,7 @@ function ListDisplayMenu({ view, label }) {
813
807
  children: [/* @__PURE__ */ jsx("span", {
814
808
  className: "text-[13px] text-(--theme-app)",
815
809
  children: t.columnHeaders
816
- }), /* @__PURE__ */ jsx(Switch, {
810
+ }), /* @__PURE__ */ jsx(Switch$1, {
817
811
  checked: view.showColumnHeaders,
818
812
  onChange: view.toggleColumnHeaders,
819
813
  label: t.showColumnHeaders
@@ -824,7 +818,7 @@ function ListDisplayMenu({ view, label }) {
824
818
  children: [/* @__PURE__ */ jsx("span", {
825
819
  className: "text-[13px] text-(--theme-app)",
826
820
  children: t.showActions
827
- }), /* @__PURE__ */ jsx(Switch, {
821
+ }), /* @__PURE__ */ jsx(Switch$1, {
828
822
  checked: view.showActions,
829
823
  onChange: view.toggleShowActions,
830
824
  label: t.showRowActions
@@ -835,7 +829,7 @@ function ListDisplayMenu({ view, label }) {
835
829
  children: [/* @__PURE__ */ jsx("span", {
836
830
  className: "text-[13px] text-(--theme-muted-text-color)",
837
831
  children: t.alwaysShowActions
838
- }), /* @__PURE__ */ jsx(Switch, {
832
+ }), /* @__PURE__ */ jsx(Switch$1, {
839
833
  checked: view.alwaysShowActions,
840
834
  onChange: view.toggleAlwaysShowActions,
841
835
  label: t.alwaysShowRowActions
@@ -1256,4 +1250,4 @@ function HoverCardSurface({ x, y, widthClassName, relayoutKey, onKeepOpen, onLea
1256
1250
  }), document.querySelector(".ds-root") ?? document.body);
1257
1251
  }
1258
1252
  //#endregion
1259
- export { useRowMenu as A, listTableProps as C, rowContextMenuProps as D, copySubmenu as E, toRowMenuItem as O, listTableFixedProps as S, assembleRowMenu as T, ListSkeleton as _, ListSearchControl as a, RowSelectCheckbox as b, ListFilterMenu as c, DEFAULT_ORDER as d, FAVORITE_ORDER as f, ListError as g, ListActionsHeader as h, ListCount as i, useRowMenuActive as j, toolbarActionToRowAction as k, listControlButtonClasses as l, GroupRow as m, useHoverCard as n, useListSearch as o, useListView as p, ListPager as r, ListDisplayMenu as s, HoverCardSurface as t, isTextEntryContext as u, NAME_MIN_WIDTH_PX as v, MoreButton as w, actionsColumnWidth as x, NameCell as y };
1253
+ export { ToolbarButton as A, NAME_MIN_WIDTH_PX as C, listTableFixedProps as D, actionsColumnWidth as E, stickyTabsUnderHeaderClasses as M, stickyToolbarClasses as N, listTableProps as O, ListSkeleton as S, RowSelectCheckbox as T, ListRowActionsProvider as _, ListSearchControl as a, ListActionsHeader as b, ListFilterMenu as c, DEFAULT_ORDER as d, FAVORITE_ORDER as f, ListRow as g, ListPage as h, ListCount as i, stickyTabsClasses as j, ActionToolbar as k, listControlButtonClasses as l, ListColumns as m, useHoverCard as n, useListSearch as o, useListView as p, ListPager as r, ListDisplayMenu as s, HoverCardSurface as t, isTextEntryContext as u, useListNavigate as v, NameCell as w, ListError as x, GroupRow as y };