refactor toolset to use private class fields

This commit is contained in:
“chrisshank” 2024-12-19 00:38:22 -08:00
parent f80e9f2d9a
commit 5a597798c8
1 changed files with 78 additions and 86 deletions

View File

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