From e424bb763a254356287c1c1d72ba26657df76073 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Fri, 20 Sep 2024 18:41:00 -0300 Subject: [PATCH 01/48] feat: rewrite file list Signed-off-by: Vitor Mattos --- package-lock.json | 2 + package.json | 2 + src/helpers/logger.js | 11 + src/router/router.js | 5 + src/store/filters.js | 27 ++ .../FileListFilter/FileListFilter.vue | 58 +++++ .../FileListFilter/FileListFilterModified.vue | 128 ++++++++++ .../FileListFilter/FileListFilterStatus.vue | 136 ++++++++++ src/views/FilesList/FileListFilters.vue | 74 ++++++ src/views/FilesList/FilesList.vue | 232 ++++++++++++++++++ src/views/FilesList/FilesListVirtual.vue | 57 +++++ src/views/FilesList/VirtualList.vue | 13 + tsconfig.json | 2 +- 13 files changed, 746 insertions(+), 1 deletion(-) create mode 100644 src/helpers/logger.js create mode 100644 src/store/filters.js create mode 100644 src/views/FilesList/FileListFilter/FileListFilter.vue create mode 100644 src/views/FilesList/FileListFilter/FileListFilterModified.vue create mode 100644 src/views/FilesList/FileListFilter/FileListFilterStatus.vue create mode 100644 src/views/FilesList/FileListFilters.vue create mode 100644 src/views/FilesList/FilesList.vue create mode 100644 src/views/FilesList/FilesListVirtual.vue create mode 100644 src/views/FilesList/VirtualList.vue diff --git a/package-lock.json b/package-lock.json index c500a32e88..5a47c132c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,8 @@ "@fontsource/dancing-script": "^5.1.0", "@libresign/vue-pdf-editor": "^1.3.7", "@marionebl/option": "^1.0.8", + "@mdi/js": "^7.4.47", + "@mdi/svg": "^7.4.47", "@nextcloud/auth": "^2.4.0", "@nextcloud/axios": "^2.5.1", "@nextcloud/dialogs": "^6.0.1", diff --git a/package.json b/package.json index 71bfd80e7c..1c9f622b1c 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,8 @@ "@fontsource/dancing-script": "^5.1.0", "@libresign/vue-pdf-editor": "^1.3.7", "@marionebl/option": "^1.0.8", + "@mdi/js": "^7.4.47", + "@mdi/svg": "^7.4.47", "@nextcloud/auth": "^2.4.0", "@nextcloud/axios": "^2.5.1", "@nextcloud/dialogs": "^6.0.1", diff --git a/src/helpers/logger.js b/src/helpers/logger.js new file mode 100644 index 0000000000..c9565f4ba2 --- /dev/null +++ b/src/helpers/logger.js @@ -0,0 +1,11 @@ +/** + * SPDX-FileCopyrightText: 2020-2024 LibreCode coop and contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { getLoggerBuilder } from '@nextcloud/logger' + +export default getLoggerBuilder() + .setApp('libresign') + .detectUser() + .build() diff --git a/src/router/router.js b/src/router/router.js index 2784e180a5..bec603b178 100644 --- a/src/router/router.js +++ b/src/router/router.js @@ -141,6 +141,11 @@ const router = new Router({ name: 'timeline', component: () => import('../views/Timeline/Timeline.vue'), }, + { + path: '/f/filelist/sign', + name: 'fileslist', + component: () => import('../views/FilesList/FilesList.vue'), + }, { path: '/f/request', name: 'requestFiles', diff --git a/src/store/filters.js b/src/store/filters.js new file mode 100644 index 0000000000..c081bc0efd --- /dev/null +++ b/src/store/filters.js @@ -0,0 +1,27 @@ +/** + * SPDX-FileCopyrightText: 2020-2024 LibreCode coop and contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { defineStore } from 'pinia' +import logger from '../helpers/logger.js' + +export const useFiltersStore = defineStore('filter', { + state: () => ({ + chips: {}, + }), + + getters: { + activeChips(state) { + return Object.values(state.chips).flat() + }, + }, + + actions: { + onFilterUpdateChips(event) { + this.chips = { ...this.chips, [event.id]: [...event.detail] } + + logger.debug('File list filter chips updated', { chips: event.detail }) + }, + }, +}) diff --git a/src/views/FilesList/FileListFilter/FileListFilter.vue b/src/views/FilesList/FileListFilter/FileListFilter.vue new file mode 100644 index 0000000000..776461d3e3 --- /dev/null +++ b/src/views/FilesList/FileListFilter/FileListFilter.vue @@ -0,0 +1,58 @@ + + + + + diff --git a/src/views/FilesList/FileListFilter/FileListFilterModified.vue b/src/views/FilesList/FileListFilter/FileListFilterModified.vue new file mode 100644 index 0000000000..7e355d864b --- /dev/null +++ b/src/views/FilesList/FileListFilter/FileListFilterModified.vue @@ -0,0 +1,128 @@ + + + + + diff --git a/src/views/FilesList/FileListFilter/FileListFilterStatus.vue b/src/views/FilesList/FileListFilter/FileListFilterStatus.vue new file mode 100644 index 0000000000..5387855747 --- /dev/null +++ b/src/views/FilesList/FileListFilter/FileListFilterStatus.vue @@ -0,0 +1,136 @@ + + + + + diff --git a/src/views/FilesList/FileListFilters.vue b/src/views/FilesList/FileListFilters.vue new file mode 100644 index 0000000000..48d174122f --- /dev/null +++ b/src/views/FilesList/FileListFilters.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/src/views/FilesList/FilesList.vue b/src/views/FilesList/FilesList.vue new file mode 100644 index 0000000000..b654ef6a2c --- /dev/null +++ b/src/views/FilesList/FilesList.vue @@ -0,0 +1,232 @@ + + + + + diff --git a/src/views/FilesList/FilesListVirtual.vue b/src/views/FilesList/FilesListVirtual.vue new file mode 100644 index 0000000000..2a0e385d6f --- /dev/null +++ b/src/views/FilesList/FilesListVirtual.vue @@ -0,0 +1,57 @@ + + + + + diff --git a/src/views/FilesList/VirtualList.vue b/src/views/FilesList/VirtualList.vue new file mode 100644 index 0000000000..941ddf47b3 --- /dev/null +++ b/src/views/FilesList/VirtualList.vue @@ -0,0 +1,13 @@ + + + diff --git a/tsconfig.json b/tsconfig.json index 4176c09e07..3d0d2e6764 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,4 +12,4 @@ "vueCompilerOptions": { "target": 2.7, } -} \ No newline at end of file +} From f32e23fcf234a7ae0d97025351f7a80f04ac9f1c Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Mon, 23 Sep 2024 19:11:23 -0300 Subject: [PATCH 02/48] chore: start to implement FileEntry component Signed-off-by: Vitor Mattos --- src/Components/LeftSidebar/LeftSidebar.vue | 8 + src/helpers/logger.js | 2 +- src/store/filters.js | 3 +- src/store/userconfig.js | 18 + src/views/FilesList/FileEntry/FileEntry.vue | 15 + .../FilesList/FileEntry/FileEntryGrid.vue | 28 ++ .../FilesList/FileEntry/FileEntryPreview.vue | 86 ++++ .../FileListFilter/FileListFilter.vue | 2 +- .../FileListFilter/FileListFilterModified.vue | 5 +- .../FileListFilter/FileListFilterStatus.vue | 12 +- src/views/FilesList/FileListFilters.vue | 4 +- src/views/FilesList/FilesList.vue | 42 +- src/views/FilesList/FilesListTableHeader.vue | 105 +++++ .../FilesList/FilesListTableHeaderButton.vue | 74 ++++ src/views/FilesList/FilesListVirtual.vue | 392 +++++++++++++++++- src/views/FilesList/VirtualList.vue | 33 ++ 16 files changed, 798 insertions(+), 31 deletions(-) create mode 100644 src/store/userconfig.js create mode 100644 src/views/FilesList/FileEntry/FileEntry.vue create mode 100644 src/views/FilesList/FileEntry/FileEntryGrid.vue create mode 100644 src/views/FilesList/FileEntry/FileEntryPreview.vue create mode 100644 src/views/FilesList/FilesListTableHeader.vue create mode 100644 src/views/FilesList/FilesListTableHeaderButton.vue diff --git a/src/Components/LeftSidebar/LeftSidebar.vue b/src/Components/LeftSidebar/LeftSidebar.vue index 8880abfd0a..9b24041108 100644 --- a/src/Components/LeftSidebar/LeftSidebar.vue +++ b/src/Components/LeftSidebar/LeftSidebar.vue @@ -23,6 +23,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..735ce7d983 100644 --- a/src/views/FilesList/FilesListVirtual.vue +++ b/src/views/FilesList/FilesListVirtual.vue @@ -1,19 +1,34 @@ @@ -39,7 +68,41 @@ export default { overflow: auto; height: 100%; will-change: scroll-position; + + &:has(.file-list-filters__active) { + --fixed-block-start-position: calc(var(--default-clickable-area) + var(--default-grid-baseline) + var(--clickable-area-small)); + } + & :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 +115,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 @@
+ + + + + + + + + + +
From 09fba66c85552d960fd5ca08d0def7148feca4a6 Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Tue, 24 Sep 2024 11:55:30 -0300 Subject: [PATCH 03/48] fix: linter Signed-off-by: Vitor Mattos --- src/store/userconfig.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/store/userconfig.js b/src/store/userconfig.js index 2b371cbe86..7e70f4808f 100644 --- a/src/store/userconfig.js +++ b/src/store/userconfig.js @@ -4,7 +4,7 @@ */ import { defineStore } from 'pinia' -import Vue from 'vue' +import { set } from 'vue' export const useUserConfigStore = defineStore('userconfig', { state: () => ({ @@ -12,7 +12,7 @@ export const useUserConfigStore = defineStore('userconfig', { }), actions: { async update(key, value) { - Vue.set(this, key, value) + set(this, key, value) }, }, }) From eb7b5d1a638d00688b5fa964a24022cbc3802feb Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Tue, 24 Sep 2024 11:55:48 -0300 Subject: [PATCH 04/48] feat: add FileEntryName component Signed-off-by: Vitor Mattos --- .../FilesList/FileEntry/FileEntryGrid.vue | 35 ++++++++++ .../FilesList/FileEntry/FileEntryName.vue | 70 +++++++++++++++++++ src/views/FilesList/FilesListVirtual.vue | 42 +++++++++++ 3 files changed, 147 insertions(+) create mode 100644 src/views/FilesList/FileEntry/FileEntryName.vue diff --git a/src/views/FilesList/FileEntry/FileEntryGrid.vue b/src/views/FilesList/FileEntry/FileEntryGrid.vue index e578746397..3f9cd6a715 100644 --- a/src/views/FilesList/FileEntry/FileEntryGrid.vue +++ b/src/views/FilesList/FileEntry/FileEntryGrid.vue @@ -7,15 +7,28 @@ + + + + + + diff --git a/src/views/FilesList/FileEntry/FileEntryName.vue b/src/views/FilesList/FileEntry/FileEntryName.vue new file mode 100644 index 0000000000..578a82f54e --- /dev/null +++ b/src/views/FilesList/FileEntry/FileEntryName.vue @@ -0,0 +1,70 @@ + + + + + diff --git a/src/views/FilesList/FilesListVirtual.vue b/src/views/FilesList/FilesListVirtual.vue index 735ce7d983..b81a58ea17 100644 --- a/src/views/FilesList/FilesListVirtual.vue +++ b/src/views/FilesList/FilesListVirtual.vue @@ -313,6 +313,48 @@ export default { overflow: hidden; // Take as much space as possible flex: 1 1 auto; + + button.files-list__row-name-link { + display: flex; + align-items: center; + text-align: start; + // Fill cell height and width + width: 100%; + height: 100%; + // Necessary for flex grow to work + min-width: 0; + margin: 0; + padding: 0; + + // Already added to the inner text, see rule below + &:focus-visible { + outline: none !important; + } + + // Keyboard indicator a11y + &:focus .files-list__row-name-text { + outline: var(--border-width-input-focused) solid var(--color-main-text) !important; + border-radius: var(--border-radius-element); + } + &:focus:not(:focus-visible) .files-list__row-name-text { + outline: none !important; + } + } + + .files-list__row-name-text { + color: var(--color-main-text); + // Make some space for the outline + padding: var(--default-grid-baseline) calc(2 * var(--default-grid-baseline)); + padding-inline-start: -10px; + // Align two name and ext + display: inline-flex; + } + + .files-list__row-name-ext { + color: var(--color-text-maxcontrast); + // always show the extension + overflow: visible; + } } .files-list__row-actions { From bd55cc4def8c623ebef7bd9057e523f1dd5faeab Mon Sep 17 00:00:00 2001 From: Vitor Mattos Date: Tue, 24 Sep 2024 13:11:20 -0300 Subject: [PATCH 05/48] chore: add component EntryAction Signed-off-by: Vitor Mattos --- src/store/actionsmenu.js | 12 ++ .../FilesList/FileEntry/FileEntryActions.vue | 125 ++++++++++++++++++ .../FilesList/FileEntry/FileEntryGrid.vue | 33 ++++- src/views/FilesList/FilesList.vue | 3 +- src/views/FilesList/FilesListVirtual.vue | 9 +- src/views/FilesList/VirtualList.vue | 7 +- 6 files changed, 185 insertions(+), 4 deletions(-) create mode 100644 src/store/actionsmenu.js create mode 100644 src/views/FilesList/FileEntry/FileEntryActions.vue diff --git a/src/store/actionsmenu.js b/src/store/actionsmenu.js new file mode 100644 index 0000000000..16676167f2 --- /dev/null +++ b/src/store/actionsmenu.js @@ -0,0 +1,12 @@ +/** + * SPDX-FileCopyrightText: 2024 LibreCode coop and contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { defineStore } from 'pinia' + +export const useActionsMenuStore = defineStore('actionsmenu', { + state: () => ({ + opened: null, + }), +}) diff --git a/src/views/FilesList/FileEntry/FileEntryActions.vue b/src/views/FilesList/FileEntry/FileEntryActions.vue new file mode 100644 index 0000000000..df92fbd66c --- /dev/null +++ b/src/views/FilesList/FileEntry/FileEntryActions.vue @@ -0,0 +1,125 @@ + + + + + + + diff --git a/src/views/FilesList/FileEntry/FileEntryGrid.vue b/src/views/FilesList/FileEntry/FileEntryGrid.vue index 3f9cd6a715..1b643771c0 100644 --- a/src/views/FilesList/FileEntry/FileEntryGrid.vue +++ b/src/views/FilesList/FileEntry/FileEntryGrid.vue @@ -17,17 +17,30 @@ class="files-list__row-mtime"> + + + diff --git a/src/views/FilesList/FilesList.vue b/src/views/FilesList/FilesList.vue index 9dc04f48f8..5be7eaab20 100644 --- a/src/views/FilesList/FilesList.vue +++ b/src/views/FilesList/FilesList.vue @@ -68,7 +68,8 @@ + :nodes="dirContentsSorted" + :loading="loading" /> diff --git a/src/views/FilesList/FilesListVirtual.vue b/src/views/FilesList/FilesListVirtual.vue index b81a58ea17..f5df3f5792 100644 --- a/src/views/FilesList/FilesListVirtual.vue +++ b/src/views/FilesList/FilesListVirtual.vue @@ -1,5 +1,6 @@ diff --git a/src/views/FilesList/FileEntry/FileEntryGrid.vue b/src/views/FilesList/FileEntry/FileEntryGrid.vue index 1b643771c0..8e1f3c2e2a 100644 --- a/src/views/FilesList/FileEntry/FileEntryGrid.vue +++ b/src/views/FilesList/FileEntry/FileEntryGrid.vue @@ -4,7 +4,9 @@ -->