Rewrite settings bar updates to improve performance
This commit is contained in:
parent
1f99ecc529
commit
d423d530e4
|
@ -198,7 +198,7 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 410px;
|
height: 410px;
|
||||||
}
|
}
|
||||||
div.in-builder {
|
div.in-builder :global(> *) {
|
||||||
pointer-events: none !important;
|
pointer-events: none !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -11,25 +11,24 @@
|
||||||
const context = getContext("context")
|
const context = getContext("context")
|
||||||
const verticalOffset = 36
|
const verticalOffset = 36
|
||||||
const horizontalOffset = 2
|
const horizontalOffset = 2
|
||||||
|
const observer = new MutationObserver(() => debouncedUpdate())
|
||||||
|
|
||||||
let top = 0
|
let top = 0
|
||||||
let left = 0
|
let left = 0
|
||||||
let interval
|
let interval
|
||||||
let self
|
let self
|
||||||
let measured = false
|
let measured = false
|
||||||
let observer
|
let observing = false
|
||||||
|
|
||||||
let insideGrid = false
|
let insideGrid = false
|
||||||
let gridHAlign
|
let gridHAlign
|
||||||
let gridVAlign
|
let gridVAlign
|
||||||
|
let deviceEl
|
||||||
|
|
||||||
// TODO: respect dependsOn keys
|
$: id = $builderStore.selectedComponentId
|
||||||
|
$: id, reset()
|
||||||
$: componentId = $builderStore.selectedComponentId
|
|
||||||
$: measured, observeComputedStyles(componentId)
|
|
||||||
$: component = $componentStore.selectedComponent
|
$: component = $componentStore.selectedComponent
|
||||||
$: definition = $componentStore.selectedComponentDefinition
|
$: definition = $componentStore.selectedComponentDefinition
|
||||||
$: instance = componentStore.actions.getComponentInstance(componentId)
|
$: instance = componentStore.actions.getComponentInstance(id)
|
||||||
$: state = $instance?.state
|
$: state = $instance?.state
|
||||||
$: showBar =
|
$: showBar =
|
||||||
definition?.showSettingsBar !== false &&
|
definition?.showSettingsBar !== false &&
|
||||||
|
@ -37,7 +36,7 @@
|
||||||
definition &&
|
definition &&
|
||||||
!$state?.errorState
|
!$state?.errorState
|
||||||
$: settings = getBarSettings(component, definition)
|
$: settings = getBarSettings(component, definition)
|
||||||
$: isRoot = componentId === $builderStore.screen?.props?._id
|
$: isRoot = id === $builderStore.screen?.props?._id
|
||||||
$: showGridStyles =
|
$: showGridStyles =
|
||||||
insideGrid &&
|
insideGrid &&
|
||||||
(definition?.grid?.hAlign !== "stretch" ||
|
(definition?.grid?.hAlign !== "stretch" ||
|
||||||
|
@ -47,6 +46,21 @@
|
||||||
$: gridHAlignVar = getGridVar(device, GridParams.HAlign)
|
$: gridHAlignVar = getGridVar(device, GridParams.HAlign)
|
||||||
$: gridVAlignVar = getGridVar(device, GridParams.VAlign)
|
$: gridVAlignVar = getGridVar(device, GridParams.VAlign)
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
observer.disconnect()
|
||||||
|
measured = false
|
||||||
|
observing = false
|
||||||
|
insideGrid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const startObserving = domBoundary => {
|
||||||
|
observer.observe(domBoundary, {
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: ["style"],
|
||||||
|
})
|
||||||
|
observing = true
|
||||||
|
}
|
||||||
|
|
||||||
const getBarSettings = (component, definition) => {
|
const getBarSettings = (component, definition) => {
|
||||||
let allSettings = []
|
let allSettings = []
|
||||||
definition?.settings?.forEach(setting => {
|
definition?.settings?.forEach(setting => {
|
||||||
|
@ -68,102 +82,102 @@
|
||||||
if (!showBar) {
|
if (!showBar) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const id = $builderStore.selectedComponentId
|
|
||||||
let element = document.getElementsByClassName(id)?.[0]
|
|
||||||
|
|
||||||
// Check if we're inside a grid
|
// Find DOM boundary and ensure it is valid
|
||||||
insideGrid = element?.parentNode.classList.contains("grid")
|
let domBoundary = document.getElementsByClassName(id)[0]
|
||||||
|
if (!domBoundary) {
|
||||||
|
return reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're inside a grid, allow time for buttons to render
|
||||||
|
const nextInsideGrid = domBoundary.dataset.insideGrid === "true"
|
||||||
|
if (nextInsideGrid && !insideGrid) {
|
||||||
|
insideGrid = true
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
insideGrid = nextInsideGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the correct DOM boundary depending if we're inside a grid or not
|
||||||
if (!insideGrid) {
|
if (!insideGrid) {
|
||||||
element = element?.children?.[0]
|
domBoundary =
|
||||||
|
domBoundary.getElementsByClassName(`${id}-dom`)[0] ||
|
||||||
|
domBoundary.children?.[0]
|
||||||
|
}
|
||||||
|
if (!domBoundary || !self) {
|
||||||
|
return reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
// The settings bar is higher in the dom tree than the selection indicators
|
// Start observing if required
|
||||||
// as we want to be able to render the settings bar wider than the screen,
|
if (!observing) {
|
||||||
// or outside the screen.
|
startObserving(domBoundary)
|
||||||
// Therefore we use the clip root rather than the app root to determine
|
}
|
||||||
// its position.
|
|
||||||
const device = document.getElementById("clip-root")
|
|
||||||
if (element && self) {
|
|
||||||
// Batch reads to minimize reflow
|
|
||||||
const deviceBounds = device.getBoundingClientRect()
|
|
||||||
const elBounds = element.getBoundingClientRect()
|
|
||||||
const width = self.offsetWidth
|
|
||||||
const height = self.offsetHeight
|
|
||||||
const { scrollX, scrollY, innerWidth } = window
|
|
||||||
|
|
||||||
// Read grid metadata from data attributes
|
// Batch reads to minimize reflow
|
||||||
if (insideGrid) {
|
const deviceBounds = deviceEl.getBoundingClientRect()
|
||||||
if (mobile) {
|
const elBounds = domBoundary.getBoundingClientRect()
|
||||||
gridHAlign = element.dataset.gridMobileHAlign
|
const width = self.offsetWidth
|
||||||
gridVAlign = element.dataset.gridMobileVAlign
|
const height = self.offsetHeight
|
||||||
} else {
|
const { scrollX, scrollY, innerWidth } = window
|
||||||
gridHAlign = element.dataset.gridDesktopHAlign
|
|
||||||
gridVAlign = element.dataset.gridDesktopVAlign
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vertically, always render above unless no room, then render inside
|
// Read grid metadata from data attributes
|
||||||
let newTop = elBounds.top + scrollY - verticalOffset - height
|
if (insideGrid) {
|
||||||
if (newTop < deviceBounds.top - 50) {
|
if (mobile) {
|
||||||
newTop = deviceBounds.top - 50
|
gridHAlign = domBoundary.dataset.gridMobileHAlign
|
||||||
}
|
gridVAlign = domBoundary.dataset.gridMobileVAlign
|
||||||
if (newTop < 0) {
|
} else {
|
||||||
newTop = 0
|
gridHAlign = domBoundary.dataset.gridDesktopHAlign
|
||||||
}
|
gridVAlign = domBoundary.dataset.gridDesktopVAlign
|
||||||
const deviceBottom = deviceBounds.top + deviceBounds.height
|
|
||||||
if (newTop > deviceBottom - 44) {
|
|
||||||
newTop = deviceBottom - 44
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//If element is at the very top of the screen, put the bar below the element
|
// Vertically, always render above unless no room, then render inside
|
||||||
if (elBounds.top < elBounds.height && elBounds.height < 80) {
|
let newTop = elBounds.top + scrollY - verticalOffset - height
|
||||||
newTop = elBounds.bottom + verticalOffset
|
if (newTop < deviceBounds.top - 50) {
|
||||||
}
|
newTop = deviceBounds.top - 50
|
||||||
|
}
|
||||||
|
if (newTop < 0) {
|
||||||
|
newTop = 0
|
||||||
|
}
|
||||||
|
const deviceBottom = deviceBounds.top + deviceBounds.height
|
||||||
|
if (newTop > deviceBottom - 44) {
|
||||||
|
newTop = deviceBottom - 44
|
||||||
|
}
|
||||||
|
|
||||||
// Horizontally, try to center first.
|
//If element is at the very top of the screen, put the bar below the element
|
||||||
// Failing that, render to left edge of component.
|
if (elBounds.top < elBounds.height && elBounds.height < 80) {
|
||||||
// Failing that, render to right edge of component,
|
newTop = elBounds.bottom + verticalOffset
|
||||||
// Failing that, render to window left edge and accept defeat.
|
}
|
||||||
let elCenter = elBounds.left + scrollX + elBounds.width / 2
|
|
||||||
let newLeft = elCenter - width / 2
|
// Horizontally, try to center first.
|
||||||
|
// Failing that, render to left edge of component.
|
||||||
|
// Failing that, render to right edge of component,
|
||||||
|
// Failing that, render to window left edge and accept defeat.
|
||||||
|
let elCenter = elBounds.left + scrollX + elBounds.width / 2
|
||||||
|
let newLeft = elCenter - width / 2
|
||||||
|
if (newLeft < 0 || newLeft + width > innerWidth) {
|
||||||
|
newLeft = elBounds.left + scrollX - horizontalOffset
|
||||||
if (newLeft < 0 || newLeft + width > innerWidth) {
|
if (newLeft < 0 || newLeft + width > innerWidth) {
|
||||||
newLeft = elBounds.left + scrollX - horizontalOffset
|
newLeft = elBounds.right + scrollX - width + horizontalOffset
|
||||||
if (newLeft < 0 || newLeft + width > innerWidth) {
|
if (newLeft < 0 || newLeft + width > innerWidth) {
|
||||||
newLeft = elBounds.right + scrollX - width + horizontalOffset
|
newLeft = horizontalOffset
|
||||||
if (newLeft < 0 || newLeft + width > innerWidth) {
|
|
||||||
newLeft = horizontalOffset
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only update state when things changes to minimize renders
|
|
||||||
if (Math.round(newTop) !== Math.round(top)) {
|
|
||||||
top = newTop
|
|
||||||
}
|
|
||||||
if (Math.round(newLeft) !== Math.round(left)) {
|
|
||||||
left = newLeft
|
|
||||||
}
|
|
||||||
|
|
||||||
measured = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only update state when things changes to minimize renders
|
||||||
|
if (Math.round(newTop) !== Math.round(top)) {
|
||||||
|
top = newTop
|
||||||
|
}
|
||||||
|
if (Math.round(newLeft) !== Math.round(left)) {
|
||||||
|
left = newLeft
|
||||||
|
}
|
||||||
|
measured = true
|
||||||
}
|
}
|
||||||
const debouncedUpdate = Utils.domDebounce(updatePosition)
|
const debouncedUpdate = Utils.domDebounce(updatePosition)
|
||||||
|
|
||||||
const observeComputedStyles = id => {
|
|
||||||
observer?.disconnect()
|
|
||||||
const node = document.getElementsByClassName(`${id}-dom`)[0]?.parentNode
|
|
||||||
if (node) {
|
|
||||||
observer = new MutationObserver(updatePosition)
|
|
||||||
observer.observe(node, {
|
|
||||||
attributes: true,
|
|
||||||
attributeFilter: ["style"],
|
|
||||||
childList: false,
|
|
||||||
subtree: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
deviceEl = document.getElementById("clip-root")
|
||||||
debouncedUpdate()
|
debouncedUpdate()
|
||||||
interval = setInterval(debouncedUpdate, 100)
|
interval = setInterval(debouncedUpdate, 100)
|
||||||
document.addEventListener("scroll", debouncedUpdate, true)
|
document.addEventListener("scroll", debouncedUpdate, true)
|
||||||
|
@ -172,7 +186,7 @@
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
clearInterval(interval)
|
clearInterval(interval)
|
||||||
document.removeEventListener("scroll", debouncedUpdate, true)
|
document.removeEventListener("scroll", debouncedUpdate, true)
|
||||||
observer?.disconnect()
|
reset()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -190,7 +204,7 @@
|
||||||
icon="AlignLeft"
|
icon="AlignLeft"
|
||||||
title="Align left"
|
title="Align left"
|
||||||
active={gridHAlign === "start"}
|
active={gridHAlign === "start"}
|
||||||
{componentId}
|
componentId={id}
|
||||||
/>
|
/>
|
||||||
<GridStylesButton
|
<GridStylesButton
|
||||||
style={gridHAlignVar}
|
style={gridHAlignVar}
|
||||||
|
@ -198,7 +212,7 @@
|
||||||
icon="AlignCenter"
|
icon="AlignCenter"
|
||||||
title="Align center"
|
title="Align center"
|
||||||
active={gridHAlign === "center"}
|
active={gridHAlign === "center"}
|
||||||
{componentId}
|
componentId={id}
|
||||||
/>
|
/>
|
||||||
<GridStylesButton
|
<GridStylesButton
|
||||||
style={gridHAlignVar}
|
style={gridHAlignVar}
|
||||||
|
@ -206,7 +220,7 @@
|
||||||
icon="AlignRight"
|
icon="AlignRight"
|
||||||
title="Align right"
|
title="Align right"
|
||||||
active={gridHAlign === "end"}
|
active={gridHAlign === "end"}
|
||||||
{componentId}
|
componentId={id}
|
||||||
/>
|
/>
|
||||||
<GridStylesButton
|
<GridStylesButton
|
||||||
style={gridHAlignVar}
|
style={gridHAlignVar}
|
||||||
|
@ -214,7 +228,7 @@
|
||||||
icon="MoveLeftRight"
|
icon="MoveLeftRight"
|
||||||
title="Stretch horizontally"
|
title="Stretch horizontally"
|
||||||
active={gridHAlign === "stretch"}
|
active={gridHAlign === "stretch"}
|
||||||
{componentId}
|
componentId={id}
|
||||||
/>
|
/>
|
||||||
<div class="divider" />
|
<div class="divider" />
|
||||||
<GridStylesButton
|
<GridStylesButton
|
||||||
|
@ -223,7 +237,7 @@
|
||||||
icon="AlignTop"
|
icon="AlignTop"
|
||||||
title="Align top"
|
title="Align top"
|
||||||
active={gridVAlign === "start"}
|
active={gridVAlign === "start"}
|
||||||
{componentId}
|
componentId={id}
|
||||||
/>
|
/>
|
||||||
<GridStylesButton
|
<GridStylesButton
|
||||||
style={gridVAlignVar}
|
style={gridVAlignVar}
|
||||||
|
@ -231,7 +245,7 @@
|
||||||
icon="AlignMiddle"
|
icon="AlignMiddle"
|
||||||
title="Align middle"
|
title="Align middle"
|
||||||
active={gridVAlign === "center"}
|
active={gridVAlign === "center"}
|
||||||
{componentId}
|
componentId={id}
|
||||||
/>
|
/>
|
||||||
<GridStylesButton
|
<GridStylesButton
|
||||||
style={gridVAlignVar}
|
style={gridVAlignVar}
|
||||||
|
@ -239,7 +253,7 @@
|
||||||
icon="AlignBottom"
|
icon="AlignBottom"
|
||||||
title="Align bottom"
|
title="Align bottom"
|
||||||
active={gridVAlign === "end"}
|
active={gridVAlign === "end"}
|
||||||
{componentId}
|
componentId={id}
|
||||||
/>
|
/>
|
||||||
<GridStylesButton
|
<GridStylesButton
|
||||||
style={gridVAlignVar}
|
style={gridVAlignVar}
|
||||||
|
@ -247,7 +261,7 @@
|
||||||
icon="MoveUpDown"
|
icon="MoveUpDown"
|
||||||
title="Stretch vertically"
|
title="Stretch vertically"
|
||||||
active={gridVAlign === "stretch"}
|
active={gridVAlign === "stretch"}
|
||||||
{componentId}
|
componentId={id}
|
||||||
/>
|
/>
|
||||||
<div class="divider" />
|
<div class="divider" />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
Loading…
Reference in New Issue