Дезайн ыыыы
This commit is contained in:
parent
403d7086d4
commit
e7c02c7feb
174 changed files with 15802 additions and 9725 deletions
145
frontend/js/components/remotePane.js
Normal file
145
frontend/js/components/remotePane.js
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
import { state, notify } from "../store.js";
|
||||
import { t } from "../i18n.js";
|
||||
import { iconMarkup } from "../icons.js";
|
||||
import { formatSize } from "../format.js";
|
||||
import { renderFileTable } from "./fileTable.js";
|
||||
import { renderBreadcrumbs } from "./breadcrumbs.js";
|
||||
import { makeDropTarget } from "../dnd.js";
|
||||
import { openRename, openDelete } from "./opDialogs.js";
|
||||
import {
|
||||
navigateRemote,
|
||||
openRemoteEntry,
|
||||
handleDropOnRemote,
|
||||
refreshRemote,
|
||||
queueDownload,
|
||||
queueDownloadMany,
|
||||
} from "../fsActions.js";
|
||||
|
||||
const STATUS_ICON = { connected: "●", connecting: "◐", error: "×", disconnected: "○" };
|
||||
const STATUS_CLASS = { connected: "connected", connecting: "connecting", error: "error", disconnected: "disconnected" };
|
||||
|
||||
export function renderRemotePane(container, tab) {
|
||||
container.innerHTML = `
|
||||
<div class="path-bar">
|
||||
<span class="path-bar-label">${t("panels.remoteLabel", { host: tab.params.host })}</span>
|
||||
<span class="status-dot ${STATUS_CLASS[tab.status] ?? "disconnected"}">${STATUS_ICON[tab.status] ?? "○"}</span>
|
||||
<div class="breadcrumbs" data-role="breadcrumbs"></div>
|
||||
<div class="path-bar-right" data-role="loading"></div>
|
||||
</div>
|
||||
<div data-role="table" class="file-table-wrap"></div>
|
||||
<div class="pane-footer">
|
||||
<span data-role="count" class="text-dim" style="font-size:11px"></span>
|
||||
<span data-role="size" class="text-hint" style="font-size:11px"></span>
|
||||
<div class="pane-footer-right">
|
||||
<button type="button" class="btn icon-btn btn-ghost" data-action="refresh" title="${t("common.refresh")}">${iconMarkup("refresh", 13)}</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
container.querySelector('[data-action="refresh"]').addEventListener("click", () => {
|
||||
if (!tab.loading) refreshRemote(tab);
|
||||
});
|
||||
|
||||
makeDropTarget(container, {
|
||||
onDrop: (payload) => handleDropOnRemote(tab, payload, tab.remotePath),
|
||||
onHoverChange: (hover) => container.classList.toggle("drag-over", hover),
|
||||
});
|
||||
|
||||
updateRemotePane(container, tab);
|
||||
}
|
||||
|
||||
export function updateRemotePane(container, tab) {
|
||||
renderBreadcrumbs(container.querySelector('[data-role="breadcrumbs"]'), tab.remotePath || "/", (path) =>
|
||||
navigateRemote(tab, path),
|
||||
);
|
||||
|
||||
container.querySelector('[data-role="loading"]').innerHTML = tab.loading
|
||||
? `<span>${t("panels.loading")}</span><span class="spinner"></span>`
|
||||
: "";
|
||||
|
||||
renderFileTable(container.querySelector('[data-role="table"]'), {
|
||||
entries: tab.remoteEntries,
|
||||
selected: tab.remoteSelected,
|
||||
anchor: tab.remoteSelectAnchor,
|
||||
sort: tab.sort,
|
||||
onSort: (next) => {
|
||||
tab.sort = next;
|
||||
notify();
|
||||
},
|
||||
onSelect: (names, anchor) => {
|
||||
tab.remoteSelected = names;
|
||||
tab.remoteSelectAnchor = anchor;
|
||||
state.activePane = "remote";
|
||||
notify();
|
||||
},
|
||||
onOpen: (name) => openRemoteEntry(tab, name),
|
||||
onHoverOpen: (entry) => openRemoteEntry(tab, entry.name),
|
||||
dragPayload: (entry) => {
|
||||
if (entry.name === ".." || tab.status !== "connected") return null;
|
||||
const group =
|
||||
tab.remoteSelected.includes(entry.name) && tab.remoteSelected.length > 1
|
||||
? tab.remoteEntries.filter((e) => tab.remoteSelected.includes(e.name))
|
||||
: [entry];
|
||||
return {
|
||||
source: "remote",
|
||||
connectionId: tab.id,
|
||||
items: group.map((e) => ({ path: e.path, name: e.name, kind: e.kind })),
|
||||
};
|
||||
},
|
||||
onDropOnEntry: (entry, payload) => handleDropOnRemote(tab, payload, entry.path),
|
||||
contextMenuItems: (entry, selectedEntries) => {
|
||||
if (selectedEntries.length > 1) {
|
||||
const files = selectedEntries.filter((e) => e.kind === "file");
|
||||
return [
|
||||
{
|
||||
label: t("common.delete") + ` (${selectedEntries.length})`,
|
||||
icon: "trash",
|
||||
danger: true,
|
||||
onClick: () => openDeleteRemote(selectedEntries),
|
||||
},
|
||||
...(files.length > 0
|
||||
? [
|
||||
"sep",
|
||||
{
|
||||
label: t("panels.downloadToLocal") + ` (${files.length})`,
|
||||
icon: "download",
|
||||
onClick: () => queueDownloadMany(tab, files),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
}
|
||||
return [
|
||||
{ label: t("common.open"), icon: "folder", onClick: () => openRemoteEntry(tab, entry.name) },
|
||||
{ label: t("common.rename"), icon: "pen", onClick: () => openRenameRemote(entry) },
|
||||
{ label: t("common.delete"), icon: "trash", danger: true, onClick: () => openDeleteRemote([entry]) },
|
||||
"sep",
|
||||
{ label: t("common.copyPath"), icon: "copy", onClick: () => copyPath(entry.path) },
|
||||
...(entry.kind === "file"
|
||||
? [{ label: t("panels.downloadToLocal"), icon: "download", onClick: () => queueDownload(tab, entry) }]
|
||||
: []),
|
||||
];
|
||||
},
|
||||
});
|
||||
|
||||
const count = tab.remoteEntries.filter((e) => e.name !== "..").length;
|
||||
const totalSize = tab.remoteEntries.reduce((sum, e) => sum + (e.size ?? 0), 0);
|
||||
container.querySelector('[data-role="count"]').textContent = tab.loading
|
||||
? t("panels.loading")
|
||||
: t("panels.countItems", { n: count });
|
||||
container.querySelector('[data-role="size"]').textContent = totalSize > 0 ? `(${formatSize(totalSize)})` : "";
|
||||
}
|
||||
|
||||
function openRenameRemote(entry) {
|
||||
state.opTarget = "remote";
|
||||
openRename(entry.name);
|
||||
}
|
||||
function openDeleteRemote(entries) {
|
||||
state.opTarget = "remote";
|
||||
openDelete(entries.map((e) => e.name));
|
||||
}
|
||||
function copyPath(path) {
|
||||
navigator.clipboard?.writeText(path);
|
||||
state.statusMessage = t("panels.pathCopied");
|
||||
notify();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue