summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorJohn Molakvoæ <skjnldsv@protonmail.com>2023-03-24 17:17:29 +0100
committerJohn Molakvoæ <skjnldsv@protonmail.com>2023-04-06 14:49:31 +0200
commit0f717d408f73e02a0c0555f242936b75845b3a91 (patch)
tree5a20630f0920f7d788b749883611afafd8febe8e /apps
parent0e764f753a3d47bba946dc3f64cef9536ac98d43 (diff)
downloadnextcloud-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.vue9
-rw-r--r--apps/files/src/components/FilesListFooter.vue160
-rw-r--r--apps/files/src/components/FilesListHeader.vue30
-rw-r--r--apps/files/src/components/FilesListVirtual.vue24
-rw-r--r--apps/files/src/services/Navigation.ts2
-rw-r--r--apps/files/src/views/FilesList.vue5
-rw-r--r--apps/files_trashbin/src/main.ts1
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,