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