@e280/sly 0.2.0-0 → 0.2.0-10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +414 -97
- package/package.json +4 -4
- package/s/demo/demo.bundle.ts +10 -6
- package/s/demo/views/counter.ts +22 -17
- package/s/demo/views/demo.ts +10 -6
- package/s/demo/views/fastcount.ts +29 -0
- package/s/demo/views/loaders.ts +2 -2
- package/s/dom/attrs/attrs.ts +21 -0
- package/s/dom/attrs/parts/attr-fns.ts +38 -0
- package/s/dom/attrs/parts/attr-proxies.ts +35 -0
- package/s/dom/attrs/parts/attr-spec.ts +29 -0
- package/s/dom/attrs/parts/on-attrs.ts +8 -0
- package/s/dom/dom.ts +73 -0
- package/s/dom/{register.ts → parts/register.ts} +2 -7
- package/s/dom/types.ts +40 -0
- package/s/index.html.ts +4 -2
- package/s/index.ts +10 -9
- package/s/loot/drag-and-drops.ts +82 -0
- package/s/loot/drops.ts +35 -0
- package/s/loot/helpers.ts +31 -0
- package/s/loot/index.ts +5 -0
- package/s/ops/loaders/make-loader.ts +3 -3
- package/s/ops/loaders/parts/anims.ts +1 -1
- package/s/ops/loaders/parts/ascii-anim.ts +3 -3
- package/s/ops/loaders/parts/error-display.ts +2 -2
- package/s/ops/op.ts +2 -2
- package/s/{views → ui/base}/use.ts +35 -17
- package/s/ui/base/utils/attr-watcher.ts +22 -0
- package/s/ui/base/utils/reactor.ts +21 -0
- package/s/ui/base-element.ts +76 -0
- package/s/ui/types.ts +33 -0
- package/s/ui/view/make-component.ts +33 -0
- package/s/ui/view/make-view.ts +40 -0
- package/s/{views/utils → ui/view/parts}/apply-attrs.ts +4 -7
- package/s/ui/view/parts/capsule.ts +67 -0
- package/s/ui/view/parts/chain.ts +33 -0
- package/s/ui/view/parts/context.ts +10 -0
- package/s/ui/view/parts/directive.ts +29 -0
- package/s/ui/view/parts/sly-view.ts +15 -0
- package/s/ui/view.ts +24 -0
- package/x/demo/demo.bundle.js +9 -5
- package/x/demo/demo.bundle.js.map +1 -1
- package/x/demo/demo.bundle.min.js +19 -17
- package/x/demo/demo.bundle.min.js.map +4 -4
- package/x/demo/views/counter.d.ts +374 -1
- package/x/demo/views/counter.js +19 -15
- package/x/demo/views/counter.js.map +1 -1
- package/x/demo/views/demo.d.ts +4 -1
- package/x/demo/views/demo.js +10 -5
- package/x/demo/views/demo.js.map +1 -1
- package/x/demo/views/fastcount.d.ts +12 -0
- package/x/demo/views/fastcount.js +21 -0
- package/x/demo/views/fastcount.js.map +1 -0
- package/x/demo/views/loaders.js +2 -2
- package/x/demo/views/loaders.js.map +1 -1
- package/x/dom/attrs/attrs.d.ts +20 -0
- package/x/dom/attrs/attrs.js +17 -0
- package/x/dom/attrs/attrs.js.map +1 -0
- package/x/dom/attrs/parts/attr-fns.d.ts +13 -0
- package/x/dom/attrs/parts/attr-fns.js +42 -0
- package/x/dom/attrs/parts/attr-fns.js.map +1 -0
- package/x/dom/attrs/parts/attr-proxies.d.ts +8 -0
- package/x/dom/attrs/parts/attr-proxies.js +21 -0
- package/x/dom/attrs/parts/attr-proxies.js.map +1 -0
- package/x/dom/attrs/parts/attr-spec.d.ts +3 -0
- package/x/dom/attrs/parts/attr-spec.js +21 -0
- package/x/dom/attrs/parts/attr-spec.js.map +1 -0
- package/x/dom/attrs/parts/on-attrs.d.ts +2 -0
- package/x/dom/attrs/parts/on-attrs.js +7 -0
- package/x/dom/attrs/parts/on-attrs.js.map +1 -0
- package/x/dom/dom.d.ts +32 -0
- package/x/dom/dom.js +54 -0
- package/x/dom/dom.js.map +1 -0
- package/x/dom/parts/dashify.js.map +1 -0
- package/x/dom/{register.d.ts → parts/register.d.ts} +2 -6
- package/x/dom/parts/register.js.map +1 -0
- package/x/dom/types.d.ts +14 -0
- package/x/index.d.ts +9 -8
- package/x/index.html +6 -4
- package/x/index.html.js +4 -2
- package/x/index.html.js.map +1 -1
- package/x/index.js +9 -8
- package/x/index.js.map +1 -1
- package/x/loot/drag-and-drops.d.ts +30 -0
- package/x/loot/drag-and-drops.js +63 -0
- package/x/loot/drag-and-drops.js.map +1 -0
- package/x/loot/drops.d.ts +14 -0
- package/x/loot/drops.js +25 -0
- package/x/loot/drops.js.map +1 -0
- package/x/loot/helpers.d.ts +3 -0
- package/x/loot/helpers.js +21 -0
- package/x/loot/helpers.js.map +1 -0
- package/x/loot/index.d.ts +3 -0
- package/x/loot/index.js +4 -0
- package/x/loot/index.js.map +1 -0
- package/x/ops/loaders/make-loader.d.ts +1 -1
- package/x/ops/loaders/make-loader.js +2 -2
- package/x/ops/loaders/make-loader.js.map +1 -1
- package/x/ops/loaders/parts/anims.d.ts +1 -1
- package/x/ops/loaders/parts/ascii-anim.d.ts +2 -2
- package/x/ops/loaders/parts/ascii-anim.js +2 -2
- package/x/ops/loaders/parts/ascii-anim.js.map +1 -1
- package/x/ops/loaders/parts/error-display.js +2 -2
- package/x/ops/loaders/parts/error-display.js.map +1 -1
- package/x/ops/op.d.ts +2 -2
- package/x/ops/op.js +2 -2
- package/x/ops/op.js.map +1 -1
- package/x/ui/base/css-reset.js.map +1 -0
- package/x/{views → ui/base}/use.d.ts +12 -5
- package/x/{views → ui/base}/use.js +24 -10
- package/x/ui/base/use.js.map +1 -0
- package/x/ui/base/utils/apply-styles.js.map +1 -0
- package/x/ui/base/utils/attr-watcher.d.ts +8 -0
- package/x/ui/base/utils/attr-watcher.js +20 -0
- package/x/ui/base/utils/attr-watcher.js.map +1 -0
- package/x/ui/base/utils/mounts.js.map +1 -0
- package/x/ui/base/utils/reactor.d.ts +5 -0
- package/x/ui/base/utils/reactor.js +17 -0
- package/x/ui/base/utils/reactor.js.map +1 -0
- package/x/ui/base-element.d.ts +19 -0
- package/x/ui/base-element.js +52 -0
- package/x/ui/base-element.js.map +1 -0
- package/x/ui/types.d.ts +20 -0
- package/x/{views → ui}/types.js.map +1 -1
- package/x/ui/view/make-component.d.ts +5 -0
- package/x/ui/view/make-component.js +16 -0
- package/x/ui/view/make-component.js.map +1 -0
- package/x/ui/view/make-view.d.ts +2 -0
- package/x/ui/view/make-view.js +16 -0
- package/x/ui/view/make-view.js.map +1 -0
- package/x/ui/view/parts/apply-attrs.d.ts +2 -0
- package/x/{views/utils → ui/view/parts}/apply-attrs.js +2 -4
- package/x/ui/view/parts/apply-attrs.js.map +1 -0
- package/x/ui/view/parts/capsule.d.ts +13 -0
- package/x/ui/view/parts/capsule.js +49 -0
- package/x/ui/view/parts/capsule.js.map +1 -0
- package/x/ui/view/parts/chain.d.ts +11 -0
- package/x/ui/view/parts/chain.js +21 -0
- package/x/ui/view/parts/chain.js.map +1 -0
- package/x/ui/view/parts/context.d.ts +8 -0
- package/x/ui/view/parts/context.js +10 -0
- package/x/ui/view/parts/context.js.map +1 -0
- package/x/ui/view/parts/directive.d.ts +5 -0
- package/x/ui/view/parts/directive.js +18 -0
- package/x/ui/view/parts/directive.js.map +1 -0
- package/x/ui/view/parts/sly-view.d.ts +5 -0
- package/x/ui/view/parts/sly-view.js +13 -0
- package/x/ui/view/parts/sly-view.js.map +1 -0
- package/x/ui/view.d.ts +11 -0
- package/x/ui/view.js +15 -0
- package/x/ui/view.js.map +1 -0
- package/s/dom/dollar.ts +0 -27
- package/s/views/attributes.ts +0 -89
- package/s/views/types.ts +0 -40
- package/s/views/view.ts +0 -150
- package/x/dom/dashify.js.map +0 -1
- package/x/dom/dollar.d.ts +0 -10
- package/x/dom/dollar.js +0 -18
- package/x/dom/dollar.js.map +0 -1
- package/x/dom/register.js.map +0 -1
- package/x/views/attributes.d.ts +0 -10
- package/x/views/attributes.js +0 -46
- package/x/views/attributes.js.map +0 -1
- package/x/views/css-reset.js.map +0 -1
- package/x/views/types.d.ts +0 -31
- package/x/views/use.js.map +0 -1
- package/x/views/utils/apply-attrs.d.ts +0 -2
- package/x/views/utils/apply-attrs.js.map +0 -1
- package/x/views/utils/apply-styles.js.map +0 -1
- package/x/views/utils/mounts.js.map +0 -1
- package/x/views/view.d.ts +0 -9
- package/x/views/view.js +0 -116
- package/x/views/view.js.map +0 -1
- /package/s/dom/{dashify.ts → parts/dashify.ts} +0 -0
- /package/s/{views → ui/base}/css-reset.ts +0 -0
- /package/s/{views → ui/base}/utils/apply-styles.ts +0 -0
- /package/s/{views → ui/base}/utils/mounts.ts +0 -0
- /package/x/dom/{dashify.d.ts → parts/dashify.d.ts} +0 -0
- /package/x/dom/{dashify.js → parts/dashify.js} +0 -0
- /package/x/dom/{register.js → parts/register.js} +0 -0
- /package/x/{views → ui/base}/css-reset.d.ts +0 -0
- /package/x/{views → ui/base}/css-reset.js +0 -0
- /package/x/{views → ui/base}/utils/apply-styles.d.ts +0 -0
- /package/x/{views → ui/base}/utils/apply-styles.js +0 -0
- /package/x/{views → ui/base}/utils/mounts.d.ts +0 -0
- /package/x/{views → ui/base}/utils/mounts.js +0 -0
- /package/x/{views → ui}/types.js +0 -0
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import {css} from "lit"
|
|
3
3
|
import {nap, repeat} from "@e280/stz"
|
|
4
4
|
|
|
5
|
-
import {view} from "../../../
|
|
6
|
-
import {Content} from "../../../
|
|
7
|
-
import {cssReset} from "../../../
|
|
5
|
+
import {view} from "../../../ui/view.js"
|
|
6
|
+
import {Content} from "../../../ui/types.js"
|
|
7
|
+
import {cssReset} from "../../../ui/base/css-reset.js"
|
|
8
8
|
|
|
9
9
|
export function makeAsciiAnim(hz: number, frames: string[]): () => Content {
|
|
10
10
|
return () => AsciiAnim({hz, frames})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import {css, html} from "lit"
|
|
3
|
-
import {view} from "../../../
|
|
4
|
-
import {cssReset} from "../../../
|
|
3
|
+
import {view} from "../../../ui/view.js"
|
|
4
|
+
import {cssReset} from "../../../ui/base/css-reset.js"
|
|
5
5
|
|
|
6
6
|
export const ErrorDisplay = view(use => (error: any) => {
|
|
7
7
|
use.name("error")
|
package/s/ops/op.ts
CHANGED
|
@@ -16,7 +16,7 @@ export class Op<V> {
|
|
|
16
16
|
return op
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
static
|
|
19
|
+
static load<V>(fn: () => Promise<V>) {
|
|
20
20
|
return this.promise(fn())
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -74,7 +74,7 @@ export class Op<V> {
|
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
async
|
|
77
|
+
async load(fn: () => Promise<V>) {
|
|
78
78
|
return this.promise(fn())
|
|
79
79
|
}
|
|
80
80
|
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
|
|
2
2
|
import {CSSResultGroup} from "lit"
|
|
3
3
|
import {defer, MapG} from "@e280/stz"
|
|
4
|
-
import {signal} from "@e280/strata/signals"
|
|
4
|
+
import {signal, SignalOptions} from "@e280/strata/signals"
|
|
5
5
|
|
|
6
|
-
import {Op} from "
|
|
6
|
+
import {Op} from "../../ops/op.js"
|
|
7
|
+
import {dom} from "../../dom/dom.js"
|
|
8
|
+
import {Attrs} from "../../dom/types.js"
|
|
7
9
|
import {Mounts} from "./utils/mounts.js"
|
|
8
10
|
import {applyStyles} from "./utils/apply-styles.js"
|
|
9
|
-
import {attributes, AttrSpec, onAttrChange} from "./attributes.js"
|
|
10
11
|
|
|
11
12
|
export const _wrap = Symbol()
|
|
12
13
|
export const _disconnect = Symbol()
|
|
13
14
|
export const _reconnect = Symbol()
|
|
14
15
|
|
|
15
16
|
export class Use {
|
|
17
|
+
attrs: Attrs
|
|
18
|
+
|
|
16
19
|
#runs = 0
|
|
17
20
|
#position = 0
|
|
18
21
|
#values = new MapG<number, any>()
|
|
@@ -37,11 +40,13 @@ export class Use {
|
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
constructor(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
public element: HTMLElement,
|
|
44
|
+
public shadow: ShadowRoot,
|
|
45
|
+
public renderNow: () => void,
|
|
46
|
+
public render: () => Promise<void>,
|
|
47
|
+
) {
|
|
48
|
+
this.attrs = dom.attrs(this.element)
|
|
49
|
+
}
|
|
45
50
|
|
|
46
51
|
get renderCount() {
|
|
47
52
|
return this.#runs
|
|
@@ -64,11 +69,6 @@ export class Use {
|
|
|
64
69
|
return this.styles(...styles)
|
|
65
70
|
}
|
|
66
71
|
|
|
67
|
-
attrs<A extends AttrSpec>(spec: A) {
|
|
68
|
-
this.mount(() => onAttrChange(this.element, this.render))
|
|
69
|
-
return this.once(() => attributes(this.element, spec))
|
|
70
|
-
}
|
|
71
|
-
|
|
72
72
|
once<V>(fn: () => V) {
|
|
73
73
|
return this.#values.guarantee(this.#position++, fn) as V
|
|
74
74
|
}
|
|
@@ -94,15 +94,33 @@ export class Use {
|
|
|
94
94
|
op = (() => {
|
|
95
95
|
const that = this
|
|
96
96
|
function op<V>(f: () => Promise<V>) {
|
|
97
|
-
return that.once(() => Op.
|
|
97
|
+
return that.once(() => Op.load(f))
|
|
98
98
|
}
|
|
99
|
-
op.
|
|
99
|
+
op.load = op as (<V>(f: () => Promise<V>) => Op<V>)
|
|
100
100
|
op.promise = <V>(p: Promise<V>) => this.once(() => Op.promise(p))
|
|
101
101
|
return op
|
|
102
102
|
})()
|
|
103
103
|
|
|
104
|
-
signal
|
|
105
|
-
|
|
104
|
+
signal = (() => {
|
|
105
|
+
const that = this
|
|
106
|
+
function sig<V>(value: V, options?: Partial<SignalOptions>) {
|
|
107
|
+
return that.once(() => signal<V>(value, options))
|
|
108
|
+
}
|
|
109
|
+
sig.derived = function derived<V>(formula: () => V, options?: Partial<SignalOptions>) {
|
|
110
|
+
return that.once(() => signal.derived<V>(formula, options))
|
|
111
|
+
}
|
|
112
|
+
sig.lazy = function lazy<V>(formula: () => V, options?: Partial<SignalOptions>) {
|
|
113
|
+
return that.once(() => signal.lazy<V>(formula, options))
|
|
114
|
+
}
|
|
115
|
+
return sig
|
|
116
|
+
})()
|
|
117
|
+
|
|
118
|
+
derived<V>(formula: () => V, options?: Partial<SignalOptions>) {
|
|
119
|
+
return this.once(() => signal.derived<V>(formula, options))
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
lazy<V>(formula: () => V, options?: Partial<SignalOptions>) {
|
|
123
|
+
return this.once(() => signal.lazy<V>(formula, options))
|
|
106
124
|
}
|
|
107
125
|
}
|
|
108
126
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
import {dom} from "../../../dom/dom.js"
|
|
3
|
+
|
|
4
|
+
export class AttrWatcher {
|
|
5
|
+
#stopper: (() => void) | undefined
|
|
6
|
+
|
|
7
|
+
constructor(
|
|
8
|
+
private element: HTMLElement,
|
|
9
|
+
private response: () => void,
|
|
10
|
+
) {}
|
|
11
|
+
|
|
12
|
+
start() {
|
|
13
|
+
if (!this.#stopper)
|
|
14
|
+
this.#stopper = dom.attrs(this.element).on(this.response)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
stop() {
|
|
18
|
+
if (this.#stopper) this.#stopper()
|
|
19
|
+
this.#stopper = undefined
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
import {MapG} from "@e280/stz"
|
|
3
|
+
import {tracker} from "@e280/strata"
|
|
4
|
+
|
|
5
|
+
export class Reactor {
|
|
6
|
+
#map = new MapG<any, () => void>()
|
|
7
|
+
|
|
8
|
+
effect<R>(collect: () => R, respond: () => Promise<void>) {
|
|
9
|
+
const {seen, result} = tracker.observe(collect)
|
|
10
|
+
for (const item of seen)
|
|
11
|
+
this.#map.guarantee(item, () => tracker.subscribe(item, respond))
|
|
12
|
+
return result
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
clear() {
|
|
16
|
+
for (const dispose of this.#map.values())
|
|
17
|
+
dispose()
|
|
18
|
+
this.#map.clear()
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
|
|
2
|
+
import {debounce} from "@e280/stz"
|
|
3
|
+
import {CSSResultGroup} from "lit"
|
|
4
|
+
|
|
5
|
+
import {dom} from "../dom/dom.js"
|
|
6
|
+
import {Content} from "./types.js"
|
|
7
|
+
import {Reactor} from "./base/utils/reactor.js"
|
|
8
|
+
import {AttrWatcher} from "./base/utils/attr-watcher.js"
|
|
9
|
+
import {applyStyles} from "./base/utils/apply-styles.js"
|
|
10
|
+
import {Use, _disconnect, _reconnect, _wrap} from "./base/use.js"
|
|
11
|
+
|
|
12
|
+
export class BaseElement extends HTMLElement {
|
|
13
|
+
static styles: CSSResultGroup | undefined
|
|
14
|
+
|
|
15
|
+
readonly shadow: ShadowRoot
|
|
16
|
+
|
|
17
|
+
#use: Use
|
|
18
|
+
#mountCount = 0
|
|
19
|
+
#reactor = new Reactor()
|
|
20
|
+
#attrWatcher = new AttrWatcher(this, () => this.update())
|
|
21
|
+
|
|
22
|
+
/** create the shadow root. override this if you want to change the shadow root settings. */
|
|
23
|
+
createShadow() {
|
|
24
|
+
return this.attachShadow({mode: "open"})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
constructor() {
|
|
28
|
+
super()
|
|
29
|
+
this.shadow = this.createShadow()
|
|
30
|
+
this.#use = new Use(
|
|
31
|
+
this,
|
|
32
|
+
this.shadow,
|
|
33
|
+
this.updateNow,
|
|
34
|
+
this.update,
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** return some content to render. */
|
|
39
|
+
render(_use: Use): Content {}
|
|
40
|
+
|
|
41
|
+
/** immediately perform a fresh render into the shadow root. */
|
|
42
|
+
updateNow = () => {
|
|
43
|
+
this.#use[_wrap](() => {
|
|
44
|
+
dom.render(
|
|
45
|
+
this.shadow,
|
|
46
|
+
this.#reactor.effect(
|
|
47
|
+
() => this.render(this.#use),
|
|
48
|
+
this.update,
|
|
49
|
+
),
|
|
50
|
+
)
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** request a rerender which will happen soon (debounced). */
|
|
55
|
+
update = debounce(0, this.updateNow)
|
|
56
|
+
|
|
57
|
+
connectedCallback() {
|
|
58
|
+
if (this.#mountCount === 0) {
|
|
59
|
+
const styles = (this.constructor as any).styles
|
|
60
|
+
if (styles) applyStyles(this.shadow, styles)
|
|
61
|
+
this.updateNow()
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
this.#use[_reconnect]()
|
|
65
|
+
}
|
|
66
|
+
this.#attrWatcher.start()
|
|
67
|
+
this.#mountCount++
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
disconnectedCallback() {
|
|
71
|
+
this.#use[_disconnect]()
|
|
72
|
+
this.#reactor.clear()
|
|
73
|
+
this.#attrWatcher.stop()
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
package/s/ui/types.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
|
|
2
|
+
import {TemplateResult} from "lit"
|
|
3
|
+
import {Constructor} from "@e280/stz"
|
|
4
|
+
import {DirectiveResult} from "lit/directive.js"
|
|
5
|
+
|
|
6
|
+
import {Use} from "./base/use.js"
|
|
7
|
+
import {ViewChain} from "./view/parts/chain.js"
|
|
8
|
+
import {BaseElement} from "./base-element.js"
|
|
9
|
+
|
|
10
|
+
export type Content = TemplateResult | DirectiveResult | HTMLElement | string | null | undefined | void | Content[]
|
|
11
|
+
export type AttrValue = string | boolean | number | undefined | null | void
|
|
12
|
+
|
|
13
|
+
export type ViewFn<Props extends any[]> = (
|
|
14
|
+
(use: Use) =>
|
|
15
|
+
(...props: Props) =>
|
|
16
|
+
Content
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
export type View<Props extends any[]> = {
|
|
20
|
+
(...props: Props): DirectiveResult
|
|
21
|
+
props: (...props: Props) => ViewChain<Props>
|
|
22
|
+
component: <B extends Constructor<BaseElement>>(Base: B) => {
|
|
23
|
+
props: (propFn: (component: InstanceType<B>) => Props) => (
|
|
24
|
+
ComponentClass<B, Props>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type ComponentClass<B extends Constructor<BaseElement>, Props extends any[]> = {
|
|
30
|
+
view: View<Props>
|
|
31
|
+
new(): InstanceType<B>
|
|
32
|
+
} & B
|
|
33
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
|
|
2
|
+
import {Constructor} from "@e280/stz"
|
|
3
|
+
import {Use} from "../base/use.js"
|
|
4
|
+
import {ComponentClass, ViewFn} from "../types.js"
|
|
5
|
+
import {makeView} from "./make-view.js"
|
|
6
|
+
import {Reactor} from "../base/utils/reactor.js"
|
|
7
|
+
import {BaseElement} from "../base-element.js"
|
|
8
|
+
|
|
9
|
+
/** make a component from a BaseElement and a view. */
|
|
10
|
+
export function makeComponent<B extends Constructor<BaseElement>, Props extends any[]>(
|
|
11
|
+
settings: ShadowRootInit,
|
|
12
|
+
Base: B,
|
|
13
|
+
propFn: (component: InstanceType<B>) => Props,
|
|
14
|
+
viewFn: ViewFn<Props>,
|
|
15
|
+
) {
|
|
16
|
+
|
|
17
|
+
return class Component extends Base {
|
|
18
|
+
static view = makeView(viewFn, settings)
|
|
19
|
+
#reactor = new Reactor()
|
|
20
|
+
|
|
21
|
+
createShadow() {
|
|
22
|
+
return this.attachShadow(settings)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
render(use: Use) {
|
|
26
|
+
return viewFn(use)(...this.#reactor.effect(
|
|
27
|
+
() => propFn(this as any),
|
|
28
|
+
() => this.update(),
|
|
29
|
+
))
|
|
30
|
+
}
|
|
31
|
+
} as any as ComponentClass<B, Props>
|
|
32
|
+
}
|
|
33
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
|
|
2
|
+
import {Constructor} from "@e280/stz"
|
|
3
|
+
import {DirectiveResult} from "lit/async-directive.js"
|
|
4
|
+
import {View, ViewFn} from "../types.js"
|
|
5
|
+
import {ViewChain} from "./parts/chain.js"
|
|
6
|
+
import {ViewContext} from "./parts/context.js"
|
|
7
|
+
import {makeComponent} from "./make-component.js"
|
|
8
|
+
import {BaseElement} from "../base-element.js"
|
|
9
|
+
import {makeViewDirective} from "./parts/directive.js"
|
|
10
|
+
|
|
11
|
+
export function makeView<Props extends any[]>(
|
|
12
|
+
viewFn: ViewFn<Props>,
|
|
13
|
+
settings: ShadowRootInit,
|
|
14
|
+
): View<Props> {
|
|
15
|
+
|
|
16
|
+
const renderDirective = makeViewDirective(viewFn, settings)
|
|
17
|
+
|
|
18
|
+
function v(...props: Props): DirectiveResult {
|
|
19
|
+
return renderDirective(new ViewContext(props))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
v.props = (...props: Props) => new ViewChain(
|
|
23
|
+
new ViewContext(props),
|
|
24
|
+
renderDirective,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
v.component = <B extends Constructor<BaseElement>>(Base: B) => ({
|
|
28
|
+
props: (propFn: (component: InstanceType<B>) => Props) => (
|
|
29
|
+
makeComponent<B, Props>(
|
|
30
|
+
settings,
|
|
31
|
+
Base,
|
|
32
|
+
propFn,
|
|
33
|
+
viewFn,
|
|
34
|
+
)
|
|
35
|
+
)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
return v
|
|
39
|
+
}
|
|
40
|
+
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
|
|
2
|
-
import {AttrValue} from "
|
|
2
|
+
import {AttrValue} from "../../types.js"
|
|
3
3
|
|
|
4
4
|
export function applyAttrs(
|
|
5
5
|
element: HTMLElement,
|
|
6
|
-
attrs:
|
|
6
|
+
attrs: Map<string, AttrValue>,
|
|
7
7
|
) {
|
|
8
8
|
|
|
9
9
|
for (const [key, value] of Object.entries(attrs)) {
|
|
10
|
-
if (value === undefined)
|
|
11
|
-
element.removeAttribute(key)
|
|
12
|
-
|
|
13
|
-
else if (value === null)
|
|
10
|
+
if (value === undefined || value === null)
|
|
14
11
|
element.removeAttribute(key)
|
|
15
12
|
|
|
16
13
|
else if (typeof value === "string")
|
|
@@ -27,7 +24,7 @@ export function applyAttrs(
|
|
|
27
24
|
}
|
|
28
25
|
|
|
29
26
|
else
|
|
30
|
-
console.warn(`invalid attribute
|
|
27
|
+
console.warn(`invalid attribute "${key}" type is "${typeof value}"`)
|
|
31
28
|
}
|
|
32
29
|
}
|
|
33
30
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
|
|
2
|
+
import {debounce} from "@e280/stz"
|
|
3
|
+
import {ViewFn} from "../../types.js"
|
|
4
|
+
import {SlyView} from "./sly-view.js"
|
|
5
|
+
import {dom} from "../../../dom/dom.js"
|
|
6
|
+
import {ViewContext} from "./context.js"
|
|
7
|
+
import {applyAttrs} from "./apply-attrs.js"
|
|
8
|
+
import {Reactor} from "../../base/utils/reactor.js"
|
|
9
|
+
import {AttrWatcher} from "../../base/utils/attr-watcher.js"
|
|
10
|
+
import {_disconnect, _reconnect, _wrap, Use} from "../../base/use.js"
|
|
11
|
+
|
|
12
|
+
/** controls the rendering of view context into an element. */
|
|
13
|
+
export class ViewCapsule<Props extends any[]> {
|
|
14
|
+
#element = SlyView.make()
|
|
15
|
+
#reactor = new Reactor()
|
|
16
|
+
|
|
17
|
+
#use: Use
|
|
18
|
+
#shadow: ShadowRoot
|
|
19
|
+
#context!: ViewContext<Props>
|
|
20
|
+
#attrWatcher = new AttrWatcher(this.#element, () => this.#renderDebounced())
|
|
21
|
+
|
|
22
|
+
constructor(
|
|
23
|
+
private viewFn: ViewFn<Props>,
|
|
24
|
+
private settings: ShadowRootInit,
|
|
25
|
+
) {
|
|
26
|
+
this.#shadow = this.#element.attachShadow(this.settings)
|
|
27
|
+
this.#use = new Use(
|
|
28
|
+
this.#element,
|
|
29
|
+
this.#shadow,
|
|
30
|
+
this.#renderNow,
|
|
31
|
+
this.#renderDebounced,
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
update(context: ViewContext<Props>) {
|
|
36
|
+
this.#context = context
|
|
37
|
+
this.#renderNow()
|
|
38
|
+
return this.#element
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
#renderNow = () => {
|
|
42
|
+
this.#use[_wrap](() => {
|
|
43
|
+
const content = this.#reactor.effect(
|
|
44
|
+
() => this.viewFn(this.#use)(...this.#context.props),
|
|
45
|
+
() => this.#renderDebounced(),
|
|
46
|
+
)
|
|
47
|
+
applyAttrs(this.#element, this.#context.attrs)
|
|
48
|
+
dom.render(this.#shadow, content)
|
|
49
|
+
dom.render(this.#element, this.#context.children)
|
|
50
|
+
this.#attrWatcher.start()
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
#renderDebounced = debounce(0, this.#renderNow)
|
|
55
|
+
|
|
56
|
+
disconnected() {
|
|
57
|
+
this.#use[_disconnect]()
|
|
58
|
+
this.#reactor.clear()
|
|
59
|
+
this.#attrWatcher.stop()
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
reconnected() {
|
|
63
|
+
this.#use[_reconnect]()
|
|
64
|
+
this.#attrWatcher.start()
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
|
|
2
|
+
import {DirectiveResult} from "lit/async-directive.js"
|
|
3
|
+
import {ViewContext} from "./context.js"
|
|
4
|
+
import {AttrValue, Content} from "../../types.js"
|
|
5
|
+
|
|
6
|
+
/** provides fluent chaining interface for adding context to rendering a view, think view.props().attr().children().render() */
|
|
7
|
+
export class ViewChain<Props extends any[]> {
|
|
8
|
+
#render: (context: ViewContext<Props>) => DirectiveResult
|
|
9
|
+
#context: ViewContext<Props>
|
|
10
|
+
|
|
11
|
+
constructor(
|
|
12
|
+
context: ViewContext<Props>,
|
|
13
|
+
renderDirective: (context: ViewContext<Props>) => DirectiveResult,
|
|
14
|
+
) {
|
|
15
|
+
this.#context = context
|
|
16
|
+
this.#render = renderDirective
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
attr(key: string, value: AttrValue) {
|
|
20
|
+
this.#context.attrs.set(key, value)
|
|
21
|
+
return this
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
children(...contents: Content[]) {
|
|
25
|
+
this.#context.children.push(...contents)
|
|
26
|
+
return this
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
render() {
|
|
30
|
+
return this.#render(this.#context)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
|
|
2
|
+
import {AttrValue, Content} from "../../types.js"
|
|
3
|
+
|
|
4
|
+
/** the information we need to render a view. */
|
|
5
|
+
export class ViewContext<Props extends any[]> {
|
|
6
|
+
attrs = new Map<string, AttrValue>()
|
|
7
|
+
children: Content[] = []
|
|
8
|
+
constructor(public props: Props) {}
|
|
9
|
+
}
|
|
10
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
|
|
2
|
+
import {AsyncDirective, directive, DirectiveResult} from "lit/async-directive.js"
|
|
3
|
+
import {ViewFn} from "../../types.js"
|
|
4
|
+
import {ViewCapsule} from "./capsule.js"
|
|
5
|
+
import {ViewContext} from "./context.js"
|
|
6
|
+
|
|
7
|
+
/** creates a lit directive fn, which when called, emits a funky lit thing to inject in your html templates. */
|
|
8
|
+
export function makeViewDirective<Props extends any[]>(
|
|
9
|
+
viewFn: ViewFn<Props>,
|
|
10
|
+
settings: ShadowRootInit,
|
|
11
|
+
) {
|
|
12
|
+
|
|
13
|
+
return directive(class ViewDirective extends AsyncDirective {
|
|
14
|
+
#capsule = new ViewCapsule(viewFn, settings)
|
|
15
|
+
|
|
16
|
+
render(context: ViewContext<Props>) {
|
|
17
|
+
return this.#capsule.update(context)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
disconnected() {
|
|
21
|
+
this.#capsule.disconnected()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
reconnected() {
|
|
25
|
+
this.#capsule.reconnected()
|
|
26
|
+
}
|
|
27
|
+
}) as (context: ViewContext<Props>) => DirectiveResult
|
|
28
|
+
}
|
|
29
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
import {dom} from "../../../dom/dom.js"
|
|
3
|
+
|
|
4
|
+
/** <sly-view> element that views are rendered into. */
|
|
5
|
+
export class SlyView extends HTMLElement {
|
|
6
|
+
static #registered = false
|
|
7
|
+
static make() {
|
|
8
|
+
if (!this.#registered) {
|
|
9
|
+
dom.register({SlyView}, {soft: true, upgrade: true})
|
|
10
|
+
this.#registered = true
|
|
11
|
+
}
|
|
12
|
+
return document.createElement("sly-view") as SlyView
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
package/s/ui/view.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
import {Content, ViewFn} from "./types.js"
|
|
3
|
+
import {makeView} from "./view/make-view.js"
|
|
4
|
+
import {_disconnect, _reconnect, Use} from "./base/use.js"
|
|
5
|
+
import { BaseElement } from "./base-element.js"
|
|
6
|
+
|
|
7
|
+
export function view<Props extends any[]>(fn: ViewFn<Props>) {
|
|
8
|
+
return makeView(fn, {mode: "open"})
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
view.settings = (settings: ShadowRootInit) => ({
|
|
12
|
+
render: <Props extends any[]>(fn: ViewFn<Props>) => {
|
|
13
|
+
return makeView(fn, settings)
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
view.render = view
|
|
18
|
+
|
|
19
|
+
view.component = (fn: (use: Use) => Content) => (
|
|
20
|
+
view(use => () => fn(use))
|
|
21
|
+
.component(BaseElement)
|
|
22
|
+
.props(() => [])
|
|
23
|
+
)
|
|
24
|
+
|
package/x/demo/demo.bundle.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { dom } from "../dom/dom.js";
|
|
2
|
+
import { CounterComponent } from "./views/counter.js";
|
|
3
|
+
import { DemoComponent } from "./views/demo.js";
|
|
4
|
+
import { FastcountElement } from "./views/fastcount.js";
|
|
5
|
+
dom.register({
|
|
6
|
+
DemoComponent,
|
|
7
|
+
CounterComponent,
|
|
8
|
+
FastcountElement,
|
|
9
|
+
});
|
|
6
10
|
console.log("🦝 sly");
|
|
7
11
|
//# sourceMappingURL=demo.bundle.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"demo.bundle.js","sourceRoot":"","sources":["../../s/demo/demo.bundle.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,
|
|
1
|
+
{"version":3,"file":"demo.bundle.js","sourceRoot":"","sources":["../../s/demo/demo.bundle.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,GAAG,EAAC,MAAM,eAAe,CAAA;AACjC,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAA;AACnD,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EAAC,gBAAgB,EAAC,MAAM,sBAAsB,CAAA;AAErD,GAAG,CAAC,QAAQ,CAAC;IACZ,aAAa;IACb,gBAAgB;IAChB,gBAAgB;CAChB,CAAC,CAAA;AAEF,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA"}
|