Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 43 additions & 19 deletions src/components/fileTree/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ export default class FileTree {

// Child file tree for nested folders
let childTree = null;
$content._fileTree = null;

const toggle = async () => {
const isExpanded = !$wrapper.classList.contains("hidden");
Expand All @@ -179,6 +180,7 @@ export default class FileTree {
childTree.destroy();
this.childTrees.delete(url);
childTree = null;
$content._fileTree = null;
}
this.options.onExpandedChange?.(url, false);
} else {
Expand All @@ -192,6 +194,7 @@ export default class FileTree {
_depth: this.depth + 1,
});
this.childTrees.set(url, childTree);
$content._fileTree = childTree;
try {
await childTree.load(url);
} finally {
Expand All @@ -216,20 +219,34 @@ export default class FileTree {
queueMicrotask(() => toggle());
}

// Add properties for external access (keep unclasped for collapsableList compatibility)
Object.defineProperties($wrapper, {
collapsed: { get: () => $wrapper.classList.contains("hidden") },
expanded: { get: () => !$wrapper.classList.contains("hidden") },
unclasped: { get: () => !$wrapper.classList.contains("hidden") }, // Legacy compatibility
$title: { get: () => $title },
$ul: { get: () => $content },
expand: {
value: () => !$wrapper.classList.contains("hidden") || toggle(),
},
collapse: {
value: () => $wrapper.classList.contains("hidden") || toggle(),
},
});
const defineCollapsibleAccessors = ($el, { includeTitle = true } = {}) => {
const properties = {
collapsed: { get: () => $wrapper.classList.contains("hidden") },
expanded: { get: () => !$wrapper.classList.contains("hidden") },
unclasped: { get: () => !$wrapper.classList.contains("hidden") }, // Legacy compatibility
$ul: { get: () => $content },
fileTree: { get: () => childTree },
refresh: {
value: () => childTree?.refresh(),
},
expand: {
value: () => !$wrapper.classList.contains("hidden") || toggle(),
},
collapse: {
value: () => $wrapper.classList.contains("hidden") || toggle(),
},
};

if (includeTitle) {
properties.$title = { get: () => $title };
}

Object.defineProperties($el, properties);
};

// Keep nested folders compatible with the legacy collapsableList API.
defineCollapsibleAccessors($wrapper);
defineCollapsibleAccessors($title, { includeTitle: false });

return $wrapper;
}
Expand Down Expand Up @@ -281,11 +298,7 @@ export default class FileTree {
* Clear all rendered content
*/
clear() {
// Destroy all child trees
for (const childTree of this.childTrees.values()) {
childTree.destroy();
}
this.childTrees.clear();
this.destroyChildTrees();

if (this.virtualList) {
this.virtualList.destroy();
Expand Down Expand Up @@ -322,6 +335,16 @@ export default class FileTree {
}
}

/**
* Destroy all expanded child trees and clear their references.
*/
destroyChildTrees() {
for (const childTree of this.childTrees.values()) {
childTree.destroy();
}
this.childTrees.clear();
}

/**
* Append a new entry to the tree
* @param {string} name
Expand Down Expand Up @@ -356,6 +379,7 @@ export default class FileTree {
this.virtualList.setItems(this.entries);
} else {
// Fragment mode: re-render
this.destroyChildTrees();
this.container.innerHTML = "";
this.renderWithFragment();
}
Expand Down
89 changes: 69 additions & 20 deletions src/lib/openFolder.js
Original file line number Diff line number Diff line change
Expand Up @@ -702,20 +702,14 @@ function execOperation(type, action, url, $target, name) {
if (!newUrl.created) return;

if (isNestedPath) {
openFolder.find(url)?.reload();
await refreshOpenFolder(url);
await FileList.refresh();
toast(strings.success);
return;
}

newName = Url.basename(newUrl.uri);
if ($target.unclasped) {
if (newUrl.type === "file") {
appendTile($target, createFileTile(newName, newUrl.uri));
} else if (newUrl.type === "folder") {
appendList($target, createFolderTile(newName, newUrl.uri));
}
}
appendEntryToOpenFolder(url, newUrl.uri, newUrl.type);

FileList.append(url, newUrl.uri);
toast(strings.success);
Expand Down Expand Up @@ -995,6 +989,72 @@ function appendList($target, $list) {
else $target.append($list);
}

/**
* Get the active file tree for a folder element, if it has been loaded.
* @param {HTMLElement} $el
* @returns {FileTree|null}
*/
function getLoadedFileTree($el) {
return (
$el?.$ul?._fileTree || $el?.fileTree || $el?.nextElementSibling?._fileTree
);
}

/**
* Update matching expanded folder views with a new entry.
* @param {string} parentUrl
* @param {string} entryUrl
* @param {"file"|"folder"} type
*/
function appendEntryToOpenFolder(parentUrl, entryUrl, type) {
const filesApp = sidebarApps.get("files");
const $els = filesApp.getAll(`[data-url="${parentUrl}"]`);
const isDirectory = type === "folder";
const name = Url.basename(entryUrl);

Array.from($els).forEach(($el) => {
if (!(helpers.isDir($el.dataset.type) || $el.dataset.type === "root")) {
return;
}

if (!$el.unclasped) return;

const fileTree = getLoadedFileTree($el);
if (fileTree) {
fileTree.appendEntry(name, entryUrl, isDirectory);
return;
}

if (isDirectory) {
appendList($el, createFolderTile(name, entryUrl));
} else {
appendTile($el, createFileTile(name, entryUrl));
}
});
}

/**
* Refresh matching expanded folder views.
* @param {string} folderUrl
*/
async function refreshOpenFolder(folderUrl) {
const filesApp = sidebarApps.get("files");
const $els = filesApp.getAll(`[data-url="${folderUrl}"]`);

await Promise.all(
Array.from($els).map(async ($el) => {
if (!(helpers.isDir($el.dataset.type) || $el.dataset.type === "root")) {
return;
}

const fileTree = getLoadedFileTree($el);
if (fileTree) {
await fileTree.refresh();
}
}),
);
}

/**
* Create a folder tile
* @param {string} name
Expand Down Expand Up @@ -1039,18 +1099,7 @@ function createFileTile(name, url) {
openFolder.add = async (url, type) => {
const { url: parent } = await fsOperation(Url.dirname(url)).stat();
FileList.append(parent, url);

const filesApp = sidebarApps.get("files");
const $els = filesApp.getAll(`[data-url="${parent}"]`);
Array.from($els).forEach(($el) => {
if ($el.dataset.type !== "dir") return;

if (type === "file") {
appendTile($el, createFileTile(Url.basename(url), url));
} else {
appendList($el, createFolderTile(Url.basename(url), url));
}
});
appendEntryToOpenFolder(parent, url, type);
};

openFolder.renameItem = (oldFile, newFile, newFilename) => {
Expand Down