mirror of
https://github.com/nextcloud/server.git
synced 2026-04-15 22:11:17 -04:00
Merge pull request #43213 from nextcloud/fix/43139/sharing--migrate-quick-share-select-to-nc-actions
fix(sharing): migrate QuickShareSelect to NcActions
This commit is contained in:
parent
181abb52d4
commit
a7265dd46c
10 changed files with 100 additions and 205 deletions
|
|
@ -29,7 +29,7 @@
|
|||
:menu-position="'left'"
|
||||
:url="share.shareWithAvatar" />
|
||||
|
||||
<div class="sharing-entry__summary" @click.prevent="toggleQuickShareSelect">
|
||||
<div class="sharing-entry__summary">
|
||||
<component :is="share.shareWithLink ? 'a' : 'div'"
|
||||
:title="tooltip"
|
||||
:aria-label="tooltip"
|
||||
|
|
@ -41,14 +41,13 @@
|
|||
<small v-if="hasStatus && share.status.message">({{ share.status.message }})</small>
|
||||
</span>
|
||||
</component>
|
||||
<QuickShareSelect :share="share"
|
||||
<SharingEntryQuickShareSelect :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-no-background"
|
||||
type="tertiary"
|
||||
@click="openSharingDetails(share)">
|
||||
<template #icon>
|
||||
<DotsHorizontalIcon :size="20" />
|
||||
|
|
@ -63,7 +62,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 QuickShareSelect from './SharingEntryQuickShareSelect.vue'
|
||||
import SharingEntryQuickShareSelect from './SharingEntryQuickShareSelect.vue'
|
||||
|
||||
import SharesMixin from '../mixins/SharesMixin.js'
|
||||
import ShareDetails from '../mixins/ShareDetails.js'
|
||||
|
|
@ -76,16 +75,11 @@ export default {
|
|||
NcAvatar,
|
||||
DotsHorizontalIcon,
|
||||
NcSelect,
|
||||
QuickShareSelect,
|
||||
SharingEntryQuickShareSelect,
|
||||
},
|
||||
|
||||
mixins: [SharesMixin, ShareDetails],
|
||||
|
||||
data() {
|
||||
return {
|
||||
showDropdown: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
let title = this.share.shareWithDisplayName
|
||||
|
|
@ -140,9 +134,6 @@ export default {
|
|||
onMenuClose() {
|
||||
this.onNoteSubmit()
|
||||
},
|
||||
toggleQuickShareSelect() {
|
||||
this.showDropdown = !this.showDropdown
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
@ -158,6 +149,7 @@ export default {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
flex: 1 0;
|
||||
min-width: 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -27,17 +27,16 @@
|
|||
class="sharing-entry__avatar" />
|
||||
|
||||
<div class="sharing-entry__summary">
|
||||
<div class="sharing-entry__desc" @click.prevent="toggleQuickShareSelect">
|
||||
<div class="sharing-entry__desc">
|
||||
<span class="sharing-entry__title" :title="title">
|
||||
{{ title }}
|
||||
</span>
|
||||
<p v-if="subtitle">
|
||||
{{ subtitle }}
|
||||
</p>
|
||||
<QuickShareSelect v-if="share && share.permissions !== undefined"
|
||||
<SharingEntryQuickShareSelect v-if="share && share.permissions !== undefined"
|
||||
:share="share"
|
||||
:file-info="fileInfo"
|
||||
:toggle="showDropdown"
|
||||
@open-sharing-details="openShareDetailsForCustomSettings(share)" />
|
||||
</div>
|
||||
|
||||
|
|
@ -199,7 +198,7 @@ import NcAvatar from '@nextcloud/vue/dist/Components/NcAvatar.js'
|
|||
|
||||
import Tune from 'vue-material-design-icons/Tune.vue'
|
||||
|
||||
import QuickShareSelect from './SharingEntryQuickShareSelect.vue'
|
||||
import SharingEntryQuickShareSelect from './SharingEntryQuickShareSelect.vue'
|
||||
|
||||
import ExternalShareAction from './ExternalShareAction.vue'
|
||||
import GeneratePassword from '../utils/GeneratePassword.js'
|
||||
|
|
@ -220,7 +219,7 @@ export default {
|
|||
NcActionSeparator,
|
||||
NcAvatar,
|
||||
Tune,
|
||||
QuickShareSelect,
|
||||
SharingEntryQuickShareSelect,
|
||||
},
|
||||
|
||||
mixins: [SharesMixin, ShareDetails],
|
||||
|
|
@ -238,7 +237,6 @@ export default {
|
|||
|
||||
data() {
|
||||
return {
|
||||
showDropdown: false,
|
||||
copySuccess: true,
|
||||
copied: false,
|
||||
|
||||
|
|
@ -730,10 +728,6 @@ export default {
|
|||
// YET. We can safely delete the share :)
|
||||
this.$emit('remove:share', this.share)
|
||||
},
|
||||
|
||||
toggleQuickShareSelect() {
|
||||
this.showDropdown = !this.showDropdown
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,32 +1,25 @@
|
|||
<template>
|
||||
<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 }}
|
||||
<NcActions ref="quickShareActions"
|
||||
class="share-select"
|
||||
:menu-name="selectedOption"
|
||||
:aria-label="ariaLabel"
|
||||
type="tertiary-no-background"
|
||||
force-name>
|
||||
<template #icon>
|
||||
<DropdownIcon :size="15" />
|
||||
</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>
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -34,37 +27,48 @@ 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'
|
||||
|
||||
import { createFocusTrap } from 'focus-trap'
|
||||
|
||||
export default {
|
||||
name: 'SharingEntryQuickShareSelect',
|
||||
|
||||
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')
|
||||
},
|
||||
|
|
@ -91,11 +95,23 @@ export default {
|
|||
|
||||
},
|
||||
options() {
|
||||
const options = [this.canViewText, this.canEditText]
|
||||
const options = [{
|
||||
label: this.canViewText,
|
||||
icon: IconEyeOutline,
|
||||
}, {
|
||||
label: this.canEditText,
|
||||
icon: IconPencil,
|
||||
}]
|
||||
if (this.supportsFileDrop) {
|
||||
options.push(this.fileDropText)
|
||||
options.push({
|
||||
label: this.fileDropText,
|
||||
icon: IconFileUpload,
|
||||
})
|
||||
}
|
||||
options.push(this.customPermissionsText)
|
||||
options.push({
|
||||
label: this.customPermissionsText,
|
||||
icon: IconTune,
|
||||
})
|
||||
|
||||
return options
|
||||
},
|
||||
|
|
@ -119,96 +135,23 @@ 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) {
|
||||
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)
|
||||
|
||||
created() {
|
||||
this.selectedOption = this.preSelectedOption
|
||||
},
|
||||
|
||||
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
|
||||
if (option === this.customPermissionsText) {
|
||||
selectOption(optionLabel) {
|
||||
this.selectedOption = optionLabel
|
||||
if (optionLabel === 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()
|
||||
},
|
||||
},
|
||||
|
||||
|
|
@ -217,65 +160,31 @@ export default {
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.share-select {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
|
||||
.trigger-text {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
font-size: 12.5px;
|
||||
gap: 2px;
|
||||
color: var(--color-primary-element);
|
||||
}
|
||||
// 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;
|
||||
|
||||
.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);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background-color: var(--color-background-dark);
|
||||
}
|
||||
.button-vue__text {
|
||||
font-weight: normal !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Optional: Add a transition effect for smoother dropdown animation */
|
||||
.share-select-dropdown {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.3s ease;
|
||||
}
|
||||
.button-vue__icon {
|
||||
height: 24px !important;
|
||||
min-height: 24px !important;
|
||||
width: 24px !important;
|
||||
min-width: 24px !important;
|
||||
}
|
||||
|
||||
&.active .share-select-dropdown {
|
||||
max-height: 200px;
|
||||
/* Adjust the value to your desired height */
|
||||
.button-vue__wrapper {
|
||||
// Emulate NcButton's alignment=center-reverse
|
||||
flex-direction: row-reverse !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
3
dist/1794-1794.js
vendored
3
dist/1794-1794.js
vendored
File diff suppressed because one or more lines are too long
1
dist/1794-1794.js.map
vendored
1
dist/1794-1794.js.map
vendored
File diff suppressed because one or more lines are too long
3
dist/744-744.js
vendored
Normal file
3
dist/744-744.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/744-744.js.map
vendored
Normal file
1
dist/744-744.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
4
dist/files_sharing-files_sharing_tab.js
vendored
4
dist/files_sharing-files_sharing_tab.js
vendored
File diff suppressed because one or more lines are too long
2
dist/files_sharing-files_sharing_tab.js.map
vendored
2
dist/files_sharing-files_sharing_tab.js.map
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue