]> source.dussan.org Git - nextcloud-server.git/commitdiff
fix(files): move header actions out from the table
authorGrigorii K. Shartsev <me@shgk.me>
Mon, 8 Jan 2024 15:10:30 +0000 (16:10 +0100)
committerGrigorii K. Shartsev <me@shgk.me>
Wed, 10 Jan 2024 16:52:24 +0000 (17:52 +0100)
Having actions in the table header is no valid for a11y and counts as a column name.

Signed-off-by: Grigorii K. Shartsev <me@shgk.me>
apps/files/src/components/FilesListTableHeader.vue
apps/files/src/components/FilesListTableHeaderActions.vue
apps/files/src/components/FilesListVirtual.vue
apps/files/src/components/VirtualList.vue

index 285036a2f94a797cdd20ea42b1ff4e2cb175b2a7..c679b2068c128ed50cbfe34a842557e9c26cb39d 100644 (file)
                        <NcCheckboxRadioSwitch v-bind="selectAllBind" @update:checked="onToggleAll" />
                </th>
 
-               <!-- Actions multiple if some are selected -->
-               <FilesListTableHeaderActions v-if="!isNoneSelected"
-                       :current-view="currentView"
-                       :selected-nodes="selectedNodes" />
-
                <!-- Columns display -->
-               <template v-else>
-                       <!-- Link to file -->
-                       <th class="files-list__column files-list__row-name files-list__column--sortable"
-                               :aria-sort="ariaSortForMode('basename')">
-                               <!-- Icon or preview -->
-                               <span class="files-list__row-icon" />
-
-                               <!-- Name -->
-                               <FilesListTableHeaderButton :name="t('files', 'Name')" mode="basename" />
-                       </th>
-
-                       <!-- Actions -->
-                       <th class="files-list__row-actions" />
-
-                       <!-- Size -->
-                       <th v-if="isSizeAvailable"
-                               :class="{'files-list__column--sortable': isSizeAvailable}"
-                               class="files-list__column files-list__row-size"
-                               :aria-sort="ariaSortForMode('size')">
-                               <FilesListTableHeaderButton :name="t('files', 'Size')" mode="size" />
-                       </th>
-
-                       <!-- Mtime -->
-                       <th v-if="isMtimeAvailable"
-                               :class="{'files-list__column--sortable': isMtimeAvailable}"
-                               class="files-list__column files-list__row-mtime"
-                               :aria-sort="ariaSortForMode('mtime')">
-                               <FilesListTableHeaderButton :name="t('files', 'Modified')" mode="mtime" />
-                       </th>
-
-                       <!-- Custom views columns -->
-                       <th v-for="column in columns"
-                               :key="column.id"
-                               :class="classForColumn(column)"
-                               :aria-sort="ariaSortForMode(column.id)">
-                               <FilesListTableHeaderButton v-if="!!column.sort" :name="column.title" :mode="column.id" />
-                               <span v-else>
-                                       {{ column.title }}
-                               </span>
-                       </th>
-               </template>
+
+               <!-- Link to file -->
+               <th class="files-list__column files-list__row-name files-list__column--sortable"
+                       :aria-sort="ariaSortForMode('basename')">
+                       <!-- Icon or preview -->
+                       <span class="files-list__row-icon" />
+
+                       <!-- Name -->
+                       <FilesListTableHeaderButton :name="t('files', 'Name')" mode="basename" />
+               </th>
+
+               <!-- Actions -->
+               <th class="files-list__row-actions" />
+
+               <!-- Size -->
+               <th v-if="isSizeAvailable"
+                       class="files-list__column files-list__row-size"
+                       :class="{ 'files-list__column--sortable': isSizeAvailable }"
+                       :aria-sort="ariaSortForMode('size')">
+                       <FilesListTableHeaderButton :name="t('files', 'Size')" mode="size" />
+               </th>
+
+               <!-- Mtime -->
+               <th v-if="isMtimeAvailable"
+                       class="files-list__column files-list__row-mtime"
+                       :class="{ 'files-list__column--sortable': isMtimeAvailable }"
+                       :aria-sort="ariaSortForMode('mtime')">
+                       <FilesListTableHeaderButton :name="t('files', 'Modified')" mode="mtime" />
+               </th>
+
+               <!-- Custom views columns -->
+               <th v-for="column in columns"
+                       :key="column.id"
+                       :class="classForColumn(column)"
+                       :aria-sort="ariaSortForMode(column.id)">
+                       <FilesListTableHeaderButton v-if="!!column.sort" :name="column.title" :mode="column.id" />
+                       <span v-else>
+                               {{ column.title }}
+                       </span>
+               </th>
        </tr>
 </template>
 
