146 lines
3.6 KiB
TypeScript
146 lines
3.6 KiB
TypeScript
import { css, PropertyValues } from '@lit/reactive-element';
|
|
import { FolkRope } from './folk-rope.ts';
|
|
import { property } from '@lit/reactive-element/decorators.js';
|
|
import { Propagator } from '@propagators';
|
|
|
|
export class FolkEventPropagator extends FolkRope {
|
|
static override tagName = 'folk-event-propagator';
|
|
|
|
static styles = [
|
|
...FolkRope.styles,
|
|
css`
|
|
.input-container {
|
|
position: absolute;
|
|
display: flex;
|
|
flex-direction: column;
|
|
translate: -50% -50%;
|
|
}
|
|
|
|
textarea {
|
|
width: auto;
|
|
min-width: 3ch;
|
|
height: auto;
|
|
resize: none;
|
|
background: rgba(256, 256, 256, 0.8);
|
|
border: 1px solid #ccc;
|
|
padding: 4px;
|
|
pointer-events: auto;
|
|
overflow: hidden;
|
|
field-sizing: content;
|
|
box-sizing: content-box;
|
|
}
|
|
|
|
.trigger {
|
|
border-radius: 5px 5px 0 0;
|
|
border-bottom: none;
|
|
width: fit-content;
|
|
}
|
|
|
|
.expression {
|
|
border-radius: 0 5px 5px 5px;
|
|
}
|
|
`,
|
|
];
|
|
|
|
@property({ type: String, reflect: true }) trigger?: string;
|
|
@property({ type: String, reflect: true }) expression?: string;
|
|
|
|
#triggerTextarea = document.createElement('textarea');
|
|
#expressionTextarea = document.createElement('textarea');
|
|
#propagator: Propagator | null = null;
|
|
#container = document.createElement('div');
|
|
#hasError = false;
|
|
|
|
override createRenderRoot() {
|
|
const root = super.createRenderRoot();
|
|
|
|
this.#container.className = 'input-container';
|
|
this.#triggerTextarea.className = 'trigger';
|
|
this.#expressionTextarea.className = 'expression';
|
|
|
|
this.#triggerTextarea.addEventListener('change', () => {
|
|
this.trigger = this.#triggerTextarea.value;
|
|
});
|
|
|
|
this.#expressionTextarea.addEventListener('input', () => {
|
|
this.expression = this.#expressionTextarea.value;
|
|
});
|
|
|
|
this.#expressionTextarea.addEventListener('focusout', () => {
|
|
if (this.#hasError) {
|
|
super.cut();
|
|
}
|
|
});
|
|
|
|
this.#triggerTextarea.value = this.trigger ?? '';
|
|
this.#expressionTextarea.value = this.expression ?? '';
|
|
|
|
this.#container.append(this.#triggerTextarea, this.#expressionTextarea);
|
|
|
|
root.append(this.#container);
|
|
|
|
return root;
|
|
}
|
|
|
|
override updated(changedProperties: PropertyValues<this>): void {
|
|
super.update(changedProperties);
|
|
|
|
if (changedProperties.has('trigger')) {
|
|
this.#triggerTextarea.value = this.trigger ?? '';
|
|
}
|
|
if (changedProperties.has('expression')) {
|
|
this.#expressionTextarea.value = this.expression ?? '';
|
|
}
|
|
|
|
if (changedProperties.has('trigger') || changedProperties.has('expression')) {
|
|
this.#initializePropagator();
|
|
}
|
|
}
|
|
|
|
override disconnectedCallback(): void {
|
|
super.disconnectedCallback();
|
|
this.#propagator?.dispose();
|
|
}
|
|
|
|
#initializePropagator() {
|
|
this.#propagator?.dispose();
|
|
this.#propagator = new Propagator({
|
|
source: this.sourceElement,
|
|
target: this.targetElement,
|
|
event: this.trigger,
|
|
handler: this.expression,
|
|
onParseError: () => {
|
|
this.#hasError = true;
|
|
},
|
|
onParseSuccess: () => {
|
|
if (this.#hasError) {
|
|
super.mend();
|
|
}
|
|
this.#hasError = false;
|
|
},
|
|
});
|
|
}
|
|
|
|
override render() {
|
|
super.render();
|
|
|
|
const point = this.getPointAt(0.5);
|
|
if (point) {
|
|
this.#container.style.left = `${point.pos.x}px`;
|
|
this.#container.style.top = `${point.pos.y}px`;
|
|
}
|
|
}
|
|
|
|
override cut(atPercentage?: number): void {
|
|
super.cut(atPercentage);
|
|
|
|
this.#propagator?.dispose();
|
|
}
|
|
|
|
override mend(): void {
|
|
super.mend();
|
|
|
|
this.#initializePropagator();
|
|
}
|
|
}
|