diff --git a/labs/folk-toolset.ts b/labs/folk-toolset.ts index 5653e61..7e62c80 100644 --- a/labs/folk-toolset.ts +++ b/labs/folk-toolset.ts @@ -2,9 +2,17 @@ import { Vector } from '@lib'; import { FolkEventPropagator } from './folk-event-propagator'; import { FolkShape } from './folk-shape'; -export abstract class FolkInteractionHandler extends HTMLElement { - abstract readonly events: string[]; - abstract handleEvent(event: Event): void; +export class FolkInteractionHandler extends HTMLElement { + static tagName = ''; + + static define() { + if (!customElements.get(this.tagName)) return + customElements.define(this.tagName, this); + } + + readonly events: string[] = []; + + handleEvent(event: Event): void {} static toolbar: FolkToolset | null = null; @@ -40,10 +48,11 @@ export abstract class FolkInteractionHandler extends HTMLElement { export class FolkPropagatorTool extends FolkInteractionHandler { static tagName = 'folk-propagator-tool'; + readonly events = ['pointerdown', 'pointermove', 'pointerup']; - private currentPropagator: FolkEventPropagator | null = null; - private startPoint: { x: number; y: number } | null = null; + #currentPropagator: FolkEventPropagator | null = null; + #startPoint: { x: number; y: number } | null = null; constructor() { super(); @@ -60,36 +69,36 @@ export class FolkPropagatorTool extends FolkInteractionHandler { event.stopImmediatePropagation(); event.preventDefault(); - this.startPoint = { x: event.clientX, y: event.clientY }; + this.#startPoint = { x: event.clientX, y: event.clientY }; if (!target.id) { target.id = `folk-source-${Date.now()}`; } - this.currentPropagator = new FolkEventPropagator(); - this.currentPropagator.source = `#${target.id}`; + this.#currentPropagator = new FolkEventPropagator(); + this.#currentPropagator.source = `#${target.id}`; - document.body.appendChild(this.currentPropagator); + document.body.appendChild(this.#currentPropagator); break; case 'pointermove': - if (!this.currentPropagator) return; + if (!this.#currentPropagator) return; event.stopImmediatePropagation(); // Update the target position to follow the mouse const rect = document.body.getBoundingClientRect(); const targetPoint = `${event.clientX - rect.left}, ${event.clientY - rect.top}`; - this.currentPropagator.target = targetPoint; - this.currentPropagator.stretch(); + this.#currentPropagator.target = targetPoint; + this.#currentPropagator.stretch(); break; case 'pointerup': - if (!this.currentPropagator) return; + if (!this.#currentPropagator) return; event.stopImmediatePropagation(); const finalTarget = document.elementFromPoint(event.clientX, event.clientY) as HTMLElement; - const distance = Vector.distance(this.startPoint || { x: 0, y: 0 }, { x: event.clientX, y: event.clientY }); + const distance = Vector.distance(this.#startPoint || { x: 0, y: 0 }, { x: event.clientX, y: event.clientY }); if ( !finalTarget || @@ -97,40 +106,33 @@ export class FolkPropagatorTool extends FolkInteractionHandler { finalTarget instanceof FolkInteractionHandler || distance <= 1 ) { - this.currentPropagator.remove(); + this.#currentPropagator.remove(); } else { if (!finalTarget.id) { finalTarget.id = `folk-target-${Date.now()}`; } - this.currentPropagator.target = `#${finalTarget.id}`; + this.#currentPropagator.target = `#${finalTarget.id}`; } - this.currentPropagator.trigger = 'transform'; - this.currentPropagator.expression = 'x: from.y'; - this.currentPropagator = null; + this.#currentPropagator.trigger = 'transform'; + this.#currentPropagator.expression = 'x: from.y'; + this.#currentPropagator = null; break; } } - - static define() { - if (!customElements.get(this.tagName)) { - customElements.define(this.tagName, this); - } - } } -// Add this line at the bottom of the file with the other define() calls - export class FolkShapeTool extends FolkInteractionHandler { static tagName = 'folk-shape-tool'; readonly events = ['pointerdown', 'pointermove', 'pointerup']; - private currentShape: FolkShape | null = null; - private startPoint: { x: number; y: number } | null = null; + #currentShape: FolkShape | null = null; + #startPoint: { x: number; y: number } | null = null; constructor() { super(); + this.button.textContent = 'Create Shape'; } @@ -144,23 +146,23 @@ export class FolkShapeTool extends FolkInteractionHandler { event.stopImmediatePropagation(); const rect = target.getBoundingClientRect(); - this.startPoint = { + this.#startPoint = { x: event.clientX - rect.left, y: event.clientY - rect.top, }; - this.currentShape = new FolkShape(); - this.currentShape.x = this.startPoint.x; - this.currentShape.y = this.startPoint.y; - this.currentShape.width = 0; - this.currentShape.height = 0; + this.#currentShape = new FolkShape(); + this.#currentShape.x = this.#startPoint.x; + this.#currentShape.y = this.#startPoint.y; + this.#currentShape.width = 0; + this.#currentShape.height = 0; - target.appendChild(this.currentShape); + target.appendChild(this.#currentShape); target.setPointerCapture(event.pointerId); break; case 'pointermove': - if (!this.currentShape || !this.startPoint) return; + if (!this.#currentShape || !this.#startPoint) return; event.stopImmediatePropagation(); const rect2 = target.getBoundingClientRect(); @@ -168,53 +170,47 @@ export class FolkShapeTool extends FolkInteractionHandler { const currentY = event.clientY - rect2.top; // Calculate width and height based on drag direction - const width = currentX - this.startPoint.x; - const height = currentY - this.startPoint.y; + const width = currentX - this.#startPoint.x; + const height = currentY - this.#startPoint.y; // Update shape position and size based on drag direction if (width < 0) { - this.currentShape.x = currentX; - this.currentShape.width = Math.abs(width); + this.#currentShape.x = currentX; + this.#currentShape.width = Math.abs(width); } else { - this.currentShape.width = width; + this.#currentShape.width = width; } if (height < 0) { - this.currentShape.y = currentY; - this.currentShape.height = Math.abs(height); + this.#currentShape.y = currentY; + this.#currentShape.height = Math.abs(height); } else { - this.currentShape.height = height; + this.#currentShape.height = height; } break; case 'pointerup': - if (!this.currentShape) return; + if (!this.#currentShape) return; event.stopImmediatePropagation(); // If the shape is too small (meaning almost no drag occurred) // create a default sized shape instead - if (this.currentShape.width <= 1 || this.currentShape.height <= 1) { + if (this.#currentShape.width <= 1 || this.#currentShape.height <= 1) { const defaultSize = 100; - this.currentShape.width = defaultSize; - this.currentShape.height = defaultSize; + this.#currentShape.width = defaultSize; + this.#currentShape.height = defaultSize; // Center the shape on the click point - this.currentShape.x = this.startPoint!.x - defaultSize / 2; - this.currentShape.y = this.startPoint!.y - defaultSize / 2; + this.#currentShape.x = this.#startPoint!.x - defaultSize / 2; + this.#currentShape.y = this.#startPoint!.y - defaultSize / 2; } - this.currentShape.focus(); + this.#currentShape.focus(); target.releasePointerCapture(event.pointerId); - this.currentShape = null; - this.startPoint = null; + this.#currentShape = null; + this.#startPoint = null; break; } } - - static define() { - if (!customElements.get(this.tagName)) { - customElements.define(this.tagName, this); - } - } } export class FolkDeleteTool extends FolkInteractionHandler { @@ -233,66 +229,62 @@ export class FolkDeleteTool extends FolkInteractionHandler { event.stopImmediatePropagation(); target.remove(); } - - static define() { - if (!customElements.get(this.tagName)) { - customElements.define(this.tagName, this); - } - } } export class FolkToolset extends HTMLElement { static tagName = 'folk-toolset'; - private static instance: FolkToolset | null = null; - private currentHandler: ((event: Event) => void) | null = null; - private activeTool: FolkInteractionHandler | null = null; + + static #instance: FolkToolset | null = null; + + #currentHandler: ((event: Event) => void) | null = null; + #activeTool: FolkInteractionHandler | null = null; static setActiveTool(tool: FolkInteractionHandler) { - if (this.instance) { - this.instance.activateTool(tool); + if (this.#instance) { + this.#instance.#activateTool(tool); } } constructor() { super(); - FolkToolset.instance = this; + + FolkToolset.#instance = this; } - private activateTool(tool: FolkInteractionHandler) { + #activateTool(tool: FolkInteractionHandler) { // Remove active class from previous tool - if (this.activeTool) { - this.activeTool.classList.remove('active'); + if (this.#activeTool) { + this.#activeTool.classList.remove('active'); } // Deactivate current handler - if (this.currentHandler) { + if (this.#currentHandler) { tool.events.forEach((event) => { - this.removeEventListener(event, this.currentHandler!, true); + this.removeEventListener(event, this.#currentHandler!, true); }); } // If clicking same tool, just deactivate - if (this.activeTool === tool) { - this.currentHandler = null; - this.activeTool = null; + if (this.#activeTool === tool) { + this.#currentHandler = null; + this.#activeTool = null; return; } // Activate new handler - this.currentHandler = tool.handleEvent.bind(tool); + this.#currentHandler = tool.handleEvent.bind(tool); tool.events.forEach((event) => { - this.addEventListener(event, this.currentHandler!, true); + this.addEventListener(event, this.#currentHandler!, true); }); // Add active class to new tool tool.classList.add('active'); - this.activeTool = tool; + this.#activeTool = tool; } static define() { - if (!customElements.get(this.tagName)) { - customElements.define(this.tagName, this); - } + if (!customElements.get(this.tagName)) return; + customElements.define(this.tagName, this); } }