Increase DND performance via minimzing component updates

This commit is contained in:
Andrew Kingston 2025-02-06 16:01:45 +00:00
parent 8ff2faaa24
commit 61940705b9
No known key found for this signature in database
7 changed files with 24 additions and 124 deletions

View File

@ -261,8 +261,7 @@
class="component"
class:selected={selectedIndex === orderMap[component.component]}
on:click={() => addComponent(component.component)}
on:mouseover={() => (selectedIndex = null)}
on:focus
on:mouseenter={() => (selectedIndex = null)}
>
<Icon name={component.icon} />
<Body size="XS">{component.name}</Body>

View File

@ -25,6 +25,7 @@
$: source = $dndStore.source
$: target = $dndStore.target
$: drop = $dndStore.drop
$: gridScreen = $isGridScreen
// Local flag for whether we are awaiting an async drop event
let dropping = false
@ -235,7 +236,7 @@
// Callback when on top of a component
const onDragOver = (e: DragEvent) => {
if (!source || !target || $isGridScreen) {
if (!source || !target || gridScreen) {
return
}
handleEvent(e)
@ -243,7 +244,7 @@
// Callback when entering a potential drop target
const onDragEnter = async (e: DragEvent) => {
if (!source || $isGridScreen || !(e.target instanceof HTMLElement)) {
if (!source || gridScreen || !(e.target instanceof HTMLElement)) {
return
}
@ -278,7 +279,7 @@
source.type,
drop.parent,
drop.index,
$dndStore.source?.props
$dndStore.meta?.props
)
dropping = false
stopDragging()

View File

@ -1,108 +0,0 @@
<!--<script>-->
<!-- import { onMount, tick } from "svelte"-->
<!-- import { Utils } from "@budibase/frontend-core"-->
<!-- import { componentStore, dndNewComponentType, isGridScreen } from "@/stores"-->
<!-- import { DNDPlaceholderID } from "@/constants"-->
<!-- import IndicatorSet from "@/components/preview/IndicatorSet.svelte"-->
<!-- let left, top, height, width-->
<!-- let observing = false-->
<!-- let hasGridStyles = false-->
<!-- // On grid screens, we need to wait for grid styles to be properly set on-->
<!-- // the hidden placeholder component before rendering this overlay-->
<!-- $: waitingForGrid = $isGridScreen && !hasGridStyles-->
<!-- $: instance = componentStore.actions.getComponentInstance(DNDPlaceholderID)-->
<!-- $: state = $instance?.state-->
<!-- $: styles = $state?.styles?.normal || {}-->
<!-- $: {-->
<!-- if ($isGridScreen && !hasGridStyles) {-->
<!-- checkGridStyles(styles)-->
<!-- }-->
<!-- }-->
<!-- // We pull the component name from the definition-->
<!-- $: definition =-->
<!-- componentStore.actions.getComponentDefinition($dndNewComponentType)-->
<!-- // Wait for grid styles to be set, then tick and await a position update-->
<!-- // before finally signalling we're allowed to render-->
<!-- const checkGridStyles = async styles => {-->
<!-- const hasStyles = Object.keys(styles).some(key => key.startsWith("&#45;&#45;grid"))-->
<!-- if (hasStyles) {-->
<!-- await tick()-->
<!-- updatePosition()-->
<!-- hasGridStyles = true-->
<!-- }-->
<!-- }-->
<!-- // Observe style changes in the placeholder DOM node and use this to trigger-->
<!-- // a redraw of our overlay-->
<!-- const observer = new MutationObserver(mutations => {-->
<!-- if (mutations.some(mutation => mutation.attributeName === "style")) {-->
<!-- debouncedUpdate()-->
<!-- }-->
<!-- })-->
<!-- const updatePosition = () => {-->
<!-- const wrapperNode = document.getElementsByClassName(DNDPlaceholderID)[0]-->
<!-- let domNode = wrapperNode-->
<!-- const insideGrid = wrapperNode?.dataset.insideGrid === "true"-->
<!-- if (!insideGrid) {-->
<!-- domNode = document.getElementsByClassName(`${DNDPlaceholderID}-dom`)[0]-->
<!-- }-->
<!-- if (!domNode) {-->
<!-- height = 0-->
<!-- width = 0-->
<!-- } else {-->
<!-- const bounds = domNode.getBoundingClientRect()-->
<!-- left = bounds.left-->
<!-- top = bounds.top-->
<!-- height = bounds.height-->
<!-- width = bounds.width-->
<!-- }-->
<!-- // Initialise observer if not already done-->
<!-- if (!observing && wrapperNode) {-->
<!-- observing = true-->
<!-- observer.observe(wrapperNode, { attributes: true })-->
<!-- }-->
<!-- }-->
<!-- const debouncedUpdate = Utils.domDebounce(updatePosition)-->
<!-- onMount(() => {-->
<!-- const interval = setInterval(debouncedUpdate, 100)-->
<!-- return () => {-->
<!-- observer.disconnect()-->
<!-- clearInterval(interval)-->
<!-- }-->
<!-- })-->
<!--</script>-->
<!--&lt;!&ndash;{#if left != null && top != null && width && height && !waitingForGrid}&ndash;&gt;-->
<!--&lt;!&ndash; <div&ndash;&gt;-->
<!--&lt;!&ndash; class="overlay"&ndash;&gt;-->
<!--&lt;!&ndash; class:animate={!$isGridScreen}&ndash;&gt;-->
<!--&lt;!&ndash; style="left:{left}px; top:{top}px; width:{width}px; height:{height}px;"&ndash;&gt;-->
<!--&lt;!&ndash; >&ndash;&gt;-->
<!--&lt;!&ndash; {definition?.name || ""}&ndash;&gt;-->
<!--&lt;!&ndash; </div>&ndash;&gt;-->
<!--&lt;!&ndash; <IndicatorSet componentId={DNDPlaceholderID} color="red" />&ndash;&gt;-->
<!--&lt;!&ndash;{/if}&ndash;&gt;-->
<!--<style>-->
<!-- .overlay {-->
<!-- position: fixed;-->
<!-- z-index: 800;-->
<!-- background: hsl(160, 64%, 90%);-->
<!-- border-radius: 4px;-->
<!-- border: 2px solid var(&#45;&#45;spectrum-global-color-static-green-500);-->
<!-- display: grid;-->
<!-- place-items: center;-->
<!-- color: hsl(160, 64%, 40%);-->
<!-- font-size: 14px;-->
<!-- }-->
<!-- .overlay.animate {-->
<!-- transition: all 130ms ease-out;-->
<!-- }-->
<!--</style>-->

View File

@ -1,12 +1,18 @@
<script lang="ts">
import { isGridScreen, dndParent, dndSource, dndIsDragging } from "@/stores"
import {
isGridScreen,
dndParent,
dndSource,
dndIsDragging,
dndStore,
} from "@/stores"
import { DNDPlaceholderID } from "@/constants"
import IndicatorSet from "./IndicatorSet.svelte"
// On grid screens, don't draw the indicator until we've dragged over the
// screen. When this happens, the dndSource props will be set as we will have
// attached grid metadata styles.
$: waitingForGrid = $isGridScreen && !$dndSource?.props
$: waitingForGrid = $isGridScreen && !$dndStore.meta?.props
</script>
{#if $dndIsDragging}

View File

@ -85,7 +85,7 @@
// real component to render in the new position before updating the DND
// store, preventing the green DND overlay from being out of position
if ($dndSource?.isNew && styles) {
dndStore.actions.updateSourceProps({
dndStore.actions.updateNewComponentProps({
_styles: {
normal: styles,
},

View File

@ -93,6 +93,7 @@
const observeMutations = (node: Node) => {
mutationObserver.observe(node, {
attributes: true,
attributeFilter: ["style"],
})
observingMutations = true
}

View File

@ -16,7 +16,6 @@ interface DNDSource {
icon?: string
type: string
isNew: boolean
props?: Record<string, any>
}
interface DNDTarget {
@ -32,10 +31,15 @@ interface DNDDrop {
index: number
}
interface DNDMeta {
props?: Record<string, any>
}
interface DNDState {
source?: DNDSource
target?: DNDTarget
drop?: DNDDrop
meta?: DNDMeta
}
const createDndStore = () => {
@ -106,15 +110,12 @@ const createDndStore = () => {
store.set({})
}
const updateSourceProps = (props: Record<string, any>) => {
const updateNewComponentProps = (props: Record<string, any>) => {
store.update(state => {
if (!state.source) {
return state
}
return {
...state,
source: {
...state.source,
meta: {
...state.meta,
props,
},
}
@ -129,7 +130,7 @@ const createDndStore = () => {
updateTarget,
updateDrop,
reset,
updateSourceProps,
updateNewComponentProps,
},
}
}