refactor toolset to use private class fields
This commit is contained in:
parent
f80e9f2d9a
commit
5a597798c8
|
|
@ -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);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue