mirror of
https://github.com/multipleof4/octopus-file-browser.git
synced 2026-09-25 12:35:25 +00:00
Add an opt-in context menu for files and directories
Apps set browser.contextMenu to a list of { label, action, when?, danger? }
items; when(entry, path) scopes an item to directories, files, or any
other match. Right-click, the Menu key, or a touch long press opens it.
The demo adds sample items, including a .json-only one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+81
-10
@@ -1,19 +1,90 @@
|
||||
import './demo.css';
|
||||
import './octopus-file-browser.js';
|
||||
import { formatFileSize } from './octopus-file-browser.js';
|
||||
import tree from 'virtual:octopus-repo-tree';
|
||||
|
||||
const browser = document.querySelector('octopus-file-browser');
|
||||
const status = document.querySelector('.demo-status');
|
||||
const say = (text) => { status.textContent = text; };
|
||||
const pathText = (path) => `/${path.join('/')}`;
|
||||
const isDirectory = (entry) => entry.type === 'directory';
|
||||
|
||||
// Stands in for an app backend: returns one directory listing per request.
|
||||
function list(path) {
|
||||
// Stands in for an app backend: `tree` is its filesystem and `list` returns one directory per request.
|
||||
function directory(path) {
|
||||
return path.reduce((entries, name) =>
|
||||
entries.find((entry) => entry.type === 'directory' && entry.name === name)?.children || [], tree)
|
||||
.map(({ children, ...entry }) => entry);
|
||||
entries.find((entry) => isDirectory(entry) && entry.name === name)?.children || [], tree);
|
||||
}
|
||||
|
||||
browser.addEventListener('octopus:navigate', ({ detail }) => {
|
||||
browser.path = detail.path;
|
||||
browser.entries = list(detail.path);
|
||||
});
|
||||
function list(path) {
|
||||
return directory(path).map(({ children, ...entry }) => entry);
|
||||
}
|
||||
|
||||
browser.entries = list([]);
|
||||
function show(path) {
|
||||
browser.path = path;
|
||||
browser.entries = list(path);
|
||||
}
|
||||
|
||||
function stored(path) {
|
||||
return directory(path.slice(0, -1)).find((entry) => entry.name === path.at(-1));
|
||||
}
|
||||
|
||||
browser.contextMenu = [
|
||||
{ label: 'Open', when: isDirectory, action: (entry, path) => show(path) },
|
||||
{
|
||||
label: 'New folder…',
|
||||
when: isDirectory,
|
||||
action: (entry, path) => {
|
||||
const name = prompt(`New folder in ${pathText(path)}`)?.trim();
|
||||
if (!name) return;
|
||||
directory(path).push({ name, type: 'directory', modified: new Date().toISOString(), children: [] });
|
||||
say(`Created ${pathText([...path, name])}`);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Download',
|
||||
when: (entry) => entry.type === 'file',
|
||||
action: (entry, path) => say(`"Download" on ${pathText(path)} (${formatFileSize(entry.size)})`),
|
||||
},
|
||||
{
|
||||
label: 'Validate JSON',
|
||||
when: (entry) => entry.type === 'file' && entry.name.toLowerCase().endsWith('.json'),
|
||||
action: (entry, path) => say(`"Validate JSON" on ${pathText(path)}`),
|
||||
},
|
||||
{
|
||||
label: 'Copy path',
|
||||
action: async (entry, path) => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(pathText(path));
|
||||
say(`Copied ${pathText(path)}`);
|
||||
} catch {
|
||||
say(`Could not copy ${pathText(path)}`);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Rename…',
|
||||
action: (entry, path) => {
|
||||
const name = prompt(`Rename ${entry.name}`, entry.name)?.trim();
|
||||
if (!name || name === entry.name) return;
|
||||
if (list(browser.path).some((sibling) => sibling.name === name)) return say(`${name} already exists`);
|
||||
stored(path).name = name;
|
||||
browser.entries = list(browser.path);
|
||||
say(`Renamed ${pathText(path)} to ${name}`);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
danger: true,
|
||||
action: (entry, path) => {
|
||||
if (!confirm(`Delete ${pathText(path)}?`)) return;
|
||||
const siblings = directory(path.slice(0, -1));
|
||||
siblings.splice(siblings.indexOf(stored(path)), 1);
|
||||
browser.entries = list(browser.path);
|
||||
say(`Deleted ${pathText(path)}`);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
browser.addEventListener('octopus:navigate', ({ detail }) => show(detail.path));
|
||||
browser.addEventListener('octopus:open', ({ detail }) => say(`Opened ${pathText(detail.path)}`));
|
||||
|
||||
show([]);
|
||||
|
||||
Reference in New Issue
Block a user