@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.
Files changed (187) hide show
  1. package/README.md +414 -97
  2. package/package.json +4 -4
  3. package/s/demo/demo.bundle.ts +10 -6
  4. package/s/demo/views/counter.ts +22 -17
  5. package/s/demo/views/demo.ts +10 -6
  6. package/s/demo/views/fastcount.ts +29 -0
  7. package/s/demo/views/loaders.ts +2 -2
  8. package/s/dom/attrs/attrs.ts +21 -0
  9. package/s/dom/attrs/parts/attr-fns.ts +38 -0
  10. package/s/dom/attrs/parts/attr-proxies.ts +35 -0
  11. package/s/dom/attrs/parts/attr-spec.ts +29 -0
  12. package/s/dom/attrs/parts/on-attrs.ts +8 -0
  13. package/s/dom/dom.ts +73 -0
  14. package/s/dom/{register.ts → parts/register.ts} +2 -7
  15. package/s/dom/types.ts +40 -0
  16. package/s/index.html.ts +4 -2
  17. package/s/index.ts +10 -9
  18. package/s/loot/drag-and-drops.ts +82 -0
  19. package/s/loot/drops.ts +35 -0
  20. package/s/loot/helpers.ts +31 -0
  21. package/s/loot/index.ts +5 -0
  22. package/s/ops/loaders/make-loader.ts +3 -3
  23. package/s/ops/loaders/parts/anims.ts +1 -1
  24. package/s/ops/loaders/parts/ascii-anim.ts +3 -3
  25. package/s/ops/loaders/parts/error-display.ts +2 -2
  26. package/s/ops/op.ts +2 -2
  27. package/s/{views → ui/base}/use.ts +35 -17
  28. package/s/ui/base/utils/attr-watcher.ts +22 -0
  29. package/s/ui/base/utils/reactor.ts +21 -0
  30. package/s/ui/base-element.ts +76 -0
  31. package/s/ui/types.ts +33 -0
  32. package/s/ui/view/make-component.ts +33 -0
  33. package/s/ui/view/make-view.ts +40 -0
  34. package/s/{views/utils → ui/view/parts}/apply-attrs.ts +4 -7
  35. package/s/ui/view/parts/capsule.ts +67 -0
  36. package/s/ui/view/parts/chain.ts +33 -0
  37. package/s/ui/view/parts/context.ts +10 -0
  38. package/s/ui/view/parts/directive.ts +29 -0
  39. package/s/ui/view/parts/sly-view.ts +15 -0
  40. package/s/ui/view.ts +24 -0
  41. package/x/demo/demo.bundle.js +9 -5
  42. package/x/demo/demo.bundle.js.map +1 -1
  43. package/x/demo/demo.bundle.min.js +19 -17
  44. package/x/demo/demo.bundle.min.js.map +4 -4
  45. package/x/demo/views/counter.d.ts +374 -1
  46. package/x/demo/views/counter.js +19 -15
  47. package/x/demo/views/counter.js.map +1 -1
  48. package/x/demo/views/demo.d.ts +4 -1
  49. package/x/demo/views/demo.js +10 -5
  50. package/x/demo/views/demo.js.map +1 -1
  51. package/x/demo/views/fastcount.d.ts +12 -0
  52. package/x/demo/views/fastcount.js +21 -0
  53. package/x/demo/views/fastcount.js.map +1 -0
  54. package/x/demo/views/loaders.js +2 -2
  55. package/x/demo/views/loaders.js.map +1 -1
  56. package/x/dom/attrs/attrs.d.ts +20 -0
  57. package/x/dom/attrs/attrs.js +17 -0
  58. package/x/dom/attrs/attrs.js.map +1 -0
  59. package/x/dom/attrs/parts/attr-fns.d.ts +13 -0
  60. package/x/dom/attrs/parts/attr-fns.js +42 -0
  61. package/x/dom/attrs/parts/attr-fns.js.map +1 -0
  62. package/x/dom/attrs/parts/attr-proxies.d.ts +8 -0
  63. package/x/dom/attrs/parts/attr-proxies.js +21 -0
  64. package/x/dom/attrs/parts/attr-proxies.js.map +1 -0
  65. package/x/dom/attrs/parts/attr-spec.d.ts +3 -0
  66. package/x/dom/attrs/parts/attr-spec.js +21 -0
  67. package/x/dom/attrs/parts/attr-spec.js.map +1 -0
  68. package/x/dom/attrs/parts/on-attrs.d.ts +2 -0
  69. package/x/dom/attrs/parts/on-attrs.js +7 -0
  70. package/x/dom/attrs/parts/on-attrs.js.map +1 -0
  71. package/x/dom/dom.d.ts +32 -0
  72. package/x/dom/dom.js +54 -0
  73. package/x/dom/dom.js.map +1 -0
  74. package/x/dom/parts/dashify.js.map +1 -0
  75. package/x/dom/{register.d.ts → parts/register.d.ts} +2 -6
  76. package/x/dom/parts/register.js.map +1 -0
  77. package/x/dom/types.d.ts +14 -0
  78. package/x/index.d.ts +9 -8
  79. package/x/index.html +6 -4
  80. package/x/index.html.js +4 -2
  81. package/x/index.html.js.map +1 -1
  82. package/x/index.js +9 -8
  83. package/x/index.js.map +1 -1
  84. package/x/loot/drag-and-drops.d.ts +30 -0
  85. package/x/loot/drag-and-drops.js +63 -0
  86. package/x/loot/drag-and-drops.js.map +1 -0
  87. package/x/loot/drops.d.ts +14 -0
  88. package/x/loot/drops.js +25 -0
  89. package/x/loot/drops.js.map +1 -0
  90. package/x/loot/helpers.d.ts +3 -0
  91. package/x/loot/helpers.js +21 -0
  92. package/x/loot/helpers.js.map +1 -0
  93. package/x/loot/index.d.ts +3 -0
  94. package/x/loot/index.js +4 -0
  95. package/x/loot/index.js.map +1 -0
  96. package/x/ops/loaders/make-loader.d.ts +1 -1
  97. package/x/ops/loaders/make-loader.js +2 -2
  98. package/x/ops/loaders/make-loader.js.map +1 -1
  99. package/x/ops/loaders/parts/anims.d.ts +1 -1
  100. package/x/ops/loaders/parts/ascii-anim.d.ts +2 -2
  101. package/x/ops/loaders/parts/ascii-anim.js +2 -2
  102. package/x/ops/loaders/parts/ascii-anim.js.map +1 -1
  103. package/x/ops/loaders/parts/error-display.js +2 -2
  104. package/x/ops/loaders/parts/error-display.js.map +1 -1
  105. package/x/ops/op.d.ts +2 -2
  106. package/x/ops/op.js +2 -2
  107. package/x/ops/op.js.map +1 -1
  108. package/x/ui/base/css-reset.js.map +1 -0
  109. package/x/{views → ui/base}/use.d.ts +12 -5
  110. package/x/{views → ui/base}/use.js +24 -10
  111. package/x/ui/base/use.js.map +1 -0
  112. package/x/ui/base/utils/apply-styles.js.map +1 -0
  113. package/x/ui/base/utils/attr-watcher.d.ts +8 -0
  114. package/x/ui/base/utils/attr-watcher.js +20 -0
  115. package/x/ui/base/utils/attr-watcher.js.map +1 -0
  116. package/x/ui/base/utils/mounts.js.map +1 -0
  117. package/x/ui/base/utils/reactor.d.ts +5 -0
  118. package/x/ui/base/utils/reactor.js +17 -0
  119. package/x/ui/base/utils/reactor.js.map +1 -0
  120. package/x/ui/base-element.d.ts +19 -0
  121. package/x/ui/base-element.js +52 -0
  122. package/x/ui/base-element.js.map +1 -0
  123. package/x/ui/types.d.ts +20 -0
  124. package/x/{views → ui}/types.js.map +1 -1
  125. package/x/ui/view/make-component.d.ts +5 -0
  126. package/x/ui/view/make-component.js +16 -0
  127. package/x/ui/view/make-component.js.map +1 -0
  128. package/x/ui/view/make-view.d.ts +2 -0
  129. package/x/ui/view/make-view.js +16 -0
  130. package/x/ui/view/make-view.js.map +1 -0
  131. package/x/ui/view/parts/apply-attrs.d.ts +2 -0
  132. package/x/{views/utils → ui/view/parts}/apply-attrs.js +2 -4
  133. package/x/ui/view/parts/apply-attrs.js.map +1 -0
  134. package/x/ui/view/parts/capsule.d.ts +13 -0
  135. package/x/ui/view/parts/capsule.js +49 -0
  136. package/x/ui/view/parts/capsule.js.map +1 -0
  137. package/x/ui/view/parts/chain.d.ts +11 -0
  138. package/x/ui/view/parts/chain.js +21 -0
  139. package/x/ui/view/parts/chain.js.map +1 -0
  140. package/x/ui/view/parts/context.d.ts +8 -0
  141. package/x/ui/view/parts/context.js +10 -0
  142. package/x/ui/view/parts/context.js.map +1 -0
  143. package/x/ui/view/parts/directive.d.ts +5 -0
  144. package/x/ui/view/parts/directive.js +18 -0
  145. package/x/ui/view/parts/directive.js.map +1 -0
  146. package/x/ui/view/parts/sly-view.d.ts +5 -0
  147. package/x/ui/view/parts/sly-view.js +13 -0
  148. package/x/ui/view/parts/sly-view.js.map +1 -0
  149. package/x/ui/view.d.ts +11 -0
  150. package/x/ui/view.js +15 -0
  151. package/x/ui/view.js.map +1 -0
  152. package/s/dom/dollar.ts +0 -27
  153. package/s/views/attributes.ts +0 -89
  154. package/s/views/types.ts +0 -40
  155. package/s/views/view.ts +0 -150
  156. package/x/dom/dashify.js.map +0 -1
  157. package/x/dom/dollar.d.ts +0 -10
  158. package/x/dom/dollar.js +0 -18
  159. package/x/dom/dollar.js.map +0 -1
  160. package/x/dom/register.js.map +0 -1
  161. package/x/views/attributes.d.ts +0 -10
  162. package/x/views/attributes.js +0 -46
  163. package/x/views/attributes.js.map +0 -1
  164. package/x/views/css-reset.js.map +0 -1
  165. package/x/views/types.d.ts +0 -31
  166. package/x/views/use.js.map +0 -1
  167. package/x/views/utils/apply-attrs.d.ts +0 -2
  168. package/x/views/utils/apply-attrs.js.map +0 -1
  169. package/x/views/utils/apply-styles.js.map +0 -1
  170. package/x/views/utils/mounts.js.map +0 -1
  171. package/x/views/view.d.ts +0 -9
  172. package/x/views/view.js +0 -116
  173. package/x/views/view.js.map +0 -1
  174. /package/s/dom/{dashify.ts → parts/dashify.ts} +0 -0
  175. /package/s/{views → ui/base}/css-reset.ts +0 -0
  176. /package/s/{views → ui/base}/utils/apply-styles.ts +0 -0
  177. /package/s/{views → ui/base}/utils/mounts.ts +0 -0
  178. /package/x/dom/{dashify.d.ts → parts/dashify.d.ts} +0 -0
  179. /package/x/dom/{dashify.js → parts/dashify.js} +0 -0
  180. /package/x/dom/{register.js → parts/register.js} +0 -0
  181. /package/x/{views → ui/base}/css-reset.d.ts +0 -0
  182. /package/x/{views → ui/base}/css-reset.js +0 -0
  183. /package/x/{views → ui/base}/utils/apply-styles.d.ts +0 -0
  184. /package/x/{views → ui/base}/utils/apply-styles.js +0 -0
  185. /package/x/{views → ui/base}/utils/mounts.d.ts +0 -0
  186. /package/x/{views → ui/base}/utils/mounts.js +0 -0
  187. /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 "../../../views/view.js"
