diff --git a/demo/arrows.html b/demo/arrows.html new file mode 100644 index 0000000..914f331 --- /dev/null +++ b/demo/arrows.html @@ -0,0 +1,45 @@ + + + + + + Arrows + + + + + Hello World + + + + + + + diff --git a/demo/chains-of-thought/main.ts b/demo/chains-of-thought/main.ts index 3924965..99fc521 100644 --- a/demo/chains-of-thought/main.ts +++ b/demo/chains-of-thought/main.ts @@ -33,6 +33,7 @@ class SpatialThought extends HTMLElement { handleEvent(event: PointerEvent): void { if (event.type === 'click' && event.target === this.#deleteButton) { this.#geometry.remove(); + document .querySelectorAll( `spatial-connection[source="spatial-geometry[id='${this.#geometry.id}']"], diff --git a/demo/index.html b/demo/index.html index d0b7399..7d1c55e 100644 --- a/demo/index.html +++ b/demo/index.html @@ -24,7 +24,7 @@
  • Maps
  • Music
  • Ink
  • -
  • Arrow
  • +
  • Arrows
  • Canvasify
  • Spreadsheet
  • Chains of thought
  • diff --git a/src/arrows/abstract-arrow.ts b/src/arrows/abstract-arrow.ts index 750256d..cc54bc5 100644 --- a/src/arrows/abstract-arrow.ts +++ b/src/arrows/abstract-arrow.ts @@ -2,6 +2,24 @@ import { VisualObserverEntry, VisualObserverManager } from './visual-observer'; const visualObserver = new VisualObserverManager(); +interface Vertex { + x: number; + y: number; +} + +const vertexRegex = /(?-?([0-9]*[.])?[0-9]+),\s*(?-?([0-9]*[.])?[0-9]+)/; + +function parseVertex(str: string): Vertex | null { + const results = vertexRegex.exec(str); + + if (results === null) return null; + + return { + x: Number(results.groups?.x), + y: Number(results.groups?.y), + }; +} + export class AbstractArrow extends HTMLElement { static tagName = 'abstract-arrow'; @@ -9,8 +27,6 @@ export class AbstractArrow extends HTMLElement { customElements.define(this.tagName, this); } - static observedAttributes = ['source', 'target']; - #source = ''; /** A CSS selector for the source of the arrow. */ get source() { @@ -18,10 +34,15 @@ export class AbstractArrow extends HTMLElement { } set source(source) { - this.setAttribute('source', source); + this.#source = source; + this.observeSource(); } #sourceRect!: DOMRectReadOnly; + get sourceRect() { + return this.#sourceRect; + } + #sourceElement: Element | null = null; get sourceElement() { @@ -38,14 +59,17 @@ export class AbstractArrow extends HTMLElement { get target() { return this.#target; } - set target(target) { - this.setAttribute('target', target); + this.#target = target; + this.observeTarget(); } #targetRect!: DOMRectReadOnly; - #targetElement: Element | null = null; + get targetRect() { + return this.#targetRect; + } + #targetElement: Element | null = null; get targetElement() { return this.#targetElement; } @@ -55,14 +79,9 @@ export class AbstractArrow extends HTMLElement { this.update(); }; - attributeChangedCallback(name: string, _oldValue: string, newValue: string) { - if (name === 'source') { - this.#source = newValue; - this.observeSource(); - } else if (name === 'target') { - this.#target = newValue; - this.observeTarget(); - } + connectedCallback() { + this.source = this.getAttribute('source') || ''; + this.target = this.getAttribute('target') || ''; } disconnectedCallback() { @@ -70,16 +89,29 @@ export class AbstractArrow extends HTMLElement { this.unobserveTarget(); } + // TODO: why reparse the vertex? + setSourceVertex(vertex: Vertex) { + this.target = `${vertex.x},${vertex.y}`; + } + observeSource() { this.unobserveSource(); - const el = document.querySelector(this.source); - if (el === null) { - throw new Error('source is not a valid element'); + const vertex = parseVertex(this.#source); + + if (vertex) { + this.#sourceRect = DOMRectReadOnly.fromRect(vertex); + this.update(); + } else { + const el = document.querySelector(this.source); + + if (el === null) { + throw new Error('source is not a valid element'); + } + + this.#sourceElement = el; + visualObserver.observe(this.#sourceElement, this.#sourceCallback); } - - this.#sourceElement = el; - visualObserver.observe(this.#sourceElement, this.#sourceCallback); } unobserveSource() { @@ -90,13 +122,21 @@ export class AbstractArrow extends HTMLElement { observeTarget() { this.unobserveTarget(); - this.#targetElement = document.querySelector(this.target); - if (!this.#targetElement) { - throw new Error('target is not a valid element'); + const vertex = parseVertex(this.#target); + + if (vertex) { + this.#targetRect = DOMRectReadOnly.fromRect(vertex); + this.update(); + } else { + this.#targetElement = document.querySelector(this.#target); + + if (!this.#targetElement) { + throw new Error('target is not a valid element'); + } + + visualObserver.observe(this.#targetElement, this.#targetCallback); } - - visualObserver.observe(this.#targetElement, this.#targetCallback); } unobserveTarget() { @@ -106,25 +146,10 @@ export class AbstractArrow extends HTMLElement { } update() { - if ( - this.#sourceRect === undefined || - this.#targetRect === undefined || - this.#sourceElement === null || - this.#targetElement === null - ) - return; + if (this.#sourceRect === undefined || this.#targetRect === undefined) return; - this.render(this.#sourceRect, this.#targetRect, this.#sourceElement, this.#targetElement); + this.render(); } - render( - // @ts-ignore - sourceRect: DOMRectReadOnly, - // @ts-ignore - targetRect: DOMRectReadOnly, - // @ts-ignore - sourceElement: Element, - // @ts-ignore - targetElement: Element - ) {} + render() {} } diff --git a/src/arrows/spatial-connection.ts b/src/arrows/spatial-connection.ts index f10806a..410616e 100644 --- a/src/arrows/spatial-connection.ts +++ b/src/arrows/spatial-connection.ts @@ -53,8 +53,10 @@ export class SpatialConnection extends AbstractArrow { }, }; - render(sourceRect: DOMRectReadOnly, targetRect: DOMRectReadOnly) { - const [sx, sy, cx, cy, ex, ey, ae] = getBoxToBoxArrow( + render() { + const { sourceRect, targetRect } = this; + + const [sx, sy, cx, cy, ex, ey] = getBoxToBoxArrow( sourceRect.x, sourceRect.y, sourceRect.width,