-
Notifications
You must be signed in to change notification settings - Fork 290
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add support for resizing elements (#569)
* Add UI * Implement resizing 🎉 * Improve readability * Fix user-select * Fix formatting (semicolon and whitespace) * Move clamp to utilities folder Co-authored-by: Mayank <[email protected]>
- Loading branch information
Showing
11 changed files
with
297 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
@import "../_variables.css"; | ||
|
||
:host { | ||
display: grid; | ||
grid-area: 1 / -1; | ||
place-self: var(--align-self, center) var(--justify-self, center); | ||
transform: translate(var(--translate-x, 0), var(--translate-y, 0)); | ||
} | ||
|
||
:host([hidden]) { | ||
display: none; | ||
} | ||
|
||
:host > button { | ||
pointer-events: auto; | ||
background-color: white; | ||
border: 1px solid hotpink; | ||
padding: 0; | ||
width: 0.5rem; | ||
height: 0.5rem; | ||
border-radius: 50%; | ||
position: relative; | ||
cursor: var(--cursor); | ||
|
||
/* increase tap target size */ | ||
&::before { | ||
content: ''; | ||
position: absolute; | ||
inset: -0.5rem; | ||
} | ||
} | ||
|
||
:host([placement^="top"]) { | ||
--align-self: start; | ||
--translate-y: -50%; | ||
} | ||
|
||
:host([placement^="bottom"]) { | ||
--align-self: end; | ||
--translate-y: 50%; | ||
} | ||
|
||
:host([placement$="start"]) { | ||
--justify-self: start; | ||
--translate-x: -50%; | ||
} | ||
|
||
:host([placement$="end"]) { | ||
--justify-self: end; | ||
--translate-x: 50%; | ||
} | ||
|
||
:host([placement^="top"]), | ||
:host([placement^="bottom"]) { | ||
--cursor: ns-resize; | ||
} | ||
|
||
:host([placement$="start"]), | ||
:host([placement$="end"]) { | ||
--cursor: ew-resize; | ||
} | ||
|
||
:host([placement="top-start"]) { | ||
--cursor: nw-resize; | ||
} | ||
|
||
:host([placement="top-end"]) { | ||
--cursor: ne-resize; | ||
} | ||
|
||
:host([placement="bottom-start"]) { | ||
--cursor: sw-resize; | ||
} | ||
|
||
:host([placement="bottom-end"]) { | ||
--cursor: se-resize; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
import $ from 'blingblingjs' | ||
import { HandleStyles } from '../styles.store' | ||
import { clamp } from '../../utilities/numbers' | ||
|
||
export class Handle extends HTMLElement { | ||
|
||
constructor() { | ||
super() | ||
this.$shadow = this.attachShadow({mode: 'closed'}) | ||
this.styles = [HandleStyles] | ||
} | ||
|
||
connectedCallback() { | ||
this.$shadow.adoptedStyleSheets = this.styles | ||
this.$shadow.innerHTML = this.render() | ||
|
||
this.button = this.$shadow.querySelector('button') | ||
this.button.addEventListener('pointerdown', this.on_element_resize_start.bind(this)) | ||
|
||
this.placement = this.getAttribute('placement') | ||
} | ||
|
||
static get observedAttributes() { | ||
return ['placement'] | ||
} | ||
|
||
attributeChangedCallback(name, oldValue, newValue) { | ||
if (name === 'placement') { | ||
this.placement = newValue | ||
} | ||
} | ||
|
||
on_element_resize_start(e) { | ||
e.preventDefault() | ||
e.stopPropagation() | ||
|
||
if (e.button !== 0) return | ||
|
||
const placement = this.placement | ||
const handlesEl = e.path.find(el => el.tagName === 'VISBUG-HANDLES') | ||
const nodeLabelId = handlesEl.getAttribute('data-label-id') | ||
const [sourceEl] = $(`[data-label-id="${nodeLabelId}"]`) | ||
|
||
if (!sourceEl) return | ||
|
||
const { x: initialX, y: initialY } = e | ||
const initialStyle = getComputedStyle(sourceEl) | ||
const initialWidth = parseFloat(initialStyle.width) | ||
const initialHeight = parseFloat(initialStyle.height) | ||
const initialTransform = new DOMMatrix(initialStyle.transform) | ||
|
||
const originalElTransition = sourceEl.style.transition | ||
const originalDocumentCursor = document.body.style.cursor | ||
const originalDocumentUserSelect = document.body.style.userSelect | ||
sourceEl.style.transition = 'none' | ||
document.body.style.cursor = getComputedStyle(this).getPropertyValue('--cursor') | ||
document.body.style.userSelect = 'none' | ||
|
||
document.addEventListener('pointermove', on_element_resize_move) | ||
|
||
function on_element_resize_move(e) { | ||
e.preventDefault() | ||
e.stopPropagation() | ||
|
||
const newX = clamp(0, e.clientX, document.documentElement.clientWidth) | ||
const newY = clamp(0, e.clientY, document.documentElement.clientHeight) | ||
|
||
const diffX = newX - initialX | ||
const diffY = newY - initialY | ||
|
||
switch (placement) { | ||
case 'top-start': { | ||
const newWidth = initialWidth - diffX | ||
const newHeight = initialHeight - diffY | ||
const newTranslate = initialTransform.translate(diffX, diffY).transformPoint() | ||
|
||
requestAnimationFrame(() => { | ||
sourceEl.style.width = `${newWidth}px` | ||
sourceEl.style.height = `${newHeight}px` | ||
sourceEl.style.transform = `translate(${newTranslate.x}px, ${newTranslate.y}px)` | ||
}) | ||
break | ||
} | ||
case 'top-center': { | ||
const newHeight = initialHeight - diffY | ||
const newTranslate = initialTransform.translate(0, diffY).transformPoint() | ||
|
||
requestAnimationFrame(() => { | ||
sourceEl.style.height = `${newHeight}px` | ||
sourceEl.style.transform = `translate(${newTranslate.x}px, ${newTranslate.y}px)` | ||
}) | ||
break | ||
} | ||
case 'top-end': { | ||
const newWidth = initialWidth + diffX | ||
const newHeight = initialHeight - diffY | ||
const newTranslate = initialTransform.translate(0, diffY).transformPoint() | ||
|
||
requestAnimationFrame(() => { | ||
sourceEl.style.width = `${newWidth}px` | ||
sourceEl.style.height = `${newHeight}px` | ||
sourceEl.style.transform = `translate(${newTranslate.x}px, ${newTranslate.y}px)` | ||
}) | ||
break | ||
} | ||
case 'middle-start': { | ||
const newWidth = initialWidth - diffX | ||
const newTranslate = initialTransform.translate(diffX).transformPoint() | ||
|
||
requestAnimationFrame(() => { | ||
sourceEl.style.width = `${newWidth}px` | ||
sourceEl.style.transform = `translate(${newTranslate.x}px, ${newTranslate.y}px)` | ||
}) | ||
break | ||
} | ||
case 'middle-end': { | ||
const newWidth = initialWidth + diffX | ||
|
||
requestAnimationFrame(() => { | ||
sourceEl.style.width = `${newWidth}px` | ||
}) | ||
break | ||
} | ||
case 'bottom-start': { | ||
const newWidth = initialWidth - diffX | ||
const newHeight = initialHeight + diffY | ||
const newTranslate = initialTransform.translate(diffX, 0).transformPoint() | ||
|
||
requestAnimationFrame(() => { | ||
sourceEl.style.width = `${newWidth}px` | ||
sourceEl.style.height = `${newHeight}px` | ||
sourceEl.style.transform = `translate(${newTranslate.x}px, ${newTranslate.y}px)` | ||
}) | ||
break | ||
} | ||
case 'bottom-center': { | ||
const newHeight = initialHeight + diffY | ||
|
||
requestAnimationFrame(() => { | ||
sourceEl.style.height = `${newHeight}px` | ||
}) | ||
break | ||
} | ||
case 'bottom-end': { | ||
const newWidth = initialWidth + diffX | ||
const newHeight = initialHeight + diffY | ||
|
||
requestAnimationFrame(() => { | ||
sourceEl.style.width = `${newWidth}px` | ||
sourceEl.style.height = `${newHeight}px` | ||
}) | ||
break | ||
} | ||
} | ||
} | ||
|
||
document.addEventListener('pointerup', on_element_resize_end, { once: true }) | ||
document.addEventListener('mouseleave', on_element_resize_end, { once: true }) | ||
|
||
function on_element_resize_end() { | ||
document.removeEventListener('pointermove', on_element_resize_move) | ||
document.body.style.cursor = originalDocumentCursor | ||
document.body.style.userSelect = originalDocumentUserSelect | ||
sourceEl.style.transition = originalElTransition | ||
} | ||
} | ||
|
||
disconnectedCallback() { | ||
this.button.removeEventListener('pointerdown', this.on_element_resize_start.bind(this)) | ||
} | ||
|
||
render() { | ||
return ` | ||
<button type="button" aria-label="Resize"></button> | ||
` | ||
} | ||
} | ||
|
||
customElements.define('visbug-handle', Handle) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,18 @@ | ||
@import "../_variables.css"; | ||
|
||
:host > svg { | ||
:host { | ||
position: var(--position, 'absolute'); | ||
top: var(--top); | ||
left: var(--left); | ||
overflow: visible; | ||
pointer-events: none; | ||
z-index: var(--layer-3); | ||
width: var(--width); | ||
height: var(--height); | ||
display: grid; | ||
isolation: isolate; | ||
} | ||
|
||
:host > svg { | ||
position: absolute; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.