Skip to content
Open
25 changes: 25 additions & 0 deletions app/changes.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!doctype html>
<meta charset="utf-8">
<title>Change tracking fixture</title>
<style>
body { font: 14px system-ui; margin: 0; padding: 24px; }
.hero { padding: 16px; background: #eee; }
.cta { padding: 8px 16px; background: #333; color: white; border: 0; border-radius: 4px; }
.grid { display: flex; gap: 8px; margin-top: 20px; }
.tile { padding: 12px; background: #ddd; }
</style>

<section class="hero" id="hero">
<h1 class="hero__title">Welcome</h1>
<button class="cta primary" data-testid="hero-cta">Get started</button>
<a class="cta" href="/docs">Read docs</a>
</section>

<div class="grid" id="grid">
<div class="tile">one</div>
<div class="tile">two</div>
<div class="tile">three</div>
</div>

<vis-bug></vis-bug>
<script src="bundle.js" type="module" defer></script>
10 changes: 9 additions & 1 deletion app/components/hotkey-map/base.element.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,16 @@ export class HotkeyMap extends HTMLElement {
})
}

/**
* What one press is worth. Overridden by tools that walk a token scale,
* where "by 1" would be a lie.
*/
amountFor({hotkeys}) {
return hotkeys.shift ? 10 : 1
}

createCommand({e:{code}, hotkeys}) {
let amount = hotkeys.shift ? 10 : 1
let amount = this.amountFor({hotkeys})
let negative = hotkeys.alt ? 'Subtract' : 'Add'
let negative_modifier = hotkeys.alt ? 'from' : 'to'

Expand Down
73 changes: 56 additions & 17 deletions app/components/hotkey-map/boxshadow.element.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,72 @@
import { HotkeyMap } from './base.element'
import { boxshadow as icon } from '../vis-bug/vis-bug.icons'
import { metaKey } from '../../utilities';
import { metaKey, altKey } from '../../utilities';
import { hasScale } from '../../core'

export class BoxshadowHotkeys extends HotkeyMap {
constructor() {
super()

this._hotkey = 'd'
this._usedkeys = ['shift',metaKey]
this._usedkeys = ['shift',metaKey,altKey,'[',']']
this.tool = 'boxshadow'
}

show() {
this.$shadow.host.style.display = 'flex'
createCommand({e:{code}, hotkeys}) {
let amount = hotkeys.shift ? 10 : 1
let negative = '[increase/decrease]'
let negative_modifier = 'by'
let side = '[arrow key]'

// whole-value tokens: a design system hands you the shadow, not its parts
if (code === 'BracketLeft' || code === 'BracketRight') {
side = hotkeys.shift ? 'border radius' : 'box shadow'

if (hasScale(hotkeys.shift ? 'radius' : 'shadow')) {
amount = 'one scale step'
negative = code === 'BracketRight' ? 'increase' : 'decrease'
}
else {
negative = 'this page defines no'
negative_modifier = ''
amount = 'scale'
}
}
else if (hotkeys[metaKey] && (code === 'ArrowLeft' || code === 'ArrowRight')) {
side = 'shadow opacity'
amount = hotkeys.shift ? '10%' : '1%'
negative = code === 'ArrowRight' ? 'increase' : 'decrease'
}
else if (hotkeys[metaKey] && (code === 'ArrowUp' || code === 'ArrowDown')) {
side = 'inset'
amount = ''
negative_modifier = ''
negative = code === 'ArrowDown' ? 'set' : 'unset'
}
else if (code === 'ArrowLeft' || code === 'ArrowRight') {
side = hotkeys.alt ? 'shadow spread' : 'shadow x offset'
amount = `${amount}px`
negative = code === 'ArrowRight' ? 'increase' : 'decrease'
}
else if (code === 'ArrowUp' || code === 'ArrowDown') {
side = hotkeys.alt ? 'shadow blur' : 'shadow y offset'
amount = `${amount}px`
negative = code === 'ArrowDown' ? 'increase' : 'decrease'
}

return { negative, negative_modifier, amount, side }
}

render() {
displayCommand({negative, negative_modifier, side, amount}) {
if (negative === `±[${altKey}] `)
negative = '[ ] step shadow, shift+[ ] step radius'
if (negative_modifier === ' to ')
negative_modifier = ''

return `
<article>
<div tool-icon>
<span>
${icon}
${this._tool} Tool
</span>
</div>
<div command>
coming soon
</div>
</article>
<span negative>${negative}</span>
<span side tool>${side === '[arrow key]' ? '' : side}</span>
<span light>${negative_modifier}</span>
<span amount>${amount === 1 ? '' : amount}</span>
`
}
}
Expand Down
16 changes: 12 additions & 4 deletions app/components/hotkey-map/font.element.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { HotkeyMap } from './base.element'
import { metaKey, altKey } from '../../utilities';
import { hasScale } from '../../core'

export class FontHotkeys extends HotkeyMap {
constructor() {
super()

this._hotkey = 'f'
this._usedkeys = ['shift',metaKey]
this._usedkeys = ['shift',metaKey,altKey]
this.tool = 'font'
}

/** A scale step where the page has a scale, pixels where it doesn't. */
step(kind) {
return hasScale(kind) ? 'one scale step' : '1px'
}

createCommand({e:{code}, hotkeys}) {
let amount = hotkeys.shift ? 10 : 1
let negative = '[increase/decrease]'
Expand All @@ -29,7 +35,7 @@ export class FontHotkeys extends HotkeyMap {
// leading
else if (hotkeys.shift && (code === 'ArrowUp' || code === 'ArrowDown')) {
side = 'leading'
amount = '1px'
amount = this.step('lineHeight')

if (code === 'ArrowUp')
negative = 'increase'
Expand All @@ -47,10 +53,12 @@ export class FontHotkeys extends HotkeyMap {
if (code === 'ArrowDown')
negative = 'decrease'
}
// font size
// font size — alt is the way back to pixels once a type scale is in play
else if (code === 'ArrowUp' || code === 'ArrowDown') {
side = 'font size'
amount = '1px'
amount = hotkeys.alt
? `${hotkeys.shift ? 10 : 1}px`
: this.step('fontSize')

if (code === 'ArrowUp')
negative = 'increase'
Expand Down
9 changes: 9 additions & 0 deletions app/components/hotkey-map/margin.element.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { HotkeyMap } from './base.element'
import { metaKey, altKey } from '../../utilities/'
import { hasScale } from '../../core'

export class MarginHotkeys extends HotkeyMap {
constructor() {
Expand All @@ -10,6 +11,14 @@ export class MarginHotkeys extends HotkeyMap {

this.tool = 'margin'
}

// bare arrows walk the page's spacing scale when it has one; shift is what
// still counts in pixels
amountFor({hotkeys}) {
return !hotkeys.shift && hasScale('space')
? 'one scale step'
: super.amountFor({hotkeys})
}
}

customElements.define('hotkeys-margin', MarginHotkeys)
9 changes: 9 additions & 0 deletions app/components/hotkey-map/padding.element.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { HotkeyMap } from './base.element'
import { metaKey, altKey } from '../../utilities/'
import { hasScale } from '../../core'

export class PaddingHotkeys extends HotkeyMap {
constructor() {
Expand All @@ -10,6 +11,14 @@ export class PaddingHotkeys extends HotkeyMap {

this.tool = 'padding'
}

// bare arrows walk the page's spacing scale when it has one; shift is what
// still counts in pixels
amountFor({hotkeys}) {
return !hotkeys.shift && hasScale('space')
? 'one scale step'
: super.amountFor({hotkeys})
}
}

customElements.define('hotkeys-padding', PaddingHotkeys)
1 change: 1 addition & 0 deletions app/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export { Overlay } from './selection/overlay.element'
export { BoxModel } from './selection/box-model.element'
export { Corners } from './selection/corners.element'
export { Grip } from './selection/grip.element'
export { Insertion } from './selection/insertion.element'

export { Metatip } from './metatip/metatip.element'
export { Ally } from './metatip/ally.element'
Expand Down
6 changes: 5 additions & 1 deletion app/components/selection/box-model.element.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { BoxModelStyles } from '../styles.store'
import { labelForLength } from '../../core'

export class BoxModel extends HTMLElement {

Expand Down Expand Up @@ -177,7 +178,10 @@ export class BoxModel extends HTMLElement {

createMeasurement(line_model, node_label_id=0) {
const measurement = document.createElement('visbug-distance')
measurement.position = { line_model, node_label_id }
measurement.position = {
line_model: { ...line_model, label: labelForLength('space', line_model.d) },
node_label_id,
}
this.$shadow.appendChild(measurement)
}
}
Expand Down
4 changes: 2 additions & 2 deletions app/components/selection/distance.element.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ export class Distance extends HTMLElement {
: '.5 0 1')
}

render({q,d}, node_label_id) {
render({q,d,label}, node_label_id) {
this.$shadow.host.setAttribute('data-label-id', node_label_id)

return `
<figure quadrant="${q}">
<div></div>
<figcaption>${Math.round(d)}</figcaption>
<figcaption>${label || Math.round(d)}</figcaption>
<div></div>
</figure>
`
Expand Down
60 changes: 60 additions & 0 deletions app/components/selection/insertion.element.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
@import "../_variables.css";

:host {
position: var(--position, absolute);
/* `auto` on right/bottom is what keeps the popover UA styles
(inset: 0; margin: auto) from centering us in the viewport */
inset: var(--top) auto auto var(--left);
width: var(--width);
height: var(--height);
margin: 0;
padding: 0;
border: none;
background: transparent;
overflow: visible;
pointer-events: none;
z-index: var(--layer-5);
display: block;
isolation: isolate;
}

:host::backdrop {
background: none !important;
}

:host([kind="bar"]) .indicator {
width: 100%;
height: 100%;
border-radius: var(--radius-round, 1e5px);
background: var(--neon-pink, hsl(328 100% 54%));
box-shadow: 0 0 0 1px hsl(0 0% 100% / 40%), 0 0 8px 0 var(--neon-pink, hsl(328 100% 54%));
}

/* pips on the ends read as an insertion caret rather than a border */
:host([kind="bar"]) .cap {
position: absolute;
width: 5px;
height: 5px;
border-radius: 50%;
background: var(--neon-pink, hsl(328 100% 54%));
}

:host([kind="bar"][axis="y"]) .cap.start { top: -2px; left: 50%; translate: -50% 0; }
:host([kind="bar"][axis="y"]) .cap.end { bottom: -2px; left: 50%; translate: -50% 0; }
:host([kind="bar"][axis="x"]) .cap.start { left: -2px; top: 50%; translate: 0 -50%; }
:host([kind="bar"][axis="x"]) .cap.end { right: -2px; top: 50%; translate: 0 -50%; }

:host([kind="cell"]) .indicator,
:host([kind="container"]) .indicator {
width: 100%;
height: 100%;
border-radius: 2px;
outline: 2px dashed var(--neon-purple, hsl(267 100% 58%));
outline-offset: -2px;
background: hsl(267 100% 58% / 12%);
}

:host([kind="container"]) .indicator {
outline-style: solid;
background: hsl(267 100% 58% / 6%);
}
50 changes: 50 additions & 0 deletions app/components/selection/insertion.element.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { InsertionStyles } from '../styles.store'

/**
* The drop indicator for layout-aware dragging.
*
* Unlike the other selection overlays it isn't anchored to an element —
* an insertion point lives *between* elements, so it takes a raw rect from
* the resolver in app/features/dropzones.js.
*
* kind 'bar' the caret shown between siblings in flex/block flow
* kind 'cell' a grid cell about to receive the element
* kind 'container' the container that would accept the drop
*
* axis 'x' a vertical bar (inserting along a horizontal/row axis)
* axis 'y' a horizontal bar (inserting along a vertical/column axis)
*/
export class Insertion extends HTMLElement {

constructor() {
super()
this.$shadow = this.attachShadow({ mode: 'closed' })
}

connectedCallback() {
this.$shadow.adoptedStyleSheets = [InsertionStyles]
this.setAttribute('popover', 'manual')
this.showPopover && this.showPopover()
}

disconnectedCallback() {
this.hidePopover && this.hidePopover()
}

set placement({ rect, kind = 'bar', axis = 'y', isFixed = false }) {
this.setAttribute('kind', kind)
this.setAttribute('axis', axis)

this.style.setProperty('--position', isFixed ? 'fixed' : 'absolute')
this.style.setProperty('--top', `${rect.top + (isFixed ? 0 : window.scrollY)}px`)
this.style.setProperty('--left', `${rect.left + (isFixed ? 0 : window.scrollX)}px`)
this.style.setProperty('--width', `${rect.width}px`)
this.style.setProperty('--height', `${rect.height}px`)

this.$shadow.innerHTML = kind === 'bar'
? `<div class="indicator"></div><i class="cap start"></i><i class="cap end"></i>`
: `<div class="indicator"></div>`
}
}

customElements.define('visbug-insertion', Insertion)
2 changes: 2 additions & 0 deletions app/components/styles.store.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ 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 insertion_css } from './selection/insertion.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'
Expand Down Expand Up @@ -44,6 +45,7 @@ export const OverlayStyles = constructStylesheet(overlay_css)
export const BoxModelStyles = constructStylesheet(boxmodel_css)
export const HotkeymapStyles = constructStylesheet(hotkeymap_css)
export const GripStyles = constructStylesheet(grip_css)
export const InsertionStyles = constructStylesheet(insertion_css)

export const LightTheme = constructStylesheet(light_css)
export const VisBugLightStyles = constructStylesheet(visbug_light_css)
Expand Down
Loading