6
- import {Content} from "../../../views/types.js"
7
- import {cssReset} from "../../../views/css-reset.js"
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 "../../../views/view.js"
4
- import {cssReset} from "../../../views/css-reset.js"
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 fn<V>(fn: () => Promise<V>) {
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 fn(fn: () => Promise<V>) {
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 "../ops/op.js"
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
- public element: HTMLElement,
41
- public shadow: ShadowRoot,
42
- public renderNow: () => void,
43
- public render: () => Promise<void>,
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.fn(f))
97
+ return that.once(() => Op.load(f))
98
98
  }
99
- op.fn = op as (<V>(f: () => Promise<V>) => Op<V>)
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<V>(value: V) {
105
- return this.once(() => signal<V>(value))
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 "../types.js"
2
+ import {AttrValue} from "../../types.js"
3
3
 
4
4
  export function applyAttrs(
5
5
  element: HTMLElement,
6
- attrs: Record<string, AttrValue>,
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 type ${key} is ${typeof value}`)
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
+
@@ -1,7 +1,11 @@
1
- import { $ } from "../dom/dollar.js";
2
- import { DemoView } from "./views/demo.js";
3
- import { CounterView } from "./views/counter.js";
4
- $.render($(".demo"), DemoView());
5
- $.register({ DemoCounter: CounterView.component(1) });
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,CAAC,EAAC,MAAM,kBAAkB,CAAA;AAClC,OAAO,EAAC,QAAQ,EAAC,MAAM,iBAAiB,CAAA;AACxC,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAA;AAE9C,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAA;AAChC,CAAC,CAAC,QAAQ,CAAC,EAAC,WAAW,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,EAAC,CAAC,CAAA;AAEnD,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA"}
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"}