diff options
author | John Molakvoæ <skjnldsv@protonmail.com> | 2023-03-24 17:17:29 +0100 |
---|---|---|
committer | John Molakvoæ <skjnldsv@protonmail.com> | 2023-04-06 14:49:31 +0200 |
commit | 0f717d408f73e02a0c0555f242936b75845b3a91 (patch) | |
tree | 5a20630f0920f7d788b749883611afafd8febe8e /apps | |
parent | 0e764f753a3d47bba946dc3f64cef9536ac98d43 (diff) | |
download | nextcloud-server-0f717d408f73e02a0c0555f242936b75845b3a91.tar.gz nextcloud-server-0f717d408f73e02a0c0555f242936b75845b3a91.zip |
feat(accessibility): add files table caption and summary
Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
Diffstat (limited to 'apps')
-rw-r--r-- | apps/files/src/components/FileEntry.vue | 9 | ||||
-rw-r--r-- | apps/files/src/components/FilesListFooter.vue | 160 | ||||
-rw-r--r-- | apps/files/src/components/FilesListHeader.vue | 30 | ||||
-rw-r--r-- | apps/files/src/components/FilesListVirtual.vue | 24 | ||||
-rw-r--r-- | apps/files/src/services/Navigation.ts | 2 | ||||
-rw-r--r-- | apps/files/src/views/FilesList.vue | 5 | ||||
-rw-r--r-- | apps/files_trashbin/src/main.ts | 1 |
7 files changed, 187 insertions, 44 deletions
diff --git a/apps/files/src/components/FileEntry.vue b/apps/files/src/components/FileEntry.vue index fcada72e4ce..4dfe3da0e4c 100644 --- a/apps/files/src/components/FileEntry.vue +++ b/apps/files/src/components/FileEntry.vue @@ -57,12 +57,9 @@ <!-- Actions --> <td :class="`files-list__row-actions-${uniqueId}`" class="files-list__row-actions"> <!-- Inline actions --> - <template v-if="false" v-for="action in enabledInlineActions"> - <CustomElementRender v-if="false" - :key="action.id" - :element="action.renderInline(source, currentView)" /> - <NcButton v-else - :key="action.id" + <template v-for="action in enabledInlineActions"> + <!-- TODO: implement CustomElementRender --> + <NcButton :key="action.id" type="tertiary" @click="onActionClick(action)"> <template #icon> diff --git a/apps/files/src/components/FilesListFooter.vue b/apps/files/src/components/FilesListFooter.vue new file mode 100644 index 00000000000..51907e03a9c --- /dev/null +++ b/apps/files/src/components/FilesListFooter.vue @@ -0,0 +1,160 @@ +<!-- + - @copyright Copyright (c) 2019 Gary Kim <gary@garykim.dev> + - + - @author Gary Kim <gary@garykim.dev> + - + - @license GNU AGPL version 3 or any later version + - + - This program is free software: you can redistribute it and/or modify + - it under the terms of the GNU Affero General Public License as + - published by the Free Software Foundation, either version 3 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, + - but WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + - GNU Affero General Public License for more details. + - + - You should have received a copy of the GNU Affero General Public License + - along with this program. If not, see <http://www.gnu.org/licenses/>. + - + --> +<template> + <tr> + <th class="files-list__row-checkbox"> + <span class="hidden-visually">{{ t('files', 'Total rows summary') }}</span> + </th> + + <!-- Link to file --> + <td class="files-list__row-name"> + <!-- Icon or preview --> + <span class="files-list__row-icon" /> + + <!-- Summary --> + <span>{{ summary }}</span> + </td> + + <!-- Actions --> + <td class="files-list__row-actions" /> + + <!-- Size --> + <td v-if="isSizeAvailable" class="files-list__column files-list__row-size"> + <span>{{ totalSize }}</span> + </td> + + <!-- Custom views columns --> + <th v-for="column in columns" + :key="column.id" + :class="classForColumn(column)"> + <span>{{ column.summary?.(nodes, currentView) }}</span> + </th> + </tr> +</template> + +<script lang="ts"> +import { formatFileSize } from '@nextcloud/files' +import { translate } from '@nextcloud/l10n' +import Vue from 'vue' + +import Navigation from '../services/Navigation' +import { useFilesStore } from '../store/files' +import { usePathsStore } from '../store/paths' + +export default Vue.extend({ + name: 'FilesListFooter', + + components: { + }, + + props: { + isSizeAvailable: { + type: Boolean, + default: false, + }, + nodes: { + type: Array, + required: true, + }, + summary: { + type: String, + default: '', + }, + }, + + setup() { + const pathsStore = usePathsStore() + const filesStore = useFilesStore() + return { + filesStore, + pathsStore, + } + }, + + computed: { + currentView() { + return this.$navigation.active + }, + + dir() { + // Remove any trailing slash but leave root slash + return (this.$route?.query?.dir || '/').replace(/^(.+)\/$/, '$1') + }, + + currentFolder() { + if (!this.currentView?.id) { + return + } + + if (this.dir === '/') { + return this.filesStore.getRoot(this.currentView.id) + } + const fileId = this.pathsStore.getPath(this.currentView.id, this.dir) + return this.filesStore.getNode(fileId) + }, + + columns() { + return this.currentView?.columns || [] + }, + + totalSize() { + // If we have the size already, let's use it + if (this.currentFolder?.size) { + return formatFileSize(this.currentFolder.size, true) + } + + // Otherwise let's compute it + return formatFileSize(this.nodes.reduce((total, node) => total + node.size || 0, 0), true) + }, + }, + + methods: { + classForColumn(column) { + return { + 'files-list__row-column-custom': true, + [`files-list__row-${this.currentView.id}-${column.id}`]: true, + } + }, + + t: translate, + }, +}) +</script> + +<style scoped lang="scss"> +@import '../mixins/fileslist-row.scss'; + +// Scoped row +tr { + padding-bottom: 300px; + border-top: 1px solid var(--color-border); + // Prevent hover effect on the whole row + background-color: transparent !important; +} + +td { + user-select: none; + // Make sure the cell colors don't apply to column headers + color: var(--color-text-maxcontrast) !important; +} + +</style> diff --git a/apps/files/src/components/FilesListHeader.vue b/apps/files/src/components/FilesListHeader.vue index b69a394a812..e39d7b4cade 100644 --- a/apps/files/src/components/FilesListHeader.vue +++ b/apps/files/src/components/FilesListHeader.vue @@ -60,9 +60,6 @@ <script lang="ts"> import { mapState } from 'pinia' import { translate } from '@nextcloud/l10n' -import MenuDown from 'vue-material-design-icons/MenuDown.vue' -import MenuUp from 'vue-material-design-icons/MenuUp.vue' -import NcButton from '@nextcloud/vue/dist/Components/NcButton.js' import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js' import Vue from 'vue' @@ -168,16 +165,6 @@ export default Vue.extend({ } }, - sortAriaLabel(column) { - const direction = this.isAscSorting - ? this.t('files', 'ascending') - : this.t('files', 'descending') - return this.t('files', 'Sort list by {column} ({direction})', { - column, - direction, - }) - }, - onToggleAll(selected) { if (selected) { const selection = this.nodes.map(node => node.attributes.fileid.toString()) @@ -189,23 +176,6 @@ export default Vue.extend({ } }, - toggleSortBy(key) { - // If we're already sorting by this key, flip the direction - if (this.sortingMode === key) { - this.sortingStore.toggleSortingDirection(this.currentView.id) - return - } - // else sort ASC by this new key - this.sortingStore.setSortingBy(key, this.currentView.id) - }, - - toggleSortByCustomColumn(column) { - if (!column.sort) { - return - } - this.toggleSortBy(column.id) - }, - t: translate, }, }) diff --git a/apps/files/src/components/FilesListVirtual.vue b/apps/files/src/components/FilesListVirtual.vue index eafac678310..af83578d704 100644 --- a/apps/files/src/components/FilesListVirtual.vue +++ b/apps/files/src/components/FilesListVirtual.vue @@ -35,14 +35,20 @@ <FileEntry :is-size-available="isSizeAvailable" :source="item" /> </template> - <!-- <template #before> - <caption v-show="false" class="files-list__caption"> - {{ summary }} + <template #before> + <!-- Accessibility description --> + <caption class="hidden-visually"> + {{ currentView.caption || '' }} + {{ t('files', 'This list is not fully rendered for performances reasons. The files will be rendered as you navigate through the list.') }} </caption> - </template> --> - <template #before> - <FilesListHeader :nodes="nodes" :is-size-available="isSizeAvailable" /> + <!-- Thead--> + <FilesListHeader :is-size-available="isSizeAvailable" :nodes="nodes" /> + </template> + + <template #after> + <!-- Tfoot--> + <FilesListFooter :is-size-available="isSizeAvailable" :nodes="nodes" :summary="summary" /> </template> </RecycleScroller> </template> @@ -54,6 +60,7 @@ import Vue from 'vue' import FileEntry from './FileEntry.vue' import FilesListHeader from './FilesListHeader.vue' +import FilesListFooter from './FilesListFooter.vue' export default Vue.extend({ name: 'FilesListVirtual', @@ -62,9 +69,14 @@ export default Vue.extend({ RecycleScroller, FileEntry, FilesListHeader, + FilesListFooter, }, props: { + currentView: { + type: Object, + required: true, + }, nodes: { type: Array, required: true, diff --git a/apps/files/src/services/Navigation.ts b/apps/files/src/services/Navigation.ts index 247243a8a74..a39b04b642a 100644 --- a/apps/files/src/services/Navigation.ts +++ b/apps/files/src/services/Navigation.ts @@ -41,7 +41,7 @@ export interface Column { sort?: (nodeA: Node, nodeB: Node) => number /** Custom summary of the column to display at the end of the list. Will not be displayed if nothing is provided */ - summary?: (node: Node[]) => string + summary?: (node: Node[], view: Navigation) => string } export interface Navigation { diff --git a/apps/files/src/views/FilesList.vue b/apps/files/src/views/FilesList.vue index 98e917b5191..70f814870f6 100644 --- a/apps/files/src/views/FilesList.vue +++ b/apps/files/src/views/FilesList.vue @@ -56,7 +56,10 @@ </NcEmptyContent> <!-- File list --> - <FilesListVirtual v-else ref="filesListVirtual" :nodes="dirContents" /> + <FilesListVirtual v-else + ref="filesListVirtual" + :current-view="currentView" + :nodes="dirContents" /> </NcAppContent> </template> diff --git a/apps/files_trashbin/src/main.ts b/apps/files_trashbin/src/main.ts index dfa88bded93..118f0ec72ee 100644 --- a/apps/files_trashbin/src/main.ts +++ b/apps/files_trashbin/src/main.ts @@ -35,6 +35,7 @@ const Navigation = window.OCP.Files.Navigation as NavigationService Navigation.register({ id: 'trashbin', name: t('files_trashbin', 'Deleted files'), + caption: t('files_trashbin', 'List of files that have been deleted.'), icon: DeleteSvg, order: 50, |