index 73b9c429a3d5771eaf927207f302d03a5c0448aa..c65f5fd9b4fddb93ae4994359ae3854bd13b0258 100644 (file)
@@ -20,7 +20,7 @@
   -
   -->
 <template>
-       <th class="files-list__column files-list__row-actions-batch" colspan="2">
+       <div class="files-list__column files-list__row-actions-batch">
                <NcActions ref="actionsMenu"
                        :disabled="!!loading || areSomeNodesLoading"
                        :force-name="true"
@@ -38,7 +38,7 @@
                                {{ action.displayName(nodes, currentView) }}
                        </NcActionButton>
                </NcActions>
-       </th>
+       </div>
 </template>
 
 <script lang="ts">
@@ -220,7 +220,7 @@ export default Vue.extend({
        flex: 1 1 100% !important;
 
        // Remove when https://github.com/nextcloud/nextcloud-vue/pull/3936 is merged
-       ::v-deep .button-vue__wrapper {
+       :deep(.button-vue__wrapper) {
                width: 100%;
                span.button-vue__text {
                        overflow: hidden;
index 88bb7e2076aac8e4c06415af05a2db3d839198b0..9ae520b6e9da9e04310da823267bee83b32178a6 100644 (file)
                }"
                :scroll-to-index="scrollToIndex"
                :caption="caption">
+               <template v-if="!isNoneSelected" #header-overlay>
+                       <FilesListTableHeaderActions :current-view="currentView"
+                               :selected-nodes="selectedNodes" />
+               </template>
+
                <template #before>
                        <!-- Headers -->
                        <FilesListHeader v-for="header in sortedHeaders"
@@ -76,6 +81,7 @@ import { defineComponent } from 'vue'
 
 import { action as sidebarAction } from '../actions/sidebarAction.ts'
 import { useUserConfigStore } from '../store/userconfig.ts'
+import { useSelectionStore } from '../store/selection.js'
 
 import FileEntry from './FileEntry.vue'
 import FileEntryGrid from './FileEntryGrid.vue'
@@ -85,6 +91,7 @@ import FilesListTableHeader from './FilesListTableHeader.vue'
 import filesListWidthMixin from '../mixins/filesListWidth.ts'
 import VirtualList from './VirtualList.vue'
 import logger from '../logger.js'
+import FilesListTableHeaderActions from './FilesListTableHeaderActions.vue'
 
 export default defineComponent({
        name: 'FilesListVirtual',
@@ -94,6 +101,7 @@ export default defineComponent({
                FilesListTableFooter,
                FilesListTableHeader,
                VirtualList,
+               FilesListTableHeaderActions,
        },
 
        mixins: [
@@ -117,8 +125,10 @@ export default defineComponent({
 
        setup() {
                const userConfigStore = useUserConfigStore()
+               const selectionStore = useSelectionStore()
                return {
                        userConfigStore,
+                       selectionStore,
                }
        },
 
@@ -185,6 +195,14 @@ export default defineComponent({
                        const virtualListNote = t('files', 'This list is not fully rendered for performance reasons. The files will be rendered as you navigate through the list.')
                        return `${viewCaption}\n${sortableCaption}\n${virtualListNote}`
                },
+
+               selectedNodes() {
+                       return this.selectionStore.selected
+               },
+
+               isNoneSelected() {
+                       return this.selectedNodes.length === 0
+               },
        },
 
        watch: {
@@ -298,6 +316,7 @@ export default defineComponent({
        --clickable-area: 44px;
        --icon-preview-size: 32px;
 
+       position: relative;
        overflow: auto;
        height: 100%;
        will-change: scroll-position;
@@ -333,6 +352,22 @@ export default defineComponent({
                        display: block;
                }
 
+               .files-list__thead-overlay {
+                       position: absolute;
+                       top: 0;
+                       left: var(--row-height); // Save space for a row checkbox
+                       right: 0;
+                       z-index: 1000;
+
+                       display: flex;
+                       align-items: center;
+
+                       // Reuse row styles
+                       background-color: var(--color-main-background);
+                       border-bottom: 1px solid var(--color-border);
+                       height: var(--row-height);
+               }
+
                .files-list__thead,
                .files-list__tfoot {
                        display: flex;
index 0a99f19bb5f14628cd39b0d19e79b6c463d8264d..77454772f5554a14b4e1e1eba7c9a504b3022dba 100644 (file)
@@ -1,5 +1,9 @@
 <template>
        <div class="files-list" data-cy-files-list>
+               <div v-if="!!$scopedSlots['header-overlay']" class="files-list__thead-overlay">
+                       <slot name="header-overlay" />
+               </div>
+
                <!-- Header -->
                <div ref="before" class="files-list__before">
                        <slot name="before" />