@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/README.md CHANGED
@@ -4,13 +4,16 @@
4
4
  # 🦝 sly
5
5
  > *mischievous shadow views*
6
6
 
7
- [@e280](https://e280.org/)'s shiny new [lit](https://lit.dev/)-based frontend lib for webdevs. *(sly replaces its predecessor, [slate](https://github.com/benevolent-games/slate))*
8
-
9
- - 🍋 [**views**](#views) — hooks-based, shadow-dom'd, componentizable
10
- - 🪵 [**base element**](#base-element) — for a more classical experience
11
- - 🪄 [**dom**](#dom) — the "it's not jquery" multitool
12
- - 🫛 [**ops**](#ops) — tools for async operations and loading spinners
13
- - 🪙 [**loot**](#loot) — drag-and-drop facilities
7
+ [@e280](https://e280.org/)'s new [lit](https://lit.dev/)-based frontend webdev library. *(sly replaces its predecessor, [slate](https://github.com/benevolent-games/slate))*
8
+
9
+ - **✨[shiny](https://shiny.e280.org/)✨**our wip component library https://shiny.e280.org/
10
+ - 🍋 [**#views**](#views) — shadow-dom'd, hooks-based, componentizable
11
+ - 🪵 [**#base-element**](#base-element) — for a more classical experience
12
+ - 🪄 [**#dom**](#dom) — the "it's not jquery" multitool
13
+ - 🫛 [**#ops**](#ops) — reactive tooling for async operations
14
+ - ⏳ [**#loaders**](#loaders) — animated loading spinners for rendering ops
15
+ - 💅 [**#spa**](#spa) — hash routing for your spa-day
16
+ - 🪙 [**#loot**](#loot) — drag-and-drop facilities
14
17
  - 🧪 testing page — https://sly.e280.org/
15
18
 
16
19
 
@@ -18,9 +21,10 @@
18
21
  <br/><br/>
19
22
 
20
23
  ## 🦝 sly and friends
24
+ > `@e280/sly`
21
25
 
22
26
  ```sh
23
- npm install @e280/sly lit
27
+ npm install @e280/sly lit @e280/strata @e280/stz
24
28
  ```
25
29
 
26
30
  > [!NOTE]
@@ -29,75 +33,87 @@ npm install @e280/sly lit
29
33
  > - 🏂 [@e280/stz](https://github.com/e280/stz), our ts standard library
30
34
  > - 🐢 [@e280/scute](https://github.com/e280/scute), our buildy-bundly-buddy
31
35
 
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...
39
+
32
40
 
33
41
 
34
42
  <br/><br/>
35
43
  <a id="views"></a>
36
44
 
37
- ## 🦝🍋 sly views
38
- > *views are the crown jewel of sly.. shadow-dom'd.. hooks-based.. "ergonomics"..*
45
+ ## 🍋🦝 sly views
46
+ > `@e280/sly/view`
47
+ > *the crown jewel of sly*
39
48
 
40
49
  ```ts
41
50
  view(use => () => html`<p>hello world</p>`)
42
51
  ```
43
52
 
44
- - views are not [web components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components), but they do have [shadow roots](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM) and support [slots](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots)
45
- - any view can be registered as a web component, perfect for entrypoints or sharing widgets with html authors
46
- - views are typescript-native and comfy for webdevs building apps
47
- - views automatically rerender whenever any [strata-compatible](https://github.com/e280/strata) state changes
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)
48
59
 
49
60
  ### 🍋 view example
50
61
  ```ts
51
- import {view, dom} from "@e280/sly"
62
+ import {view, dom, BaseElement} from "@e280/sly"
52
63
  import {html, css} from "lit"
53
64
  ```
54
- - **declare a view**
65
+ - **declare view**
55
66
  ```ts
56
67
  export const CounterView = view(use => (start: number) => {
57
- use.name("counter")
58
68
  use.styles(css`p {color: green}`)
59
69
 
60
70
  const $count = use.signal(start)
61
71
  const increment = () => $count.value++
62
72
 
63
73
  return html`
64
- <p>count ${$count.value}</p>
65
- <button @click="${increment}">+</button>
74
+ <button @click="${increment}">
75
+ ${$count.value}
76
+ </button>
66
77
  `
67
78
  })
68
79
  ```
69
- - each view renders into a `<sly-view view="counter">` host (where "counter" is the `use.name` you provided)
70
- - **inject a view into the dom**
80
+ - `$count` is a [strata signal](https://github.com/e280/strata#readme) *(we like those)*
81
+ - **inject view into dom**
71
82
  ```ts
72
83
  dom.in(".app").render(html`
73
84
  <h1>cool counter demo</h1>
74
85
  ${CounterView(1)}
75
86
  `)
76
87
  ```
77
- - 🤯 **register a view as a web component**
88
+ - 🤯 **register view as web component**
78
89
  ```ts
79
- dom.register({MyCounter: CounterView.component(1)})
80
- // <my-counter></my-counter>
90
+ dom.register({
91
+ MyCounter: CounterView
92
+ .component()
93
+ .props(() => [1]),
94
+ })
95
+ ```
96
+ ```html
97
+ <my-counter></my-counter>
81
98
  ```
82
99
 
83
- ### 🍋 view declaration settings
84
- - special settings for views at declaration-time
100
+ ### 🍋 view settings
101
+ - optional settings for views you should know about
85
102
  ```ts
86
103
  export const CoolView = view
87
104
  .settings({mode: "open", delegatesFocus: true})
88
- .declare(use => (greeting: string) => {
89
- return html`😎 ${greeting} <slot></slot>`
90
- })
105
+ .render(use => (greeting: string) => html`😎 ${greeting} <slot></slot>`)
91
106
  ```
92
107
  - all [attachShadow params](https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow#parameters) (like `mode` and `delegatesFocus`) are valid `settings`
93
108
  - note the `<slot></slot>` we'll use in the next example lol
94
109
 
95
- ### 🍋 view injection options
96
- - options for views at the template injection site
110
+ ### 🍋 view chains
111
+ - views have this sick chaining syntax for supplying more stuff at the template injection site
97
112
  ```ts
98
113
  dom.in(".app").render(html`
99
114
  <h2>cool example</h2>
100
- ${CoolView.props("hello")
115
+ ${CoolView
116
+ .props("hello")
101
117
  .attr("class", "hero")
102
118
  .children(html`<em>spongebob</em>`)
103
119
  .render()}
@@ -105,30 +121,102 @@ import {html, css} from "lit"
105
121
  ```
106
122
  - `props` — provide props and start a view chain
107
123
  - `attr` — set html attributes on the `<sly-view>` host element
108
- - `children` — nested content in the host element, can be [slotted](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots)
124
+ - `children` — add nested [slottable](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots) content
109
125
  - `render` — end the view chain and render the lit directive
110
126
 
111
- ### 🍋 view web components
112
- - **build a component directly**
127
+ ### 🍋 view/component universality
128
+ - **you can start with a view,**
129
+ ```ts
130
+ export const GreeterView = view(use => (name: string) => {
131
+ return html`<p>hello ${name}</p>`
132
+ })
133
+ ```
134
+ - view usage
135
+ ```ts
136
+ GreeterView("pimsley")
137
+ ```
138
+ **then you can convert it to a component.**
113
139
  ```ts
114
- const MyComponent = view.component(use => html`<p>hello world</p>`)
140
+ export class GreeterComponent extends (
141
+ GreeterView
142
+ .component()
143
+ .props(component => [component.getAttribute("name") ?? "unknown"])
144
+ ) {}
115
145
  ```
116
- - notice that direct components don't take props (do `use.attrs` instead)
117
- - **convert any view into a web component**
146
+ - html usage
147
+ ```html
148
+ <greeter-component name="pimsley"></greeter-component>
149
+ ```
150
+ - **you can start with a component,**
118
151
  ```ts
119
- const MyCounter = CounterView.component(1)
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
+ ) {}
120
159
  ```
121
- - to convert a view to a component, you provide props
122
- - note that the component instance has a render method like `element.render(2)` which can take new props at runtime
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
+ ```
123
206
  - **register web components to the dom**
124
207
  ```ts
125
- dom.register({MyComponent, MyCounter})
126
- // <my-component></my-component>
127
- // <my-counter></my-counter>
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
+ })
128
215
  ```
129
- - `dom.register` automatically dashes the tag names (`MyComponent` becomes `<my-component>`)
130
216
 
131
- ### 🍋 view "use" hooks reference
217
+ <a id="use"></a>
218
+
219
+ ### 🍋 "use" hooks reference
132
220
  - 👮 **follow the hooks rules**
133
221
  > just like [react hooks](https://react.dev/warnings/invalid-hook-call-warning), the execution order of sly's `use` hooks actually matters..
134
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..*
@@ -198,20 +286,46 @@ import {html, css} from "lit"
198
286
 
199
287
  v // 123
200
288
  ```
201
- - **use.attrs** — ergonomic typed html attribute access
202
- *(see [dom.attrs](#dom.attrs) for more details)*
289
+ - **use.events** — attach event listeners to the element (auto-cleaned up)
203
290
  ```ts
204
- const attrs = use.attrs({
205
- name: String,
206
- count: Number,
207
- active: Boolean,
291
+ use.events({
292
+ keydown: (e: KeyboardEvent) => console.log("keydown", e.code),
293
+ keyup: (e: KeyboardEvent) => console.log("keyup", e.code),
208
294
  })
209
295
  ```
296
+ - **use.states** — [internal states](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/states) helper
210
297
  ```ts
211
- attrs.name // "chase"
212
- attrs.count // 123
213
- attrs.active // true
298
+ const states = use.states()
299
+ states.assign("active", "cool")
214
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
+ ```
215
329
  - **use.render** — rerender the view (debounced)
216
330
  ```ts
217
331
  use.render()
@@ -239,15 +353,15 @@ import {html, css} from "lit"
239
353
  const op = use.op.promise(doAsyncWork())
240
354
  ```
241
355
 
242
- ### 🍋 view "use" recipes
243
- - make a ticker — mount, repeat, and nap
356
+ ### 🍋 "use" recipes
357
+ - make a ticker — mount, cycle, and nap
244
358
  ```ts
245
- import {repeat, nap} from "@e280/stz"
359
+ import {cycle, nap} from "@e280/stz"
246
360
  ```
247
361
  ```ts
248
362
  const $seconds = use.signal(0)
249
363
 
250
- use.mount(() => repeat(async() => {
364
+ use.mount(() => cycle(async() => {
251
365
  await nap(1000)
252
366
  $seconds.value++
253
367
  }))
@@ -264,19 +378,36 @@ import {html, css} from "lit"
264
378
  <br/><br/>
265
379
  <a id="base-element"></a>
266
380
 
267
- ## 🦝🪵 sly base element
268
- > *the classic experience*
381
+ ## 🪵🦝 sly base element
382
+ > `@e280/sly/base`
383
+ > *the classic experience*
269
384
 
270
385
  ```ts
271
386
  import {BaseElement, Use, dom} from "@e280/sly"
272
387
  import {html, css} from "lit"
273
388
  ```
274
389
 
275
- `BaseElement` is a class-based approach to create a custom element web component.
276
-
277
- it lets you expose js properties on the element instance, which helps you setup a better developer experience for people interacting with your element through the dom.
278
-
279
- base element enjoys the same `use` hooks as views.
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`
280
411
 
281
412
  ### 🪵 base element setup
282
413
  - **declare your element class**
@@ -285,10 +416,10 @@ base element enjoys the same `use` hooks as views.
285
416
  static styles = css`span{color:orange}`
286
417
 
287
418
  // custom property
288
- start = 10
419
+ $start = signal(10)
289
420
 
290
421
  // custom attributes
291
- attrs = dom.attrs(this, {
422
+ attrs = dom.attrs(this).spec({
292
423
  multiply: Number,
293
424
  })
294
425
 
@@ -301,9 +432,9 @@ base element enjoys the same `use` hooks as views.
301
432
  const $count = use.signal(1)
302
433
  const increment = () => $count.value++
303
434
 
304
- const {start} = this
435
+ const {$start} = this
305
436
  const {multiply = 1} = this.attrs
306
- const result = start + (multiply * $count())
437
+ const result = $start() + (multiply * $count())
307
438
 
308
439
  return html`
309
440
  <span>${result}</span>
@@ -329,7 +460,7 @@ base element enjoys the same `use` hooks as views.
329
460
  const myElement = dom<MyElement>("my-element")
330
461
 
331
462
  // js property
332
- myElement.start = 100
463
+ myElement.$start(100)
333
464
 
334
465
  // html attributes
335
466
  myElement.attrs.multiply = 2
@@ -344,8 +475,9 @@ base element enjoys the same `use` hooks as views.
344
475
  <br/><br/>
345
476
  <a id="dom"></a>
346
477
 
347
- ## 🦝🪄 sly dom
348
- > *the "it's not jquery!" multitool*
478
+ ## 🪄🦝 sly dom
479
+ > `@e280/sly/dom`
480
+ > *the "it's not jquery!" multitool*
349
481
 
350
482
  ```ts
351
483
  import {dom} from "@e280/sly"
@@ -357,50 +489,107 @@ import {dom} from "@e280/sly"
357
489
  dom(".demo")
358
490
  // HTMLElement (or throws)
359
491
  ```
492
+ ```ts
493
+ // alias
494
+ dom.require(".demo")
495
+ // HTMLElement (or throws)
496
+ ```
360
497
  - `maybe` get an element
361
498
  ```ts
362
499
  dom.maybe(".demo")
363
500
  // HTMLElement | undefined
364
501
  ```
365
- - `select` all elements
502
+ - `all` matching elements in an array
366
503
  ```ts
367
504
  dom.all(".demo ul li")
368
505
  // HTMLElement[]
369
506
  ```
370
- - `in` the scope of an element
507
+
508
+ ### 🪄 dom.in scope
509
+ - make a scope
371
510
  ```ts
372
- dom(element).require("li")
373
- // HTMLElement (or throws)
511
+ dom.in(".demo") // selector
512
+ // Dom instance
374
513
  ```
375
514
  ```ts
376
- dom(element).maybe("li")
377
- // HTMLElement | undefined
515
+ dom.in(demoElement) // element
516
+ // Dom instance
378
517
  ```
518
+ - run queries in that scope
379
519
  ```ts
380
- dom(element).all("li")
381
- // HTMLElement[]
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")
382
527
  ```
383
528
 
384
529
  ### 🪄 dom utilities
385
- - `register` web components
530
+ - `dom.register` web components
386
531
  ```ts
387
532
  dom.register({MyComponent, AnotherCoolComponent})
388
533
  // <my-component>
389
534
  // <another-cool-component>
390
535
  ```
391
- - `render` content into an element
536
+ - `dom.register` automatically dashes the tag names (`MyComponent` becomes `<my-component>`)
537
+ - `dom.render` content into an element
392
538
  ```ts
393
- dom(element).render(html`<p>hello world</p>`)
539
+ dom.render(element, html`<p>hello world</p>`)
394
540
  ```
395
541
  ```ts
396
542
  dom.in(".demo").render(html`<p>hello world</p>`)
397
543
  ```
544
+ - `dom.el` little element builder
398
545
  ```ts
399
- dom.render(element, html`<p>hello world</p>`)
546
+ const div = dom.el("div", {"data-whatever": 123, "data-active": true})
547
+ // <div data-whatever="123" data-active></div>
400
548
  ```
401
- - `attrs` <a id="dom.attrs"></a> to setup a type-happy html attribute helper
549
+ - `dom.elmer` make an element with a fluent chain
402
550
  ```ts
403
- const attrs = dom.attrs(element, {
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({
404
593
  name: String,
405
594
  count: Number,
406
595
  active: Boolean,
@@ -420,18 +609,26 @@ import {dom} from "@e280/sly"
420
609
  attrs.name = undefined // removes the attr
421
610
  attrs.count = undefined // removes the attr
422
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
+ ```
423
619
 
424
620
 
425
621
 
426
622
  <br/><br/>
427
623
  <a id="ops"></a>
428
624
 
429
- ## 🦝🫛 sly ops
430
- > *tools for async operations and loading spinners*
625
+ ## 🫛🦝 sly ops
626
+ > `@e280/sly/ops`
627
+ > *tools for async operations and loading spinners*
431
628
 
432
629
  ```ts
433
630
  import {nap} from "@e280/stz"
434
- import {Pod, podium, Op, makeLoader, anims} from "@e280/sly"
631
+ import {Pod, podium, Op, loaders} from "@e280/sly"
435
632
  ```
436
633
 
437
634
  ### 🫛 pods: loading/ready/error
@@ -530,14 +727,29 @@ import {Pod, podium, Op, makeLoader, anims} from "@e280/sly"
530
727
  - loading if any ops are in loading, otherwise
531
728
  - ready if all the ops are ready
532
729
 
533
- ### 🫛 loaders: animated loading spinners
534
- - create a `loader` using `makeLoader`
730
+
731
+
732
+ <br/><br/>
733
+ <a id="loaders"></a>
734
+
735
+ ## ⏳🦝 sly loaders
736
+ > `@e280/sly/loaders`
737
+ > *animated loading spinners for ops*
738
+
739
+ ```ts
740
+ import {loaders} from "@e280/sly"
741
+ ```
742
+
743
+ ### ⏳ make a loader, choose an anim
744
+ - create a loader fn
535
745
  ```ts
536
- const loader = makeLoader(anims.dots)
746
+ const loader = loaders.make(loaders.anims.dots)
537
747
  ```
538
748
  - see all the anims available on the testing page https://sly.e280.org/
539
749
  - ngl, i made too many.. *i was having fun, okay?*
540
- - use the loader to render your op
750
+
751
+ ### ⏳ render an op with it
752
+ - use your loader to render an op
541
753
  ```ts
542
754
  return html`
543
755
  <h2>cool stuff</h2>
@@ -553,11 +765,101 @@ import {Pod, podium, Op, makeLoader, anims} from "@e280/sly"
553
765
 
554
766
 
555
767
 
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
+
556
857
  <br/><br/>
557
858
  <a id="loot"></a>
558
859
 
559
- ## 🦝🪙 loot
560
- > *drag-and-drop facilities*
860
+ ## 🪙🦝 loot
861
+ > `@e280/sly/loot`
862
+ > *drag-and-drop facilities*
561
863
 
562
864
  ```ts
563
865
  import {loot, view, dom} from "@e280/sly"
@@ -599,7 +901,7 @@ import {ev} from "@e280/stz"
599
901
  })
600
902
 
601
903
  // sly attribute handler for the body
602
- const attrs = dom.attrs(document.body, {
904
+ const attrs = dom.attrs(document.body).spec({
603
905
  "data-indicator": Boolean,
604
906
  })
605
907
 
@@ -678,7 +980,7 @@ import {ev} from "@e280/stz"
678
980
  <br/><br/>
679
981
  <a id="e280"></a>
680
982
 
681
- ## 🦝🧑‍💻 sly is by e280
983
+ ## 🧑‍💻🦝 sly is by e280
682
984
  reward us with github stars
683
985
  build with us at https://e280.org/ but only if you're cool
684
986