feat: 5+ améliorations finales — empty state, offline, pin, thème viewer
B6 Empty state: message contextuel (racine vs dossier). Offline banner.
E6 Thème cohérent: file viewer respecte le thème via request cookies.
G4 Offline: bannière rouge 'You are offline', listeners online/offline.
E4 Pin: togglePin(id) persisté localStorage, icône 📌 sur items hover.
F3 Touch: _haptic() via navigator.vibrate intégré à pin.
B6 Empty actions: boutons icônes dans l'état vide.
Online state reactif (navigator.onLine + event listeners).
This commit is contained in:
@@ -77,6 +77,14 @@
|
||||
.modified-dot{width:6px;height:6px;background:var(--accent);border-radius:50%;margin-left:4px;flex-shrink:0;display:none;}
|
||||
.modified-dot.show{display:block;}
|
||||
|
||||
/* Offline banner */
|
||||
.offline-banner{position:fixed;top:0;left:0;right:0;background:#f85149;color:#fff;text-align:center;padding:6px 12px;font-size:12px;z-index:500;font-weight:500;}
|
||||
|
||||
/* Pinned indicator */
|
||||
.pin-icon{font-size:10px;color:var(--text-tertiary);cursor:pointer;margin-left:4px;opacity:0;transition:opacity 150ms;}
|
||||
.ws-tree-item:hover .pin-icon{opacity:1;}
|
||||
.pin-icon.pinned{opacity:1;color:var(--accent);}
|
||||
|
||||
.modal-overlay{position:fixed;inset:0;background:rgba(0,0,0,.6);display:flex;align-items:center;justify-content:center;z-index:999;backdrop-filter:blur(4px);}
|
||||
.modal-box{background:#1F1F1F;border:1px solid var(--border-strong);border-radius:16px;padding:28px;width:420px;max-width:90vw;box-shadow:0 20px 60px rgba(0,0,0,.5);}
|
||||
.modal-box h3{font-size:16px;margin-bottom:6px;display:flex;align-items:center;gap:8px;}
|
||||
@@ -336,14 +344,19 @@
|
||||
<!-- Empty -->
|
||||
<div class="empty-state" x-show="tree.length===0&&loaded">
|
||||
<div class="empty-icon">📁</div>
|
||||
<h2>Empty folder</h2>
|
||||
<p>Create a new file or folder to get started</p>
|
||||
<h2 x-text="currentFolder > 0 ? 'Empty folder' : 'Welcome to your Workspace'"></h2>
|
||||
<p x-text="currentFolder > 0 ? 'Create a new file or folder to get started' : 'Drag & drop files from your computer, or create new ones.'"></p>
|
||||
<div class="empty-actions">
|
||||
<button class="btn-icon" @click="openModal('file')" title="New file">📄</button>
|
||||
<button class="btn-icon" @click="openModal('folder')" title="New folder">📁</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Offline banner -->
|
||||
<div class="offline-banner" x-show="!online" x-transition>
|
||||
<span>⚠️ You are offline. Changes will sync when connection is restored.</span>
|
||||
</div>
|
||||
|
||||
<!-- Create Modal -->
|
||||
<div class="modal-overlay" x-show="showCreate" @click.away="showCreate=false" @keydown.escape="showCreate=false">
|
||||
<div class="modal-box">
|
||||
@@ -452,6 +465,13 @@ document.addEventListener('alpine:init', function() {
|
||||
dragCount: 0,
|
||||
dragOverParentId: null,
|
||||
|
||||
// Online/offline
|
||||
online: navigator.onLine,
|
||||
pinnedIds: (function() {
|
||||
try { return JSON.parse(localStorage.getItem('fd_ws_pinned') || '[]'); }
|
||||
catch(e) { return []; }
|
||||
})(),
|
||||
|
||||
// Navigation history
|
||||
navHistory: [],
|
||||
navIndex: -1,
|
||||
@@ -494,9 +514,26 @@ document.addEventListener('alpine:init', function() {
|
||||
this.navIndex = 0;
|
||||
// Expose globally for renderChildren onclick handlers
|
||||
window._wsData = this;
|
||||
// Listen for online/offline
|
||||
var self = this;
|
||||
window.addEventListener('online', function() { self.online = true; });
|
||||
window.addEventListener('offline', function() { self.online = false; });
|
||||
} catch(e) { console.error('Tree load error:', e); this.loaded = true; }
|
||||
},
|
||||
|
||||
// ── Pin/Favorite items ──
|
||||
togglePin(id) {
|
||||
var idx = this.pinnedIds.indexOf(id);
|
||||
if (idx === -1) this.pinnedIds.push(id);
|
||||
else this.pinnedIds.splice(idx, 1);
|
||||
try { localStorage.setItem('fd_ws_pinned', JSON.stringify(this.pinnedIds)); }
|
||||
catch(e) {}
|
||||
this._haptic();
|
||||
},
|
||||
isPinned(id) {
|
||||
return this.pinnedIds.indexOf(id) !== -1;
|
||||
},
|
||||
|
||||
_markTree(nodes, depth) {
|
||||
for (var i=0; i<nodes.length; i++) {
|
||||
var n = nodes[i];
|
||||
|
||||
Reference in New Issue
Block a user