@parallelworks/ui 0.77.0 → 0.79.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 +42 -6
- package/dist/access/AccessDrawer.d.ts +49 -7
- package/dist/access/AccessDrawer.js +243 -38
- package/dist/access/AccessList.d.ts +19 -1
- package/dist/access/AccessList.js +220 -9
- package/dist/access/GrantView.d.ts +3 -1
- package/dist/access/GrantView.js +17 -4
- package/dist/access/PrincipalPicker.js +5 -5
- package/dist/access/focus.js +1 -1
- package/dist/access/index.d.ts +2 -2
- package/dist/access/index.js +2 -2
- package/dist/access/strings.d.ts +25 -1
- package/dist/access/strings.js +27 -3
- package/dist/access/types.d.ts +26 -0
- package/dist/ai/components/AllocationSelector.js +1 -1
- package/dist/ai/components/AttachmentManager.js +5 -5
- package/dist/ai/components/AttachmentUpload.js +2 -2
- package/dist/ai/components/BranchNavigator.js +1 -1
- package/dist/ai/components/ChatActivity.js +1 -1
- package/dist/ai/components/ChatEmptyState.js +3 -3
- package/dist/ai/components/ChatInput.js +3 -5
- package/dist/ai/components/ChatLayout.js +1 -1
- package/dist/ai/components/ChatMessage.js +1 -1
- package/dist/ai/components/ChatMessageList.js +23 -8
- package/dist/ai/components/ChatSidebar.js +3 -3
- package/dist/ai/components/ChatThread.js +4 -4
- package/dist/ai/components/ComposerAddMenu.js +9 -20
- package/dist/ai/components/ModelSelector.js +3 -3
- package/dist/ai/components/PastedText.js +5 -5
- package/dist/ai/components/SessionView.js +1 -1
- package/dist/ai/components/ShareDialog.js +3 -3
- package/dist/ai/components/SlashMenu.d.ts +31 -4
- package/dist/ai/components/SlashMenu.js +36 -63
- package/dist/ai/components/agent/ActivityLine.js +1 -1
- package/dist/ai/components/agent/AgentMessageParts.js +13 -13
- package/dist/ai/components/conversationActions.js +3 -3
- package/dist/ai/core/ChatProvider.js +1 -1
- package/dist/ai/core/config.js +1 -1
- package/dist/ai/ui/ConversationSidebar.js +3 -3
- package/dist/ai/ui/sidebar.js +3 -3
- package/dist/assets/yaml.worker-DOBV2lND.js +884 -0
- package/dist/components/ActionMenu.d.ts +38 -9
- package/dist/components/ActionMenu.js +48 -22
- package/dist/components/Avatar.d.ts +66 -6
- package/dist/components/Avatar.js +130 -18
- package/dist/components/Breadcrumbs.d.ts +4 -0
- package/dist/components/Breadcrumbs.js +26 -10
- package/dist/components/Button.d.ts +3 -2
- package/dist/components/Button.js +11 -1
- package/dist/components/Callout.d.ts +3 -2
- package/dist/components/Callout.js +3 -2
- package/dist/components/Card.d.ts +33 -9
- package/dist/components/Card.js +24 -8
- package/dist/components/Checkbox.d.ts +3 -1
- package/dist/components/Checkbox.js +4 -2
- package/dist/components/ColorInput.js +1 -1
- package/dist/components/Combobox.d.ts +23 -0
- package/dist/components/Combobox.js +252 -86
- package/dist/components/ConfirmModal.d.ts +28 -2
- package/dist/components/ConfirmModal.js +100 -15
- package/dist/components/ContextMenu.d.ts +29 -2
- package/dist/components/ContextMenu.js +60 -8
- package/dist/components/CopyButton.d.ts +21 -8
- package/dist/components/CopyButton.js +29 -9
- package/dist/components/CreateModal.d.ts +20 -4
- package/dist/components/CreateModal.js +28 -17
- package/dist/components/DatePicker.d.ts +31 -1
- package/dist/components/DatePicker.js +178 -47
- package/dist/components/DescriptionList.d.ts +13 -3
- package/dist/components/DescriptionList.js +43 -11
- package/dist/components/Disclosure.d.ts +10 -2
- package/dist/components/Disclosure.js +18 -7
- package/dist/components/Drawer.js +5 -5
- package/dist/components/EmptyState.d.ts +12 -4
- package/dist/components/EmptyState.js +18 -10
- package/dist/components/ErrorPage.d.ts +40 -0
- package/dist/components/ErrorPage.js +101 -0
- package/dist/components/ExternalLink.d.ts +4 -1
- package/dist/components/ExternalLink.js +4 -5
- package/dist/components/Field.d.ts +21 -6
- package/dist/components/Field.js +32 -16
- package/dist/components/FileButton.d.ts +19 -0
- package/dist/components/FileButton.js +32 -0
- package/dist/components/HoverCard.d.ts +26 -6
- package/dist/components/HoverCard.js +11 -5
- package/dist/components/IconButton.js +4 -2
- package/dist/components/InfoTip.js +3 -3
- package/dist/components/Input.d.ts +33 -3
- package/dist/components/Input.js +149 -14
- package/dist/components/Kbd.d.ts +40 -0
- package/dist/components/Kbd.js +168 -0
- package/dist/components/Listbox.d.ts +103 -0
- package/dist/components/Listbox.js +190 -0
- package/dist/components/Loader.d.ts +8 -2
- package/dist/components/Loader.js +16 -1
- package/dist/components/Markdown.js +1 -1
- package/dist/components/MarkdownImpl.js +1 -1
- package/dist/components/Menu.d.ts +20 -2
- package/dist/components/Menu.js +48 -19
- package/dist/components/MenuEntries.d.ts +41 -4
- package/dist/components/MenuEntries.js +266 -39
- package/dist/components/Meter.d.ts +50 -11
- package/dist/components/Meter.js +71 -21
- package/dist/components/Modal.d.ts +9 -3
- package/dist/components/Modal.js +68 -29
- package/dist/components/OneTimeCodeInput.js +1 -1
- package/dist/components/PageHeader.js +111 -7
- package/dist/components/PageHeaderAction.d.ts +8 -3
- package/dist/components/PageHeaderAction.js +122 -34
- package/dist/components/Pagination.d.ts +13 -1
- package/dist/components/Pagination.js +90 -19
- package/dist/components/Popover.d.ts +16 -2
- package/dist/components/Popover.js +20 -21
- package/dist/components/Provider.d.ts +172 -5
- package/dist/components/Provider.js +157 -14
- package/dist/components/RefreshButton.d.ts +13 -6
- package/dist/components/RefreshButton.js +17 -9
- package/dist/components/RunStatusIcon.d.ts +16 -7
- package/dist/components/RunStatusIcon.js +21 -8
- package/dist/components/Section.d.ts +11 -4
- package/dist/components/Section.js +33 -22
- package/dist/components/SegmentedControl.d.ts +16 -3
- package/dist/components/SegmentedControl.js +7 -3
- package/dist/components/Select.d.ts +19 -3
- package/dist/components/Select.js +114 -68
- package/dist/components/SelectionCopy.d.ts +10 -0
- package/dist/components/SelectionCopy.js +105 -0
- package/dist/components/SettingModeOption.js +1 -1
- package/dist/components/SettingsCard.js +1 -1
- package/dist/components/Skeleton.js +1 -1
- package/dist/components/StatusBadge.d.ts +55 -10
- package/dist/components/StatusBadge.js +91 -15
- package/dist/components/StickyBar.js +1 -1
- package/dist/components/Table.d.ts +22 -4
- package/dist/components/Table.js +46 -24
- package/dist/components/Tabs.d.ts +9 -4
- package/dist/components/Tabs.js +9 -4
- package/dist/components/Tag.d.ts +49 -0
- package/dist/components/Tag.js +199 -0
- package/dist/components/ThemeEditor.js +1 -1
- package/dist/components/Toast.js +1 -1
- package/dist/components/ToggleGroup.d.ts +4 -1
- package/dist/components/ToggleGroup.js +30 -13
- package/dist/components/Tooltip.d.ts +9 -3
- package/dist/components/Tooltip.js +55 -39
- package/dist/components/buttonClasses.d.ts +5 -2
- package/dist/components/buttonClasses.js +11 -10
- package/dist/components/chart.d.ts +69 -0
- package/dist/components/chart.js +145 -0
- package/dist/components/comboboxItems.d.ts +7 -1
- package/dist/components/comboboxItems.js +6 -2
- package/dist/components/dismiss.d.ts +5 -1
- package/dist/components/dismiss.js +6 -2
- package/dist/components/floating.d.ts +4 -4
- package/dist/components/floating.js +5 -5
- package/dist/components/headerActionsFit.d.ts +37 -0
- package/dist/components/headerActionsFit.js +81 -0
- package/dist/components/pageHeaderStore.d.ts +3 -0
- package/dist/components/pageHeaderStore.js +3 -0
- package/dist/components/pathBreaks.js +2 -2
- package/dist/components/popupContainer.d.ts +17 -0
- package/dist/components/popupContainer.js +69 -0
- package/dist/components/relativeTime.d.ts +21 -10
- package/dist/components/relativeTime.js +50 -22
- package/dist/components/segmented.d.ts +18 -4
- package/dist/components/segmented.js +48 -6
- package/dist/components/typedDate.d.ts +5 -0
- package/dist/components/typedDate.js +50 -0
- package/dist/components/useClosingValue.d.ts +4 -0
- package/dist/components/useClosingValue.js +12 -0
- package/dist/components/useCopy.d.ts +6 -0
- package/dist/components/useCopy.js +3 -3
- package/dist/components/useNarrow.d.ts +2 -0
- package/dist/components/useNarrow.js +16 -0
- package/dist/editor/Editor.js +1 -1
- package/dist/editor/FormCodePanel.js +1 -1
- package/dist/editor/Monaco.js +1 -1
- package/dist/editor/workers.js +1 -1
- package/dist/file-explorer/FileExplorer.js +16 -15
- package/dist/file-explorer/FileExplorerContext.js +2 -2
- package/dist/file-explorer/FilePreview/Viewer.js +3 -3
- package/dist/file-explorer/FilePreview/index.js +2 -2
- package/dist/file-explorer/FileRowsWindow.js +3 -3
- package/dist/file-explorer/FileTable.js +1 -1
- package/dist/file-explorer/LoadMoreRow.js +1 -1
- package/dist/file-explorer/TreeView.js +5 -5
- package/dist/file-explorer/helpers.js +2 -2
- package/dist/file-explorer/toasts.js +2 -2
- package/dist/form/Fields/CheckboxGroupField.js +1 -1
- package/dist/form/Fields/DropdownField.js +1 -1
- package/dist/form/Fields/DurationField.js +2 -2
- package/dist/form/Fields/MultiDropdownField.js +1 -1
- package/dist/form/Fields/NumberField.js +1 -1
- package/dist/form/Fields/ObjectField.js +1 -1
- package/dist/form/Fields/RangeField.js +1 -1
- package/dist/form/Fields/StringField.js +1 -1
- package/dist/form/Form.js +3 -3
- package/dist/form/FormLayout.js +1 -1
- package/dist/form/Wizard/WizardContainer.js +1 -1
- package/dist/form/Wizard/WizardNavigation.js +1 -1
- package/dist/form/Wizard/WizardStepContent.js +1 -1
- package/dist/form/fieldRegistry.js +1 -1
- package/dist/form/formEditing.js +1 -1
- package/dist/format.d.ts +17 -7
- package/dist/format.js +39 -16
- package/dist/graph/AnnotationBanner.js +2 -2
- package/dist/graph/Connectors.js +5 -5
- package/dist/graph/DependencyGraph.js +10 -9
- package/dist/graph/GraphEditor.js +4 -4
- package/dist/graph/GraphEditorDialogs.js +13 -12
- package/dist/graph/GraphNode.js +1 -1
- package/dist/graph/InputDialog.js +5 -5
- package/dist/graph/InputsEditor.js +10 -9
- package/dist/graph/MatrixGroup.js +2 -2
- package/dist/graph/ProblemPageLink.d.ts +9 -0
- package/dist/graph/ProblemPageLink.js +21 -0
- package/dist/graph/ProblemsButton.js +26 -16
- package/dist/graph/Reveal.js +1 -1
- package/dist/graph/RunWarningsList.js +21 -12
- package/dist/graph/ShortcutsButton.js +25 -8
- package/dist/graph/StepSummaries.js +1 -1
- package/dist/graph/TreeView.js +2 -2
- package/dist/graph/WorkflowSettingsDialog.js +3 -3
- package/dist/graph/editorApi.d.ts +10 -0
- package/dist/graph/editorFields.js +3 -3
- package/dist/graph/editorPrimitives.js +1 -1
- package/dist/graph/editorRows.js +8 -6
- package/dist/graph/editorStrings.d.ts +1 -0
- package/dist/graph/editorStrings.js +1 -0
- package/dist/graph/fieldProblems.js +2 -2
- package/dist/graph/inputRefs.js +7 -6
- package/dist/graph/runWarnings.d.ts +12 -1
- package/dist/graph/stepWith.js +2 -2
- package/dist/index.d.ts +30 -21
- package/dist/index.js +20 -12
- package/dist/list/ActionToolbar.d.ts +4 -0
- package/dist/list/ActionToolbar.js +35 -12
- package/dist/list/ListFilterBar.d.ts +42 -0
- package/dist/list/ListFilterBar.js +789 -0
- package/dist/list/ListPage.d.ts +42 -4
- package/dist/list/ListPage.js +56 -22
- package/dist/list/ListTable.d.ts +27 -3
- package/dist/list/ListTable.js +48 -23
- package/dist/list/ListToolbar.d.ts +31 -0
- package/dist/list/ListToolbar.js +43 -0
- package/dist/list/ListViewControls.d.ts +8 -4
- package/dist/list/ListViewControls.js +22 -14
- package/dist/list/UserHoverCard.d.ts +8 -2
- package/dist/list/UserHoverCard.js +26 -13
- package/dist/list/filterModel.d.ts +122 -0
- package/dist/list/filterModel.js +308 -0
- package/dist/list/index.d.ts +6 -2
- package/dist/list/index.js +6 -2
- package/dist/list/listSearch.d.ts +1 -1
- package/dist/list/listSearch.js +8 -4
- package/dist/list/listView.d.ts +24 -2
- package/dist/list/listView.js +47 -45
- package/dist/list/rowMenu.d.ts +10 -5
- package/dist/list/rowMenu.js +17 -7
- package/dist/list/useListFilters.d.ts +68 -0
- package/dist/list/useListFilters.js +252 -0
- package/dist/logviewer/LogViewer.js +4 -4
- package/dist/styles/theme.css +94 -0
- package/dist/styles.css +1 -1
- package/package.json +11 -11
- package/dist/assets/yaml.worker-k9AdOqzf.js +0 -1441
- package/dist/components/fullscreen.d.ts +0 -4
- package/dist/components/fullscreen.js +0 -18
|
@@ -1,43 +1,74 @@
|
|
|
1
1
|
import { useStrings } from "./Provider.js";
|
|
2
2
|
import { Button } from "./Button.js";
|
|
3
|
+
import { ErrorText } from "./Field.js";
|
|
3
4
|
import { Modal } from "./Modal.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
5
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
6
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
6
7
|
//#region src/components/ConfirmModal.tsx
|
|
8
|
+
/** What the reader types or picks in, which takes focus ahead of the buttons. A checkbox or
|
|
9
|
+
* radio doesn't: a destructive dialog with one still opens on Cancel. Inputs a picker keeps
|
|
10
|
+
* out of the tab order, for its form value, aren't fields. */
|
|
11
|
+
var FIELDS = [
|
|
12
|
+
"input:not([disabled]):not([type=\"hidden\"]):not([type=\"checkbox\"]):not([type=\"radio\"])",
|
|
13
|
+
"select:not([disabled])",
|
|
14
|
+
"textarea:not([disabled])",
|
|
15
|
+
"[role=\"combobox\"]:not([disabled]):not([aria-disabled=\"true\"])",
|
|
16
|
+
"[contenteditable=\"true\"]"
|
|
17
|
+
].map((field) => `${field}:not([tabindex="-1"]):not([aria-hidden="true"])`).join(", ");
|
|
7
18
|
/** Asks before an action: its title the question, its description what follows,
|
|
8
19
|
* and Cancel beside a confirm button named for the action. */
|
|
9
|
-
function ConfirmModal({ open, onClose, title, description, children, confirmLabel, onConfirm, confirmDisabled = false, destructive = false, align = "top", cancelLabel, closeOnConfirm = true }) {
|
|
20
|
+
function ConfirmModal({ open, onClose, title, description, children, confirmLabel, onConfirm, confirmDisabled = false, destructive = false, align = "top", cancelLabel, closeOnConfirm = true, initialFocus, pending: pendingProp = false, error, size = "md" }) {
|
|
10
21
|
const { common } = useStrings();
|
|
11
|
-
const [
|
|
12
|
-
const
|
|
22
|
+
const [running, setRunning] = useState(false);
|
|
23
|
+
const fired = useRef(false);
|
|
24
|
+
const cancelButton = useRef(null);
|
|
25
|
+
const confirmButton = useRef(null);
|
|
26
|
+
const bodyElement = useRef(null);
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (open) fired.current = false;
|
|
29
|
+
}, [open]);
|
|
30
|
+
const pending = running || pendingProp;
|
|
13
31
|
const runConfirm = async () => {
|
|
14
|
-
if (
|
|
15
|
-
|
|
16
|
-
|
|
32
|
+
if (fired.current || pendingProp) return;
|
|
33
|
+
fired.current = true;
|
|
34
|
+
setRunning(true);
|
|
35
|
+
let closing = false;
|
|
17
36
|
try {
|
|
18
37
|
await onConfirm();
|
|
19
|
-
|
|
38
|
+
closing = closeOnConfirm;
|
|
39
|
+
if (closing) onClose();
|
|
20
40
|
} finally {
|
|
21
|
-
|
|
22
|
-
|
|
41
|
+
fired.current = closing;
|
|
42
|
+
setRunning(false);
|
|
23
43
|
}
|
|
24
44
|
};
|
|
45
|
+
const body = children !== void 0 && children !== null && children !== false ? error ? /* @__PURE__ */ jsxs("div", {
|
|
46
|
+
className: "flex flex-col gap-3",
|
|
47
|
+
children: [children, /* @__PURE__ */ jsx(ErrorText, { children: error })]
|
|
48
|
+
}) : children : error ? /* @__PURE__ */ jsx(ErrorText, { children: error }) : void 0;
|
|
25
49
|
return /* @__PURE__ */ jsx(Modal, {
|
|
26
50
|
alert: destructive,
|
|
27
51
|
open,
|
|
28
52
|
onClose,
|
|
29
53
|
align,
|
|
30
54
|
dismissible: !pending,
|
|
31
|
-
size
|
|
55
|
+
size,
|
|
32
56
|
title,
|
|
33
57
|
description,
|
|
34
|
-
|
|
58
|
+
initialFocus: () => {
|
|
59
|
+
const button = (initialFocus === "cancel" || initialFocus !== "confirm" && destructive ? cancelButton : confirmButton).current;
|
|
60
|
+
if (initialFocus === "cancel" || initialFocus === "confirm") return button ?? true;
|
|
61
|
+
return bodyElement.current?.querySelector(FIELDS) ?? button ?? true;
|
|
62
|
+
},
|
|
63
|
+
footer: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Button, {
|
|
64
|
+
ref: cancelButton,
|
|
35
65
|
variant: "ghost",
|
|
36
66
|
size: "sm",
|
|
37
67
|
onClick: onClose,
|
|
38
68
|
disabled: pending,
|
|
39
69
|
children: cancelLabel ?? common.cancel
|
|
40
70
|
}), /* @__PURE__ */ jsx(Button, {
|
|
71
|
+
ref: confirmButton,
|
|
41
72
|
variant: destructive ? "danger" : "primary",
|
|
42
73
|
size: "sm",
|
|
43
74
|
onClick: runConfirm,
|
|
@@ -45,8 +76,62 @@ function ConfirmModal({ open, onClose, title, description, children, confirmLabe
|
|
|
45
76
|
loading: pending,
|
|
46
77
|
children: confirmLabel
|
|
47
78
|
})] }),
|
|
48
|
-
children
|
|
79
|
+
children: body !== void 0 && /* @__PURE__ */ jsx("div", {
|
|
80
|
+
ref: bodyElement,
|
|
81
|
+
className: "contents",
|
|
82
|
+
children: body
|
|
83
|
+
})
|
|
49
84
|
});
|
|
50
85
|
}
|
|
86
|
+
/** Asks from anywhere, such as a menu row, without keeping a dialog's state by hand:
|
|
87
|
+
* `const { confirm, dialog } = useConfirm()`, render `dialog` once, then
|
|
88
|
+
* `confirm({ title, confirmLabel, destructive, onConfirm })`. It resolves true once
|
|
89
|
+
* confirmed and `onConfirm` has settled, and false when the reader cancels. */
|
|
90
|
+
function useConfirm() {
|
|
91
|
+
const [request, setRequest] = useState(null);
|
|
92
|
+
const [open, setOpen] = useState(false);
|
|
93
|
+
const [error, setError] = useState(void 0);
|
|
94
|
+
const settle = useRef(null);
|
|
95
|
+
const finish = useCallback((confirmed) => {
|
|
96
|
+
const resolve = settle.current;
|
|
97
|
+
settle.current = null;
|
|
98
|
+
resolve?.(confirmed);
|
|
99
|
+
}, []);
|
|
100
|
+
const confirm = useCallback((next) => {
|
|
101
|
+
finish(false);
|
|
102
|
+
setRequest(next);
|
|
103
|
+
setError(void 0);
|
|
104
|
+
setOpen(true);
|
|
105
|
+
return new Promise((resolve) => {
|
|
106
|
+
settle.current = resolve;
|
|
107
|
+
});
|
|
108
|
+
}, [finish]);
|
|
109
|
+
const close = useCallback(() => {
|
|
110
|
+
setOpen(false);
|
|
111
|
+
finish(false);
|
|
112
|
+
}, [finish]);
|
|
113
|
+
return {
|
|
114
|
+
confirm,
|
|
115
|
+
dialog: request && /* @__PURE__ */ jsx(ConfirmModal, {
|
|
116
|
+
...request,
|
|
117
|
+
open,
|
|
118
|
+
onClose: close,
|
|
119
|
+
error,
|
|
120
|
+
closeOnConfirm: false,
|
|
121
|
+
onConfirm: async () => {
|
|
122
|
+
if (!settle.current) return;
|
|
123
|
+
setError(void 0);
|
|
124
|
+
try {
|
|
125
|
+
await request.onConfirm();
|
|
126
|
+
} catch (failure) {
|
|
127
|
+
setError(request.errorMessage?.(failure));
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
finish(true);
|
|
131
|
+
if (request.closeOnConfirm !== false) setOpen(false);
|
|
132
|
+
}
|
|
133
|
+
})
|
|
134
|
+
};
|
|
135
|
+
}
|
|
51
136
|
//#endregion
|
|
52
|
-
export { ConfirmModal };
|
|
137
|
+
export { ConfirmModal, useConfirm };
|
|
@@ -1,17 +1,44 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
1
2
|
import { type FloatingAnchor } from './floating.js';
|
|
3
|
+
import { type MenuSize } from './Menu.js';
|
|
2
4
|
import { type MenuEntry, type MenuSearch } from './MenuEntries.js';
|
|
3
5
|
export interface OpenMenuOptions {
|
|
4
6
|
search?: MenuSearch | undefined;
|
|
5
7
|
/** Called once the menu closes, or another menu replaces it. */
|
|
6
8
|
onClose?: (() => void) | undefined;
|
|
9
|
+
/** As `MenuPopup`'s: `sm` rows for a compact surface. */
|
|
10
|
+
size?: MenuSize | undefined;
|
|
11
|
+
/** As `MenuPopup`'s: classes on the menu and its submenus, such as a terminal's colors. */
|
|
12
|
+
themeClassName?: string | undefined;
|
|
7
13
|
}
|
|
8
|
-
|
|
14
|
+
/** Where a menu opens: an element, such as the button that opened it, or a pointer event,
|
|
15
|
+
* such as a right-click. An event with Shift held is left to the browser, whose own menu
|
|
16
|
+
* then shows; any other has its default prevented. */
|
|
17
|
+
export type MenuOpener = FloatingAnchor & {
|
|
18
|
+
shiftKey?: boolean;
|
|
19
|
+
preventDefault?: () => void;
|
|
20
|
+
};
|
|
21
|
+
/** Opens a menu of `items` at `at`. Returns false when it opened none: no items, Shift held,
|
|
22
|
+
* or `at` is the element whose menu is open, which it closes instead. */
|
|
23
|
+
export type OpenMenu = (at: MenuOpener, items: MenuEntry[], options?: OpenMenuOptions) => boolean | void;
|
|
9
24
|
/**
|
|
10
25
|
* One menu that any number of rows, buttons or a canvas open with their own
|
|
11
26
|
* items, so a long list holds a single menu rather than one per row. Render
|
|
12
|
-
* `contextMenu` once.
|
|
27
|
+
* `contextMenu` once. Most hosts want the one `UIProvider` mounts, through
|
|
28
|
+
* `useOpenMenu`; this is for a menu scoped to one region.
|
|
13
29
|
*/
|
|
14
30
|
export declare function useContextMenu(): {
|
|
15
31
|
openMenu: OpenMenu;
|
|
16
32
|
contextMenu: import("react").JSX.Element | null;
|
|
33
|
+
openedBy: Element | null;
|
|
17
34
|
};
|
|
35
|
+
/** The one menu every row, button and canvas below it opens through `useOpenMenu`, so none
|
|
36
|
+
* renders a menu of its own. `UIProvider` mounts it; nothing else needs to. */
|
|
37
|
+
export declare function ContextMenuProvider({ children }: {
|
|
38
|
+
children: ReactNode;
|
|
39
|
+
}): import("react").JSX.Element;
|
|
40
|
+
/** Opens the app's one shared menu: `openMenu(e, items)` from a right-click, or
|
|
41
|
+
* `openMenu(button, items)` under a button, which a second press closes. */
|
|
42
|
+
export declare function useOpenMenu(): OpenMenu;
|
|
43
|
+
/** The element the shared menu is open under, so its button can say it's expanded. */
|
|
44
|
+
export declare function useMenuOpenedBy(): Element | null;
|
|
@@ -1,38 +1,60 @@
|
|
|
1
1
|
import { toPositionerAnchor } from "./floating.js";
|
|
2
2
|
import { MenuPopup } from "./Menu.js";
|
|
3
|
+
import { isDev } from "../dev.js";
|
|
3
4
|
import { MenuEntries, MenuFilter } from "./MenuEntries.js";
|
|
4
5
|
import { Menu } from "@base-ui/react/menu";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
6
|
+
import { createContext, use, useCallback, useRef, useState } from "react";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
8
|
//#region src/components/ContextMenu.tsx
|
|
8
9
|
/**
|
|
9
10
|
* One menu that any number of rows, buttons or a canvas open with their own
|
|
10
11
|
* items, so a long list holds a single menu rather than one per row. Render
|
|
11
|
-
* `contextMenu` once.
|
|
12
|
+
* `contextMenu` once. Most hosts want the one `UIProvider` mounts, through
|
|
13
|
+
* `useOpenMenu`; this is for a menu scoped to one region.
|
|
12
14
|
*/
|
|
13
15
|
function useContextMenu() {
|
|
14
16
|
const [opened, setOpened] = useState(null);
|
|
15
17
|
const [open, setOpen] = useState(false);
|
|
16
18
|
const owner = useRef(null);
|
|
19
|
+
const current = useRef(null);
|
|
17
20
|
const release = useCallback(() => {
|
|
18
21
|
const was = owner.current;
|
|
19
22
|
owner.current = null;
|
|
20
23
|
was?.onClose?.();
|
|
21
24
|
}, []);
|
|
22
25
|
const openMenu = useCallback((at, items, options) => {
|
|
23
|
-
|
|
26
|
+
const element = at instanceof Element ? at : null;
|
|
27
|
+
if (!element && at.shiftKey || items.length === 0) return false;
|
|
28
|
+
if (element && current.current?.element === element) {
|
|
29
|
+
current.current = null;
|
|
30
|
+
setOpen(false);
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
if (!element) at.preventDefault?.();
|
|
24
34
|
release();
|
|
25
35
|
const next = {
|
|
26
36
|
...options,
|
|
27
37
|
anchor: toPositionerAnchor(at),
|
|
38
|
+
element,
|
|
28
39
|
items
|
|
29
40
|
};
|
|
30
41
|
owner.current = next;
|
|
42
|
+
current.current = next;
|
|
31
43
|
setOpened(next);
|
|
32
44
|
setOpen(true);
|
|
45
|
+
return true;
|
|
33
46
|
}, [release]);
|
|
34
|
-
const onOpenChange = useCallback((next) => {
|
|
35
|
-
if (
|
|
47
|
+
const onOpenChange = useCallback((next, details) => {
|
|
48
|
+
if (next) return;
|
|
49
|
+
const element = current.current?.element;
|
|
50
|
+
const event = details.event;
|
|
51
|
+
const target = details.reason === "focus-out" && event instanceof FocusEvent ? event.relatedTarget : event?.target;
|
|
52
|
+
if ((details.reason === "outside-press" || details.reason === "focus-out") && element && target instanceof Node && element.contains(target)) {
|
|
53
|
+
details.cancel();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
current.current = null;
|
|
57
|
+
setOpen(false);
|
|
36
58
|
}, []);
|
|
37
59
|
const onOpenChangeComplete = useCallback((next) => {
|
|
38
60
|
if (!next) release();
|
|
@@ -48,6 +70,8 @@ function useContextMenu() {
|
|
|
48
70
|
modal: false,
|
|
49
71
|
children: /* @__PURE__ */ jsx(MenuPopup, {
|
|
50
72
|
anchor: opened.anchor,
|
|
73
|
+
size: opened.size,
|
|
74
|
+
themeClassName: opened.themeClassName,
|
|
51
75
|
children: /* @__PURE__ */ jsx(MenuEntries, {
|
|
52
76
|
entries: opened.items,
|
|
53
77
|
search: opened.search,
|
|
@@ -55,8 +79,36 @@ function useContextMenu() {
|
|
|
55
79
|
})
|
|
56
80
|
})
|
|
57
81
|
})
|
|
58
|
-
}, opened.search ? "search" : "plain")
|
|
82
|
+
}, opened.search ? "search" : "plain"),
|
|
83
|
+
openedBy: open ? opened?.element ?? null : null
|
|
59
84
|
};
|
|
60
85
|
}
|
|
86
|
+
var OpenMenuContext = createContext(null);
|
|
87
|
+
var OpenedByContext = createContext(null);
|
|
88
|
+
/** The one menu every row, button and canvas below it opens through `useOpenMenu`, so none
|
|
89
|
+
* renders a menu of its own. `UIProvider` mounts it; nothing else needs to. */
|
|
90
|
+
function ContextMenuProvider({ children }) {
|
|
91
|
+
const { openMenu, contextMenu, openedBy } = useContextMenu();
|
|
92
|
+
return /* @__PURE__ */ jsx(OpenMenuContext, {
|
|
93
|
+
value: openMenu,
|
|
94
|
+
children: /* @__PURE__ */ jsxs(OpenedByContext, {
|
|
95
|
+
value: openedBy,
|
|
96
|
+
children: [children, contextMenu]
|
|
97
|
+
})
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
var warnNoProvider = () => {
|
|
101
|
+
if (isDev()) console.warn("useOpenMenu: no ContextMenuProvider or UIProvider above, so no menu opens.");
|
|
102
|
+
return false;
|
|
103
|
+
};
|
|
104
|
+
/** Opens the app's one shared menu: `openMenu(e, items)` from a right-click, or
|
|
105
|
+
* `openMenu(button, items)` under a button, which a second press closes. */
|
|
106
|
+
function useOpenMenu() {
|
|
107
|
+
return use(OpenMenuContext) ?? warnNoProvider;
|
|
108
|
+
}
|
|
109
|
+
/** The element the shared menu is open under, so its button can say it's expanded. */
|
|
110
|
+
function useMenuOpenedBy() {
|
|
111
|
+
return use(OpenedByContext);
|
|
112
|
+
}
|
|
61
113
|
//#endregion
|
|
62
|
-
export { useContextMenu };
|
|
114
|
+
export { ContextMenuProvider, useContextMenu, useMenuOpenedBy, useOpenMenu };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ComponentPropsWithRef, type ReactNode } from 'react';
|
|
2
2
|
import { type ButtonSize, type ButtonVariant } from './buttonClasses.js';
|
|
3
3
|
export interface CopyButtonProps extends Omit<ComponentPropsWithRef<'button'>, 'onClick' | 'children' | 'type'> {
|
|
4
4
|
variant?: ButtonVariant | undefined;
|
|
@@ -14,25 +14,38 @@ export interface CopyButtonProps extends Omit<ComponentPropsWithRef<'button'>, '
|
|
|
14
14
|
/** Shows "Copy" (then "Copied") beside the icon, for a copy that is a toolbar's
|
|
15
15
|
* command rather than a detail beside a value. */
|
|
16
16
|
showLabel?: boolean | undefined;
|
|
17
|
+
/** A glyph in place of the copy icon, such as a branch for a branch name; the check
|
|
18
|
+
* still shows once it has copied. */
|
|
19
|
+
icon?: ReactNode | undefined;
|
|
20
|
+
/** The whole success toast, in place of "Copied …", as `useCopy` takes it. */
|
|
21
|
+
success?: string | undefined;
|
|
22
|
+
/** The whole failure toast, such as one that says where else to copy it from. */
|
|
23
|
+
failure?: string | undefined;
|
|
17
24
|
}
|
|
18
25
|
/** The one copy control: a button that copies its text, turns into a check for a
|
|
19
26
|
* moment, and says so in a toast, which screen readers announce. */
|
|
20
|
-
export declare function CopyButton({ text, label, quiet, showLabel, variant, size, ...props }: CopyButtonProps): import("react").JSX.Element;
|
|
27
|
+
export declare function CopyButton({ text, label, quiet, showLabel, variant, size, icon: glyph, success, failure, ...props }: CopyButtonProps): import("react").JSX.Element;
|
|
21
28
|
export interface CopyCodeBlockProps {
|
|
22
29
|
/** What the button copies: the code as the reader would type it. */
|
|
23
30
|
text: string;
|
|
24
31
|
/** Names what it copies, as CopyButton's `label` does, such as "install command". */
|
|
25
32
|
label?: string | undefined;
|
|
26
|
-
/** The code as shown,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
/** The code as shown, such as highlighted; `text` by default. A secret's own children
|
|
34
|
+
* must mask it while it is hidden. */
|
|
35
|
+
children?: ReactNode;
|
|
36
|
+
/** Adds an eye button that shows or hides the value. Without `children` the block masks
|
|
37
|
+
* the value itself, with dots that don't give away its length. */
|
|
30
38
|
secret?: boolean | undefined;
|
|
39
|
+
/** Whether a secret shows. Leave it out for the block to keep its own state, hidden first. */
|
|
31
40
|
revealed?: boolean | undefined;
|
|
41
|
+
defaultRevealed?: boolean | undefined;
|
|
32
42
|
onRevealedChange?: ((revealed: boolean) => void) | undefined;
|
|
43
|
+
/** Breaks a long value across lines instead of scrolling it, such as a token. */
|
|
44
|
+
wrap?: boolean | undefined;
|
|
33
45
|
className?: string | undefined;
|
|
34
46
|
}
|
|
35
47
|
/** A block of code, such as a command to run, with a button that copies it. The
|
|
36
48
|
* buttons show while the pointer is over the block or focus is in it, and always
|
|
37
|
-
* where there is no hover to reveal them.
|
|
38
|
-
|
|
49
|
+
* where there is no hover to reveal them. With `secret` it holds a value shown once,
|
|
50
|
+
* such as a new API key: masked until the eye shows it, and copied either way. */
|
|
51
|
+
export declare function CopyCodeBlock({ text, label, children, secret, revealed: revealedProp, defaultRevealed, onRevealedChange, wrap, className, }: CopyCodeBlockProps): import("react").JSX.Element;
|
|
@@ -4,18 +4,21 @@ import { CheckIcon, CopyIcon, EyeIcon, EyeOffIcon } from "../icons.js";
|
|
|
4
4
|
import { useStrings } from "./Provider.js";
|
|
5
5
|
import { useCopy } from "./useCopy.js";
|
|
6
6
|
import { IconButton } from "./IconButton.js";
|
|
7
|
-
import {
|
|
7
|
+
import { useState } from "react";
|
|
8
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
8
9
|
//#region src/components/CopyButton.tsx
|
|
9
10
|
/** The one copy control: a button that copies its text, turns into a check for a
|
|
10
11
|
* moment, and says so in a toast, which screen readers announce. */
|
|
11
|
-
function CopyButton({ text, label, quiet = false, showLabel = false, variant = showLabel ? "outline" : "ghost", size = "sm", ...props }) {
|
|
12
|
+
function CopyButton({ text, label, quiet = false, showLabel = false, variant = showLabel ? "outline" : "ghost", size = "sm", icon: glyph, success, failure, ...props }) {
|
|
12
13
|
const { common: t } = useStrings();
|
|
13
14
|
const { copy, copied } = useCopy();
|
|
14
15
|
const name = label ? copied ? t.copiedThing(label) : t.copyThing(label) : copied ? t.copied : t.copy;
|
|
15
|
-
const icon = copied ? /* @__PURE__ */ jsx(CheckIcon, { className: "text-(--theme-success)" }) : /* @__PURE__ */ jsx(CopyIcon, {});
|
|
16
|
+
const icon = copied ? /* @__PURE__ */ jsx(CheckIcon, { className: "text-(--theme-success)" }) : glyph ?? /* @__PURE__ */ jsx(CopyIcon, {});
|
|
16
17
|
const onClick = () => void copy(typeof text === "function" ? text() : text, {
|
|
17
18
|
label,
|
|
18
|
-
quiet
|
|
19
|
+
quiet,
|
|
20
|
+
success,
|
|
21
|
+
failure
|
|
19
22
|
});
|
|
20
23
|
if (showLabel) return /* @__PURE__ */ jsxs("button", {
|
|
21
24
|
type: "button",
|
|
@@ -37,24 +40,41 @@ function CopyButton({ text, label, quiet = false, showLabel = false, variant = s
|
|
|
37
40
|
onClick
|
|
38
41
|
});
|
|
39
42
|
}
|
|
43
|
+
var MASK = "•".repeat(24);
|
|
40
44
|
/** A block of code, such as a command to run, with a button that copies it. The
|
|
41
45
|
* buttons show while the pointer is over the block or focus is in it, and always
|
|
42
|
-
* where there is no hover to reveal them.
|
|
43
|
-
|
|
46
|
+
* where there is no hover to reveal them. With `secret` it holds a value shown once,
|
|
47
|
+
* such as a new API key: masked until the eye shows it, and copied either way. */
|
|
48
|
+
function CopyCodeBlock({ text, label, children, secret = false, revealed: revealedProp, defaultRevealed = false, onRevealedChange, wrap = false, className }) {
|
|
44
49
|
const { common: t } = useStrings();
|
|
50
|
+
const [ownRevealed, setOwnRevealed] = useState(defaultRevealed);
|
|
51
|
+
const revealed = revealedProp ?? ownRevealed;
|
|
52
|
+
const setRevealed = (next) => {
|
|
53
|
+
setOwnRevealed(next);
|
|
54
|
+
onRevealedChange?.(next);
|
|
55
|
+
};
|
|
45
56
|
const reveal = "opacity-0 group-hover/code:opacity-100 group-focus-within/code:opacity-100 pointer-coarse:opacity-100";
|
|
57
|
+
let shown = children ?? text;
|
|
58
|
+
if (secret && !revealed && children === void 0) shown = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
59
|
+
"aria-hidden": "true",
|
|
60
|
+
className: "whitespace-nowrap",
|
|
61
|
+
children: MASK
|
|
62
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
63
|
+
className: "sr-only",
|
|
64
|
+
children: t.hiddenValue
|
|
65
|
+
})] });
|
|
46
66
|
return /* @__PURE__ */ jsxs("div", {
|
|
47
67
|
className: cx("group/code relative", className),
|
|
48
68
|
children: [/* @__PURE__ */ jsx("pre", {
|
|
49
|
-
className: cx("m-0
|
|
50
|
-
children
|
|
69
|
+
className: cx("m-0 rounded-lg border border-(--theme-border) bg-(--theme-muted-panel-bg) p-4 text-left font-mono text-sm text-(--theme-app)", wrap ? "whitespace-pre-wrap break-all" : "overflow-x-auto", secret ? "pr-20" : "pr-12"),
|
|
70
|
+
children: shown
|
|
51
71
|
}), /* @__PURE__ */ jsxs("div", {
|
|
52
72
|
className: "absolute top-2 right-2 flex items-center gap-1",
|
|
53
73
|
children: [secret && /* @__PURE__ */ jsx(IconButton, {
|
|
54
74
|
label: t.showValue,
|
|
55
75
|
icon: revealed ? /* @__PURE__ */ jsx(EyeIcon, {}) : /* @__PURE__ */ jsx(EyeOffIcon, {}),
|
|
56
76
|
pressed: revealed,
|
|
57
|
-
onClick: () =>
|
|
77
|
+
onClick: () => setRevealed(!revealed),
|
|
58
78
|
className: cx("bg-(--theme-muted-panel-bg)", reveal)
|
|
59
79
|
}), /* @__PURE__ */ jsx(CopyButton, {
|
|
60
80
|
text,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
import { type ComboboxItems } from './comboboxItems.js';
|
|
3
|
-
import type { MenuEntry } from './MenuEntries.js';
|
|
3
|
+
import type { MenuEntry, MenuSearch } from './MenuEntries.js';
|
|
4
|
+
import { type ModalSize } from './Modal.js';
|
|
4
5
|
interface CreateModalProps {
|
|
5
6
|
open: boolean;
|
|
6
7
|
onClose: () => void;
|
|
@@ -8,11 +9,16 @@ interface CreateModalProps {
|
|
|
8
9
|
typeLabel: string;
|
|
9
10
|
/** Makes the type chip a dropdown to switch between sibling form types. */
|
|
10
11
|
typeMenu?: MenuEntry[] | undefined;
|
|
12
|
+
/** A search atop the type chip's menu, for a long list such as many teams. */
|
|
13
|
+
typeMenuSearch?: MenuSearch | undefined;
|
|
11
14
|
namePlaceholder: string;
|
|
12
15
|
hideName?: boolean;
|
|
13
16
|
initialName?: string | undefined;
|
|
14
17
|
showDescription?: boolean;
|
|
15
18
|
descriptionPlaceholder?: string;
|
|
19
|
+
/** Takes the description's place, such as a rich text editor. Typing in it counts as
|
|
20
|
+
* unsaved input, as the description's does. */
|
|
21
|
+
descriptionField?: ReactNode;
|
|
16
22
|
submitLabel?: string;
|
|
17
23
|
submittingLabel?: string;
|
|
18
24
|
validateName?: ((name: string) => string | null) | undefined;
|
|
@@ -34,11 +40,21 @@ interface CreateModalProps {
|
|
|
34
40
|
dirty?: boolean;
|
|
35
41
|
/** Show a favorite toggle in the footer. */
|
|
36
42
|
showFavorite?: boolean;
|
|
37
|
-
/**
|
|
38
|
-
|
|
43
|
+
/** Muted text at the footer's start, such as which keys submit. `error` takes its place. */
|
|
44
|
+
footerHint?: ReactNode;
|
|
45
|
+
/** Why the last submit failed, at the footer's start, read out as it appears. */
|
|
46
|
+
error?: ReactNode;
|
|
47
|
+
/** Its width, as `Modal`'s: `xl` for a long body such as a rich text editor. It narrows to
|
|
48
|
+
* fit a small window. */
|
|
49
|
+
size?: ModalSize | undefined;
|
|
50
|
+
/** Resolves true to close the modal, false to keep it open (e.g. on error). `modifier` is
|
|
51
|
+
* true when submitted with ⌘/Ctrl+Enter, so a host can create and then open what it made. */
|
|
52
|
+
onSubmit: (name: string, description: string, favorite: boolean, how: {
|
|
53
|
+
modifier: boolean;
|
|
54
|
+
}) => Promise<boolean>;
|
|
39
55
|
}
|
|
40
56
|
/** Submits on Enter in the name field or ⌘/Ctrl+Enter from anywhere in the panel. */
|
|
41
|
-
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;
|
|
57
|
+
export declare function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, typeMenuSearch, namePlaceholder, hideName, initialName, showDescription, descriptionPlaceholder, descriptionField, submitLabel, submittingLabel, validateName, sanitizeName, nameHint, children, properties, advanced, submitDisabled, hideSubmit, nameOptional, onNameChange, dirty, showFavorite, footerHint, error, size, onSubmit, }: CreateModalProps): import("react").JSX.Element;
|
|
42
58
|
/** Compact property pill with an on/off switch. */
|
|
43
59
|
export declare function CreateModalSwitch({ label, checked, onCheckedChange, tooltip, }: {
|
|
44
60
|
label: string;
|
|
@@ -5,17 +5,18 @@ import { Tooltip } from "./Tooltip.js";
|
|
|
5
5
|
import { useStrings } from "./Provider.js";
|
|
6
6
|
import { ActionMenu } from "./ActionMenu.js";
|
|
7
7
|
import { Button } from "./Button.js";
|
|
8
|
+
import { ErrorText } from "./Field.js";
|
|
8
9
|
import { comboboxLeaves } from "./comboboxItems.js";
|
|
9
10
|
import { Combobox } from "./Combobox.js";
|
|
10
11
|
import { Modal } from "./Modal.js";
|
|
11
12
|
import { Disclosure } from "./Disclosure.js";
|
|
12
13
|
import { Switch } from "./Switch.js";
|
|
13
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
14
14
|
import { useId, useRef, useState } from "react";
|
|
15
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
15
16
|
import { Toggle } from "@base-ui/react/toggle";
|
|
16
17
|
//#region src/components/CreateModal.tsx
|
|
17
18
|
/** Submits on Enter in the name field or ⌘/Ctrl+Enter from anywhere in the panel. */
|
|
18
|
-
function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, namePlaceholder, hideName = false, initialName, showDescription = true, descriptionPlaceholder, submitLabel, submittingLabel, validateName, sanitizeName, nameHint, children, properties, advanced, submitDisabled = false, hideSubmit = false, nameOptional = false, onNameChange, dirty = false, showFavorite = false, onSubmit }) {
|
|
19
|
+
function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, typeMenuSearch, namePlaceholder, hideName = false, initialName, showDescription = true, descriptionPlaceholder, descriptionField, submitLabel, submittingLabel, validateName, sanitizeName, nameHint, children, properties, advanced, submitDisabled = false, hideSubmit = false, nameOptional = false, onNameChange, dirty = false, showFavorite = false, footerHint, error, size = "lg", onSubmit }) {
|
|
19
20
|
const t = useStrings().modal;
|
|
20
21
|
const { common } = useStrings();
|
|
21
22
|
const submitText = submitLabel ?? common.create;
|
|
@@ -54,7 +55,7 @@ function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, namePlaceho
|
|
|
54
55
|
setNameFlash(false);
|
|
55
56
|
requestAnimationFrame(() => setNameFlash(true));
|
|
56
57
|
};
|
|
57
|
-
const handleSubmit = async () => {
|
|
58
|
+
const handleSubmit = async (modifier = false) => {
|
|
58
59
|
if (!canSubmit) {
|
|
59
60
|
if (!hideName && !submitting && (!name.trim() || nameError)) flashName();
|
|
60
61
|
return;
|
|
@@ -62,7 +63,7 @@ function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, namePlaceho
|
|
|
62
63
|
setSubmitting(true);
|
|
63
64
|
let close = false;
|
|
64
65
|
try {
|
|
65
|
-
close = await onSubmit(name.trim(), description.trim(), favorite);
|
|
66
|
+
close = await onSubmit(name.trim(), description.trim(), favorite, { modifier });
|
|
66
67
|
} catch (error) {
|
|
67
68
|
console.error(error);
|
|
68
69
|
} finally {
|
|
@@ -74,19 +75,19 @@ function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, namePlaceho
|
|
|
74
75
|
open,
|
|
75
76
|
onClose,
|
|
76
77
|
"aria-label": `${submitText}: ${typeLabel}`,
|
|
77
|
-
size
|
|
78
|
+
size,
|
|
78
79
|
dismissible: !isDirty,
|
|
79
80
|
initialFocus: () => nameRef.current ?? true,
|
|
80
81
|
onKeyDown: (e) => {
|
|
81
|
-
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) handleSubmit();
|
|
82
|
+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) handleSubmit(true);
|
|
82
83
|
},
|
|
83
84
|
onMouseDown: (e) => {
|
|
84
85
|
if (e.target instanceof Element && !e.target.closest("input, textarea, button, a, select, label, code, pre, .select-all")) e.preventDefault();
|
|
85
86
|
},
|
|
86
|
-
footer: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
87
|
-
showFavorite && /* @__PURE__ */
|
|
88
|
-
className: "me-auto",
|
|
89
|
-
children: /* @__PURE__ */ jsx(Tooltip, {
|
|
87
|
+
footer: /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
88
|
+
(showFavorite || footerHint || error) && /* @__PURE__ */ jsxs("div", {
|
|
89
|
+
className: "me-auto flex min-w-0 items-center gap-3",
|
|
90
|
+
children: [showFavorite && /* @__PURE__ */ jsx(Tooltip, {
|
|
90
91
|
content: favorite ? t.favoriteHintOn : t.favoriteHintOff,
|
|
91
92
|
children: /* @__PURE__ */ jsxs(Toggle, {
|
|
92
93
|
pressed: favorite,
|
|
@@ -98,7 +99,10 @@ function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, namePlaceho
|
|
|
98
99
|
}),
|
|
99
100
|
children: [favorite ? /* @__PURE__ */ jsx(FavoriteIcon, { className: "text-(--theme-favorite)" }) : /* @__PURE__ */ jsx(FavoriteOutlineIcon, {}), favorite ? t.favorited : t.favorite]
|
|
100
101
|
})
|
|
101
|
-
})
|
|
102
|
+
}), error ? /* @__PURE__ */ jsx(ErrorText, { children: error }) : footerHint && /* @__PURE__ */ jsx("p", {
|
|
103
|
+
className: "m-0 min-w-0 text-xs text-(--theme-muted-text-color)",
|
|
104
|
+
children: footerHint
|
|
105
|
+
})]
|
|
102
106
|
}),
|
|
103
107
|
/* @__PURE__ */ jsx(Button, {
|
|
104
108
|
variant: "ghost",
|
|
@@ -109,7 +113,7 @@ function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, namePlaceho
|
|
|
109
113
|
!hideSubmit && /* @__PURE__ */ jsx(Button, {
|
|
110
114
|
variant: "primary",
|
|
111
115
|
size: "sm",
|
|
112
|
-
onClick: handleSubmit,
|
|
116
|
+
onClick: () => handleSubmit(false),
|
|
113
117
|
"aria-disabled": !canSubmit,
|
|
114
118
|
loading: submitting,
|
|
115
119
|
children: submitting ? submittingText : submitText
|
|
@@ -117,11 +121,15 @@ function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, namePlaceho
|
|
|
117
121
|
] }),
|
|
118
122
|
children: /* @__PURE__ */ jsxs("div", {
|
|
119
123
|
onChangeCapture: () => setInteracted(true),
|
|
124
|
+
onInputCapture: (e) => {
|
|
125
|
+
if (e.target instanceof HTMLElement && e.target.isContentEditable) setInteracted(true);
|
|
126
|
+
},
|
|
120
127
|
children: [
|
|
121
128
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
122
129
|
typeMenu && typeMenu.length > 0 ? /* @__PURE__ */ jsxs(ActionMenu, {
|
|
123
130
|
items: typeMenu,
|
|
124
131
|
label: typeLabel,
|
|
132
|
+
search: typeMenuSearch,
|
|
125
133
|
triggerClassName: "inline-flex items-center gap-1.5 px-1.5 py-1 rounded-md bg-(--theme-muted-panel-bg) text-xs font-medium text-(--theme-muted-text-color) transition-colors hover:text-(--theme-app)",
|
|
126
134
|
children: [
|
|
127
135
|
typeIcon,
|
|
@@ -132,7 +140,7 @@ function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, namePlaceho
|
|
|
132
140
|
className: "inline-flex items-center gap-1.5 px-1.5 py-1 rounded-md bg-(--theme-muted-panel-bg) text-xs font-medium text-(--theme-muted-text-color)",
|
|
133
141
|
children: [typeIcon, typeLabel]
|
|
134
142
|
}),
|
|
135
|
-
hideName ? /* @__PURE__ */ jsx("div", { className: "mt-3" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
143
|
+
hideName ? /* @__PURE__ */ jsx("div", { className: "mt-3" }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
136
144
|
/* @__PURE__ */ jsx("input", {
|
|
137
145
|
ref: nameRef,
|
|
138
146
|
type: "text",
|
|
@@ -151,7 +159,7 @@ function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, namePlaceho
|
|
|
151
159
|
if (e.key === "Enter" && !e.nativeEvent.isComposing && !e.metaKey && !e.ctrlKey) {
|
|
152
160
|
e.preventDefault();
|
|
153
161
|
e.stopPropagation();
|
|
154
|
-
handleSubmit();
|
|
162
|
+
handleSubmit(false);
|
|
155
163
|
}
|
|
156
164
|
},
|
|
157
165
|
placeholder: namePlaceholder,
|
|
@@ -172,7 +180,10 @@ function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, namePlaceho
|
|
|
172
180
|
className: "mt-1 text-xs text-(--theme-muted-text-color)",
|
|
173
181
|
children: hint
|
|
174
182
|
}),
|
|
175
|
-
showDescription ? /* @__PURE__ */ jsx("
|
|
183
|
+
showDescription && descriptionField ? /* @__PURE__ */ jsx("div", {
|
|
184
|
+
className: "mt-1.5",
|
|
185
|
+
children: descriptionField
|
|
186
|
+
}) : showDescription ? /* @__PURE__ */ jsx("textarea", {
|
|
176
187
|
autoComplete: "off",
|
|
177
188
|
"data-1p-ignore": true,
|
|
178
189
|
value: description,
|
|
@@ -232,9 +243,9 @@ function CreateModalSelect({ label, icon, value, items, onValueChange, disabled
|
|
|
232
243
|
label: item
|
|
233
244
|
} : item).find((item) => item.value === value);
|
|
234
245
|
const selectedLabel = selected?.label ?? (value || void 0);
|
|
235
|
-
const pillContent = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
246
|
+
const pillContent = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
236
247
|
selected?.icon ?? icon,
|
|
237
|
-
selectedLabel ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
248
|
+
selectedLabel ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
238
249
|
className: "font-normal text-(--theme-muted-text-color)",
|
|
239
250
|
children: label
|
|
240
251
|
}), selectedLabel] }) : label,
|