@e280/sly 0.2.5 → 0.3.0-2

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 (263) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +303 -614
  3. package/package.json +6 -8
  4. package/s/_archive/README.md +1221 -0
  5. package/s/_archive/view/index.ts +7 -0
  6. package/s/_archive/view/types.ts +45 -0
  7. package/s/demo/demo.bundle.ts +2 -9
  8. package/s/demo/views/counter-light.ts +13 -0
  9. package/s/demo/views/counter-shadow.ts +16 -0
  10. package/s/demo/views/demo.ts +21 -20
  11. package/s/demo/views/loaders.ts +7 -7
  12. package/s/dom/dom.ts +1 -1
  13. package/s/index.html.ts +30 -33
  14. package/s/index.ts +0 -2
  15. package/s/loaders/make.ts +1 -1
  16. package/s/loaders/parts/ascii-anim.ts +6 -8
  17. package/s/loaders/parts/error-display.ts +9 -9
  18. package/s/tests.test.ts +1 -4
  19. package/s/view/common/css-reset.ts +19 -0
  20. package/s/view/hooks/plumbing/hooks.ts +28 -0
  21. package/s/view/hooks/plumbing/hookscope.ts +12 -0
  22. package/s/view/hooks/use-css.ts +14 -0
  23. package/s/view/hooks/use-cx.ts +41 -0
  24. package/s/view/hooks/use-life.ts +17 -0
  25. package/s/view/hooks/use-mount.ts +30 -0
  26. package/s/view/hooks/use-name.ts +10 -0
  27. package/s/view/hooks/use-once.ts +9 -0
  28. package/s/view/hooks/use-op.ts +12 -0
  29. package/s/view/hooks/use-ref.ts +11 -0
  30. package/s/view/hooks/use-signal.ts +16 -0
  31. package/s/view/hooks/use-state.ts +20 -0
  32. package/s/view/hooks/use-wake.ts +8 -0
  33. package/s/view/index.ts +17 -4
  34. package/s/view/light.ts +50 -0
  35. package/s/view/parts/apply-attrs.ts +22 -0
  36. package/s/view/parts/apply-styles.ts +21 -0
  37. package/s/view/parts/cx.ts +26 -0
  38. package/s/view/parts/reactivity.ts +22 -0
  39. package/s/view/parts/sly-shadow.ts +8 -0
  40. package/s/view/shadow.ts +93 -0
  41. package/s/view/types.ts +15 -34
  42. package/x/demo/demo.bundle.js +2 -8
  43. package/x/demo/demo.bundle.js.map +1 -1
  44. package/x/demo/demo.bundle.min.js +45 -63
  45. package/x/demo/demo.bundle.min.js.map +4 -4
  46. package/x/demo/views/counter-light.d.ts +1 -0
  47. package/x/demo/views/counter-light.js +10 -0
  48. package/x/demo/views/counter-light.js.map +1 -0
  49. package/x/demo/views/counter-shadow.d.ts +1 -0
  50. package/x/demo/views/counter-shadow.js +12 -0
  51. package/x/demo/views/counter-shadow.js.map +1 -0
  52. package/x/demo/views/demo.d.ts +1 -4
  53. package/x/demo/views/demo.js +21 -20
  54. package/x/demo/views/demo.js.map +1 -1
  55. package/x/demo/views/loaders.d.ts +1 -1
  56. package/x/demo/views/loaders.js +7 -7
  57. package/x/demo/views/loaders.js.map +1 -1
  58. package/x/dom/dom.d.ts +1 -1
  59. package/x/dom/dom.js.map +1 -1
  60. package/x/index.d.ts +0 -2
  61. package/x/index.html +30 -140
  62. package/x/index.html.js +31 -31
  63. package/x/index.html.js.map +1 -1
  64. package/x/index.js +0 -2
  65. package/x/index.js.map +1 -1
  66. package/x/loaders/make.d.ts +1 -1
  67. package/x/loaders/parts/ascii-anim.d.ts +1 -1
  68. package/x/loaders/parts/ascii-anim.js +6 -7
  69. package/x/loaders/parts/ascii-anim.js.map +1 -1
  70. package/x/loaders/parts/error-display.d.ts +1 -1
  71. package/x/loaders/parts/error-display.js +9 -9
  72. package/x/loaders/parts/error-display.js.map +1 -1
  73. package/x/tests.test.js +1 -4
  74. package/x/tests.test.js.map +1 -1
  75. package/x/view/common/css-reset.js +17 -0
  76. package/x/view/common/css-reset.js.map +1 -0
  77. package/x/view/hooks/plumbing/hooks.d.ts +11 -0
  78. package/x/view/hooks/plumbing/hooks.js +26 -0
  79. package/x/view/hooks/plumbing/hooks.js.map +1 -0
  80. package/x/view/hooks/plumbing/hookscope.d.ts +10 -0
  81. package/x/view/hooks/plumbing/hookscope.js +12 -0
  82. package/x/view/hooks/plumbing/hookscope.js.map +1 -0
  83. package/x/view/hooks/use-css.d.ts +4 -0
  84. package/x/view/hooks/use-css.js +10 -0
  85. package/x/view/hooks/use-css.js.map +1 -0
  86. package/x/view/hooks/use-cx.d.ts +10 -0
  87. package/x/view/hooks/use-cx.js +33 -0
  88. package/x/view/hooks/use-cx.js.map +1 -0
  89. package/x/view/hooks/use-life.d.ts +2 -0
  90. package/x/view/hooks/use-life.js +13 -0
  91. package/x/view/hooks/use-life.js.map +1 -0
  92. package/x/{base/utils/mounts.d.ts → view/hooks/use-mount.d.ts} +1 -0
  93. package/x/{base/utils/mounts.js → view/hooks/use-mount.js} +7 -1
  94. package/x/view/hooks/use-mount.js.map +1 -0
  95. package/x/view/hooks/use-name.d.ts +2 -0
  96. package/x/view/hooks/use-name.js +8 -0
  97. package/x/view/hooks/use-name.js.map +1 -0
  98. package/x/view/hooks/use-once.d.ts +2 -0
  99. package/x/view/hooks/use-once.js +7 -0
  100. package/x/view/hooks/use-once.js.map +1 -0
  101. package/x/view/hooks/use-op.d.ts +3 -0
  102. package/x/view/hooks/use-op.js +9 -0
  103. package/x/view/hooks/use-op.js.map +1 -0
  104. package/x/view/hooks/use-ref.d.ts +5 -0
  105. package/x/view/hooks/use-ref.js +11 -0
  106. package/x/view/hooks/use-ref.js.map +1 -0
  107. package/x/view/hooks/use-signal.d.ts +3 -0
  108. package/x/view/hooks/use-signal.js +12 -0
  109. package/x/view/hooks/use-signal.js.map +1 -0
  110. package/x/view/hooks/use-state.d.ts +1 -0
  111. package/x/view/hooks/use-state.js +17 -0
  112. package/x/view/hooks/use-state.js.map +1 -0
  113. package/x/view/hooks/use-wake.d.ts +2 -0
  114. package/x/view/hooks/use-wake.js +6 -0
  115. package/x/view/hooks/use-wake.js.map +1 -0
  116. package/x/view/index.d.ts +15 -4
  117. package/x/view/index.js +15 -4
  118. package/x/view/index.js.map +1 -1
  119. package/x/view/light.d.ts +2 -0
  120. package/x/view/light.js +41 -0
  121. package/x/view/light.js.map +1 -0
  122. package/x/view/parts/apply-attrs.d.ts +2 -0
  123. package/x/view/parts/apply-attrs.js +22 -0
  124. package/x/view/parts/apply-attrs.js.map +1 -0
  125. package/x/{base/utils → view/parts}/apply-styles.js.map +1 -1
  126. package/x/view/parts/cx.d.ts +12 -0
  127. package/x/view/parts/cx.js +24 -0
  128. package/x/view/parts/cx.js.map +1 -0
  129. package/x/view/parts/reactivity.d.ts +5 -0
  130. package/x/view/parts/reactivity.js +18 -0
  131. package/x/view/parts/reactivity.js.map +1 -0
  132. package/x/view/parts/sly-shadow.d.ts +3 -0
  133. package/x/view/parts/sly-shadow.js +7 -0
  134. package/x/view/parts/sly-shadow.js.map +1 -0
  135. package/x/view/shadow.d.ts +5 -0
  136. package/x/view/shadow.js +72 -0
  137. package/x/view/shadow.js.map +1 -0
  138. package/x/view/types.d.ts +13 -21
  139. package/s/demo/views/counter.ts +0 -50
  140. package/s/demo/views/fastcount.ts +0 -29
  141. package/s/demo/views/mounting.ts +0 -36
  142. package/x/base/css-reset.js +0 -19
  143. package/x/base/css-reset.js.map +0 -1
  144. package/x/base/element.d.ts +0 -19
  145. package/x/base/element.js +0 -55
  146. package/x/base/element.js.map +0 -1
  147. package/x/base/index.d.ts +0 -5
  148. package/x/base/index.js +0 -6
  149. package/x/base/index.js.map +0 -1
  150. package/x/base/types.d.ts +0 -3
  151. package/x/base/types.js +0 -3
  152. package/x/base/types.js.map +0 -1
  153. package/x/base/use.d.ts +0 -59
  154. package/x/base/use.js +0 -129
  155. package/x/base/use.js.map +0 -1
  156. package/x/base/utils/attr-watcher.d.ts +0 -8
  157. package/x/base/utils/attr-watcher.js +0 -20
  158. package/x/base/utils/attr-watcher.js.map +0 -1
  159. package/x/base/utils/mounts.js.map +0 -1
  160. package/x/base/utils/reactor.d.ts +0 -5
  161. package/x/base/utils/reactor.js +0 -25
  162. package/x/base/utils/reactor.js.map +0 -1
  163. package/x/base/utils/states.d.ts +0 -13
  164. package/x/base/utils/states.js +0 -41
  165. package/x/base/utils/states.js.map +0 -1
  166. package/x/base/utils/use-attrs.d.ts +0 -11
  167. package/x/base/utils/use-attrs.js +0 -18
  168. package/x/base/utils/use-attrs.js.map +0 -1
  169. package/x/demo/views/counter.d.ts +0 -375
  170. package/x/demo/views/counter.js +0 -42
  171. package/x/demo/views/counter.js.map +0 -1
  172. package/x/demo/views/fastcount.d.ts +0 -12
  173. package/x/demo/views/fastcount.js +0 -21
  174. package/x/demo/views/fastcount.js.map +0 -1
  175. package/x/demo/views/mounting.d.ts +0 -3
  176. package/x/demo/views/mounting.js +0 -28
  177. package/x/demo/views/mounting.js.map +0 -1
  178. package/x/spa/index.barrel.d.ts +0 -4
  179. package/x/spa/index.barrel.js +0 -3
  180. package/x/spa/index.barrel.js.map +0 -1
  181. package/x/spa/index.d.ts +0 -2
  182. package/x/spa/index.js +0 -2
  183. package/x/spa/index.js.map +0 -1
  184. package/x/spa/plumbing/braces.d.ts +0 -12
  185. package/x/spa/plumbing/braces.js +0 -55
  186. package/x/spa/plumbing/braces.js.map +0 -1
  187. package/x/spa/plumbing/primitives.d.ts +0 -22
  188. package/x/spa/plumbing/primitives.js +0 -65
  189. package/x/spa/plumbing/primitives.js.map +0 -1
  190. package/x/spa/plumbing/router-core.d.ts +0 -13
  191. package/x/spa/plumbing/router-core.js +0 -38
  192. package/x/spa/plumbing/router-core.js.map +0 -1
  193. package/x/spa/plumbing/types.d.ts +0 -35
  194. package/x/spa/plumbing/types.js +0 -2
  195. package/x/spa/plumbing/types.js.map +0 -1
  196. package/x/spa/router.d.ts +0 -13
  197. package/x/spa/router.js +0 -39
  198. package/x/spa/router.js.map +0 -1
  199. package/x/spa/spa.test.d.ts +0 -15
  200. package/x/spa/spa.test.js +0 -78
  201. package/x/spa/spa.test.js.map +0 -1
  202. package/x/view/utils/contextualize.d.ts +0 -13
  203. package/x/view/utils/contextualize.js +0 -18
  204. package/x/view/utils/contextualize.js.map +0 -1
  205. package/x/view/utils/make-component.d.ts +0 -5
  206. package/x/view/utils/make-component.js +0 -17
  207. package/x/view/utils/make-component.js.map +0 -1
  208. package/x/view/utils/make-view.d.ts +0 -2
  209. package/x/view/utils/make-view.js +0 -32
  210. package/x/view/utils/make-view.js.map +0 -1
  211. package/x/view/utils/parts/capsule.d.ts +0 -12
  212. package/x/view/utils/parts/capsule.js +0 -56
  213. package/x/view/utils/parts/capsule.js.map +0 -1
  214. package/x/view/utils/parts/chain.d.ts +0 -13
  215. package/x/view/utils/parts/chain.js +0 -26
  216. package/x/view/utils/parts/chain.js.map +0 -1
  217. package/x/view/utils/parts/context.d.ts +0 -9
  218. package/x/view/utils/parts/context.js +0 -10
  219. package/x/view/utils/parts/context.js.map +0 -1
  220. package/x/view/utils/parts/directive.d.ts +0 -5
  221. package/x/view/utils/parts/directive.js +0 -20
  222. package/x/view/utils/parts/directive.js.map +0 -1
  223. package/x/view/utils/parts/naked.d.ts +0 -18
  224. package/x/view/utils/parts/naked.js +0 -57
  225. package/x/view/utils/parts/naked.js.map +0 -1
  226. package/x/view/utils/parts/sly-view.d.ts +0 -6
  227. package/x/view/utils/parts/sly-view.js +0 -16
  228. package/x/view/utils/parts/sly-view.js.map +0 -1
  229. package/x/view/view.d.ts +0 -11
  230. package/x/view/view.js +0 -15
  231. package/x/view/view.js.map +0 -1
  232. /package/s/{base → _archive/base}/css-reset.ts +0 -0
  233. /package/s/{base → _archive/base}/element.ts +0 -0
  234. /package/s/{base → _archive/base}/index.ts +0 -0
  235. /package/s/{base → _archive/base}/types.ts +0 -0
  236. /package/s/{base → _archive/base}/use.ts +0 -0
  237. /package/s/{base → _archive/base}/utils/apply-styles.ts +0 -0
  238. /package/s/{base → _archive/base}/utils/attr-watcher.ts +0 -0
  239. /package/s/{base → _archive/base}/utils/mounts.ts +0 -0
  240. /package/s/{base → _archive/base}/utils/reactor.ts +0 -0
  241. /package/s/{base → _archive/base}/utils/states.ts +0 -0
  242. /package/s/{base → _archive/base}/utils/use-attrs.ts +0 -0
  243. /package/s/{spa → _archive/spa}/index.barrel.ts +0 -0
  244. /package/s/{spa → _archive/spa}/index.ts +0 -0
  245. /package/s/{spa → _archive/spa}/plumbing/braces.ts +0 -0
  246. /package/s/{spa → _archive/spa}/plumbing/primitives.ts +0 -0
  247. /package/s/{spa → _archive/spa}/plumbing/router-core.ts +0 -0
  248. /package/s/{spa → _archive/spa}/plumbing/types.ts +0 -0
  249. /package/s/{spa → _archive/spa}/router.ts +0 -0
  250. /package/s/{spa → _archive/spa}/spa.test.ts +0 -0
  251. /package/s/{view → _archive/view}/utils/contextualize.ts +0 -0
  252. /package/s/{view → _archive/view}/utils/make-component.ts +0 -0
  253. /package/s/{view → _archive/view}/utils/make-view.ts +0 -0
  254. /package/s/{view → _archive/view}/utils/parts/capsule.ts +0 -0
  255. /package/s/{view → _archive/view}/utils/parts/chain.ts +0 -0
  256. /package/s/{view → _archive/view}/utils/parts/context.ts +0 -0
  257. /package/s/{view → _archive/view}/utils/parts/directive.ts +0 -0
  258. /package/s/{view → _archive/view}/utils/parts/naked.ts +0 -0
  259. /package/s/{view → _archive/view}/utils/parts/sly-view.ts +0 -0
  260. /package/s/{view → _archive/view}/view.ts +0 -0
  261. /package/x/{base → view/common}/css-reset.d.ts +0 -0
  262. /package/x/{base/utils → view/parts}/apply-styles.d.ts +0 -0
  263. /package/x/{base/utils → view/parts}/apply-styles.js +0 -0
