@e280/sly 0.2.0-2 → 0.2.0-21

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 (285) hide show
  1. package/README.md +552 -98
  2. package/package.json +12 -5
  3. package/s/base/element.ts +76 -0
  4. package/s/base/index.ts +5 -0
  5. package/s/{views → base}/use.ts +17 -18
  6. package/s/base/utils/attr-watcher.ts +22 -0
  7. package/s/base/utils/reactor.ts +32 -0
  8. package/s/base/utils/use-attrs.ts +27 -0
  9. package/s/demo/demo.bundle.ts +9 -5
  10. package/s/demo/views/counter.ts +21 -24
  11. package/s/demo/views/demo.ts +10 -6
  12. package/s/demo/views/fastcount.ts +29 -0
  13. package/s/demo/views/loaders.ts +7 -7
  14. package/s/dom/attrs/attrs.ts +21 -0
  15. package/s/dom/attrs/parts/attr-fns.ts +38 -0
  16. package/s/dom/attrs/parts/attr-proxies.ts +35 -0
  17. package/s/dom/attrs/parts/attr-spec.ts +29 -0
  18. package/s/dom/attrs/parts/on-attrs.ts +8 -0
  19. package/s/dom/dom.ts +46 -16
  20. package/s/dom/index.ts +4 -0
  21. package/s/dom/parts/el.ts +14 -0
  22. package/s/dom/parts/eve.ts +24 -0
  23. package/s/dom/{register.ts → parts/register.ts} +2 -7
  24. package/s/dom/types.ts +39 -2
  25. package/s/index.html.ts +4 -2
  26. package/s/index.ts +7 -18
  27. package/s/loaders/index.barrel.ts +9 -0
  28. package/s/loaders/index.ts +3 -0
  29. package/s/loaders/make.ts +14 -0
  30. package/s/loaders/mock.ts +11 -0
  31. package/s/{ops/loaders → loaders}/parts/anims.ts +1 -1
  32. package/s/{ops/loaders → loaders}/parts/ascii-anim.ts +4 -3
  33. package/s/{ops/loaders → loaders}/parts/error-display.ts +2 -2
  34. package/s/loaders/types.ts +6 -0
  35. package/s/loot/drag-and-drops.ts +82 -0
  36. package/s/loot/{drop.ts → drops.ts} +8 -17
  37. package/s/loot/helpers.ts +3 -3
  38. package/s/loot/index.barrel.ts +5 -0
  39. package/s/loot/index.ts +1 -3
  40. package/s/ops/index.ts +5 -0
  41. package/s/ops/op.ts +3 -2
  42. package/s/spa/index.barrel.ts +6 -0
  43. package/s/spa/index.ts +3 -0
  44. package/s/spa/plumbing/braces.ts +76 -0
  45. package/s/spa/plumbing/primitives.ts +85 -0
  46. package/s/spa/plumbing/router-core.ts +49 -0
  47. package/s/spa/plumbing/types.ts +45 -0
  48. package/s/spa/router.ts +49 -0
  49. package/s/spa/spa.test.ts +91 -0
  50. package/s/tests.test.ts +4 -1
  51. package/s/view/index.ts +6 -0
  52. package/s/view/types.ts +40 -0
  53. package/s/view/utils/make-component.ts +34 -0
  54. package/s/view/utils/make-view.ts +48 -0
  55. package/s/view/utils/parts/capsule.ts +67 -0
  56. package/s/view/utils/parts/chain.ts +33 -0
  57. package/s/view/utils/parts/context.ts +10 -0
  58. package/s/view/utils/parts/directive.ts +29 -0
  59. package/s/view/utils/parts/set-attrs.ts +33 -0
  60. package/s/view/utils/parts/sly-view.ts +15 -0
  61. package/s/view/view.ts +24 -0
  62. package/x/base/css-reset.js.map +1 -0
  63. package/x/base/element.d.ts +19 -0
  64. package/x/base/element.js +52 -0
  65. package/x/base/element.js.map +1 -0
  66. package/x/base/index.d.ts +3 -0
  67. package/x/base/index.js +4 -0
  68. package/x/base/index.js.map +1 -0
  69. package/x/{views → base}/use.d.ts +5 -5
  70. package/x/{views → base}/use.js +9 -11
  71. package/x/base/use.js.map +1 -0
  72. package/x/base/utils/apply-styles.js.map +1 -0
  73. package/x/base/utils/attr-watcher.d.ts +8 -0
  74. package/x/base/utils/attr-watcher.js +20 -0
  75. package/x/base/utils/attr-watcher.js.map +1 -0
  76. package/x/base/utils/mounts.js.map +1 -0
  77. package/x/base/utils/reactor.d.ts +5 -0
  78. package/x/base/utils/reactor.js +25 -0
  79. package/x/base/utils/reactor.js.map +1 -0
  80. package/x/base/utils/use-attrs.d.ts +11 -0
  81. package/x/base/utils/use-attrs.js +19 -0
  82. package/x/base/utils/use-attrs.js.map +1 -0
  83. package/x/demo/demo.bundle.js +8 -4
  84. package/x/demo/demo.bundle.js.map +1 -1
  85. package/x/demo/demo.bundle.min.js +19 -22
  86. package/x/demo/demo.bundle.min.js.map +4 -4
  87. package/x/demo/views/counter.d.ts +374 -1
  88. package/x/demo/views/counter.js +19 -22
  89. package/x/demo/views/counter.js.map +1 -1
  90. package/x/demo/views/demo.d.ts +4 -1
  91. package/x/demo/views/demo.js +10 -5
  92. package/x/demo/views/demo.js.map +1 -1
  93. package/x/demo/views/fastcount.d.ts +12 -0
  94. package/x/demo/views/fastcount.js +21 -0
  95. package/x/demo/views/fastcount.js.map +1 -0
  96. package/x/demo/views/loaders.js +6 -6
  97. package/x/demo/views/loaders.js.map +1 -1
  98. package/x/dom/attrs/attrs.d.ts +20 -0
  99. package/x/dom/attrs/attrs.js +17 -0
  100. package/x/dom/attrs/attrs.js.map +1 -0
  101. package/x/dom/attrs/parts/attr-fns.d.ts +13 -0
  102. package/x/dom/attrs/parts/attr-fns.js +42 -0
  103. package/x/dom/attrs/parts/attr-fns.js.map +1 -0
  104. package/x/dom/attrs/parts/attr-proxies.d.ts +8 -0
  105. package/x/dom/attrs/parts/attr-proxies.js +21 -0
  106. package/x/dom/attrs/parts/attr-proxies.js.map +1 -0
  107. package/x/dom/attrs/parts/attr-spec.d.ts +3 -0
  108. package/x/dom/attrs/parts/attr-spec.js +21 -0
  109. package/x/dom/attrs/parts/attr-spec.js.map +1 -0
  110. package/x/dom/attrs/parts/on-attrs.d.ts +2 -0
  111. package/x/dom/attrs/parts/on-attrs.js +7 -0
  112. package/x/dom/attrs/parts/on-attrs.js.map +1 -0
  113. package/x/dom/dom.d.ts +22 -7
  114. package/x/dom/dom.js +32 -12
  115. package/x/dom/dom.js.map +1 -1
  116. package/x/dom/index.d.ts +2 -0
  117. package/x/dom/index.js +3 -0
  118. package/x/dom/index.js.map +1 -0
  119. package/x/dom/parts/dashify.js.map +1 -0
  120. package/x/dom/parts/el.d.ts +2 -0
  121. package/x/dom/parts/el.js +7 -0
  122. package/x/dom/parts/el.js.map +1 -0
  123. package/x/dom/parts/eve.d.ts +7 -0
  124. package/x/dom/parts/eve.js +16 -0
  125. package/x/dom/parts/eve.js.map +1 -0
  126. package/x/dom/{register.d.ts → parts/register.d.ts} +2 -6
  127. package/x/dom/parts/register.js.map +1 -0
  128. package/x/dom/types.d.ts +14 -2
  129. package/x/index.d.ts +7 -15
  130. package/x/index.html +6 -4
  131. package/x/index.html.js +4 -2
  132. package/x/index.html.js.map +1 -1
  133. package/x/index.js +7 -15
  134. package/x/index.js.map +1 -1
  135. package/x/loaders/index.barrel.d.ts +6 -0
  136. package/x/loaders/index.barrel.js +7 -0
  137. package/x/loaders/index.barrel.js.map +1 -0
  138. package/x/loaders/index.d.ts +1 -0
  139. package/x/loaders/index.js +2 -0
  140. package/x/loaders/index.js.map +1 -0
  141. package/x/loaders/make.d.ts +3 -0
  142. package/x/loaders/make.js +6 -0
  143. package/x/loaders/make.js.map +1 -0
  144. package/x/loaders/mock.d.ts +2 -0
  145. package/x/loaders/mock.js +8 -0
  146. package/x/loaders/mock.js.map +1 -0
  147. package/x/{ops/loaders → loaders}/parts/anims.d.ts +1 -1
  148. package/x/loaders/parts/anims.js.map +1 -0
  149. package/x/{ops/loaders → loaders}/parts/ascii-anim.d.ts +2 -2
  150. package/x/{ops/loaders → loaders}/parts/ascii-anim.js +2 -2
  151. package/x/loaders/parts/ascii-anim.js.map +1 -0
  152. package/x/loaders/parts/error-display.d.ts +1 -0
  153. package/x/{ops/loaders → loaders}/parts/error-display.js +2 -2
  154. package/x/loaders/parts/error-display.js.map +1 -0
  155. package/x/loaders/types.d.ts +3 -0
  156. package/x/loaders/types.js.map +1 -0
  157. package/x/loot/drag-and-drops.d.ts +30 -0
  158. package/x/loot/drag-and-drops.js +63 -0
  159. package/x/loot/drag-and-drops.js.map +1 -0
  160. package/x/loot/{drop.d.ts → drops.d.ts} +3 -5
  161. package/x/loot/drops.js +25 -0
  162. package/x/loot/drops.js.map +1 -0
  163. package/x/loot/helpers.d.ts +3 -3
  164. package/x/loot/helpers.js +3 -3
  165. package/x/loot/helpers.js.map +1 -1
  166. package/x/loot/index.barrel.d.ts +3 -0
  167. package/x/loot/index.barrel.js +4 -0
  168. package/x/loot/index.barrel.js.map +1 -0
  169. package/x/loot/index.d.ts +1 -3
  170. package/x/loot/index.js +1 -3
  171. package/x/loot/index.js.map +1 -1
  172. package/x/ops/index.d.ts +3 -0
  173. package/x/ops/index.js +4 -0
  174. package/x/ops/index.js.map +1 -0
  175. package/x/ops/op.d.ts +2 -2
  176. package/x/ops/op.js +3 -2
  177. package/x/ops/op.js.map +1 -1
  178. package/x/spa/index.barrel.d.ts +4 -0
  179. package/x/spa/index.barrel.js +3 -0
  180. package/x/spa/index.barrel.js.map +1 -0
  181. package/x/spa/index.d.ts +1 -0
  182. package/x/spa/index.js +2 -0
  183. package/x/spa/index.js.map +1 -0
  184. package/x/spa/plumbing/braces.d.ts +12 -0
  185. package/x/spa/plumbing/braces.js +55 -0
  186. package/x/spa/plumbing/braces.js.map +1 -0
  187. package/x/spa/plumbing/primitives.d.ts +22 -0
  188. package/x/spa/plumbing/primitives.js +65 -0
  189. package/x/spa/plumbing/primitives.js.map +1 -0
  190. package/x/spa/plumbing/router-core.d.ts +13 -0
  191. package/x/spa/plumbing/router-core.js +38 -0
  192. package/x/spa/plumbing/router-core.js.map +1 -0
  193. package/x/spa/plumbing/types.d.ts +35 -0
  194. package/x/spa/plumbing/types.js +2 -0
  195. package/x/spa/plumbing/types.js.map +1 -0
  196. package/x/spa/router.d.ts +16 -0
  197. package/x/spa/router.js +39 -0
  198. package/x/spa/router.js.map +1 -0
  199. package/x/spa/spa.test.d.ts +15 -0
  200. package/x/spa/spa.test.js +78 -0
  201. package/x/spa/spa.test.js.map +1 -0
  202. package/x/tests.test.js +4 -1
  203. package/x/tests.test.js.map +1 -1
  204. package/x/view/index.d.ts +4 -0
  205. package/x/view/index.js +5 -0
  206. package/x/view/index.js.map +1 -0
  207. package/x/view/types.d.ts +22 -0
  208. package/x/view/types.js +2 -0
  209. package/x/{views → view}/types.js.map +1 -1
  210. package/x/view/utils/make-component.d.ts +5 -0
  211. package/x/view/utils/make-component.js +17 -0
  212. package/x/view/utils/make-component.js.map +1 -0
  213. package/x/view/utils/make-view.d.ts +2 -0
  214. package/x/view/utils/make-view.js +24 -0
  215. package/x/view/utils/make-view.js.map +1 -0
  216. package/x/view/utils/parts/capsule.d.ts +13 -0
  217. package/x/view/utils/parts/capsule.js +49 -0
  218. package/x/view/utils/parts/capsule.js.map +1 -0
  219. package/x/view/utils/parts/chain.d.ts +11 -0
  220. package/x/view/utils/parts/chain.js +21 -0
  221. package/x/view/utils/parts/chain.js.map +1 -0
  222. package/x/view/utils/parts/context.d.ts +8 -0
  223. package/x/view/utils/parts/context.js +10 -0
  224. package/x/view/utils/parts/context.js.map +1 -0
  225. package/x/view/utils/parts/directive.d.ts +5 -0
  226. package/x/view/utils/parts/directive.js +18 -0
  227. package/x/view/utils/parts/directive.js.map +1 -0
  228. package/x/view/utils/parts/set-attrs.d.ts +3 -0
  229. package/x/view/utils/parts/set-attrs.js +21 -0
  230. package/x/view/utils/parts/set-attrs.js.map +1 -0
  231. package/x/view/utils/parts/sly-view.d.ts +5 -0
  232. package/x/view/utils/parts/sly-view.js +13 -0
  233. package/x/view/utils/parts/sly-view.js.map +1 -0
  234. package/x/view/view.d.ts +11 -0
  235. package/x/view/view.js +15 -0
  236. package/x/view/view.js.map +1 -0
  237. package/s/loot/drag-drop.ts +0 -76
  238. package/s/ops/loaders/make-loader.ts +0 -18
  239. package/s/views/attributes.ts +0 -89
  240. package/s/views/types.ts +0 -40
  241. package/s/views/utils/apply-attrs.ts +0 -33
  242. package/s/views/view.ts +0 -150
  243. package/x/dom/dashify.js.map +0 -1
  244. package/x/dom/register.js.map +0 -1
  245. package/x/loot/drag-drop.d.ts +0 -29
  246. package/x/loot/drag-drop.js +0 -54
  247. package/x/loot/drag-drop.js.map +0 -1
  248. package/x/loot/drop.js +0 -32
  249. package/x/loot/drop.js.map +0 -1
  250. package/x/ops/loaders/make-loader.d.ts +0 -5
  251. package/x/ops/loaders/make-loader.js +0 -7
  252. package/x/ops/loaders/make-loader.js.map +0 -1
  253. package/x/ops/loaders/parts/anims.js.map +0 -1
  254. package/x/ops/loaders/parts/ascii-anim.js.map +0 -1
  255. package/x/ops/loaders/parts/error-display.d.ts +0 -1
  256. package/x/ops/loaders/parts/error-display.js.map +0 -1
  257. package/x/views/attributes.d.ts +0 -10
  258. package/x/views/attributes.js +0 -46
  259. package/x/views/attributes.js.map +0 -1
  260. package/x/views/css-reset.js.map +0 -1
  261. package/x/views/types.d.ts +0 -31
  262. package/x/views/use.js.map +0 -1
  263. package/x/views/utils/apply-attrs.d.ts +0 -2
  264. package/x/views/utils/apply-attrs.js +0 -21
  265. package/x/views/utils/apply-attrs.js.map +0 -1
  266. package/x/views/utils/apply-styles.js.map +0 -1
  267. package/x/views/utils/mounts.js.map +0 -1
  268. package/x/views/view.d.ts +0 -9
  269. package/x/views/view.js +0 -116
  270. package/x/views/view.js.map +0 -1
  271. /package/s/{views → base}/css-reset.ts +0 -0
  272. /package/s/{views → base}/utils/apply-styles.ts +0 -0
  273. /package/s/{views → base}/utils/mounts.ts +0 -0
  274. /package/s/dom/{dashify.ts → parts/dashify.ts} +0 -0
  275. /package/x/{views → base}/css-reset.d.ts +0 -0
  276. /package/x/{views → base}/css-reset.js +0 -0
  277. /package/x/{views → base}/utils/apply-styles.d.ts +0 -0
  278. /package/x/{views → base}/utils/apply-styles.js +0 -0
  279. /package/x/{views → base}/utils/mounts.d.ts +0 -0
  280. /package/x/{views → base}/utils/mounts.js +0 -0
  281. /package/x/dom/{dashify.d.ts → parts/dashify.d.ts} +0 -0
  282. /package/x/dom/{dashify.js → parts/dashify.js} +0 -0
  283. /package/x/dom/{register.js → parts/register.js} +0 -0
  284. /package/x/{ops/loaders → loaders}/parts/anims.js +0 -0
  285. /package/x/{views → loaders}/types.js +0 -0
package/package.json CHANGED
@@ -1,12 +1,19 @@
1
1
  {
2
2
  "name": "@e280/sly",
3
- "version": "0.2.0-2",
3
+ "version": "0.2.0-21",
4
4
  "description": "web shadow views",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "main": "./x/index.js",
8
8
  "exports": {
9
- ".": "./x/index.js"
9
+ ".": "./x/index.js",
10
+ "./base": "./x/base/index.js",
11
+ "./dom": "./x/dom/index.js",
12
+ "./loaders": "./x/loaders/index.js",
13
+ "./loot": "./x/loot/index.js",
14
+ "./ops": "./x/ops/index.js",
15
+ "./spa": "./x/spa/index.js",
16
+ "./view": "./x/view/index.js"
10
17
  },
11
18
  "files": [
12
19
  "x",
@@ -16,12 +23,12 @@
16
23
  "lit": "^3.3.1"
17
24
  },
18
25
  "dependencies": {
19
- "@e280/strata": "^0.2.0-6",
20
- "@e280/stz": "^0.2.0"
26
+ "@e280/strata": "^0.2.0-14",
27
+ "@e280/stz": "^0.2.6"
21
28
  },
22
29
  "devDependencies": {
23
30
  "@e280/science": "^0.1.2",
24
- "@e280/scute": "^0.0.0",
31
+ "@e280/scute": "^0.1.0",
25
32
  "http-server": "^14.1.1",
26
33
  "npm-run-all": "^4.1.5",
27
34
  "typescript": "^5.9.2"
@@ -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 "../view/types.js"
7
+ import {Reactor} from "./utils/reactor.js"
8
+ import {AttrWatcher} from "./utils/attr-watcher.js"
9
+ import {applyStyles} from "./utils/apply-styles.js"
10
+ import {Use, _disconnect, _reconnect, _wrap} from "./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
+
@@ -0,0 +1,5 @@
1
+
2
+ export * from "./css-reset.js"
3
+ export * from "./element.js"
4
+ export * from "./use.js"
5
+
@@ -1,18 +1,20 @@
1
1
 
2
2
  import {CSSResultGroup} from "lit"
3
3
  import {defer, MapG} from "@e280/stz"
4
- import {Derive, Lazy, Signal, signal, SignalOptions} from "@e280/strata/signals"
4
+ import {signal, SignalOptions} from "@e280/strata/signals"
5
5
 
6
6
  import {Op} from "../ops/op.js"
7
7
  import {Mounts} from "./utils/mounts.js"
8
+ import {UseAttrs} from "./utils/use-attrs.js"
8
9
  import {applyStyles} from "./utils/apply-styles.js"
9
- import {attributes, AttrSpec, onAttrChange} from "./attributes.js"
10
10
 
11
11
  export const _wrap = Symbol()
12
12
  export const _disconnect = Symbol()
13
13
  export const _reconnect = Symbol()
14
14
 
15
15
  export class Use {
16
+ readonly attrs: UseAttrs
17
+
16
18
  #runs = 0
17
19
  #position = 0
18
20
  #values = new MapG<number, any>()
@@ -37,11 +39,13 @@ export class Use {
37
39
  }
38
40
 
39
41
  constructor(
40
- public element: HTMLElement,
41
- public shadow: ShadowRoot,
42
- public renderNow: () => void,
43
- public render: () => Promise<void>,
44
- ) {}
42
+ public element: HTMLElement,
43
+ public shadow: ShadowRoot,
44
+ public renderNow: () => void,
45
+ public render: () => Promise<void>,
46
+ ) {
47
+ this.attrs = new UseAttrs(this)
48
+ }
45
49
 
46
50
  get renderCount() {
47
51
  return this.#runs
@@ -64,11 +68,6 @@ export class Use {
64
68
  return this.styles(...styles)
65
69
  }
66
70
 
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
71
  once<V>(fn: () => V) {
73
72
  return this.#values.guarantee(this.#position++, fn) as V
74
73
  }
@@ -94,9 +93,9 @@ export class Use {
94
93
  op = (() => {
95
94
  const that = this
96
95
  function op<V>(f: () => Promise<V>) {
97
- return that.once(() => Op.fn(f))
96
+ return that.once(() => Op.load(f))
98
97
  }
99
- op.fn = op as (<V>(f: () => Promise<V>) => Op<V>)
98
+ op.load = op as (<V>(f: () => Promise<V>) => Op<V>)
100
99
  op.promise = <V>(p: Promise<V>) => this.once(() => Op.promise(p))
101
100
  return op
102
101
  })()
@@ -106,8 +105,8 @@ export class Use {
106
105
  function sig<V>(value: V, options?: Partial<SignalOptions>) {
107
106
  return that.once(() => signal<V>(value, options))
108
107
  }
109
- sig.derive = function derive<V>(formula: () => V, options?: Partial<SignalOptions>) {
110
- return that.once(() => signal.derive<V>(formula, options))
108
+ sig.derived = function derived<V>(formula: () => V, options?: Partial<SignalOptions>) {
109
+ return that.once(() => signal.derived<V>(formula, options))
111
110
  }
112
111
  sig.lazy = function lazy<V>(formula: () => V, options?: Partial<SignalOptions>) {
113
112
  return that.once(() => signal.lazy<V>(formula, options))
@@ -115,8 +114,8 @@ export class Use {
115
114
  return sig
116
115
  })()
117
116
 
118
- derive<V>(formula: () => V, options?: Partial<SignalOptions>) {
119
- return this.once(() => signal.derive<V>(formula, options))
117
+ derived<V>(formula: () => V, options?: Partial<SignalOptions>) {
118
+ return this.once(() => signal.derived<V>(formula, options))
120
119
  }
121
120
 
122
121
  lazy<V>(formula: () => V, options?: Partial<SignalOptions>) {
@@ -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,32 @@
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
+
11
+ // add seen items
12
+ for (const item of seen)
13
+ this.#map.guarantee(item, () => tracker.subscribe(item, respond))
14
+
15
+ // remove orphaned items
16
+ for (const [item, dispose] of this.#map) {
17
+ if (!seen.has(item)) {
18
+ dispose()
19
+ this.#map.delete(item)
20
+ }
21
+ }
22
+
23
+ return result
24
+ }
25
+
26
+ clear() {
27
+ for (const dispose of this.#map.values())
28
+ dispose()
29
+ this.#map.clear()
30
+ }
31
+ }
32
+
@@ -0,0 +1,27 @@
1
+
2
+ import {Use} from "../use.js"
3
+ import {dom} from "../../dom/dom.js"
4
+ import {Attrs, AttrSpec} from "../../dom/types.js"
5
+
6
+ export class UseAttrs {
7
+ #use: Use
8
+ #attrs: Attrs
9
+
10
+ constructor(use: Use) {
11
+ this.#use = use
12
+ this.#attrs = dom.attrs(use.element)
13
+ }
14
+
15
+ get strings() { return this.#attrs.strings }
16
+ get numbers() { return this.#attrs.numbers }
17
+ get booleans() { return this.#attrs.booleans }
18
+
19
+ spec<A extends AttrSpec>(spec: A) {
20
+ return this.#use.once(() => this.#attrs.spec(spec))
21
+ }
22
+
23
+ on(fn: () => void) {
24
+ return this.#use.mount(() => this.#attrs.on(fn))
25
+ }
26
+ }
27
+
@@ -1,10 +1,14 @@
1
1
 
2
2
  import {dom} from "../dom/dom.js"
3
- import {DemoView} from "./views/demo.js"
4
- import {CounterView} from "./views/counter.js"
5
-
6
- dom.in(".demo").render(DemoView())
7
- dom.register({DemoCounter: CounterView.component(1)})
3
+ import {CounterComponent} from "./views/counter.js"
4
+ import {DemoComponent} from "./views/demo.js"
5
+ import {FastcountElement} from "./views/fastcount.js"
6
+
7
+ dom.register({
8
+ DemoComponent,
9
+ CounterComponent,
10
+ FastcountElement,
11
+ })
8
12
 
9
13
  console.log("🦝 sly")
10
14
 
@@ -1,44 +1,41 @@
1
1
 
2
2
  import {css, html} from "lit"
3
- import {repeat} from "@e280/stz"
4
3
 
5
- import {view} from "../../views/view.js"
6
- import {cssReset} from "../../views/css-reset.js"
4
+ import {dom} from "../../dom/dom.js"
5
+ import {view} from "../../view/view.js"
6
+ import {cssReset} from "../../base/css-reset.js"
7
+ import {BaseElement} from "../../base/element.js"
7
8
 
8
- export const CounterView = view(use => (initial: number) => {
9
+ export const CounterView = view(use => (start: number, step: number) => {
9
10
  use.name("counter")
10
11
  use.styles(cssReset, styles)
11
12
 
12
- const $seconds = use.signal(0)
13
- const start = use.once(() => Date.now())
14
- use.mount(() => repeat(async() => {
15
- const since = Date.now() - start
16
- $seconds.set(Math.floor(since / 1000))
17
- }))
18
-
19
- const $count = use.signal(initial)
20
- const increment = () => $count.value++
21
-
22
- const $product = use.signal
23
- .derive(() => $count() * $seconds())
13
+ const $count = use.signal(start)
14
+ const increment = () => { $count.value += step }
24
15
 
25
16
  return html`
26
17
  <slot></slot>
27
18
  <div>
28
- <span>${$seconds.get()}</span>
29
- </div>
30
- <div>
31
- <span>${$count.get()}</span>
19
+ <span>${$count()}</span>
32
20
  </div>
33
21
  <div>
34
- <span>${$product.get()}</span>
35
- </div>
36
- <div>
37
- <button @click="${increment}">+</button>
22
+ <button @click="${increment}">++</button>
38
23
  </div>
39
24
  `
40
25
  })
41
26
 
27
+ // convert a view into a web component
28
+ export class CounterComponent extends (
29
+ CounterView
30
+ .component(class extends BaseElement {
31
+ attrs = dom.attrs(this).spec({
32
+ start: Number,
33
+ step: Number,
34
+ })
35
+ })
36
+ .props(c => [c.attrs.start ?? 0, c.attrs.step ?? 1])
37
+ ) {}
38
+
42
39
  const styles = css`
43
40
  :host {
44
41
  display: flex;
@@ -1,24 +1,28 @@
1
1
 
2
2
  import {css, html} from "lit"
3
- import {view} from "../../views/view.js"
3
+
4
+ import {view} from "../../view/view.js"
4
5
  import {CounterView} from "./counter.js"
5
6
  import {LoadersView} from "./loaders.js"
6
- import {cssReset} from "../../views/css-reset.js"
7
+ import {cssReset} from "../../base/css-reset.js"
7
8
 
8
- export const DemoView = view(use => () => {
9
+ export class DemoComponent extends (view.component(use => {
9
10
  use.name("demo")
10
11
  use.styles(cssReset, styles)
11
-
12
12
  return html`
13
- ${CounterView.props(2).children("view").render()}
13
+ ${CounterView
14
+ .props(768, 3)
15
+ .children("view")
16
+ .render()}
14
17
  ${LoadersView()}
15
18
  `
16
- })
19
+ })) {}
17
20
 
18
21
  const styles = css`
19
22
  :host {
20
23
  display: flex;
21
24
  flex-direction: column;
25
+ align-items: center;
22
26
  gap: 1em;
23
27
  }
24
28
  `
@@ -0,0 +1,29 @@
1
+
2
+ import {css, html} from "lit"
3
+ import {nap, repeat} from "@e280/stz"
4
+
5
+ import {dom} from "../../dom/dom.js"
6
+ import {Use} from "../../base/use.js"
7
+ import {BaseElement} from "../../base/element.js"
8
+
9
+ export class FastcountElement extends BaseElement {
10
+ static styles = css`span{color:orange}`
11
+
12
+ attrs = dom.attrs(this).spec({value: Number})
13
+ something = {whatever: "rofl"}
14
+
15
+ render(use: Use) {
16
+ const {value = 1} = this.attrs
17
+ const $count = use.signal(0)
18
+
19
+ use.mount(() => repeat(async() => {
20
+ await nap(10)
21
+ await $count($count() + 1)
22
+ }))
23
+
24
+ return html`
25
+ <span>${$count() * value}</span>
26
+ `
27
+ }
28
+ }
29
+
@@ -1,9 +1,9 @@
1
1
 
2
2
  import {css, html} from "lit"
3
3
  import {Op} from "../../ops/op.js"
4
- import {view} from "../../views/view.js"
5
- import {cssReset} from "../../views/css-reset.js"
6
- import {anims, makeLoader} from "../../ops/loaders/make-loader.js"
4
+ import {view} from "../../view/view.js"
5
+ import {loaders} from "../../loaders/index.js"
6
+ import {cssReset} from "../../base/css-reset.js"
7
7
 
8
8
  export const LoadersView = view(use => () => {
9
9
  use.name("loaders")
@@ -11,14 +11,14 @@ export const LoadersView = view(use => () => {
11
11
 
12
12
  const op = use.once(() => Op.loading())
13
13
 
14
- const loaders = use.once(() =>
15
- Object.entries(anims).map(([key, anim]) => ({
14
+ const library = use.once(() =>
15
+ Object.entries(loaders.anims).map(([key, anim]) => ({
16
16
  key,
17
- loader: makeLoader(anim)
17
+ loader: loaders.make(anim)
18
18
  }))
19
19
  )
20
20
 
21
- return loaders.map(({key, loader}) => html`
21
+ return library.map(({key, loader}) => html`
22
22
  <div data-anim="${key}">
23
23
  <span>${key}</span>
24
24
  <span>${loader(op, () => null)}</span>
@@ -0,0 +1,21 @@
1
+
2
+ import {AttrSpec} from "../types.js"
3
+ import {onAttrs} from "./parts/on-attrs.js"
4
+ import {attrFns} from "./parts/attr-fns.js"
5
+ import {attrSpec} from "./parts/attr-spec.js"
6
+ import {AttrProxies} from "./parts/attr-proxies.js"
7
+
8
+ export function attrs(element: HTMLElement) {
9
+ const proxies = new AttrProxies(element)
10
+ return {
11
+ strings: proxies.strings,
12
+ numbers: proxies.numbers,
13
+ booleans: proxies.booleans,
14
+ on: (fn: () => void) => onAttrs(element, fn),
15
+ spec: <A extends AttrSpec>(spec: A) => attrSpec(element, spec),
16
+ }
17
+ }
18
+
19
+ attrs.get = attrFns.get
20
+ attrs.set = attrFns.set
21
+
@@ -0,0 +1,38 @@
1
+
2
+ /** fns for getting and setting html attributes of various types */
3
+ export const attrFns = {
4
+ get: {
5
+ string: (e: HTMLElement, key: string) => {
6
+ return e.getAttribute(key) ?? undefined
7
+ },
8
+ number: (e: HTMLElement, key: string) => {
9
+ const raw = e.getAttribute(key)
10
+ return (raw === null || !raw)
11
+ ? undefined
12
+ : Number(raw)
13
+ },
14
+ boolean: (e: HTMLElement, key: string) => {
15
+ const raw = e.getAttribute(key)
16
+ return raw !== null
17
+ },
18
+ },
19
+
20
+ set: {
21
+ string: (e: HTMLElement, key: string, value: string | undefined) => {
22
+ if (value === undefined) e.removeAttribute(key)
23
+ else e.setAttribute(key, value)
24
+ return true
25
+ },
26
+ number: (e: HTMLElement, key: string, value: number | undefined) => {
27
+ if (value === undefined) e.removeAttribute(key)
28
+ else e.setAttribute(key, value.toString())
29
+ return true
30
+ },
31
+ boolean: (e: HTMLElement, key: string, value: boolean | undefined) => {
32
+ if (value) e.setAttribute(key, "")
33
+ else e.removeAttribute(key)
34
+ return true
35
+ },
36
+ },
37
+ }
38
+
@@ -0,0 +1,35 @@
1
+
2
+ import {attrFns} from "./attr-fns.js"
3
+
4
+ /** a typed proxy accessor for html attributes */
5
+ export class AttrProxies {
6
+ constructor(public element: HTMLElement) {}
7
+
8
+ strings = new Proxy({}, {
9
+ get: (_t, key: string) => (
10
+ attrFns.get.string(this.element, key)
11
+ ),
12
+ set: (_t, key: string, value: string | undefined) => (
13
+ attrFns.set.string(this.element, key, value)
14
+ ),
15
+ }) as Record<string, string | undefined>
16
+
17
+ numbers = new Proxy({}, {
18
+ get: (_t, key: string) => (
19
+ attrFns.get.number(this.element, key)
20
+ ),
21
+ set: (_t, key: string, value: number | undefined) => (
22
+ attrFns.set.number(this.element, key, value)
23
+ ),
24
+ }) as Record<string, number | undefined>
25
+
26
+ booleans = new Proxy({}, {
27
+ get: (_t, key: string) => (
28
+ attrFns.get.boolean(this.element, key)
29
+ ),
30
+ set: (_t, key: string, value: boolean | undefined) => (
31
+ attrFns.set.boolean(this.element, key, value)
32
+ ),
33
+ }) as Record<string, boolean | undefined>
34
+ }
35
+
@@ -0,0 +1,29 @@
1
+
2
+ import {AttrSpec, AttrTypes} from "../../types.js"
3
+ import {attrFns} from "./attr-fns.js"
4
+
5
+ /** specify available html attributes and their types and create a proxy accessor */
6
+ export const attrSpec = <A extends AttrSpec>(
7
+ e: HTMLElement,
8
+ spec: A,
9
+ ) => new Proxy(spec, {
10
+
11
+ get: (_target, key: string) => {
12
+ switch (spec[key]) {
13
+ case String: return attrFns.get.string(e, key)
14
+ case Number: return attrFns.get.number(e, key)
15
+ case Boolean: return attrFns.get.boolean(e, key)
16
+ default: throw new Error(`invalid attribute type for "${key}"`)
17
+ }
18
+ },
19
+
20
+ set: (_target, key: string, value: any) => {
21
+ switch (spec[key]) {
22
+ case String: return attrFns.set.string(e, key, value)
23
+ case Number: return attrFns.set.number(e, key, value)
24
+ case Boolean: return attrFns.set.boolean(e, key, value)
25
+ default: throw new Error(`invalid attribute type for "${key}"`)
26
+ }
27
+ },
28
+ }) as any as AttrTypes<A>
29
+
@@ -0,0 +1,8 @@
1
+
2
+ /** respond when any attribute changes on the html element */
3
+ export function onAttrs(element: HTMLElement, fn: () => void) {
4
+ const observer = new MutationObserver(fn)
5
+ observer.observe(element, {attributes: true})
6
+ return () => observer.disconnect()
7
+ }
8
+