diff --git a/labs/folk-base-connection.ts b/labs/folk-base-connection.ts index 9725617..52dbac4 100644 --- a/labs/folk-base-connection.ts +++ b/labs/folk-base-connection.ts @@ -38,8 +38,6 @@ export class FolkBaseConnection extends FolkElement { } override willUpdate(changedProperties: PropertyValues) { - super.willUpdate(changedProperties); - if (changedProperties.has('source')) { this.#unobserveSource(); diff --git a/labs/folk-base-set.ts b/labs/folk-base-set.ts index 9400c04..0759e98 100644 --- a/labs/folk-base-set.ts +++ b/labs/folk-base-set.ts @@ -45,8 +45,6 @@ export class FolkBaseSet extends FolkElement { } override willUpdate(changedProperties: PropertyValues) { - super.willUpdate(changedProperties); - if (changedProperties.has('sources')) { this.#observeSources(); } diff --git a/labs/folk-shape.ts b/labs/folk-shape.ts index 7e15caa..46f1bd7 100644 --- a/labs/folk-shape.ts +++ b/labs/folk-shape.ts @@ -1,7 +1,8 @@ import { getResizeCursorUrl, getRotateCursorUrl } from '@labs/utils/cursors'; -import { DOMRectTransform, DOMRectTransformReadonly, Point, TransformEvent, Vector } from '@lib'; +import { DOMRectTransform, DOMRectTransformReadonly, FolkElement, Point, TransformEvent, Vector } from '@lib'; import { ResizeManager } from '@lib/resize-manger'; -import { css, html } from '@lib/tags'; +import { html } from '@lib/tags'; +import { css, PropertyValues } from '@lit/reactive-element'; const resizeManager = new ResizeManager(); @@ -163,15 +164,11 @@ declare global { } } -export class FolkShape extends HTMLElement { +export class FolkShape extends FolkElement { static tagName = 'folk-shape'; - static define() { - if (customElements.get(this.tagName)) return; - customElements.define(this.tagName, this); - } + static styles = styles; - #shadow = this.attachShadow({ mode: 'open' }); #internals = this.attachInternals(); #attrWidth: Dimension = 0; @@ -181,7 +178,7 @@ export class FolkShape extends HTMLElement { #previousRect = new DOMRectTransform(); #readonlyRect = new DOMRectTransformReadonly(); - #handles: Record; + #handles!: Record; #startAngle = 0; @@ -192,7 +189,7 @@ export class FolkShape extends HTMLElement { set x(x) { this.#previousRect.x = this.#rect.x; this.#rect.x = x; - this.#requestUpdate(); + this.requestUpdate('x'); } get y() { @@ -202,7 +199,7 @@ export class FolkShape extends HTMLElement { set y(y) { this.#previousRect.y = this.#rect.y; this.#rect.y = y; - this.#requestUpdate(); + this.requestUpdate('x'); } get width(): number { @@ -220,7 +217,7 @@ export class FolkShape extends HTMLElement { this.#rect.width = width; } this.#attrWidth = width; - this.#requestUpdate(); + this.requestUpdate('width'); } get height(): number { @@ -239,7 +236,7 @@ export class FolkShape extends HTMLElement { } this.#attrHeight = height; - this.#requestUpdate(); + this.requestUpdate('height'); } get rotation(): number { @@ -249,7 +246,7 @@ export class FolkShape extends HTMLElement { set rotation(rotation: number) { this.#previousRect.rotation = this.#rect.rotation; this.#rect.rotation = rotation; - this.#requestUpdate(); + this.requestUpdate('rotation'); } #highlighted = false; @@ -264,17 +261,16 @@ export class FolkShape extends HTMLElement { highlighted ? this.#internals.states.add('highlighted') : this.#internals.states.delete('highlighted'); } - constructor() { - super(); + override createRenderRoot() { + const root = super.createRenderRoot(); this.addEventListener('pointerdown', this); this.addEventListener('keydown', this); - this.#shadow.adoptedStyleSheets.push(styles); // Ideally we would creating these lazily on first focus, but the resize handlers need to be around for delegate focus to work. // Maybe can add the first resize handler here, and lazily instantiate the rest when needed? // I can see it becoming important at scale - this.#shadow.setHTMLUnsafe( + (root as ShadowRoot).setHTMLUnsafe( html` @@ -287,7 +283,7 @@ export class FolkShape extends HTMLElement { ); this.#handles = Object.fromEntries( - Array.from(this.#shadow.querySelectorAll('[part]')).map((el) => [ + Array.from(root.querySelectorAll('[part]')).map((el) => [ el.getAttribute('part') as ResizeHandle | RotateHandle, el as HTMLElement, ]), @@ -305,14 +301,10 @@ export class FolkShape extends HTMLElement { this.#rect.rotateOrigin = { x: 0.5, y: 0.5 }; this.#previousRect = new DOMRectTransform(this.#rect); - } - #isConnected = false; - - connectedCallback() { this.setAttribute('tabindex', '0'); - this.#isConnected = true; - this.#update(); + + return root; } // todo: rename to `getDOMRectTransform` @@ -320,18 +312,8 @@ export class FolkShape extends HTMLElement { return this.#readonlyRect; } - // Similar to `Element.getClientBoundingRect()`, but returns an SVG path that precisely outlines the shape. - getBoundingPath(): string { - return ''; - } - - // We might also want some kind of utility function that maps a path into an approximate set of vertices. - getBoundingVertices() { - return []; - } - handleEvent(event: PointerEvent | KeyboardEvent) { - const focusedElement = this.#shadow.activeElement as HTMLElement | null; + const focusedElement = (this.renderRoot as ShadowRoot).activeElement as HTMLElement | null; const target = event.composedPath()[0] as HTMLElement; let handle: Handle | null = null; if (target) { @@ -457,52 +439,38 @@ export class FolkShape extends HTMLElement { } } - #isUpdating = false; + protected override update(changedProperties: PropertyValues): void { + super.update(changedProperties); - async #requestUpdate() { - if (!this.#isConnected) return; - - if (this.#isUpdating) return; - - this.#isUpdating = true; - await true; - this.#isUpdating = false; - this.#update(); - } - - // Any updates that should be batched should happen here like updating the DOM or emitting events should be executed here. - #update() { this.#dispatchTransformEvent(); } #dispatchTransformEvent() { - const emmittedRect = new DOMRectTransform(this.#rect); - console.log('emit X', this.#rect.x); - const event = new TransformEvent(emmittedRect, this.#previousRect); + const emittedRect = new DOMRectTransform(this.#rect); + const event = new TransformEvent(emittedRect, this.#previousRect); this.dispatchEvent(event); if (event.xPrevented) { - emmittedRect.x = this.#previousRect.x; + emittedRect.x = this.#previousRect.x; } if (event.yPrevented) { - emmittedRect.y = this.#previousRect.y; + emittedRect.y = this.#previousRect.y; } if (event.widthPrevented) { - emmittedRect.width = this.#previousRect.width; + emittedRect.width = this.#previousRect.width; } if (event.heightPrevented) { - emmittedRect.height = this.#previousRect.height; + emittedRect.height = this.#previousRect.height; } if (event.rotatePrevented) { - emmittedRect.rotation = this.#previousRect.rotation; + emittedRect.rotation = this.#previousRect.rotation; } - console.log('applied X', emmittedRect.x); - this.style.transform = emmittedRect.toCssString(); - this.style.width = this.#attrWidth === 'auto' ? '' : `${emmittedRect.width}px`; - this.style.height = this.#attrHeight === 'auto' ? '' : `${emmittedRect.height}px`; + this.style.transform = emittedRect.toCssString(); + this.style.width = this.#attrWidth === 'auto' ? '' : `${emittedRect.width}px`; + this.style.height = this.#attrHeight === 'auto' ? '' : `${emittedRect.height}px`; - this.#readonlyRect = new DOMRectTransformReadonly(emmittedRect); + this.#readonlyRect = new DOMRectTransformReadonly(emittedRect); } #onAutoResize = (entry: ResizeObserverEntry) => { @@ -561,7 +529,7 @@ export class FolkShape extends HTMLElement { nextHandle = flipYHandleMap[handle]; } - const newTarget = this.#shadow.querySelector(`[part="${nextHandle}"]`) as HTMLElement; + const newTarget = this.renderRoot.querySelector(`[part="${nextHandle}"]`) as HTMLElement; if (newTarget) { // Update focus for keyboard events @@ -587,6 +555,6 @@ export class FolkShape extends HTMLElement { } } - this.#requestUpdate(); + this.requestUpdate(); } }