Skip to content

Commit ea5e385

Browse files
common-frp-lit (#35)
Introduces watch async directive to watch a common-frp signal within an HTML template. Also changes common-frp effect to support multiple signals (this will be the most common case).
1 parent e5218ff commit ea5e385

File tree

7 files changed

+218
-12
lines changed

7 files changed

+218
-12
lines changed

typescript/package-lock.json

Lines changed: 24 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

typescript/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"./packages/example-module:build",
3737
"./packages/lookslike-prototype:build",
3838
"./packages/common-frp:build",
39+
"./packages/common-frp-lit:build",
3940
"./common/data:build",
4041
"./common/io:build",
4142
"./common/module:build",
@@ -52,6 +53,7 @@
5253
"./packages/example-module:clean",
5354
"./packages/lookslike-prototype:clean",
5455
"./packages/common-frp:clean",
56+
"./packages/common-frp-lit:clean",
5557
"./common/data:clean",
5658
"./common/io:clean",
5759
"./common/module:clean",
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
{
2+
"name": "@commontools/common-frp-lit",
3+
"author": "The Common Authors",
4+
"version": "0.0.1",
5+
"description": "common-frp integration for Lit",
6+
"license": "MIT",
7+
"private": true,
8+
"type": "module",
9+
"scripts": {
10+
"build": "wireit",
11+
"clean": "wireit"
12+
},
13+
"repository": {
14+
"type": "git",
15+
"url": "git+https://github.com/commontoolsinc/labs.git"
16+
},
17+
"bugs": {
18+
"url": "https://github.com/commontoolsinc/labs/issues"
19+
},
20+
"homepage": "https://github.com/commontoolsinc/labs#readme",
21+
"exports": "./lib/index.js",
22+
"files": [
23+
"./lib/index.js"
24+
],
25+
"dependencies": {
26+
"lit": "^3.1.4"
27+
},
28+
"devDependencies": {
29+
"tslib": "^2.6.2",
30+
"typescript": "^5.2.2",
31+
"vite": "^5.2.13",
32+
"wireit": "^0.14.4"
33+
},
34+
"wireit": {
35+
"build": {
36+
"dependencies": [
37+
"../common-frp:build"
38+
],
39+
"files": [
40+
"./src/**/*"
41+
],
42+
"output": [
43+
"./lib/**/*"
44+
],
45+
"command": "tsc --build -f"
46+
},
47+
"clean": {
48+
"command": "rm -rf ./lib ./.wireit"
49+
}
50+
}
51+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { signal } from "@commontools/common-frp";
2+
import {directive} from 'lit/directive.js';
3+
import {AsyncDirective} from 'lit/async-directive.js';
4+
5+
const {state, effect} = signal;
6+
7+
class WatchDirective extends AsyncDirective {
8+
#cancel: (() => void) | undefined = undefined;
9+
#isWatching;
10+
11+
constructor(part: any) {
12+
super(part);
13+
this.#isWatching = state(true);
14+
}
15+
16+
override render(signal: any) {
17+
this.#cancel?.();
18+
this.#cancel = effect([this.#isWatching, signal], (isWatching, value) => {
19+
if (isWatching) {
20+
this.setValue(value);
21+
}
22+
});
23+
return signal.get();
24+
}
25+
26+
protected override disconnected(): void {
27+
this.#isWatching.send(false);
28+
}
29+
30+
protected override reconnected(): void {
31+
this.#isWatching.send(true);
32+
}
33+
}
34+
35+
/**
36+
* Renders a signal and subscribes to it, updating the part when the signal
37+
* changes.
38+
*/
39+
export const watch = directive(WatchDirective);
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"extends": "../../tsconfig.base.json",
3+
"compilerOptions": {
4+
"lib": ["es2022", "DOM"],
5+
"outDir": "./lib",
6+
"rootDir": "./src",
7+
"strict": false,
8+
"paths": {
9+
"common:module/module@0.0.1": [
10+
"../../node_modules/@commontools/module/lib/index.d.ts"
11+
],
12+
"common:io/state@0.0.1": [
13+
"../../node_modules/@commontools/io/lib/index.d.ts"
14+
]
15+
}
16+
},
17+
"include": [
18+
"src/**/*",
19+
]
20+
}

