@parallelworks/ui 0.51.0 → 0.53.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 +197 -17
- package/dist/ai/agent/toolStrings.d.ts +2 -0
- package/dist/ai/agent/toolStrings.js +20 -18
- package/dist/ai/ai.css +17 -3
- package/dist/ai/components/AllocationSelector.js +5 -1
- package/dist/ai/components/AttachmentManager.js +51 -42
- package/dist/ai/components/AttachmentUpload.d.ts +4 -2
- package/dist/ai/components/AttachmentUpload.js +9 -9
- package/dist/ai/components/BlockedGroupBanner.js +8 -14
- package/dist/ai/components/BranchNavigator.js +11 -12
- package/dist/ai/components/ChatEmptyState.js +12 -19
- package/dist/ai/components/ChatInput.d.ts +4 -3
- package/dist/ai/components/ChatInput.js +27 -13
- package/dist/ai/components/ChatMessage.js +45 -121
- package/dist/ai/components/ChatMessageList.d.ts +5 -2
- package/dist/ai/components/ChatMessageList.js +89 -51
- package/dist/ai/components/ChatSidebar.js +11 -7
- package/dist/ai/components/ChatThread.js +1 -13
- package/dist/ai/components/DragOverlay.js +3 -3
- package/dist/ai/components/MessageAttachments.d.ts +10 -0
- package/dist/ai/components/MessageAttachments.js +78 -0
- package/dist/ai/components/ModelSelector.js +3 -3
- package/dist/ai/components/PastedText.js +2 -2
- package/dist/ai/components/ProviderIssueBanner.js +19 -23
- package/dist/ai/components/ShareDialog.js +2 -2
- package/dist/ai/components/agent/ActivityLine.d.ts +0 -2
- package/dist/ai/components/agent/ActivityLine.js +8 -51
- package/dist/ai/components/agent/AgentMessageParts.js +114 -53
- package/dist/ai/core/ChatProvider.d.ts +16 -5
- package/dist/ai/core/ChatProvider.js +41 -8
- package/dist/ai/core/config.js +4 -3
- package/dist/ai/index.d.ts +1 -1
- package/dist/ai/index.js +3 -3
- package/dist/ai/strings.d.ts +29 -3
- package/dist/ai/strings.js +80 -26
- package/dist/ai/types.d.ts +4 -0
- package/dist/ai/ui/Notice.d.ts +2 -2
- package/dist/ai/ui/Notice.js +29 -62
- package/dist/ai/ui/sidebar.d.ts +5 -3
- package/dist/ai/ui/sidebar.js +22 -10
- package/dist/components/ActionMenu.d.ts +1 -0
- package/dist/components/ActionMenu.js +6 -1
- package/dist/components/AppLink.d.ts +26 -0
- package/dist/components/AppLink.js +51 -0
- package/dist/components/Avatar.d.ts +2 -1
- package/dist/components/Avatar.js +4 -2
- package/dist/components/Button.d.ts +15 -5
- package/dist/components/Button.js +47 -40
- package/dist/components/Callout.d.ts +31 -7
- package/dist/components/Callout.js +50 -19
- package/dist/components/Card.d.ts +11 -6
- package/dist/components/Card.js +14 -8
- package/dist/components/Checkbox.js +2 -1
- package/dist/components/ColorInput.js +5 -3
- package/dist/components/Combobox.js +49 -19
- package/dist/components/ConfirmModal.d.ts +19 -8
- package/dist/components/ConfirmModal.js +31 -53
- package/dist/components/CopyButton.d.ts +38 -0
- package/dist/components/CopyButton.js +68 -0
- package/dist/components/CreateModal.js +118 -111
- package/dist/components/DatePicker.js +12 -8
- package/dist/components/DescriptionList.d.ts +25 -5
- package/dist/components/DescriptionList.js +56 -22
- package/dist/components/Disclosure.js +1 -1
- package/dist/components/Drawer.js +10 -16
- package/dist/components/Field.d.ts +16 -5
- package/dist/components/Field.js +63 -8
- package/dist/components/IconButton.d.ts +14 -5
- package/dist/components/IconButton.js +25 -14
- package/dist/components/InfoTip.d.ts +12 -1
- package/dist/components/InfoTip.js +13 -2
- package/dist/components/Input.d.ts +14 -8
- package/dist/components/Input.js +46 -31
- package/dist/components/Loader.js +1 -1
- package/dist/components/MarkdownImpl.d.ts +1 -0
- package/dist/components/MarkdownImpl.js +1 -0
- package/dist/components/MenuEntries.d.ts +4 -0
- package/dist/components/MenuEntries.js +11 -12
- package/dist/components/Meter.js +5 -4
- package/dist/components/Modal.d.ts +28 -4
- package/dist/components/Modal.js +37 -14
- package/dist/components/Overview.d.ts +20 -12
- package/dist/components/Overview.js +14 -9
- package/dist/components/PageHeader.d.ts +47 -65
- package/dist/components/PageHeader.js +275 -153
- package/dist/components/PageHeaderAction.d.ts +67 -0
- package/dist/components/PageHeaderAction.js +107 -0
- package/dist/components/Pagination.js +6 -16
- package/dist/components/Popover.d.ts +4 -1
- package/dist/components/Popover.js +5 -3
- package/dist/components/Provider.d.ts +79 -19
- package/dist/components/Provider.js +140 -130
- package/dist/components/Radio.js +2 -1
- package/dist/components/RefreshButton.js +7 -8
- package/dist/components/RequiredMark.js +1 -1
- package/dist/components/Section.js +1 -1
- package/dist/components/Select.d.ts +4 -9
- package/dist/components/Select.js +24 -11
- package/dist/components/SettingModeOption.d.ts +1 -1
- package/dist/components/SettingModeOption.js +7 -1
- package/dist/components/SettingsCard.d.ts +5 -3
- package/dist/components/SettingsCard.js +27 -20
- package/dist/components/Slider.js +1 -1
- package/dist/components/StatusBadge.js +2 -2
- package/dist/components/Switch.js +5 -4
- package/dist/components/Table.d.ts +30 -20
- package/dist/components/Table.js +50 -73
- package/dist/components/Tabs.d.ts +3 -7
- package/dist/components/Tabs.js +6 -10
- package/dist/components/ThemeEditor.js +18 -8
- package/dist/components/Toast.d.ts +11 -3
- package/dist/components/Toast.js +45 -20
- package/dist/components/buttonClasses.js +1 -1
- package/dist/components/comboboxItems.d.ts +4 -0
- package/dist/components/comboboxItems.js +6 -1
- package/dist/components/dismiss.d.ts +12 -0
- package/dist/components/dismiss.js +19 -0
- package/dist/components/fieldBox.d.ts +4 -0
- package/dist/components/fieldBox.js +7 -3
- package/dist/components/floating.d.ts +3 -3
- package/dist/components/floating.js +1 -1
- package/dist/components/focus.d.ts +5 -0
- package/dist/components/focus.js +21 -1
- package/dist/components/markdownCodeBreaks.js +3 -4
- package/dist/components/pageHeaderStore.d.ts +41 -0
- package/dist/components/pageHeaderStore.js +67 -0
- package/dist/components/pathBreaks.d.ts +8 -0
- package/dist/components/pathBreaks.js +28 -0
- package/dist/components/segmented.js +1 -1
- package/dist/components/shortcutKey.d.ts +2 -1
- package/dist/components/shortcutKey.js +19 -2
- package/dist/components/splitter.js +1 -1
- package/dist/components/useCssIsDark.js +40 -18
- package/dist/components/useTheme.js +9 -10
- package/dist/dev.d.ts +9 -0
- package/dist/dev.js +14 -0
- package/dist/editor/Editor.d.ts +3 -2
- package/dist/editor/Editor.js +24 -5
- package/dist/editor/EditorField.d.ts +2 -1
- package/dist/editor/EditorField.js +11 -29
- package/dist/editor/FormCodePanel.d.ts +7 -3
- package/dist/editor/FormCodePanel.js +14 -7
- package/dist/editor/Monaco.d.ts +22 -6
- package/dist/editor/Monaco.js +177 -113
- package/dist/editor/editorModel.d.ts +1 -1
- package/dist/editor/editorModel.js +1 -1
- package/dist/editor/index.d.ts +6 -5
- package/dist/editor/index.js +5 -4
- package/dist/editor/languages.d.ts +24 -0
- package/dist/editor/languages.js +31 -0
- package/dist/editor/monacoFeatures.d.ts +7 -0
- package/dist/editor/monacoFeatures.js +19 -0
- package/dist/editor/themes.d.ts +8 -5
- package/dist/editor/themes.js +65 -32
- package/dist/editor/workers.d.ts +3 -3
- package/dist/editor/workers.js +8 -4
- package/dist/editor/yaml.d.ts +6 -2
- package/dist/editor/yaml.js +11 -2
- package/dist/editor/yamlLanguage.js +55 -38
- package/dist/file-explorer/FileExplorer.d.ts +18 -4
- package/dist/file-explorer/FileExplorer.js +204 -142
- package/dist/file-explorer/FileExplorerContext.js +1 -1
- package/dist/file-explorer/FilePreview/StreamedBadge.js +3 -1
- package/dist/file-explorer/FilePreview/Viewer.js +14 -10
- package/dist/file-explorer/FilePreview/index.js +4 -4
- package/dist/file-explorer/FileRow.d.ts +6 -2
- package/dist/file-explorer/FileRow.js +35 -17
- package/dist/file-explorer/FileRowsWindow.d.ts +2 -1
- package/dist/file-explorer/FileRowsWindow.js +36 -4
- package/dist/file-explorer/FileTable.d.ts +3 -1
- package/dist/file-explorer/FileTable.js +6 -3
- package/dist/file-explorer/TreeView.d.ts +5 -1
- package/dist/file-explorer/TreeView.js +86 -39
- package/dist/file-explorer/helpers.d.ts +2 -1
- package/dist/file-explorer/helpers.js +24 -23
- package/dist/file-explorer/lib/utils.d.ts +3 -0
- package/dist/file-explorer/lib/utils.js +6 -1
- package/dist/file-explorer/toasts.js +27 -19
- package/dist/file-explorer/treeRows.d.ts +6 -0
- package/dist/file-explorer/treeRows.js +19 -1
- package/dist/file-explorer/useNearEnd.js +3 -6
- package/dist/form/Fields/CheckboxGroupField.js +12 -5
- package/dist/form/Fields/DurationField.js +1 -1
- package/dist/form/Fields/LabelField.js +1 -1
- package/dist/form/Fields/ObjectField.d.ts +1 -1
- package/dist/form/Fields/ObjectField.js +3 -2
- package/dist/form/Fields/StringField.js +16 -29
- package/dist/form/Fields/TextAreaField.js +3 -4
- package/dist/form/Form.js +3 -8
- package/dist/form/Wizard/WizardNavigation.js +0 -3
- package/dist/form/Wizard/utils.js +2 -1
- package/dist/form/fieldFrame.d.ts +1 -1
- package/dist/form/fieldFrame.js +2 -2
- package/dist/form/types/fieldComponentTypes.d.ts +2 -1
- package/dist/graph/AnnotationBanner.d.ts +1 -2
- package/dist/graph/AnnotationBanner.js +30 -32
- package/dist/graph/Connectors.js +4 -1
- package/dist/graph/DependencyGraph.js +2 -2
- package/dist/graph/GraphEditor.js +11 -9
- package/dist/graph/GraphEditorDialogs.js +1 -1
- package/dist/graph/GraphNode.js +7 -4
- package/dist/graph/InputDialog.js +1 -1
- package/dist/graph/InputsEditor.js +14 -11
- package/dist/graph/MatrixGroup.js +9 -3
- package/dist/graph/Reveal.js +18 -10
- package/dist/graph/ShortcutsButton.d.ts +11 -2
- package/dist/graph/ShortcutsButton.js +14 -3
- package/dist/graph/TreeView.js +35 -18
- package/dist/graph/WorkflowSettingsDialog.js +1 -1
- package/dist/graph/editorFields.js +1 -1
- package/dist/graph/editorRows.js +16 -10
- package/dist/graph/editorStrings.d.ts +7 -0
- package/dist/graph/editorStrings.js +8 -1
- package/dist/graph/inputRefs.js +1 -1
- package/dist/graph/settingsViews.js +11 -18
- package/dist/graph/stepWith.js +1 -1
- package/dist/icons.d.ts +2 -0
- package/dist/icons.js +4 -2
- package/dist/index.d.ts +12 -12
- package/dist/index.js +16 -16
- package/dist/list/ActionToolbar.d.ts +42 -6
- package/dist/list/ActionToolbar.js +298 -101
- package/dist/list/ListPage.d.ts +15 -23
- package/dist/list/ListPage.js +42 -43
- package/dist/list/ListTable.d.ts +37 -7
- package/dist/list/ListTable.js +52 -12
- package/dist/list/ListViewControls.js +1 -1
- package/dist/list/UserHoverCard.js +7 -15
- package/dist/list/index.d.ts +2 -2
- package/dist/list/index.js +2 -2
- package/dist/list/listSearch.js +12 -10
- package/dist/list/listView.d.ts +10 -1
- package/dist/list/listView.js +15 -1
- package/dist/logviewer/LogViewer.d.ts +4 -2
- package/dist/logviewer/LogViewer.js +87 -69
- package/dist/logviewer/logviewer.css +3 -2
- package/dist/reducedMotion.d.ts +5 -0
- package/dist/reducedMotion.js +20 -0
- package/dist/safeUrl.d.ts +1 -1
- package/dist/safeUrl.js +4 -2
- package/dist/styles.css +1 -1
- package/package.json +5 -2
- package/dist/components/Breadcrumbs.d.ts +0 -37
- package/dist/components/Breadcrumbs.js +0 -90
- package/dist/components/CopyToClipboard.d.ts +0 -17
- package/dist/components/CopyToClipboard.js +0 -66
- package/dist/components/Sort.d.ts +0 -9
- package/dist/components/Sort.js +0 -42
- package/dist/file-explorer/FilePreview/CodePreviewEditor.d.ts +0 -6
- package/dist/file-explorer/FilePreview/CodePreviewEditor.js +0 -64
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { cx } from "../../cx.js";
|
|
2
2
|
import { ChevronLeftIcon, ChevronRightIcon, CloseIcon, DownloadIcon, FileIcon, LinkIcon, ZoomInIcon, ZoomOutIcon } from "../../icons.js";
|
|
3
3
|
import { usePersistedState, useStrings } from "../../components/Provider.js";
|
|
4
|
-
import { Modal } from "../../components/Modal.js";
|
|
5
4
|
import { IconButton } from "../../components/IconButton.js";
|
|
5
|
+
import { Modal } from "../../components/Modal.js";
|
|
6
6
|
import { formatBytes } from "../../format.js";
|
|
7
7
|
import { SegmentedControl } from "../../components/SegmentedControl.js";
|
|
8
8
|
import { getPreviewKind } from "./previewType.js";
|
|
@@ -20,7 +20,7 @@ function isDocumentView(kind) {
|
|
|
20
20
|
function FilePreview({ open, node, siblings, storage, getPresignedUrl, onNavigate, onClose, onDownload, onCopyUri }) {
|
|
21
21
|
const t = useStrings().fileExplorer;
|
|
22
22
|
const { common } = useStrings();
|
|
23
|
-
const [style, setStyle] = usePersistedState("fileExplorer.previewStyle", "
|
|
23
|
+
const [style, setStyle] = usePersistedState("fileExplorer.previewStyle", "compact");
|
|
24
24
|
const [zoom, setZoom] = useState(100);
|
|
25
25
|
const { url, loading: urlLoading, error: urlError } = usePresignedUrl(node, getPresignedUrl, {
|
|
26
26
|
enabled: open,
|
|
@@ -67,8 +67,8 @@ function FilePreview({ open, node, siblings, storage, getPresignedUrl, onNavigat
|
|
|
67
67
|
value: style,
|
|
68
68
|
onValueChange: setStyle,
|
|
69
69
|
options: [{
|
|
70
|
-
value: "
|
|
71
|
-
label: t.preview.
|
|
70
|
+
value: "compact",
|
|
71
|
+
label: t.preview.compact
|
|
72
72
|
}, {
|
|
73
73
|
value: "overlay",
|
|
74
74
|
label: t.preview.fullWindow
|
|
@@ -16,6 +16,8 @@ interface FileRowProps {
|
|
|
16
16
|
rowIndex: number;
|
|
17
17
|
/** The keyboard cursor row. Focus stays on the container, so nothing else marks it. */
|
|
18
18
|
active: boolean;
|
|
19
|
+
/** Makes the row a link to the node. */
|
|
20
|
+
href?: string | undefined;
|
|
19
21
|
checked: boolean;
|
|
20
22
|
canPreview: boolean;
|
|
21
23
|
getItems: () => MenuEntry[];
|
|
@@ -24,13 +26,15 @@ interface FileRowProps {
|
|
|
24
26
|
onToggleCheck: (node: TreeNode, checked: boolean) => void;
|
|
25
27
|
onPreview: (node: TreeNode) => void;
|
|
26
28
|
}
|
|
27
|
-
export declare function UserRow({ node, icon, storageCount, getItems, openMenu, onOpen, }: {
|
|
29
|
+
export declare function UserRow({ node, icon, href, storageCount, getItems, openMenu, onOpen, }: {
|
|
28
30
|
node: TreeNode;
|
|
29
31
|
icon: ReactNode;
|
|
32
|
+
/** Makes the row a link to the user. */
|
|
33
|
+
href?: string | undefined;
|
|
30
34
|
storageCount: number;
|
|
31
35
|
getItems: () => MenuEntry[];
|
|
32
36
|
openMenu: OpenMenu;
|
|
33
37
|
onOpen: (node: TreeNode) => void;
|
|
34
38
|
}): import("react").JSX.Element;
|
|
35
|
-
export declare function FileRow({ node, icon, rowId, rowIndex, active, checked, canPreview, getItems, openMenu, onOpen, onToggleCheck, onPreview, }: FileRowProps): import("react").JSX.Element;
|
|
39
|
+
export declare function FileRow({ node, icon, rowId, rowIndex, active, href, checked, canPreview, getItems, openMenu, onOpen, onToggleCheck, onPreview, }: FileRowProps): import("react").JSX.Element;
|
|
36
40
|
export {};
|
|
@@ -2,10 +2,11 @@ import { cx } from "../cx.js";
|
|
|
2
2
|
import { PreviewIcon } from "../icons.js";
|
|
3
3
|
import { Tooltip } from "../components/Tooltip.js";
|
|
4
4
|
import { useStrings } from "../components/Provider.js";
|
|
5
|
+
import { AppLink, isPlainClick } from "../components/AppLink.js";
|
|
5
6
|
import { formatBytes } from "../format.js";
|
|
6
7
|
import { RelativeTime } from "../components/relativeTime.js";
|
|
7
8
|
import { Table } from "../components/Table.js";
|
|
8
|
-
import { RowSelectCheckbox } from "../list/ListTable.js";
|
|
9
|
+
import { RowSelectCheckbox, STRETCHED_LINK } from "../list/ListTable.js";
|
|
9
10
|
import { MoreButton, rowContextMenuProps, useRowMenuActive } from "../list/rowMenu.js";
|
|
10
11
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
12
|
import { DateTime } from "luxon";
|
|
@@ -20,16 +21,31 @@ function fileRowHeight(node) {
|
|
|
20
21
|
function isInteractiveTarget(e) {
|
|
21
22
|
return !!e.target.closest("a,button,input,label");
|
|
22
23
|
}
|
|
23
|
-
|
|
24
|
+
/** Opens the row on a click its controls don't take. Beside a link, which
|
|
25
|
+
* covers the row, that is only a click on a gap the link leaves, and a
|
|
26
|
+
* modified one there asks for a new tab, which only the link can give. */
|
|
27
|
+
function rowClickHandler(onOpen, href) {
|
|
28
|
+
return (e) => {
|
|
29
|
+
if (isInteractiveTarget(e) || href && !isPlainClick(e)) return;
|
|
30
|
+
onOpen();
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/** A row's name: with an href, the link that covers the row, so the whole row
|
|
34
|
+
* opens in a new tab, shows its URL and reads as a link. */
|
|
35
|
+
function RowName({ name, href }) {
|
|
36
|
+
return href ? /* @__PURE__ */ jsx(AppLink, {
|
|
37
|
+
href,
|
|
38
|
+
"data-row-link": true,
|
|
39
|
+
className: STRETCHED_LINK,
|
|
40
|
+
children: name
|
|
41
|
+
}) : name;
|
|
42
|
+
}
|
|
43
|
+
function UserRow({ node, icon, href, storageCount, getItems, openMenu, onOpen }) {
|
|
24
44
|
const t = useStrings().fileExplorer;
|
|
25
45
|
const { active: menuActive, openMenu: openRowMenu } = useRowMenuActive(openMenu);
|
|
26
|
-
const handleRowClick = (e) => {
|
|
27
|
-
if (isInteractiveTarget(e)) return;
|
|
28
|
-
onOpen(node);
|
|
29
|
-
};
|
|
30
46
|
return /* @__PURE__ */ jsxs("tr", {
|
|
31
|
-
className: cx("group cursor-pointer", menuActive ? "theme-hover" : "hover:theme-hover"),
|
|
32
|
-
onClick:
|
|
47
|
+
className: cx("group cursor-pointer", menuActive ? "theme-hover" : "hover:theme-hover", href && "relative [&_:is(a,button,input,label,[role=checkbox]):not([data-row-link])]:relative [&_:is(a,button,input,label,[role=checkbox]):not([data-row-link])]:z-[1]"),
|
|
48
|
+
onClick: rowClickHandler(() => onOpen(node), href),
|
|
33
49
|
...rowContextMenuProps(openRowMenu, getItems),
|
|
34
50
|
children: [
|
|
35
51
|
/* @__PURE__ */ jsx(Table.Item, {
|
|
@@ -41,7 +57,10 @@ function UserRow({ node, icon, storageCount, getItems, openMenu, onOpen }) {
|
|
|
41
57
|
children: icon
|
|
42
58
|
}), /* @__PURE__ */ jsx("span", {
|
|
43
59
|
className: "truncate text-[13px] font-medium text-(--theme-app)",
|
|
44
|
-
children:
|
|
60
|
+
children: /* @__PURE__ */ jsx(RowName, {
|
|
61
|
+
name: node.displayName || node.name,
|
|
62
|
+
href
|
|
63
|
+
})
|
|
45
64
|
})]
|
|
46
65
|
})
|
|
47
66
|
}),
|
|
@@ -66,22 +85,18 @@ function UserRow({ node, icon, storageCount, getItems, openMenu, onOpen }) {
|
|
|
66
85
|
]
|
|
67
86
|
});
|
|
68
87
|
}
|
|
69
|
-
function FileRow({ node, icon, rowId, rowIndex, active, checked, canPreview, getItems, openMenu, onOpen, onToggleCheck, onPreview }) {
|
|
88
|
+
function FileRow({ node, icon, rowId, rowIndex, active, href, checked, canPreview, getItems, openMenu, onOpen, onToggleCheck, onPreview }) {
|
|
70
89
|
const t = useStrings().fileExplorer;
|
|
71
90
|
const { active: menuActive, openMenu: openRowMenu } = useRowMenuActive(openMenu);
|
|
72
91
|
const isDir = node.type === "directory";
|
|
73
92
|
const modifiedDt = node.modified ? DateTime.fromISO(node.modified) : null;
|
|
74
93
|
const typeLabel = node.root ? node.storageType ?? node.type : node.type;
|
|
75
|
-
const handleRowClick = (e) => {
|
|
76
|
-
if (isInteractiveTarget(e)) return;
|
|
77
|
-
onOpen(node);
|
|
78
|
-
};
|
|
79
94
|
return /* @__PURE__ */ jsxs("tr", {
|
|
80
95
|
id: rowId,
|
|
81
96
|
"aria-rowindex": rowIndex,
|
|
82
97
|
"aria-selected": checked,
|
|
83
|
-
className: cx("group", checked ? "bg-
|
|
84
|
-
onClick:
|
|
98
|
+
className: cx("group", checked ? "bg-(--theme-accent-subtle)/60" : menuActive ? "theme-hover" : "hover:theme-hover", active && "group-focus-visible/grid:ring-1 group-focus-visible/grid:ring-inset group-focus-visible/grid:ring-(--theme-link)", "cursor-pointer", href && "relative [&_:is(a,button,input,label,[role=checkbox]):not([data-row-link])]:relative [&_:is(a,button,input,label,[role=checkbox]):not([data-row-link])]:z-[1]"),
|
|
99
|
+
onClick: rowClickHandler(() => onOpen(node), href),
|
|
85
100
|
style: { height: fileRowHeight(node) },
|
|
86
101
|
...rowContextMenuProps(openRowMenu, getItems),
|
|
87
102
|
children: [
|
|
@@ -103,7 +118,10 @@ function FileRow({ node, icon, rowId, rowIndex, active, checked, canPreview, get
|
|
|
103
118
|
className: "min-w-0",
|
|
104
119
|
children: [/* @__PURE__ */ jsx("div", {
|
|
105
120
|
className: "truncate text-[13px] font-medium leading-tight text-(--theme-app)",
|
|
106
|
-
children:
|
|
121
|
+
children: /* @__PURE__ */ jsx(RowName, {
|
|
122
|
+
name: node.displayName || node.name,
|
|
123
|
+
href
|
|
124
|
+
})
|
|
107
125
|
}), isTwoLineRow(node) && /* @__PURE__ */ jsx("div", {
|
|
108
126
|
className: "truncate text-xs leading-tight text-(--theme-muted-text-color)",
|
|
109
127
|
children: node.contentType
|
|
@@ -30,8 +30,9 @@ interface FileRowsWindowProps {
|
|
|
30
30
|
onOpen: (node: TreeNode) => void;
|
|
31
31
|
onToggleCheck: (node: TreeNode, checked: boolean) => void;
|
|
32
32
|
onPreview: (node: TreeNode) => void;
|
|
33
|
+
pathHref?: ((path: string) => string) | undefined;
|
|
33
34
|
}
|
|
34
35
|
/** Must stay a single child of `<Table>`: Table re-keys children by traversal
|
|
35
36
|
* index, so a count that varied with scroll position would remount every row. */
|
|
36
|
-
export declare function FileRowsWindow({ items, padTop, padBottom, nodes, checkedItems, activeIndex, rowIdPrefix, scrollEl, loadMore, getItems, openMenu, onOpen, onToggleCheck, onPreview, }: FileRowsWindowProps): import("react").JSX.Element;
|
|
37
|
+
export declare function FileRowsWindow({ items, padTop, padBottom, nodes, checkedItems, activeIndex, rowIdPrefix, scrollEl, loadMore, getItems, openMenu, onOpen, onToggleCheck, onPreview, pathHref, }: FileRowsWindowProps): import("react").JSX.Element;
|
|
37
38
|
export {};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { FileIcon, FolderIcon } from "../icons.js";
|
|
2
|
+
import { isDev } from "../dev.js";
|
|
2
3
|
import { canPreview } from "./FilePreview/previewType.js";
|
|
3
4
|
import { getNodeIcon } from "./helpers.js";
|
|
4
|
-
import { FileRow } from "./FileRow.js";
|
|
5
|
+
import { FileRow, ROW_HEIGHT_TALL, isTwoLineRow } from "./FileRow.js";
|
|
5
6
|
import { LoadMoreRow } from "./LoadMoreRow.js";
|
|
6
7
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
8
|
import { useEffect } from "react";
|
|
@@ -22,7 +23,7 @@ function spacerHeights(items, totalSize, scrollMargin) {
|
|
|
22
23
|
}
|
|
23
24
|
/** Must stay a single child of `<Table>`: Table re-keys children by traversal
|
|
24
25
|
* index, so a count that varied with scroll position would remount every row. */
|
|
25
|
-
function FileRowsWindow({ items, padTop, padBottom, nodes, checkedItems, activeIndex, rowIdPrefix, scrollEl, loadMore, getItems, openMenu, onOpen, onToggleCheck, onPreview }) {
|
|
26
|
+
function FileRowsWindow({ items, padTop, padBottom, nodes, checkedItems, activeIndex, rowIdPrefix, scrollEl, loadMore, getItems, openMenu, onOpen, onToggleCheck, onPreview, pathHref }) {
|
|
26
27
|
useNestedScrollerWarning(scrollEl, nodes.length);
|
|
27
28
|
useRowPitchAudit(scrollEl, nodes, items);
|
|
28
29
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -43,6 +44,7 @@ function FileRowsWindow({ items, padTop, padBottom, nodes, checkedItems, activeI
|
|
|
43
44
|
rowId: `${rowIdPrefix}-${item.index}`,
|
|
44
45
|
rowIndex: item.index + 2,
|
|
45
46
|
active: item.index === activeIndex,
|
|
47
|
+
href: pathHref?.(node.path),
|
|
46
48
|
node,
|
|
47
49
|
icon,
|
|
48
50
|
checked: checkedItems.has(node.path),
|
|
@@ -73,13 +75,43 @@ function Spacer({ height }) {
|
|
|
73
75
|
/** A nested overflow container stealing the scroll leaves the virtualizer at
|
|
74
76
|
* scrollTop 0 forever, rendering only the first window. */
|
|
75
77
|
function useNestedScrollerWarning(scrollEl, count) {
|
|
76
|
-
useEffect(() => {
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (!isDev() || !scrollEl || count <= 100) return;
|
|
80
|
+
if (scrollEl.scrollHeight <= scrollEl.clientHeight) console.warn("FileRowsWindow: scrollEl is not scrolling — a nested overflow container likely stole it, so only the first window will ever render.");
|
|
81
|
+
}, [scrollEl, count]);
|
|
82
|
+
}
|
|
83
|
+
/** Pitch per kind, from the first adjacent same-kind pair: a mixed pair says
|
|
84
|
+
* nothing about either one. */
|
|
85
|
+
function measurePitches(rows) {
|
|
86
|
+
const pitches = /* @__PURE__ */ new Map();
|
|
87
|
+
rows.forEach((row, i) => {
|
|
88
|
+
const next = rows[i + 1];
|
|
89
|
+
if (next && next.kind === row.kind && !pitches.has(row.kind)) pitches.set(row.kind, next.top - row.top);
|
|
90
|
+
});
|
|
91
|
+
return pitches;
|
|
77
92
|
}
|
|
78
93
|
/** The constants must equal the row *pitch*, not the row's own height —
|
|
79
94
|
* under border-collapse the shared border sits between rows. A 1px error
|
|
80
95
|
* compounds to 10,000px across a 10k-row directory. */
|
|
81
96
|
function useRowPitchAudit(scrollEl, nodes, items) {
|
|
82
|
-
useEffect(() => {
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
if (!isDev() || !scrollEl || items.length < 3) return;
|
|
99
|
+
const rows = [...scrollEl.querySelectorAll("tbody tr")].filter((row) => !row.hasAttribute("aria-hidden")).map((row, i) => {
|
|
100
|
+
const node = nodes[items[i]?.index ?? -1];
|
|
101
|
+
return node ? {
|
|
102
|
+
kind: isTwoLineRow(node) ? "tall" : "short",
|
|
103
|
+
top: row.getBoundingClientRect().top
|
|
104
|
+
} : null;
|
|
105
|
+
}).filter((row) => row !== null);
|
|
106
|
+
const expected = {
|
|
107
|
+
short: 45,
|
|
108
|
+
tall: ROW_HEIGHT_TALL
|
|
109
|
+
};
|
|
110
|
+
for (const [kind, measured] of measurePitches(rows)) if (measured > 0 && Math.abs(measured - expected[kind]) > .5) {
|
|
111
|
+
const name = `ROW_HEIGHT_${kind.toUpperCase()}`;
|
|
112
|
+
console.warn(`FileRowsWindow: ${name} is ${expected[kind]} but the rendered pitch is ${measured}. Set ${name} = ${measured} in FileRow.tsx, or the scrollbar will not reach the last row.`);
|
|
113
|
+
}
|
|
114
|
+
}, [
|
|
83
115
|
scrollEl,
|
|
84
116
|
nodes,
|
|
85
117
|
items
|
|
@@ -13,11 +13,13 @@ interface FileTableProps {
|
|
|
13
13
|
onOpen: (node: TreeNode) => void;
|
|
14
14
|
onToggleCheck: (node: TreeNode, checked: boolean) => void;
|
|
15
15
|
onPreview: (node: TreeNode) => void;
|
|
16
|
+
/** The URL of a path; set, each row is a link to its node. */
|
|
17
|
+
pathHref?: ((path: string) => string) | undefined;
|
|
16
18
|
/** Set when the folder has pages left to fetch. */
|
|
17
19
|
loadMore?: LoadMoreState | undefined;
|
|
18
20
|
}
|
|
19
21
|
/** The file table. Give it a `key` of the folder path: a folder change must reset
|
|
20
22
|
* the cursor, the scroll offset and the virtualizer's measurements, and a remount
|
|
21
23
|
* does all three without a synchronising effect. */
|
|
22
|
-
export declare function FileTable({ nodes, checkedItems, allSelected, someSelected, onSelectAll, getItems, openMenu, onOpen, onToggleCheck, onPreview, loadMore, }: FileTableProps): import("react").JSX.Element;
|
|
24
|
+
export declare function FileTable({ nodes, checkedItems, allSelected, someSelected, onSelectAll, getItems, openMenu, onOpen, onToggleCheck, onPreview, pathHref, loadMore, }: FileTableProps): import("react").JSX.Element;
|
|
23
25
|
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cx } from "../cx.js";
|
|
2
|
+
import { useStrings } from "../components/Provider.js";
|
|
2
3
|
import { Table } from "../components/Table.js";
|
|
3
4
|
import { listTableFixedProps } from "../list/ListTable.js";
|
|
4
5
|
import { fileRowHeight } from "./FileRow.js";
|
|
@@ -11,11 +12,12 @@ import { useVirtualizer } from "@tanstack/react-virtual";
|
|
|
11
12
|
/** The file table. Give it a `key` of the folder path: a folder change must reset
|
|
12
13
|
* the cursor, the scroll offset and the virtualizer's measurements, and a remount
|
|
13
14
|
* does all three without a synchronising effect. */
|
|
14
|
-
function FileTable({ nodes, checkedItems, allSelected, someSelected, onSelectAll, getItems, openMenu, onOpen, onToggleCheck, onPreview, loadMore }) {
|
|
15
|
+
function FileTable({ nodes, checkedItems, allSelected, someSelected, onSelectAll, getItems, openMenu, onOpen, onToggleCheck, onPreview, pathHref, loadMore }) {
|
|
15
16
|
const [scrollEl, setScrollEl] = useState(null);
|
|
16
17
|
const [activeIndex, setActiveIndex] = useState(-1);
|
|
17
18
|
const [theadHeight, setTheadHeight] = useState(0);
|
|
18
19
|
const rowIdPrefix = useId();
|
|
20
|
+
const t = useStrings().fileExplorer;
|
|
19
21
|
useLayoutEffect(() => {
|
|
20
22
|
const thead = scrollEl?.querySelector("thead");
|
|
21
23
|
if (thead instanceof HTMLElement) setTheadHeight(thead.offsetHeight);
|
|
@@ -137,7 +139,7 @@ function FileTable({ nodes, checkedItems, allSelected, someSelected, onSelectAll
|
|
|
137
139
|
wrapperClassName: "overflow-x-auto",
|
|
138
140
|
tableProps: {
|
|
139
141
|
role: "grid",
|
|
140
|
-
"aria-label":
|
|
142
|
+
"aria-label": t.chrome.files,
|
|
141
143
|
"aria-rowcount": loadMore ? -1 : nodes.length + 1,
|
|
142
144
|
"aria-multiselectable": true,
|
|
143
145
|
tabIndex: 0,
|
|
@@ -163,7 +165,8 @@ function FileTable({ nodes, checkedItems, allSelected, someSelected, onSelectAll
|
|
|
163
165
|
openMenu,
|
|
164
166
|
onOpen: handleOpen,
|
|
165
167
|
onToggleCheck,
|
|
166
|
-
onPreview
|
|
168
|
+
onPreview,
|
|
169
|
+
pathHref
|
|
167
170
|
})]
|
|
168
171
|
})
|
|
169
172
|
});
|
|
@@ -13,6 +13,10 @@ interface TreeViewProps {
|
|
|
13
13
|
onDropToFolder: (folderPath: string, files: FileList | DataTransferItemList) => void;
|
|
14
14
|
onContextMenu: (e: React.MouseEvent, node: TreeNode) => void;
|
|
15
15
|
onPreviewFile: (node: TreeNode) => void;
|
|
16
|
+
/** The URL of a path; set, each row is a link to its path. */
|
|
17
|
+
pathHref?: ((path: string) => string) | undefined;
|
|
18
|
+
/** A plain click on a row's link, which moves to its path on its own. */
|
|
19
|
+
onFollowLink?: ((node: TreeNode) => void) | undefined;
|
|
16
20
|
/** Directories with pages left to fetch, and how to fetch them. */
|
|
17
21
|
paging?: {
|
|
18
22
|
incompletePaths: ReadonlySet<string>;
|
|
@@ -23,5 +27,5 @@ interface TreeViewProps {
|
|
|
23
27
|
}
|
|
24
28
|
/** The sidebar tree, flattened and windowed. The flat array is the single source
|
|
25
29
|
* of truth for rendering, arrow keys and ARIA. */
|
|
26
|
-
export declare function TreeView({ rootNodes, parentToChildrenMap, expandedPaths, selectedPath, loadingPaths, onToggleExpand, onSelect, onDropToFolder, onContextMenu, onPreviewFile, paging, }: TreeViewProps): import("react").JSX.Element;
|
|
30
|
+
export declare function TreeView({ rootNodes, parentToChildrenMap, expandedPaths, selectedPath, loadingPaths, onToggleExpand, onSelect, onDropToFolder, onContextMenu, onPreviewFile, pathHref, onFollowLink, paging, }: TreeViewProps): import("react").JSX.Element;
|
|
27
31
|
export {};
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { cx } from "../cx.js";
|
|
2
2
|
import { ChevronRightIcon, FolderIcon, FolderOpenIcon } from "../icons.js";
|
|
3
|
+
import { isDev } from "../dev.js";
|
|
3
4
|
import { positionKeys } from "../components/keys.js";
|
|
4
5
|
import { useStrings } from "../components/Provider.js";
|
|
6
|
+
import { AppLink, isPlainClick } from "../components/AppLink.js";
|
|
5
7
|
import { Skeleton } from "../components/Skeleton.js";
|
|
6
|
-
import { getParentPath } from "./lib/utils.js";
|
|
8
|
+
import { carriesFiles, getParentPath } from "./lib/utils.js";
|
|
7
9
|
import { canPreview } from "./FilePreview/previewType.js";
|
|
8
10
|
import { getNodeIcon } from "./helpers.js";
|
|
9
|
-
import { findRowIndex, flattenTreeRows, nextFileLimit, stepRow } from "./treeRows.js";
|
|
11
|
+
import { edgeRow, findRowIndex, firstChildRow, flattenTreeRows, nextFileLimit, parentRow, stepRow } from "./treeRows.js";
|
|
10
12
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
11
13
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
12
14
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
@@ -22,8 +24,9 @@ function rowHeight(row) {
|
|
|
22
24
|
}
|
|
23
25
|
/** The sidebar tree, flattened and windowed. The flat array is the single source
|
|
24
26
|
* of truth for rendering, arrow keys and ARIA. */
|
|
25
|
-
function TreeView({ rootNodes, parentToChildrenMap, expandedPaths, selectedPath, loadingPaths, onToggleExpand, onSelect, onDropToFolder, onContextMenu, onPreviewFile, paging }) {
|
|
27
|
+
function TreeView({ rootNodes, parentToChildrenMap, expandedPaths, selectedPath, loadingPaths, onToggleExpand, onSelect, onDropToFolder, onContextMenu, onPreviewFile, pathHref, onFollowLink, paging }) {
|
|
26
28
|
const [scrollEl, setScrollEl] = useState(null);
|
|
29
|
+
const treeLabel = useStrings().fileExplorer.chrome.folders;
|
|
27
30
|
const [fileLimits, setFileLimits] = useState(() => /* @__PURE__ */ new Map());
|
|
28
31
|
const incompletePaths = paging?.incompletePaths;
|
|
29
32
|
const rows = useMemo(() => flattenTreeRows(rootNodes, parentToChildrenMap, expandedPaths, {
|
|
@@ -73,11 +76,11 @@ function TreeView({ rootNodes, parentToChildrenMap, expandedPaths, selectedPath,
|
|
|
73
76
|
getItemKey: getRowKey,
|
|
74
77
|
overscan: 10
|
|
75
78
|
});
|
|
76
|
-
const move = useCallback((from,
|
|
79
|
+
const move = useCallback((from, pick) => {
|
|
77
80
|
const cursorIndex = findRowIndex(rows, cursorRef.current ?? "");
|
|
78
81
|
const index = cursorIndex >= 0 ? cursorIndex : findRowIndex(rows, from.path);
|
|
79
82
|
if (index === -1) return;
|
|
80
|
-
const target =
|
|
83
|
+
const target = pick(index);
|
|
81
84
|
if (!target) return;
|
|
82
85
|
cursorRef.current = target.node.path;
|
|
83
86
|
pendingNavRef.current.add(target.node.path);
|
|
@@ -110,14 +113,23 @@ function TreeView({ rootNodes, parentToChildrenMap, expandedPaths, selectedPath,
|
|
|
110
113
|
onPreviewFile(node);
|
|
111
114
|
return;
|
|
112
115
|
}
|
|
113
|
-
|
|
116
|
+
const target = {
|
|
117
|
+
ArrowDown: (i) => stepRow(rows, i, 1),
|
|
118
|
+
ArrowUp: (i) => stepRow(rows, i, -1),
|
|
119
|
+
ArrowRight: (i) => firstChildRow(rows, i),
|
|
120
|
+
ArrowLeft: (i) => parentRow(rows, i),
|
|
121
|
+
Home: () => edgeRow(rows, "first"),
|
|
122
|
+
End: () => edgeRow(rows, "last")
|
|
123
|
+
}[e.key];
|
|
124
|
+
if (target) {
|
|
114
125
|
e.preventDefault();
|
|
115
|
-
move(node,
|
|
116
|
-
} else if (e.key === "ArrowUp") {
|
|
117
|
-
e.preventDefault();
|
|
118
|
-
move(node, -1);
|
|
126
|
+
move(node, target);
|
|
119
127
|
}
|
|
120
|
-
}, [
|
|
128
|
+
}, [
|
|
129
|
+
move,
|
|
130
|
+
onPreviewFile,
|
|
131
|
+
rows
|
|
132
|
+
]);
|
|
121
133
|
const items = virtualizer.getVirtualItems();
|
|
122
134
|
useTreePitchAudit(scrollEl, rows, items);
|
|
123
135
|
const renderRow = (index, top, height) => {
|
|
@@ -140,6 +152,8 @@ function TreeView({ rootNodes, parentToChildrenMap, expandedPaths, selectedPath,
|
|
|
140
152
|
}) : /* @__PURE__ */ jsx(TreeRowItem, {
|
|
141
153
|
row,
|
|
142
154
|
isSelected: row.node.path === selectedPath,
|
|
155
|
+
href: pathHref?.(row.node.path),
|
|
156
|
+
onFollowLink,
|
|
143
157
|
onToggleExpand,
|
|
144
158
|
onSelect,
|
|
145
159
|
onDropToFolder,
|
|
@@ -167,6 +181,7 @@ function TreeView({ rootNodes, parentToChildrenMap, expandedPaths, selectedPath,
|
|
|
167
181
|
ref: setScrollEl,
|
|
168
182
|
className: "overflow-auto flex-1 px-2 py-2",
|
|
169
183
|
role: "tree",
|
|
184
|
+
"aria-label": treeLabel,
|
|
170
185
|
onDragOver: (e) => e.preventDefault(),
|
|
171
186
|
onDrop: (e) => e.preventDefault(),
|
|
172
187
|
children: /* @__PURE__ */ jsx("div", {
|
|
@@ -212,7 +227,7 @@ function TreeMoreRow({ depth, hiddenFiles, partial, loading, failed, onReveal })
|
|
|
212
227
|
style: { paddingLeft: depth * INDENT_PER_LEVEL + ROW_PAD_FILE },
|
|
213
228
|
children: loading ? /* @__PURE__ */ jsx("span", { children: t.loadingMore }) : /* @__PURE__ */ jsx("button", {
|
|
214
229
|
type: "button",
|
|
215
|
-
className: cx("cursor-pointer underline", failed ? "text-
|
|
230
|
+
className: cx("cursor-pointer underline", failed ? "text-(--theme-error-text)" : "theme-link"),
|
|
216
231
|
onClick: onReveal,
|
|
217
232
|
children: label()
|
|
218
233
|
})
|
|
@@ -257,6 +272,7 @@ function useSpringLoadedDrop({ node, dropPath, isDirectory, isExpanded, onToggle
|
|
|
257
272
|
dragHandlers: {
|
|
258
273
|
onDragEnter: (e) => {
|
|
259
274
|
e.preventDefault();
|
|
275
|
+
if (!carriesFiles(e.dataTransfer)) return;
|
|
260
276
|
e.stopPropagation();
|
|
261
277
|
hoverCount.current += 1;
|
|
262
278
|
setDragActive(true);
|
|
@@ -268,6 +284,7 @@ function useSpringLoadedDrop({ node, dropPath, isDirectory, isExpanded, onToggle
|
|
|
268
284
|
onDragOver: (e) => e.preventDefault(),
|
|
269
285
|
onDragLeave: (e) => {
|
|
270
286
|
e.preventDefault();
|
|
287
|
+
if (!carriesFiles(e.dataTransfer)) return;
|
|
271
288
|
e.stopPropagation();
|
|
272
289
|
hoverCount.current -= 1;
|
|
273
290
|
if (hoverCount.current > 0) return;
|
|
@@ -275,8 +292,9 @@ function useSpringLoadedDrop({ node, dropPath, isDirectory, isExpanded, onToggle
|
|
|
275
292
|
clear();
|
|
276
293
|
},
|
|
277
294
|
onDrop: (e) => {
|
|
278
|
-
e.stopPropagation();
|
|
279
295
|
e.preventDefault();
|
|
296
|
+
if (!carriesFiles(e.dataTransfer)) return;
|
|
297
|
+
e.stopPropagation();
|
|
280
298
|
hoverCount.current = 0;
|
|
281
299
|
setDragActive(false);
|
|
282
300
|
clear();
|
|
@@ -285,11 +303,12 @@ function useSpringLoadedDrop({ node, dropPath, isDirectory, isExpanded, onToggle
|
|
|
285
303
|
}
|
|
286
304
|
};
|
|
287
305
|
}
|
|
288
|
-
/** Keys a row handles itself
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
function dispatchRowKey(e, { node, isDirectory, isExpanded, onSelect, onToggleExpand, onDelegate }) {
|
|
306
|
+
/** Keys a row handles itself: Enter, Space, and Right or Left on a folder that
|
|
307
|
+
* can open or close. The keys that move between rows delegate upward, where
|
|
308
|
+
* the flat array is. There is no typeahead. */
|
|
309
|
+
function dispatchRowKey(e, { node, isDirectory, isExpanded, isLink, onSelect, onToggleExpand, onDelegate }) {
|
|
292
310
|
if (e.key === "Enter") {
|
|
311
|
+
if (isLink && (e.metaKey || e.ctrlKey || e.shiftKey)) return;
|
|
293
312
|
e.preventDefault();
|
|
294
313
|
onSelect(e);
|
|
295
314
|
return;
|
|
@@ -307,7 +326,7 @@ function dispatchRowKey(e, { node, isDirectory, isExpanded, onSelect, onToggleEx
|
|
|
307
326
|
}
|
|
308
327
|
onDelegate(e, node);
|
|
309
328
|
}
|
|
310
|
-
var TreeRowItem = memo(function TreeRowItem({ row, isSelected, onToggleExpand, onSelect, onDropToFolder, onContextMenu, onKeyDown }) {
|
|
329
|
+
var TreeRowItem = memo(function TreeRowItem({ row, isSelected, href, onFollowLink, onToggleExpand, onSelect, onDropToFolder, onContextMenu, onKeyDown }) {
|
|
311
330
|
const { node, depth, isExpanded } = row;
|
|
312
331
|
const isDirectory = node.type === "directory";
|
|
313
332
|
const { dragActive, dragHandlers } = useSpringLoadedDrop({
|
|
@@ -323,47 +342,75 @@ var TreeRowItem = memo(function TreeRowItem({ row, isSelected, onToggleExpand, o
|
|
|
323
342
|
onSelect(node.path, node);
|
|
324
343
|
if (isDirectory) onToggleExpand(node.path);
|
|
325
344
|
};
|
|
345
|
+
const handleLinkClick = (e) => {
|
|
346
|
+
if (!isPlainClick(e)) return;
|
|
347
|
+
onFollowLink?.(node);
|
|
348
|
+
if (isDirectory) onToggleExpand(node.path);
|
|
349
|
+
};
|
|
326
350
|
const handleKeyDown = (e) => dispatchRowKey(e, {
|
|
327
351
|
node,
|
|
328
352
|
isDirectory,
|
|
329
353
|
isExpanded,
|
|
354
|
+
isLink: !!href,
|
|
330
355
|
onSelect: handleSelect,
|
|
331
356
|
onToggleExpand,
|
|
332
357
|
onDelegate: onKeyDown
|
|
333
358
|
});
|
|
334
359
|
const customIcon = getNodeIcon(node);
|
|
335
360
|
const label = isDirectory && node.storageId && node.displayName ? node.displayName : node.name;
|
|
361
|
+
const tabIndex = isSelected ? 0 : -1;
|
|
362
|
+
const itemProps = {
|
|
363
|
+
className: cx("group relative flex items-center gap-1.5 pr-2 py-1 rounded-md cursor-pointer", "transition-colors duration-150", "focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-(--theme-link)", isSelected ? "bg-(--theme-accent)/15 font-medium" : dragActive ? "theme-hover" : "hover:theme-hover"),
|
|
364
|
+
style: indentStyle(depth, isDirectory),
|
|
365
|
+
onContextMenu: (e) => onContextMenu(e, node),
|
|
366
|
+
"aria-level": depth + 1,
|
|
367
|
+
"aria-posinset": row.posInSet,
|
|
368
|
+
"aria-setsize": row.setSize,
|
|
369
|
+
"aria-selected": isSelected,
|
|
370
|
+
...isDirectory && { "aria-expanded": isExpanded },
|
|
371
|
+
children: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(RowIcons, {
|
|
372
|
+
isDirectory,
|
|
373
|
+
isExpanded,
|
|
374
|
+
customIcon
|
|
375
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
376
|
+
className: "text-sm truncate",
|
|
377
|
+
children: label
|
|
378
|
+
})] })
|
|
379
|
+
};
|
|
336
380
|
return /* @__PURE__ */ jsxs("div", {
|
|
337
381
|
className: cx("relative h-full rounded-md transition-[box-shadow,background-color] duration-150", dragActive && "ring-1 ring-(--theme-link)/60"),
|
|
338
382
|
...dragHandlers,
|
|
339
|
-
children: [/* @__PURE__ */ jsx(IndentGuides, { depth }), /* @__PURE__ */
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
383
|
+
children: [/* @__PURE__ */ jsx(IndentGuides, { depth }), href ? /* @__PURE__ */ jsx(AppLink, {
|
|
384
|
+
...itemProps,
|
|
385
|
+
role: "treeitem",
|
|
386
|
+
tabIndex,
|
|
387
|
+
href,
|
|
388
|
+
onClick: handleLinkClick,
|
|
389
|
+
onKeyDown: handleKeyDown
|
|
390
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
391
|
+
...itemProps,
|
|
345
392
|
role: "treeitem",
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
"aria-selected": isSelected,
|
|
350
|
-
...isDirectory && { "aria-expanded": isExpanded },
|
|
351
|
-
tabIndex: isSelected ? 0 : -1,
|
|
352
|
-
children: [/* @__PURE__ */ jsx(RowIcons, {
|
|
353
|
-
isDirectory,
|
|
354
|
-
isExpanded,
|
|
355
|
-
customIcon
|
|
356
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
357
|
-
className: "text-sm truncate",
|
|
358
|
-
children: label
|
|
359
|
-
})]
|
|
393
|
+
tabIndex,
|
|
394
|
+
onClick: handleSelect,
|
|
395
|
+
onKeyDown: handleKeyDown
|
|
360
396
|
})]
|
|
361
397
|
});
|
|
362
398
|
});
|
|
363
399
|
/** The height constants must equal the rendered pitch, or the scrollbar
|
|
364
400
|
* drifts from the content. Reports the measured value to use. */
|
|
365
401
|
function useTreePitchAudit(scrollEl, rows, items) {
|
|
366
|
-
useEffect(() => {
|
|
402
|
+
useEffect(() => {
|
|
403
|
+
if (!isDev() || !scrollEl || items.length < 2) return;
|
|
404
|
+
for (const item of items) {
|
|
405
|
+
const row = rows[item.index];
|
|
406
|
+
const el = scrollEl.querySelector(`[data-index="${item.index}"]`);
|
|
407
|
+
if (row?.kind !== "node" || !(el instanceof HTMLElement)) continue;
|
|
408
|
+
const inner = el.firstElementChild?.lastElementChild;
|
|
409
|
+
const measured = inner ? inner.getBoundingClientRect().height : 0;
|
|
410
|
+
if (measured > 30.5) console.warn(`TreeView: TREE_ROW_HEIGHT is 30 but a row's content measures ${measured}. Set TREE_ROW_HEIGHT = ${Math.ceil(measured)} in TreeView.tsx.`);
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
}, [
|
|
367
414
|
scrollEl,
|
|
368
415
|
rows,
|
|
369
416
|
items
|
|
@@ -19,6 +19,7 @@ declare function CorsIssuePreview({ selectedStorage, onRefresh, }: {
|
|
|
19
19
|
selectedStorage: TStorage | null;
|
|
20
20
|
onRefresh?: CorsAwareRefresh | undefined;
|
|
21
21
|
}): JSX.Element;
|
|
22
|
+
declare function CorsPropagatingPreview(): JSX.Element;
|
|
22
23
|
declare function NoDataAvailablePreview({ selectedStorage, error, onRefresh, connectionIssueMessage, className, canWrite, }: {
|
|
23
24
|
selectedStorage: TStorage | null;
|
|
24
25
|
error?: Error | null;
|
|
@@ -39,4 +40,4 @@ interface IShareFileButtonProps {
|
|
|
39
40
|
onShare: (node: TreeNode) => Promise<void>;
|
|
40
41
|
}
|
|
41
42
|
declare function ShareFileButton({ node, onShare }: IShareFileButtonProps): JSX.Element;
|
|
42
|
-
export { CorsIssuePreview, DragDropZone, getNodeIcon, NoDataAvailablePreview, NoFileSelectedPreview, NotFoundPreview, renderUploadNodes, ShareFileButton, SIDEBAR_WIDTH_DEFAULT, SIDEBAR_WIDTH_STORAGE_KEY, SkeletonPreview, };
|
|
43
|
+
export { CorsIssuePreview, CorsPropagatingPreview, DragDropZone, getNodeIcon, NoDataAvailablePreview, NoFileSelectedPreview, NotFoundPreview, renderUploadNodes, ShareFileButton, SIDEBAR_WIDTH_DEFAULT, SIDEBAR_WIDTH_STORAGE_KEY, SkeletonPreview, };
|