diff options
author | Louis Chemineau <louis@chmn.me> | 2024-02-08 15:31:19 +0100 |
---|---|---|
committer | Louis Chemineau <louis@chmn.me> | 2024-02-08 15:31:19 +0100 |
commit | 898df41de968321926e10ad532a64c3915ddad29 (patch) | |
tree | 57a0e5ada151890ddf71550f22b502e1f67aeffd /apps/files_sharing | |
parent | d9d60238c7aaab9c61bf2d50c15aa59bc88c8975 (diff) | |
download | nextcloud-server-898df41de968321926e10ad532a64c3915ddad29.tar.gz nextcloud-server-898df41de968321926e10ad532a64c3915ddad29.zip |
Revert "Merge branch 'master' of github.com:nextcloud/server"
This reverts commit d9d60238c7aaab9c61bf2d50c15aa59bc88c8975, reversing
changes made to ba3fdb0cdcfbb84f0080a2146a4ba2f01569915d.
Diffstat (limited to 'apps/files_sharing')
3 files changed, 200 insertions, 95 deletions
diff --git a/apps/files_sharing/src/components/SharingEntry.vue b/apps/files_sharing/src/components/SharingEntry.vue index 74bff87560a..84525fa2f0c 100644 --- a/apps/files_sharing/src/components/SharingEntry.vue +++ b/apps/files_sharing/src/components/SharingEntry.vue @@ -29,7 +29,7 @@ :menu-position="'left'" :url="share.shareWithAvatar" /> - <div class="sharing-entry__summary"> + <div class="sharing-entry__summary" @click.prevent="toggleQuickShareSelect"> <component :is="share.shareWithLink ? 'a' : 'div'" :title="tooltip" :aria-label="tooltip" @@ -41,13 +41,14 @@ <small v-if="hasStatus && share.status.message">({{ share.status.message }})</small> </span> </component> - <SharingEntryQuickShareSelect :share="share" + <QuickShareSelect :share="share" :file-info="fileInfo" + :toggle="showDropdown" @open-sharing-details="openShareDetailsForCustomSettings(share)" /> </div> <NcButton class="sharing-entry__action" :aria-label="t('files_sharing', 'Open Sharing Details')" - type="tertiary" + type="tertiary-no-background" @click="openSharingDetails(share)"> <template #icon> <DotsHorizontalIcon :size="20" /> @@ -62,7 +63,7 @@ import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js' import NcAvatar from '@nextcloud/vue/dist/Components/NcAvatar.js' import DotsHorizontalIcon from 'vue-material-design-icons/DotsHorizontal.vue' -import SharingEntryQuickShareSelect from './SharingEntryQuickShareSelect.vue' +import QuickShareSelect from './SharingEntryQuickShareSelect.vue' import SharesMixin from '../mixins/SharesMixin.js' import ShareDetails from '../mixins/ShareDetails.js' @@ -75,11 +76,16 @@ export default { NcAvatar, DotsHorizontalIcon, NcSelect, - SharingEntryQuickShareSelect, + QuickShareSelect, }, mixins: [SharesMixin, ShareDetails], + data() { + return { + showDropdown: false, + } + }, computed: { title() { let title = this.share.shareWithDisplayName @@ -134,6 +140,9 @@ export default { onMenuClose() { this.onNoteSubmit() }, + toggleQuickShareSelect() { + this.showDropdown = !this.showDropdown + }, }, } </script> @@ -149,7 +158,6 @@ export default { display: flex; flex-direction: column; justify-content: center; - align-items: flex-start; flex: 1 0; min-width: 0; diff --git a/apps/files_sharing/src/components/SharingEntryLink.vue b/apps/files_sharing/src/components/SharingEntryLink.vue index d9060881f15..d59f569dd0e 100644 --- a/apps/files_sharing/src/components/SharingEntryLink.vue +++ b/apps/files_sharing/src/components/SharingEntryLink.vue @@ -27,16 +27,17 @@ class="sharing-entry__avatar" /> <div class="sharing-entry__summary"> - <div class="sharing-entry__desc"> + <div class="sharing-entry__desc" @click.prevent="toggleQuickShareSelect"> <span class="sharing-entry__title" :title="title"> {{ title }} </span> <p v-if="subtitle"> {{ subtitle }} </p> - <SharingEntryQuickShareSelect v-if="share && share.permissions !== undefined" + <QuickShareSelect v-if="share && share.permissions !== undefined" :share="share" :file-info="fileInfo" + :toggle="showDropdown" @open-sharing-details="openShareDetailsForCustomSettings(share)" /> </div> @@ -198,7 +199,7 @@ import NcAvatar from '@nextcloud/vue/dist/Components/NcAvatar.js' import Tune from 'vue-material-design-icons/Tune.vue' -import SharingEntryQuickShareSelect from './SharingEntryQuickShareSelect.vue' +import QuickShareSelect from './SharingEntryQuickShareSelect.vue' import ExternalShareAction from './ExternalShareAction.vue' import GeneratePassword from '../utils/GeneratePassword.js' @@ -219,7 +220,7 @@ export default { NcActionSeparator, NcAvatar, Tune, - SharingEntryQuickShareSelect, + QuickShareSelect, }, mixins: [SharesMixin, ShareDetails], @@ -237,6 +238,7 @@ export default { data() { return { + showDropdown: false, copySuccess: true, copied: false, @@ -728,6 +730,10 @@ export default { // YET. We can safely delete the share :) this.$emit('remove:share', this.share) }, + + toggleQuickShareSelect() { + this.showDropdown = !this.showDropdown + }, }, } </script> diff --git a/apps/files_sharing/src/components/SharingEntryQuickShareSelect.vue b/apps/files_sharing/src/components/SharingEntryQuickShareSelect.vue index 79302ec6fbe..2bf30533b59 100644 --- a/apps/files_sharing/src/components/SharingEntryQuickShareSelect.vue +++ b/apps/files_sharing/src/components/SharingEntryQuickShareSelect.vue @@ -1,25 +1,32 @@ <template> - <NcActions ref="quickShareActions" - class="share-select" - :menu-name="selectedOption" - :aria-label="ariaLabel" - type="tertiary-no-background" - force-name> - <template #icon> + <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" /> - </template> - <NcActionButton v-for="option in options" - :key="option.label" - type="radio" - :model-value="option.label === selectedOption" - close-after-click - @click="selectOption(option.label)"> - <template #icon> - <component :is="option.icon" /> - </template> - {{ option.label }} - </NcActionButton> - </NcActions> + </span> + <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 }} + </button> + </div> + </div> </template> <script> @@ -27,48 +34,37 @@ import DropdownIcon from 'vue-material-design-icons/TriangleSmallDown.vue' import SharesMixin from '../mixins/SharesMixin.js' import ShareDetails from '../mixins/ShareDetails.js' import ShareTypes from '../mixins/ShareTypes.js' -import NcActions from '@nextcloud/vue/dist/Components/NcActions.js' -import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js' -import IconEyeOutline from 'vue-material-design-icons/EyeOutline.vue' -import IconPencil from 'vue-material-design-icons/Pencil.vue' -import IconFileUpload from 'vue-material-design-icons/FileUpload.vue' -import IconTune from 'vue-material-design-icons/Tune.vue' import { BUNDLED_PERMISSIONS, ATOMIC_PERMISSIONS, } from '../lib/SharePermissionsToolBox.js' -export default { - name: 'SharingEntryQuickShareSelect', +import { createFocusTrap } from 'focus-trap' +export default { components: { DropdownIcon, - NcActions, - NcActionButton, }, - mixins: [SharesMixin, ShareDetails, ShareTypes], - props: { share: { type: Object, required: true, }, + toggle: { + type: Boolean, + default: false, + }, }, - - emits: ['open-sharing-details'], - data() { return { selectedOption: '', + showDropdown: this.toggle, + focusTrap: null, } }, - computed: { - ariaLabel() { - return t('files_sharing', 'Quick share options, the current selected is "{selectedOption}"', { selectedOption: this.selectedOption }) - }, canViewText() { return t('files_sharing', 'View only') }, @@ -95,23 +91,11 @@ export default { }, options() { - const options = [{ - label: this.canViewText, - icon: IconEyeOutline, - }, { - label: this.canEditText, - icon: IconPencil, - }] + const options = [this.canViewText, this.canEditText] if (this.supportsFileDrop) { - options.push({ - label: this.fileDropText, - icon: IconFileUpload, - }) + options.push(this.fileDropText) } - options.push({ - label: this.customPermissionsText, - icon: IconTune, - }) + options.push(this.customPermissionsText) return options }, @@ -135,23 +119,96 @@ export default { return BUNDLED_PERMISSIONS.READ_ONLY } }, + dropdownId() { + // Generate a unique ID for ARIA attributes + return `dropdown-${Math.random().toString(36).substr(2, 9)}` + }, }, - - created() { - this.selectedOption = this.preSelectedOption + watch: { + toggle(toggleValue) { + this.showDropdown = toggleValue + }, + }, + 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: { - selectOption(optionLabel) { - this.selectedOption = optionLabel - if (optionLabel === this.customPermissionsText) { + 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 + if (option === this.customPermissionsText) { this.$emit('open-sharing-details') } else { this.share.permissions = this.dropDownPermissionValue this.queueUpdate('permissions') - // TODO: Add a focus method to NcActions or configurable returnFocus enabling to NcActionButton with closeAfterClick - this.$refs.quickShareActions.$refs.menuButton.$el.focus() } + this.showDropdown = false + }, + 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() }, }, @@ -160,31 +217,65 @@ export default { <style lang="scss" scoped> .share-select { - display: block; - - // TODO: NcActions should have a slot for custom trigger button like NcPopover - // Overrider NcActionms button to make it small - :deep(.action-item__menutoggle) { - color: var(--color-primary-element) !important; - font-size: 12.5px !important; - height: auto !important; - min-height: auto !important; - - .button-vue__text { - font-weight: normal !important; - } + position: relative; + cursor: pointer; - .button-vue__icon { - height: 24px !important; - min-height: 24px !important; - width: 24px !important; - min-width: 24px !important; - } + .trigger-text { + display: flex; + flex-direction: row; + align-items: center; + font-size: 12.5px; + gap: 2px; + color: var(--color-primary-element); + } + + .share-select-dropdown { + position: absolute; + display: flex; + flex-direction: column; + top: 100%; + left: 0; + background-color: var(--color-main-background); + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + border: 1px solid var(--color-border); + padding: 4px 0; + z-index: 1; + + .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: var(--color-background-dark); + } - .button-vue__wrapper { - // Emulate NcButton's alignment=center-reverse - flex-direction: row-reverse !important; + &.selected { + background-color: var(--color-background-dark); + } } } + + /* Optional: Add a transition effect for smoother dropdown animation */ + .share-select-dropdown { + max-height: 0; + overflow: hidden; + transition: max-height 0.3s ease; + } + + &.active .share-select-dropdown { + max-height: 200px; + /* Adjust the value to your desired height */ + } } </style> |