Refactor client app data tags to be classnames and simplify logic

This commit is contained in:
Andrew Kingston 2021-09-20 15:34:51 +01:00
parent 07e0cbb210
commit 488ec4d988
5 changed files with 51 additions and 46 deletions

View File

@ -51,11 +51,11 @@
$: children = instance._children || []
$: id = instance._id
$: name = instance._instanceName
$: empty =
!children.length &&
definition?.hasChildren &&
definition?.showEmptyState !== false &&
$builderStore.inBuilder
$: interactive =
$builderStore.inBuilder &&
($builderStore.previewType === "layout" || insideScreenslot)
$: empty = interactive && !children.length && definition?.hasChildren
$: emptyState = empty && definition?.showEmptyState !== false
$: rawProps = getRawProps(instance)
$: instanceKey = JSON.stringify(rawProps)
$: updateComponentProps(rawProps, instanceKey, $context)
@ -63,9 +63,6 @@
$builderStore.inBuilder &&
$builderStore.selectedComponentId === instance._id
$: inSelectedPath = $builderStore.selectedComponentPath?.includes(id)
$: interactive =
$builderStore.inBuilder &&
($builderStore.previewType === "layout" || insideScreenslot)
$: evaluateConditions(enrichedSettings?._conditions)
$: componentSettings = { ...enrichedSettings, ...conditionalSettings }
@ -73,8 +70,8 @@
$: componentStore.set({
id,
children: children.length,
styles: { ...instance._styles, id, empty, interactive },
empty,
styles: { ...instance._styles, id, empty: emptyState, interactive },
empty: emptyState,
selected,
name,
})
@ -177,38 +174,44 @@
// Drag and drop helper tags
$: draggable = interactive && !isLayout && !isScreen
$: droppable = interactive && !isLayout && !isScreen
$: dropInside = interactive && definition?.hasChildren && !children.length
</script>
{#key propsHash}
{#if constructor && componentSettings && (visible || inSelectedPath)}
<div
class={`component ${id}`}
data-type={interactive ? "component" : "readonly"}
data-id={id}
data-name={name}
data-draggable={draggable}
data-droppable={droppable}
data-droppable-inside={dropInside}
>
<svelte:component this={constructor} {...componentSettings}>
{#if children.length}
{#each children as child (child._id)}
<svelte:self instance={child} />
{/each}
{:else if empty}
<Placeholder />
{/if}
</svelte:component>
</div>
{/if}
{#key empty}
{#if constructor && componentSettings && (visible || inSelectedPath)}
<!-- The ID is used as a class because getElementsByClassName is O(1) -->
<!-- and the performance matters for the selection indicators -->
<div
class={`component ${id}`}
class:draggable
class:droppable
class:empty
class:interactive
data-id={id}
data-name={name}
>
<svelte:component this={constructor} {...componentSettings}>
{#if children.length}
{#each children as child (child._id)}
<svelte:self instance={child} />
{/each}
{:else if emptyState}
<Placeholder />
{/if}
</svelte:component>
</div>
{/if}
{/key}
{/key}
<style>
.component {
display: contents;
}
[data-draggable="true"] :global(*:hover) {
.interactive :global(*:hover) {
cursor: pointer;
}
.draggable :global(*:hover) {
cursor: grab;
}
</style>

View File

@ -34,7 +34,7 @@
display: flex;
max-width: 100%;
}
.valid-container :global([data-type="component"] > *) {
.valid-container :global(.component > *) {
max-width: 100%;
}
.direction-row {
@ -46,7 +46,7 @@
/* Grow containers inside a row need 0 width 0 so that they ignore content */
/* The nested selector for data-type is the wrapper around all components */
.direction-row :global(> [data-type="component"] > .size-grow) {
.direction-row :global(> .component > .size-grow) {
width: 0;
}

View File

@ -9,16 +9,15 @@
let dropInfo
const getDOMNodeForComponent = component => {
const parent = component.closest("[data-type='component']")
const parent = component.closest(".component")
const children = Array.from(parent.childNodes)
return children?.find(node => node?.nodeType === 1)
}
// Callback when initially starting a drag on a draggable component
const onDragStart = e => {
const parent = e.target.closest("[data-type='component']")
const child = getDOMNodeForComponent(e.target)
if (!parent?.dataset?.id || !child) {
const parent = e.target.closest(".component")
if (!parent?.classList.contains("draggable")) {
return
}
@ -31,7 +30,10 @@
builderStore.actions.setDragging(true)
// Highlight being dragged by setting opacity
child.style.opacity = "0.5"
const child = getDOMNodeForComponent(e.target)
if (child) {
child.style.opacity = "0.5"
}
}
// Callback when drag stops (whether dropped or not)
@ -102,10 +104,10 @@
return
}
const element = e.target.closest("[data-type='component']")
const element = e.target.closest(".component")
if (
element &&
element.dataset.droppable === "true" &&
element.classList.contains("droppable") &&
element.dataset.id !== dragInfo.target
) {
// Do nothing if this is the same target
@ -130,7 +132,7 @@
dropInfo = {
target,
name: element.dataset.name,
droppableInside: element.dataset.droppableInside === "true",
droppableInside: element.classList.contains("empty"),
bounds,
}
} else {

View File

@ -7,7 +7,7 @@
$: zIndex = componentId === $builderStore.selectedComponentId ? 900 : 920
const onMouseOver = e => {
const element = e.target.closest("[data-type='component']")
const element = e.target.closest(".interactive.component")
const newId = element?.dataset?.id
if (newId !== componentId) {
componentId = newId

View File

@ -24,8 +24,8 @@ export const styleable = (node, styles = {}) => {
let selectComponent
// Allow dragging if required
const parent = node.closest("[data-type='component']")
if (parent && parent.dataset.draggable === "true") {
const parent = node.closest(".component")
if (parent && parent.classList.contains("draggable")) {
node.setAttribute("draggable", true)
}