refactor event propagator to lit
This commit is contained in:
parent
24b9442a43
commit
fb3895e0d4
|
|
@ -41,14 +41,14 @@
|
||||||
<folk-event-propagator
|
<folk-event-propagator
|
||||||
source="folk-metronome"
|
source="folk-metronome"
|
||||||
target="#kick"
|
target="#kick"
|
||||||
triggers="beat"
|
trigger="beat"
|
||||||
expression="play(): from.beat === 1"
|
expression="play(): from.beat === 1"
|
||||||
></folk-event-propagator>
|
></folk-event-propagator>
|
||||||
|
|
||||||
<folk-event-propagator
|
<folk-event-propagator
|
||||||
source="folk-metronome"
|
source="folk-metronome"
|
||||||
target="#hat"
|
target="#hat"
|
||||||
triggers="beat"
|
trigger="beat"
|
||||||
expression="play(): from.beat % 3"
|
expression="play(): from.beat % 3"
|
||||||
></folk-event-propagator>
|
></folk-event-propagator>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -90,14 +90,14 @@
|
||||||
<folk-event-propagator
|
<folk-event-propagator
|
||||||
source="#recipe"
|
source="#recipe"
|
||||||
target="folk-llm"
|
target="folk-llm"
|
||||||
triggers="click"
|
trigger="click"
|
||||||
expression="prompt: `double this list of ingredients '${from.innerHTML}'`"
|
expression="prompt: `double this list of ingredients '${from.innerHTML}'`"
|
||||||
></folk-event-propagator>
|
></folk-event-propagator>
|
||||||
|
|
||||||
<folk-event-propagator
|
<folk-event-propagator
|
||||||
source="folk-llm"
|
source="folk-llm"
|
||||||
target="folk-timer"
|
target="folk-timer"
|
||||||
triggers="started"
|
trigger="started"
|
||||||
expression="reset(): true
|
expression="reset(): true
|
||||||
start(): true"
|
start(): true"
|
||||||
>
|
>
|
||||||
|
|
@ -106,7 +106,7 @@ start(): true"
|
||||||
<folk-event-propagator
|
<folk-event-propagator
|
||||||
source="folk-llm"
|
source="folk-llm"
|
||||||
target="folk-timer"
|
target="folk-timer"
|
||||||
triggers="finished"
|
trigger="finished"
|
||||||
expression="stop(): true"
|
expression="stop(): true"
|
||||||
></folk-event-propagator>
|
></folk-event-propagator>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@
|
||||||
<folk-event-propagator
|
<folk-event-propagator
|
||||||
source="#box1"
|
source="#box1"
|
||||||
target="#box2"
|
target="#box2"
|
||||||
triggers="click"
|
trigger="click"
|
||||||
expression="textContent: to.textContent + 'r'"
|
expression="textContent: to.textContent + 'r'"
|
||||||
></folk-event-propagator>
|
></folk-event-propagator>
|
||||||
|
|
||||||
|
|
@ -48,13 +48,12 @@
|
||||||
<folk-event-propagator
|
<folk-event-propagator
|
||||||
source="#box3"
|
source="#box3"
|
||||||
target="#box4"
|
target="#box4"
|
||||||
triggers="transform"
|
trigger="transform"
|
||||||
expression="y: from.x,
|
expression="y: from.x,
|
||||||
rotation: from.x"
|
rotation: from.x"
|
||||||
></folk-event-propagator>
|
></folk-event-propagator>
|
||||||
|
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import 'https://cdn.jsdelivr.net/npm/@zachleat/snow-fall';
|
|
||||||
import '../src/standalone/folk-shape.ts';
|
import '../src/standalone/folk-shape.ts';
|
||||||
import '../src/standalone/folk-event-propagator.ts';
|
import '../src/standalone/folk-event-propagator.ts';
|
||||||
|
|
||||||
|
|
@ -80,6 +79,8 @@ rotation: from.x"
|
||||||
}
|
}
|
||||||
isBlowing = !isBlowing;
|
isBlowing = !isBlowing;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
requestIdleCallback(() => import('https://cdn.jsdelivr.net/npm/@zachleat/snow-fall'));
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
<folk-event-propagator
|
<folk-event-propagator
|
||||||
source="#box1"
|
source="#box1"
|
||||||
target="#box2"
|
target="#box2"
|
||||||
triggers="click"
|
trigger="click"
|
||||||
expression="textContent: to.textContent + '!'"
|
expression="textContent: to.textContent + '!'"
|
||||||
></folk-event-propagator>
|
></folk-event-propagator>
|
||||||
|
|
||||||
|
|
@ -39,7 +39,7 @@
|
||||||
<folk-event-propagator
|
<folk-event-propagator
|
||||||
source="#box3"
|
source="#box3"
|
||||||
target="#box4"
|
target="#box4"
|
||||||
triggers="transform"
|
trigger="transform"
|
||||||
expression="y: from.x,
|
expression="y: from.x,
|
||||||
rotation: from.x"
|
rotation: from.x"
|
||||||
></folk-event-propagator>
|
></folk-event-propagator>
|
||||||
|
|
|
||||||
|
|
@ -52,21 +52,21 @@
|
||||||
<folk-event-propagator
|
<folk-event-propagator
|
||||||
source="textarea"
|
source="textarea"
|
||||||
target="sl-qr-code"
|
target="sl-qr-code"
|
||||||
triggers="input"
|
trigger="input"
|
||||||
expression="value: from.value"
|
expression="value: from.value"
|
||||||
></folk-event-propagator>
|
></folk-event-propagator>
|
||||||
|
|
||||||
<folk-event-propagator
|
<folk-event-propagator
|
||||||
source="input[type='range']"
|
source="input[type='range']"
|
||||||
target="sl-qr-code"
|
target="sl-qr-code"
|
||||||
triggers="input"
|
trigger="input"
|
||||||
expression="radius: from.value"
|
expression="radius: from.value"
|
||||||
></folk-event-propagator>
|
></folk-event-propagator>
|
||||||
|
|
||||||
<folk-event-propagator
|
<folk-event-propagator
|
||||||
source="input[type='color']"
|
source="input[type='color']"
|
||||||
target="sl-qr-code"
|
target="sl-qr-code"
|
||||||
triggers="input"
|
trigger="input"
|
||||||
expression="fill: from.value"
|
expression="fill: from.value"
|
||||||
></folk-event-propagator>
|
></folk-event-propagator>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,14 +76,14 @@
|
||||||
<folk-event-propagator
|
<folk-event-propagator
|
||||||
source="input[type='range']"
|
source="input[type='range']"
|
||||||
target="folk-map"
|
target="folk-map"
|
||||||
triggers="input"
|
trigger="input"
|
||||||
expression="lat: from.value"
|
expression="lat: from.value"
|
||||||
></folk-event-propagator>
|
></folk-event-propagator>
|
||||||
|
|
||||||
<folk-event-propagator
|
<folk-event-propagator
|
||||||
source="folk-map"
|
source="folk-map"
|
||||||
target="input[type='range']"
|
target="input[type='range']"
|
||||||
triggers="recenter"
|
trigger="recenter"
|
||||||
expression="value: from.lat"
|
expression="value: from.lat"
|
||||||
></folk-event-propagator>
|
></folk-event-propagator>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -143,21 +143,21 @@
|
||||||
<folk-event-propagator
|
<folk-event-propagator
|
||||||
source="#map1"
|
source="#map1"
|
||||||
target="folk-cell[column='A'][row='1']"
|
target="folk-cell[column='A'][row='1']"
|
||||||
triggers="recenter"
|
trigger="recenter"
|
||||||
expression="expression: from.coordinates.lat"
|
expression="expression: from.coordinates.lat"
|
||||||
></folk-event-propagator>
|
></folk-event-propagator>
|
||||||
|
|
||||||
<folk-event-propagator
|
<folk-event-propagator
|
||||||
source="#map1"
|
source="#map1"
|
||||||
target="folk-cell[column='A'][row='2']"
|
target="folk-cell[column='A'][row='2']"
|
||||||
triggers="recenter"
|
trigger="recenter"
|
||||||
expression="expression: from.coordinates.lng"
|
expression="expression: from.coordinates.lng"
|
||||||
></folk-event-propagator>
|
></folk-event-propagator>
|
||||||
|
|
||||||
<folk-event-propagator
|
<folk-event-propagator
|
||||||
source="folk-cell[column='A'][row='3']"
|
source="folk-cell[column='A'][row='3']"
|
||||||
target="#map2"
|
target="#map2"
|
||||||
triggers="propagate"
|
trigger="propagate"
|
||||||
expression="coordinates: from.value"
|
expression="coordinates: from.value"
|
||||||
></folk-event-propagator>
|
></folk-event-propagator>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,7 @@ const folkObserver = new FolkObserver();
|
||||||
export class FolkBaseConnection extends FolkElement {
|
export class FolkBaseConnection extends FolkElement {
|
||||||
@property({ type: String, reflect: true }) source = '';
|
@property({ type: String, reflect: true }) source = '';
|
||||||
|
|
||||||
#sourceElement: Element | null = null;
|
@state() sourceElement: Element | null = null;
|
||||||
|
|
||||||
get sourceElement() {
|
|
||||||
return this.#sourceElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
@state() sourceRect: DOMRectReadOnly | null = null;
|
@state() sourceRect: DOMRectReadOnly | null = null;
|
||||||
|
|
||||||
|
|
@ -22,11 +18,7 @@ export class FolkBaseConnection extends FolkElement {
|
||||||
|
|
||||||
@state() targetRect: DOMRectReadOnly | null = null;
|
@state() targetRect: DOMRectReadOnly | null = null;
|
||||||
|
|
||||||
#targetElement: Element | null = null;
|
@state() targetElement: Element | null = null;
|
||||||
|
|
||||||
get targetElement() {
|
|
||||||
return this.#targetElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
override disconnectedCallback() {
|
override disconnectedCallback() {
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
|
|
@ -58,20 +50,20 @@ export class FolkBaseConnection extends FolkElement {
|
||||||
if (vertex) {
|
if (vertex) {
|
||||||
this.sourceRect = DOMRectReadOnly.fromRect(vertex);
|
this.sourceRect = DOMRectReadOnly.fromRect(vertex);
|
||||||
} else {
|
} else {
|
||||||
this.#sourceElement = document.querySelector(this.source);
|
this.sourceElement = document.querySelector(this.source);
|
||||||
|
|
||||||
if (this.#sourceElement === null) {
|
if (this.sourceElement === null) {
|
||||||
this.sourceRect = null;
|
this.sourceRect = null;
|
||||||
} else {
|
} else {
|
||||||
folkObserver.observe(this.#sourceElement, this.#sourceCallback);
|
folkObserver.observe(this.sourceElement, this.#sourceCallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unobserveSource() {
|
unobserveSource() {
|
||||||
if (this.#sourceElement === null) return;
|
if (this.sourceElement === null) return;
|
||||||
|
|
||||||
folkObserver.unobserve(this.#sourceElement, this.#sourceCallback);
|
folkObserver.unobserve(this.sourceElement, this.#sourceCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
#targetCallback = (entry: ClientRectObserverEntry) => {
|
#targetCallback = (entry: ClientRectObserverEntry) => {
|
||||||
|
|
@ -86,18 +78,18 @@ export class FolkBaseConnection extends FolkElement {
|
||||||
if (vertex) {
|
if (vertex) {
|
||||||
this.targetRect = DOMRectReadOnly.fromRect(vertex);
|
this.targetRect = DOMRectReadOnly.fromRect(vertex);
|
||||||
} else {
|
} else {
|
||||||
this.#targetElement = document.querySelector(this.target);
|
this.targetElement = document.querySelector(this.target);
|
||||||
|
|
||||||
if (this.#targetElement === null) {
|
if (this.targetElement === null) {
|
||||||
this.targetRect = null;
|
this.targetRect = null;
|
||||||
} else {
|
} else {
|
||||||
folkObserver.observe(this.#targetElement, this.#targetCallback);
|
folkObserver.observe(this.targetElement, this.#targetCallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unobserveTarget() {
|
unobserveTarget() {
|
||||||
if (this.#targetElement === null) return;
|
if (this.targetElement === null) return;
|
||||||
folkObserver.unobserve(this.#targetElement, this.#targetCallback);
|
folkObserver.unobserve(this.targetElement, this.#targetCallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { css, PropertyValues } from '@lit/reactive-element';
|
import { css, PropertyValues } from '@lit/reactive-element';
|
||||||
import { FolkRope } from './folk-rope.ts';
|
import { FolkRope } from './folk-rope.ts';
|
||||||
|
import { property } from '@lit/reactive-element/decorators.js';
|
||||||
// import * as parser from '@babel/parser';
|
// import * as parser from '@babel/parser';
|
||||||
|
|
||||||
export class FolkEventPropagator extends FolkRope {
|
export class FolkEventPropagator extends FolkRope {
|
||||||
|
|
@ -30,30 +31,77 @@ export class FolkEventPropagator extends FolkRope {
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
#triggers: string[] = [];
|
@property({ type: String, reflect: true }) trigger = '';
|
||||||
get triggers() {
|
|
||||||
return this.#triggers;
|
|
||||||
}
|
|
||||||
set triggers(triggers: string | string[]) {
|
|
||||||
if (typeof triggers === 'string') {
|
|
||||||
triggers = triggers.split(',');
|
|
||||||
}
|
|
||||||
this.#removeEventListenersToSource();
|
|
||||||
|
|
||||||
this.#triggers = triggers;
|
@property({ type: String, reflect: true }) expression = '';
|
||||||
|
|
||||||
this.#addEventListenersToSource();
|
|
||||||
}
|
|
||||||
|
|
||||||
#expression = '';
|
|
||||||
#function: Function | null = null;
|
#function: Function | null = null;
|
||||||
get expression() {
|
#triggerTextarea = document.createElement('textarea');
|
||||||
return this.#expression;
|
#expressionTextarea = document.createElement('textarea');
|
||||||
|
|
||||||
|
override firstUpdated(changedProperties: PropertyValues<this>): void {
|
||||||
|
super.firstUpdated(changedProperties);
|
||||||
|
|
||||||
|
this.#triggerTextarea.addEventListener('change', () => {
|
||||||
|
this.trigger = this.#triggerTextarea.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.#expressionTextarea.addEventListener('input', () => {
|
||||||
|
this.expression = this.#expressionTextarea.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.#triggerTextarea.value = this.trigger;
|
||||||
|
|
||||||
|
this.#expressionTextarea.value = this.expression;
|
||||||
|
|
||||||
|
this.renderRoot.append(this.#triggerTextarea, this.#expressionTextarea);
|
||||||
}
|
}
|
||||||
set expression(expression) {
|
|
||||||
this.mend();
|
override updated(changedProperties: PropertyValues<this>): void {
|
||||||
this.#expression = expression;
|
super.update(changedProperties);
|
||||||
const processedExp = expression.trim();
|
|
||||||
|
if (changedProperties.has('trigger')) {
|
||||||
|
this.sourceElement?.removeEventListener(this.trigger, this.#evaluateExpression);
|
||||||
|
this.sourceElement?.addEventListener(this.trigger, this.#evaluateExpression);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changedProperties.has('expression')) {
|
||||||
|
this.#parseExpression();
|
||||||
|
}
|
||||||
|
|
||||||
|
const previousSourceElement = changedProperties.get('sourceElement');
|
||||||
|
if (previousSourceElement) {
|
||||||
|
const trigger = changedProperties.get('trigger') || this.trigger;
|
||||||
|
previousSourceElement.removeEventListener(trigger, this.#evaluateExpression);
|
||||||
|
this.sourceElement?.addEventListener(this.trigger, this.#evaluateExpression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override disconnectedCallback(): void {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
this.sourceElement?.removeEventListener(this.trigger, this.#evaluateExpression);
|
||||||
|
}
|
||||||
|
|
||||||
|
override draw() {
|
||||||
|
super.draw();
|
||||||
|
|
||||||
|
const triggerPoint = this.points[Math.floor(this.points.length / 5)];
|
||||||
|
|
||||||
|
if (triggerPoint) {
|
||||||
|
this.#triggerTextarea.style.left = `${triggerPoint.pos.x}px`;
|
||||||
|
this.#triggerTextarea.style.top = `${triggerPoint.pos.y}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const expressionPoint = this.points[Math.floor(this.points.length / 2)];
|
||||||
|
|
||||||
|
if (expressionPoint) {
|
||||||
|
this.#expressionTextarea.style.left = `${expressionPoint.pos.x}px`;
|
||||||
|
this.#expressionTextarea.style.top = `${expressionPoint.pos.y}px`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#parseExpression() {
|
||||||
|
const processedExp = this.expression.trim();
|
||||||
|
|
||||||
const codeLines: string[] = [];
|
const codeLines: string[] = [];
|
||||||
|
|
||||||
|
|
@ -103,6 +151,7 @@ to.${key} = ${value};`);
|
||||||
try {
|
try {
|
||||||
// parseAst(functionBody);
|
// parseAst(functionBody);
|
||||||
this.#function = new Function('from', 'to', 'event', functionBody);
|
this.#function = new Function('from', 'to', 'event', functionBody);
|
||||||
|
this.mend();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('Failed to parse expression:', error, functionBody);
|
console.warn('Failed to parse expression:', error, functionBody);
|
||||||
this.cut();
|
this.cut();
|
||||||
|
|
@ -110,80 +159,8 @@ to.${key} = ${value};`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#triggerTextarea = document.createElement('textarea');
|
#evaluateExpression = (event?: Event) => {
|
||||||
#expressionTextarea = document.createElement('textarea');
|
console.log('eval');
|
||||||
|
|
||||||
override firstUpdated(changedProperties: PropertyValues<this>): void {
|
|
||||||
super.firstUpdated(changedProperties);
|
|
||||||
|
|
||||||
this.#triggerTextarea.addEventListener('change', () => {
|
|
||||||
this.triggers = this.#triggerTextarea.value;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.triggers = this.#triggerTextarea.value = this.getAttribute('triggers') || '';
|
|
||||||
|
|
||||||
this.renderRoot.appendChild(this.#triggerTextarea);
|
|
||||||
|
|
||||||
this.#expressionTextarea.addEventListener('input', () => {
|
|
||||||
this.expression = this.#expressionTextarea.value;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.renderRoot.appendChild(this.#expressionTextarea);
|
|
||||||
|
|
||||||
this.expression = this.#expressionTextarea.value = this.getAttribute('expression') || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
override draw() {
|
|
||||||
super.draw();
|
|
||||||
|
|
||||||
const triggerPoint = this.points[Math.floor(this.points.length / 5)];
|
|
||||||
|
|
||||||
if (triggerPoint) {
|
|
||||||
this.#triggerTextarea.style.left = `${triggerPoint.pos.x}px`;
|
|
||||||
this.#triggerTextarea.style.top = `${triggerPoint.pos.y}px`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const expressionPoint = this.points[Math.floor(this.points.length / 2)];
|
|
||||||
|
|
||||||
if (expressionPoint) {
|
|
||||||
this.#expressionTextarea.style.left = `${expressionPoint.pos.x}px`;
|
|
||||||
this.#expressionTextarea.style.top = `${expressionPoint.pos.y}px`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override observeSource() {
|
|
||||||
super.observeSource();
|
|
||||||
|
|
||||||
this.#addEventListenersToSource();
|
|
||||||
}
|
|
||||||
|
|
||||||
#addEventListenersToSource() {
|
|
||||||
for (const trigger of this.#triggers) {
|
|
||||||
// TODO: add special triggers for intersection, rAF, etc.
|
|
||||||
this.sourceElement?.addEventListener(trigger, this.evaluateExpression);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override unobserveSource() {
|
|
||||||
super.unobserveSource();
|
|
||||||
this.#removeEventListenersToSource();
|
|
||||||
}
|
|
||||||
|
|
||||||
#removeEventListenersToSource() {
|
|
||||||
for (const trigger of this.#triggers) {
|
|
||||||
this.sourceElement?.removeEventListener(trigger, this.evaluateExpression);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override observeTarget() {
|
|
||||||
super.observeTarget();
|
|
||||||
}
|
|
||||||
|
|
||||||
override unobserveTarget() {
|
|
||||||
super.unobserveTarget();
|
|
||||||
}
|
|
||||||
|
|
||||||
evaluateExpression = (event?: Event) => {
|
|
||||||
if (this.sourceElement === null || this.targetElement === null) return;
|
if (this.sourceElement === null || this.targetElement === null) return;
|
||||||
this.stroke = 'black';
|
this.stroke = 'black';
|
||||||
if (!this.#function) return;
|
if (!this.#function) return;
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ export class FolkToolbar extends HTMLElement {
|
||||||
<folk-event-propagator
|
<folk-event-propagator
|
||||||
source="#${sourceId}"
|
source="#${sourceId}"
|
||||||
target="#${targetId}"
|
target="#${targetId}"
|
||||||
triggers="click"
|
trigger="click"
|
||||||
expression="rotation: Math.random() * 360"
|
expression="rotation: Math.random() * 360"
|
||||||
></folk-event-propagator>
|
></folk-event-propagator>
|
||||||
`,
|
`,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue