Improve client indicators by properly caching all properties to avoid inconsistent and stale states

This commit is contained in:
Andrew Kingston 2024-03-21 13:22:03 +00:00
parent 7d31536014
commit 07f8e1981a
5 changed files with 60 additions and 58 deletions

View File

@ -345,8 +345,7 @@
<IndicatorSet <IndicatorSet
componentId={$dndParent} componentId={$dndParent}
color="var(--spectrum-global-color-static-green-500)" color="var(--spectrum-global-color-static-green-500)"
zIndex="930" zIndex={920}
transition
prefix="Inside" prefix="Inside"
/> />

View File

@ -1,9 +1,11 @@
<script> <script>
import { onMount, onDestroy } from "svelte" import { onMount, onDestroy } from "svelte"
import IndicatorSet from "./IndicatorSet.svelte" import IndicatorSet from "./IndicatorSet.svelte"
import { dndIsDragging, hoverStore } from "stores" import { dndIsDragging, hoverStore, builderStore } from "stores"
$: componentId = $hoverStore.hoveredComponentId $: componentId = $hoverStore.hoveredComponentId
$: selectedComponentId = $builderStore.selectedComponentId
$: selected = componentId === selectedComponentId
const onMouseOver = e => { const onMouseOver = e => {
// Ignore if dragging // Ignore if dragging
@ -44,7 +46,6 @@
<IndicatorSet <IndicatorSet
componentId={$dndIsDragging ? null : componentId} componentId={$dndIsDragging ? null : componentId}
color="var(--spectrum-global-color-static-blue-200)" color="var(--spectrum-global-color-static-blue-200)"
transition zIndex={selected ? 890 : 910}
zIndex="890"
allowResizeAnchors allowResizeAnchors
/> />

View File

@ -1,5 +1,4 @@
<script> <script>
import { fade } from "svelte/transition"
import { Icon } from "@budibase/bbui" import { Icon } from "@budibase/bbui"
export let top export let top
@ -11,7 +10,6 @@
export let color export let color
export let zIndex export let zIndex
export let componentId export let componentId
export let transition = false
export let line = false export let line = false
export let alignRight = false export let alignRight = false
export let showResizeAnchors = false export let showResizeAnchors = false
@ -31,10 +29,6 @@
</script> </script>
<div <div
transition:fade|local={{
delay: transition ? 100 : 0,
duration: transition ? 100 : 0,
}}
class="indicator" class="indicator"
class:flipped class:flipped
class:line class:line

View File

@ -4,28 +4,39 @@
import { domDebounce } from "utils/domDebounce" import { domDebounce } from "utils/domDebounce"
import { builderStore } from "stores" import { builderStore } from "stores"
export let componentId export let componentId = null
export let color export let color = null
export let transition export let zIndex = 900
export let zIndex
export let prefix = null export let prefix = null
export let allowResizeAnchors = false export let allowResizeAnchors = false
let indicators = [] const errorColor = "var(--spectrum-global-color-static-red-600)"
const defaultState = () => ({
// Cached props
componentId,
color,
zIndex,
prefix,
allowResizeAnchors,
// Computed state
indicators: [],
text: null,
icon: null,
insideGrid: false,
error: false,
})
let interval let interval
let text let state = defaultState()
let icon let nextState = null
let insideGrid = false
let errorState = false
$: visibleIndicators = indicators.filter(x => x.visible)
$: offset = $builderStore.inBuilder ? 0 : 2
$: componentId, debouncedUpdate()
let updating = false let updating = false
let observers = [] let observers = []
let callbackCount = 0 let callbackCount = 0
let nextIndicators = []
$: visibleIndicators = state.indicators.filter(x => x.visible)
$: offset = $builderStore.inBuilder ? 0 : 2
$: $$props, debouncedUpdate()
const checkInsideGrid = id => { const checkInsideGrid = id => {
const component = document.getElementsByClassName(id)[0] const component = document.getElementsByClassName(id)[0]
@ -45,10 +56,10 @@
if (callbackCount >= observers.length) { if (callbackCount >= observers.length) {
return return
} }
nextIndicators[idx].visible = nextState.indicators[idx].visible =
nextIndicators[idx].insideSidePanel || entries[0].isIntersecting nextState.indicators[idx].insideSidePanel || entries[0].isIntersecting
if (++callbackCount === observers.length) { if (++callbackCount === observers.length) {
indicators = nextIndicators state = nextState
updating = false updating = false
} }
} }
@ -60,7 +71,7 @@
// Sanity check // Sanity check
if (!componentId) { if (!componentId) {
indicators = [] state = defaultState()
return return
} }
@ -69,25 +80,25 @@
callbackCount = 0 callbackCount = 0
observers.forEach(o => o.disconnect()) observers.forEach(o => o.disconnect())
observers = [] observers = []
nextIndicators = [] nextState = defaultState()
// Check if we're inside a grid // Check if we're inside a grid
if (allowResizeAnchors) { if (allowResizeAnchors) {
insideGrid = checkInsideGrid(componentId) nextState.insideGrid = checkInsideGrid(componentId)
} }
// Determine next set of indicators // Determine next set of indicators
const parents = document.getElementsByClassName(componentId) const parents = document.getElementsByClassName(componentId)
if (parents.length) { if (parents.length) {
text = parents[0].dataset.name nextState.text = parents[0].dataset.name
if (prefix) { if (nextState.prefix) {
text = `${prefix} ${text}` nextState.text = `${nextState.prefix} ${nextState.text}`
} }
if (parents[0].dataset.icon) { if (parents[0].dataset.icon) {
icon = parents[0].dataset.icon nextState.icon = parents[0].dataset.icon
} }
} }
errorState = parents?.[0]?.classList.contains("error") nextState.error = parents?.[0]?.classList.contains("error")
// Batch reads to minimize reflow // Batch reads to minimize reflow
const scrollX = window.scrollX const scrollX = window.scrollX
@ -103,8 +114,9 @@
// If there aren't any nodes then reset // If there aren't any nodes then reset
if (!children.length) { if (!children.length) {
indicators = [] state = defaultState()
updating = false updating = false
return
} }
const device = document.getElementById("app-root") const device = document.getElementById("app-root")
@ -120,7 +132,7 @@
observers.push(observer) observers.push(observer)
const elBounds = child.getBoundingClientRect() const elBounds = child.getBoundingClientRect()
nextIndicators.push({ nextState.indicators.push({
top: elBounds.top + scrollY - deviceBounds.top - offset, top: elBounds.top + scrollY - deviceBounds.top - offset,
left: elBounds.left + scrollX - deviceBounds.left - offset, left: elBounds.left + scrollX - deviceBounds.left - offset,
width: elBounds.width + 4, width: elBounds.width + 4,
@ -145,20 +157,17 @@
}) })
</script> </script>
{#key componentId} {#each visibleIndicators as indicator, idx}
{#each visibleIndicators as indicator, idx} <Indicator
<Indicator top={indicator.top}
top={indicator.top} left={indicator.left}
left={indicator.left} width={indicator.width}
width={indicator.width} height={indicator.height}
height={indicator.height} text={idx === 0 ? state.text : null}
text={idx === 0 ? text : null} icon={idx === 0 ? state.icon : null}
icon={idx === 0 ? icon : null} showResizeAnchors={state.allowResizeAnchors && state.insideGrid}
showResizeAnchors={allowResizeAnchors && insideGrid} color={state.error ? errorColor : state.color}
color={errorState ? "var(--spectrum-global-color-static-red-600)" : color} componentId={state.componentId}
{componentId} zIndex={state.zIndex}
{transition} />
{zIndex} {/each}
/>
{/each}
{/key}

View File

@ -10,7 +10,6 @@
<IndicatorSet <IndicatorSet
componentId={$builderStore.selectedComponentId} componentId={$builderStore.selectedComponentId}
{color} {color}
zIndex="910" zIndex={900}
transition
allowResizeAnchors allowResizeAnchors
/> />