summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/files-app.js57
1 files changed, 30 insertions, 27 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/files-app.js b/packages/meshbay-hub/src/meshbay_hub/static/files-app.js
index ab7e254..d6cb3b9 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/files-app.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/files-app.js
@@ -29,7 +29,6 @@ function FilesPanel({
isNodeAdmin, operatorPaired, mayUpload, userId, setError, onPreview,
showGroup, readOnly, getTransport, onRefreshIndex, showRefresh,
}) {
- const [selecting, setSelecting] = useState(false);
const [selected, setSelected] = useState(() => new Set());
const [sortKey, setSortKey] = useState('name');
const [sortAsc, setSortAsc] = useState(true);
@@ -234,13 +233,27 @@ function FilesPanel({
return next;
});
+ const allVisibleKeys = [...subdirs.map(d => dirKey(d)), ...sorted.map(e => e.id)];
+ const allSelected = allVisibleKeys.length > 0 && allVisibleKeys.every(k => selected.has(k));
+ const someSelected = allVisibleKeys.some(k => selected.has(k));
+ const toggleAll = () => {
+ if (allSelected) {
+ setSelected(new Set());
+ } else {
+ setSelected(prev => {
+ const next = new Set(prev);
+ for (const k of allVisibleKeys) next.add(k);
+ return next;
+ });
+ }
+ };
+
const onlyFile = selectedFiles.length === 1 && selectedDirs.length === 0
? selectedFiles[0] : null;
const deletableFiles = selectedFiles.filter(
e => isNodeAdmin || (userId && e.uploader_id === userId));
const run = (fn) => {
- setSelecting(false);
setSelected(new Set());
Promise.resolve().then(fn).catch(err => {
if (err && err.name !== 'AbortError') setError(err.message);
@@ -365,21 +378,17 @@ function FilesPanel({
<input type="text" placeholder="${t('group.filter')}"
value=${filter} onInput=${e => setFilter(e.target.value)} />
</div>`}
- <button class="tb-btn ${selecting ? 'active' : ''}"
- onClick=${() => {
- setSelecting(v => !v);
- setSelected(new Set());
- }}>
- <${Icon} name=${selecting ? 'check' : 'checkbox'} />
- ${selecting ? t('group.select_done') : t('group.select')}
- </button>
- ${selecting && html`<div class="tb-actions">${actionItems}</div>`}
+ <div class="tb-actions">${actionItems}</div>
</div>
</div>
<table class="file-table">
<thead>
<tr>
- ${selecting && html`<th class="sel-cell"></th>`}
+ <th class="sel-cell">
+ <input type="checkbox" checked=${allSelected}
+ ref=${(el) => { if (el) el.indeterminate = someSelected && !allSelected; }}
+ onChange=${toggleAll} />
+ </th>
<th></th>
<th class="sortable" onClick=${() => toggleSort('name')}>
${t('group.col_name')} ${sortKey === 'name' ? (sortAsc ? '▲' : '▼') : ''}
@@ -402,15 +411,12 @@ function FilesPanel({
const inside = entriesUnder(entries, full);
const bytes = inside.reduce((n, f) => n + (f.entry.size || 0), 0);
return html`
- <tr class="file-row dir-row" key=${full} onClick=${() =>
- selecting ? toggle(dirKey(d)) : setCurrentPath(full)}>
- ${selecting && html`
+ <tr class="file-row dir-row" key=${full} onClick=${() => setCurrentPath(full)}>
<td class="sel-cell">
<input type="checkbox" checked=${selected.has(dirKey(d))}
onClick=${(ev) => ev.stopPropagation()}
onChange=${() => toggle(dirKey(d))} />
</td>
- `}
<td>${unavailableHere.includes(d) ? '\u{26A0}' : '\u{1F4C1}'}</td>
<td>${d}${unavailableHere.includes(d) ? html`
<span class="root-offline"> ${t('group.root_unavailable')}</span>
@@ -422,18 +428,15 @@ function FilesPanel({
</tr>
`; })}
${sorted.map(e => html`
- <tr class="file-row" key=${e.id}
- onClick=${() => selecting && toggle(e.id)}>
- ${selecting && html`
- <td class="sel-cell">
- <input type="checkbox" checked=${selected.has(e.id)}
- onClick=${(ev) => ev.stopPropagation()}
- onChange=${() => toggle(e.id)} />
- </td>
- `}
+ <tr class="file-row" key=${e.id}>
+ <td class="sel-cell">
+ <input type="checkbox" checked=${selected.has(e.id)}
+ onClick=${(ev) => ev.stopPropagation()}
+ onChange=${() => toggle(e.id)} />
+ </td>
<td>${FILE_ICONS[e.type] || FILE_ICONS.other}</td>
<td class="file-name">
- ${!selecting && canPreview(e)
+ ${canPreview(e)
? html`<a class="file-link" onClick=${() => onPreview(e)}>${e.name}</a>`
: e.name
}
@@ -450,7 +453,7 @@ function FilesPanel({
</tr>
`)}
${sorted.length === 0 && subdirs.length === 0 && html`
- <tr><td colspan=${(selecting ? 6 : 5) + (showGroup ? 1 : 0)} class="file-empty">
+ <tr><td colspan=${6 + (showGroup ? 1 : 0)} class="file-empty">
${filter ? t('group.empty_filter') : t('group.empty_dir')}
</td></tr>
`}