fix: multi-sélection — Set() remplacé par {} pour réactivité Alpine.js
Cause: Alpine.js ne détecte pas les mutations de Set() → les checkboxes
et la bulk bar ne se mettaient jamais à jour.
Fix: selectedIds devenu un objet {} (clés = IDs, valeurs = true).
selectedIdsCount (number) remplace .size pour la réactivité.
toggleSelect: Object.assign({}, selectedIds) force la réactivité.
bulkDelete: Object.keys(selectedIds) au lieu de Array.from(Set).
selectAll + Clear: boucles for..in au lieu de .clear().
Alpine détecte les changements sur les objets simples,
contrairement aux objets Set().
This commit is contained in:
@@ -277,10 +277,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Bulk actions bar -->
|
<!-- Bulk actions bar -->
|
||||||
<div class="bulk-bar" x-show="selectedIds.size > 0" x-transition>
|
<div class="bulk-bar" x-show="selectedIdsCount > 0" x-transition>
|
||||||
<span x-text="selectedIds.size + ' selected'"></span>
|
<span x-text="selectedIdsCount + ' selected'"></span>
|
||||||
<button class="btn btn-danger" @click="bulkDelete()">🗑 Delete</button>
|
<button class="btn btn-danger" @click="bulkDelete()">🗑 Delete</button>
|
||||||
<button class="btn btn-secondary" @click="selectedIds.clear(); selectedIds=new Set()">✕ Clear</button>
|
<button class="btn btn-secondary" @click="for(var k in selectedIds) delete selectedIds[k]; selectedIdsCount=0">✕ Clear</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Tree -->
|
<!-- Tree -->
|
||||||
@@ -317,9 +317,9 @@
|
|||||||
:data-folder-id="node.is_folder ? node.id : null"
|
:data-folder-id="node.is_folder ? node.id : null"
|
||||||
@dragstart="onInternalDragStart($event, node.id)"
|
@dragstart="onInternalDragStart($event, node.id)"
|
||||||
@dragend="draggedItemId = null; $el.style.opacity = ''"
|
@dragend="draggedItemId = null; $el.style.opacity = ''"
|
||||||
:class="{ selected: selectedIds.has(node.id) }">
|
:class="{ selected: !!selectedIds[node.id] }">
|
||||||
<input type="checkbox" class="item-checkbox"
|
<input type="checkbox" class="item-checkbox"
|
||||||
:checked="selectedIds.has(node.id)"
|
:checked="!!selectedIds[node.id]"
|
||||||
@click.stop="toggleSelect(node, $event)"
|
@click.stop="toggleSelect(node, $event)"
|
||||||
title="Select item">
|
title="Select item">
|
||||||
<!-- chevron -->
|
<!-- chevron -->
|
||||||
@@ -384,8 +384,8 @@
|
|||||||
</tr></thead>
|
</tr></thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<template x-for="node in flatTree" :key="'t'+node.id">
|
<template x-for="node in flatTree" :key="'t'+node.id">
|
||||||
<tr :class="{ selected: selectedIds.has(node.id) }">
|
<tr :class="{ selected: !!selectedIds[node.id] }">
|
||||||
<td><input type="checkbox" :checked="selectedIds.has(node.id)" @click.stop="toggleSelect(node, $event)"></td>
|
<td><input type="checkbox" :checked="!!selectedIds[node.id]" @click.stop="toggleSelect(node, $event)"></td>
|
||||||
<td>
|
<td>
|
||||||
<span class="table-icon" x-text="_fileIcon(node.name, node.is_folder, node.content_format)"></span>
|
<span class="table-icon" x-text="_fileIcon(node.name, node.is_folder, node.content_format)"></span>
|
||||||
<span class="table-name" @click="node.is_folder ? navigateToFolder(node.id) : openPage(node.id)" x-text="node.name"></span>
|
<span class="table-name" @click="node.is_folder ? navigateToFolder(node.id) : openPage(node.id)" x-text="node.name"></span>
|
||||||
@@ -504,7 +504,8 @@ document.addEventListener('alpine:init', function() {
|
|||||||
viewMode: 'tree',
|
viewMode: 'tree',
|
||||||
|
|
||||||
// Multi-select
|
// Multi-select
|
||||||
selectedIds: new Set(),
|
selectedIds: {},
|
||||||
|
selectedIdsCount: 0,
|
||||||
lastSelectedId: null,
|
lastSelectedId: null,
|
||||||
|
|
||||||
// Undo
|
// Undo
|
||||||
@@ -604,20 +605,20 @@ document.addEventListener('alpine:init', function() {
|
|||||||
var end = allIds.indexOf(node.id);
|
var end = allIds.indexOf(node.id);
|
||||||
if (start !== -1 && end !== -1) {
|
if (start !== -1 && end !== -1) {
|
||||||
var from = Math.min(start, end), to = Math.max(start, end);
|
var from = Math.min(start, end), to = Math.max(start, end);
|
||||||
for (var i = from; i <= to; i++) this.selectedIds.add(allIds[i]);
|
for (var i = from; i <= to; i++) this.selectedIds[allIds[i]] = true;
|
||||||
}
|
}
|
||||||
} else if (ev.ctrlKey || ev.metaKey) {
|
} else if (ev.ctrlKey || ev.metaKey) {
|
||||||
// Toggle single
|
// Toggle single
|
||||||
if (this.selectedIds.has(node.id)) this.selectedIds.delete(node.id);
|
if (!!this.selectedIds[node.id]) delete this.selectedIds[node.id];
|
||||||
else this.selectedIds.add(node.id);
|
else this.selectedIds[node.id] = true;
|
||||||
} else {
|
} else {
|
||||||
// Replace selection
|
// Replace selection
|
||||||
this.selectedIds.clear();
|
for (var k in this.selectedIds) delete this.selectedIds[k];
|
||||||
this.selectedIds.add(node.id);
|
this.selectedIds[node.id] = true;
|
||||||
}
|
}
|
||||||
this.lastSelectedId = node.id;
|
this.lastSelectedId = node.id;
|
||||||
// Force reactivity
|
// Force reactivity
|
||||||
this.selectedIds = new Set(this.selectedIds);
|
this.selectedIds = Object.assign({}, this.selectedIds); this.selectedIdsCount = Object.keys(this.selectedIds).length;
|
||||||
},
|
},
|
||||||
_collectVisibleIds(nodes) {
|
_collectVisibleIds(nodes) {
|
||||||
var ids = [];
|
var ids = [];
|
||||||
@@ -628,25 +629,25 @@ document.addEventListener('alpine:init', function() {
|
|||||||
return ids;
|
return ids;
|
||||||
},
|
},
|
||||||
async bulkDelete() {
|
async bulkDelete() {
|
||||||
var ids = Array.from(this.selectedIds);
|
var ids = Object.keys(this.selectedIds);
|
||||||
if (ids.length === 0) return;
|
if (ids.length === 0) return;
|
||||||
if (!confirm('Delete ' + ids.length + ' selected items?')) return;
|
if (!confirm('Delete ' + ids.length + ' selected items?')) return;
|
||||||
var self = this;
|
var self = this;
|
||||||
for (var i=0; i<ids.length; i++) {
|
for (var i=0; i<ids.length; i++) {
|
||||||
await fetch('/api/local-workspace/items/' + ids[i], { method: 'DELETE' });
|
await fetch('/api/local-workspace/items/' + ids[i], { method: 'DELETE' });
|
||||||
}
|
}
|
||||||
this.selectedIds.clear();
|
for (var k in this.selectedIds) delete this.selectedIds[k];
|
||||||
this.selectedIds = new Set();
|
this.selectedIdsCount = 0;
|
||||||
this._reloadAfterAction();
|
this._reloadAfterAction();
|
||||||
},
|
},
|
||||||
selectAll(ev) {
|
selectAll(ev) {
|
||||||
if (ev.target.checked) {
|
if (ev.target.checked) {
|
||||||
var all = this._collectVisibleIds(this.displayTree);
|
var all = this._collectVisibleIds(this.displayTree);
|
||||||
for (var i=0; i<all.length; i++) this.selectedIds.add(all[i]);
|
for (var i=0; i<all.length; i++) this.selectedIds[all[i]] = true;
|
||||||
} else {
|
} else {
|
||||||
this.selectedIds.clear();
|
for (var k in this.selectedIds) delete this.selectedIds[k];
|
||||||
}
|
}
|
||||||
this.selectedIds = new Set(this.selectedIds);
|
this.selectedIds = Object.assign({}, this.selectedIds); this.selectedIdsCount = Object.keys(this.selectedIds).length;
|
||||||
},
|
},
|
||||||
|
|
||||||
// ── Table view data ──
|
// ── Table view data ──
|
||||||
|
|||||||
Reference in New Issue
Block a user