diff --git a/static/css/components/cards.css b/static/css/components/cards.css index c95d2bf1..b18af5a1 100644 --- a/static/css/components/cards.css +++ b/static/css/components/cards.css @@ -1,11 +1,16 @@ /* Files grid */ -.files-grid { +.files-grid-view { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 20px; position: relative; } +/* header not visible in grid mode */ +.files-grid-view .list-header { + display: none; +} + /* Rubber band / lasso selection rectangle */ .selection-rect { position: fixed; @@ -17,7 +22,7 @@ display: none; } -.file-card { +.files-grid-view .file-item { background-color: white; border-radius: 12px; border: 2px solid #e2e8f0; @@ -27,65 +32,67 @@ align-items: center; box-shadow: 0 1px 3px rgba(0,0,0,0.05); cursor: pointer; - transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s; width: 100%; min-height: 160px; position: relative; } -.file-card:hover { +.files-grid-view .file-item:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0,0,0,0.08); border-color: #cbd5e0; } -.file-card.selected { +.files-grid-view .file-item.selected { border-color: #ff5e3a; background: #fff8f6; box-shadow: 0 0 0 1px rgba(255, 94, 58, 0.15), 0 4px 12px rgba(255, 94, 58, 0.1); } -.file-card.selected:hover { +.files-grid-view .file-item.selected:hover { border-color: #ff5e3a; box-shadow: 0 0 0 1px rgba(255, 94, 58, 0.2), 0 6px 18px rgba(255, 94, 58, 0.15); } +/* element hidden on grid view */ +.files-grid-view .file-item .date-cell, +.files-grid-view .file-item .size-cell { + display: none; +} /* Selection checkbox */ -.file-card-checkbox { +.files-grid-view .file-item .checkbox-cell { position: absolute; top: 10px; left: 10px; width: 22px; height: 22px; border-radius: 6px; - border: 2px solid #cbd5e0; background: white; display: flex; align-items: center; justify-content: center; opacity: 0; - transition: opacity 0.15s, background 0.15s, border-color 0.15s; z-index: 10; cursor: pointer; } -.file-card:hover .file-card-checkbox { +.files-grid-view .file-item:hover .checkbox-cell { opacity: 1; } -.file-card.selected .file-card-checkbox { +.files-grid-view .file-item.selected .checkbox-cell { opacity: 1; background: #ff5e3a; border-color: #ff5e3a; } -.file-card.selected .file-card-checkbox i { +.files-grid-view .file-item.selected .checkbox-cell i { color: white; font-size: 11px; } /* More actions button (three dots) */ -.file-card-more { +.files-grid-view .file-actions { position: absolute; top: 8px; right: 8px; @@ -98,24 +105,23 @@ align-items: center; justify-content: center; opacity: 0; - transition: opacity 0.15s, background 0.15s; z-index: 10; cursor: pointer; color: #64748b; font-size: 16px; } -.file-card:hover .file-card-more { +.files-grid-view .file-item:hover .file-actions { opacity: 1; } -.file-card-more:hover { +.files-grid-view .file-actions:hover { background: #f1f5f9; color: #334155; } -/* Favorite star (mirrors file-card-more button pattern) */ -button.favorite-star { +/* Favorite star (mirrors file-actions button pattern) */ +.files-grid-view .file-item button.favorite-star { position: absolute; top: 8px; right: 38px; @@ -128,7 +134,6 @@ button.favorite-star { align-items: center; justify-content: center; opacity: 0; - transition: opacity 0.15s, color 0.15s, background 0.15s; z-index: 12; cursor: pointer; color: #cbd5e0; @@ -137,40 +142,45 @@ button.favorite-star { line-height: 1; } -.file-card:hover .favorite-star { +.files-grid-view .file-item:hover button.favorite-star { opacity: 1; } -.favorite-star:hover { +.files-grid-view .file-item button.favorite-star:hover { color: #fbbf24; } -.favorite-star.active { +.files-grid-view .file-item button.favorite-star.active { opacity: 1; color: #f59e0b; } -.favorite-star.active:hover { +.files-grid-view .file-item button.favorite-star.active:hover { color: #d97706; } -.file-card.dragging { +.files-grid-view .file-item.dragging { opacity: 0.5; transform: scale(0.95); box-shadow: none; } -.file-card.drop-target { +.files-grid-view .file-item.drop-target { background-color: rgba(255, 193, 7, 0.1); border: 2px dashed #ffc107; } +/* ensure icon centered */ +.files-grid-view .file-item .name-cell .file-icon { + margin: auto; +} + /* Hide FontAwesome / SVG icons inside grid card thumbnails — the decorative CSS shapes (::before / ::after) already convey the file type visually. Icons.js replaces with , so we target both. List-view icons (.file-item .file-icon) are NOT affected. */ -.file-card .file-icon > i, -.file-card .file-icon > svg { +.files-grid-view .file-item .file-icon > i, +.files-grid-view .file-item .file-icon > svg { display: none !important; } @@ -331,7 +341,7 @@ button.favorite-star { object-fit: cover; z-index: 1; } - +/* .file-icon.image-icon::before { content: ""; position: absolute; @@ -342,7 +352,7 @@ button.favorite-star { background-color: #ffda79; border-radius: 50%; } - +*/ .file-icon.video-icon { width: 100px; height: 70px; @@ -735,7 +745,7 @@ button.favorite-star { opacity: 0.5; } -.file-name { +.files-grid-view .file-item .name-cell { font-size: 14px; font-weight: 500; text-align: center; @@ -745,51 +755,62 @@ button.favorite-star { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + margin-top: 8px; } -.file-info { +.files-grid-view .file-item .name-cell span { + padding-top: 8px; + padding-bottom: 8px; + display: block; +} + +.files-grid-view .file-item .name-cell svg.favorite-star-inline { + display: none; +} + +.files-grid-view .file-item .type-cell { font-size: 12px; color: #718096; text-align: center; } -[data-theme="dark"] .file-card { +[data-theme="dark"] .files-grid-view .file-item { background-color: #1e293b; border-color: #334155; } -[data-theme="dark"] .file-card:hover { +[data-theme="dark"] .files-grid-view .file-item:hover { border-color: #475569; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); } -[data-theme="dark"] .file-card.selected { +[data-theme="dark"] .files-grid-view .file-item.selected { border-color: #ff5e3a; background: #1a1520; } -[data-theme="dark"] .file-card-checkbox { +[data-theme="dark"] .files-grid-view .file-item .checkbox-cell { background: #1e293b; border-color: #475569; } -[data-theme="dark"] .file-card-more { +[data-theme="dark"] .files-grid-view .file-actions { color: #94a3b8; } -[data-theme="dark"] .file-card-more:hover { +[data-theme="dark"] .files-grid-view .file-actions:hover { background: #334155; color: #e2e8f0; } -[data-theme="dark"] .file-name { +[data-theme="dark"] .files-grid-view .file-item .name-cell { color: #e2e8f0; } -[data-theme="dark"] .file-info { +[data-theme="dark"] .files-grid-view .file-item .type-cell { color: #94a3b8; } -[data-theme="dark"] .file-card.drop-target { +[data-theme="dark"] .files-grid-view .file-item.drop-target { background-color: rgba(255, 193, 7, 0.05); } diff --git a/static/css/components/fileList.css b/static/css/components/fileList.css index fee4d12a..783ffc3c 100644 --- a/static/css/components/fileList.css +++ b/static/css/components/fileList.css @@ -1,6 +1,6 @@ /* File list - Improved style */ .files-list-view { - --files-list-columns: 36px minmax(200px, 2fr) 100px 110px 130px 36px; + --files-list-columns: 36px minmax(200px, 2fr) 100px 110px 130px 64px; display: flex; flex-direction: column; width: 100%; @@ -22,7 +22,7 @@ align-items: center; } -.file-item { +.files-list-view .file-item { display: grid; grid-template-columns: var(--files-list-columns); column-gap: 12px; @@ -30,15 +30,14 @@ border-bottom: 1px solid #f0f0f0; align-items: center; cursor: pointer; - transition: background-color 0.2s; background-color: white; } -.file-item.selected { +.files-list-view .file-item.selected { background-color: #fff8f6; } -.file-item.selected:hover { +.files-list-view .file-item.selected:hover { background-color: #fff0ec; } @@ -50,22 +49,22 @@ grid-template-columns: minmax(180px, 1.5fr) 0.5fr 1fr 140px 100px; } -.file-item:hover { +.files-list-view .file-item:hover { background-color: #f0f8ff; } .list-header > div, -.file-item > div { +.files-list-view .file-item > div { min-width: 0; } .list-header > div:nth-child(4), -.file-item .size-cell { +.files-list-view .file-item .size-cell { justify-self: end; text-align: right; } -.file-item .name-cell { +.files-list-view .file-item .name-cell { display: flex; align-items: center; gap: 12px; @@ -73,17 +72,13 @@ overflow: hidden; } -.file-item .action-cell { - align-items: center; -} - -.file-item .name-cell span { +.files-list-view .file-item .name-cell span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } -.file-item .file-icon { +.files-list-view .file-item .file-icon { width: 36px; height: 36px; display: flex; @@ -95,7 +90,7 @@ flex-shrink: 0; } -.file-item .file-icon.folder-icon { +.files-list-view .file-item .file-icon.folder-icon { background-color: #ffeaa7; position: relative; width: 36px; @@ -103,7 +98,7 @@ margin-bottom: 0; } -.file-item .file-icon.folder-icon::before { +.files-list-view .file-item .file-icon.folder-icon::before { content: ""; position: absolute; top: 0; @@ -114,90 +109,90 @@ border-radius: 8px 8px 0 0; } -.file-item .file-icon.folder-icon i, -.file-item .file-icon.folder-icon svg { +.files-list-view .file-item .file-icon.folder-icon i, +.files-list-view .file-item .file-icon.folder-icon svg { display: none; } -.file-item .file-icon.doc-icon { +.files-list-view .file-item .file-icon.doc-icon { background-color: #e0ecff; } -.file-item .file-icon.doc-icon i, -.file-item .file-icon.doc-icon svg { +.files-list-view .file-item .file-icon.doc-icon i, +.files-list-view .file-item .file-icon.doc-icon svg { color: #3171d8; display: flex !important; } -.file-item .file-icon.doc-icon::before, -.file-item .file-icon.doc-icon::after { +.files-list-view .file-item .file-icon.doc-icon::before, +.files-list-view .file-item .file-icon.doc-icon::after { display: none; } -.file-item .file-icon.pdf-icon { +.files-list-view .file-item .file-icon.pdf-icon { background-color: #fee2e2; } -.file-item .file-icon.pdf-icon i, -.file-item .file-icon.pdf-icon svg { +.files-list-view .file-item .file-icon.pdf-icon i, +.files-list-view .file-item .file-icon.pdf-icon svg { color: #e53e3e; display: flex !important; } -.file-item .file-icon.pdf-icon::before, -.file-item .file-icon.pdf-icon::after { +.files-list-view .file-item .file-icon.pdf-icon::before, +.files-list-view .file-item .file-icon.pdf-icon::after { display: none; } -.file-item .file-icon.image-icon { +.files-list-view .file-item .file-icon.image-icon { background-color: #e0f2fe; position: relative; overflow: hidden; border-radius: 4px; } -.file-item .file-icon.image-icon i, -.file-item .file-icon.image-icon svg { +.files-list-view .file-item .file-icon.image-icon i, +.files-list-view .file-item .file-icon.image-icon svg { color: #3b82f6; display: flex !important; } -.file-item .file-icon.image-icon::before, -.file-item .file-icon.image-icon::after { +.files-list-view .file-item .file-icon.image-icon::before, +.files-list-view .file-item .file-icon.image-icon::after { display: none; } -.file-item .file-icon.video-icon { +.files-list-view .file-item .file-icon.video-icon { background: linear-gradient(135deg, #ede9fe, #fce7f3); } -.file-item .file-icon.video-icon i, -.file-item .file-icon.video-icon svg { +.files-list-view .file-item .file-icon.video-icon i, +.files-list-view .file-item .file-icon.video-icon svg { color: #8b5cf6; display: flex !important; } -.file-item .file-icon.video-icon::before, -.file-item .file-icon.video-icon::after { +.files-list-view .file-item .file-icon.video-icon::before, +.files-list-view .file-item .file-icon.video-icon::after { display: none; } -.file-item .file-icon.audio-icon { +.files-list-view .file-item .file-icon.audio-icon { background-color: #fef3c7; } -.file-item .file-icon.audio-icon i, -.file-item .file-icon.audio-icon svg { +.files-list-view .file-item .file-icon.audio-icon i, +.files-list-view .file-item .file-icon.audio-icon svg { color: #f59e0b; display: flex !important; } -.file-item .file-icon.audio-icon::before, -.file-item .file-icon.audio-icon::after { +.files-list-view .file-item .file-icon.audio-icon::before, +.files-list-view .file-item .file-icon.audio-icon::after { display: none; } -.file-item .file-icon.spreadsheet-icon { +.files-list-view .file-item .file-icon.spreadsheet-icon { background-color: #e6f4ea; width: 36px; height: 36px; @@ -205,18 +200,18 @@ border-top: none; } -.file-item .file-icon.spreadsheet-icon i, -.file-item .file-icon.spreadsheet-icon svg { +.files-list-view .file-item .file-icon.spreadsheet-icon i, +.files-list-view .file-item .file-icon.spreadsheet-icon svg { color: #0d904f; display: flex !important; } -.file-item .file-icon.spreadsheet-icon::before, -.file-item .file-icon.spreadsheet-icon::after { +.files-list-view .file-item .file-icon.spreadsheet-icon::before, +.files-list-view .file-item .file-icon.spreadsheet-icon::after { display: none; } -.file-item .file-icon.presentation-icon { +.files-list-view .file-item .file-icon.presentation-icon { background-color: #fef3e2; width: 36px; height: 36px; @@ -224,18 +219,18 @@ border-top: none; } -.file-item .file-icon.presentation-icon i, -.file-item .file-icon.presentation-icon svg { +.files-list-view .file-item .file-icon.presentation-icon i, +.files-list-view .file-item .file-icon.presentation-icon svg { color: #d04423; display: flex !important; } -.file-item .file-icon.presentation-icon::before, -.file-item .file-icon.presentation-icon::after { +.files-list-view .file-item .file-icon.presentation-icon::before, +.files-list-view .file-item .file-icon.presentation-icon::after { display: none; } -.file-item .file-icon.audio-icon { +.files-list-view .file-item .file-icon.audio-icon { background-color: #fff3e0; width: 36px; height: 36px; @@ -243,7 +238,7 @@ border-top: none; } -.file-item .file-icon.archive-icon { +.files-list-view .file-item .file-icon.archive-icon { background-color: #f5f0eb; width: 36px; height: 36px; @@ -251,18 +246,18 @@ border-top: none; } -.file-item .file-icon.archive-icon i, -.file-item .file-icon.archive-icon svg { +.files-list-view .file-item .file-icon.archive-icon i, +.files-list-view .file-item .file-icon.archive-icon svg { color: #8d6e63; display: flex !important; } -.file-item .file-icon.archive-icon::before, -.file-item .file-icon.archive-icon::after { +.files-list-view .file-item .file-icon.archive-icon::before, +.files-list-view .file-item .file-icon.archive-icon::after { display: none; } -.file-item .file-icon.installer-icon { +.files-list-view .file-item .file-icon.installer-icon { background-color: #f3e8ff; width: 36px; height: 36px; @@ -270,100 +265,115 @@ border-top: none; } -.file-item .file-icon.installer-icon i, -.file-item .file-icon.installer-icon svg { +.files-list-view .file-item .file-icon.installer-icon i, +.files-list-view .file-item .file-icon.installer-icon svg { color: #7c3aed; display: flex !important; } -.file-item .file-icon.installer-icon::before, -.file-item .file-icon.installer-icon::after { +.files-list-view .file-item .file-icon.installer-icon::before, +.files-list-view .file-item .file-icon.installer-icon::after { display: none; } -.file-item .file-icon.script-icon { +.files-list-view .file-item .file-icon.script-icon { background-color: #e8f5e9; } -.file-item .file-icon.script-icon i, -.file-item .file-icon.script-icon svg { +.files-list-view .file-item .file-icon.script-icon i, +.files-list-view .file-item .file-icon.script-icon svg { color: #4eaa25; display: flex !important; } -.file-item .file-icon.script-icon::before, -.file-item .file-icon.script-icon::after { +.files-list-view .file-item .file-icon.script-icon::before, +.files-list-view .file-item .file-icon.script-icon::after { display: none; } -.file-item .file-icon.config-icon { +.files-list-view .file-item .file-icon.config-icon { background-color: #f1f3f5; } -.file-item .file-icon.config-icon i, -.file-item .file-icon.config-icon svg { +.files-list-view .file-item .file-icon.config-icon i, +.files-list-view .file-item .file-icon.config-icon svg { color: #718096; display: flex !important; } -.file-item .file-icon.config-icon::before, -.file-item .file-icon.config-icon::after { +.files-list-view .file-item .file-icon.config-icon::before, +.files-list-view .file-item .file-icon.config-icon::after { display: none; } .list-header > div:nth-child(5), -.file-item .date-cell { +.files-list-view .file-item .date-cell { justify-self: center; text-align: center; } -.file-item .date-cell { +.files-list-view .file-item .date-cell { color: #718096; font-size: 14px; } -.file-item .size-cell { +.files-list-view .file-item .size-cell { color: #718096; font-size: 14px; text-align: right; font-variant-numeric: tabular-nums; } -.file-item .type-cell { +.files-list-view .file-item .type-cell { color: #4b5563; font-weight: 500; font-size: 14px; } -.file-item.dragging { +.files-list-view .file-item.dragging { opacity: 0.5; background-color: #f9fafb; } -.file-item.drop-target { +.files-list-view .file-item.drop-target { background-color: rgba(255, 193, 7, 0.1); border: 2px dashed #ffc107; } -/* More actions button (three dots) */ -.file-more { - width: 30px; - height: 30px; +.files-list-view .file-item .action-cell { + align-items: center; + text-align: right; +} + +.files-list-view .file-item .action-cell button { + display: inline; + width: 28px; + height: 28px; border-radius: 8px; border: none; background: transparent; align-items: center; justify-content: center; - transition: opacity 0.15s, background 0.15s; cursor: pointer; color: #64748b; font-size: 16px; } -.file-more:hover { +.files-list-view .file-item .action-cell button:hover { background: #e0e5e8; color: #334155; } +/* could be visible if we want */ +.files-list-view .file-item .action-cell button.favorite-star { + display: none; + border: none; +} + +.files-list-view .file-item:hover .action-cell button.favorite-star { + display: inline; +} + + [data-theme="dark"] .files-list-view { background-color: #1e293b; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); @@ -375,49 +385,49 @@ border-bottom-color: #334155; } -[data-theme="dark"] .file-item { +[data-theme="dark"] .files-list-view .file-item { background-color: #1e293b; border-bottom-color: #293548; } -[data-theme="dark"] .file-item:hover { +[data-theme="dark"] .files-list-view .file-item:hover { background-color: #253345; } -[data-theme="dark"] .file-item.selected { +[data-theme="dark"] .files-list-view .file-item.selected { background-color: #1a1520; } -[data-theme="dark"] .file-item.selected:hover { +[data-theme="dark"] .files-list-view .file-item.selected:hover { background-color: #231a2a; } -[data-theme="dark"] .file-item .name-cell { +[data-theme="dark"] .files-list-view .file-item .name-cell { color: #e2e8f0; } -[data-theme="dark"] .file-item .date-cell, -[data-theme="dark"] .file-item .size-cell { +[data-theme="dark"] .files-list-view .file-item .date-cell, +[data-theme="dark"] .files-list-view .file-item .size-cell { color: #64748b; } -[data-theme="dark"] .file-item .type-cell { +[data-theme="dark"] .files-list-view .file-item .type-cell { color: #94a3b8; } -[data-theme="dark"] .file-item.dragging { +[data-theme="dark"] .files-list-view .file-item.dragging { background-color: #334155; } -[data-theme="dark"] .file-item.drop-target { +[data-theme="dark"] .files-list-view .file-item.drop-target { background-color: rgba(255, 193, 7, 0.05); } -[data-theme="dark"] .file-more { +[data-theme="dark"] .files-list-view .file-actions { color: #94a3b8; } -[data-theme="dark"] .file-more:hover { +[data-theme="dark"] .files-list-view .file-actions:hover { background: #334155; color: #e2e8f0; } diff --git a/static/css/components/multiSelect.css b/static/css/components/multiSelect.css index 39c48cd6..947ca9f7 100644 --- a/static/css/components/multiSelect.css +++ b/static/css/components/multiSelect.css @@ -1,13 +1,13 @@ /* Multi-Select – checkboxes & batch action bar */ .list-header-checkbox, -.list-item-checkbox { +.file-item .checkbox-cell { display: flex; align-items: center; justify-content: center; } .list-header-checkbox input[type="checkbox"], -.list-item-checkbox input[type="checkbox"] { +.file-item .checkbox-cell input[type="checkbox"] { width: 17px; height: 17px; cursor: pointer; @@ -125,8 +125,8 @@ } [data-theme="dark"] .list-header-checkbox input, -[data-theme="dark"] .list-item-checkbox input, -[data-theme="dark"] .file-card-checkbox input { +[data-theme="dark"] .file-item .checkbox-cell input, +[data-theme="dark"] .file-item .checkbox-cell input { accent-color: #ff5e3a; } diff --git a/static/css/layout/content.css b/static/css/layout/content.css index f4685c96..9488b8be 100644 --- a/static/css/layout/content.css +++ b/static/css/layout/content.css @@ -48,18 +48,76 @@ width: 360px; } -.dragged-items > div { - position: relative; - display: inline-block; - padding: 4px; - width: 360px; +.dragged-items { + --dragged-files-list-columns: 36px minmax(200px, 1fr); + /*display: flex;*/ + flex-direction: column; + /*width: 360px;*/ + background-color: transparent; + overflow: hidden; + border-radius: 10px; + width: 100%; + /*box-shadow: 0 1px 3px rgba(0,0,0,0.05);*/ +} + +.dragged-items .file-item { + display: grid; + grid-template-columns: var(--dragged-files-list-columns); + column-gap: 12px; + padding: 12px 15px; + /*border-bottom: 1px solid #f0f0f0;*/ + align-items: center; background-color: white; +} + +.dragged-items .file-item { + display: flex; + padding: 4px; + width: 336px; height: 46px; } -.dragged-items > div.fading { - -webkit-mask-image: linear-gradient(to bottom, white 20%, transparent); - mask-image: linear-gradient(to bottom, white 20%, transparent); +/* file name, text is wrapped */ +.dragged-items .file-item div:nth-child(2) { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.dragged-items .file-item .file-icon { + background-color: #ffeaa7; + position: relative; + width: 36px; + height: 36px; + margin-bottom: 0; +} + +.dragged-items .file-item .file-icon.folder-icon { + background-color: #ffeaa7; + position: relative; + width: 36px; + height: 36px; + margin-bottom: 0; +} +.dragged-items .file-item .file-icon.folder-icon::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 8px; + background-color: #fdcb6e; + border-radius: 8px 8px 0 0; +} + +.dragged-items .file-item .file-icon.folder-icon i, +.dragged-items .file-item .file-icon.folder-icon svg { + display: none; +} + +.dragged-items div.fading { + -webkit-mask-image: linear-gradient(to bottom, white 10%, transparent); + mask-image: linear-gradient(to bottom, white 10%, transparent); } .dragged-items-badge { diff --git a/static/css/views/favorites.css b/static/css/views/favorites.css index d729af2e..6ab9a8bb 100644 --- a/static/css/views/favorites.css +++ b/static/css/views/favorites.css @@ -37,7 +37,7 @@ } /* Adjustments for grid view */ -.file-card.favorite-item { +.file-item.favorite-item { border-left: 3px solid #ffc107; [dir='rtl'] & { @@ -101,13 +101,13 @@ /* ── Star badge on file/folder cards (normal view) ── */ /* Grid view: keep interactive star button behavior from style.css */ -.file-card .favorite-star { +.file-item .favorite-star { pointer-events: auto; z-index: 12; } -.file-card .favorite-star.active i, -.file-card .favorite-star.active svg { +.file-item .favorite-star.active i, +.file-item .favorite-star.active svg { animation: favorite-pulse 0.3s ease; } @@ -123,4 +123,4 @@ margin-left: 0; margin-right: 6px; } -} \ No newline at end of file +} diff --git a/static/css/views/recent.css b/static/css/views/recent.css index 008907f0..357e618b 100644 --- a/static/css/views/recent.css +++ b/static/css/views/recent.css @@ -26,7 +26,7 @@ } /* Adjustments for grid view */ -.file-card.recent-item { +.files-grid-view .file-item.recent-item { border-left: 3px solid #6c757d; [dir='rtl'] & { @@ -77,7 +77,7 @@ } /* Tooltip for access time */ -.recent-item .file-info { +.recent-item .type-cell { cursor: help; } @@ -89,4 +89,4 @@ @keyframes recent-fade-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } -} \ No newline at end of file +} diff --git a/static/css/views/trash.css b/static/css/views/trash.css index f1e29111..f34a564e 100644 --- a/static/css/views/trash.css +++ b/static/css/views/trash.css @@ -10,8 +10,8 @@ gap: 8px; } -.file-card.trash-item:hover .trash-actions, -.file-item.trash-item:hover .actions-cell { +.files-grid-view .file-card.trash-item:hover .trash-actions, +.files-list-view .file-item.trash-item:hover .actions-cell { display: flex; } diff --git a/static/index.html b/static/index.html index 3989ff6b..a4af491e 100644 --- a/static/index.html +++ b/static/index.html @@ -259,29 +259,12 @@
- -
- -
- -
@@ -338,6 +321,8 @@ +
+ diff --git a/static/js/app/filesView.js b/static/js/app/filesView.js index f34e2bcd..14ed1051 100644 --- a/static/js/app/filesView.js +++ b/static/js/app/filesView.js @@ -128,12 +128,12 @@ async function loadFiles(options = { insertHistory: true}) { // This to avoid blinking page, a better solution would be to put loading on an overlay and remove timeout let loadingFiles = setTimeout( () => { // display loader after few delay (will be canceled if result take less time) - elements.filesGrid.innerHTML = ` + window.ui.showError(`
${window.i18n ? window.i18n.t('files.loading') : 'Loading files…'}
- `; + `) }, 100); if (!app.userHomeFolderId) { @@ -195,16 +195,8 @@ async function loadFiles(options = { insertHistory: true}) { if (response.status === 401 || response.status === 403) { console.warn("Auth error when loading files, showing empty list"); - elements.filesGrid.innerHTML = '

Could not load files

'; - elements.filesListView.innerHTML = ` -
-
-
Name
-
Type
-
Size
-
Modified
-
- `; + // FIXME: i18n + window.ui.showError(`

Could not load files

`); return; } @@ -216,17 +208,17 @@ async function loadFiles(options = { insertHistory: true}) { if (window.multiSelect) window.multiSelect.clear(); window.ui._items.clear(); - elements.filesGrid.innerHTML = ''; + const _t = (window.i18n && window.i18n.t) ? window.i18n.t : k => k.split('.').pop(); - elements.filesListView.innerHTML = ` + window.ui.showError(`
${_t('files.name')}
${_t('files.type')}
${_t('files.size')}
${_t('files.modified')}
-
- `; + ` + ); const selectAllCb = document.getElementById('select-all-checkbox'); if (selectAllCb && window.multiSelect) { @@ -237,9 +229,9 @@ async function loadFiles(options = { insertHistory: true}) { const fileList = Array.isArray(listing.files) ? listing.files : []; if (folderList.length === 0 && fileList.length === 0) { - window.showEmptyList(true); + window.ui.showEmptyList(); } else { - window.showEmptyList(false); + window.ui.resetFilesList(); window.ui.renderFolders(folderList); window.ui.renderFiles(fileList); } diff --git a/static/js/app/main.js b/static/js/app/main.js index 6f39aab4..436c11a1 100644 --- a/static/js/app/main.js +++ b/static/js/app/main.js @@ -408,8 +408,7 @@ function cacheElements() { elements.uploadBtn = document.getElementById('upload-btn'); elements.dropzone = document.getElementById('dropzone'); elements.fileInput = document.getElementById('file-input'); - elements.filesGrid = document.getElementById('files-grid'); - elements.filesListView = document.getElementById('files-list-view'); + elements.filesList = document.getElementById('files-list'); elements.newFolderBtn = document.getElementById('new-folder-btn'); elements.gridViewBtn = document.getElementById('grid-view-btn'); elements.listViewBtn = document.getElementById('list-view-btn'); diff --git a/static/js/app/navigation.js b/static/js/app/navigation.js index d1d8e98c..dfdb727c 100644 --- a/static/js/app/navigation.js +++ b/static/js/app/navigation.js @@ -8,27 +8,34 @@ * based on the current view preference. */ function syncViewContainers() { - const filesGrid = document.getElementById('files-grid'); - const filesListView = document.getElementById('files-list-view'); + const filesList = document.getElementById('files-list'); + const gridViewBtn = document.getElementById('grid-view-btn'); + const listViewBtn = document.getElementById('list-view-btn'); + const isGrid = window.app.currentView === 'grid'; - if (filesGrid) { - filesGrid.style.display = isGrid ? 'grid' : 'none'; - filesGrid.classList.toggle('hidden', !isGrid); + if (isGrid) { + filesList.classList.remove("files-list-view"); + filesList.classList.add("files-grid-view"); + + gridViewBtn?.classList.add('active'); + listViewBtn?.classList.remove('active'); } - if (filesListView) { - filesListView.style.display = isGrid ? 'none' : 'flex'; - filesListView.classList.toggle('hidden', isGrid); + else { + filesList.classList.add("files-list-view"); + filesList.classList.remove("files-grid-view"); + + gridViewBtn?.classList.remove('active'); + listViewBtn?.classList.add('active'); } } /** - * Hide both grid and list containers (used when switching to non-file views). + * Hide file containers (used when switching to non-file views). + * @param {boolean} show false to hide, true to show */ -function hideFileContainers() { - const filesGrid = document.getElementById('files-grid'); - const filesListView = document.getElementById('files-list-view'); - if (filesGrid) { filesGrid.style.display = 'none'; filesGrid.classList.add('hidden'); } - if (filesListView) { filesListView.style.display = 'none'; filesListView.classList.add('hidden'); } +function toggleFileContainer(show) { + const filesList = document.getElementById('files-list'); + filesList.classList.toggle('hidden', !show); } /** @@ -162,20 +169,15 @@ function switchToSharedSection() { // Hide breadcrumb (only shown in Files view) const breadcrumb = document.querySelector('.breadcrumb'); - if (breadcrumb) breadcrumb.style.display = 'none'; + breadcrumb?.classList.add("hidden"); // Hide actions-bar for shared view window.setActionsBarMode('hidden'); - const filesGrid = document.getElementById('files-grid'); - const filesListView = document.getElementById('files-list-view'); - if (filesGrid) filesGrid.style.display = 'none'; - if (filesListView) filesListView.style.display = 'none'; - if (filesGrid) filesGrid.classList.add('hidden'); - if (filesListView) filesListView.classList.add('hidden'); + toggleFileContainer(false); - //hide by default empty list - window.showEmptyList(false); + //reset files view + remove any error + window.ui.resetFilesList(); // Show shared view if (window.sharedView) { @@ -194,17 +196,16 @@ function switchToFilesSection() { // Show breadcrumb (only in Files view) const breadcrumb = document.querySelector('.breadcrumb'); - if (breadcrumb) breadcrumb.style.display = ''; + breadcrumb?.classList.remove("hidden"); - const filesGrid = document.getElementById('files-grid'); - const filesListView = document.getElementById('files-list-view'); - if (filesGrid) filesGrid.style.display = window.app.currentView === 'grid' ? 'grid' : 'none'; - if (filesGrid) filesGrid.classList.toggle('hidden', window.app.currentView !== 'grid'); - if (filesListView) filesListView.style.display = window.app.currentView === 'list' ? 'flex' : 'none'; - if (filesListView) filesListView.classList.toggle('hidden', window.app.currentView !== 'list'); + // show files container + toggleFileContainer(true); - //hide by default empty list - window.showEmptyList(false); + // ensure correct view + syncViewContainers(); + + //reset files view + remove any error + window.ui.resetFilesList(); // Reset to home folder and update breadcrumb window.app.currentPath = window.app.userHomeFolderId || ''; @@ -223,31 +224,26 @@ function switchToFavoritesSection() { // Hide breadcrumb (only shown in Files view) const breadcrumb = document.querySelector('.breadcrumb'); - if (breadcrumb) breadcrumb.style.display = 'none'; + breadcrumb?.classList.add("hidden"); - const filesGrid = document.getElementById('files-grid'); - const filesListView = document.getElementById('files-list-view'); - if (filesGrid) filesGrid.style.display = window.app.currentView === 'grid' ? 'grid' : 'none'; - if (filesGrid) filesGrid.classList.toggle('hidden', window.app.currentView !== 'grid'); - if (filesListView) filesListView.style.display = window.app.currentView === 'list' ? 'flex' : 'none'; - if (filesListView) filesListView.classList.toggle('hidden', window.app.currentView !== 'list'); + // show files container + toggleFileContainer(true); + + // ensure correct view + syncViewContainers(); - //hide by default empty list - window.showEmptyList(false); + //reset files view + remove any error + window.ui.resetFilesList(); if (window.favorites) { window.favorites.displayFavorites(); } else { console.error('Favorites module not loaded or initialized'); - const filesGridError = document.getElementById('files-grid'); - if (filesGridError) { - filesGridError.innerHTML = ` -
- -

Error loading the favorites module

-
- `; - } + window.ui.showError(` + +

Error loading the favorites module

+ ` + ); } if (window.multiSelect) window.multiSelect.clear(); @@ -262,31 +258,26 @@ function switchToRecentFilesSection() { // Hide breadcrumb (only shown in Files view) const breadcrumb = document.querySelector('.breadcrumb'); - if (breadcrumb) breadcrumb.style.display = 'none'; + breadcrumb?.classList.add("hidden"); - const filesGrid = document.getElementById('files-grid'); - const filesListView = document.getElementById('files-list-view'); - if (filesGrid) filesGrid.style.display = window.app.currentView === 'grid' ? 'grid' : 'none'; - if (filesGrid) filesGrid.classList.toggle('hidden', window.app.currentView !== 'grid'); - if (filesListView) filesListView.style.display = window.app.currentView === 'list' ? 'flex' : 'none'; - if (filesListView) filesListView.classList.toggle('hidden', window.app.currentView !== 'list'); + // show files container + toggleFileContainer(true); - //hide by default empty list - window.showEmptyList(false); + // ensure correct view + syncViewContainers(); + + //reset files view + remove any error + window.ui.resetFilesList(); if (window.recent) { window.recent.displayRecentFiles(); } else { console.error('Recent files module not loaded or initialized'); - const filesGridError = document.getElementById('files-grid'); - if (filesGridError) { - filesGridError.innerHTML = ` -
- -

Error loading the recent files module

-
- `; - } + window.ui.showError(` + +

Error loading the recent module

+ ` + ); } if (window.multiSelect) window.multiSelect.clear(); } @@ -296,19 +287,16 @@ function switchToPhotosSection() { // Hide breadcrumb const breadcrumb = document.querySelector('.breadcrumb'); - if (breadcrumb) breadcrumb.style.display = 'none'; + breadcrumb?.classList.add("hidden"); + + // Hide file containers + toggleFileContainer(false); // Hide actions-bar (photos has its own upload via selection bar) window.setActionsBarMode('hidden'); - // Hide file containers - const filesGrid = document.getElementById('files-grid'); - const filesListView = document.getElementById('files-list-view'); - if (filesGrid) { filesGrid.style.display = 'none'; filesGrid.classList.add('hidden'); } - if (filesListView) { filesListView.style.display = 'none'; filesListView.classList.add('hidden'); } - - //hide by default empty list - window.showEmptyList(false); + //reset files view + remove any error + window.ui.resetFilesList(); // Show photos view if (window.photosView) { @@ -323,18 +311,19 @@ function switchToTrashSection() { // Hide breadcrumb (only shown in Files view) const breadcrumb = document.querySelector('.breadcrumb'); - if (breadcrumb) breadcrumb.style.display = 'none'; + breadcrumb?.classList.add("hidden"); // Show files containers (to be filled with trash) - const filesGrid = document.getElementById('files-grid'); - const filesListView = document.getElementById('files-list-view'); - if (filesGrid) { filesGrid.style.display = app.currentView === 'grid' ? 'grid' : 'none'; filesGrid.classList.toggle('hidden', app.currentView !== 'grid'); } - if (filesListView) { filesListView.style.display = app.currentView === 'list' ? 'flex' : 'none'; filesListView.classList.toggle('hidden', app.currentView !== 'list'); } + const filesList = document.getElementById('files-list'); + filesList.classList.remove('hidden'); setActionsBarMode('trash'); - //hide by default empty list - window.showEmptyList(false); + //reset files view + remove any error + window.ui.resetFilesList(); + + //ensure buttons match the current view + syncViewContainers(); // Load trash items window.loadTrashItems(); @@ -348,3 +337,4 @@ window.switchToFavoritesSection = switchToFavoritesSection; window.switchToRecentFilesSection = switchToRecentFilesSection; window.switchToPhotosSection = switchToPhotosSection; window.switchToTrashSection = switchToTrashSection; +window.syncViewContainers = syncViewContainers; diff --git a/static/js/app/searchView.js b/static/js/app/searchView.js index 41560cc0..5221eff9 100644 --- a/static/js/app/searchView.js +++ b/static/js/app/searchView.js @@ -11,14 +11,10 @@ async function performSearch(query, sortBy) { app.isSearchMode = true; window.ui.updateBreadcrumb(`Search: "${query}"`); - const filesGrid = document.getElementById('files-grid'); - if (filesGrid) { - filesGrid.innerHTML = ` -
-

Searching for "${query}"...

-
- `; - } + window.ui.showError( + `

Searching for "${query}"...

` + ); + const options = { recursive: true, diff --git a/static/js/app/trashView.js b/static/js/app/trashView.js index 50dbb2a9..6f397631 100644 --- a/static/js/app/trashView.js +++ b/static/js/app/trashView.js @@ -7,9 +7,9 @@ async function loadTrashItems() { try { if (window.multiSelect) window.multiSelect.clear(); - elements.filesGrid.innerHTML = ''; + window.ui.resetFilesList(); // ensure also list visible & error hidden const _tt = (window.i18n && window.i18n.t) ? window.i18n.t : k => k.split('.').pop(); - elements.filesListView.innerHTML = ` + elements.filesList.innerHTML = `
${_tt('files.name')}
${_tt('files.type')}
@@ -24,13 +24,10 @@ async function loadTrashItems() { const trashItems = await window.fileOps.getTrashItems(); if (trashItems.length === 0) { - const emptyState = document.createElement('div'); - emptyState.className = 'empty-state'; - emptyState.innerHTML = ` + window.ui.showError(`

${window.i18n ? window.i18n.t('trash.empty_state') : 'The trash is empty'}

- `; - elements.filesGrid.appendChild(emptyState); + `); return; } @@ -74,43 +71,6 @@ function addTrashItemToView(item) { ? 'file-icon folder-icon' : `file-icon ${iconSpecialClass}`.trim(); - const gridElement = document.createElement('div'); - gridElement.className = 'file-card trash-item'; - gridElement.dataset.trashId = item.id; - gridElement.dataset.originalId = item.original_id; - gridElement.dataset.itemType = item.item_type; - gridElement.innerHTML = ` -
- -
-
${escapeHtml(item.name)}
-
${escapeHtml(typeLabel)} - ${escapeHtml(formattedDate)}
-
- - -
- `; - - gridElement.querySelector('.btn-restore').addEventListener('click', async (e) => { - e.stopPropagation(); - if (await window.fileOps.restoreFromTrash(item.id)) { - window.loadTrashItems(); - } - }); - - gridElement.querySelector('.btn-delete').addEventListener('click', async (e) => { - e.stopPropagation(); - if (await window.fileOps.deletePermanently(item.id)) { - window.loadTrashItems(); - } - }); - - elements.filesGrid.appendChild(gridElement); - const listElement = document.createElement('div'); listElement.className = 'file-item trash-item'; listElement.dataset.trashId = item.id; @@ -151,7 +111,7 @@ function addTrashItemToView(item) { } }); - elements.filesListView.appendChild(listElement); + elements.filesList.appendChild(listElement); } window.loadTrashItems = loadTrashItems; diff --git a/static/js/app/ui.js b/static/js/app/ui.js index 894d17ac..4362b166 100644 --- a/static/js/app/ui.js +++ b/static/js/app/ui.js @@ -7,6 +7,10 @@ // UI Module const ui = { + + /** @type {HTMLDListElement | null} */ + //dragPreview, + /** * Initialize context menus and dialogs */ @@ -450,42 +454,24 @@ const ui = { * Switch to grid view */ switchToGridView() { - const filesGrid = document.getElementById('files-grid'); - const filesListView = document.getElementById('files-list-view'); - const gridViewBtn = document.getElementById('grid-view-btn'); - const listViewBtn = document.getElementById('list-view-btn'); + this._hydrateViewIfNeeded(); - this._hydrateViewIfNeeded('grid'); - - filesGrid.style.display = 'grid'; - filesGrid.classList.remove('hidden'); - filesListView.style.display = 'none'; - filesListView.classList.add('hidden'); - gridViewBtn.classList.add('active'); - listViewBtn.classList.remove('active'); window.app.currentView = 'grid'; localStorage.setItem('oxicloud-view', 'grid'); + + window.syncViewContainers(); }, /** * Switch to list view */ switchToListView() { - const filesGrid = document.getElementById('files-grid'); - const filesListView = document.getElementById('files-list-view'); - const gridViewBtn = document.getElementById('grid-view-btn'); - const listViewBtn = document.getElementById('list-view-btn'); + this._hydrateViewIfNeeded(); - this._hydrateViewIfNeeded('list'); - - filesGrid.style.display = 'none'; - filesGrid.classList.add('hidden'); - filesListView.style.display = 'flex'; - filesListView.classList.remove('hidden'); - gridViewBtn.classList.remove('active'); - listViewBtn.classList.add('active'); window.app.currentView = 'list'; localStorage.setItem('oxicloud-view', 'list'); + + window.syncViewContainers(); }, /** @@ -679,34 +665,36 @@ const ui = { return stored === 'list' ? 'list' : 'grid'; }, - _renderFoldersToView(folders, view) { + /** + * + * @param {Object[]} folders + * @returns + */ + _renderFoldersToView(folders) { if (!Array.isArray(folders) || folders.length === 0) return; - const target = view === 'list' - ? document.getElementById('files-list-view') - : document.getElementById('files-grid'); + const target = document.getElementById('files-list') if (!target) return; const frag = document.createDocumentFragment(); for (const folder of folders) { - frag.appendChild(view === 'list' - ? this._createFolderItem(folder) - : this._createFolderCard(folder)); + frag.appendChild( this._createFolderItem(folder)); } target.appendChild(frag); }, - _renderFilesToView(files, view) { + /** + * + * @param {Object[]} files + * @returns + */ + _renderFilesToView(files) { if (!Array.isArray(files) || files.length === 0) return; - const target = view === 'list' - ? document.getElementById('files-list-view') - : document.getElementById('files-grid'); + const target = document.getElementById('files-list') if (!target) return; const frag = document.createDocumentFragment(); for (const file of files) { - frag.appendChild(view === 'list' - ? this._createFileItem(file) - : this._createFileCard(file)); + frag.appendChild( this._createFileItem(file)) } target.appendChild(frag); }, @@ -729,7 +717,7 @@ const ui = { */ async _dropToFolder(action, targetFolderId, dataTransfer) { let selection = window.multiSelect.getSelection(targetFolderId); - + window.multiSelect.clear(); if (selection.fileIds.length == 0 && selection.folderIds.length == 0) { @@ -766,57 +754,45 @@ const ui = { result = await window.fileOps.batchCopy(selection.fileIds, selection.folderIds, targetFolderId); break; - case "move": + case "move": result = await window.fileOps.batchMove(selection.fileIds, selection.folderIds, targetFolderId); - // redraw directory + // redraw directory if (result.success > 0) window.loadFiles(); break; default: - console.error(`drag and drop: action ${action} unknown`); - return; + console.error(`drag and drop: action ${action} unknown`); + return; } window.multiSelect.showBatchResult(action, result); console.log( result); }, - - _hydrateViewIfNeeded(view) { + + _hydrateViewIfNeeded() { // Only hydrate if there is at least one rendered item in the opposite/current DOM. // This prevents stale cache hydration in empty-state screens. - const hasAnyRenderedItem = !!document.querySelector('#files-grid .file-card, #files-list-view .file-item'); + const hasAnyRenderedItem = !!document.querySelector('#files-list .file-item'); if (!hasAnyRenderedItem) return; - if (view === 'grid') { - const grid = document.getElementById('files-grid'); - if (!grid) return; - if (grid.children.length > 0) return; + // FIXME: thre is the header... + const listView = document.getElementById('files-list'); + if (!listView) return; + if (listView.children.length > 1) return; - this._renderFoldersToView(this._lastFolders, 'grid'); - this._renderFilesToView(this._lastFiles, 'grid'); - } - - if (view === 'list') { - const list = document.getElementById('files-list-view'); - if (!list) return; - // list view keeps a static header row as first child - if (list.querySelector('.file-item')) return; - - this._renderFoldersToView(this._lastFolders, 'list'); - this._renderFilesToView(this._lastFiles, 'list'); - } + this._renderFoldersToView(this._lastFolders); + this._renderFilesToView(this._lastFiles); }, /** * Attach a fixed set of delegated event listeners to the two - * container elements (files-grid, files-list-view). + * container elements (files-list). * Called once – idempotent. */ initDelegation() { if (this._delegationReady) return; - const grid = document.getElementById('files-grid'); - const list = document.getElementById('files-list-view'); - if (!grid || !list) return; + const filesList = document.getElementById('files-list'); + if (!filesList) return; this._delegationReady = true; const self = this; @@ -878,12 +854,12 @@ const ui = { } }; - // ── GRID: click (open / navigate; select only via checkbox) ── - grid.addEventListener('click', (e) => { - const card = e.target.closest('.file-card'); + // ── click (open / navigate; select only via checkbox) ── + filesList.addEventListener('click', (e) => { + const card = e.target.closest('.file-item'); if (!card) return; - if (e.target.closest('.file-card-more')) { + if (e.target.closest('.file-actions')) { e.stopPropagation(); e.preventDefault(); const info = itemInfo(card); @@ -892,11 +868,11 @@ const ui = { const menuId = info.type === 'folder' ? 'folder-context-menu' : 'file-context-menu'; showContextMenuAtElement( - e.target.closest('.file-card-more'), menuId); + e.target.closest('.file-actions'), menuId); return; } - if (e.target.closest('.file-card-checkbox')) { + if (e.target.closest('.checkbox-cell')) { toggleCardSelection(card, e); return; } @@ -916,178 +892,144 @@ const ui = { }); // ── GRID: dblclick (navigate / open) ────────────────────── - grid.addEventListener('dblclick', (e) => { + filesList.addEventListener('dblclick', (e) => { // Single-click already handles open/navigate. // Prevent duplicate actions on double-click. e.preventDefault(); }); - // ── LIST: click (navigate / open) ───────────────────────── - list.addEventListener('click', (e) => { - if (e.target.closest('.list-header')) return; + // ── shared events ────────────────────── + + filesList.addEventListener('contextmenu', (e) => { const card = e.target.closest('.file-item'); if (!card) return; - - if (e.target.closest('.file-more')) { - e.stopPropagation(); - e.preventDefault(); - const info = itemInfo(card); - if (!info) return; - setContextTarget(card, info); - const menuId = info.type === 'folder' - ? 'folder-context-menu' : 'file-context-menu'; - showContextMenuAtElement( - e.target.closest('.file-more'), menuId); - return; - } - - if (e.target.closest('.list-item-checkbox') || - e.target.closest('.item-checkbox')) { - toggleCardSelection(card, e); - return; - } - + e.preventDefault(); const info = itemInfo(card); if (!info) return; - - if (info.type === 'folder') { - navigateFolder(card); - } else { - openFile(info.data); + setContextTarget(card, info); + const menuId = info.type === 'folder' + ? 'folder-context-menu' : 'file-context-menu'; + const menu = document.getElementById(menuId); + if (window.contextMenus && typeof window.contextMenus.syncFavoriteOptionLabels === 'function') { + window.contextMenus.syncFavoriteOptionLabels(); } + if (window.contextMenus && typeof window.contextMenus.syncWopiOptionVisibility === 'function') { + window.contextMenus.syncWopiOptionVisibility().catch(function(){}); + } + menu.style.left = `${e.pageX}px`; + menu.style.top = `${e.pageY}px`; + menu.style.display = 'block'; }); - // ── shared events on both containers ────────────────────── - for (const container of [grid, list]) { - const sel = container === grid ? '.file-card' : '.file-item'; - - // contextmenu - container.addEventListener('contextmenu', (e) => { - const card = e.target.closest(sel); - if (!card) return; - e.preventDefault(); - const info = itemInfo(card); - if (!info) return; - setContextTarget(card, info); - const menuId = info.type === 'folder' - ? 'folder-context-menu' : 'file-context-menu'; - const menu = document.getElementById(menuId); - if (window.contextMenus && typeof window.contextMenus.syncFavoriteOptionLabels === 'function') { - window.contextMenus.syncFavoriteOptionLabels(); - } - if (window.contextMenus && typeof window.contextMenus.syncWopiOptionVisibility === 'function') { - window.contextMenus.syncWopiOptionVisibility().catch(function(){}); - } - menu.style.left = `${e.pageX}px`; - menu.style.top = `${e.pageY}px`; - menu.style.display = 'block'; - }); - - // dragstart - container.addEventListener('dragstart', (e) => { - let card = e.target.closest(sel); - if (!card) { e.preventDefault(); return; } - - const info = itemInfo(card); - if (!info) { e.preventDefault(); return; } - - if (container === grid) { - // pickup the equivalent item in list - const selector = (info.type === 'folder') ? "data-folder-id" : "data-file-id"; - card = list.querySelector(`div.file-item[${selector}="${info.id}"]`); - } - - e.dataTransfer.setData('text/plain', info.id); - if (info.type === 'folder') { - e.dataTransfer.setData( - 'application/oxicloud-folder', 'true'); - } - // allow copy or move (handled by the browser) - e.dataTransfer.effectAllowed = "copyMove"; - - self.draggedItems = document.createElement("div"); - self.draggedItems.className = "dragged-items"; - - // Fplease note that dragged elements are taken from list view (uniformisation) - let selectedCardFromList = list.querySelectorAll(`div.selected > div.name-cell`); - if (selectedCardFromList.length == 0) { - // fallback to current element - selectedCardFromList = card.querySelectorAll('div.name-cell'); - } - - let index = 0; - const maxElements = 4; - let lastItemDiv = null; - - while (index < selectedCardFromList.length && index < maxElements) { - let div = document.createElement("div"); - div.className="file-item"; - let clone = selectedCardFromList[index].cloneNode(true); - let star = clone.querySelector('.favorite-star-inline'); - clone.querySelectorAll('img').forEach((img) => { img.loading="eager"; } ); - if (star) clone.removeChild(star); - div.appendChild(clone); - self.draggedItems.appendChild( div); - index += 1; - lastItemDiv = div; - } - - // if more than 1 item, display the badge - if (selectedCardFromList.length > 1) { - let badge = document.createElement("span"); - badge.className="dragged-items-badge"; - badge.innerText=selectedCardFromList.length; - self.draggedItems.appendChild( badge); - } - - // if more than maxElements display the fading - if (selectedCardFromList.length > maxElements) { - lastItemDiv.classList.add("fading"); - } - - self.dragPreview.appendChild( self.draggedItems); - e.dataTransfer.setDragImage(self.draggedItems, -20, -20); + // dragstart + filesList.addEventListener('dragstart', (e) => { + let card = e.target.closest('.file-item'); + if (!card) { e.preventDefault(); return; } - }); + const info = itemInfo(card); + if (!info) { e.preventDefault(); return; } - // dragend - container.addEventListener('dragend', (e) => { - - self.dragPreview.removeChild( self.draggedItems); - document.querySelectorAll('.drop-target') - .forEach(el => el.classList.remove('drop-target')); - }); + e.dataTransfer.setData('text/plain', info.id); + if (info.type === 'folder') { + e.dataTransfer.setData( + 'application/oxicloud-folder', 'true'); + } + // allow copy or move (handled by the browser) + e.dataTransfer.effectAllowed = "copyMove"; + + self.draggedItems = document.createElement("div"); + self.draggedItems.className = "dragged-items"; + + let selectedCardFromList = filesList.querySelectorAll(`div.selected > div.name-cell`); + if (selectedCardFromList.length == 0) { + // fallback to current element + selectedCardFromList = card.querySelectorAll('div.name-cell'); + } + + let index = 0; + const maxElements = 4; + let lastItemDiv = null; - // dragover – only folders are valid drop targets - container.addEventListener('dragover', (e) => { - const card = e.target.closest(sel); - if (!card || card.dataset.fileId) return; - if (!card.dataset.folderId) return; - e.preventDefault(); - card.classList.add('drop-target'); - }); + while (index < selectedCardFromList.length && index < maxElements) { + let iconCell = document.createElement("div"); + let icon = selectedCardFromList[index].getElementsByClassName("file-icon").item(0)?.cloneNode(true); + if (icon) { + iconCell.appendChild( icon); + iconCell.querySelectorAll('img')?.forEach((img) => { img.loading="eager"; } ); + } - // dragleave - container.addEventListener('dragleave', (e) => { - const card = e.target.closest(sel); - if (!card || card.dataset.fileId) return; - card.classList.remove('drop-target'); - }); + let nameCell = document.createElement("div"); + let name = selectedCardFromList[index].getElementsByTagName("span").item(0)?.cloneNode(true); + if (name) { + nameCell.appendChild(name); + } - // drop – only folders accept drops - container.addEventListener('drop', async (e) => { - const card = e.target.closest(sel); - if (!card || card.dataset.fileId) return; - const targetFolderId = card.dataset.folderId; - if (!targetFolderId) return; + let div = document.createElement("div"); + div.className="file-item"; + div.appendChild(iconCell); + div.appendChild(nameCell); - e.preventDefault(); - card.classList.remove('drop-target'); + self.draggedItems.appendChild( div); + index += 1; + lastItemDiv = div; + } - const action = e.dataTransfer.dropEffect; - await self._dropToFolder( action, targetFolderId, e.dataTransfer); - }); - } + // if more than 1 item, display the badge + if (selectedCardFromList.length > 1) { + let badge = document.createElement("span"); + badge.className="dragged-items-badge"; + badge.innerText=`${selectedCardFromList.length}`; + self.draggedItems.appendChild( badge); + } + + // if more than maxElements display the fading + if (selectedCardFromList.length > maxElements) { + lastItemDiv.classList.add("fading"); + } + + self.dragPreview.appendChild( self.draggedItems); + e.dataTransfer.setDragImage(self.draggedItems, 0, 0); + + }); + + // dragend + filesList.addEventListener('dragend', (e) => { + + self.dragPreview.removeChild( self.draggedItems); + document.querySelectorAll('.drop-target') + .forEach(el => el.classList.remove('drop-target')); + }); + + // dragover – only folders are valid drop targets + filesList.addEventListener('dragover', (e) => { + const card = e.target.closest('.file-item'); + if (!card || card.dataset.fileId) return; + if (!card.dataset.folderId) return; + e.preventDefault(); + card.classList.add('drop-target'); + }); + + // dragleave + filesList.addEventListener('dragleave', (e) => { + const card = e.target.closest('.file-item'); + if (!card || card.dataset.fileId) return; + card.classList.remove('drop-target'); + }); + + // drop – only folders accept drops + filesList.addEventListener('drop', async (e) => { + const card = e.target.closest('.file-item'); + if (!card || card.dataset.fileId) return; + const targetFolderId = card.dataset.folderId; + if (!targetFolderId) return; + + e.preventDefault(); + card.classList.remove('drop-target'); + + const action = e.dataTransfer.dropEffect; + await self._dropToFolder( action, targetFolderId, e.dataTransfer); + }); + }, /* ================================================================ @@ -1130,15 +1072,11 @@ const ui = { * Sync favorite visuals for a file/folder across grid and list views. */ setFavoriteVisualState(itemId, itemType, isFavorite) { - const cardSelector = itemType === 'folder' - ? `.file-card[data-folder-id="${itemId}"]` - : `.file-card[data-file-id="${itemId}"]`; + const selector = itemType === 'folder' + ? `#files-list .file-item[data-folder-id="${itemId}"]` + : `#files-list .file-item[data-file-id="${itemId}"]`; - const listSelector = itemType === 'folder' - ? `.file-item[data-folder-id="${itemId}"]` - : `.file-item[data-file-id="${itemId}"]`; - - const card = document.querySelector(cardSelector); + const card = document.querySelector(selector); const starBtn = card ? card.querySelector('.favorite-star') : null; if (starBtn) { @@ -1162,8 +1100,8 @@ const ui = { i.classList.add(isFavorite ? 'fas' : 'far'); } } - - const listItem = document.querySelector(listSelector); + + const listItem = document.querySelector(selector); if (listItem) { const nameCell = listItem.querySelector('.name-cell'); if (nameCell) { @@ -1186,37 +1124,6 @@ const ui = { * Element-creation helpers * ================================================================ */ - /** Create a grid card for a folder */ - _createFolderCard(folder) { - const el = document.createElement('div'); - el.className = 'file-card'; - el.dataset.folderId = folder.id; - el.dataset.folderName = folder.name; - el.dataset.parentId = folder.parent_id || ""; - - const isFav = window.favorites && - window.favorites.isFavorite(folder.id, 'folder'); - - el.innerHTML = ` -
- - -
- -
-
${escapeHtml(folder.name)}
-
${window.i18n ? window.i18n.t('files.file_types.folder') : 'Folder'}
- `; - - if (window.app.currentPath !== "") { - el.setAttribute('draggable', 'true'); - } - this._bindStarClick(el); - return el; - }, - /** Create a list row for a folder */ _createFolderItem(folder) { const el = document.createElement('div'); @@ -1225,16 +1132,11 @@ const ui = { el.dataset.folderName = folder.name; el.dataset.parentId = folder.parent_id || ""; - const isFav = window.favorites && - window.favorites.isFavorite(folder.id, 'folder'); + const isFav = window.favorites && window.favorites.isFavorite(folder.id, 'folder'); const formattedDate = window.formatDateTime(folder.modified_at); - if (window.app.currentPath !== "") { - el.setAttribute('draggable', 'true'); - } - el.innerHTML = ` -
+
@@ -1245,61 +1147,32 @@ const ui = {
${window.i18n ? window.i18n.t('files.file_types.folder') : 'Folder'}
--
${formattedDate}
-
- `; - return el; - }, - - /** Create a grid card for a file */ - _createFileCard(file) { - const iconClass = file.icon_class || this.getIconClass(file.name); - const iconSpecialClass = file.icon_special_class || this.getIconSpecialClass(file.name); - const isFileFav = window.favorites && - window.favorites.isFavorite(file.id, 'file'); - const formattedDate = window.formatDateTime(file.modified_at); - - const el = document.createElement('div'); - el.className = 'file-card'; - el.dataset.fileId = file.id; - el.dataset.fileName = file.name; - el.dataset.folderId = file.folder_id || ""; - el.setAttribute('draggable', 'true'); - - el.innerHTML = ` -
- - -
- ${iconSpecialClass === 'image-icon' ? `` : ''} - +
+ +
-
${escapeHtml(file.name)}
-
Modified ${formattedDate.split(' ')[0]}
`; - var thumb = el.querySelector('.file-thumb'); - if (thumb) thumb.addEventListener('error', function() { this.style.display = 'none'; }); + + if (window.app.currentPath !== "") { + el.setAttribute('draggable', 'true'); + } this._bindStarClick(el); return el; }, - /** Create a list row for a file */ + /** Create a grid card for a file */ _createFileItem(file) { const iconClass = file.icon_class || this.getIconClass(file.name); const iconSpecialClass = file.icon_special_class || this.getIconSpecialClass(file.name); const cat = file.category || ''; const typeLabel = cat - ? (window.i18n - ? window.i18n.t(`files.file_types.${cat.toLowerCase()}`) || cat - : cat) - : (window.i18n - ? window.i18n.t('files.file_types.document') - : 'Document'); + ? (window.i18n ? window.i18n.t(`files.file_types.${cat.toLowerCase()}`) || cat : cat) + : (window.i18n ? window.i18n.t('files.file_types.document') : 'Document'); const fileSize = file.size_formatted || window.formatFileSize(file.size); const formattedDate = window.formatDateTime(file.modified_at); - const isFileFav = window.favorites && - window.favorites.isFavorite(file.id, 'file'); + const isFav = window.favorites && window.favorites.isFavorite(file.id, 'file'); const el = document.createElement('div'); el.className = 'file-item'; @@ -1309,22 +1182,29 @@ const ui = { el.setAttribute('draggable', 'true'); el.innerHTML = ` -
+ +
${iconSpecialClass === 'image-icon' ? `` : ''}
${escapeHtml(file.name)} - ${isFileFav ? '' : ''} + ${isFav ? '' : ''}
${typeLabel}
${fileSize}
${formattedDate}
-
+
+ + +
`; var thumb = el.querySelector('.file-thumb'); if (thumb) thumb.addEventListener('error', function() { this.style.display = 'none'; }); + this._bindStarClick(el); return el; }, @@ -1332,6 +1212,55 @@ const ui = { * Batch rendering with DocumentFragment * ================================================================ */ + resetFilesList() { + const filesList = document.getElementById('files-list'); + const filesContainerError=document.getElementById("files-container-error"); + + if (!filesList) return; + filesList.innerHTML=` +
+
+
Name
+
Type
+
Size
+
Modified
+
+
`; + + if (window.i18n && window.i18n.translateElement) + window.i18n.translateElement( filesList); + + filesList.classList.remove("hidden"); + filesContainerError?.classList.add("hidden"); + }, + + showEmptyList() { + this.showError(` + +

+

+ ` + ); + }, + + /** + * + * @param {string} content + * + */ + showError(content) { + const filesContainerError=document.getElementById("files-container-error"); + const filesList=document.getElementById("files-list"); + if (filesContainerError) + filesContainerError.innerHTML=content; + + if (window.i18n && window.i18n.translateElement) + window.i18n.translateElement( filesContainerError); + + filesContainerError?.classList.remove("hidden"); + filesList?.classList.add("hidden"); + }, + /** * Render an array of folders into both grid and list views * using DocumentFragment for minimal reflows. @@ -1345,8 +1274,7 @@ const ui = { this._items.set(folder.id, folder); } - this._renderFoldersToView(safeFolders, 'grid'); - this._renderFoldersToView(safeFolders, 'list'); + this._renderFoldersToView(safeFolders); }, /** @@ -1362,8 +1290,7 @@ const ui = { this._items.set(file.id, file); } - this._renderFilesToView(safeFiles, 'grid'); - this._renderFilesToView(safeFiles, 'list'); + this._renderFilesToView(safeFiles); }, /* ================================================================ @@ -1378,16 +1305,14 @@ const ui = { if (!this._delegationReady) this.initDelegation(); // Duplicate guard - if (document.querySelector(`.file-card[data-folder-id="${folder.id}"]`) || - document.querySelector(`.file-item[data-folder-id="${folder.id}"]`)) { + if (document.querySelector(`.file-item[data-folder-id="${folder.id}"]`)) { console.log(`Folder ${folder.name} (${folder.id}) already exists in the view, not duplicating`); return; } this._items.set(folder.id, folder); this._upsertById(this._lastFolders, folder); - this._renderFoldersToView([folder], 'grid'); - this._renderFoldersToView([folder], 'list'); + this._renderFoldersToView([folder]); }, /** @@ -1398,16 +1323,14 @@ const ui = { if (!this._delegationReady) this.initDelegation(); // Duplicate guard - if (document.querySelector(`.file-card[data-file-id="${file.id}"]`) || - document.querySelector(`.file-item[data-file-id="${file.id}"]`)) { + if (document.querySelector(`.file-item[data-file-id="${file.id}"]`)) { console.log(`File ${file.name} (${file.id}) already exists in the view, not duplicating`); return; } this._items.set(file.id, file); this._upsertById(this._lastFiles, file); - this._renderFilesToView([file], 'grid'); - this._renderFilesToView([file], 'list'); + this._renderFilesToView([file]); } }; @@ -1476,16 +1399,15 @@ function initRubberBandSelection() { let active = false; let startX = 0, startY = 0; - const list = document.getElementById('files-list-view'); // We listen on the whole files-container (covers grid + empty space) - const container = document.querySelector('.files-container') || document.getElementById('files-grid'); + const container = document.querySelector('.files-container') || document.getElementById('files-list'); if (!container) return; container.addEventListener('mousedown', (e) => { // Only start if clicking empty area (not on a card, button, menu, input…) if (e.button !== 0) return; // left click only - if (e.target.closest('.file-card') || e.target.closest('.file-item') || + if (e.target.closest('.file-item') || e.target.closest('.context-menu') || e.target.closest('.upload-dropdown') || e.target.closest('button') || e.target.closest('input') || e.target.closest('.breadcrumb') || @@ -1528,7 +1450,7 @@ function initRubberBandSelection() { // Highlight cards that intersect with the rectangle const rectBounds = { left, top, right: left + width, bottom: top + height }; - document.querySelectorAll('#files-grid .file-card').forEach(card => { + document.querySelectorAll('#files-list .file-item').forEach(card => { const cardRect = card.getBoundingClientRect(); const intersects = cardRect.left < rectBounds.right && @@ -1542,25 +1464,13 @@ function initRubberBandSelection() { // Sync with multiSelect module if (window.multiSelect) { const info = window.multiSelect._extractInfo(card); - - // select the equivalent list-item to activate it too - let selector = (info.type === 'folder') ? "data-folder-id" : "data-file-id"; - const fileItem = list?.querySelector(`div.file-item[${selector}="${info.id}"]`); - fileItem?.classList.add('selected'); - if (info) window.multiSelect.select(info.id, info.name, info.type, info.parentId); } } else { card.classList.remove('selected'); // Deselect from multiSelect module if (window.multiSelect) { - const info = window.multiSelect._extractInfo(card); - - // select the equivalent list-item to activate it too - let selector = (info.type === 'folder') ? "data-folder-id" : "data-file-id"; - const fileItem = list?.querySelector(`div.file-item[${selector}="${info.id}"]`); - fileItem?.classList.remove('selected'); - + const info = window.multiSelect._extractInfo(card) if (info) window.multiSelect.deselect(info.id); } } @@ -1590,20 +1500,10 @@ if (document.readyState === 'loading') { initRubberBandSelection(); } -/** - * - * @param {boolean} show - */ -function showEmptyList(show) { - const emptyArea=document.getElementById("empty-files-state"); - emptyArea.classList.toggle("hidden", !show); -} - // Expose helpers globally window.toggleCardSelection = toggleCardSelection; window.showContextMenuAtElement = showContextMenuAtElement; window.initRubberBandSelection = initRubberBandSelection; -window.showEmptyList = showEmptyList; /** diff --git a/static/js/features/files/multiSelect.js b/static/js/features/files/multiSelect.js index 4cd111f7..723ab321 100644 --- a/static/js/features/files/multiSelect.js +++ b/static/js/features/files/multiSelect.js @@ -55,15 +55,14 @@ const multiSelect = { clear() { this._selected.clear(); this._lastClickedIndex = -1; - document.querySelectorAll('.file-card.selected, .file-item.selected') + document.querySelectorAll('.file-item.selected') .forEach(el => el.classList.remove('selected')); document.querySelectorAll('.item-checkbox').forEach(cb => cb.checked = false); this._syncUI(); }, selectAll() { - this._selectAllInContainer('files-grid', '.file-card'); - this._selectAllInContainer('files-list-view', '.file-item'); + this._selectAllInContainer('files-list', '.file-item'); this._syncUI(); }, @@ -148,7 +147,7 @@ const multiSelect = { }, _getAllVisibleItems() { - return [...document.querySelectorAll('.file-item, .file-card')]; + return [...document.querySelectorAll('.file-item')]; }, _extractInfo(el) { @@ -482,9 +481,10 @@ const multiSelect = { document.addEventListener('keydown', (e) => { if (e.target.closest('input, textarea, [contenteditable], .rename-dialog, .share-dialog, .confirm-dialog')) return; + // ctrl+a cmd+a if ((e.ctrlKey || e.metaKey) && e.key === 'a') { - const grid = document.getElementById('files-grid'); - if (grid && grid.closest('.files-container')) { + const filesList = document.getElementById('files-list'); + if (filesList && filesList.closest('.files-container')) { e.preventDefault(); this.selectAll(); } @@ -512,4 +512,4 @@ const multiSelect = { }; // Expose globally -window.multiSelect = multiSelect; \ No newline at end of file +window.multiSelect = multiSelect; diff --git a/static/js/features/files/search.js b/static/js/features/files/search.js index b3f3bb9f..77b7058f 100644 --- a/static/js/features/files/search.js +++ b/static/js/features/files/search.js @@ -103,17 +103,18 @@ const search = { * @param {Object} results - Enriched search results from backend */ displaySearchResults(results) { - const filesGrid = document.getElementById('files-grid'); - const filesListView = document.getElementById('files-list-view'); + window.ui.resetFilesList(); // ensure also list visible & error hidden + const filesList = document.getElementById('files-list'); - filesGrid.innerHTML = ''; - filesListView.innerHTML = ` + // reset list + filesList.innerHTML = `
Name
Type
Size
Modified
+
`; @@ -142,7 +143,7 @@ const search = {
`; - filesGrid.appendChild(searchHeader); + filesList.appendChild(searchHeader); // Sort dropdown — re-searches with new sort order (server-side) const sortSelect = document.getElementById('search-sort-select'); @@ -172,13 +173,10 @@ const search = { // Empty state if (results.files.length === 0 && results.folders.length === 0) { - const emptyState = document.createElement('div'); - emptyState.className = 'empty-state'; - emptyState.innerHTML = ` + window.ui.showError(`

No results found for this search

- `; - filesGrid.appendChild(emptyState); + `); return; } diff --git a/static/js/features/library/favorites.js b/static/js/features/library/favorites.js index b4ff5972..b3747df6 100644 --- a/static/js/features/library/favorites.js +++ b/static/js/features/library/favorites.js @@ -167,11 +167,10 @@ const favorites = { await this._fetchFromServer(); } - const filesGrid = document.getElementById('files-grid'); - const filesListView = document.getElementById('files-list-view'); + window.ui.resetFilesList(); // ensure also list visible & error hidden + const filesList = document.getElementById('files-list'); - filesGrid.innerHTML = ''; - filesListView.innerHTML = ` + filesList.innerHTML = `
Name
@@ -184,14 +183,11 @@ const favorites = { window.ui.updateBreadcrumb(''); if (this._cache.size === 0) { - const emptyState = document.createElement('div'); - emptyState.className = 'empty-state'; - emptyState.innerHTML = ` + window.ui.showError( `

${window.i18n ? window.i18n.t('favorites.empty_state') : 'No favorite items'}

${window.i18n ? window.i18n.t('favorites.empty_hint') : 'To mark as favorite, right-click on any file or folder'}

- `; - filesGrid.appendChild(emptyState); + `); return; } diff --git a/static/js/features/library/recent.js b/static/js/features/library/recent.js index 46b1f94e..c19b731c 100644 --- a/static/js/features/library/recent.js +++ b/static/js/features/library/recent.js @@ -86,32 +86,28 @@ const recent = { const recentItems = await response.json(); - const filesGrid = document.getElementById('files-grid'); - const filesListView = document.getElementById('files-list-view'); + window.ui.resetFilesList(); // ensure also list visible & error hidden + const filesList = document.getElementById('files-list'); - filesGrid.innerHTML = ''; - filesListView.innerHTML = ` + filesList.innerHTML = `
Name
Type
Size
Accessed
+
`; window.ui.updateBreadcrumb(''); if (recentItems.length === 0) { - const emptyState = document.createElement('div'); - emptyState.className = 'empty-state'; - emptyState.innerHTML = ` + window.ui.showError(`

${window.i18n ? window.i18n.t('recent.empty_state') : 'No recent files'}

${window.i18n ? window.i18n.t('recent.empty_hint') : 'Files you open will appear here'}

- `; - filesGrid.appendChild(emptyState); - return; + `); } const folders = []; diff --git a/static/js/views/shared/sharedView.js b/static/js/views/shared/sharedView.js index cc186dfa..07e48d8c 100644 --- a/static/js/views/shared/sharedView.js +++ b/static/js/views/shared/sharedView.js @@ -25,11 +25,13 @@ const sharedView = { this.displayUI(); this.attachEventListeners(); this.loadItems().then(() => this.filterAndSortItems()); + const c = document.getElementById('shared-container'); + if (c) c.classList.remove("hidden"); }, hide() { const c = document.getElementById('shared-container'); - if (c) c.style.display = 'none'; + if (c) c.classList.add("hidden"); }, // Load shared items from backend API @@ -63,7 +65,6 @@ const sharedView = { if (contentArea) contentArea.appendChild(container); } - container.style.display = 'block'; container.innerHTML = `
@@ -180,12 +181,6 @@ const sharedView = {
`; - // Hide other UI elements - const filesGrid = document.getElementById('files-grid'); - const filesListView = document.getElementById('files-list-view'); - if (filesGrid) filesGrid.style.display = 'none'; - if (filesListView) filesListView.style.display = 'none'; - if (window.i18n && window.i18n.translateElement) { window.i18n.translateElement(container); } @@ -326,13 +321,14 @@ const sharedView = { sharedItemsList.innerHTML = ''; if (this.filteredItems.length === 0) { - emptyState.style.display = 'flex'; - listContainer.style.display = 'none'; + emptyState.classList.remove("hidden"); + listContainer.classList.add("hidden"); return; } - emptyState.style.display = 'none'; - listContainer.style.display = 'block'; + emptyState.classList.add("hidden"); + listContainer.classList.remove("hidden"); + this.filteredItems.forEach(item => { const row = document.createElement('tr');