diff options
author | Arthur Schiwon <blizzz@arthur-schiwon.de> | 2023-09-07 15:32:09 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-09-07 15:32:09 +0200 |
commit | 865e32e4317f97c30018d9938b404b68e9fd6710 (patch) | |
tree | d5bdecfb63da6ae24dfa38845e5dde438272274c /apps | |
parent | a5f71a75023989d4faf77c925b0eeed14b13d0db (diff) | |
parent | c0b97ea89a2f419fb8dd19b9cb260639c5d0109f (diff) | |
download | nextcloud-server-865e32e4317f97c30018d9938b404b68e9fd6710.tar.gz nextcloud-server-865e32e4317f97c30018d9938b404b68e9fd6710.zip |
Merge pull request #40327 from nextcloud/manual/backport/stable27/40266
[stable27] Backport Polish new sharing flow : accesibility, expand bahavior, click outside behaviour
Diffstat (limited to 'apps')
-rw-r--r-- | apps/files_sharing/src/components/SharingEntryQuickShareSelect.vue | 110 | ||||
-rw-r--r-- | apps/files_sharing/src/views/SharingDetailsTab.vue | 16 |
2 files changed, 109 insertions, 17 deletions
diff --git a/apps/files_sharing/src/components/SharingEntryQuickShareSelect.vue b/apps/files_sharing/src/components/SharingEntryQuickShareSelect.vue index 8128b3925ee..a83ef503048 100644 --- a/apps/files_sharing/src/components/SharingEntryQuickShareSelect.vue +++ b/apps/files_sharing/src/components/SharingEntryQuickShareSelect.vue @@ -1,16 +1,30 @@ <template> - <div :class="{ 'active': showDropdown, 'share-select': true }"> - <span class="trigger-text" @click="toggleDropdown"> + <div ref="quickShareDropdownContainer" + :class="{ 'active': showDropdown, 'share-select': true }"> + <span :id="dropdownId" + class="trigger-text" + :aria-expanded="showDropdown" + :aria-haspopup="true" + aria-label="Quick share options dropdown" + @click="toggleDropdown"> {{ selectedOption }} <DropdownIcon :size="15" /> </span> - <div v-if="showDropdown" class="share-select-dropdown-container"> - <div v-for="option in options" + <div v-if="showDropdown" + ref="quickShareDropdown" + class="share-select-dropdown" + :aria-labelledby="dropdownId" + tabindex="0" + @keydown.down="handleArrowDown" + @keydown.up="handleArrowUp" + @keydown.esc="closeDropdown"> + <button v-for="option in options" :key="option" :class="{ 'dropdown-item': true, 'selected': option === selectedOption }" + :aria-selected="option === selectedOption" @click="selectOption(option)"> {{ option }} - </div> + </button> </div> </div> </template> @@ -26,6 +40,8 @@ import { ATOMIC_PERMISSIONS, } from '../lib/SharePermissionsToolBox.js' +import { createFocusTrap } from 'focus-trap' + export default { components: { DropdownIcon, @@ -45,6 +61,7 @@ export default { return { selectedOption: '', showDropdown: this.toggle, + focusTrap: null, } }, computed: { @@ -102,6 +119,10 @@ export default { return BUNDLED_PERMISSIONS.READ_ONLY } }, + dropdownId() { + // Generate a unique ID for ARIA attributes + return `dropdown-${Math.random().toString(36).substr(2, 9)}` + }, }, watch: { toggle(toggleValue) { @@ -110,10 +131,26 @@ export default { }, mounted() { this.initializeComponent() + window.addEventListener('click', this.handleClickOutside) + }, + beforeDestroy() { + // Remove the global click event listener to prevent memory leaks + window.removeEventListener('click', this.handleClickOutside) }, methods: { toggleDropdown() { this.showDropdown = !this.showDropdown + if (this.showDropdown) { + this.$nextTick(() => { + this.useFocusTrap() + }) + } else { + this.clearFocusTrap() + } + }, + closeDropdown() { + this.clearFocusTrap() + this.showDropdown = false }, selectOption(option) { this.selectedOption = option @@ -128,6 +165,51 @@ export default { initializeComponent() { this.selectedOption = this.preSelectedOption }, + handleClickOutside(event) { + const dropdownContainer = this.$refs.quickShareDropdownContainer + + if (dropdownContainer && !dropdownContainer.contains(event.target)) { + this.showDropdown = false + } + }, + useFocusTrap() { + // Create global stack if undefined + // Use in with trapStack to avoid conflicting traps + Object.assign(window, { _nc_focus_trap: window._nc_focus_trap || [] }) + const dropdownElement = this.$refs.quickShareDropdown + this.focusTrap = createFocusTrap(dropdownElement, { + allowOutsideClick: true, + trapStack: window._nc_focus_trap, + }) + + this.focusTrap.activate() + }, + clearFocusTrap() { + this.focusTrap?.deactivate() + this.focusTrap = null + }, + shiftFocusForward() { + const currentElement = document.activeElement + let nextElement = currentElement.nextElementSibling + if (!nextElement) { + nextElement = this.$refs.quickShareDropdown.firstElementChild + } + nextElement.focus() + }, + shiftFocusBackward() { + const currentElement = document.activeElement + let previousElement = currentElement.previousElementSibling + if (!previousElement) { + previousElement = this.$refs.quickShareDropdown.lastElementChild + } + previousElement.focus() + }, + handleArrowUp() { + this.shiftFocusBackward() + }, + handleArrowDown() { + this.shiftFocusForward() + }, }, } @@ -147,8 +229,10 @@ export default { color: var(--color-primary-element); } - .share-select-dropdown-container { + .share-select-dropdown { position: absolute; + display: flex; + flex-direction: column; top: 100%; left: 0; background-color: var(--color-main-background); @@ -160,6 +244,16 @@ export default { .dropdown-item { padding: 8px; font-size: 12px; + background: none; + border: none; + border-radius: 0; + font: inherit; + cursor: pointer; + color: inherit; + outline: none; + width: 100%; + white-space: nowrap; + text-align: left; &:hover { background-color: #f2f2f2; @@ -172,13 +266,13 @@ export default { } /* Optional: Add a transition effect for smoother dropdown animation */ - .share-select-dropdown-container { + .share-select-dropdown { max-height: 0; overflow: hidden; transition: max-height 0.3s ease; } - &.active .share-select-dropdown-container { + &.active .share-select-dropdown { max-height: 200px; /* Adjust the value to your desired height */ } diff --git a/apps/files_sharing/src/views/SharingDetailsTab.vue b/apps/files_sharing/src/views/SharingDetailsTab.vue index 175812a0ff2..6368bb20b9f 100644 --- a/apps/files_sharing/src/views/SharingDetailsTab.vue +++ b/apps/files_sharing/src/views/SharingDetailsTab.vue @@ -61,7 +61,7 @@ name="sharing_permission_radio" type="radio" button-variant-grouped="vertical" - @update:checked="toggleCustomPermissions"> + @update:checked="expandCustomPermissions"> {{ t('files_sharing', 'Custom permissions') }} <small>{{ t('files_sharing', customPermissionsList) }}</small> <template #icon> @@ -666,16 +666,14 @@ export default { this.$set(this.share, 'hasDownloadPermission', isDownloadChecked) } }, - - toggleCustomPermissions(selectedPermission) { - if (this.sharingPermission === 'custom') { + expandCustomPermissions() { + if (!this.advancedSectionAccordionExpanded) { this.advancedSectionAccordionExpanded = true - this.setCustomPermissions = true - } else { - this.advancedSectionAccordionExpanded = false - this.revertSharingPermission = selectedPermission - this.setCustomPermissions = false } + this.toggleCustomPermissions() + }, + toggleCustomPermissions() { + this.setCustomPermissions = this.sharingPermission === 'custom' }, initializeAttributes() { |