Skip to content

Commit

Permalink
Merge pull request #5809 from nextcloud-libraries/backport/5806/next
Browse files Browse the repository at this point in the history
[next] fix(NcActions): Use full window height
  • Loading branch information
susnux authored Jan 20, 2025
2 parents 24b4345 + dec8ca4 commit 285780d
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 19 deletions.
76 changes: 57 additions & 19 deletions src/components/NcActions/NcActions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -918,16 +918,17 @@ export default {
</docs>

<script>
import { useElementBounding, useWindowSize } from '@vueuse/core'
import { Fragment, computed, h, mergeProps, ref, toRef, warn } from 'vue'
import { getTrapStack } from '../../utils/focusTrap.js'
import { t } from '../../l10n.js'

import NcButton from '../NcButton/index.ts'
import NcPopover from '../NcPopover/index.js'
import GenRandomId from '../../utils/GenRandomId.js'
import isSlotPopulated from '../../utils/isSlotPopulated.ts'
import { getTrapStack } from '../../utils/focusTrap.js'
import { t } from '../../l10n.js'

import DotsHorizontal from 'vue-material-design-icons/DotsHorizontal.vue'

import { computed, h, Fragment, warn, mergeProps } from 'vue'
import IconDotsHorizontal from 'vue-material-design-icons/DotsHorizontal.vue'

const focusableSelector = '.focusable'

Expand All @@ -944,7 +945,6 @@ export default {

components: {
NcButton,
DotsHorizontal,
NcPopover,
},

Expand Down Expand Up @@ -1073,7 +1073,7 @@ export default {
*/
boundariesElement: {
type: Element,
default: () => document.querySelector('body'),
default: () => document.getElementById('content-vue') ?? document.querySelector('body'),
},

/**
Expand Down Expand Up @@ -1111,11 +1111,45 @@ export default {
'click',
],

setup(props) {
const randomId = `menu-${GenRandomId()}`
const triggerRandomId = `trigger-${randomId}`

const triggerButton = ref()

const { top, bottom } = useElementBounding(triggerButton)
const { top: boundaryTop, bottom: boundaryBottom } = useElementBounding(toRef(() => props.boundariesElement))
const { height: windowHeight } = useWindowSize()
const maxMenuHeight = computed(() => Math.max(
// Either expand to the top
Math.min(
// max height is the top position of the trigger minus the header height minus the wedge and the padding
top.value - 84,
// and also limited to the space in the boundary
top.value - boundaryTop.value,
),
// or expand to the bottom
Math.min(
// the max height is the window height minus current position of the trigger minus the wedge and padding
windowHeight.value - bottom.value - 34,
// and limit to the available space in the boundary
boundaryBottom.value - bottom.value,
),
))

return {
triggerButton,
maxMenuHeight,

randomId,
triggerRandomId,
}
},

data() {
return {
opened: this.open,
focusIndex: 0,
randomId: `menu-${GenRandomId()}`,
/**
* @type {'menu'|'navigation'|'dialog'|'tooltip'|'unknown'}
*/
Expand Down Expand Up @@ -1330,8 +1364,8 @@ export default {
this.focusIndex = 0

if (returnFocus) {
// Focus back the menu button
this.$refs.menuButton?.$el.focus()
// Focus back the trigger button
this.$refs.triggerButton?.$el.focus()
}
},

Expand All @@ -1346,30 +1380,32 @@ export default {
},

/**
* Hanle resizing the popover to make sure users can discover there is more to scroll
* Handle resizing the popover to make sure users can discover there is more to scroll
*/
resizePopover() {
// Get the inner v-popper element that defines the popover height (from floating-vue)
const inner = this.$refs.menu.closest('.v-popper__inner')
const maxHeight = Number.parseFloat(window.getComputedStyle(inner).maxHeight)
const height = this.$refs.menu.clientHeight

// If the popover height is limited by the max-height (scrollbars shown) limit the height to half of the last element
if (height > maxHeight) {
if (height > this.maxMenuHeight) {
// sum of action heights
let currentHeight = 0
// last action height
let actionHeight = 0
for (const action of this.$refs.menuList.children) {
// If the max height would be overflown by half of the current element,
// then we limit the height to the half of the previous element
if ((currentHeight + action.clientHeight / 2) > maxHeight) {
if ((currentHeight + action.clientHeight / 2) > this.maxMenuHeight) {
inner.style.height = `${currentHeight - actionHeight / 2}px`
break
}
// otherwise sum up the height
actionHeight = action.clientHeight
currentHeight += actionHeight
}
} else {
inner.style.height = 'fit-content'
}
},

Expand All @@ -1380,12 +1416,14 @@ export default {
getCurrentActiveMenuItemElement() {
return this.$refs.menu.querySelector('li.active')
},

/**
* @return {NodeListOf<HTMLElement>}
*/
getFocusableMenuItemElements() {
return this.$refs.menu.querySelectorAll(focusableSelector)
},

/**
* Focus nearest focusable item on mouse move.
* DO NOT change the focus if the target is already focused
Expand Down Expand Up @@ -1739,7 +1777,7 @@ export default {
? this.$slots.icon?.()
: (this.defaultIcon
? h('span', { class: ['icon', this.defaultIcon] })
: h(DotsHorizontal, { size: 20 })
: h(IconDotsHorizontal, { size: 20 })
)
const triggerRandomId = `${this.randomId}-trigger`
return h(NcPopover,
Expand All @@ -1754,7 +1792,7 @@ export default {
...this.manualOpen && { triggers: [] },
popoverBaseClass: 'action-item__popper',
popupRole: this.config.popupRole,
setReturnFocus: this.config.withFocusTrap ? this.$refs.menuButton?.$el : null,
setReturnFocus: this.config.withFocusTrap ? this.$refs.triggerButton?.$el : null,
focusTrap: this.config.withFocusTrap,
onShow: this.openMenu,
onApplyShow: this.onOpen,
Expand All @@ -1767,7 +1805,7 @@ export default {
type: this.triggerBtnType,
disabled: this.disabled,
ariaHidden: this.ariaHidden,
ref: 'menuButton',
ref: 'triggerButton',
'aria-label': this.menuName ? null : this.ariaLabel,
// 'aria-controls' should only present together with a valid aria-haspopup
'aria-controls': this.opened && this.config.popupRole ? this.randomId : null,
Expand Down Expand Up @@ -1937,12 +1975,12 @@ export default {
// the popover__inner for actions only.
.v-popper--theme-dropdown.v-popper__popper.action-item__popper .v-popper__wrapper {
border-radius: var(--border-radius-large);
overflow:hidden;
overflow: hidden;

.v-popper__inner {
border-radius: var(--border-radius-large);
padding: 4px;
max-height: calc(50vh - 16px);
max-height: calc(100vh - var(--header-height));
overflow: auto;
}
}
Expand Down
1 change: 1 addition & 0 deletions styleguide.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ module.exports = async () => {
path.join(__dirname, 'styleguide/global.requires.js'),
path.join(__dirname, 'styleguide/assets/icons.css'),
path.join(__dirname, 'styleguide/assets/additional.css'),
path.join(__dirname, 'styleguide/assets/styleguide.css'),
],
pagePerSection: true,
minimize: true,
Expand Down
13 changes: 13 additions & 0 deletions styleguide/assets/styleguide.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*!
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

#rsg-root {
height: 100%;
}

#rsg-root .rsg--root-1 {
height: 100%;
overflow: scroll;
}

0 comments on commit 285780d

Please sign in to comment.