diff --git a/app/components/selection/handles.element.js b/app/components/selection/handles.element.js index eefa74f1..212ea4de 100644 --- a/app/components/selection/handles.element.js +++ b/app/components/selection/handles.element.js @@ -33,7 +33,7 @@ export class Handles extends HTMLElement { } set position({el, node_label_id}) { - this.$shadow.innerHTML = this.render(el.getBoundingClientRect(), node_label_id) + this.$shadow.innerHTML = this.render(el['vis-box'], node_label_id) if (this._backdrop) { this.backdrop = { diff --git a/app/components/selection/label.element.js b/app/components/selection/label.element.js index 31fdc6ed..51e80c74 100644 --- a/app/components/selection/label.element.js +++ b/app/components/selection/label.element.js @@ -28,7 +28,7 @@ export class Label extends HTMLElement { this.position = { node_label_id, - boundingRect: source_el.getBoundingClientRect(), + boundingRect: source_el['vis-box'], } }) } diff --git a/app/features/guides.js b/app/features/guides.js index 4431d9cf..8c1a8e03 100644 --- a/app/features/guides.js +++ b/app/features/guides.js @@ -1,5 +1,5 @@ import $ from 'blingblingjs' -import { isOffBounds, deepElementFromPoint } from '../utilities/' +import { isOffBounds, deepElementFromPoint, setVisbox } from '../utilities/' import { clearMeasurements, takeMeasurementOwnership } from './measurements' const state = { @@ -30,9 +30,10 @@ export function Guides(visbug) { } } -const on_hover = e => { +const on_hover = async e => { const target = deepElementFromPoint(e.clientX, e.clientY) if (isOffBounds(target)) return + await setVisbox([target]) showGridlines(target) } @@ -91,11 +92,11 @@ const on_hoverout = () => const showGridlines = node => { if (state.gridlines) { state.gridlines.style.display = null - state.gridlines.update = node.getBoundingClientRect() + state.gridlines.update = node['vis-box'] } else { state.gridlines = document.createElement('visbug-gridlines') - state.gridlines.position = node.getBoundingClientRect() + state.gridlines.position = node['vis-box'] document.body.appendChild(state.gridlines) } diff --git a/app/features/imageswap.js b/app/features/imageswap.js index 5fd1e099..ce171c01 100644 --- a/app/features/imageswap.js +++ b/app/features/imageswap.js @@ -178,7 +178,7 @@ const getPictureSourcesToUpdate = img => const showOverlay = (node, i) => { if (!state.watching) return - const rect = node.getBoundingClientRect() + const rect = node['vis-box'] const overlay = overlays[i] if (overlay) { diff --git a/app/features/margin.js b/app/features/margin.js index d52b5e2d..1f4ca44a 100644 --- a/app/features/margin.js +++ b/app/features/margin.js @@ -89,7 +89,7 @@ function removeBackgrounds(els) { } export function createMarginVisual(el, hover = false) { - const bounds = el.getBoundingClientRect() + const bounds = el['vis-box'] const styleOM = el.computedStyleMap() const calculatedStyle = getStyle(el, 'margin') const boxdisplay = document.createElement('visbug-boxmodel') diff --git a/app/features/measurements.js b/app/features/measurements.js index 1c39e4c8..1845d7eb 100644 --- a/app/features/measurements.js +++ b/app/features/measurements.js @@ -11,8 +11,8 @@ export function createMeasurements({$anchor, $target}) { if (state.distances.length) clearMeasurements() - const anchorBounds = $anchor.getBoundingClientRect() - const targetBounds = $target.getBoundingClientRect() + const anchorBounds = $anchor['vis-box'] + const targetBounds = $target['vis-box'] const measurements = [] diff --git a/app/features/metatip.js b/app/features/metatip.js index 758f75b6..380b5ef9 100644 --- a/app/features/metatip.js +++ b/app/features/metatip.js @@ -4,7 +4,7 @@ import { TinyColor } from '@ctrl/tinycolor' import { queryPage } from './search' import { getStyles, camelToDash, isOffBounds, deepElementFromPoint, getShadowValues, - getTextShadowValues + getTextShadowValues, setVisbox } from '../utilities/' const state = { @@ -35,7 +35,7 @@ export function MetaTip({select}) { } } -const mouseMove = e => { +const mouseMove = async e => { const target = deepElementFromPoint(e.clientX, e.clientY) if (isOffBounds(target) || target.nodeName === 'VISBUG-METATIP' || target.hasAttribute('data-metatip')) { // aka: mouse out @@ -50,7 +50,7 @@ const mouseMove = e => { } toggleTargetCursor(e.altKey, target) - + await setVisbox([target]) showTip(target, e) } @@ -130,7 +130,7 @@ export function removeAll() { } const render = (el, tip = document.createElement('visbug-metatip')) => { - const { width, height } = el.getBoundingClientRect() + const { width, height } = el['vis-box'] const colormode = $('vis-bug')[0].colorMode const styles = getStyles(el) @@ -238,19 +238,17 @@ const togglePinned = e => { const linkQueryClicked = ({detail:{ text, activator }}) => { if (!text) return - queryPage('[data-pseudo-select]', el => - el.removeAttribute('data-pseudo-select')) + unPseudoQuery() - queryPage(text + ':not([data-selected])', el => + queryPage(text + ':not([data-selected])', els => activator === 'mouseenter' - ? el.setAttribute('data-pseudo-select', true) - : services.selectors.select(el)) + ? $(els).attr('data-pseudo-select', true) + : services.selectors.select(els)) } -const linkQueryHoverOut = e => { - queryPage('[data-pseudo-select]', el => - el.removeAttribute('data-pseudo-select')) -} +const unPseudoQuery = _ => + queryPage('[data-pseudo-select]', els => + $(els).attr('data-pseudo-select', null)) const toggleTargetCursor = (key, target) => key @@ -259,13 +257,13 @@ const toggleTargetCursor = (key, target) => const observe = ({tip, target}) => { $(tip).on('query', linkQueryClicked) - $(tip).on('unquery', linkQueryHoverOut) + $(tip).on('unquery', unPseudoQuery) $(target).on('DOMNodeRemoved', handleBlur) } const unobserve = ({tip, target}) => { $(tip).off('query', linkQueryClicked) - $(tip).off('unquery', linkQueryHoverOut) + $(tip).off('unquery', unPseudoQuery) $(target).off('DOMNodeRemoved', handleBlur) } diff --git a/app/features/padding.js b/app/features/padding.js index 2c5900b6..ab6d9b53 100644 --- a/app/features/padding.js +++ b/app/features/padding.js @@ -89,7 +89,7 @@ function removeBackgrounds(els) { } export function createPaddingVisual(el, hover = false) { - const bounds = el.getBoundingClientRect() + const bounds = el['vis-box'] const styleOM = el.computedStyleMap() const calculatedStyle = getStyle(el, 'padding') const boxdisplay = document.createElement('visbug-boxmodel') diff --git a/app/features/search.js b/app/features/search.js index c60b964a..96633b0a 100644 --- a/app/features/search.js +++ b/app/features/search.js @@ -91,11 +91,10 @@ export function queryPage(query, fn) { try { let matches = querySelectorAllDeep(query + notList) if (!matches.length) matches = querySelectorAllDeep(query) - if (matches.length) { - matches.forEach(el => - fn - ? fn(el) - : SelectorEngine.select(el)) + + else if (matches.length) { + if (fn) fn(matches) + else SelectorEngine.select(matches) } } catch (err) {} diff --git a/app/features/selectable.js b/app/features/selectable.js index 387ed4cd..fed10aa9 100644 --- a/app/features/selectable.js +++ b/app/features/selectable.js @@ -16,7 +16,7 @@ import { metaKey, htmlStringToDom, createClassname, camelToDash, isOffBounds, getStyles, deepElementFromPoint, getShadowValues, isSelectorValid, findNearestChildElement, findNearestParentElement, - getTextShadowValues + getTextShadowValues, setVisbox } from '../utilities/' export function Selectable(visbug) { @@ -385,7 +385,7 @@ export function Selectable(visbug) { }) } - const on_hover = e => { + const on_hover = async e => { const $target = deepElementFromPoint(e.clientX, e.clientY) const tool = visbug.activeTool @@ -394,11 +394,19 @@ export function Selectable(visbug) { return clearHover() } + await setVisbox([$target]) + + const gui = document.createDocumentFragment() overlayHoverUI({ el: $target, // no_hover: tool === 'guides', no_label: tool !== 'guides', }) + + gui.append(hover_state.element) + gui.append(hover_state.label) + + document.body.append(gui) if (tool === 'guides' && selected.length >= 1 && !selected.includes($target)) { $target.setAttribute('data-measuring', true) @@ -418,22 +426,37 @@ export function Selectable(visbug) { } } - const select = el => { - const id = handles.length - const tool = visbug.activeTool - - el.setAttribute('data-selected', true) - el.setAttribute('data-label-id', id) + const select = async els => { + if (!els.length) + els = [els] clearHover() + await setVisbox(els) + + // for each item to be selected + // set visbug attributes and create gui overlay nodes + // reduce those nodes into 1 root fragment for one DOM append / incision + const gui = els.reduce((gui, el) => { + const id = handles.length + const tool = visbug.activeTool + + el.setAttribute('data-selected', true) + el.setAttribute('data-label-id', id) + + const nodes = overlayMetaUI({ + el, + id, + no_label: tool !== 'inspector' && tool !== 'accessibility', + }) - overlayMetaUI({ - el, - id, - no_label: tool !== 'inspector' && tool !== 'accessibility', - }) + nodes.forEach(node => gui.append(node)) + + return gui + }, document.createDocumentFragment()) - selected.unshift(el) + document.body.append(gui) + + selected = [...selected, ...els] tellWatchers() } @@ -552,8 +575,8 @@ export function Selectable(visbug) { } const overlayMetaUI = ({el, id, no_label = true}) => { - let handle = createHandle({el, id}) - let label = no_label + const handle = createHandle({el, id}) + const label = no_label ? null : createLabel({ el, @@ -571,8 +594,8 @@ export function Selectable(visbug) { ` }) - let observer = createObserver(el, {handle,label}) - let parentObserver = createObserver(el, {handle,label}) + const observer = createObserver(el, {handle,label}) + const parentObserver = createObserver(el, {handle,label}) observer.observe(el, { attributes: true }) parentObserver.observe(el.parentNode, { childList:true, subtree:true }) @@ -581,10 +604,12 @@ export function Selectable(visbug) { observer.disconnect() parentObserver.disconnect() }) + + return [handle, label] } const setLabel = (el, label) => - label.update = el.getBoundingClientRect() + label.update = el['vis-box'] const createLabel = ({el, id, template}) => { if (!labels[id]) { @@ -592,30 +617,28 @@ export function Selectable(visbug) { label.text = template label.position = { - boundingRect: el.getBoundingClientRect(), + boundingRect: el['vis-box'], node_label_id: id, } - document.body.appendChild(label) - $(label).on('query', ({detail}) => { if (!detail.text) return this.query_text = detail.text - queryPage('[data-pseudo-select]', el => - el.removeAttribute('data-pseudo-select')) + queryPage('[data-pseudo-select]', els => + $(els).attr('data-pseudo-select', null)) - queryPage(this.query_text + ':not([data-selected])', el => + queryPage(this.query_text + ':not([data-selected])', els => detail.activator === 'mouseenter' - ? el.setAttribute('data-pseudo-select', true) - : select(el)) + ? $(els).attr('data-pseudo-select', true) + : select(els)) }) $(label).on('mouseleave', e => { e.preventDefault() e.stopPropagation() - queryPage('[data-pseudo-select]', el => - el.removeAttribute('data-pseudo-select')) + queryPage('[data-pseudo-select]', els => + $(els).attr('data-pseudo-select', null)) }) labels[labels.length] = label @@ -629,10 +652,8 @@ export function Selectable(visbug) { const handle = document.createElement('visbug-handles') handle.position = { el, node_label_id: id } - - document.body.appendChild(handle) - handles[handles.length] = handle + return handle } } @@ -643,7 +664,6 @@ export function Selectable(visbug) { hover_state.element.remove() hover_state.element = document.createElement('visbug-hover') - document.body.appendChild(hover_state.element) hover_state.element.position = {el} return hover_state.element @@ -656,17 +676,15 @@ export function Selectable(visbug) { hover_state.label.remove() hover_state.label = document.createElement('visbug-label') - document.body.appendChild(hover_state.label) hover_state.label.text = text hover_state.label.position = { - boundingRect: el.getBoundingClientRect(), + boundingRect: el['vis-box'], node_label_id: 'hover', } hover_state.label.style.setProperty(`--label-bg`, `hsl(267, 100%, 58%)`) - return hover_state.label } } diff --git a/app/utilities/styles.js b/app/utilities/styles.js index fba4dc29..b6b60bb0 100644 --- a/app/utilities/styles.js +++ b/app/utilities/styles.js @@ -58,6 +58,24 @@ export const getStyles = el => { }) } +export const setVisbox = els => { + return new Promise(resolve => { + const observer = new IntersectionObserver(entries => { + observer.disconnect() + + for (const entry of entries) { + const el = els.find(el => el === entry.target) + el['vis-box'] = entry.boundingClientRect + } + + resolve(els) + }) + + for (const el of els) + observer.observe(el) + }) +} + export const getComputedBackgroundColor = el => { let background = getStyle(el, 'background-color')