@parallelworks/ui 0.4.0 → 0.5.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 +0 -6
- package/dist/chunks/BareModal-DBa84JZO.js +80 -0
- package/dist/chunks/{CodePreviewEditor-B3688Fx4.js → CodePreviewEditor-KwzSyk3-.js} +2 -1
- package/dist/chunks/CreateModal-CXSi9ELO.js +595 -0
- package/dist/chunks/Loader-BXoXTZGc.js +47 -0
- package/dist/chunks/{Monaco-BEj--GTx.js → Monaco-FlGHL7zR.js} +3 -2
- package/dist/chunks/{Provider-uUHgWOWc.js → Provider-wC9G01Ou.js} +22 -1
- package/dist/chunks/RowContextMenu-D3G3DPQk.js +319 -0
- package/dist/chunks/{SwitchToggle-BqD0dE-L.js → SwitchToggle-CkYT1isd.js} +1 -1
- package/dist/chunks/{Table-C5z6qWjM.js → Table-ByziK93x.js} +1 -1
- package/dist/chunks/{editor-CdQbKtYO.js → editor-DbDUZn8w.js} +5 -41
- package/dist/chunks/{list-D5f5Fq-O.js → list-CGbR-ueU.js} +4 -316
- package/dist/chunks/useCssIsDark-DHr05dG2.js +38 -0
- package/dist/components/AddMenu.d.ts +17 -0
- package/dist/components/BareModal.d.ts +17 -0
- package/dist/components/Breadcrumbs.d.ts +28 -0
- package/dist/components/ConfirmModal.d.ts +17 -0
- package/dist/components/CreateModal.d.ts +92 -0
- package/dist/components/Loader.d.ts +2 -1
- package/dist/components/PageHeader.d.ts +69 -0
- package/dist/components/Provider.d.ts +25 -12
- package/dist/components/ghostButton.d.ts +4 -0
- package/dist/editor/index.d.ts +0 -1
- package/dist/editor/index.js +2 -2
- package/dist/file-explorer/index.js +24 -25
- package/dist/form/index.js +4 -4
- package/dist/index.d.ts +8 -1
- package/dist/index.js +237 -10
- package/dist/list/index.js +2 -1
- package/dist/logviewer/index.js +1 -1
- package/dist/styles/theme.css +22 -0
- package/dist/styles.css +1 -1
- package/dist/theme/index.d.ts +1 -1
- package/dist/theme/index.js +6 -0
- package/dist/workflow/index.js +8 -8
- package/package.json +1 -1
- package/dist/chunks/Loader-DQiZ75fa.js +0 -25
- package/dist/chunks/Markdown-iFhq8Gb0.js +0 -101
- package/dist/chunks/Modal-sOUEwebV.js +0 -121
- package/dist/components/Modal.d.ts +0 -26
- /package/dist/{editor → components}/useCssIsDark.d.ts +0 -0
|
@@ -1,324 +1,12 @@
|
|
|
1
|
-
import { CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon,
|
|
2
|
-
import { c as useStrings,
|
|
1
|
+
import { CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, DisplayIcon, ErrorIcon, FilterIcon, SearchIcon, XIcon } from "../icons.js";
|
|
2
|
+
import { c as useStrings, n as useLink } from "./Provider-wC9G01Ou.js";
|
|
3
3
|
import { n as TOOLTIP_ID, r as TooltipInfo } from "./Tooltip-WgbiC_nd.js";
|
|
4
|
-
import { n as Table } from "./Table-
|
|
4
|
+
import { n as Table } from "./Table-ByziK93x.js";
|
|
5
5
|
import cx from "classnames";
|
|
6
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
8
8
|
import { createPortal } from "react-dom";
|
|
9
9
|
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
|
-
};
|
|
20
|
-
return {
|
|
21
|
-
kind: "action",
|
|
22
|
-
label: action.label,
|
|
23
|
-
icon: action.icon,
|
|
24
|
-
onSelect: action.onSelect ?? (() => {}),
|
|
25
|
-
disabled: action.disabled,
|
|
26
|
-
destructive: action.destructive
|
|
27
|
-
};
|
|
28
|
-
}
|
|
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) {
|
|
33
|
-
const Icon = action.icon;
|
|
34
|
-
return {
|
|
35
|
-
key: action.key,
|
|
36
|
-
label: action.label,
|
|
37
|
-
icon: /* @__PURE__ */ jsx(Icon, {}),
|
|
38
|
-
onSelect: action.onClick,
|
|
39
|
-
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
|
-
}, [])
|
|
85
|
-
})
|
|
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
|
-
};
|
|
98
|
-
}
|
|
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
|
-
} };
|
|
105
|
-
}
|
|
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"
|
|
169
|
-
},
|
|
170
|
-
onClick: (e) => e.stopPropagation(),
|
|
171
|
-
children: /* @__PURE__ */ jsx(MenuItemList, {
|
|
172
|
-
items: state.items,
|
|
173
|
-
onClose,
|
|
174
|
-
side
|
|
175
|
-
})
|
|
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}`));
|
|
189
|
-
}
|
|
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
|
-
});
|
|
223
|
-
}
|
|
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
|
-
});
|
|
248
|
-
}
|
|
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
|
|
297
|
-
}
|
|
298
|
-
}));
|
|
299
|
-
return {
|
|
300
|
-
kind: "submenu",
|
|
301
|
-
label: t ? t("common.copy") : "Copy",
|
|
302
|
-
icon: /* @__PURE__ */ jsx(ClipboardIcon, {}),
|
|
303
|
-
items
|
|
304
|
-
};
|
|
305
|
-
}
|
|
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" })
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
//#endregion
|
|
322
10
|
//#region src/list/ListTable.tsx
|
|
323
11
|
var NAME_TEXT = "font-medium text-[13px] leading-tight text-(--theme-app) truncate";
|
|
324
12
|
var SUBTITLE_TEXT = "text-xs leading-tight text-(--theme-muted-text-color) truncate";
|
|
@@ -1256,4 +944,4 @@ function HoverCardSurface({ x, y, widthClassName, relayoutKey, onKeepOpen, onLea
|
|
|
1256
944
|
}), document.querySelector(".ds-root") ?? document.body);
|
|
1257
945
|
}
|
|
1258
946
|
//#endregion
|
|
1259
|
-
export {
|
|
947
|
+
export { listTableProps as C, listTableFixedProps as S, 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, 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, actionsColumnWidth as x, NameCell as y };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
//#region src/components/useCssIsDark.ts
|
|
3
|
+
function readIsDark() {
|
|
4
|
+
const scheme = getComputedStyle(document.documentElement).colorScheme;
|
|
5
|
+
const dark = scheme.includes("dark");
|
|
6
|
+
if (dark !== scheme.includes("light")) return dark;
|
|
7
|
+
if (typeof window.matchMedia !== "function") return false;
|
|
8
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Follows the host's CSS color-scheme, the same signal the log viewer's
|
|
12
|
+
* light-dark() palette uses — no provider API involved. For the rare concrete
|
|
13
|
+
* pick (editor themes, image variants) the tokens cannot express.
|
|
14
|
+
*/
|
|
15
|
+
function useCssIsDark() {
|
|
16
|
+
const [isDark, setIsDark] = useState(readIsDark);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
const update = () => setIsDark(readIsDark());
|
|
19
|
+
const media = typeof window.matchMedia === "function" ? window.matchMedia("(prefers-color-scheme: dark)") : void 0;
|
|
20
|
+
media?.addEventListener("change", update);
|
|
21
|
+
const observer = new MutationObserver(update);
|
|
22
|
+
observer.observe(document.documentElement, {
|
|
23
|
+
attributes: true,
|
|
24
|
+
attributeFilter: [
|
|
25
|
+
"style",
|
|
26
|
+
"class",
|
|
27
|
+
"data-theme"
|
|
28
|
+
]
|
|
29
|
+
});
|
|
30
|
+
return () => {
|
|
31
|
+
media?.removeEventListener("change", update);
|
|
32
|
+
observer.disconnect();
|
|
33
|
+
};
|
|
34
|
+
}, []);
|
|
35
|
+
return isDark;
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { useCssIsDark as t };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export interface HeaderAddMenuItem {
|
|
3
|
+
label: string;
|
|
4
|
+
icon?: ReactNode;
|
|
5
|
+
to?: string;
|
|
6
|
+
onSelect?: () => void;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
separatorAbove?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function AddMenu({ items, label, buttonClassName, zClassName, children, }: {
|
|
11
|
+
items: HeaderAddMenuItem[];
|
|
12
|
+
label: string;
|
|
13
|
+
buttonClassName?: string;
|
|
14
|
+
/** Raise above modals when needed (e.g. z-[9999]). */
|
|
15
|
+
zClassName?: string;
|
|
16
|
+
children: ReactNode;
|
|
17
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { KeyboardEvent as ReactKeyboardEvent, MouseEvent as ReactMouseEvent, ReactNode } from 'react';
|
|
2
|
+
/** Omit when the content supplies its own panel styling. */
|
|
3
|
+
export declare const modalPanelClasses = "rounded-xl border border-(--theme-border) bg-(--theme-app-bg) text-(--theme-app) shadow-2xl";
|
|
4
|
+
export declare function BareModal({ open, onClose, ariaLabel, className, onPanelKeyDown, onPanelMouseDown, align, preventClose, children, }: {
|
|
5
|
+
open: boolean;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
ariaLabel?: string;
|
|
8
|
+
className?: string;
|
|
9
|
+
onPanelKeyDown?: (e: ReactKeyboardEvent<HTMLDivElement>) => void;
|
|
10
|
+
onPanelMouseDown?: (e: ReactMouseEvent<HTMLDivElement>) => void;
|
|
11
|
+
/** Vertical placement of the panel. Defaults to the ds top-anchored position. */
|
|
12
|
+
align?: 'top' | 'center';
|
|
13
|
+
/** Block the accidental-dismiss paths (backdrop click, Escape) so typed
|
|
14
|
+
* input isn't lost. The explicit close controls still work. */
|
|
15
|
+
preventClose?: boolean;
|
|
16
|
+
children: ReactNode;
|
|
17
|
+
}): import("react").ReactPortal | null;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface IBreadcrumbItem {
|
|
3
|
+
label: string;
|
|
4
|
+
href?: string;
|
|
5
|
+
newWindow?: boolean;
|
|
6
|
+
}
|
|
7
|
+
interface BreadcrumbContextType {
|
|
8
|
+
breadcrumbs: IBreadcrumbItem[];
|
|
9
|
+
setBreadcrumbs: (breadcrumbs: IBreadcrumbItem[]) => void;
|
|
10
|
+
rightsideBreadcrumb?: IBreadcrumbItem | null;
|
|
11
|
+
setRightsideBreadcrumb: (breadcrumb: IBreadcrumbItem | null) => void;
|
|
12
|
+
hidden: boolean;
|
|
13
|
+
setHidden: (hidden: boolean) => void;
|
|
14
|
+
/** Null whenever the breadcrumb strip isn't rendered (classic chrome). */
|
|
15
|
+
titleActionSlot: HTMLElement | null;
|
|
16
|
+
setTitleActionSlot: (el: HTMLElement | null) => void;
|
|
17
|
+
rightActionSlot: HTMLElement | null;
|
|
18
|
+
setRightActionSlot: (el: HTMLElement | null) => void;
|
|
19
|
+
}
|
|
20
|
+
export declare const useBreadcrumb: () => BreadcrumbContextType;
|
|
21
|
+
export declare function BreadcrumbProvider({ children, }: {
|
|
22
|
+
children: React.ReactNode;
|
|
23
|
+
}): React.JSX.Element;
|
|
24
|
+
export declare function useSetBreadcrumbs(getBreadcrumbs: (() => IBreadcrumbItem[]) | IBreadcrumbItem[]): void;
|
|
25
|
+
/** For pages that render their own header instead of the breadcrumb strip. */
|
|
26
|
+
export declare function useClearBreadcrumbs(): void;
|
|
27
|
+
export declare function useHideBreadcrumbs(hidden: boolean): void;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export declare function ConfirmModal({ open, onClose, title, description, children, confirmLabel, onConfirm, confirmDisabled, destructive, align, cancelLabel, closeOnConfirm, }: {
|
|
3
|
+
open: boolean;
|
|
4
|
+
onClose: () => void;
|
|
5
|
+
title: string;
|
|
6
|
+
description?: ReactNode;
|
|
7
|
+
children?: ReactNode;
|
|
8
|
+
confirmLabel: string;
|
|
9
|
+
/** An async handler keeps both buttons disabled until it settles. */
|
|
10
|
+
onConfirm: () => void | Promise<unknown>;
|
|
11
|
+
confirmDisabled?: boolean;
|
|
12
|
+
destructive?: boolean;
|
|
13
|
+
align?: 'top' | 'center';
|
|
14
|
+
cancelLabel?: string;
|
|
15
|
+
/** When false, the dialog stays open after a successful confirm. */
|
|
16
|
+
closeOnConfirm?: boolean;
|
|
17
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { InputHTMLAttributes, ReactNode, TextareaHTMLAttributes } from 'react';
|
|
2
|
+
import { type HeaderAddMenuItem } from './AddMenu.js';
|
|
3
|
+
interface CreateModalProps {
|
|
4
|
+
open: boolean;
|
|
5
|
+
onClose: () => void;
|
|
6
|
+
typeIcon?: ReactNode;
|
|
7
|
+
typeLabel: string;
|
|
8
|
+
/** Makes the type chip a dropdown to switch between sibling form types. */
|
|
9
|
+
typeMenu?: HeaderAddMenuItem[];
|
|
10
|
+
namePlaceholder: string;
|
|
11
|
+
hideName?: boolean;
|
|
12
|
+
initialName?: string;
|
|
13
|
+
showDescription?: boolean;
|
|
14
|
+
descriptionPlaceholder?: string;
|
|
15
|
+
submitLabel?: string;
|
|
16
|
+
submittingLabel?: string;
|
|
17
|
+
validateName?: (name: string) => string | null;
|
|
18
|
+
/** Normalize the name as the user types (e.g. strip disallowed characters). */
|
|
19
|
+
sanitizeName?: (name: string) => string;
|
|
20
|
+
nameHint?: (name: string) => ReactNode;
|
|
21
|
+
children?: ReactNode;
|
|
22
|
+
properties?: ReactNode;
|
|
23
|
+
advanced?: ReactNode;
|
|
24
|
+
submitDisabled?: boolean;
|
|
25
|
+
/** Hide the primary submit button and label the dismiss button "Close". */
|
|
26
|
+
hideSubmit?: boolean;
|
|
27
|
+
nameOptional?: boolean;
|
|
28
|
+
onNameChange?: (name: string) => void;
|
|
29
|
+
/** Additional unsaved input in `children`/`properties` that should block an
|
|
30
|
+
* accidental dismiss (name/description/favorite are already tracked). */
|
|
31
|
+
dirty?: boolean;
|
|
32
|
+
/** Show a favorite toggle in the footer. */
|
|
33
|
+
showFavorite?: boolean;
|
|
34
|
+
/** Resolves true to close the modal, false to keep it open (e.g. on error). */
|
|
35
|
+
onSubmit: (name: string, description: string, favorite: boolean) => Promise<boolean>;
|
|
36
|
+
}
|
|
37
|
+
/** Submits on Enter in the name field or ⌘/Ctrl+Enter from anywhere in the panel. */
|
|
38
|
+
export declare function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, namePlaceholder, hideName, initialName, showDescription, descriptionPlaceholder, submitLabel, submittingLabel, validateName, sanitizeName, nameHint, children, properties, advanced, submitDisabled, hideSubmit, nameOptional, onNameChange, dirty, showFavorite, onSubmit, }: CreateModalProps): import("react").JSX.Element;
|
|
39
|
+
/** Labeled single-line input row for the dialog's extra fields. */
|
|
40
|
+
export declare function CreateModalField({ label, tooltip, ...props }: InputHTMLAttributes<HTMLInputElement> & {
|
|
41
|
+
label: string;
|
|
42
|
+
/** Help text surfaced via an info icon next to the label. */
|
|
43
|
+
tooltip?: string;
|
|
44
|
+
}): import("react").JSX.Element;
|
|
45
|
+
/** Labeled multi-line field for credential blobs (JSON keys, PEM keys). */
|
|
46
|
+
export declare function CreateModalTextarea({ label, tooltip, rows, ...props }: TextareaHTMLAttributes<HTMLTextAreaElement> & {
|
|
47
|
+
label: string;
|
|
48
|
+
/** Help text surfaced via an info icon next to the label. */
|
|
49
|
+
tooltip?: string;
|
|
50
|
+
}): import("react").JSX.Element;
|
|
51
|
+
/** Labeled input row with typeahead suggestions; free-form values allowed. */
|
|
52
|
+
export declare function CreateModalAutocompleteField({ label, tooltip, value, onChange, options, placeholder, }: {
|
|
53
|
+
label: string;
|
|
54
|
+
/** Help text surfaced via an info icon next to the label. */
|
|
55
|
+
tooltip?: string;
|
|
56
|
+
value: string;
|
|
57
|
+
onChange: (value: string) => void;
|
|
58
|
+
options: string[];
|
|
59
|
+
placeholder?: string;
|
|
60
|
+
}): import("react").JSX.Element;
|
|
61
|
+
/** Compact property pill with an on/off switch. */
|
|
62
|
+
export declare function CreateModalSwitch({ label, checked, onChange, tooltip, }: {
|
|
63
|
+
label: string;
|
|
64
|
+
checked: boolean;
|
|
65
|
+
onChange: (checked: boolean) => void;
|
|
66
|
+
/** Help text surfaced on hover (explains what the toggle does). */
|
|
67
|
+
tooltip?: string;
|
|
68
|
+
}): import("react").JSX.Element;
|
|
69
|
+
export interface CreateModalSelectOption {
|
|
70
|
+
label: string;
|
|
71
|
+
value: string;
|
|
72
|
+
/** Small leading icon (e.g. the resource's provider icon). */
|
|
73
|
+
icon?: ReactNode;
|
|
74
|
+
/** Category header to group this option under in the dropdown. */
|
|
75
|
+
group?: string;
|
|
76
|
+
}
|
|
77
|
+
/** Compact property pill with a dropdown of options. Options may carry leading
|
|
78
|
+
* icons and group headers (e.g. Clusters vs Kubernetes). */
|
|
79
|
+
export declare function CreateModalSelect({ label, icon, value, options, onChange, disabled, disabledHint, tooltip, }: {
|
|
80
|
+
label: string;
|
|
81
|
+
icon?: ReactNode;
|
|
82
|
+
value?: string;
|
|
83
|
+
options: CreateModalSelectOption[];
|
|
84
|
+
onChange: (value: string) => void;
|
|
85
|
+
/** Gray the pill out (e.g. while a prerequisite pill is unset). */
|
|
86
|
+
disabled?: boolean;
|
|
87
|
+
/** Shown as the pill's tooltip while disabled (e.g. the prerequisite). */
|
|
88
|
+
disabledHint?: string;
|
|
89
|
+
/** Help text surfaced on hover (explains what the pill selects). */
|
|
90
|
+
tooltip?: string;
|
|
91
|
+
}): import("react").JSX.Element;
|
|
92
|
+
export {};
|
|
@@ -5,6 +5,7 @@ interface LoaderProps {
|
|
|
5
5
|
color?: string;
|
|
6
6
|
full?: boolean;
|
|
7
7
|
loaderStyle?: React.CSSProperties;
|
|
8
|
+
variant?: 'spinner' | 'logo';
|
|
8
9
|
}
|
|
9
|
-
export default function Loader({ size, text, textSize, full, color, loaderStyle, }: LoaderProps): import("react").JSX.Element;
|
|
10
|
+
export default function Loader({ size, text, textSize, full, color, loaderStyle, variant, }: LoaderProps): import("react").JSX.Element;
|
|
10
11
|
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { ComponentType, ReactNode } from 'react';
|
|
2
|
+
import { type RowMenuItem } from '../list/RowContextMenu.js';
|
|
3
|
+
import { type HeaderAddMenuItem } from './AddMenu.js';
|
|
4
|
+
export interface PageHeaderBreadcrumb {
|
|
5
|
+
label: string;
|
|
6
|
+
href?: string;
|
|
7
|
+
}
|
|
8
|
+
/** A rendered crumb row entry, as handed to the breadcrumbPin slot. */
|
|
9
|
+
export interface PageHeaderCrumb {
|
|
10
|
+
label: ReactNode;
|
|
11
|
+
href?: string;
|
|
12
|
+
}
|
|
13
|
+
/** With menu, the plus opens a chooser; otherwise navigates to or calls the
|
|
14
|
+
* single action. `word` is the visible verb; `label` stays the full action for
|
|
15
|
+
* the tooltip and screen readers. */
|
|
16
|
+
export declare function HeaderAddButton({ to, onClick, label, word, menu, }: {
|
|
17
|
+
to?: string;
|
|
18
|
+
onClick?: () => void;
|
|
19
|
+
label: string;
|
|
20
|
+
word?: 'add' | 'create';
|
|
21
|
+
menu?: HeaderAddMenuItem[];
|
|
22
|
+
}): import("react").JSX.Element;
|
|
23
|
+
export declare function HeaderActionMenu({ items, label, }: {
|
|
24
|
+
items: HeaderAddMenuItem[];
|
|
25
|
+
label?: string;
|
|
26
|
+
}): import("react").JSX.Element | null;
|
|
27
|
+
/** The header "…" trigger for a RowMenuItem menu — same rendering (submenus,
|
|
28
|
+
* destructive styling) as the list pages' right-click context menus. */
|
|
29
|
+
export declare function HeaderRowMenu({ items, label, }: {
|
|
30
|
+
items: RowMenuItem[];
|
|
31
|
+
label?: string;
|
|
32
|
+
}): import("react").JSX.Element | null;
|
|
33
|
+
export declare function HeaderEditButton({ to, onClick, label, disabled, tooltip, }: {
|
|
34
|
+
to?: string;
|
|
35
|
+
onClick?: () => void;
|
|
36
|
+
label?: string;
|
|
37
|
+
disabled?: boolean;
|
|
38
|
+
tooltip?: string;
|
|
39
|
+
}): import("react").JSX.Element;
|
|
40
|
+
/** Renders nothing when the modern breadcrumb strip isn't mounted (classic chrome or self-headered pages). */
|
|
41
|
+
export declare function BreadcrumbsTitleAction({ children }: {
|
|
42
|
+
children: ReactNode;
|
|
43
|
+
}): import("react").ReactPortal | null;
|
|
44
|
+
export declare function BreadcrumbsRightAction({ children }: {
|
|
45
|
+
children: ReactNode;
|
|
46
|
+
}): import("react").ReactPortal | null;
|
|
47
|
+
export declare function BreadcrumbsActionButton({ icon: Icon, label, to, onClick, disabled, tooltip, }: {
|
|
48
|
+
icon: ComponentType<{
|
|
49
|
+
className?: string;
|
|
50
|
+
}>;
|
|
51
|
+
label: string;
|
|
52
|
+
to?: string;
|
|
53
|
+
onClick?: () => void;
|
|
54
|
+
disabled?: boolean;
|
|
55
|
+
tooltip?: string;
|
|
56
|
+
}): import("react").JSX.Element;
|
|
57
|
+
interface PageHeaderProps {
|
|
58
|
+
breadcrumbs?: PageHeaderBreadcrumb[];
|
|
59
|
+
title?: ReactNode;
|
|
60
|
+
titleAction?: ReactNode;
|
|
61
|
+
actions?: ReactNode;
|
|
62
|
+
toolbar?: ReactNode;
|
|
63
|
+
className?: string;
|
|
64
|
+
sticky?: boolean;
|
|
65
|
+
/** Negative offset (e.g. -top-4) prevents rows peeking above the header when the container has top padding. */
|
|
66
|
+
stickyTopClassName?: string;
|
|
67
|
+
}
|
|
68
|
+
export declare function PageHeader({ breadcrumbs, title, titleAction, actions, toolbar, className, sticky, stickyTopClassName, }: PageHeaderProps): import("react").JSX.Element;
|
|
69
|
+
export {};
|