@e280/sly 0.2.0-8 → 0.2.0

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 (293) hide show
  1. package/README.md +400 -98
  2. package/package.json +14 -7
  3. package/s/base/element.ts +76 -0
  4. package/s/base/index.ts +6 -0
  5. package/s/{views → base}/use.ts +22 -14
  6. package/s/base/utils/attr-watcher.ts +22 -0
  7. package/s/base/utils/reactor.ts +32 -0
  8. package/s/base/utils/states.ts +49 -0
  9. package/s/base/utils/use-attrs.ts +36 -0
  10. package/s/demo/demo.bundle.ts +6 -7
  11. package/s/demo/views/counter.ts +21 -24
  12. package/s/demo/views/demo.ts +9 -6
  13. package/s/demo/views/{incredi.ts → fastcount.ts} +7 -6
  14. package/s/demo/views/loaders.ts +7 -7
  15. package/s/dom/attrs/attrs.ts +21 -0
  16. package/s/dom/attrs/parts/attr-fns.ts +68 -0
  17. package/s/dom/attrs/parts/attr-proxies.ts +35 -0
  18. package/s/dom/attrs/parts/attr-spec.ts +29 -0
  19. package/s/dom/attrs/parts/on-attrs.ts +8 -0
  20. package/s/dom/dom.ts +23 -60
  21. package/s/dom/index.ts +4 -0
  22. package/s/dom/parts/dom-scope.ts +46 -0
  23. package/s/dom/parts/el.ts +14 -0
  24. package/s/dom/parts/elmer.ts +38 -0
  25. package/s/dom/parts/eve.ts +24 -0
  26. package/s/dom/parts/mk.ts +9 -0
  27. package/s/dom/parts/queries.ts +26 -0
  28. package/s/dom/{register.ts → parts/register.ts} +2 -10
  29. package/s/dom/types.ts +50 -0
  30. package/s/index.html.ts +4 -3
  31. package/s/index.ts +7 -20
  32. package/s/loaders/index.barrel.ts +10 -0
  33. package/s/loaders/index.ts +4 -0
  34. package/s/loaders/make.ts +14 -0
  35. package/s/loaders/mock.ts +11 -0
  36. package/s/{ops/loaders → loaders}/parts/anims.ts +1 -1
  37. package/s/{ops/loaders → loaders}/parts/ascii-anim.ts +6 -5
  38. package/s/{ops/loaders → loaders}/parts/error-display.ts +2 -2
  39. package/s/loaders/types.ts +6 -0
  40. package/s/loot/index.barrel.ts +5 -0
  41. package/s/loot/index.ts +2 -3
  42. package/s/ops/index.ts +5 -0
  43. package/s/ops/op.ts +1 -0
  44. package/s/spa/index.barrel.ts +6 -0
  45. package/s/spa/index.ts +4 -0
  46. package/s/spa/plumbing/braces.ts +76 -0
  47. package/s/spa/plumbing/primitives.ts +85 -0
  48. package/s/spa/plumbing/router-core.ts +49 -0
  49. package/s/spa/plumbing/types.ts +45 -0
  50. package/s/spa/router.ts +49 -0
  51. package/s/spa/spa.test.ts +91 -0
  52. package/s/tests.test.ts +4 -1
  53. package/s/view/index.ts +7 -0
  54. package/s/view/types.ts +39 -0
  55. package/s/view/utils/contextualize.ts +45 -0
  56. package/s/view/utils/make-component.ts +34 -0
  57. package/s/view/utils/make-view.ts +48 -0
  58. package/s/view/utils/parts/capsule.ts +67 -0
  59. package/s/view/utils/parts/chain.ts +40 -0
  60. package/s/view/utils/parts/context.ts +11 -0
  61. package/s/view/utils/parts/directive.ts +29 -0
  62. package/s/view/utils/parts/sly-view.ts +15 -0
  63. package/s/view/view.ts +24 -0
  64. package/x/base/css-reset.js.map +1 -0
  65. package/x/base/element.d.ts +19 -0
  66. package/x/base/element.js +52 -0
  67. package/x/base/element.js.map +1 -0
  68. package/x/base/index.d.ts +4 -0
  69. package/x/base/index.js +5 -0
  70. package/x/base/index.js.map +1 -0
  71. package/x/{views → base}/use.d.ts +6 -2
  72. package/x/{views → base}/use.js +13 -8
  73. package/x/base/use.js.map +1 -0
  74. package/x/base/utils/apply-styles.js.map +1 -0
  75. package/x/base/utils/attr-watcher.d.ts +8 -0
  76. package/x/base/utils/attr-watcher.js +20 -0
  77. package/x/base/utils/attr-watcher.js.map +1 -0
  78. package/x/base/utils/mounts.js.map +1 -0
  79. package/x/base/utils/reactor.d.ts +5 -0
  80. package/x/base/utils/reactor.js +25 -0
  81. package/x/base/utils/reactor.js.map +1 -0
  82. package/x/base/utils/states.d.ts +13 -0
  83. package/x/base/utils/states.js +41 -0
  84. package/x/base/utils/states.js.map +1 -0
  85. package/x/base/utils/use-attrs.d.ts +11 -0
  86. package/x/base/utils/use-attrs.js +18 -0
  87. package/x/base/utils/use-attrs.js.map +1 -0
  88. package/x/demo/demo.bundle.js +6 -6
  89. package/x/demo/demo.bundle.js.map +1 -1
  90. package/x/demo/demo.bundle.min.js +17 -23
  91. package/x/demo/demo.bundle.min.js.map +4 -4
  92. package/x/demo/views/counter.d.ts +374 -1
  93. package/x/demo/views/counter.js +19 -22
  94. package/x/demo/views/counter.js.map +1 -1
  95. package/x/demo/views/demo.d.ts +4 -1
  96. package/x/demo/views/demo.js +9 -5
  97. package/x/demo/views/demo.js.map +1 -1
  98. package/x/demo/views/{incredi.d.ts → fastcount.d.ts} +3 -3
  99. package/x/demo/views/{incredi.js → fastcount.js} +6 -6
  100. package/x/demo/views/fastcount.js.map +1 -0
  101. package/x/demo/views/loaders.js +6 -6
  102. package/x/demo/views/loaders.js.map +1 -1
  103. package/x/dom/attrs/attrs.d.ts +23 -0
  104. package/x/dom/attrs/attrs.js +17 -0
  105. package/x/dom/attrs/attrs.js.map +1 -0
  106. package/x/dom/attrs/parts/attr-fns.d.ts +16 -0
  107. package/x/dom/attrs/parts/attr-fns.js +64 -0
  108. package/x/dom/attrs/parts/attr-fns.js.map +1 -0
  109. package/x/dom/attrs/parts/attr-proxies.d.ts +8 -0
  110. package/x/dom/attrs/parts/attr-proxies.js +21 -0
  111. package/x/dom/attrs/parts/attr-proxies.js.map +1 -0
  112. package/x/dom/attrs/parts/attr-spec.d.ts +3 -0
  113. package/x/dom/attrs/parts/attr-spec.js +21 -0
  114. package/x/dom/attrs/parts/attr-spec.js.map +1 -0
  115. package/x/dom/attrs/parts/on-attrs.d.ts +2 -0
  116. package/x/dom/attrs/parts/on-attrs.js +7 -0
  117. package/x/dom/attrs/parts/on-attrs.js.map +1 -0
  118. package/x/dom/dom.d.ts +16 -22
  119. package/x/dom/dom.js +21 -47
  120. package/x/dom/dom.js.map +1 -1
  121. package/x/dom/index.d.ts +2 -0
  122. package/x/dom/index.js +3 -0
  123. package/x/dom/index.js.map +1 -0
  124. package/x/dom/parts/dashify.js.map +1 -0
  125. package/x/dom/parts/dom-scope.d.ts +15 -0
  126. package/x/dom/parts/dom-scope.js +35 -0
  127. package/x/dom/parts/dom-scope.js.map +1 -0
  128. package/x/dom/parts/el.d.ts +2 -0
  129. package/x/dom/parts/el.js +7 -0
  130. package/x/dom/parts/el.js.map +1 -0
  131. package/x/dom/parts/elmer.d.ts +11 -0
  132. package/x/dom/parts/elmer.js +32 -0
  133. package/x/dom/parts/elmer.js.map +1 -0
  134. package/x/dom/parts/eve.d.ts +7 -0
  135. package/x/dom/parts/eve.js +16 -0
  136. package/x/dom/parts/eve.js.map +1 -0
  137. package/x/dom/parts/mk.d.ts +2 -0
  138. package/x/dom/parts/mk.js +7 -0
  139. package/x/dom/parts/mk.js.map +1 -0
  140. package/x/dom/parts/queries.d.ts +4 -0
  141. package/x/dom/parts/queries.js +13 -0
  142. package/x/dom/parts/queries.js.map +1 -0
  143. package/x/dom/{register.d.ts → parts/register.d.ts} +2 -10
  144. package/x/dom/parts/register.js.map +1 -0
  145. package/x/dom/types.d.ts +22 -0
  146. package/x/{views → dom}/types.js.map +1 -1
  147. package/x/index.d.ts +7 -17
  148. package/x/index.html +6 -5
  149. package/x/index.html.js +4 -3
  150. package/x/index.html.js.map +1 -1
  151. package/x/index.js +7 -17
  152. package/x/index.js.map +1 -1
  153. package/x/loaders/index.barrel.d.ts +7 -0
  154. package/x/loaders/index.barrel.js +7 -0
  155. package/x/loaders/index.barrel.js.map +1 -0
  156. package/x/loaders/index.d.ts +2 -0
  157. package/x/loaders/index.js +2 -0
  158. package/x/loaders/index.js.map +1 -0
  159. package/x/loaders/make.d.ts +3 -0
  160. package/x/loaders/make.js +6 -0
  161. package/x/loaders/make.js.map +1 -0
  162. package/x/loaders/mock.d.ts +2 -0
  163. package/x/loaders/mock.js +8 -0
  164. package/x/loaders/mock.js.map +1 -0
  165. package/x/{ops/loaders → loaders}/parts/anims.d.ts +1 -1
  166. package/x/loaders/parts/anims.js.map +1 -0
  167. package/x/{ops/loaders → loaders}/parts/ascii-anim.d.ts +2 -2
  168. package/x/{ops/loaders → loaders}/parts/ascii-anim.js +4 -4
  169. package/x/loaders/parts/ascii-anim.js.map +1 -0
  170. package/x/loaders/parts/error-display.d.ts +1 -0
  171. package/x/{ops/loaders → loaders}/parts/error-display.js +2 -2
  172. package/x/loaders/parts/error-display.js.map +1 -0
  173. package/x/loaders/types.d.ts +3 -0
  174. package/x/loaders/types.js +2 -0
  175. package/x/loaders/types.js.map +1 -0
  176. package/x/loot/index.barrel.d.ts +3 -0
  177. package/x/loot/index.barrel.js +4 -0
  178. package/x/loot/index.barrel.js.map +1 -0
  179. package/x/loot/index.d.ts +2 -3
  180. package/x/loot/index.js +1 -3
  181. package/x/loot/index.js.map +1 -1
  182. package/x/ops/index.d.ts +3 -0
  183. package/x/ops/index.js +4 -0
  184. package/x/ops/index.js.map +1 -0
  185. package/x/ops/op.js +1 -0
  186. package/x/ops/op.js.map +1 -1
  187. package/x/spa/index.barrel.d.ts +4 -0
  188. package/x/spa/index.barrel.js +3 -0
  189. package/x/spa/index.barrel.js.map +1 -0
  190. package/x/spa/index.d.ts +2 -0
  191. package/x/spa/index.js +2 -0
  192. package/x/spa/index.js.map +1 -0
  193. package/x/spa/plumbing/braces.d.ts +12 -0
  194. package/x/spa/plumbing/braces.js +55 -0
  195. package/x/spa/plumbing/braces.js.map +1 -0
  196. package/x/spa/plumbing/primitives.d.ts +22 -0
  197. package/x/spa/plumbing/primitives.js +65 -0
  198. package/x/spa/plumbing/primitives.js.map +1 -0
  199. package/x/spa/plumbing/router-core.d.ts +13 -0
  200. package/x/spa/plumbing/router-core.js +38 -0
  201. package/x/spa/plumbing/router-core.js.map +1 -0
  202. package/x/spa/plumbing/types.d.ts +35 -0
  203. package/x/spa/plumbing/types.js +2 -0
  204. package/x/spa/plumbing/types.js.map +1 -0
  205. package/x/spa/router.d.ts +13 -0
  206. package/x/spa/router.js +39 -0
  207. package/x/spa/router.js.map +1 -0
  208. package/x/spa/spa.test.d.ts +15 -0
  209. package/x/spa/spa.test.js +78 -0
  210. package/x/spa/spa.test.js.map +1 -0
  211. package/x/tests.test.js +4 -1
  212. package/x/tests.test.js.map +1 -1
  213. package/x/view/index.d.ts +5 -0
  214. package/x/view/index.js +6 -0
  215. package/x/view/index.js.map +1 -0
  216. package/x/view/types.d.ts +21 -0
  217. package/x/view/types.js +2 -0
  218. package/x/view/types.js.map +1 -0
  219. package/x/view/utils/contextualize.d.ts +13 -0
  220. package/x/view/utils/contextualize.js +18 -0
  221. package/x/view/utils/contextualize.js.map +1 -0
  222. package/x/view/utils/make-component.d.ts +5 -0
  223. package/x/view/utils/make-component.js +17 -0
  224. package/x/view/utils/make-component.js.map +1 -0
  225. package/x/view/utils/make-view.d.ts +2 -0
  226. package/x/view/utils/make-view.js +24 -0
  227. package/x/view/utils/make-view.js.map +1 -0
  228. package/x/view/utils/parts/capsule.d.ts +13 -0
  229. package/x/view/utils/parts/capsule.js +49 -0
  230. package/x/view/utils/parts/capsule.js.map +1 -0
  231. package/x/view/utils/parts/chain.d.ts +13 -0
  232. package/x/view/utils/parts/chain.js +26 -0
  233. package/x/view/utils/parts/chain.js.map +1 -0
  234. package/x/view/utils/parts/context.d.ts +9 -0
  235. package/x/view/utils/parts/context.js +10 -0
  236. package/x/view/utils/parts/context.js.map +1 -0
  237. package/x/view/utils/parts/directive.d.ts +5 -0
  238. package/x/view/utils/parts/directive.js +18 -0
  239. package/x/view/utils/parts/directive.js.map +1 -0
  240. package/x/view/utils/parts/sly-view.d.ts +5 -0
  241. package/x/view/utils/parts/sly-view.js +13 -0
  242. package/x/view/utils/parts/sly-view.js.map +1 -0
  243. package/x/view/view.d.ts +11 -0
  244. package/x/view/view.js +15 -0
  245. package/x/view/view.js.map +1 -0
  246. package/s/dom/attributes.ts +0 -89
  247. package/s/ops/loaders/make-loader.ts +0 -18
  248. package/s/views/base-element.ts +0 -84
  249. package/s/views/types.ts +0 -40
  250. package/s/views/utils/apply-attrs.ts +0 -33
  251. package/s/views/view.ts +0 -150
  252. package/x/demo/views/incredi.js.map +0 -1
  253. package/x/dom/attributes.d.ts +0 -10
  254. package/x/dom/attributes.js +0 -46
  255. package/x/dom/attributes.js.map +0 -1
  256. package/x/dom/dashify.js.map +0 -1
  257. package/x/dom/register.js.map +0 -1
  258. package/x/ops/loaders/make-loader.d.ts +0 -5
  259. package/x/ops/loaders/make-loader.js +0 -7
  260. package/x/ops/loaders/make-loader.js.map +0 -1
  261. package/x/ops/loaders/parts/anims.js.map +0 -1
  262. package/x/ops/loaders/parts/ascii-anim.js.map +0 -1
  263. package/x/ops/loaders/parts/error-display.d.ts +0 -1
  264. package/x/ops/loaders/parts/error-display.js.map +0 -1
  265. package/x/views/base-element.d.ts +0 -14
  266. package/x/views/base-element.js +0 -62
  267. package/x/views/base-element.js.map +0 -1
  268. package/x/views/css-reset.js.map +0 -1
  269. package/x/views/types.d.ts +0 -31
  270. package/x/views/use.js.map +0 -1
  271. package/x/views/utils/apply-attrs.d.ts +0 -2
  272. package/x/views/utils/apply-attrs.js +0 -21
  273. package/x/views/utils/apply-attrs.js.map +0 -1
  274. package/x/views/utils/apply-styles.js.map +0 -1
  275. package/x/views/utils/mounts.js.map +0 -1
  276. package/x/views/view.d.ts +0 -9
  277. package/x/views/view.js +0 -116
  278. package/x/views/view.js.map +0 -1
  279. /package/s/{views → base}/css-reset.ts +0 -0
  280. /package/s/{views → base}/utils/apply-styles.ts +0 -0
  281. /package/s/{views → base}/utils/mounts.ts +0 -0
  282. /package/s/dom/{dashify.ts → parts/dashify.ts} +0 -0
  283. /package/x/{views → base}/css-reset.d.ts +0 -0
  284. /package/x/{views → base}/css-reset.js +0 -0
  285. /package/x/{views → base}/utils/apply-styles.d.ts +0 -0
  286. /package/x/{views → base}/utils/apply-styles.js +0 -0
  287. /package/x/{views → base}/utils/mounts.d.ts +0 -0
  288. /package/x/{views → base}/utils/mounts.js +0 -0
  289. /package/x/dom/{dashify.d.ts → parts/dashify.d.ts} +0 -0
  290. /package/x/dom/{dashify.js → parts/dashify.js} +0 -0
  291. /package/x/dom/{register.js → parts/register.js} +0 -0
  292. /package/x/{views → dom}/types.js +0 -0
  293. /package/x/{ops/loaders → loaders}/parts/anims.js +0 -0
