feat (ui): selecteer meerdere bestanden tegelijkertijd

This commit is contained in:
kodi
2026-03-09 18:24:00 +01:00
parent c5b873c63a
commit 4941b4161c
4 changed files with 101 additions and 9 deletions
+53 -9
View File
@@ -17,6 +17,7 @@
modalKnownFiles: {},
modalFileFilter: "",
modalVisibleFiles: [],
modalSelectionAnchorPath: null,
syncScrolling: false,
settings: {
set_file_date_to_first_aired_date: false,
@@ -755,6 +756,7 @@
};
});
state.modalFileFilter = "";
state.modalSelectionAnchorPath = null;
el.fileModalTitle.textContent = "File Discovery";
el.modalAddSelectedFilesBtn.style.display = "";
if (el.modalFileFilterInput) {
@@ -819,22 +821,62 @@
const isSelected = state.modalSelectedFilePaths.has(file.path);
if (isSelected) li.classList.add("selected");
li.addEventListener("click", () => {
if (state.modalSelectedFilePaths.has(file.path)) {
state.modalSelectedFilePaths.delete(file.path);
li.classList.remove("selected");
} else {
state.modalSelectedFilePaths.add(file.path);
li.classList.add("selected");
if (state.modalSelectionAnchorPath && state.modalSelectionAnchorPath === file.path) {
li.classList.add("modal-anchor");
}
updateModalSelectionCount();
li.addEventListener("click", (event) => {
handleModalFileRowClick(file.path, event);
});
el.modalFilesList.appendChild(li);
});
updateModalSelectionCount();
}
function handleModalFileRowClick(path, event) {
const clickedPath = (path || "").toString().trim();
if (!clickedPath) return;
const visiblePaths = (state.modalVisibleFiles || [])
.map((f) => (f && f.path ? f.path : ""))
.filter((p) => p);
const isShift = !!(event && event.shiftKey);
const isToggle = !!(event && (event.ctrlKey || event.metaKey));
if (isShift) {
const anchor = state.modalSelectionAnchorPath || "";
const from = visiblePaths.indexOf(anchor);
const to = visiblePaths.indexOf(clickedPath);
if (from >= 0 && to >= 0) {
const start = Math.min(from, to);
const end = Math.max(from, to);
const rangePaths = visiblePaths.slice(start, end + 1);
state.modalSelectedFilePaths = new Set(rangePaths);
} else {
// Fallback: no valid anchor in current visible list.
state.modalSelectedFilePaths = new Set([clickedPath]);
}
state.modalSelectionAnchorPath = clickedPath;
renderModalFiles();
return;
}
if (isToggle) {
if (state.modalSelectedFilePaths.has(clickedPath)) {
state.modalSelectedFilePaths.delete(clickedPath);
} else {
state.modalSelectedFilePaths.add(clickedPath);
}
state.modalSelectionAnchorPath = clickedPath;
renderModalFiles();
return;
}
state.modalSelectedFilePaths = new Set([clickedPath]);
state.modalSelectionAnchorPath = clickedPath;
renderModalFiles();
}
async function loadModalFiles(subpath) {
const rootId = el.modalRootSelect.value;
const chosenSubpath = (subpath || "").trim();
@@ -845,6 +887,7 @@
const data = await api(
`/api/files/discover?root_id=${encodeURIComponent(rootId)}&subpath=${encodeURIComponent(chosenSubpath)}&recursive=${recursive}&limit=200`
);
state.modalSelectionAnchorPath = null;
state.modalFiles = data.items || [];
state.modalFiles.forEach((file) => {
const path = file.path || "";
@@ -891,6 +934,7 @@
function clearModalSelection() {
state.modalSelectedFilePaths.clear();
state.modalSelectionAnchorPath = null;
renderModalFiles();
}
+4
View File
@@ -506,6 +506,10 @@ button.secondary {
cursor: pointer;
}
#modalFilesList li.modal-anchor {
box-shadow: inset 0 0 0 1px var(--button-primary-border);
}
.modal-files-tools {
margin-bottom: 8px;
}
Binary file not shown.
+44
View File
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
set -euo pipefail
BASE_URL="${BASE_URL:-http://127.0.0.1:8085}"
ALT_BASE_URL="http://host.containers.internal:8085"
detect_base_url() {
if curl -fsS --max-time 2 "$BASE_URL/api/health" >/dev/null 2>&1; then
echo "$BASE_URL"
return
fi
if curl -fsS --max-time 2 "$ALT_BASE_URL/api/health" >/dev/null 2>&1; then
echo "$ALT_BASE_URL"
return
fi
echo "$BASE_URL"
}
BASE_URL="$(detect_base_url)"
echo "Using BASE_URL=$BASE_URL"
echo "== Feature test 1: modal selection modifiers are implemented =="
grep -q "modalSelectionAnchorPath" app/static/app.js || { echo "anchor state missing"; exit 1; }
grep -q "event.shiftKey" app/static/app.js || { echo "shift handling missing"; exit 1; }
grep -q "event.ctrlKey || event.metaKey" app/static/app.js || { echo "ctrl/meta handling missing"; exit 1; }
echo "modifier selection validation passed"
echo
echo "== Feature test 2: anchor reset on clear and folder reload =="
grep -q "state.modalSelectionAnchorPath = null;" app/static/app.js || { echo "anchor reset missing"; exit 1; }
grep -q "function clearModalSelection()" app/static/app.js || { echo "clearModalSelection missing"; exit 1; }
grep -q "async function loadModalFiles(subpath)" app/static/app.js || { echo "loadModalFiles missing"; exit 1; }
echo "anchor reset validation passed"
echo
echo "== Feature test 3: modal file list UI still present with batch actions =="
page="$(curl -fsS "$BASE_URL/")"
echo "$page" | grep -q 'id="modalFilesList"' || { echo "modalFilesList missing"; exit 1; }
echo "$page" | grep -q 'id="modalSelectAllFilesBtn"' || { echo "Select All button missing"; exit 1; }
echo "$page" | grep -q 'id="modalClearSelectionBtn"' || { echo "Clear Selection button missing"; exit 1; }
echo "modal UI validation passed"
echo
echo "All file modal selection feature tests passed."