mirror of https://github.com/Budibase/budibase.git
9 changed files with 217 additions and 44 deletions
@ -0,0 +1,74 @@ |
|||
<script> |
|||
import { onMount, onDestroy } from "svelte" |
|||
import { builderStore } from "../store" |
|||
import Indicator from "./Indicator.svelte" |
|||
|
|||
const offset = 2 |
|||
let indicators = [] |
|||
let interval |
|||
let componentId |
|||
let componentName |
|||
|
|||
const updatePosition = () => { |
|||
let newIndicators = [] |
|||
|
|||
if (componentId) { |
|||
const parents = document.getElementsByClassName(componentId) |
|||
|
|||
// Batch reads to minimize reflow |
|||
const scrollX = window.scrollX |
|||
const scrollY = window.scrollY |
|||
|
|||
for (let i = 0; i < parents.length; i++) { |
|||
const child = parents[i]?.childNodes?.[0] |
|||
if (child) { |
|||
const elBounds = child.getBoundingClientRect() |
|||
newIndicators.push({ |
|||
top: elBounds.top + scrollY - offset * 2, |
|||
left: elBounds.left + scrollX - offset * 2, |
|||
width: elBounds.width + offset * 2, |
|||
height: elBounds.height + offset * 2, |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
|
|||
indicators = newIndicators |
|||
} |
|||
|
|||
const onMouseOver = e => { |
|||
const element = e.target.closest("[data-type='component']") |
|||
componentId = element?.dataset?.id |
|||
componentName = element?.dataset?.name |
|||
} |
|||
|
|||
const onMouseLeave = () => { |
|||
componentId = null |
|||
componentName = null |
|||
} |
|||
|
|||
onMount(() => { |
|||
interval = setInterval(updatePosition, 100) |
|||
window.addEventListener("mouseover", onMouseOver) |
|||
document.documentElement.addEventListener("mouseleave", onMouseLeave) |
|||
}) |
|||
|
|||
onDestroy(() => { |
|||
clearInterval(interval) |
|||
window.removeEventListener("mouseover", onMouseOver) |
|||
document.documentElement.removeEventListener("mouseleave", onMouseLeave) |
|||
}) |
|||
</script> |
|||
|
|||
{#if componentId !== $builderStore.selectedComponentId} |
|||
{#each indicators as indicator, idx} |
|||
<Indicator |
|||
top={indicator.top} |
|||
left={indicator.left} |
|||
width={indicator.width} |
|||
height={indicator.height} |
|||
text={idx === 0 ? componentName : null} |
|||
color="rgb(120, 170, 244)" |
|||
/> |
|||
{/each} |
|||
{/if} |
|||
@ -0,0 +1,53 @@ |
|||
<script> |
|||
export let top |
|||
export let left |
|||
export let width |
|||
export let height |
|||
export let text |
|||
export let color |
|||
</script> |
|||
|
|||
<div |
|||
class="indicator" |
|||
style="top: {top}px; left: {left}px; width: {width}px; height: {height}px; --color: {color};" |
|||
> |
|||
{#if text} |
|||
<div class="text" class:flipped={top < 22}> |
|||
{text} |
|||
</div> |
|||
{/if} |
|||
</div> |
|||
|
|||
<style> |
|||
.indicator { |
|||
position: absolute; |
|||
z-index: 910; |
|||
border: 2px solid var(--color); |
|||
pointer-events: none; |
|||
border-radius: 4px; |
|||
} |
|||
.text { |
|||
background-color: var(--color); |
|||
color: white; |
|||
position: absolute; |
|||
top: 0; |
|||
left: -2px; |
|||
height: 20px; |
|||
padding: 0 8px 2px 8px; |
|||
transform: translateY(-100%); |
|||
font-size: 11px; |
|||
border-top-left-radius: 2px; |
|||
border-top-right-radius: 2px; |
|||
border-bottom-right-radius: 2px; |
|||
white-space: nowrap; |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: flex-start; |
|||
align-items: center; |
|||
} |
|||
.flipped { |
|||
border-top-left-radius: 0; |
|||
transform: translateY(0%); |
|||
top: -2px; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,52 @@ |
|||
<script> |
|||
import { onMount, onDestroy } from "svelte" |
|||
import { builderStore } from "../store" |
|||
import Indicator from "./Indicator.svelte" |
|||
|
|||
const offset = 2 |
|||
let indicators = [] |
|||
let interval |
|||
|
|||
const updatePosition = () => { |
|||
const id = $builderStore.selectedComponentId |
|||
const parents = document.getElementsByClassName(id) |
|||
|
|||
// Batch reads to minimize reflow |
|||
const scrollX = window.scrollX |
|||
const scrollY = window.scrollY |
|||
|
|||
let newIndicators = [] |
|||
for (let i = 0; i < parents.length; i++) { |
|||
const child = parents[i]?.childNodes?.[0] |
|||
if (child) { |
|||
const elBounds = child.getBoundingClientRect() |
|||
newIndicators.push({ |
|||
top: elBounds.top + scrollY - offset * 2, |
|||
left: elBounds.left + scrollX - offset * 2, |
|||
width: elBounds.width + offset * 2, |
|||
height: elBounds.height + offset * 2, |
|||
}) |
|||
} |
|||
} |
|||
indicators = newIndicators |
|||
} |
|||
|
|||
onMount(() => { |
|||
interval = setInterval(updatePosition, 100) |
|||
}) |
|||
|
|||
onDestroy(() => { |
|||
clearInterval(interval) |
|||
}) |
|||
</script> |
|||
|
|||
{#each indicators as indicator, idx} |
|||
<Indicator |
|||
top={indicator.top} |
|||
left={indicator.left} |
|||
width={indicator.width} |
|||
height={indicator.height} |
|||
text={idx === 0 ? $builderStore.selectedComponent._instanceName : null} |
|||
color="rgb(66, 133, 244)" |
|||
/> |
|||
{/each} |
|||
Loading…
Reference in new issue