package/package.json CHANGED
@@ -1,12 +1,19 @@
1
1
  {
2
2
  "name": "@e280/sly",
3
- "version": "0.2.0-8",
3
+ "version": "0.2.0",
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,15 +23,15 @@
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.2",
27
+ "@e280/stz": "^0.2.14"
21
28
  },
22
29
  "devDependencies": {
23
- "@e280/science": "^0.1.2",
24
- "@e280/scute": "^0.0.0",
30
+ "@e280/science": "^0.1.3",
31
+ "@e280/scute": "^0.1.1",
25
32
  "http-server": "^14.1.1",
26
33
  "npm-run-all": "^4.1.5",
27
- "typescript": "^5.9.2"
34
+ "typescript": "^5.9.3"
28
35
  },
29
36
  "scripts": {
30
37
  "build": "run-s _clean _ln _tsc _scute",
@@ -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,6 @@
1
+
2
+ export * from "./utils/states.js"
3
+ export * from "./css-reset.js"
4
+ export * from "./element.js"
5
+ export * from "./use.js"
6
+
@@ -1,22 +1,25 @@
1
1
 
2
2
  import {CSSResultGroup} from "lit"
3
- import {defer, MapG} from "@e280/stz"
3
+ import {defer, GMap} from "@e280/stz"
4
4
  import {signal, SignalOptions} from "@e280/strata/signals"
5
5
 
6
6
  import {Op} from "../ops/op.js"
7
- import {dom} from "../dom/dom.js"
8
7
  import {Mounts} from "./utils/mounts.js"
8
+ import {States} from "./utils/states.js"
9
+ import {eve, EveSpec} from "../dom/parts/eve.js"
9
10
  import {applyStyles} from "./utils/apply-styles.js"
10
- import {AttrSpec, onAttrChange} from "../dom/attributes.js"
11
+ import {useAttrs, UseAttrs} from "./utils/use-attrs.js"
11
12
 
12
13
  export const _wrap = Symbol()
13
14
  export const _disconnect = Symbol()
14
15
  export const _reconnect = Symbol()
15
16
 
16
17
  export class Use {
18
+ readonly attrs: UseAttrs
19
+
17
20
  #runs = 0
18
21
  #position = 0
19
- #values = new MapG<number, any>()
22
+ #values = new GMap<number, any>()
20
23
  #rendered = defer()
21
24
  #mounts = new Mounts()
22
25
 
@@ -38,11 +41,13 @@ export class Use {
38
41
  }
39
42
 
40
43
  constructor(
41
- public element: HTMLElement,
42
- public shadow: ShadowRoot,
43
- public renderNow: () => void,
44
- public render: () => Promise<void>,
45
- ) {}
44
+ public element: HTMLElement,
45
+ public shadow: ShadowRoot,
46
+ public renderNow: () => void,
47
+ public render: () => Promise<void>,
48
+ ) {
49
+ this.attrs = useAttrs(this)
50
+ }
46
51
 
47
52
  get renderCount() {
48
53
  return this.#runs
@@ -65,11 +70,6 @@ export class Use {
65
70
  return this.styles(...styles)
66
71
  }
67
72
 
68
- attrs<A extends AttrSpec>(spec: A) {
69
- this.mount(() => onAttrChange(this.element, this.render))
70
- return this.once(() => dom.attrs(this.element, spec))
71
- }
72
-
73
73
  once<V>(fn: () => V) {
74
74
  return this.#values.guarantee(this.#position++, fn) as V
75
75
  }
@@ -92,6 +92,14 @@ export class Use {
92
92
  return this.life(() => [fn(), () => {}])
93
93
  }
94
94
 
95
+ events(spec: EveSpec) {
96
+ return this.mount(() => eve(this.element, spec))
97
+ }
98
+
99
+ states<S extends string = string>() {
100
+ return this.once(() => new States<S>(this.element))
101
+ }
102
+
95
103
  op = (() => {
96
104
  const that = this
97
105
  function op<V>(f: () => Promise<V>) {
@@ -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 {GMap} from "@e280/stz"
3
+ import {tracker} from "@e280/strata"
4
+
5
+ export class Reactor {
6
+ #map = new GMap<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,49 @@
1
+
2
+ export class States<S extends string = string> {
3
+ #states: Set<S>
4
+
5
+ constructor(element: HTMLElement) {
6
+ this.#states = element.attachInternals().states as any
7
+ }
8
+
9
+ ;[Symbol.iterator]() {
10
+ return this.#states.values()
11
+ }
12
+
13
+ values() {
14
+ return this.#states.values()
15
+ }
16
+
17
+ forEach(fn: (s: S) => void) {
18
+ this.#states.forEach(fn)
19
+ return this
20
+ }
21
+
22
+ get size() {
23
+ return this.#states.size
24
+ }
25
+
26
+ has(state: S) {
27
+ return this.#states.has(state)
28
+ }
29
+
30
+ add(...states: S[]) {
31
+ for (const s of states) this.#states.add(s)
32
+ return this
33
+ }
34
+
35
+ delete(...states: S[]) {
36
+ for (const s of states) this.#states.delete(s)
37
+ return this
38
+ }
39
+
40
+ clear() {
41
+ this.#states.clear()
42
+ return this
43
+ }
44
+
45
+ assign(...states: S[]) {
46
+ return this.clear().add(...states)
47
+ }
48
+ }
49
+
@@ -0,0 +1,36 @@
1
+
2
+ import {Use} from "../use.js"
3
+ import {dom} from "../../dom/dom.js"
4
+ import {AttrSpec, AttrTypes} from "../../dom/types.js"
5
+
6
+ export type UseAttrs = {
7
+ <A extends AttrSpec>(spec: A): AttrTypes<A>
8
+ strings: Record<string, undefined | string>
9
+ numbers: Record<string, undefined | number>
10
+ booleans: Record<string, undefined | boolean>
11
+ spec<A extends AttrSpec>(spec: A): AttrTypes<A>
12
+ on(fn: () => void): void
13
+ }
14
+
15
+ export function useAttrs(use: Use): UseAttrs {
16
+ const attrs = dom.attrs(use.element)
17
+
18
+ function attrsFn<A extends AttrSpec>(spec: A) {
19
+ return use.once(() => attrs.spec<A>(spec))
20
+ }
21
+
22
+ attrsFn.strings = attrs.strings
23
+ attrsFn.numbers = attrs.numbers
24
+ attrsFn.booleans = attrs.booleans
25
+
26
+ attrsFn.spec = <A extends AttrSpec>(spec: A) => {
27
+ return use.once(() => attrs.spec<A>(spec))
28
+ }
29
+
30
+ attrsFn.on = (fn: () => void) => {
31
+ return use.mount(() => attrs.on(fn))
32
+ }
33
+
34
+ return attrsFn
35
+ }
36
+
@@ -1,14 +1,13 @@
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
- import {IncrediElement} from "./views/incredi.js"
6
-
7
- dom.in(".demo").render(DemoView())
3
+ import {CounterComponent} from "./views/counter.js"
4
+ import {DemoComponent} from "./views/demo.js"
5
+ import {FastcountElement} from "./views/fastcount.js"
8
6
 
9
7
  dom.register({
10
- IncrediElement,
11
- DemoCounter: CounterView.component(1),
8
+ DemoComponent,
9
+ CounterComponent,
10
+ FastcountElement,
12
11
  })
13
12
 
14
13
  console.log("🦝 sly")
@@ -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
- .derived(() => $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,19 +1,22 @@
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 {
@@ -1,21 +1,22 @@
1
1
 
2
2
  import {css, html} from "lit"
3
- import {nap, repeat} from "@e280/stz"
3
+ import {nap, cycle} from "@e280/stz"
4
4
 
5
5
  import {dom} from "../../dom/dom.js"
6
- import {Use} from "../../views/use.js"
7
- import {BaseElement} from "../../views/base-element.js"
6
+ import {Use} from "../../base/use.js"
7
+ import {BaseElement} from "../../base/element.js"
8
8
 
9
- export class IncrediElement extends BaseElement {
9
+ export class FastcountElement extends BaseElement {
10
10
  static styles = css`span{color:orange}`
11
- attrs = dom.attrs(this, {value: Number})
11
+
12
+ attrs = dom.attrs(this).spec({value: Number})
12
13
  something = {whatever: "rofl"}
13
14
 
14
15
  render(use: Use) {
15
16
  const {value = 1} = this.attrs
16
17
  const $count = use.signal(0)
17
18
 
18
- use.mount(() => repeat(async() => {
19
+ use.mount(() => cycle(async() => {
19
20
  await nap(10)
20
21
  await $count($count() + 1)
21
22
  }))
@@ -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 {attrSpec} from "./parts/attr-spec.js"
5
+ import {AttrProxies} from "./parts/attr-proxies.js"
6
+ import {attrGet, attrSet} from "./parts/attr-fns.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 = attrGet
20
+ attrs.set = attrSet
21
+
@@ -0,0 +1,68 @@
1
+
2
+ import {AttrValue} from "../../types.js"
3
+
4
+ /** get html attributes */
5
+ export const attrGet = {
6
+ string: (e: HTMLElement, key: string) => {
7
+ return e.getAttribute(key) ?? undefined
8
+ },
9
+ number: (e: HTMLElement, key: string) => {
10
+ const raw = e.getAttribute(key)
11
+ return (raw === null || !raw)
12
+ ? undefined
13
+ : Number(raw)
14
+ },
15
+ boolean: (e: HTMLElement, key: string) => {
16
+ const raw = e.getAttribute(key)
17
+ return raw !== null
18
+ },
19
+ }
20
+
21
+ /** set html attributes */
22
+ export const attrSet = {
23
+ string: (e: HTMLElement, key: string, value: string | undefined) => {
24
+ if (value === undefined) e.removeAttribute(key)
25
+ else e.setAttribute(key, value)
26
+ return true
27
+ },
28
+ number: (e: HTMLElement, key: string, value: number | undefined) => {
29
+ if (value === undefined) e.removeAttribute(key)
30
+ else e.setAttribute(key, value.toString())
31
+ return true
32
+ },
33
+ boolean: (e: HTMLElement, key: string, value: boolean | undefined) => {
34
+ if (value) e.setAttribute(key, "")
35
+ else e.removeAttribute(key)
36
+ return true
37
+ },
38
+
39
+ any: (element: HTMLElement, key: string, value: AttrValue) => {
40
+ if (value === undefined || value === null)
41
+ element.removeAttribute(key)
42
+
43
+ else if (typeof value === "string")
44
+ element.setAttribute(key, value)
45
+
46
+ else if (typeof value === "number")
47
+ element.setAttribute(key, value.toString())
48
+
49
+ else if (typeof value === "boolean") {
50
+ if (value === true)
51
+ element.setAttribute(key, "")
52
+ else
53
+ element.removeAttribute(key)
54
+ }
55
+
56
+ else console.warn(`invalid attribute "${key}" type is "${typeof value}"`)
57
+ },
58
+
59
+ entries: (element: HTMLElement, entries: Iterable<[key: string, value: AttrValue]>) => {
60
+ for (const [key, value] of entries)
61
+ attrSet.any(element, key, value)
62
+ },
63
+
64
+ record: (element: HTMLElement, record: Record<string, AttrValue>) => {
65
+ return attrSet.entries(element, Object.entries(record))
66
+ },
67
+ }
68
+
@@ -0,0 +1,35 @@
1
+
2
+ import {attrGet, attrSet} 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
+ attrGet.string(this.element, key)
11
+ ),
12
+ set: (_t, key: string, value: string | undefined) => (
13
+ attrSet.string(this.element, key, value)
14
+ ),
15
+ }) as Record<string, string | undefined>
16
+
17
+ numbers = new Proxy({}, {
18
+ get: (_t, key: string) => (
19
+ attrGet.number(this.element, key)
20
+ ),
21
+ set: (_t, key: string, value: number | undefined) => (
22
+ attrSet.number(this.element, key, value)
23
+ ),
24
+ }) as Record<string, number | undefined>
25
+
26
+ booleans = new Proxy({}, {
27
+ get: (_t, key: string) => (
28
+ attrGet.boolean(this.element, key)
29
+ ),
30
+ set: (_t, key: string, value: boolean | undefined) => (
31
+ attrSet.boolean(this.element, key, value)
32
+ ),
33
+ }) as Record<string, boolean | undefined>
34
+ }
35
+
@@ -0,0 +1,29 @@
1
+
2
+ import {attrGet, attrSet} from "./attr-fns.js"
3
+ import {AttrSpec, AttrTypes} from "../../types.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 attrGet.string(e, key)
14
+ case Number: return attrGet.number(e, key)
15
+ case Boolean: return attrGet.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 attrSet.string(e, key, value)
23
+ case Number: return attrSet.number(e, key, value)
24
+ case Boolean: return attrSet.boolean(e, key, value)
25
+ default: throw new Error(`invalid attribute type for "${key}"`)
26
+ }
27
+ },
28
+ }) as any as AttrTypes<A>
29
+