@parallelworks/ui 0.36.0 → 0.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/dist/ai/agent/permissions.d.ts +2 -2
- package/dist/ai/agent/permissions.js +3 -1
- package/dist/ai/components/ChatSidebar.js +7 -4
- package/dist/ai/components/agent/PermissionPicker.js +1 -0
- package/dist/ai/index.d.ts +1 -1
- package/dist/ai/index.js +2 -2
- package/dist/ai/strings.d.ts +2 -0
- package/dist/ai/strings.js +2 -0
- package/dist/ai/ui/sidebar.d.ts +6 -0
- package/dist/ai/ui/sidebar.js +15 -1
- package/dist/components/Dropdown.d.ts +2 -0
- package/dist/components/Dropdown.js +7 -2
- package/dist/components/Input.d.ts +11 -6
- package/dist/components/Input.js +93 -74
- package/dist/components/Provider.d.ts +31 -0
- package/dist/components/Provider.js +50 -2
- package/dist/editing.d.ts +404 -0
- package/dist/editor/EditorField.js +1 -1
- package/dist/editor/Monaco.d.ts +15 -1
- package/dist/editor/Monaco.js +117 -13
- package/dist/editor/editorModel.d.ts +5 -0
- package/dist/editor/editorModel.js +23 -0
- package/dist/editor/index.d.ts +4 -0
- package/dist/editor/index.js +5 -1
- package/dist/editor/lintContext.d.ts +21 -0
- package/dist/editor/lintContext.js +136 -0
- package/dist/editor/lintOwner.d.ts +2 -0
- package/dist/editor/lintOwner.js +5 -0
- package/dist/editor/nestedText.d.ts +13 -0
- package/dist/editor/nestedText.js +15 -0
- package/dist/editor/reveal.d.ts +4 -0
- package/dist/editor/reveal.js +15 -0
- package/dist/editor/settingsYaml.d.ts +12 -0
- package/dist/editor/settingsYaml.js +41 -0
- package/dist/form/FieldWrapper.js +1 -0
- package/dist/form/Fields/DropdownField.js +1 -1
- package/dist/form/Fields/DurationField.js +1 -1
- package/dist/form/Form.js +65 -24
- package/dist/form/Wizard/WizardContainer.js +66 -6
- package/dist/form/Wizard/WizardStepContent.js +23 -10
- package/dist/form/Wizard/wizardPages.d.ts +12 -0
- package/dist/form/Wizard/wizardPages.js +5 -0
- package/dist/form/fieldRegistry.js +1 -1
- package/dist/form/formEditing.d.ts +25 -0
- package/dist/form/formEditing.js +20 -0
- package/dist/form/index.js +5 -5
- package/dist/graph/InputDialog.d.ts +80 -0
- package/dist/graph/InputDialog.js +2110 -0
- package/dist/graph/InputsEditor.d.ts +12 -0
- package/dist/graph/InputsEditor.js +1594 -0
- package/dist/graph/ProblemsButton.d.ts +9 -0
- package/dist/graph/ProblemsButton.js +61 -0
- package/dist/graph/ShortcutsButton.d.ts +15 -0
- package/dist/graph/ShortcutsButton.js +56 -0
- package/dist/graph/SuggestionInput.d.ts +18 -0
- package/dist/graph/SuggestionInput.js +29 -0
- package/dist/graph/ToolbarPopover.d.ts +8 -0
- package/dist/graph/ToolbarPopover.js +71 -0
- package/dist/graph/editorApi.d.ts +51 -0
- package/dist/graph/editorApi.js +8 -0
- package/dist/graph/editorChrome.d.ts +27 -0
- package/dist/graph/editorChrome.js +75 -0
- package/dist/graph/editorFields.d.ts +180 -0
- package/dist/graph/editorFields.js +394 -0
- package/dist/graph/editorPrimitives.d.ts +60 -0
- package/dist/graph/editorPrimitives.js +150 -0
- package/dist/graph/editorStrings.d.ts +695 -0
- package/dist/graph/editorStrings.js +707 -0
- package/dist/graph/expressionRefs.d.ts +12 -0
- package/dist/graph/fieldProblems.d.ts +32 -0
- package/dist/graph/fieldProblems.js +77 -0
- package/dist/graph/index.d.ts +7 -0
- package/dist/graph/index.js +6 -1
- package/dist/graph/inputLayout.d.ts +25 -0
- package/dist/graph/inputLayout.js +337 -0
- package/dist/graph/inputPages.d.ts +33 -0
- package/dist/graph/inputPages.js +186 -0
- package/dist/graph/inputRefs.d.ts +103 -0
- package/dist/graph/inputRefs.js +196 -0
- package/dist/graph/inputRows.d.ts +43 -0
- package/dist/graph/inputRows.js +65 -0
- package/dist/graph/records.d.ts +3 -0
- package/dist/graph/records.js +7 -0
- package/dist/graph/refusalText.d.ts +6 -0
- package/dist/graph/settingsViews.d.ts +26 -0
- package/dist/graph/settingsViews.js +62 -0
- package/dist/graph/usesInputs.d.ts +17 -0
- package/dist/graph/usesInputs.js +40 -0
- package/dist/icons/bi/BiRedo.js +16 -0
- package/dist/icons/bi/BiUndo.js +16 -0
- package/dist/icons/lu/LuBraces.js +27 -0
- package/dist/icons/lu/LuCode.js +27 -0
- package/dist/icons/lu/LuKeyboard.js +71 -0
- package/dist/icons/lu/LuNetwork.js +63 -0
- package/dist/icons/lu/LuTextCursorInput.js +45 -0
- package/dist/icons.d.ts +2 -2
- package/dist/icons.js +8 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -2
- package/dist/list/ActionToolbar.d.ts +2 -0
- package/dist/list/ActionToolbar.js +18 -5
- package/dist/list/RowContextMenu.d.ts +10 -1
- package/dist/list/RowContextMenu.js +185 -30
- package/dist/styles.css +1 -1
- package/package.json +1 -1
|
@@ -24,15 +24,24 @@ export type RowMenuItem = {
|
|
|
24
24
|
text: string;
|
|
25
25
|
label: string;
|
|
26
26
|
};
|
|
27
|
+
/** More text a menu's search matches, beside the label. */
|
|
28
|
+
keywords?: string;
|
|
27
29
|
} | {
|
|
28
30
|
kind: 'submenu';
|
|
29
31
|
label: string;
|
|
30
32
|
icon?: ReactNode;
|
|
31
33
|
items: RowMenuItem[];
|
|
34
|
+
search?: MenuSearch | undefined;
|
|
32
35
|
} | {
|
|
33
36
|
kind: 'divider';
|
|
34
37
|
};
|
|
35
|
-
|
|
38
|
+
/** A field atop a menu that narrows it, submenus included, to the items it matches. */
|
|
39
|
+
export interface MenuSearch {
|
|
40
|
+
placeholder: string;
|
|
41
|
+
/** Shown when nothing matches. */
|
|
42
|
+
empty: string;
|
|
43
|
+
}
|
|
44
|
+
export type OpenMenu = (x: number, y: number, items: RowMenuItem[], onClose?: () => void, search?: MenuSearch) => void;
|
|
36
45
|
/** The shared shape every entity action hook's per-row actions boil down to.
|
|
37
46
|
* Each hook's bespoke `*RowAction` type is structurally assignable to this. */
|
|
38
47
|
export interface RowAction {
|
|
@@ -14,7 +14,7 @@ import { keyedByContent } from "../components/keys.js";
|
|
|
14
14
|
import { TOOLTIP_ID } from "../components/Tooltip.js";
|
|
15
15
|
import cx from "classnames";
|
|
16
16
|
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
17
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
17
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
18
18
|
import { createPortal } from "react-dom";
|
|
19
19
|
//#region src/list/RowContextMenu.tsx
|
|
20
20
|
/** Converts a single row action to a menu item: a `to` makes it a link, otherwise an action. */
|
|
@@ -74,14 +74,15 @@ function assembleRowMenu({ lead = [], actions, exclude, order, trailing = ["dele
|
|
|
74
74
|
function useRowMenu(zClassName = "z-50") {
|
|
75
75
|
const [menu, setMenu] = useState(null);
|
|
76
76
|
const onCloseRef = useRef(void 0);
|
|
77
|
-
const openMenu = useCallback((x, y, items, onClose) => {
|
|
77
|
+
const openMenu = useCallback((x, y, items, onClose, search) => {
|
|
78
78
|
if (items.length > 0) {
|
|
79
79
|
onCloseRef.current?.();
|
|
80
80
|
onCloseRef.current = onClose;
|
|
81
81
|
setMenu({
|
|
82
82
|
x,
|
|
83
83
|
y,
|
|
84
|
-
items
|
|
84
|
+
items,
|
|
85
|
+
search
|
|
85
86
|
});
|
|
86
87
|
}
|
|
87
88
|
}, []);
|
|
@@ -104,9 +105,12 @@ function useRowMenuActive(openMenu) {
|
|
|
104
105
|
const [active, setActive] = useState(false);
|
|
105
106
|
return {
|
|
106
107
|
active,
|
|
107
|
-
openMenu: useCallback((x, y, items) => {
|
|
108
|
+
openMenu: useCallback((x, y, items, onClose, search) => {
|
|
108
109
|
setActive(true);
|
|
109
|
-
openMenu(x, y, items, () =>
|
|
110
|
+
openMenu(x, y, items, () => {
|
|
111
|
+
setActive(false);
|
|
112
|
+
onClose?.();
|
|
113
|
+
}, search);
|
|
110
114
|
}, [openMenu])
|
|
111
115
|
};
|
|
112
116
|
}
|
|
@@ -120,22 +124,46 @@ function rowContextMenuProps(openMenu, items) {
|
|
|
120
124
|
function RowContextMenu({ state, onClose, zClassName }) {
|
|
121
125
|
const ref = useRef(null);
|
|
122
126
|
const [pos, setPos] = useState(null);
|
|
127
|
+
const opener = useRef(null);
|
|
128
|
+
const wasOpen = useRef(false);
|
|
123
129
|
useLayoutEffect(() => {
|
|
124
|
-
|
|
130
|
+
const menu = ref.current;
|
|
131
|
+
if (!state || !menu) {
|
|
125
132
|
setPos(null);
|
|
133
|
+
wasOpen.current = false;
|
|
126
134
|
return;
|
|
127
135
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
136
|
+
if (!wasOpen.current) {
|
|
137
|
+
opener.current = document.activeElement;
|
|
138
|
+
wasOpen.current = true;
|
|
139
|
+
}
|
|
140
|
+
const { width, height } = menu.getBoundingClientRect();
|
|
141
|
+
const pad = MENU_EDGE;
|
|
142
|
+
const openUp = height > window.innerHeight - pad - state.y && state.y - height - pad >= 0;
|
|
143
|
+
const top = Math.max(pad, openUp ? state.y - height : Math.min(state.y, window.innerHeight - height - pad));
|
|
131
144
|
setPos({
|
|
132
145
|
left: Math.max(pad, Math.min(state.x, window.innerWidth - width - pad)),
|
|
133
|
-
top
|
|
146
|
+
top,
|
|
147
|
+
maxHeight: (openUp ? state.y : window.innerHeight - pad) - top
|
|
134
148
|
});
|
|
135
149
|
}, [state]);
|
|
150
|
+
const placed = pos !== null;
|
|
151
|
+
useEffect(() => {
|
|
152
|
+
const menu = ref.current;
|
|
153
|
+
if (!placed || !menu) return;
|
|
154
|
+
const from = opener.current;
|
|
155
|
+
const target = menu.querySelector(":scope > div > input[type=\"search\"]") ?? (focusVisible(from) ? menu.querySelector(MENU_ITEMS) : null);
|
|
156
|
+
if (!target) return;
|
|
157
|
+
target.focus({ preventScroll: true });
|
|
158
|
+
return () => {
|
|
159
|
+
if (from instanceof HTMLElement && document.activeElement === document.body) from.focus({ preventScroll: true });
|
|
160
|
+
};
|
|
161
|
+
}, [placed, state]);
|
|
136
162
|
useEffect(() => {
|
|
137
163
|
if (!state) return;
|
|
138
|
-
const close = () =>
|
|
164
|
+
const close = (e) => {
|
|
165
|
+
if (!(e.target instanceof Node && ref.current?.contains(e.target))) onClose();
|
|
166
|
+
};
|
|
139
167
|
const onKey = (e) => {
|
|
140
168
|
if (e.key === "Escape") onClose();
|
|
141
169
|
};
|
|
@@ -178,23 +206,116 @@ function RowContextMenu({ state, onClose, zClassName }) {
|
|
|
178
206
|
children: /* @__PURE__ */ jsx("div", {
|
|
179
207
|
ref,
|
|
180
208
|
role: "menu",
|
|
181
|
-
className: "absolute min-w-44 rounded-lg border border-(--theme-border) bg-(--theme-panel-bg) py-1 shadow-lg backdrop-blur-xl",
|
|
209
|
+
className: "absolute flex min-w-44 flex-col rounded-lg border border-(--theme-border) bg-(--theme-panel-bg) py-1 shadow-lg backdrop-blur-xl",
|
|
182
210
|
style: {
|
|
183
211
|
left: pos?.left ?? state.x,
|
|
184
212
|
top: pos?.top ?? state.y,
|
|
213
|
+
maxHeight: state.search ? pos?.maxHeight : void 0,
|
|
185
214
|
visibility: pos ? "visible" : "hidden"
|
|
186
215
|
},
|
|
216
|
+
onKeyDown: (e) => {
|
|
217
|
+
const step = {
|
|
218
|
+
ArrowDown: 1,
|
|
219
|
+
ArrowUp: -1
|
|
220
|
+
}[e.key];
|
|
221
|
+
if (!step || e.defaultPrevented || !ref.current) return;
|
|
222
|
+
e.preventDefault();
|
|
223
|
+
const items = [...ref.current.querySelectorAll(MENU_ITEMS)];
|
|
224
|
+
const at = items.indexOf(document.activeElement);
|
|
225
|
+
focusItem(items[((at < 0 ? step > 0 ? 0 : items.length - 1 : at + step) + items.length) % items.length]);
|
|
226
|
+
},
|
|
187
227
|
children: /* @__PURE__ */ jsx(MenuItemList, {
|
|
188
228
|
items: state.items,
|
|
229
|
+
search: state.search,
|
|
189
230
|
onClose,
|
|
190
231
|
side
|
|
191
232
|
})
|
|
192
233
|
})
|
|
193
234
|
}), document.body);
|
|
194
235
|
}
|
|
236
|
+
var MENU_EDGE = 8;
|
|
237
|
+
function focusItem(item) {
|
|
238
|
+
item?.focus({ preventScroll: true });
|
|
239
|
+
item?.scrollIntoView?.({ block: "nearest" });
|
|
240
|
+
}
|
|
241
|
+
function SubmenuPanel({ side, panelRef, searchable, children }) {
|
|
242
|
+
const [place, setPlace] = useState(null);
|
|
243
|
+
useLayoutEffect(() => {
|
|
244
|
+
const rect = panelRef.current?.getBoundingClientRect();
|
|
245
|
+
if (rect) {
|
|
246
|
+
const bottom = window.innerHeight - MENU_EDGE;
|
|
247
|
+
const lift = Math.max(0, Math.min(rect.bottom - bottom, rect.top - MENU_EDGE));
|
|
248
|
+
setPlace({
|
|
249
|
+
lift,
|
|
250
|
+
maxHeight: bottom - (rect.top - lift)
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}, [panelRef]);
|
|
254
|
+
return /* @__PURE__ */ jsx("div", {
|
|
255
|
+
ref: panelRef,
|
|
256
|
+
className: cx("absolute -top-1 z-10 flex min-w-44 flex-col rounded-lg border border-(--theme-border) bg-(--theme-panel-bg) py-1 shadow-lg", side === "left" ? "right-full" : "left-full"),
|
|
257
|
+
style: place ? {
|
|
258
|
+
...searchable ? { maxHeight: place.maxHeight } : {},
|
|
259
|
+
...place.lift > 0 ? { transform: `translateY(-${place.lift}px)` } : {}
|
|
260
|
+
} : void 0,
|
|
261
|
+
children
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
var MENU_ITEMS = "button:not([disabled]), a[href]";
|
|
265
|
+
function focusVisible(element) {
|
|
266
|
+
try {
|
|
267
|
+
return !!element?.matches(":focus-visible");
|
|
268
|
+
} catch {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
195
272
|
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";
|
|
196
|
-
function
|
|
197
|
-
|
|
273
|
+
function matchesSearch(item, needle) {
|
|
274
|
+
const keywords = item.kind === "action" ? item.keywords ?? "" : "";
|
|
275
|
+
return `${item.label} ${keywords}`.toLowerCase().includes(needle);
|
|
276
|
+
}
|
|
277
|
+
function searchMatches(items, needle) {
|
|
278
|
+
const found = /* @__PURE__ */ new Map();
|
|
279
|
+
const walk = (list) => {
|
|
280
|
+
for (const item of list) if (item.kind === "submenu") walk(item.items);
|
|
281
|
+
else if (item.kind !== "divider" && !found.has(item.label) && matchesSearch(item, needle)) found.set(item.label, item);
|
|
282
|
+
};
|
|
283
|
+
walk(items);
|
|
284
|
+
return [...found.values()];
|
|
285
|
+
}
|
|
286
|
+
function MenuSearchField({ search, query, onQuery, onEnter }) {
|
|
287
|
+
return /* @__PURE__ */ jsx("div", {
|
|
288
|
+
className: "px-2 pt-0.5 pb-1",
|
|
289
|
+
children: /* @__PURE__ */ jsx("input", {
|
|
290
|
+
type: "search",
|
|
291
|
+
"aria-label": search.placeholder,
|
|
292
|
+
placeholder: search.placeholder,
|
|
293
|
+
value: query,
|
|
294
|
+
onChange: (e) => onQuery(e.target.value),
|
|
295
|
+
onKeyDown: (e) => {
|
|
296
|
+
if (e.key === "Enter") {
|
|
297
|
+
e.preventDefault();
|
|
298
|
+
onEnter();
|
|
299
|
+
} else if (e.key === "ArrowDown") {
|
|
300
|
+
e.preventDefault();
|
|
301
|
+
focusItem(e.currentTarget.parentElement?.parentElement?.querySelector(MENU_ITEMS));
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
className: "w-full rounded-md border border-(--theme-border) bg-(--theme-muted-panel-bg) px-2 py-1 text-[13px] text-(--theme-app) placeholder:text-(--theme-muted-text-color) focus:border-(--theme-element) focus:outline-none"
|
|
305
|
+
})
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
function MenuItemList({ items, search, onClose, side }) {
|
|
309
|
+
const [query, setQuery] = useState("");
|
|
310
|
+
const needle = query.trim().toLowerCase();
|
|
311
|
+
const searching = !!search && !!needle;
|
|
312
|
+
const shown = searching ? searchMatches(items, needle) : items;
|
|
313
|
+
const run = useRunItem(onClose);
|
|
314
|
+
const pickFirst = () => {
|
|
315
|
+
const first = shown.find((item) => item.kind === "action" && !item.disabled);
|
|
316
|
+
if (first?.kind === "action") run(first);
|
|
317
|
+
};
|
|
318
|
+
const rows = keyedByContent(shown, (it) => it.kind === "divider" ? "divider" : it.label).map(({ key, item }) => item.kind === "divider" ? /* @__PURE__ */ jsx("div", {
|
|
198
319
|
"aria-hidden": "true",
|
|
199
320
|
className: "my-1 h-px bg-(--theme-border)"
|
|
200
321
|
}, key) : /* @__PURE__ */ jsx(MenuRow, {
|
|
@@ -202,13 +323,30 @@ function MenuItemList({ items, onClose, side }) {
|
|
|
202
323
|
onClose,
|
|
203
324
|
side
|
|
204
325
|
}, key));
|
|
326
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
327
|
+
search && /* @__PURE__ */ jsx(MenuSearchField, {
|
|
328
|
+
search,
|
|
329
|
+
query,
|
|
330
|
+
onQuery: setQuery,
|
|
331
|
+
onEnter: pickFirst
|
|
332
|
+
}),
|
|
333
|
+
searching ? /* @__PURE__ */ jsx("div", {
|
|
334
|
+
className: "max-h-[min(20rem,60vh)] min-h-0 overflow-y-auto",
|
|
335
|
+
children: rows
|
|
336
|
+
}) : rows,
|
|
337
|
+
searching && shown.length === 0 && /* @__PURE__ */ jsx("div", {
|
|
338
|
+
className: "px-3 py-1.5 text-[13px] text-(--theme-muted-text-color)",
|
|
339
|
+
children: search.empty
|
|
340
|
+
})
|
|
341
|
+
] });
|
|
205
342
|
}
|
|
206
343
|
function MenuRow({ item, onClose, side }) {
|
|
207
344
|
const Link = useLink();
|
|
208
|
-
const
|
|
209
|
-
const { list: t } = useStrings();
|
|
345
|
+
const run = useRunItem(onClose);
|
|
210
346
|
const [openSub, setOpenSub] = useState(false);
|
|
211
347
|
const closeTimer = useRef(void 0);
|
|
348
|
+
const panel = useRef(null);
|
|
349
|
+
const hovered = useRef(false);
|
|
212
350
|
if (item.kind === "submenu") {
|
|
213
351
|
const open = () => {
|
|
214
352
|
clearTimeout(closeTimer.current);
|
|
@@ -220,10 +358,19 @@ function MenuRow({ item, onClose, side }) {
|
|
|
220
358
|
return /* @__PURE__ */ jsxs("div", {
|
|
221
359
|
className: "relative",
|
|
222
360
|
role: "none",
|
|
223
|
-
onMouseEnter:
|
|
224
|
-
|
|
361
|
+
onMouseEnter: () => {
|
|
362
|
+
hovered.current = true;
|
|
363
|
+
open();
|
|
364
|
+
},
|
|
365
|
+
onMouseLeave: () => {
|
|
366
|
+
hovered.current = false;
|
|
367
|
+
if (!panel.current?.contains(document.activeElement)) close();
|
|
368
|
+
},
|
|
225
369
|
onFocus: open,
|
|
226
|
-
onBlur:
|
|
370
|
+
onBlur: (e) => {
|
|
371
|
+
const to = e.relatedTarget;
|
|
372
|
+
if (!e.currentTarget.contains(to) && (to !== null || !hovered.current)) close();
|
|
373
|
+
},
|
|
227
374
|
children: [/* @__PURE__ */ jsxs("button", {
|
|
228
375
|
type: "button",
|
|
229
376
|
"aria-expanded": openSub,
|
|
@@ -234,10 +381,13 @@ function MenuRow({ item, onClose, side }) {
|
|
|
234
381
|
className: "flex items-center gap-2.5",
|
|
235
382
|
children: [item.icon, item.label]
|
|
236
383
|
}), /* @__PURE__ */ jsx(LuChevronRight, { className: "h-3 w-3 shrink-0 opacity-60" })]
|
|
237
|
-
}), openSub && /* @__PURE__ */ jsx(
|
|
238
|
-
|
|
384
|
+
}), openSub && /* @__PURE__ */ jsx(SubmenuPanel, {
|
|
385
|
+
side,
|
|
386
|
+
panelRef: panel,
|
|
387
|
+
searchable: !!item.search,
|
|
239
388
|
children: /* @__PURE__ */ jsx(MenuItemList, {
|
|
240
389
|
items: item.items,
|
|
390
|
+
search: item.search,
|
|
241
391
|
onClose,
|
|
242
392
|
side
|
|
243
393
|
})
|
|
@@ -267,15 +417,7 @@ function MenuRow({ item, onClose, side }) {
|
|
|
267
417
|
"data-tooltip-id": TOOLTIP_ID,
|
|
268
418
|
"data-tooltip-content": item.tooltip
|
|
269
419
|
} : {},
|
|
270
|
-
onClick: () =>
|
|
271
|
-
if (item.disabled) return;
|
|
272
|
-
if (item.copy) {
|
|
273
|
-
const { text, label } = item.copy;
|
|
274
|
-
navigator.clipboard?.writeText(text).then(() => notify.success(t.copied(label)), () => notify.error(t.couldntCopy(label)));
|
|
275
|
-
}
|
|
276
|
-
item.onSelect?.();
|
|
277
|
-
onClose();
|
|
278
|
-
},
|
|
420
|
+
onClick: () => run(item),
|
|
279
421
|
className: cx(MENU_ITEM_CLASSES, item.destructive ? "text-red-500" : "text-(--theme-app)", item.disabled && "opacity-40 cursor-not-allowed hover:bg-transparent"),
|
|
280
422
|
children: [
|
|
281
423
|
item.icon,
|
|
@@ -284,6 +426,19 @@ function MenuRow({ item, onClose, side }) {
|
|
|
284
426
|
]
|
|
285
427
|
});
|
|
286
428
|
}
|
|
429
|
+
function useRunItem(onClose) {
|
|
430
|
+
const notify = useNotify();
|
|
431
|
+
const { list: t } = useStrings();
|
|
432
|
+
return (item) => {
|
|
433
|
+
if (item.disabled) return;
|
|
434
|
+
if (item.copy) {
|
|
435
|
+
const { text, label } = item.copy;
|
|
436
|
+
navigator.clipboard?.writeText(text).then(() => notify.success(t.copied(label)), () => notify.error(t.couldntCopy(label)));
|
|
437
|
+
}
|
|
438
|
+
item.onSelect?.();
|
|
439
|
+
onClose();
|
|
440
|
+
};
|
|
441
|
+
}
|
|
287
442
|
/** Returns a builder for the Copy submenu, labeled from the provider's strings. */
|
|
288
443
|
function useCopySubmenu() {
|
|
289
444
|
const { list: t } = useStrings();
|