diff --git a/src/Components/LeftSidebar/LeftSidebar.vue b/src/Components/LeftSidebar/LeftSidebar.vue index c3915253c5..7e92e8eb7a 100644 --- a/src/Components/LeftSidebar/LeftSidebar.vue +++ b/src/Components/LeftSidebar/LeftSidebar.vue @@ -39,6 +39,14 @@ + + + ({ + grid_view: true, + }), + actions: { + async update(key, value) { + Vue.set(this, key, value) + }, + }, +}) diff --git a/src/views/FilesList/FileEntry/FileEntry.vue b/src/views/FilesList/FileEntry/FileEntry.vue new file mode 100644 index 0000000000..dc93584abf --- /dev/null +++ b/src/views/FilesList/FileEntry/FileEntry.vue @@ -0,0 +1,15 @@ + + + + + diff --git a/src/views/FilesList/FileEntry/FileEntryGrid.vue b/src/views/FilesList/FileEntry/FileEntryGrid.vue new file mode 100644 index 0000000000..e578746397 --- /dev/null +++ b/src/views/FilesList/FileEntry/FileEntryGrid.vue @@ -0,0 +1,28 @@ + + + + + diff --git a/src/views/FilesList/FileEntry/FileEntryPreview.vue b/src/views/FilesList/FileEntry/FileEntryPreview.vue new file mode 100644 index 0000000000..5ea56f7725 --- /dev/null +++ b/src/views/FilesList/FileEntry/FileEntryPreview.vue @@ -0,0 +1,86 @@ + + + diff --git a/src/views/FilesList/FileListFilter/FileListFilter.vue b/src/views/FilesList/FileListFilter/FileListFilter.vue index 776461d3e3..d235074b5e 100644 --- a/src/views/FilesList/FileListFilter/FileListFilter.vue +++ b/src/views/FilesList/FileListFilter/FileListFilter.vue @@ -19,8 +19,8 @@ + + diff --git a/src/views/FilesList/FilesListTableHeaderButton.vue b/src/views/FilesList/FilesListTableHeaderButton.vue new file mode 100644 index 0000000000..982d41617d --- /dev/null +++ b/src/views/FilesList/FilesListTableHeaderButton.vue @@ -0,0 +1,74 @@ + + + + + + diff --git a/src/views/FilesList/FilesListVirtual.vue b/src/views/FilesList/FilesListVirtual.vue index 2a0e385d6f..50e175fb81 100644 --- a/src/views/FilesList/FilesListVirtual.vue +++ b/src/views/FilesList/FilesListVirtual.vue @@ -1,19 +1,34 @@ @@ -40,6 +69,35 @@ export default { height: 100%; will-change: scroll-position; & :deep() { + // Table head, body and footer + tbody { + will-change: padding; + contain: layout paint style; + display: flex; + flex-direction: column; + width: 100%; + // Necessary for virtual scrolling absolute + position: relative; + + /* Hover effect on tbody lines only */ + tr { + contain: strict; + &:hover, + &:focus { + background-color: var(--color-background-dark); + } + } + } + + .files-list__table { + display: block; + + &.files-list__table--with-thead-overlay { + // Hide the table header below the overlay + margin-block-start: calc(-1 * var(--row-height)); + } + } + .files-list__filters { // Pinned on top when scrolling above table header position: sticky; @@ -52,6 +110,331 @@ export default { height: var(--fixed-block-start-position); width: 100%; } + + .files-list__thead, + .files-list__tfoot { + display: flex; + flex-direction: column; + width: 100%; + background-color: var(--color-main-background); + + } + + // Table header + .files-list__thead { + // Pinned on top when scrolling + position: sticky; + z-index: 10; + top: var(--fixed-block-start-position); + } + + tr { + position: relative; + display: flex; + align-items: center; + width: 100%; + user-select: none; + border-block-end: 1px solid var(--color-border); + box-sizing: border-box; + user-select: none; + height: var(--row-height); + } + + td, th { + display: flex; + align-items: center; + flex: 0 0 auto; + justify-content: start; + width: var(--row-height); + height: var(--row-height); + margin: 0; + padding: 0; + color: var(--color-text-maxcontrast); + border: none; + + // Columns should try to add any text + // node wrapped in a span. That should help + // with the ellipsis on overflow. + span { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + } + + .files-list__row-checkbox { + justify-content: center; + + .checkbox-radio-switch { + display: flex; + justify-content: center; + + --icon-size: var(--checkbox-size); + + label.checkbox-radio-switch__label { + width: var(--clickable-area); + height: var(--clickable-area); + margin: 0; + padding: calc((var(--clickable-area) - var(--checkbox-size)) / 2); + } + + .checkbox-radio-switch__icon { + margin: 0 !important; + } + } + } + + .files-list__row { + &:hover, &:focus, &:active, &--active, &--dragover { + // WCAG AA compliant + background-color: var(--color-background-hover); + // text-maxcontrast have been designed to pass WCAG AA over + // a white background, we need to adjust then. + --color-text-maxcontrast: var(--color-main-text); + > * { + --color-border: var(--color-border-dark); + } + + // Hover state of the row should also change the favorite markers background + .favorite-marker-icon svg path { + stroke: var(--color-background-hover); + } + } + + &--dragover * { + // Prevent dropping on row children + pointer-events: none; + } + } + + // Entry preview or mime icon + .files-list__row-icon { + position: relative; + display: flex; + overflow: visible; + align-items: center; + // No shrinking or growing allowed + flex: 0 0 var(--icon-preview-size); + justify-content: center; + width: var(--icon-preview-size); + height: 100%; + // Show same padding as the checkbox right padding for visual balance + margin-inline-end: var(--checkbox-padding); + color: var(--color-primary-element); + + // Icon is also clickable + * { + cursor: pointer; + } + + & > span { + justify-content: flex-start; + + &:not(.files-list__row-icon-favorite) svg { + width: var(--icon-preview-size); + height: var(--icon-preview-size); + } + + // Slightly increase the size of the folder icon + &.folder-icon, + &.folder-open-icon { + margin: -3px; + svg { + width: calc(var(--icon-preview-size) + 6px); + height: calc(var(--icon-preview-size) + 6px); + } + } + } + + &-preview-container { + position: relative; // Needed for the blurshash to be positioned correctly + overflow: hidden; + width: var(--icon-preview-size); + height: var(--icon-preview-size); + border-radius: var(--border-radius); + } + + &-blurhash { + position: absolute; + inset-block-start: 0; + inset-inline-start: 0; + height: 100%; + width: 100%; + object-fit: cover; + } + + &-preview { + // Center and contain the preview + object-fit: contain; + object-position: center; + + height: 100%; + width: 100%; + + /* Preview not loaded animation effect */ + &:not(.files-list__row-icon-preview--loaded) { + background: var(--color-loading-dark); + // animation: preview-gradient-fade 1.2s ease-in-out infinite; + } + } + + &-favorite { + position: absolute; + top: 0px; + inset-inline-end: -10px; + } + + // File and folder overlay + &-overlay { + position: absolute; + max-height: calc(var(--icon-preview-size) * 0.5); + max-width: calc(var(--icon-preview-size) * 0.5); + color: var(--color-primary-element-text); + // better alignment with the folder icon + margin-block-start: 2px; + + // Improve icon contrast with a background for files + &--file { + color: var(--color-main-text); + background: var(--color-main-background); + border-radius: 100%; + } + } + } + + // Entry link + .files-list__row-name { + // Prevent link from overflowing + overflow: hidden; + // Take as much space as possible + flex: 1 1 auto; + } + + .files-list__row-actions { + // take as much space as necessary + width: auto; + + // Add margin to all cells after the actions + & ~ td, + & ~ th { + margin: 0 var(--cell-margin); + } + + button { + .button-vue__text { + // Remove bold from default button styling + font-weight: normal; + } + } + } + + .files-list__row-column-custom { + width: calc(var(--row-height) * 2); + } + } + +} + + + diff --git a/src/views/FilesList/VirtualList.vue b/src/views/FilesList/VirtualList.vue index 941ddf47b3..fddcaa33ac 100644 --- a/src/views/FilesList/VirtualList.vue +++ b/src/views/FilesList/VirtualList.vue @@ -3,11 +3,44 @@
+ + + + + + + + + + +