diff --git a/app/components/selection/box-model.element.js b/app/components/selection/box-model.element.js index a3f24563..7287ea08 100644 --- a/app/components/selection/box-model.element.js +++ b/app/components/selection/box-model.element.js @@ -29,7 +29,7 @@ export class BoxModel extends HTMLElement { height: bounds.height, width: bounds.width, top: bounds.top + window.scrollY, - left: bounds.left, + left: bounds.left + window.scrollX, rotation: 'rotate(-45)', } } @@ -38,7 +38,7 @@ export class BoxModel extends HTMLElement { height: total_height, width: total_width, top: bounds.top + window.scrollY - sides.top, - left: bounds.left - sides.left, + left: bounds.left + window.scrollX - sides.left, rotation: 'rotate(45)', } } diff --git a/app/components/selection/distance.element.js b/app/components/selection/distance.element.js index b3d13299..7667e027 100644 --- a/app/components/selection/distance.element.js +++ b/app/components/selection/distance.element.js @@ -20,8 +20,8 @@ export class Distance extends HTMLElement { set styleProps({y,x,d,q,v = false, color}) { this.style.setProperty('--top', `${y + window.scrollY}px`) - this.style.setProperty('--right', q === 'left' ? `${x}px` : 'auto') - this.style.setProperty('--left', q !== 'left' ? `${x}px` : 'auto') + this.style.setProperty('--right', q === 'left' ? `${x-window.scrollX}px` : 'auto') + this.style.setProperty('--left', q !== 'left' ? `${x+window.scrollX}px` : 'auto') this.style.setProperty('--direction', v ? 'column' : 'row') this.style.setProperty('--quadrant', q) diff --git a/app/components/selection/gridlines.element.js b/app/components/selection/gridlines.element.js index a2563812..7f109ee1 100644 --- a/app/components/selection/gridlines.element.js +++ b/app/components/selection/gridlines.element.js @@ -22,6 +22,8 @@ export class Gridlines extends HTMLElement { const { winHeight, winWidth } = windowBounds() const [svg] = this.$shadow.children const [rect,line1,line2,line3,line4] = svg.children + top = top + window.scrollY + left = left + window.scrollX this.$shadow.host.style.display = 'block' @@ -33,18 +35,17 @@ export class Gridlines extends HTMLElement { line1.setAttribute('x2', left) line2.setAttribute('x1', left + width) line2.setAttribute('x2', left + width) - line3.setAttribute('y1', top + window.scrollY) - line3.setAttribute('y2', top + window.scrollY) - line3.setAttribute('x2', winWidth) - line4.setAttribute('y1', top + window.scrollY + height) - line4.setAttribute('y2', top + window.scrollY + height) - line4.setAttribute('x2', winWidth) + line3.setAttribute('y1', top) + line3.setAttribute('y2', top) + line3.setAttribute('x2', left + winWidth) + line4.setAttribute('y1', top + height) + line4.setAttribute('y2', top + height) + line4.setAttribute('x2', left + winWidth) } render({ x, y, width, height, top, left }) { const { winWidth, winHeight } = windowBounds() - const { offsetHeight } = document.body - const calced_y = y + window.scrollY + const { offsetHeight, offsetWidth } = document.body return ` - - + + ` } diff --git a/app/components/selection/handles.element.js b/app/components/selection/handles.element.js index e1fa76e6..172a6eee 100644 --- a/app/components/selection/handles.element.js +++ b/app/components/selection/handles.element.js @@ -58,8 +58,8 @@ export class Handles extends HTMLElement { render({ x, y, width, height, top, left }, node_label_id, isFixed) { this.$shadow.host.setAttribute('data-label-id', node_label_id) - this.style.setProperty('--top', `${top + (isFixed ? 0 : window.scrollY)}px`) - this.style.setProperty('--left', `${left}px`) + this.style.setProperty('--top', `${top+window.scrollY}px`) + this.style.setProperty('--left', `${left+window.scrollX}px`) this.style.setProperty('--position', isFixed ? 'fixed' : 'absolute') return ` diff --git a/app/components/selection/hover.element.js b/app/components/selection/hover.element.js index 5786517b..1ca00caa 100644 --- a/app/components/selection/hover.element.js +++ b/app/components/selection/hover.element.js @@ -9,8 +9,8 @@ export class Hover extends Handles { } render({ width, height, top, left }, node_label_id, isFixed) { - this.style.setProperty('--top', `${top + (isFixed ? 0 : window.scrollY)}px`) - this.style.setProperty('--left', `${left}px`) + this.style.setProperty('--top', `${top + window.scrollY}px`) + this.style.setProperty('--left', `${left + window.scrollX}px`) this.style.setProperty('--position', isFixed ? 'fixed' : 'absolute') return ` diff --git a/app/components/selection/label.element.js b/app/components/selection/label.element.js index ffea5671..10949c3d 100644 --- a/app/components/selection/label.element.js +++ b/app/components/selection/label.element.js @@ -56,7 +56,7 @@ export class Label extends HTMLElement { set update({boundingRect, isFixed}) { this.style.setProperty('--top', `${boundingRect.y + (isFixed ? 0 : window.scrollY)}px`) - this.style.setProperty('--left', `${boundingRect.x - 1}px`) + this.style.setProperty('--left', `${boundingRect.x + window.scrollX - 1}px`) this.style.setProperty('--max-width', `${boundingRect.width + (window.innerWidth - boundingRect.x - boundingRect.width - 20)}px`) this.style.setProperty('--position', isFixed ? 'fixed' : 'absolute'); } diff --git a/app/components/selection/overlay.element.js b/app/components/selection/overlay.element.js index af84dd0d..f2a8c627 100644 --- a/app/components/selection/overlay.element.js +++ b/app/components/selection/overlay.element.js @@ -23,7 +23,7 @@ export class Overlay extends HTMLElement { this.$shadow.host.style.display = 'block' svg.style.display = 'block' - this.style.setProperty('--top', `${top + window.scrollY}px`) + this.style.setProperty('--top', `${top}px`) this.style.setProperty('--left', `${left - 1}px`) svg.setAttribute('width', width + 'px') diff --git a/app/components/styles.store.js b/app/components/styles.store.js index 994e3ea2..1000e0e8 100644 --- a/app/components/styles.store.js +++ b/app/components/styles.store.js @@ -1,17 +1,18 @@ import 'construct-style-sheets-polyfill' -import { default as visbug_css } from './vis-bug/vis-bug.element.css' -import { default as handle_css } from './selection/handles.element.css' -import { default as hover_css } from './selection/hover.element.css' -import { default as corners_css } from './selection/corners.element.css' -import { default as distance_css } from './selection/distance.element.css' -import { default as gridline_css } from './selection/gridlines.element.css' -import { default as label_css } from './selection/label.element.css' -import { default as overlay_css } from './selection/overlay.element.css' -import { default as boxmodel_css } from './selection/box-model.element.css' -import { default as metatip_css } from './metatip/metatip.element.css' -import { default as hotkeymap_css } from './hotkey-map/base.element.css' -import { default as grip_css } from './selection/grip.element.css' +import { default as visbug_css } from './vis-bug/vis-bug.element.css' +import { default as visbug_artboard_css } from './vis-bug/vis-bug.artboard.css' +import { default as handle_css } from './selection/handles.element.css' +import { default as hover_css } from './selection/hover.element.css' +import { default as corners_css } from './selection/corners.element.css' +import { default as distance_css } from './selection/distance.element.css' +import { default as gridline_css } from './selection/gridlines.element.css' +import { default as label_css } from './selection/label.element.css' +import { default as overlay_css } from './selection/overlay.element.css' +import { default as boxmodel_css } from './selection/box-model.element.css' +import { default as metatip_css } from './metatip/metatip.element.css' +import { default as hotkeymap_css } from './hotkey-map/base.element.css' +import { default as grip_css } from './selection/grip.element.css' import { default as light_css } from './_variables_light.css' import { default as visbug_light_css } from './vis-bug/vis-bug.element_light.css' @@ -29,6 +30,7 @@ const constructStylesheet = (styles, stylesheet = new CSSStyleSheet()) => { } export const VisBugStyles = constructStylesheet(visbug_css) +export const ArtboardStyles = constructStylesheet(visbug_artboard_css) export const HandleStyles = constructStylesheet(handle_css) export const HoverStyles = constructStylesheet(hover_css) export const CornerStyles = constructStylesheet(corners_css) diff --git a/app/components/vis-bug/vis-bug.artboard.css b/app/components/vis-bug/vis-bug.artboard.css new file mode 100644 index 00000000..feec6295 --- /dev/null +++ b/app/components/vis-bug/vis-bug.artboard.css @@ -0,0 +1,21 @@ +html { + display: grid; + grid: auto / 100vw; + place-content: center; + background-color: Canvas !important; + overflow: scroll !important; +} + +body { + min-height: 100vh; + outline: 1px solid ButtonFace; + box-shadow: 0 2px 5px hsl(0 0% 0% / 25%) !important; + box-shadow: + 0 3px 2px hsl(0 0% 0% / 2%), + 0 7px 5px hsl(0 0% 0% / 2%), + 0 13px 10px hsl(0 0% 0% / 3%), + 0 22px 18px hsl(0 0% 0% / 4%), + 0 42px 33px hsl(0 0% 0% / 5%), + 0 100px 80px hsl(0 0% 0% / 7%) !important + ; +} \ No newline at end of file diff --git a/app/components/vis-bug/vis-bug.css b/app/components/vis-bug/vis-bug.css index cbdb0750..dd7754a9 100644 --- a/app/components/vis-bug/vis-bug.css +++ b/app/components/vis-bug/vis-bug.css @@ -1,7 +1,3 @@ -body { - min-height: 100vh; -} - body:not([testing]) [data-selected=true] { transition: all 0.15s ease; } diff --git a/app/components/vis-bug/vis-bug.element.js b/app/components/vis-bug/vis-bug.element.js index fd95f0cc..158fa7cf 100644 --- a/app/components/vis-bug/vis-bug.element.js +++ b/app/components/vis-bug/vis-bug.element.js @@ -9,7 +9,7 @@ import { import { Selectable, Moveable, Padding, Margin, EditText, Font, Flex, Search, ColorPicker, BoxShadow, HueShift, MetaTip, - Guides, Screenshot, Position, Accessibility, draggable + Guides, Screenshot, Position, Accessibility, draggable, Zoom } from '../../features/' import { @@ -55,6 +55,9 @@ export default class VisBug extends HTMLElement { provideSelectorEngine(this.selectorEngine) + if (this.getAttribute('viewmode') == 'artboard') + Zoom.start(this.selectorEngine) + this.toolSelected($('[data-tool="guides"]', this.$shadow)[0]) } @@ -65,10 +68,19 @@ export default class VisBug extends HTMLElement { hotkeys.unbind( Object.keys(this.toolbar_model).reduce((events, key) => events += ',' + key, '')) - hotkeys.unbind(`${metaKey}+/`) + hotkeys.unbind(`${metaKey}+/,${metaKey}+.`) + + if (this.getAttribute('viewmode') == 'artboard') + Zoom.stop() } + static get observedAttributes() { return ['viewmode','color-scheme'] } + attributeChangedCallback(name, oldValue, newValue) { + newValue === 'artboard' + ? Zoom.start(this.selectorEngine) + : Zoom.stop() + if (name === 'color-scheme') this.applyScheme(newValue) } @@ -80,6 +92,10 @@ export default class VisBug extends HTMLElement { ? this.getAttribute('color-mode') : this.setAttribute('color-mode', 'hex') + this.hasAttribute('viewmode') + ? this.getAttribute('viewmode') + : this.setAttribute('viewmode', 'document') + this.hasAttribute('color-scheme') ? this.getAttribute('color-scheme') : this.setAttribute('color-scheme', 'auto') @@ -114,8 +130,6 @@ export default class VisBug extends HTMLElement { .filter(node => node.nodeName.includes('VISBUG')) .forEach(el => el.remove()) - this.teardown(); - document.querySelectorAll('[data-pseudo-select=true]') .forEach(el => el.removeAttribute('data-pseudo-select')) diff --git a/app/demo.js b/app/demo.js new file mode 100644 index 00000000..106e11b7 --- /dev/null +++ b/app/demo.js @@ -0,0 +1,15 @@ +const metaKey = window.navigator.platform.includes('Mac') + ? 'cmd' + : 'ctrl' + +document.firstElementChild.prepend(document.querySelector('vis-bug')) + +if ('ontouchstart' in document.documentElement) + document.getElementById('mobile-info').style.display = '' + +if (metaKey === 'ctrl') + [...document.querySelectorAll('kbd')] + .forEach(node => { + node.textContent = node.textContent.replace('cmd','ctrl') + node.textContent = node.textContent.replace('opt','alt') + }) \ No newline at end of file diff --git a/app/demo/index.css b/app/demo/index.css index 23b68307..73afe007 100644 --- a/app/demo/index.css +++ b/app/demo/index.css @@ -52,7 +52,6 @@ html[light] { body { margin: 0; - padding-left: 4rem; background: var(--dark-grey); color: var(--lighter-grey); font-family: system-ui; diff --git a/app/features/accessibility.js b/app/features/accessibility.js index 814bc122..e1503551 100644 --- a/app/features/accessibility.js +++ b/app/features/accessibility.js @@ -53,7 +53,7 @@ const mouseMove = e => { export function showTip(target, e) { if (!state.active.tip) { // create const tip = render(target) - document.body.appendChild(tip) + document.body.insertAdjacentElement('afterend', tip) positionTip(tip, e) observe({tip, target}) diff --git a/app/features/guides.js b/app/features/guides.js index 4431d9cf..c2a1ac0a 100644 --- a/app/features/guides.js +++ b/app/features/guides.js @@ -47,7 +47,7 @@ export function createGuide(vert = true) { z-index: 2147483643; ` - vert + vert ? styles += ` width: 1px; height: 100vh; @@ -97,11 +97,11 @@ const showGridlines = node => { state.gridlines = document.createElement('visbug-gridlines') state.gridlines.position = node.getBoundingClientRect() - document.body.appendChild(state.gridlines) + document.body.insertAdjacentElement('afterend', state.gridlines) } } -const hideGridlines = () => { +export const hideGridlines = () => { if (!state.gridlines) return state.gridlines.style.display = 'none' } diff --git a/app/features/index.js b/app/features/index.js index 2af5becf..1cbfe207 100644 --- a/app/features/index.js +++ b/app/features/index.js @@ -14,4 +14,4 @@ export { Guides } from './guides' export { Screenshot } from './screenshot' export { Position, draggable } from './position' export { Accessibility } from './accessibility' - +export { Zoom } from './zoom' diff --git a/app/features/measurements.js b/app/features/measurements.js index 1c39e4c8..a3d6361d 100644 --- a/app/features/measurements.js +++ b/app/features/measurements.js @@ -139,7 +139,7 @@ export function createMeasurements({$anchor, $target}) { node_label_id: state.distances.length, } - document.body.appendChild($measurement) + document.body.insertAdjacentElement('afterend', $measurement) state.distances[state.distances.length] = $measurement }) } diff --git a/app/features/metatip.js b/app/features/metatip.js index 2be4b9ff..e0e28e17 100644 --- a/app/features/metatip.js +++ b/app/features/metatip.js @@ -63,7 +63,7 @@ const mouseMove = e => { export function showTip(target, e) { if (!state.active.tip) { // create const tip = render(target) - document.body.appendChild(tip) + document.body.insertAdjacentElement('afterend', tip) positionTip(tip, e) observe({tip, target}) diff --git a/app/features/selectable.js b/app/features/selectable.js index bd42b801..b4a09e64 100644 --- a/app/features/selectable.js +++ b/app/features/selectable.js @@ -139,6 +139,10 @@ export function Selectable(visbug) { did_hide = false } + + if (!e.metaKey) { + document.body.style.cursor = '' + } } } @@ -602,7 +606,7 @@ export function Selectable(visbug) { isFixed: isFixed(el), } - document.body.appendChild(label) + document.body.insertAdjacentElement('afterend', label) $(label).on('query', ({detail}) => { if (!detail.text) return @@ -635,7 +639,7 @@ export function Selectable(visbug) { handle.position = { el, node_label_id: id } - document.body.appendChild(handle) + document.body.insertAdjacentElement('afterend', handle) handles[handles.length] = handle return handle @@ -648,7 +652,7 @@ export function Selectable(visbug) { hover_state.element.remove() hover_state.element = document.createElement('visbug-hover') - document.body.appendChild(hover_state.element) + document.body.insertAdjacentElement('afterend',hover_state.element) hover_state.element.position = {el} return hover_state.element @@ -661,7 +665,7 @@ export function Selectable(visbug) { hover_state.label.remove() hover_state.label = document.createElement('visbug-label') - document.body.appendChild(hover_state.label) + document.body.insertAdjacentElement('afterend', hover_state.label) hover_state.label.text = text hover_state.label.position = { @@ -682,7 +686,7 @@ export function Selectable(visbug) { hover_state.element.remove() hover_state.element = document.createElement('visbug-corners') - document.body.appendChild(hover_state.element) + document.body.insertAdjacentElement('afterend', hover_state.element) hover_state.element.position = {el} return hover_state.element diff --git a/app/features/zoom.js b/app/features/zoom.js new file mode 100644 index 00000000..7214b03b --- /dev/null +++ b/app/features/zoom.js @@ -0,0 +1,176 @@ +import { metaKey } from '../utilities/' +import { hideGridlines } from './guides' +import { ArtboardStyles } from '../components/styles.store' + +const state = { + page: { + scale: 1, + }, + meta: { + down: false, + }, + mouse: { + x: 0, + y: 0, + } +} + +const setupDocument = () => { + const html = document.firstElementChild + + html.style.height = (window.innerHeight + document.body.clientHeight * 1.75) + 'px' + html.style.width = document.body.clientWidth * 2.5 + 'px' + document.originalAdoptedStyles = document.adoptedStyleSheets + document.adoptedStyleSheets = [...document.adoptedStyleSheets, ArtboardStyles] + + document.body.scrollIntoView({ + inline: 'center', + behavior: 'smooth', + }) +} + +const isMetaKey = e => + e[`${metaKey}Key`] || e.metaKey + +export const zoomIn = (amount = .1) => { + state.page.scale += amount + state.page.originX = state.mouse.x + state.page.originY = state.mouse.y + + scale() +} + +export const zoomOut = (amount = .1) => { + state.page.scale -= amount + state.page.originX = state.mouse.x + state.page.originY = state.mouse.y + + if (state.page.scale < .01) + state.page.scale = .01 + + scale() +} + +export const zoomToFit = async () => { + const fixedScale = ((window.innerHeight * .9) / document.body.clientHeight).toFixed(2) + state.page.scale = parseFloat(fixedScale) + + await scale() + + document.body.scrollIntoView({ + block: 'center', + inline: 'center', + behavior: 'smooth', + }) +} + +export const zoomToHomebase = async () => { + state.page.scale = 1 + + await scale() + + document.body.scrollIntoView({ + inline: 'center', + behavior: 'smooth', + }) +} + +const scale = async () => { + const stash = state.visbug.selection() + state.visbug.unselect_all() + hideGridlines() + + await document.body.animate([{ + transform: `scale(${state.page.scale})`, + transformOrigin: `${state.page.originX}px ${state.page.originY}px`, + easing: 'cubic-bezier(0.39, 0.58, 0.57, 1)', + }], { + duration: 150, + fill: 'forwards', + }).finished + + stash.forEach(el => + state.visbug.select(el)) +} + +const handleKeydown = e => { + if (!state.meta.down && isMetaKey(e)) + state.meta.down = true + + if (state.meta.down) { + switch(e.key) { + case '=': + zoomIn() + break + case '-': + zoomOut() + break + case '0': + zoomToHomebase() + break + case '9': + zoomToFit() + break + } + + e.preventDefault() + } +} + +const handleKeyup = e => { + if (state.meta.down && !isMetaKey(e)) + state.meta.down = false +} + +const handleWheel = e => { + if (state.meta.down) { + e.preventDefault() + + const scaleUp = e.deltaY < 0 + + document.body.style.cursor = scaleUp + ? 'zoom-in' + : 'zoom-out' + + state.page.originX = e.clientX + state.page.originY = e.clientY + + scaleUp + ? zoomIn((e.deltaY * .01) * -1) + : zoomOut(e.deltaY * .01) + } + else { + document.body.style.cursor = '' + } +} + +const handleMousemove = e => { + state.mouse.x = e.clientX + state.mouse.y = e.clientY +} + +const start = SelectorEngine => { + state.visbug = SelectorEngine + + setupDocument() + + window.addEventListener("keydown", handleKeydown) + window.addEventListener("keyup", handleKeyup) + window.addEventListener("wheel", handleWheel, { passive: false }) + window.addEventListener('mousemove', handleMousemove, {passive: true}) +} + +const stop = () => { + window.removeEventListener("keydown", handleKeydown) + window.removeEventListener("keyup", handleKeyup) + window.removeEventListener("wheel", handleWheel) + window.removeEventListener('mousemove', handleMousemove) + document.body.getAnimations().forEach(anim => anim.cancel()) + document.firstElementChild.removeAttribute('style') + document.adoptedStyleSheets = document.originalAdoptedStyles +} + +export const Zoom = { + start, + stop, +} diff --git a/app/index.html b/app/index.html index 3fc5288e..178aa8a1 100644 --- a/app/index.html +++ b/app/index.html @@ -36,8 +36,7 @@ - - +