aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files_sharing
diff options
context:
space:
mode:
authorLouis Chemineau <louis@chmn.me>2024-02-08 15:31:19 +0100
committerLouis Chemineau <louis@chmn.me>2024-02-08 15:31:19 +0100
commit898df41de968321926e10ad532a64c3915ddad29 (patch)
tree57a0e5ada151890ddf71550f22b502e1f67aeffd /apps/files_sharing
parentd9d60238c7aaab9c61bf2d50c15aa59bc88c8975 (diff)
downloadnextcloud-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')
-rw-r--r--apps/files_sharing/src/components/SharingEntry.vue20
-rw-r--r--apps/files_sharing/src/components/SharingEntryLink.vue14
-rw-r--r--apps/files_sharing/src/components/SharingEntryQuickShareSelect.vue261
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>