typescript/packages/common-frp/src/example/basic.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const clicks = events(button, 'click')
1111

1212
const clickCount = scan(clicks, (state, _) => state + 1, 0)
1313

14-
effect(clickCount, x => {
14+
effect([clickCount], x => {
1515
button.textContent = `Clicks: ${x}`
1616
console.log(x)
1717
})

typescript/packages/common-frp/src/signal.ts

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,90 @@ const sample = <T>(container: Gettable<T>) => container.get()
8282
export type Signal<T> = Gettable<T> & Updates<void> & Cancellable
8383
export type SignalSubject<T> = Gettable<T> & Updates<void> & Subject<T>
8484

85+
export type Effect = {
86+
<A>(
87+
upstreams: [Signal<A>],
88+
perform: (a: A) => void
89+
): Cancel
90+
91+
<A, B>(
92+
upstreams: [Signal<A>, Signal<B>],
93+
perform: (a: A, b: B) => void
94+
): Cancel
95+
96+
<A, B, C>(
97+
upstreams: [Signal<A>, Signal<B>, Signal<C>],
98+
perform: (a: A, b: B, c: C) => void
99+
): Cancel
100+
101+
<A, B, C, D>(
102+
upstreams: [Signal<A>, Signal<B>, Signal<C>, Signal<D>],
103+
perform: (a: A, b: B, c: C, d: D) => void
104+
): Cancel
105+
106+
<A, B, C, D, E>(
107+
upstreams: [Signal<A>, Signal<B>, Signal<C>, Signal<D>, Signal<E>],
108+
perform: (a: A, b: B, c: C, d: D, e: E) => void
109+
): Cancel
110+
111+
<A, B, C, D, E, F>(
112+
upstreams: [
113+
Signal<A>,
114+
Signal<B>,
115+
Signal<C>,
116+
Signal<D>,
117+
Signal<E>,
118+
Signal<F>
119+
],
120+
perform: (a: A, b: B, c: C, d: D, e: E, f: F) => void
121+
): Cancel
122+
123+
<A, B, C, D, E, F, G>(
124+
upstreams: [
125+
Signal<A>,
126+
Signal<B>,
127+
Signal<C>,
128+
Signal<D>,
129+
Signal<E>,
130+
Signal<F>,
131+
Signal<G>
132+
],
133+
perform: (a: A, b: B, c: C, d: D, e: E, f: F, g: G) => void
134+
): Cancel
135+
136+
<A, B, C, D, E, F, G, H>(
137+
upstreams: [
138+
Signal<A>,
139+
Signal<B>,
140+
Signal<C>,
141+
Signal<D>,
142+
Signal<E>,
143+
Signal<F>,
144+
Signal<G>,
145+
Signal<H>
146+
],
147+
perform: (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H) => void
148+
): Cancel
149+
150+
(
151+
upstreams: Array<Signal<any>>,
152+
perform: (...values: Array<any>) => void
153+
): Cancel
154+
}
155+
85156
/** React to a signal, producing an effect any time it changes */
86-
export const effect = <T>(
87-
signal: Signal<T>,
88-
effect: Send<T>
157+
export const effect: Effect = (
158+
upstreams: Array<Signal<any>>,
159+
perform: (...values: Array<any>) => void
89160
) => {
90-
const job = () => effect(signal.get())
161+
const job = () => perform(...upstreams.map(sample))
162+
const schedule = () => withReads(job)
163+
91164
job()
92-
return signal[__updates__](() => withReads(job))
165+
166+
return combineCancels(
167+
upstreams.map(signal => signal[__updates__](schedule))
168+
)
93169
}
94170

95171
export const state = <T>(initial: T) => {

0 commit comments

Comments
 (0)