package/README.md CHANGED
@@ -4,234 +4,168 @@
4
4
  # 🦝 sly
5
5
  > *mischievous shadow views*
6
6
 
7
+ ```sh
8
+ npm install lit @e280/sly @e280/strata @e280/stz
9
+ ```
10
+
7
11
  [@e280](https://e280.org/)'s new [lit](https://lit.dev/)-based frontend webdev library.
8
12
 
9
- - 🍋 [**#views**](#views) — shadow-dom'd, hooks-based, componentizable
10
- - 🪵 [**#base-element**](#base-element) — for a more classical experience
11
- - 🪄 [**#dom**](#dom) — the "it's not jquery" multitool
13
+ - 🎭 [**#views**](#views) — light-dom or shadow-dom reactive lit views
14
+ - 🪝 [**#hooks**](#hooks) — full reference of available view hooks
12
15
  - 🫛 [**#ops**](#ops) — reactive tooling for async operations
13
16
  - ⏳ [**#loaders**](#loaders) — animated loading spinners for rendering ops
14
- - 💅 [**#spa**](#spa) — hash routing for your spa-day
15
17
  - 🪙 [**#loot**](#loot) — drag-and-drop facilities
18
+ - 🪄 [**#dom**](#dom) — the "it's not jquery" multitool
16
19
  - 🧪 https://sly.e280.org/ — our testing page
17
- - **✨[shiny](https://shiny.e280.org/)✨** — our wip component library
18
20
 
19
21
 
20
22
 
21
23
  <br/><br/>
24
+ <a id="views"></a>
22
25
 
23
- ## 🦝 sly and friends
24
- > `@e280/sly`
26
+ ## 🎭 views
27
+ > *reactive views, light or shadow*
25
28
 
26
- ```sh
27
- npm install @e280/sly lit @e280/strata @e280/stz
28
- ```
29
+ - 🪶 **no compile step** — just god's honest javascript, via [lit](https://lit.dev/)-html tagged-template-literals
30
+ - **reactive** views auto-rerender whenever any [strata](https://github.com/e280/strata)-compatible state changes
31
+ - 🪝 **hooks-based** — familiar react-style [hooks](#hooks)
32
+ - 🌗 **light or shadow** — render directly in the dom, or inside a shadow-dom bubble
29
33
 
30
- > [!NOTE]
31
- > - 🔥 [lit](https://lit.dev/), for html rendering
32
- > - ⛏️ [@e280/strata](https://github.com/e280/strata), for state management (signals, state trees)
33
- > - 🏂 [@e280/stz](https://github.com/e280/stz), our ts standard library
34
- > - 🐢 [@e280/scute](https://github.com/e280/scute), our buildy-bundly-buddy
34
+ ```ts
35
+ import {html} from "lit"
36
+ import {light, shadow, dom} from "@e280/sly"
35
37
 
36
- > [!TIP]
37
- > you can import everything in sly from `@e280/sly`,
38
- > or from specific subpackages like `@e280/sly/view`, `@e280/sly/dom`, etc...
38
+ export const MyLightView = light(() => html`<p>blinded by the light</p>`)
39
39
 
40
+ export const MyShadowView = shadow(() => html`<p>shrouded in darkness</p>`)
41
+ ```
40
42
 
43
+ ### 🌞 light views
44
+ > *just pretend it's react*
41
45
 
42
- <br/><br/>
43
- <a id="views"></a>
46
+ - **define a light view**
47
+ ```ts
48
+ import {html} from "lit"
49
+ import {light, useSignal} from "@e280/sly"
44
50
 
45
- ## 🍋🦝 sly views
46
- > `@e280/sly/view`
47
- > *the crown jewel of sly*
51
+ export const MyCounter = light((start: number) => {
52
+ const $count = useSignal(start)
53
+ const increment = () => $count.value++
48
54
 
49
- ```ts
50
- view(use => () => html`<p>hello world</p>`)
51
- ```
55
+ return html`
56
+ <button @click="${increment}">${$count.value}</button>
57
+ `
58
+ })
59
+ ```
60
+ - **render it into the dom**
61
+ ```ts
62
+ dom.render(dom(".demo"), html`
63
+ <h1>my cool counter demo</h1>
64
+ ${MyCounter(123)}
65
+ `)
66
+ ```
52
67
 
53
- - 🪶 **no compile step** — just god's honest javascript, via [lit](https://lit.dev/)-html tagged-template-literals
54
- - 🥷 **shadow dom'd** — each view gets its own cozy [shadow](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM) bubble, and supports [slots](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots)
55
- - 🪝 **hooks-based** — declarative rendering with the [`use`](#use) family of ergonomic hooks
56
- - ⚡ **reactive** — they auto-rerender whenever any [strata](https://github.com/e280/strata)-compatible state changes
57
- - 🧐 **not components, per se** — they're comfy typescript-native ui building blocks [(technically, lit directives)](https://lit.dev/docs/templates/custom-directives/)
58
- - 🧩 **componentizable** — any view can be magically converted into a proper [web component](https://developer.mozilla.org/en-US/docs/Web/API/Web_components)
68
+ ### 🌚 shadow views
69
+ > *each shadow view gets its own cozy [shadow-dom](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM) bubble and supports [slots](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots)*
59
70
 
60
- ### 🍋 view example
61
- ```ts
62
- import {view, dom, BaseElement} from "@e280/sly"
63
- import {html, css} from "lit"
64
- ```
65
- - **declare view**
71
+ - **define a shadow view**
66
72
  ```ts
67
- export const CounterView = view(use => (start: number) => {
68
- use.styles(css`p {color: green}`)
73
+ import {css, html} from "lit"
74
+ import {shadow, useName, useCss, useSignal} from "@e280/sly"
75
+
76
+ export const MyShadowCounter = shadow((start: number) => {
77
+ useName("shadow-counter")
78
+ useCss(css`button { color: cyan }`)
69
79
 
70
- const $count = use.signal(start)
80
+ const $count = useSignal(start)
71
81
  const increment = () => $count.value++
72
82
 
73
83
  return html`
74
- <button @click="${increment}">
75
- ${$count.value}
76
- </button>
84
+ <button @click="${increment}">${$count()}</button>
85
+ <slot></slot>
77
86
  `
78
87
  })
79
88
  ```
80
- - `$count` is a [strata signal](https://github.com/e280/strata#readme) *(we like those)*
81
- - **inject view into dom**
89
+ - **render it into the dom**
82
90
  ```ts
83
- dom.in(".app").render(html`
84
- <h1>cool counter demo</h1>
85
- ${CounterView(1)}
91
+ dom.render(dom(".demo"), html`
92
+ <h1>my cool counter demo</h1>
93
+ ${MyShadowCounter(234)}
86
94
  `)
87
95
  ```
88
- - 🤯 **register view as web component**
96
+ - **.with to nest children or set attrs**
89
97
  ```ts
90
- dom.register({
91
- MyCounter: CounterView
92
- .component()
93
- .props(() => [1]),
94
- })
98
+ dom.render(dom(".demo"), html`
99
+ <h1>my cool counter demo</h1>
100
+
101
+ ${MyShadowCounter.with({
102
+ props: [234],
103
+ attrs: {"data-whatever": 555},
104
+ children: html`
105
+ <p>woah, slotting support!</p>
106
+ `,
107
+ })}
108
+ `)
95
109
  ```
96
- ```html
97
- <my-counter></my-counter>
110
+ - **you can do custom shadow config if needed**
111
+ ```ts
112
+ const MyShadowView = shadow.config(() => {
113
+ const host = document.createElement("div")
114
+ const shadow = host.attachShadow({mode: "open"})
115
+ return {host, shadow}
116
+ })(() => html`<p>shrouded in darkness</p>`)
98
117
  ```
99
118
 
100
- ### 🍋 view settings
101
- - optional settings for views you should know about
119
+
120
+
121
+ <br/><br/>
122
+ <a id="hooks"></a>
123
+
124
+ ## 🪝 hooks
125
+ > *composable view state and utilities*
126
+
127
+ ### 👮 follow the hooks rules
128
+
129
+ just like [react hooks](https://react.dev/warnings/invalid-hook-call-warning), the execution order of sly's `use` hooks actually matters.
130
+
131
+ you must not call these hooks under `if` conditionals, or `for` loops, or in callbacks, or after a conditional `return` statement, or anything like that.. *otherwise, heed my warning: weird bad stuff will happen..*
132
+
133
+ ### 🌚 shadow-only hooks
134
+ - **useName** — *(shadow only)* — set the "view" attribute value
102
135
  ```ts
103
- export const CoolView = view
104
- .settings({mode: "open", delegatesFocus: true})
105
- .render(use => (greeting: string) => html`😎 ${greeting} <slot></slot>`)
136
+ useName("squarepants")
137
+ // <sly-shadow view="squarepants">
106
138
  ```
107
- - all [attachShadow params](https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow#parameters) (like `mode` and `delegatesFocus`) are valid `settings`
108
- - note the `<slot></slot>` we'll use in the next example lol
109
-
110
- ### 🍋 view chains
111
- - views have this sick chaining syntax for supplying more stuff at the template injection site
139
+ - **useCss** *(shadow only)* — attach stylesheets (use lit's `css`!) to the shadow root
112
140
  ```ts
113
- dom.in(".app").render(html`
114
- <h2>cool example</h2>
115
- ${CoolView
116
- .props("hello")
117
- .attr("class", "hero")
118
- .children(html`<em>spongebob</em>`)
119
- .render()}
120
- `)
141
+ useCss(css1, css2, css3)
121
142
  ```
122
- - `props`provide props and start a view chain
123
- - `attr` — set html attributes on the `<sly-view>` host element
124
- - `children` — add nested [slottable](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots) content
125
- - `render` — end the view chain and render the lit directive
126
-
127
- ### 🍋 view/component universality
128
- - **you can start with a view,**
143
+ - **useHost***(shadow only)* — get the host element
129
144
  ```ts
130
- export const GreeterView = view(use => (name: string) => {
131
- return html`<p>hello ${name}</p>`
132
- })
145
+ const host = useHost()
133
146
  ```
134
- - view usage
135
- ```ts
136
- GreeterView("pimsley")
137
- ```
138
- **then you can convert it to a component.**
139
- ```ts
140
- export class GreeterComponent extends (
141
- GreeterView
142
- .component()
143
- .props(component => [component.getAttribute("name") ?? "unknown"])
144
- ) {}
145
- ```
146
- - html usage
147
- ```html
148
- <greeter-component name="pimsley"></greeter-component>
149
- ```
150
- - **you can start with a component,**
151
- ```ts
152
- export class GreeterComponent extends (
153
- view(use => (name: string) => {
154
- return html`<p>hello ${name}</p>`
155
- })
156
- .component()
157
- .props(component => [component.getAttribute("name") ?? "unknown"])
158
- ) {}
159
- ```
160
- - html usage
161
- ```html
162
- <greeter-component name="pimsley"></greeter-component>
163
- ```
164
- **and it already has `.view` ready for you.**
165
- - view usage
166
- ```ts
167
- GreeterComponent.view("pimsley")
168
- ```
169
- - **understanding `.component(BaseElement)` and `.props(fn)`**
170
- - `.props` takes a fn that is called every render, which returns the props given to the view
171
- ```ts
172
- .props(() => ["pimsley"])
173
- ```
174
- the props fn receives the component instance, so you can query html attributes or instance properties
175
- ```ts
176
- .props(component => [component.getAttribute("name") ?? "unknown"])
177
- ```
178
- - `.component` accepts a subclass of `BaseElement`, so you can define your own properties and methods for your component class
179
- ```ts
180
- const GreeterComponent = GreeterView
181
-
182
- // declare your own custom class
183
- .component(class extends BaseElement {
184
- $name = signal("jim raynor")
185
- updateName(name: string) {
186
- this.$name.value = name
187
- }
188
- })
189
-
190
- // props gets the right types on 'component'
191
- .props(component => [component.$name.value])
192
- ```
193
- - `.component` provides the devs interacting with your component, with noice typings
194
- ```ts
195
- dom<GreeterComponent>("greeter-component").updateName("mortimer")
196
- ```
197
- - typescript class wizardry
198
- - ❌ smol-brain approach exports class value, but NOT the typings
199
- ```ts
200
- export const GreeterComponent = (...)
201
- ```
202
- - ✅ giga-brain approach exports class value AND the typings
203
- ```ts
204
- export class GreeterComponent extends (...) {}
205
- ```
206
- - **register web components to the dom**
207
- ```ts
208
- dom.register({GreeterComponent})
209
- ```
210
- - **oh and don't miss out on the insta-component shorthand**
211
- ```ts
212
- dom.register({
213
- QuickComponent: view.component(use => html`⚡ incredi`),
214
- })
147
+ - **useShadow** — *(shadow only)* — get the shadow root
148
+ ```ts
149
+ const shadow = useShadow()
215
150
  ```
216
151
 
217
- <a id="use"></a>
218
-
219
- ### 🍋 "use" hooks reference
220
- - 👮 **follow the hooks rules**
221
- > just like [react hooks](https://react.dev/warnings/invalid-hook-call-warning), the execution order of sly's `use` hooks actually matters..
222
- > you must not call these hooks under `if` conditionals, or `for` loops, or in callbacks, or after a conditional `return` statement, or anything like that.. *otherwise, heed my warning: weird bad stuff will happen..*
223
- - **use.name** — set the "view" attr value, eg `<sly-view view="squarepants">`
152
+ ### 🌞 universal hooks
153
+ - **useState** — react-like hook to create some reactive state (we prefer signals)
224
154
  ```ts
225
- use.name("squarepants")
155
+ const [count, setCount] = useState(0)
156
+
157
+ const increment = () => setCount(n => n + 1)
226
158
  ```
227
- - **use.styles** — attach stylesheets into the view's shadow dom
159
+ - **useRef** — react-like hook to make a non-reactive box for a value
228
160
  ```ts
229
- use.styles(css1, css2, css3)
161
+ const ref = useRef(0)
162
+
163
+ ref.current // 0
164
+ ref.current = 1 // does not trigger rerender
230
165
  ```
231
- *(alias `use.css`)*
232
- - **use.signal** — create a [strata signal](https://github.com/e280/strata)
166
+ - **useSignal** — create a [strata](https://github.com/e280/strata) signal
233
167
  ```ts
234
- const $count = use.signal(1)
168
+ const $count = useSignal(1)
235
169
 
236
170
  // read the signal
237
171
  $count()
@@ -239,391 +173,95 @@ import {html, css} from "lit"
239
173
  // write the signal
240
174
  $count(2)
241
175
  ```
242
- - `derived` signals
243
- ```ts
244
- const $product = use.derived(() => $count() * $whatever())
245
- ```
246
- - `lazy` signals
247
- ```ts
248
- const $product = use.lazy(() => $count() * $whatever())
249
- ```
250
- - go read the [strata readme](https://github.com/e280/strata) about this stuff
251
- - **use.once** run fn at initialization, and return a value
252
- ```ts
253
- const whatever = use.once(() => {
254
- console.log("happens only once")
176
+ - see [strata readme](https://github.com/e280/strata)
177
+ - **useDerived** — create a [strata](https://github.com/e280/strata) derived signal
178
+ ```ts
179
+ const $product = useDerived(() => $count() * $whatever())
180
+ ```
181
+ - see [strata readme](https://github.com/e280/strata)
182
+ - **useOnce** run fn at initialization, and return a value
183
+ ```ts
184
+ const whatever = useOnce(() => {
185
+ console.log("happens one time")
255
186
  return 123
256
187
  })
257
188
 
258
189
  whatever // 123
259
190
  ```
260
- - **use.mount** — setup mount/unmount lifecycle
191
+ - **useMount** — setup mount/unmount lifecycle
261
192
  ```ts
262
- use.mount(() => {
263
- console.log("view mounted")
264
-
265
- return () => {
266
- console.log("view unmounted")
267
- }
193
+ useMount(() => {
194
+ console.log("mounted")
195
+ return () => console.log("unmounted")
268
196
  })
269
197
  ```
270
- - **use.wake** — run fn each time mounted, and return value
198
+ - **useWake** — run fn each time mounted, and return value
271
199
  ```ts
272
- const whatever = use.wake(() => {
273
- console.log("view mounted")
200
+ const whatever = useWake(() => {
201
+ console.log("mounted")
274
202
  return 123
275
203
  })
276
204
 
277
205
  whatever // 123
278
206
  ```
279
- - **use.life** — mount/unmount lifecycle, but also return a value
207
+ - **useLife** — mount/unmount lifecycle, but also return a value
280
208
  ```ts
281
- const v = use.life(() => {
209
+ const whatever = useLife(() => {
282
210
  console.log("mounted")
283
211
  const value = 123
284
212
  return [value, () => console.log("unmounted")]
285
213
  })
286
214
 
287
- v // 123
215
+ whatever // 123
288
216
  ```
289
- - **use.events** — attach event listeners to the element (auto-cleaned up)
217
+ - **useRender** — returns a fn to rerender the view (debounced)
290
218
  ```ts
291
- use.events({
292
- keydown: (e: KeyboardEvent) => console.log("keydown", e.code),
293
- keyup: (e: KeyboardEvent) => console.log("keyup", e.code),
294
- })
219
+ const render = useRender()
220
+
221
+ render().then(() => console.log("render done"))
295
222
  ```
296
- - **use.states** — [internal states](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/states) helper
223
+ - **useRendered** — get a promise that resolves *after* the next render
297
224
  ```ts
298
- const states = use.states()
299
- states.assign("active", "cool")
300
- ```
301
- ```css
302
- [view="my-view"]::state(active) { color: yellow; }
303
- [view="my-view"]::state(cool) { outline: 1px solid cyan; }
304
- ```
305
- - **use.attrs** — ergonomic typed html attribute access
306
- - `use.attrs` is similar to [#dom.attrs](#dom.attrs)
307
- ```ts
308
- const attrs = use.attrs({
309
- name: String,
310
- count: Number,
311
- active: Boolean,
312
- })
313
- ```
314
- ```ts
315
- attrs.name // "chase"
316
- attrs.count // 123
317
- attrs.active // true
318
- ```
319
- - use.attrs.{strings/numbers/booleans}
320
- ```ts
321
- use.attrs.strings.name // "chase"
322
- use.attrs.numbers.count // 123
323
- use.attrs.booleans.active // true
324
- ```
325
- - use.attrs.on
326
- ```ts
327
- use.attrs.on(() => console.log("an attribute changed"))
328
- ```
329
- - **use.render** — rerender the view (debounced)
330
- ```ts
331
- use.render()
332
- ```
333
- - **use.renderNow** — rerender the view instantly (not debounced)
334
- ```ts
335
- use.renderNow()
336
- ```
337
- - **use.rendered** — promise that resolves *after* the next render
338
- ```ts
339
- use.rendered.then(() => {
340
- const slot = use.shadow.querySelector("slot")
341
- console.log(slot)
342
- })
225
+ useRendered().then(() => console.log("rendered"))
343
226
  ```
344
- - **use.op** — start with an op based on an async fn
227
+ - **useOp** — start loading an op based on an async fn
345
228
  ```ts
346
- const op = use.op(async() => {
229
+ const op = useOp(async() => {
347
230
  await nap(5000)
348
231
  return 123
349
232
  })
350
233
  ```
351
- - **use.op.promise** — start with an op based on a promise
234
+ - **useOpPromise** — start loading an op based on a promise
352
235
  ```ts
353
- const op = use.op.promise(doAsyncWork())
236
+ const op = useOpPromise(doAsyncWork())
354
237
  ```
355
238
 
356
- ### 🍋 "use" recipes
239
+ ### 🧑‍🍳 happy hooks recipes
357
240
  - make a ticker — mount, cycle, and nap
358
241
  ```ts
359
242
  import {cycle, nap} from "@e280/stz"
360
243
  ```
361
244
  ```ts
362
- const $seconds = use.signal(0)
245
+ const $seconds = useSignal(0)
363
246
 
364
- use.mount(() => cycle(async() => {
247
+ useMount(() => cycle(async() => {
365
248
  await nap(1000)
366
249
  $seconds.value++
367
250
  }))
368
251
  ```
369
252
  - wake + rendered, to do something after each mount's first render
370
253
  ```ts
371
- use.wake(() => use.rendered.then(() => {
254
+ useWake(() => useRendered.then(() => {
372
255
  console.log("after first render")
373
256
  }))
374
257
  ```
375
258
 
376
259
 
377
260
 
378
- <br/><br/>
379
- <a id="base-element"></a>
380
-
381
- ## 🪵🦝 sly base element
382
- > `@e280/sly/base`
383
- > *the classic experience*
384
-
385
- ```ts
386
- import {BaseElement, Use, dom} from "@e280/sly"
387
- import {html, css} from "lit"
388
- ```
389
-
390
- `BaseElement` is more of an old-timey class-based "boomer" approach to making web components, but with a millennial twist — its `render` method gives you the same `use` hooks that views enjoy.
391
-
392
- 👮 a *BaseElement* is not a *View*, and cannot be converted into a *View*.
393
-
394
- ### 🪵 let's clarify some sly terminology
395
- - "Element"
396
- - an html element; any subclass of the browser's HTMLElement
397
- - all genuine ["web components"](https://developer.mozilla.org/en-US/docs/Web/API/Web_components) are elements
398
- - "BaseElement"
399
- - sly's own subclass of the browser-native HTMLElement
400
- - is a true element and web component (can be registered to the dom)
401
- - "View"
402
- - sly's own magic concept that uses a lit-directive to render stuff
403
- - NOT an element or web component (can NOT be registered to the dom)
404
- - NOT related to BaseElement
405
- - can be converted into a Component via `view.component().props(() => [])`
406
- - "Component"
407
- - a sly view that has been converted into an element
408
- - is a true element and web component (can be registered to the dom)
409
- - actually a subclass of BaseElement
410
- - actually contains the view on `Component.view`
411
-
412
- ### 🪵 base element setup
413
- - **declare your element class**
414
- ```ts
415
- export class MyElement extends BaseElement {
416
- static styles = css`span{color:orange}`
417
-
418
- // custom property
419
- $start = signal(10)
420
-
421
- // custom attributes
422
- attrs = dom.attrs(this).spec({
423
- multiply: Number,
424
- })
425
-
426
- // custom methods
427
- hello() {
428
- return "world"
429
- }
430
-
431
- render(use: Use) {
432
- const $count = use.signal(1)
433
- const increment = () => $count.value++
434
-
435
- const {$start} = this
436
- const {multiply = 1} = this.attrs
437
- const result = $start() + (multiply * $count())
438
-
439
- return html`
440
- <span>${result}</span>
441
- <button @click="${increment}">+</button>
442
- `
443
- }
444
- }
445
- ```
446
- - **register your element to the dom**
447
- ```ts
448
- dom.register({MyElement})
449
- ```
450
-
451
- ### 🪵 base element usage
452
- - **place the element in your html body**
453
- ```html
454
- <body>
455
- <my-element></my-element>
456
- </body>
457
- ```
458
- - **now you can interact with it**
459
- ```ts
460
- const myElement = dom<MyElement>("my-element")
461
-
462
- // js property
463
- myElement.$start(100)
464
-
465
- // html attributes
466
- myElement.attrs.multiply = 2
467
-
468
- // methods
469
- myElement.hello()
470
- // "world"
471
- ```
472
-
473
-
474
-
475
- <br/><br/>
476
- <a id="dom"></a>
477
-
478
- ## 🪄🦝 sly dom
479
- > `@e280/sly/dom`
480
- > *the "it's not jquery!" multitool*
481
-
482
- ```ts
483
- import {dom} from "@e280/sly"
484
- ```
485
-
486
- ### 🪄 dom queries
487
- - `require` an element
488
- ```ts
489
- dom(".demo")
490
- // HTMLElement (or throws)
491
- ```
492
- ```ts
493
- // alias
494
- dom.require(".demo")
495
- // HTMLElement (or throws)
496
- ```
497
- - `maybe` get an element
498
- ```ts
499
- dom.maybe(".demo")
500
- // HTMLElement | undefined
501
- ```
502
- - `all` matching elements in an array
503
- ```ts
504
- dom.all(".demo ul li")
505
- // HTMLElement[]
506
- ```
507
-
508
- ### 🪄 dom.in scope
509
- - make a scope
510
- ```ts
511
- dom.in(".demo") // selector
512
- // Dom instance
513
- ```
514
- ```ts
515
- dom.in(demoElement) // element
516
- // Dom instance
517
- ```
518
- - run queries in that scope
519
- ```ts
520
- dom.in(demoElement).require(".button")
521
- ```
522
- ```ts
523
- dom.in(demoElement).maybe(".button")
524
- ```
525
- ```ts
526
- dom.in(demoElement).all("ol li")
527
- ```
528
-
529
- ### 🪄 dom utilities
530
- - `dom.register` web components
531
- ```ts
532
- dom.register({MyComponent, AnotherCoolComponent})
533
- // <my-component>
534
- // <another-cool-component>
535
- ```
536
- - `dom.register` automatically dashes the tag names (`MyComponent` becomes `<my-component>`)
537
- - `dom.render` content into an element
538
- ```ts
539
- dom.render(element, html`<p>hello world</p>`)
540
- ```
541
- ```ts
542
- dom.in(".demo").render(html`<p>hello world</p>`)
543
- ```
544
- - `dom.el` little element builder
545
- ```ts
546
- const div = dom.el("div", {"data-whatever": 123, "data-active": true})
547
- // <div data-whatever="123" data-active></div>
548
- ```
549
- - `dom.elmer` make an element with a fluent chain
550
- ```ts
551
- const div = dom.elmer("div")
552
- .attr("data-whatever", 123)
553
- .attr("data-active")
554
- .children("hello world")
555
- .done()
556
- // HTMLElement
557
- ```
558
- - `dom.mk` make an element with a lit template (returns the first)
559
- ```ts
560
- const div = dom.mk(html`
561
- <div data-whatever="123" data-active>
562
- hello world
563
- </div>
564
- `) // HTMLElement
565
- ```
566
- - `dom.events` <a id="dom.events"></a> to attach event listeners
567
- ```ts
568
- const detach = dom.events(element, {
569
- keydown: (e: KeyboardEvent) => console.log("keydown", e.code),
570
- keyup: (e: KeyboardEvent) => console.log("keyup", e.code),
571
- })
572
- ```
573
- ```ts
574
- const detach = dom.in(".demo").events({
575
- keydown: (e: KeyboardEvent) => console.log("keydown", e.code),
576
- keyup: (e: KeyboardEvent) => console.log("keyup", e.code),
577
- })
578
- ```
579
- ```ts
580
- // unattach those event listeners when you're done
581
- detach()
582
- ```
583
- - `dom.attrs` <a id="dom.attrs"></a> to setup a type-happy html attribute helper
584
- ```ts
585
- const attrs = dom.attrs(element).spec({
586
- name: String,
587
- count: Number,
588
- active: Boolean,
589
- })
590
- ```
591
- ```ts
592
- const attrs = dom.in(".demo").attrs.spec({
593
- name: String,
594
- count: Number,
595
- active: Boolean,
596
- })
597
- ```
598
- ```ts
599
- attrs.name // "chase"
600
- attrs.count // 123
601
- attrs.active // true
602
- ```
603
- ```ts
604
- attrs.name = "zenky"
605
- attrs.count = 124
606
- attrs.active = false // removes html attr
607
- ```
608
- ```ts
609
- attrs.name = undefined // removes the attr
610
- attrs.count = undefined // removes the attr
611
- ```
612
- or if you wanna be more loosey-goosey, skip the spec
613
- ```ts
614
- const a = dom.in(".demo").attrs
615
- a.strings.name = "pimsley"
616
- a.numbers.count = 125
617
- a.booleans.active = true
618
- ```
619
-
620
-
621
-
622
261
  <br/><br/>
623
262
  <a id="ops"></a>
624
263
 
625
- ## 🫛🦝 sly ops
626
- > `@e280/sly/ops`
264
+ ## 🫛 ops
627
265
  > *tools for async operations and loading spinners*
628
266
 
629
267
  ```ts
@@ -732,8 +370,7 @@ import {Pod, podium, Op, loaders} from "@e280/sly"
732
370
  <br/><br/>
733
371
  <a id="loaders"></a>
734
372
 
735
- ## ⏳🦝 sly loaders
736
- > `@e280/sly/loaders`
373
+ ## loaders
737
374
  > *animated loading spinners for ops*
738
375
 
739
376
  ```ts
@@ -765,100 +402,10 @@ import {loaders} from "@e280/sly"
765
402
 
766
403
 
767
404
 
768
- <br/><br/>
769
- <a id="spa"></a>
770
-
771
- ## 💅🦝 sly spa
772
- > `@e280/sly/spa`
773
- > *hash router for single-page-apps*
774
-
775
- ```ts
776
- import {spa, html} from "@e280/sly"
777
- ```
778
-
779
- ### 💅 spa.Router basics
780
- - **make a spa router**
781
- ```ts
782
- const router = new spa.Router({
783
- routes: {
784
- home: spa.route("#/", async() => html`home`),
785
- settings: spa.route("#/settings", async() => html`settings`),
786
- user: spa.route("#/user/{userId}", async({userId}) => html`user ${userId}`),
787
- },
788
- })
789
- ```
790
- - all route strings must start with `#/`
791
- - use braces like `{userId}` to accept string params
792
- - home-equivalent hashes like `""` and `"#"` are normalized to `"#/"`
793
- - the router has an effect on the appearance of the url in the browser address bar -- the home `#/` is removed, aesthetically, eg, `e280.org/#/` is rewritten to `e280.org` using *history.replaceState*
794
- - you can provide `loader` option if you want to specify the loading spinner (defaults to `loaders.make()`)
795
- - you can provide `notFound` option, if you want to specify what is shown on invalid routes (defaults to `() => null`)
796
- - when `auto` is true (default), the router calls `.refresh()` and `.listen()` in the constructor.. set it to `false` if you want manual control
797
- - you can set `auto` option false if you want to omit the default initial refresh and listen calls
798
- - **render your current page**
799
- ```ts
800
- return html`
801
- <div class="my-page">
802
- ${router.render()}
803
- </div>
804
- `
805
- ```
806
- - returns lit content
807
- - shows a loading spinner when pages are loading
808
- - will display the notFound content for invalid routes (defaults to null)
809
- - **perform navigations**
810
- - go to settings page
811
- ```ts
812
- await router.nav.settings.go()
813
- // goes to "#/settings"
814
- ```
815
- - go to user page
816
- ```ts
817
- await router.nav.user.go("123")
818
- // goes to "#/user/123"
819
- ```
820
-
821
- ### 💅 spa.Router advanced
822
- - **generate a route's hash string**
823
- ```ts
824
- const hash = router.nav.user.hash("123")
825
- // "#/user/123"
826
-
827
- html`<a href="${hash}">user 123</a>`
828
- ```
829
- - **check if a route is the currently-active one**
830
- ```ts
831
- const hash = router.nav.user.active
832
- // true
833
- ```
834
- - **force-refresh the router**
835
- ```ts
836
- await router.refresh()
837
- ```
838
- - **force-navigate the router by hash**
839
- ```ts
840
- await router.refresh("#/user/123")
841
- ```
842
- - **get the current hash string (normalized)**
843
- ```ts
844
- router.hash
845
- // "#/user/123"
846
- ```
847
- - **the `route(...)` helper fn enables the braces-params syntax**
848
- - but, if you wanna do it differently, you *can* implement your own hash parser to do your own funky syntax
849
- - **dispose the router when you're done with it**
850
- ```ts
851
- router.dispose()
852
- // stop listening to hashchange events
853
- ```
854
-
855
-
856
-
857
405
  <br/><br/>
858
406
  <a id="loot"></a>
859
407
 
860
- ## 🪙🦝 loot
861
- > `@e280/sly/loot`
408
+ ## 🪙 loot
862
409
  > *drag-and-drop facilities*
863
410
 
864
411
  ```ts
@@ -881,7 +428,7 @@ import {ev} from "@e280/stz"
881
428
  - **attach event listeners to your dropzone,** one of these ways:
882
429
  - **view example**
883
430
  ```ts
884
- view(() => () => html`
431
+ light(() => html`
885
432
  <div
886
433
  ?data-indicator="${drops.$indicator()}"
887
434
  @dragover="${drops.dragover}"
@@ -937,9 +484,9 @@ import {ev} from "@e280/stz"
937
484
  ```
938
485
  - **attach dragzone listeners** (there can be many dragzones...)
939
486
  ```ts
940
- view(use => () => {
941
- const money = use.once((): Money => ({value: 280}))
942
- const dragzone = use.once(() => dnd.dragzone(() => money))
487
+ light(() => {
488
+ const money = useOnce((): Money => ({value: 280}))
489
+ const dragzone = useOnce(() => dnd.dragzone(() => money))
943
490
 
944
491
  return html`
945
492
  <div
@@ -953,9 +500,9 @@ import {ev} from "@e280/stz"
953
500
  ```
954
501
  - **attach dropzone listeners** (there can be many dropzones...)
955
502
  ```ts
956
- view(use => () => {
957
- const bag = use.once((): Bag => ({id: 1}))
958
- const dropzone = use.once(() => dnd.dropzone(() => bag))
503
+ light(() => {
504
+ const bag = useOnce((): Bag => ({id: 1}))
505
+ const dropzone = useOnce(() => dnd.dropzone(() => bag))
959
506
  const indicator = !!(dnd.dragging && dnd.hovering === bag)
960
507
 
961
508
  return html`
@@ -978,13 +525,155 @@ import {ev} from "@e280/stz"
978
525
 
979
526
 
980
527
  <br/><br/>
981
- <a id="e280"></a>
528
+ <a id="dom"></a>
982
529
 
983
- ## 🧑‍💻🦝 sly is by e280
984
- reward us with github stars
985
- build with us at https://e280.org/ but only if you're cool
530
+ ## 🪄 dom
531
+ > *the "it's not jquery!" multitool*
532
+
533
+ ```ts
534
+ import {dom} from "@e280/sly"
535
+ ```
536
+
537
+ ### 🪄 dom queries
538
+ - `require` an element
539
+ ```ts
540
+ dom(".demo")
541
+ // HTMLElement (or throws)
542
+ ```
543
+ ```ts
544
+ // alias
545
+ dom.require(".demo")
546
+ // HTMLElement (or throws)
547
+ ```
548
+ - `maybe` get an element
549
+ ```ts
550
+ dom.maybe(".demo")
551
+ // HTMLElement | undefined
552
+ ```
553
+ - `all` matching elements in an array
554
+ ```ts
555
+ dom.all(".demo ul li")
556
+ // HTMLElement[]
557
+ ```
558
+
559
+ ### 🪄 dom.in scope
560
+ - make a scope
561
+ ```ts
562
+ dom.in(".demo") // selector
563
+ // Dom instance
564
+ ```
565
+ ```ts
566
+ dom.in(demoElement) // element
567
+ // Dom instance
568
+ ```
569
+ - run queries in that scope
570
+ ```ts
571
+ dom.in(demoElement).require(".button")
572
+ ```
573
+ ```ts
574
+ dom.in(demoElement).maybe(".button")
575
+ ```
576
+ ```ts
577
+ dom.in(demoElement).all("ol li")
578
+ ```
579
+
580
+ ### 🪄 dom utilities
581
+ - `dom.register` web components
582
+ ```ts
583
+ dom.register({MyComponent, AnotherCoolComponent})
584
+ // <my-component>
585
+ // <another-cool-component>
586
+ ```
587
+ - `dom.register` automatically dashes the tag names (`MyComponent` becomes `<my-component>`)
588
+ - `dom.render` content into an element
589
+ ```ts
590
+ dom.render(element, html`<p>hello world</p>`)
591
+ ```
592
+ ```ts
593
+ dom.in(".demo").render(html`<p>hello world</p>`)
594
+ ```
595
+ - `dom.el` little element builder
596
+ ```ts
597
+ const div = dom.el("div", {"data-whatever": 123, "data-active": true})
598
+ // <div data-whatever="123" data-active></div>
599
+ ```
600
+ - `dom.elmer` make an element with a fluent chain
601
+ ```ts
602
+ const div = dom.elmer("div")
603
+ .attr("data-whatever", 123)
604
+ .attr("data-active")
605
+ .children("hello world")
606
+ .done()
607
+ // HTMLElement
608
+ ```
609
+ - `dom.mk` make an element with a lit template (returns the first)
610
+ ```ts
611
+ const div = dom.mk(html`
612
+ <div data-whatever="123" data-active>
613
+ hello world
614
+ </div>
615
+ `) // HTMLElement
616
+ ```
617
+ - `dom.events` <a id="dom.events"></a> to attach event listeners
618
+ ```ts
619
+ const detach = dom.events(element, {
620
+ keydown: (e: KeyboardEvent) => console.log("keydown", e.code),
621
+ keyup: (e: KeyboardEvent) => console.log("keyup", e.code),
622
+ })
623
+ ```
624
+ ```ts
625
+ const detach = dom.in(".demo").events({
626
+ keydown: (e: KeyboardEvent) => console.log("keydown", e.code),
627
+ keyup: (e: KeyboardEvent) => console.log("keyup", e.code),
628
+ })
629
+ ```
630
+ ```ts
631
+ // unattach those event listeners when you're done
632
+ detach()
633
+ ```
634
+ - `dom.attrs` <a id="dom.attrs"></a> to setup a type-happy html attribute helper
635
+ ```ts
636
+ const attrs = dom.attrs(element).spec({
637
+ name: String,
638
+ count: Number,
639
+ active: Boolean,
640
+ })
641
+ ```
642
+ ```ts
643
+ const attrs = dom.in(".demo").attrs.spec({
644
+ name: String,
645
+ count: Number,
646
+ active: Boolean,
647
+ })
648
+ ```
649
+ ```ts
650
+ attrs.name // "chase"
651
+ attrs.count // 123
652
+ attrs.active // true
653
+ ```
654
+ ```ts
655
+ attrs.name = "zenky"
656
+ attrs.count = 124
657
+ attrs.active = false // removes html attr
658
+ ```
659
+ ```ts
660
+ attrs.name = undefined // removes the attr
661
+ attrs.count = undefined // removes the attr
662
+ ```
663
+ or if you wanna be more loosey-goosey, skip the spec
664
+ ```ts
665
+ const {attrs} = dom.in(".demo")
666
+ attrs.strings.name = "pimsley"
667
+ attrs.numbers.count = 125
668
+ attrs.booleans.active = true
669
+ ```
986
670
 
987
671
 
988
672
 
989
673
  <br/><br/>
674
+ <a id="e280"></a>
675
+
676
+ ## 🧑‍💻 sly is by e280
677
+ reward us with github stars
678
+ build with us at https://e280.org/ but only if you're cool
990
679