1
0
Fork 0
mirror of https://gitlab.com/bramw/baserow.git synced 2025-04-15 09:34:13 +00:00

Merge branch '590-the-currently-selected-view-is-not-scrolled-to-automatically-in-the-viewscontext-menu' into 'develop'

Resolve "The currently selected view is not scrolled to automatically in the ViewsContext menu"

Closes  and 

See merge request 
This commit is contained in:
Sascha Jullmann 2021-10-04 18:31:58 +00:00
commit c684f3f942
4 changed files with 157 additions and 43 deletions
changelog.md
web-frontend/modules
core/mixins
database/components/view

View file

@ -24,6 +24,8 @@
'before_id' when moving a row by introducing a decorator to validate query parameters.
* Fixed bug where copying a cell containing a null value resulted in an error.
* Added "Multiple Select" field type.
* Fixed a bug where the currently selected view was not in the viewport of the parent.
* Fixed a bug where views context would not scroll down after a new view has been added.
## Released (2021-08-11)

View file

@ -1,6 +1,8 @@
import { isDomElement, isElement } from '@baserow/modules/core/utils/dom'
import dropdownHelpers from './dropdownHelpers'
export default {
mixins: [dropdownHelpers],
props: {
value: {
type: [String, Number, Boolean, Object],
@ -256,44 +258,14 @@ export default {
* to is the first viewable item in the dropdown window.
*/
getScrollTopAmountForNextChild(itemToScrollTo, isArrowUp) {
// Styles of the itemToScroll to. Needed in order to get margins and height
const itemToScrollToStyles =
itemToScrollTo.$el.currentStyle ||
window.getComputedStyle(itemToScrollTo.$el)
// Styles of the ref items (the dropdown window). Needed in order to get
// ::before height and ::after height
const dropdownWindowBeforeStyles =
this.$refs.items.currentStyle ||
window.getComputedStyle(this.$refs.items, ':before')
const dropdownWindowAfterStyles =
this.$refs.items.currentStyle ||
window.getComputedStyle(this.$refs.items, ':after')
const dropdownWindowBeforeHeight = parseInt(
dropdownWindowBeforeStyles.height
)
const dropdownWindowAfterHeight = parseInt(
dropdownWindowAfterStyles.height
)
const dropdownWindowHeight = this.$refs.items.clientHeight
const itemHeight = parseInt(itemToScrollToStyles.height)
const itemMarginTop = parseInt(itemToScrollToStyles.marginTop)
const itemMarginBottom = parseInt(itemToScrollToStyles.marginBottom)
const itemHeightWithMargins =
itemHeight + itemMarginTop + itemMarginBottom
// Based on the values set in the SCSS files. The height of a dropdowns select
// item is set to 32px and the height of the select_items window is set to 4 *
// 36 (select item height plus margins) plus 20 (heights of before and after
// pseudo elements) so that there is room for four elements
const itemsInView =
(dropdownWindowHeight -
dropdownWindowBeforeHeight -
dropdownWindowAfterHeight) /
itemHeightWithMargins
const {
parentContainerHeight,
parentContainerAfterHeight,
parentContainerBeforeHeight,
itemHeightWithMargins,
itemMarginTop,
itemsInView,
} = this.getStyleProperties(this.$refs.items, itemToScrollTo.$el)
// Get the direction of the scrolling.
const movingDownwards = !isArrowUp
@ -306,7 +278,7 @@ export default {
// of the dropdown minus the full height of the element
const nextItemOutOfView =
itemToScrollTo.$el.offsetTop - this.$refs.items.scrollTop >
dropdownWindowHeight - itemHeightWithMargins
parentContainerHeight - itemHeightWithMargins
// prevItemOutOfView can be used if one wants to check if the item to scroll
// to is out of view of the current dropdowns top scroll position.
@ -317,7 +289,7 @@ export default {
// When the user is scrolling downwards (i.e. pressing key down)
// and the itemToScrollTo is out of view we want to add the height of the
// elements preceding the itemToScrollTo plus the dropdownWindowBeforeHeight.
// elements preceding the itemToScrollTo plus the parentContainerBeforeHeight.
// This can be achieved by removing said heights from the itemToScrollTo's
// offsetTop
if (nextItemOutOfView && movingDownwards) {
@ -327,7 +299,7 @@ export default {
return (
itemToScrollTo.$el.offsetTop -
elementsHeightBeforeItemToScrollTo -
dropdownWindowBeforeHeight
parentContainerBeforeHeight
)
}
@ -339,7 +311,7 @@ export default {
return (
itemToScrollTo.$el.offsetTop -
itemMarginTop -
dropdownWindowAfterHeight
parentContainerAfterHeight
)
}

View file

@ -0,0 +1,71 @@
export default {
methods: {
/**
* This method is a helper for extracting certain style properties of a parent container
* and one of it's child items. This is helpful for determining the amount of pixels
* to scroll in order to get a child element aligned at the bottom of the parent container,
* either when scrolling by keydown/keyup or manually setting a scroll position.
*
* It expects the parent container to have :before and :after pseudo elements.
*
* Returns an object containing the following:
*
* parentContainerHeight: The height of the parent container.
* parentContainerAfterHeight: The height of the parents 'after' pseudo element.
* parentContainerBeforeHeight: The height of the parents 'before' pseudo element.
* itemHeightWithMargins: The height of the child item including its margins.
* itemMarginTop: The top margin of the child item.
* itemMarginBottom: The bottom margin of the child item.
* itemsInView: The amount of times the child item fits into the parent viewport.
*/
getStyleProperties(parentContainer, childItem) {
// Styles of the childItem. Needed in order to get margins and height
const childItemStyles =
childItem.currentStyle || window.getComputedStyle(childItem)
// Styles of the parent container. Needed in order to get
// ::before height and ::after height
const parentContainerBeforeStyles =
parentContainer.currentStyle ||
window.getComputedStyle(parentContainer, ':before')
const parentContainerAfterStyles =
parentContainer.currentStyle ||
window.getComputedStyle(parentContainer, ':after')
const parentContainerBeforeHeight = parseInt(
parentContainerBeforeStyles.height
)
const parentContainerAfterHeight = parseInt(
parentContainerAfterStyles.height
)
const parentContainerHeight = parentContainer.clientHeight
const itemHeight = parseInt(childItemStyles.height)
const itemMarginTop = parseInt(childItemStyles.marginTop)
const itemMarginBottom = parseInt(childItemStyles.marginBottom)
const itemHeightWithMargins =
itemHeight + itemMarginTop + itemMarginBottom
// Based on the values set in the SCSS files. The height of a dropdowns select
// item is set to 32px and the height of the select_items window is set to 4 *
// 36 (select item height plus margins) plus 20 (heights of before and after
// pseudo elements) so that there is room for four elements
const itemsInView =
(parentContainerHeight -
parentContainerBeforeHeight -
parentContainerAfterHeight) /
itemHeightWithMargins
return {
parentContainerHeight,
parentContainerAfterHeight,
parentContainerBeforeHeight,
itemHeightWithMargins,
itemMarginTop,
itemMarginBottom,
itemsInView,
}
},
},
}

View file

@ -14,11 +14,13 @@
</div>
<ul
v-if="!isLoading && views.length > 0"
ref="dropdown"
v-auto-overflow-scroll
class="select__items"
>
<ViewsContextItem
v-for="view in searchAndOrder(views)"
:ref="'view-' + view.id"
:key="view.id"
v-sortable="{ id: view.id, update: order, marginTop: -1.5 }"
:view="view"
@ -51,6 +53,7 @@
:ref="'createViewModal' + type"
:table="table"
:view-type="viewType"
@created="scrollViewDropdownToBottom()"
></CreateViewModal>
</a>
</div>
@ -62,6 +65,7 @@
import { mapState } from 'vuex'
import context from '@baserow/modules/core/mixins/context'
import dropdownHelpers from '@baserow/modules/core/mixins/dropdownHelpers'
import { notifyIf } from '@baserow/modules/core/utils/error'
import ViewsContextItem from '@baserow/modules/database/components/view/ViewsContextItem'
import CreateViewModal from '@baserow/modules/database/components/view/CreateViewModal'
@ -72,7 +76,7 @@ export default {
ViewsContextItem,
CreateViewModal,
},
mixins: [context],
mixins: [context, dropdownHelpers],
props: {
table: {
type: Object,
@ -102,11 +106,76 @@ export default {
isLoaded: (state) => state.view.loaded,
}),
},
mounted() {
this.scrollViewDropdownIfNeeded()
},
methods: {
selectedView(view) {
this.hide()
this.$emit('selected-view', view)
},
/*
If the currently selected view is not visible inside the dropdown we need to
scroll just enough so that the selected view is visible as the last element
in the dropdown.
*/
scrollViewDropdownIfNeeded() {
const dropdownElement = this.$refs.dropdown
const selectedViewItem = this.getSelectedViewItem()
const dropdownHeight = dropdownElement.clientHeight
if (
this.isSelectedViewOutOfDropdownView(selectedViewItem, dropdownHeight)
) {
dropdownElement.scrollTop = this.calculateOffsetToSelectedViewItem(
dropdownElement,
selectedViewItem
)
}
},
/**
* This method scrolls the ViewDropdown to the bottom
*/
scrollViewDropdownToBottom() {
this.$refs.dropdown.scrollTop = this.$refs.dropdown.scrollHeight
},
/**
* This method filters the view elements and returns the currently selected
* view dom item based on whether or not it is selected.
*/
getSelectedViewItem() {
const selectedViewArray = this.views.filter((item) => item._.selected)
const selectedViewItemID = selectedViewArray[0].id
return this.$refs[`view-${selectedViewItemID}`][0].$el
},
/**
* This method calculates whether or not the selectedViewItem is fully visible
* inside the ViewContext dropdown or not
*/
isSelectedViewOutOfDropdownView(selectedViewItem, dropdownHeight) {
const selectedOffsetPlusHeight =
selectedViewItem.offsetTop + selectedViewItem.clientHeight
return selectedOffsetPlusHeight > dropdownHeight
},
/**
* This method calculates the necessary offsetTop of the dropdown element so that
* the selected view item is the bottom element.
*/
calculateOffsetToSelectedViewItem(dropdownElement, selectedViewItem) {
const {
parentContainerBeforeHeight,
itemHeightWithMargins,
itemsInView,
} = this.getStyleProperties(dropdownElement, selectedViewItem)
const viewItemsBeforeSelectedViewItemHeight =
(itemsInView - 1) * itemHeightWithMargins
return (
selectedViewItem.offsetTop -
viewItemsBeforeSelectedViewItemHeight -
parentContainerBeforeHeight
)
},
toggleCreateViewModal(type) {
const target = this.$refs['createViewModalToggle' + type][0]
this.$refs['createViewModal' + type][0].toggle(target)