@parallelworks/ui 0.2.0 → 0.3.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/README.md +5 -0
- package/dist/chunks/CodePreviewEditor-CSPRILMp.js +61 -0
- package/dist/chunks/Indicator-hBSIxOPK.js +39 -0
- package/dist/chunks/Markdown-C5Re2ezs.js +101 -0
- package/dist/chunks/{Indicator-fF8AVbjQ.js → Modal-sOUEwebV.js} +1 -38
- package/dist/chunks/{Monaco-CP_cL9gp.js → Monaco-CC4ak1JG.js} +3 -31
- package/dist/chunks/Provider-DvVvScTQ.js +391 -0
- package/dist/chunks/StatusBadge-DXp4zkSd.js +46 -0
- package/dist/chunks/{Table-DWuRINhu.js → SwitchToggle-2uRw8ZeJ.js} +2 -132
- package/dist/chunks/Table-C5z6qWjM.js +91 -0
- package/dist/chunks/editor-D55tumuZ.js +372 -0
- package/dist/chunks/list-7PyDF1tz.js +465 -0
- package/dist/components/Provider.d.ts +293 -0
- package/dist/editor/index.d.ts +1 -0
- package/dist/editor/index.js +2 -219
- package/dist/editor/useCssIsDark.d.ts +6 -0
- package/dist/file-explorer/FileExplorer.d.ts +23 -0
- package/dist/file-explorer/FileExplorerContext.d.ts +17 -0
- package/dist/file-explorer/FilePreview/CodePreviewEditor.d.ts +6 -0
- package/dist/file-explorer/FilePreview/Inline.d.ts +13 -0
- package/dist/file-explorer/FilePreview/StreamedBadge.d.ts +1 -0
- package/dist/file-explorer/FilePreview/Viewer.d.ts +15 -0
- package/dist/file-explorer/FilePreview/index.d.ts +15 -0
- package/dist/file-explorer/FilePreview/parse.d.ts +24 -0
- package/dist/file-explorer/FilePreview/previewCache.d.ts +26 -0
- package/dist/file-explorer/FilePreview/previewType.d.ts +21 -0
- package/dist/file-explorer/FilePreview/readCappedBytes.d.ts +37 -0
- package/dist/file-explorer/FilePreview/useObjectBlobUrl.d.ts +16 -0
- package/dist/file-explorer/FilePreview/useObjectText.d.ts +23 -0
- package/dist/file-explorer/FilePreview/usePresignedUrl.d.ts +12 -0
- package/dist/file-explorer/FileRow.d.ts +35 -0
- package/dist/file-explorer/FileRowsWindow.d.ts +43 -0
- package/dist/file-explorer/FileTable.d.ts +26 -0
- package/dist/file-explorer/FileTableHeaders.d.ts +13 -0
- package/dist/file-explorer/LoadMoreRow.d.ts +15 -0
- package/dist/file-explorer/TreeView.d.ts +28 -0
- package/dist/file-explorer/helpers.d.ts +47 -0
- package/dist/file-explorer/index.d.ts +10 -0
- package/dist/file-explorer/index.js +5646 -0
- package/dist/file-explorer/lib/batch.d.ts +3 -0
- package/dist/file-explorer/lib/errors.d.ts +3 -0
- package/dist/file-explorer/lib/fileExplorer.d.ts +32 -0
- package/dist/file-explorer/lib/paging.d.ts +93 -0
- package/dist/file-explorer/lib/types.d.ts +92 -0
- package/dist/file-explorer/lib/utils.d.ts +37 -0
- package/dist/file-explorer/toasts.d.ts +9 -0
- package/dist/file-explorer/treeRows.d.ts +35 -0
- package/dist/file-explorer/useNearEnd.d.ts +7 -0
- package/dist/form/index.js +4 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +9 -101
- package/dist/list/ListTable.d.ts +63 -0
- package/dist/list/RowContextMenu.d.ts +113 -0
- package/dist/list/index.d.ts +2 -0
- package/dist/list/index.js +2 -0
- package/dist/logviewer/index.js +1 -1
- package/dist/styles/theme.css +30 -0
- package/dist/styles.css +1 -1
- package/dist/workflow/index.js +3 -2
- package/package.json +20 -3
- package/dist/chunks/Provider-C2VtPnh5.js +0 -151
- package/dist/chunks/workers-Cjfjb8E7.js +0 -119
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
import { CheckIcon, ChevronRightIcon, ClipboardIcon, CopyIcon, ErrorIcon, IdCardIcon, KeyIcon, LinkIcon, MailIcon, MoreIcon, UserIcon } from "../icons.js";
|
|
2
|
+
import { c as useStrings, i as useNotify, n as useLink } from "./Provider-DvVvScTQ.js";
|
|
3
|
+
import { n as Table } from "./Table-C5z6qWjM.js";
|
|
4
|
+
import cx from "classnames";
|
|
5
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
7
|
+
import { createPortal } from "react-dom";
|
|
8
|
+
//#region src/list/RowContextMenu.tsx
|
|
9
|
+
/** Converts a single row action to a menu item: a `to` makes it a link, otherwise an action. */
|
|
10
|
+
function toRowMenuItem(action) {
|
|
11
|
+
if (action.to) return {
|
|
12
|
+
kind: "link",
|
|
13
|
+
label: action.label,
|
|
14
|
+
icon: action.icon,
|
|
15
|
+
to: action.to,
|
|
16
|
+
destructive: action.destructive
|
|
17
|
+
};
|
|
18
|
+
return {
|
|
19
|
+
kind: "action",
|
|
20
|
+
label: action.label,
|
|
21
|
+
icon: action.icon,
|
|
22
|
+
onSelect: action.onSelect ?? (() => {}),
|
|
23
|
+
disabled: action.disabled,
|
|
24
|
+
destructive: action.destructive
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** Bridges a toolbar action (icon component, `onClick`/`href`) onto the shared
|
|
28
|
+
* `RowAction` shape. Typed structurally so any toolbar action shape matching
|
|
29
|
+
* these fields is assignable. */
|
|
30
|
+
function toolbarActionToRowAction(action) {
|
|
31
|
+
const Icon = action.icon;
|
|
32
|
+
return {
|
|
33
|
+
key: action.key,
|
|
34
|
+
label: action.label,
|
|
35
|
+
icon: /* @__PURE__ */ jsx(Icon, {}),
|
|
36
|
+
onSelect: action.onClick,
|
|
37
|
+
to: action.href,
|
|
38
|
+
disabled: action.disabled,
|
|
39
|
+
destructive: action.key === "delete" || action.key === "destroy"
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** Assembles a row's context menu in the shared order: leading link(s) → middle
|
|
43
|
+
* actions → extra items → Copy submenu → trailing (destructive) actions last. */
|
|
44
|
+
function assembleRowMenu({ lead = [], actions, exclude, order, trailing = ["delete"], extra = [], copy }) {
|
|
45
|
+
const trailingSet = new Set(trailing);
|
|
46
|
+
const excludeSet = new Set(exclude);
|
|
47
|
+
let middle = actions.filter((a) => !trailingSet.has(a.key) && !excludeSet.has(a.key));
|
|
48
|
+
if (order) {
|
|
49
|
+
const byKey = new Map(middle.map((a) => [a.key, a]));
|
|
50
|
+
middle = order.map((key) => byKey.get(key)).filter((a) => a !== void 0);
|
|
51
|
+
}
|
|
52
|
+
const trailingItems = trailing.map((key) => actions.find((a) => a.key === key)).filter((a) => a !== void 0);
|
|
53
|
+
return [
|
|
54
|
+
...lead,
|
|
55
|
+
...middle.map(toRowMenuItem),
|
|
56
|
+
...extra,
|
|
57
|
+
...copy ? [copy] : [],
|
|
58
|
+
...trailingItems.map(toRowMenuItem)
|
|
59
|
+
];
|
|
60
|
+
}
|
|
61
|
+
function useRowMenu() {
|
|
62
|
+
const [menu, setMenu] = useState(null);
|
|
63
|
+
const onCloseRef = useRef(void 0);
|
|
64
|
+
return {
|
|
65
|
+
openMenu: useCallback((x, y, items, onClose) => {
|
|
66
|
+
if (items.length > 0) {
|
|
67
|
+
onCloseRef.current?.();
|
|
68
|
+
onCloseRef.current = onClose;
|
|
69
|
+
setMenu({
|
|
70
|
+
x,
|
|
71
|
+
y,
|
|
72
|
+
items
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}, []),
|
|
76
|
+
contextMenu: /* @__PURE__ */ jsx(RowContextMenu, {
|
|
77
|
+
state: menu,
|
|
78
|
+
onClose: useCallback(() => {
|
|
79
|
+
onCloseRef.current?.();
|
|
80
|
+
onCloseRef.current = void 0;
|
|
81
|
+
setMenu(null);
|
|
82
|
+
}, [])
|
|
83
|
+
})
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/** The full-screen menu overlay drops CSS :hover on the triggering row; wrap openMenu here to substitute a JS-driven `active` highlight. */
|
|
87
|
+
function useRowMenuActive(openMenu) {
|
|
88
|
+
const [active, setActive] = useState(false);
|
|
89
|
+
return {
|
|
90
|
+
active,
|
|
91
|
+
openMenu: useCallback((x, y, items) => {
|
|
92
|
+
setActive(true);
|
|
93
|
+
openMenu(x, y, items, () => setActive(false));
|
|
94
|
+
}, [openMenu])
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function rowContextMenuProps(openMenu, items) {
|
|
98
|
+
return { onContextMenu: (e) => {
|
|
99
|
+
e.preventDefault();
|
|
100
|
+
if (items.length > 0) e.stopPropagation();
|
|
101
|
+
openMenu(e.clientX, e.clientY, items);
|
|
102
|
+
} };
|
|
103
|
+
}
|
|
104
|
+
function RowContextMenu({ state, onClose }) {
|
|
105
|
+
const ref = useRef(null);
|
|
106
|
+
const [pos, setPos] = useState(null);
|
|
107
|
+
useLayoutEffect(() => {
|
|
108
|
+
if (!state || !ref.current) {
|
|
109
|
+
setPos(null);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const { width, height } = ref.current.getBoundingClientRect();
|
|
113
|
+
const pad = 8;
|
|
114
|
+
const top = height > window.innerHeight - pad - state.y && state.y - height - pad >= 0 ? state.y - height : Math.min(state.y, window.innerHeight - height - pad);
|
|
115
|
+
setPos({
|
|
116
|
+
left: Math.max(pad, Math.min(state.x, window.innerWidth - width - pad)),
|
|
117
|
+
top: Math.max(pad, top)
|
|
118
|
+
});
|
|
119
|
+
}, [state]);
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
if (!state) return;
|
|
122
|
+
const close = () => onClose();
|
|
123
|
+
const onKey = (e) => {
|
|
124
|
+
if (e.key === "Escape") onClose();
|
|
125
|
+
};
|
|
126
|
+
window.addEventListener("scroll", close, true);
|
|
127
|
+
window.addEventListener("resize", close);
|
|
128
|
+
window.addEventListener("keydown", onKey);
|
|
129
|
+
return () => {
|
|
130
|
+
window.removeEventListener("scroll", close, true);
|
|
131
|
+
window.removeEventListener("resize", close);
|
|
132
|
+
window.removeEventListener("keydown", onKey);
|
|
133
|
+
};
|
|
134
|
+
}, [state, onClose]);
|
|
135
|
+
if (!state) return null;
|
|
136
|
+
const side = (pos?.left ?? state.x) > window.innerWidth * .6 ? "left" : "right";
|
|
137
|
+
return createPortal(/* @__PURE__ */ jsx("div", {
|
|
138
|
+
className: "fixed inset-0 z-50",
|
|
139
|
+
onClick: onClose,
|
|
140
|
+
onContextMenu: (e) => {
|
|
141
|
+
e.preventDefault();
|
|
142
|
+
if (ref.current?.contains(e.target)) {
|
|
143
|
+
onClose();
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const { clientX, clientY } = e;
|
|
147
|
+
const overlay = e.currentTarget;
|
|
148
|
+
overlay.style.pointerEvents = "none";
|
|
149
|
+
const under = document.elementFromPoint(clientX, clientY);
|
|
150
|
+
overlay.style.pointerEvents = "";
|
|
151
|
+
onClose();
|
|
152
|
+
under?.dispatchEvent(new MouseEvent("contextmenu", {
|
|
153
|
+
bubbles: true,
|
|
154
|
+
cancelable: true,
|
|
155
|
+
clientX,
|
|
156
|
+
clientY
|
|
157
|
+
}));
|
|
158
|
+
},
|
|
159
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
160
|
+
ref,
|
|
161
|
+
role: "menu",
|
|
162
|
+
className: "absolute min-w-44 rounded-lg border border-(--theme-border) bg-(--theme-panel-bg) py-1 shadow-lg backdrop-blur-xl",
|
|
163
|
+
style: {
|
|
164
|
+
left: pos?.left ?? state.x,
|
|
165
|
+
top: pos?.top ?? state.y,
|
|
166
|
+
visibility: pos ? "visible" : "hidden"
|
|
167
|
+
},
|
|
168
|
+
onClick: (e) => e.stopPropagation(),
|
|
169
|
+
children: /* @__PURE__ */ jsx(MenuItemList, {
|
|
170
|
+
items: state.items,
|
|
171
|
+
onClose,
|
|
172
|
+
side
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
}), document.body);
|
|
176
|
+
}
|
|
177
|
+
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";
|
|
178
|
+
function MenuItemList({ items, onClose, side }) {
|
|
179
|
+
return items.map((item, i) => item.kind === "divider" ? /* @__PURE__ */ jsx("div", {
|
|
180
|
+
"aria-hidden": "true",
|
|
181
|
+
className: "my-1 h-px bg-(--theme-border)"
|
|
182
|
+
}, `divider-${i}`) : /* @__PURE__ */ jsx(MenuRow, {
|
|
183
|
+
item,
|
|
184
|
+
onClose,
|
|
185
|
+
side
|
|
186
|
+
}, `${item.label}-${i}`));
|
|
187
|
+
}
|
|
188
|
+
function MenuRow({ item, onClose, side }) {
|
|
189
|
+
const Link = useLink();
|
|
190
|
+
const notify = useNotify();
|
|
191
|
+
const { list: t } = useStrings();
|
|
192
|
+
const [openSub, setOpenSub] = useState(false);
|
|
193
|
+
const closeTimer = useRef(void 0);
|
|
194
|
+
if (item.kind === "submenu") {
|
|
195
|
+
const open = () => {
|
|
196
|
+
clearTimeout(closeTimer.current);
|
|
197
|
+
setOpenSub(true);
|
|
198
|
+
};
|
|
199
|
+
const close = () => {
|
|
200
|
+
closeTimer.current = setTimeout(() => setOpenSub(false), 160);
|
|
201
|
+
};
|
|
202
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
203
|
+
className: "relative",
|
|
204
|
+
onMouseEnter: open,
|
|
205
|
+
onMouseLeave: close,
|
|
206
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
207
|
+
className: cx(MENU_ITEM_CLASSES, "justify-between text-(--theme-app)"),
|
|
208
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
209
|
+
className: "flex items-center gap-2.5",
|
|
210
|
+
children: [item.icon, item.label]
|
|
211
|
+
}), /* @__PURE__ */ jsx(ChevronRightIcon, { className: "h-3 w-3 shrink-0 opacity-60" })]
|
|
212
|
+
}), openSub && /* @__PURE__ */ jsx("div", {
|
|
213
|
+
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"),
|
|
214
|
+
children: /* @__PURE__ */ jsx(MenuItemList, {
|
|
215
|
+
items: item.items,
|
|
216
|
+
onClose,
|
|
217
|
+
side
|
|
218
|
+
})
|
|
219
|
+
})]
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
if (item.kind === "link") return /* @__PURE__ */ jsxs(Link, {
|
|
223
|
+
to: item.to,
|
|
224
|
+
onClick: onClose,
|
|
225
|
+
className: cx(MENU_ITEM_CLASSES, item.destructive ? "text-red-500" : "text-(--theme-app)"),
|
|
226
|
+
children: [item.icon, item.label]
|
|
227
|
+
});
|
|
228
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
229
|
+
type: "button",
|
|
230
|
+
disabled: item.disabled,
|
|
231
|
+
onClick: () => {
|
|
232
|
+
if (item.copy) {
|
|
233
|
+
const { text, label } = item.copy;
|
|
234
|
+
navigator.clipboard?.writeText(text).then(() => notify.success(t.copied(label)), () => notify.error(t.couldntCopy(label)));
|
|
235
|
+
}
|
|
236
|
+
item.onSelect?.();
|
|
237
|
+
onClose();
|
|
238
|
+
},
|
|
239
|
+
className: cx(MENU_ITEM_CLASSES, item.destructive ? "text-red-500" : "text-(--theme-app)", item.disabled && "opacity-40 cursor-not-allowed hover:bg-transparent"),
|
|
240
|
+
children: [
|
|
241
|
+
item.icon,
|
|
242
|
+
item.label,
|
|
243
|
+
item.selected && /* @__PURE__ */ jsx(CheckIcon, { className: "ml-auto opacity-80" })
|
|
244
|
+
]
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
/** Builds the Copy submenu entries for whichever fields a row has, in stable
|
|
248
|
+
* order. The menu itself performs the copy and notifies; `t` is an optional
|
|
249
|
+
* host translator for the field labels (falls back to the provider defaults'
|
|
250
|
+
* English). */
|
|
251
|
+
function copySubmenu({ name, nameLabel, email, username, uid, id, url, extra, t }) {
|
|
252
|
+
const items = [
|
|
253
|
+
{
|
|
254
|
+
label: nameLabel ?? (t ? t("rowContextMenu.labelName") : "name"),
|
|
255
|
+
value: name,
|
|
256
|
+
icon: /* @__PURE__ */ jsx(CopyIcon, {})
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
label: t ? t("rowContextMenu.labelEmail") : "email",
|
|
260
|
+
value: email,
|
|
261
|
+
icon: /* @__PURE__ */ jsx(MailIcon, {})
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
label: t ? t("rowContextMenu.labelUsername") : "username",
|
|
265
|
+
value: username,
|
|
266
|
+
icon: /* @__PURE__ */ jsx(UserIcon, {})
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
label: t ? t("rowContextMenu.labelUid") : "UID",
|
|
270
|
+
value: uid === null || uid === void 0 ? null : String(uid),
|
|
271
|
+
icon: /* @__PURE__ */ jsx(KeyIcon, {})
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
label: t ? t("rowContextMenu.labelId") : "ID",
|
|
275
|
+
value: id,
|
|
276
|
+
icon: /* @__PURE__ */ jsx(IdCardIcon, {})
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
label: t ? t("rowContextMenu.labelUrl") : "URL",
|
|
280
|
+
value: url,
|
|
281
|
+
icon: /* @__PURE__ */ jsx(LinkIcon, {})
|
|
282
|
+
},
|
|
283
|
+
...(extra ?? []).map((e) => ({
|
|
284
|
+
label: e.label,
|
|
285
|
+
value: e.value,
|
|
286
|
+
icon: e.icon ?? /* @__PURE__ */ jsx(CopyIcon, {})
|
|
287
|
+
}))
|
|
288
|
+
].filter((f) => f.value).map((f) => ({
|
|
289
|
+
kind: "action",
|
|
290
|
+
label: t ? t("rowContextMenu.copy", { label: f.label }) : `Copy ${f.label}`,
|
|
291
|
+
icon: f.icon,
|
|
292
|
+
copy: {
|
|
293
|
+
text: f.value,
|
|
294
|
+
label: f.label
|
|
295
|
+
}
|
|
296
|
+
}));
|
|
297
|
+
return {
|
|
298
|
+
kind: "submenu",
|
|
299
|
+
label: t ? t("common.copy") : "Copy",
|
|
300
|
+
icon: /* @__PURE__ */ jsx(ClipboardIcon, {}),
|
|
301
|
+
items
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
/** Stays visible while active (menu open) so it doesn't vanish behind the overlay. */
|
|
305
|
+
function MoreButton({ onOpen, active, className }) {
|
|
306
|
+
const { list: t } = useStrings();
|
|
307
|
+
return /* @__PURE__ */ jsx("button", {
|
|
308
|
+
type: "button",
|
|
309
|
+
"aria-label": t.moreActions,
|
|
310
|
+
onClick: (e) => {
|
|
311
|
+
e.stopPropagation();
|
|
312
|
+
const r = e.currentTarget.getBoundingClientRect();
|
|
313
|
+
onOpen(r.right, r.bottom);
|
|
314
|
+
},
|
|
315
|
+
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),
|
|
316
|
+
children: /* @__PURE__ */ jsx(MoreIcon, { className: "h-4 w-4" })
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
//#endregion
|
|
320
|
+
//#region src/list/ListTable.tsx
|
|
321
|
+
var NAME_TEXT = "font-medium text-[13px] leading-tight text-(--theme-app) truncate";
|
|
322
|
+
var SUBTITLE_TEXT = "text-xs leading-tight text-(--theme-muted-text-color) truncate";
|
|
323
|
+
/** table-auto + horizontal scroll; the name column has a min-width floor so it can't be crushed. */
|
|
324
|
+
var listTableProps = {
|
|
325
|
+
borderless: true,
|
|
326
|
+
panel: false,
|
|
327
|
+
rounded: false,
|
|
328
|
+
theadClassName: "text-(--theme-muted-text-color)",
|
|
329
|
+
tbodyClassName: "divide-y border-b border-(--theme-border) divide-(--theme-border)",
|
|
330
|
+
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"
|
|
331
|
+
};
|
|
332
|
+
/** Widths come only from the header row, so a windowed body cannot shift the
|
|
333
|
+
* columns as different content scrolls into view. */
|
|
334
|
+
var listTableFixedProps = {
|
|
335
|
+
...listTableProps,
|
|
336
|
+
className: listTableProps.className.replace("table-auto", "table-fixed")
|
|
337
|
+
};
|
|
338
|
+
function NameCell({ icon, name, subtitle, href }) {
|
|
339
|
+
const Link = useLink();
|
|
340
|
+
const inner = /* @__PURE__ */ jsxs(Fragment, { children: [icon, /* @__PURE__ */ jsxs("div", {
|
|
341
|
+
className: "min-w-0",
|
|
342
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
343
|
+
className: NAME_TEXT,
|
|
344
|
+
children: name
|
|
345
|
+
}), subtitle && /* @__PURE__ */ jsx("div", {
|
|
346
|
+
className: SUBTITLE_TEXT,
|
|
347
|
+
children: subtitle
|
|
348
|
+
})]
|
|
349
|
+
})] });
|
|
350
|
+
return /* @__PURE__ */ jsx(Table.Item, {
|
|
351
|
+
className: "py-2.5",
|
|
352
|
+
children: href ? /* @__PURE__ */ jsx(Link, {
|
|
353
|
+
to: href,
|
|
354
|
+
className: "flex items-center gap-2.5 min-w-0 cursor-pointer",
|
|
355
|
+
children: inner
|
|
356
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
357
|
+
className: "flex items-center gap-2.5 min-w-0",
|
|
358
|
+
children: inner
|
|
359
|
+
})
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
function GroupRow({ label, count, colSpan }) {
|
|
363
|
+
return /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsxs("td", {
|
|
364
|
+
colSpan,
|
|
365
|
+
className: "bg-(--theme-muted-panel-bg) py-1.5 text-xs font-medium",
|
|
366
|
+
children: [
|
|
367
|
+
/* @__PURE__ */ jsx("span", {
|
|
368
|
+
className: "text-(--theme-app)",
|
|
369
|
+
children: label
|
|
370
|
+
}),
|
|
371
|
+
/* @__PURE__ */ jsx("span", {
|
|
372
|
+
className: "mx-1.5 opacity-50",
|
|
373
|
+
children: "·"
|
|
374
|
+
}),
|
|
375
|
+
/* @__PURE__ */ jsx("span", {
|
|
376
|
+
className: "text-(--theme-muted-text-color)",
|
|
377
|
+
children: count
|
|
378
|
+
})
|
|
379
|
+
]
|
|
380
|
+
}) });
|
|
381
|
+
}
|
|
382
|
+
function ListError({ error, noun = "data" }) {
|
|
383
|
+
const { list: t } = useStrings();
|
|
384
|
+
const message = error && typeof error === "object" && "message" in error ? String(error.message) : void 0;
|
|
385
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
386
|
+
className: "flex flex-col items-center justify-center gap-3 py-20 text-center",
|
|
387
|
+
children: [/* @__PURE__ */ jsx(ErrorIcon, { className: "h-10 w-10 text-red-400 opacity-80" }), /* @__PURE__ */ jsxs("div", {
|
|
388
|
+
className: "space-y-1",
|
|
389
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
390
|
+
className: "text-sm font-medium text-(--theme-app)",
|
|
391
|
+
children: t.couldNotLoad(noun)
|
|
392
|
+
}), message && /* @__PURE__ */ jsx("p", {
|
|
393
|
+
className: "max-w-md text-xs text-(--theme-muted-text-color)",
|
|
394
|
+
children: message
|
|
395
|
+
})]
|
|
396
|
+
})]
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
function ListSkeleton({ rows = 6 }) {
|
|
400
|
+
return /* @__PURE__ */ jsx("div", {
|
|
401
|
+
className: "divide-y border-b border-(--theme-border) divide-(--theme-border)",
|
|
402
|
+
children: Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsxs("div", {
|
|
403
|
+
className: "flex items-center gap-3 px-4 py-2.5 animate-pulse",
|
|
404
|
+
children: [
|
|
405
|
+
/* @__PURE__ */ jsx("div", { className: "h-5 w-5 rounded bg-(--theme-muted-panel-bg)" }),
|
|
406
|
+
/* @__PURE__ */ jsxs("div", {
|
|
407
|
+
className: "space-y-1.5",
|
|
408
|
+
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)" })]
|
|
409
|
+
}),
|
|
410
|
+
/* @__PURE__ */ jsx("div", { className: "ml-auto h-3 w-20 rounded bg-(--theme-muted-panel-bg)" })
|
|
411
|
+
]
|
|
412
|
+
}, i))
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
/** Prevents the name column from being crushed under table-auto layout during horizontal overflow. */
|
|
416
|
+
var NAME_MIN_WIDTH_PX = 200;
|
|
417
|
+
/** Trailing cell geometry: icon-button size + gap + padding, sized to fit exactly its pinned buttons. */
|
|
418
|
+
var ACTION_BUTTON_PX = 24;
|
|
419
|
+
var ACTION_GAP_PX = 2;
|
|
420
|
+
var ACTIONS_CELL_PAD_PX = 28;
|
|
421
|
+
/** Width the trailing actions column reserves for `count` always-visible
|
|
422
|
+
* buttons plus the hover `⋯` trigger. */
|
|
423
|
+
function actionsColumnWidth(count) {
|
|
424
|
+
const buttons = count + 1;
|
|
425
|
+
return buttons * ACTION_BUTTON_PX + (buttons - 1) * ACTION_GAP_PX + ACTIONS_CELL_PAD_PX;
|
|
426
|
+
}
|
|
427
|
+
/** Shared leading selection checkbox for multi-select rows: stops the row-click
|
|
428
|
+
* and suppresses shift-click text selection. Wrap in <Table.Item> for `leading`. */
|
|
429
|
+
function RowSelectCheckbox({ checked, label, onToggle }) {
|
|
430
|
+
return /* @__PURE__ */ jsx("input", {
|
|
431
|
+
type: "checkbox",
|
|
432
|
+
"aria-label": label,
|
|
433
|
+
checked,
|
|
434
|
+
onChange: () => {},
|
|
435
|
+
onClick: (e) => {
|
|
436
|
+
e.stopPropagation();
|
|
437
|
+
onToggle(e);
|
|
438
|
+
},
|
|
439
|
+
onMouseDown: (e) => {
|
|
440
|
+
if (e.shiftKey) e.preventDefault();
|
|
441
|
+
},
|
|
442
|
+
className: "h-4 w-4 shrink-0 rounded border-(--theme-border) text-blue-500 focus:ring-blue-500 focus:ring-offset-0 cursor-pointer"
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
/** Sizes to fit pinned buttons + ⋯; below ~44rem only ⋯ remains. Pass a view's
|
|
446
|
+
* chrome (structurally: showActions + actionsWidth) or actionCount. */
|
|
447
|
+
function ListActionsHeader({ view, actionCount = 0, className }) {
|
|
448
|
+
if (view && !view.showActions) return null;
|
|
449
|
+
const width = view ? view.actionsWidth : actionsColumnWidth(actionCount);
|
|
450
|
+
return /* @__PURE__ */ jsx(Table.Header, {
|
|
451
|
+
caps: false,
|
|
452
|
+
style: {
|
|
453
|
+
width,
|
|
454
|
+
minWidth: width
|
|
455
|
+
},
|
|
456
|
+
className: cx("py-2 text-right @max-[43rem]:w-[52px]! @max-[43rem]:min-w-[52px]!", className),
|
|
457
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
458
|
+
className: "sr-only",
|
|
459
|
+
children: "Actions"
|
|
460
|
+
})
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
ListActionsHeader.isTableHeader = true;
|
|
464
|
+
//#endregion
|
|
465
|
+
export { useRowMenu as _, NAME_MIN_WIDTH_PX as a, actionsColumnWidth as c, MoreButton as d, assembleRowMenu as f, toolbarActionToRowAction as g, toRowMenuItem as h, ListSkeleton as i, listTableFixedProps as l, rowContextMenuProps as m, ListActionsHeader as n, NameCell as o, copySubmenu as p, ListError as r, RowSelectCheckbox as s, GroupRow as t, listTableProps as u, useRowMenuActive as v };
|