@e280/sly 0.2.0-9 → 0.2.1

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 (275) hide show
  1. package/README.md +363 -124
  2. package/package.json +14 -7
  3. package/s/{views/base-element.ts → base/element.ts} +18 -10
  4. package/s/base/index.ts +6 -0
  5. package/s/{views → base}/use.ts +15 -6
  6. package/s/{views → base}/utils/reactor.ts +13 -4
  7. package/s/base/utils/states.ts +49 -0
  8. package/s/base/utils/use-attrs.ts +36 -0
  9. package/s/demo/demo.bundle.ts +6 -19
  10. package/s/demo/views/counter.ts +14 -25
  11. package/s/demo/views/demo.ts +9 -16
  12. package/s/demo/views/{incredi.ts → fastcount.ts} +5 -5
  13. package/s/demo/views/loaders.ts +7 -7
  14. package/s/dom/attrs/attrs.ts +6 -6
  15. package/s/dom/attrs/parts/attr-fns.ts +64 -34
  16. package/s/dom/attrs/parts/attr-proxies.ts +10 -10
  17. package/s/dom/attrs/parts/attr-spec.ts +7 -7
  18. package/s/dom/dom.ts +19 -57
  19. package/s/dom/index.ts +4 -0
  20. package/s/dom/parts/dom-scope.ts +46 -0
  21. package/s/dom/parts/el.ts +14 -0
  22. package/s/dom/parts/elmer.ts +38 -0
  23. package/s/dom/parts/eve.ts +24 -0
  24. package/s/dom/parts/mk.ts +9 -0
  25. package/s/dom/parts/queries.ts +26 -0
  26. package/s/dom/types.ts +5 -0
  27. package/s/index.html.ts +4 -4
  28. package/s/index.ts +7 -17
  29. package/s/loaders/index.barrel.ts +10 -0
  30. package/s/loaders/index.ts +4 -0
  31. package/s/loaders/make.ts +14 -0
  32. package/s/loaders/mock.ts +11 -0
  33. package/s/{ops/loaders → loaders}/parts/anims.ts +1 -1
  34. package/s/{ops/loaders → loaders}/parts/ascii-anim.ts +6 -5
  35. package/s/{ops/loaders → loaders}/parts/error-display.ts +2 -2
  36. package/s/loaders/types.ts +6 -0
  37. package/s/loot/index.barrel.ts +5 -0
  38. package/s/loot/index.ts +2 -3
  39. package/s/ops/index.ts +5 -0
  40. package/s/ops/op.ts +1 -0
  41. package/s/spa/index.barrel.ts +6 -0
  42. package/s/spa/index.ts +4 -0
  43. package/s/spa/plumbing/braces.ts +76 -0
  44. package/s/spa/plumbing/primitives.ts +85 -0
  45. package/s/spa/plumbing/router-core.ts +49 -0
  46. package/s/spa/plumbing/types.ts +45 -0
  47. package/s/spa/router.ts +49 -0
  48. package/s/spa/spa.test.ts +91 -0
  49. package/s/tests.test.ts +4 -1
  50. package/s/view/index.ts +7 -0
  51. package/s/view/types.ts +39 -0
  52. package/s/view/utils/contextualize.ts +45 -0
  53. package/s/view/utils/make-component.ts +34 -0
  54. package/s/view/utils/make-view.ts +48 -0
  55. package/s/view/utils/parts/capsule.ts +67 -0
  56. package/s/view/utils/parts/chain.ts +40 -0
  57. package/s/view/utils/parts/context.ts +11 -0
  58. package/s/view/utils/parts/directive.ts +29 -0
  59. package/s/view/utils/parts/sly-view.ts +15 -0
  60. package/s/view/view.ts +24 -0
  61. package/x/base/css-reset.js.map +1 -0
  62. package/x/base/element.d.ts +19 -0
  63. package/x/{views/base-element.js → base/element.js} +14 -6
  64. package/x/base/element.js.map +1 -0
  65. package/x/base/index.d.ts +4 -0
  66. package/x/base/index.js +5 -0
  67. package/x/base/index.js.map +1 -0
  68. package/x/{views → base}/use.d.ts +6 -2
  69. package/x/{views → base}/use.js +12 -4
  70. package/x/base/use.js.map +1 -0
  71. package/x/base/utils/apply-styles.js.map +1 -0
  72. package/x/base/utils/attr-watcher.js.map +1 -0
  73. package/x/base/utils/mounts.js.map +1 -0
  74. package/x/{views → base}/utils/reactor.d.ts +0 -1
  75. package/x/{views → base}/utils/reactor.js +10 -3
  76. package/x/base/utils/reactor.js.map +1 -0
  77. package/x/base/utils/states.d.ts +13 -0
  78. package/x/base/utils/states.js +41 -0
  79. package/x/base/utils/states.js.map +1 -0
  80. package/x/base/utils/use-attrs.d.ts +11 -0
  81. package/x/base/utils/use-attrs.js +18 -0
  82. package/x/base/utils/use-attrs.js.map +1 -0
  83. package/x/demo/demo.bundle.js +6 -16
  84. package/x/demo/demo.bundle.js.map +1 -1
  85. package/x/demo/demo.bundle.min.js +20 -30
  86. package/x/demo/demo.bundle.min.js.map +4 -4
  87. package/x/demo/views/counter.d.ts +370 -3
  88. package/x/demo/views/counter.js +14 -23
  89. package/x/demo/views/counter.js.map +1 -1
  90. package/x/demo/views/demo.d.ts +4 -1
  91. package/x/demo/views/demo.js +9 -13
  92. package/x/demo/views/demo.js.map +1 -1
  93. package/x/demo/views/{incredi.d.ts → fastcount.d.ts} +3 -3
  94. package/x/demo/views/{incredi.js → fastcount.js} +5 -5
  95. package/x/demo/views/fastcount.js.map +1 -0
  96. package/x/demo/views/loaders.js +6 -6
  97. package/x/demo/views/loaders.js.map +1 -1
  98. package/x/dom/attrs/attrs.d.ts +6 -3
  99. package/x/dom/attrs/attrs.js +6 -6
  100. package/x/dom/attrs/attrs.js.map +1 -1
  101. package/x/dom/attrs/parts/attr-fns.d.ts +15 -12
  102. package/x/dom/attrs/parts/attr-fns.js +60 -38
  103. package/x/dom/attrs/parts/attr-fns.js.map +1 -1
  104. package/x/dom/attrs/parts/attr-proxies.d.ts +3 -3
  105. package/x/dom/attrs/parts/attr-proxies.js +10 -10
  106. package/x/dom/attrs/parts/attr-proxies.js.map +1 -1
  107. package/x/dom/attrs/parts/attr-spec.js +7 -7
  108. package/x/dom/attrs/parts/attr-spec.js.map +1 -1
  109. package/x/dom/dom.d.ts +14 -26
  110. package/x/dom/dom.js +18 -44
  111. package/x/dom/dom.js.map +1 -1
  112. package/x/dom/index.d.ts +2 -0
  113. package/x/dom/index.js +3 -0
  114. package/x/dom/index.js.map +1 -0
  115. package/x/dom/parts/dom-scope.d.ts +15 -0
  116. package/x/dom/parts/dom-scope.js +35 -0
  117. package/x/dom/parts/dom-scope.js.map +1 -0
  118. package/x/dom/parts/el.d.ts +2 -0
  119. package/x/dom/parts/el.js +7 -0
  120. package/x/dom/parts/el.js.map +1 -0
  121. package/x/dom/parts/elmer.d.ts +11 -0
  122. package/x/dom/parts/elmer.js +32 -0
  123. package/x/dom/parts/elmer.js.map +1 -0
  124. package/x/dom/parts/eve.d.ts +7 -0
  125. package/x/dom/parts/eve.js +16 -0
  126. package/x/dom/parts/eve.js.map +1 -0
  127. package/x/dom/parts/mk.d.ts +2 -0
  128. package/x/dom/parts/mk.js +7 -0
  129. package/x/dom/parts/mk.js.map +1 -0
  130. package/x/dom/parts/queries.d.ts +4 -0
  131. package/x/dom/parts/queries.js +13 -0
  132. package/x/dom/parts/queries.js.map +1 -0
  133. package/x/dom/types.d.ts +3 -0
  134. package/x/index.d.ts +7 -14
  135. package/x/index.html +6 -6
  136. package/x/index.html.js +4 -4
  137. package/x/index.js +7 -14
  138. package/x/index.js.map +1 -1
  139. package/x/loaders/index.barrel.d.ts +7 -0
  140. package/x/loaders/index.barrel.js +7 -0
  141. package/x/loaders/index.barrel.js.map +1 -0
  142. package/x/loaders/index.d.ts +2 -0
  143. package/x/loaders/index.js +2 -0
  144. package/x/loaders/index.js.map +1 -0
  145. package/x/loaders/make.d.ts +3 -0
  146. package/x/loaders/make.js +6 -0
  147. package/x/loaders/make.js.map +1 -0
  148. package/x/loaders/mock.d.ts +2 -0
  149. package/x/loaders/mock.js +8 -0
  150. package/x/loaders/mock.js.map +1 -0
  151. package/x/{ops/loaders → loaders}/parts/anims.d.ts +1 -1
  152. package/x/loaders/parts/anims.js.map +1 -0
  153. package/x/{ops/loaders → loaders}/parts/ascii-anim.d.ts +2 -2
  154. package/x/{ops/loaders → loaders}/parts/ascii-anim.js +4 -4
  155. package/x/loaders/parts/ascii-anim.js.map +1 -0
  156. package/x/loaders/parts/error-display.d.ts +1 -0
  157. package/x/{ops/loaders → loaders}/parts/error-display.js +2 -2
  158. package/x/loaders/parts/error-display.js.map +1 -0
  159. package/x/loaders/types.d.ts +3 -0
  160. package/x/loaders/types.js.map +1 -0
  161. package/x/loot/index.barrel.d.ts +3 -0
  162. package/x/loot/index.barrel.js +4 -0
  163. package/x/loot/index.barrel.js.map +1 -0
  164. package/x/loot/index.d.ts +2 -3
  165. package/x/loot/index.js +1 -3
  166. package/x/loot/index.js.map +1 -1
  167. package/x/ops/index.d.ts +3 -0
  168. package/x/ops/index.js +4 -0
  169. package/x/ops/index.js.map +1 -0
  170. package/x/ops/op.js +1 -0
  171. package/x/ops/op.js.map +1 -1
  172. package/x/spa/index.barrel.d.ts +4 -0
  173. package/x/spa/index.barrel.js +3 -0
  174. package/x/spa/index.barrel.js.map +1 -0
  175. package/x/spa/index.d.ts +2 -0
  176. package/x/spa/index.js +2 -0
  177. package/x/spa/index.js.map +1 -0
  178. package/x/spa/plumbing/braces.d.ts +12 -0
  179. package/x/spa/plumbing/braces.js +55 -0
  180. package/x/spa/plumbing/braces.js.map +1 -0
  181. package/x/spa/plumbing/primitives.d.ts +22 -0
  182. package/x/spa/plumbing/primitives.js +65 -0
  183. package/x/spa/plumbing/primitives.js.map +1 -0
  184. package/x/spa/plumbing/router-core.d.ts +13 -0
  185. package/x/spa/plumbing/router-core.js +38 -0
  186. package/x/spa/plumbing/router-core.js.map +1 -0
  187. package/x/spa/plumbing/types.d.ts +35 -0
  188. package/x/spa/plumbing/types.js +2 -0
  189. package/x/spa/plumbing/types.js.map +1 -0
  190. package/x/spa/router.d.ts +13 -0
  191. package/x/spa/router.js +39 -0
  192. package/x/spa/router.js.map +1 -0
  193. package/x/spa/spa.test.d.ts +15 -0
  194. package/x/spa/spa.test.js +78 -0
  195. package/x/spa/spa.test.js.map +1 -0
  196. package/x/tests.test.js +4 -1
  197. package/x/tests.test.js.map +1 -1
  198. package/x/view/index.d.ts +5 -0
  199. package/x/view/index.js +6 -0
  200. package/x/view/index.js.map +1 -0
  201. package/x/view/types.d.ts +21 -0
  202. package/x/view/types.js +2 -0
  203. package/x/{views → view}/types.js.map +1 -1
  204. package/x/view/utils/contextualize.d.ts +13 -0
  205. package/x/view/utils/contextualize.js +18 -0
  206. package/x/view/utils/contextualize.js.map +1 -0
  207. package/x/view/utils/make-component.d.ts +5 -0
  208. package/x/view/utils/make-component.js +17 -0
  209. package/x/view/utils/make-component.js.map +1 -0
  210. package/x/view/utils/make-view.d.ts +2 -0
  211. package/x/view/utils/make-view.js +24 -0
  212. package/x/view/utils/make-view.js.map +1 -0
  213. package/x/view/utils/parts/capsule.d.ts +13 -0
  214. package/x/view/utils/parts/capsule.js +49 -0
  215. package/x/view/utils/parts/capsule.js.map +1 -0
  216. package/x/view/utils/parts/chain.d.ts +13 -0
  217. package/x/view/utils/parts/chain.js +26 -0
  218. package/x/view/utils/parts/chain.js.map +1 -0
  219. package/x/view/utils/parts/context.d.ts +9 -0
  220. package/x/view/utils/parts/context.js +10 -0
  221. package/x/view/utils/parts/context.js.map +1 -0
  222. package/x/view/utils/parts/directive.d.ts +5 -0
  223. package/x/view/utils/parts/directive.js +18 -0
  224. package/x/view/utils/parts/directive.js.map +1 -0
  225. package/x/view/utils/parts/sly-view.d.ts +5 -0
  226. package/x/view/utils/parts/sly-view.js +13 -0
  227. package/x/view/utils/parts/sly-view.js.map +1 -0
  228. package/x/view/view.d.ts +11 -0
  229. package/x/view/view.js +15 -0
  230. package/x/view/view.js.map +1 -0
  231. package/s/demo/views/divine.ts +0 -22
  232. package/s/ops/loaders/make-loader.ts +0 -18
  233. package/s/views/types.ts +0 -49
  234. package/s/views/utils/apply-attrs.ts +0 -33
  235. package/s/views/view.ts +0 -185
  236. package/x/demo/views/divine.d.ts +0 -8
  237. package/x/demo/views/divine.js +0 -19
  238. package/x/demo/views/divine.js.map +0 -1
  239. package/x/demo/views/incredi.js.map +0 -1
  240. package/x/ops/loaders/make-loader.d.ts +0 -5
  241. package/x/ops/loaders/make-loader.js +0 -7
  242. package/x/ops/loaders/make-loader.js.map +0 -1
  243. package/x/ops/loaders/parts/anims.js.map +0 -1
  244. package/x/ops/loaders/parts/ascii-anim.js.map +0 -1
  245. package/x/ops/loaders/parts/error-display.d.ts +0 -1
  246. package/x/ops/loaders/parts/error-display.js.map +0 -1
  247. package/x/views/base-element.d.ts +0 -14
  248. package/x/views/base-element.js.map +0 -1
  249. package/x/views/css-reset.js.map +0 -1
  250. package/x/views/types.d.ts +0 -36
  251. package/x/views/use.js.map +0 -1
  252. package/x/views/utils/apply-attrs.d.ts +0 -2
  253. package/x/views/utils/apply-attrs.js +0 -21
  254. package/x/views/utils/apply-attrs.js.map +0 -1
  255. package/x/views/utils/apply-styles.js.map +0 -1
  256. package/x/views/utils/attr-watcher.js.map +0 -1
  257. package/x/views/utils/mounts.js.map +0 -1
  258. package/x/views/utils/reactor.js.map +0 -1
  259. package/x/views/view.d.ts +0 -13
  260. package/x/views/view.js +0 -145
  261. package/x/views/view.js.map +0 -1
  262. /package/s/{views → base}/css-reset.ts +0 -0
  263. /package/s/{views → base}/utils/apply-styles.ts +0 -0
  264. /package/s/{views → base}/utils/attr-watcher.ts +0 -0
  265. /package/s/{views → base}/utils/mounts.ts +0 -0
  266. /package/x/{views → base}/css-reset.d.ts +0 -0
  267. /package/x/{views → base}/css-reset.js +0 -0
  268. /package/x/{views → base}/utils/apply-styles.d.ts +0 -0
  269. /package/x/{views → base}/utils/apply-styles.js +0 -0
  270. /package/x/{views → base}/utils/attr-watcher.d.ts +0 -0
  271. /package/x/{views → base}/utils/attr-watcher.js +0 -0
  272. /package/x/{views → base}/utils/mounts.d.ts +0 -0
  273. /package/x/{views → base}/utils/mounts.js +0 -0
  274. /package/x/{ops/loaders → loaders}/parts/anims.js +0 -0
  275. /package/x/{views → loaders}/types.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,6 +21,7 @@
18
21
  <br/><br/>
19
22
 
20
23
  ## 🦝 sly and friends
24
+ > `@e280/sly`
21
25
 
22
26
  ```sh
23
27
  npm install @e280/sly lit @e280/strata @e280/stz
@@ -29,81 +33,87 @@ npm install @e280/sly lit @e280/strata @e280/stz
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 and components
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
- - any view can be converted into a web component
45
- - 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)
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
90
  dom.register({
80
91
  MyCounter: CounterView
81
92
  .component()
82
- .props(component => [dom.attrs(component).number.start ?? 0]),
93
+ .props(() => [1]),
83
94
  })
84
95
  ```
85
96
  ```html
86
- <my-counter start="1"></my-counter>
97
+ <my-counter></my-counter>
87
98
  ```
88
99
 
89
- ### 🍋 view declaration settings
90
- - special settings for views at declaration-time
100
+ ### 🍋 view settings
101
+ - optional settings for views you should know about
91
102
  ```ts
92
103
  export const CoolView = view
93
104
  .settings({mode: "open", delegatesFocus: true})
94
- .render(use => (greeting: string) => {
95
- return html`😎 ${greeting} <slot></slot>`
96
- })
105
+ .render(use => (greeting: string) => html`😎 ${greeting} <slot></slot>`)
97
106
  ```
98
107
  - all [attachShadow params](https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow#parameters) (like `mode` and `delegatesFocus`) are valid `settings`
99
108
  - note the `<slot></slot>` we'll use in the next example lol
100
109
 
101
- ### 🍋 view injection options
102
- - 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
103
112
  ```ts
104
113
  dom.in(".app").render(html`
105
114
  <h2>cool example</h2>
106
- ${CoolView.props("hello")
115
+ ${CoolView
116
+ .props("hello")
107
117
  .attr("class", "hero")
108
118
  .children(html`<em>spongebob</em>`)
109
119
  .render()}
@@ -111,78 +121,100 @@ import {html, css} from "lit"
111
121
  ```
112
122
  - `props` — provide props and start a view chain
113
123
  - `attr` — set html attributes on the `<sly-view>` host element
114
- - `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
115
125
  - `render` — end the view chain and render the lit directive
116
126
 
117
127
  ### 🍋 view/component universality
118
- - **start with a view,**
128
+ - **you can start with a view,**
119
129
  ```ts
120
130
  export const GreeterView = view(use => (name: string) => {
121
131
  return html`<p>hello ${name}</p>`
122
132
  })
123
-
124
- // view usage:
125
- // GreeterView("pimsley")
126
133
  ```
127
- then you can convert it to a component
134
+ - view usage
135
+ ```ts
136
+ GreeterView("pimsley")
137
+ ```
138
+ **then you can convert it to a component.**
128
139
  ```ts
129
- export class GreeterComponent extends (GreeterView
130
- .component()
131
- .props(component => [component.getAttribute("name") ?? "unknown"])
140
+ export class GreeterComponent extends (
141
+ GreeterView
142
+ .component()
143
+ .props(component => [component.getAttribute("name") ?? "unknown"])
132
144
  ) {}
133
-
134
- // html usage:
135
- // <greeter-component name="pimsley"></greeter-component>
136
145
  ```
137
- - **start with a component,**
146
+ - html usage
147
+ ```html
148
+ <greeter-component name="pimsley"></greeter-component>
149
+ ```
150
+ - **you can start with a component,**
138
151
  ```ts
139
- export class GreeterComponent extends (view
140
- .component()
141
- .props(component => [component.getAttribute("name") ?? "unknown"])
142
- .render(use => (name: string) => {
152
+ export class GreeterComponent extends (
153
+ view(use => (name: string) => {
143
154
  return html`<p>hello ${name}</p>`
144
155
  })
156
+ .component()
157
+ .props(component => [component.getAttribute("name") ?? "unknown"])
145
158
  ) {}
146
-
147
- // html usage:
148
- // <greeter-component name="pimsley"></greeter-component>
149
- ```
150
- then you can already use it as a view
151
- ```ts
152
- // view usage:
153
- // GreeterComponent.view("pimsley")
154
159
  ```
155
- - **understanding `.component(init)` and `.props(fn)`**
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)`**
156
170
  - `.props` takes a fn that is called every render, which returns the props given to the view
157
171
  ```ts
158
- .component()
159
172
  .props(() => ["pimsley"])
160
173
  ```
161
- the props fn receives the component instance, so you can query html attributes
174
+ the props fn receives the component instance, so you can query html attributes or instance properties
162
175
  ```ts
163
- .component()
164
176
  .props(component => [component.getAttribute("name") ?? "unknown"])
165
177
  ```
166
- - `.component` takes a mixin type added to the component type, so your `.props` can accept instance properties
178
+ - `.component` accepts a subclass of `BaseElement`, so you can define your own properties and methods for your component class
167
179
  ```ts
168
- .component<{name?: string}>()
169
- .props(component => [component.name ?? "unknown"])
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])
170
192
  ```
171
- - `.component` also takes an init fn, so you can do some setup, like use signals for reactivity
193
+ - `.component` provides the devs interacting with your component, with noice typings
172
194
  ```ts
173
- .component<{$name: SignalFn<string>}>(component => {
174
- component.$name = signal("pimsley")
175
- })
176
- .props(component => [component.$name])
177
- ```
178
- - `.component` lets you set instance properties, that devs can interact with via the dom
179
- ```ts
180
- dom<GreeterComponent>("my-component").$name.value = "mortimer"
195
+ dom<GreeterComponent>("greeter-component").updateName("mortimer")
181
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
+ ```
182
206
  - **register web components to the dom**
183
207
  ```ts
184
208
  dom.register({GreeterComponent})
185
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
+ })
215
+ ```
216
+
217
+ <a id="use"></a>
186
218
 
187
219
  ### 🍋 "use" hooks reference
188
220
  - 👮 **follow the hooks rules**
@@ -254,20 +286,46 @@ import {html, css} from "lit"
254
286
 
255
287
  v // 123
256
288
  ```
257
- - **use.attrs** — ergonomic typed html attribute access
258
- *(see [dom.attrs](#dom.attrs) for more details)*
289
+ - **use.events** — attach event listeners to the element (auto-cleaned up)
259
290
  ```ts
260
- const attrs = use.attrs.spec({
261
- name: String,
262
- count: Number,
263
- active: Boolean,
291
+ use.events({
292
+ keydown: (e: KeyboardEvent) => console.log("keydown", e.code),
293
+ keyup: (e: KeyboardEvent) => console.log("keyup", e.code),
264
294
  })
265
295
  ```
296
+ - **use.states** — [internal states](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/states) helper
266
297
  ```ts
267
- attrs.name // "chase"
268
- attrs.count // 123
269
- attrs.active // true
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; }
270
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
+ ```
271
329
  - **use.render** — rerender the view (debounced)
272
330
  ```ts
273
331
  use.render()
@@ -296,14 +354,14 @@ import {html, css} from "lit"
296
354
  ```
297
355
 
298
356
  ### 🍋 "use" recipes
299
- - make a ticker — mount, repeat, and nap
357
+ - make a ticker — mount, cycle, and nap
300
358
  ```ts
301
- import {repeat, nap} from "@e280/stz"
359
+ import {cycle, nap} from "@e280/stz"
302
360
  ```
303
361
  ```ts
304
362
  const $seconds = use.signal(0)
305
363
 
306
- use.mount(() => repeat(async() => {
364
+ use.mount(() => cycle(async() => {
307
365
  await nap(1000)
308
366
  $seconds.value++
309
367
  }))
@@ -320,19 +378,36 @@ import {html, css} from "lit"
320
378
  <br/><br/>
321
379
  <a id="base-element"></a>
322
380
 
323
- ## 🦝🪵 sly base element
324
- > *the classic experience*
381
+ ## 🪵🦝 sly base element
382
+ > `@e280/sly/base`
383
+ > *the classic experience*
325
384
 
326
385
  ```ts
327
386
  import {BaseElement, Use, dom} from "@e280/sly"
328
387
  import {html, css} from "lit"
329
388
  ```
330
389
 
331
- `BaseElement` is a class-based approach to create a custom element web component.
332
-
333
- 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.
334
-
335
- 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`
336
411
 
337
412
  ### 🪵 base element setup
338
413
  - **declare your element class**
@@ -341,7 +416,7 @@ base element enjoys the same `use` hooks as views.
341
416
  static styles = css`span{color:orange}`
342
417
 
343
418
  // custom property
344
- start = 10
419
+ $start = signal(10)
345
420
 
346
421
  // custom attributes
347
422
  attrs = dom.attrs(this).spec({
@@ -357,9 +432,9 @@ base element enjoys the same `use` hooks as views.
357
432
  const $count = use.signal(1)
358
433
  const increment = () => $count.value++
359
434
 
360
- const {start} = this
435
+ const {$start} = this
361
436
  const {multiply = 1} = this.attrs
362
- const result = start + (multiply * $count())
437
+ const result = $start() + (multiply * $count())
363
438
 
364
439
  return html`
365
440
  <span>${result}</span>
@@ -385,7 +460,7 @@ base element enjoys the same `use` hooks as views.
385
460
  const myElement = dom<MyElement>("my-element")
386
461
 
387
462
  // js property
388
- myElement.start = 100
463
+ myElement.$start(100)
389
464
 
390
465
  // html attributes
391
466
  myElement.attrs.multiply = 2
@@ -400,8 +475,9 @@ base element enjoys the same `use` hooks as views.
400
475
  <br/><br/>
401
476
  <a id="dom"></a>
402
477
 
403
- ## 🦝🪄 sly dom
404
- > *the "it's not jquery!" multitool*
478
+ ## 🪄🦝 sly dom
479
+ > `@e280/sly/dom`
480
+ > *the "it's not jquery!" multitool*
405
481
 
406
482
  ```ts
407
483
  import {dom} from "@e280/sly"
@@ -413,49 +489,98 @@ import {dom} from "@e280/sly"
413
489
  dom(".demo")
414
490
  // HTMLElement (or throws)
415
491
  ```
492
+ ```ts
493
+ // alias
494
+ dom.require(".demo")
495
+ // HTMLElement (or throws)
496
+ ```
416
497
  - `maybe` get an element
417
498
  ```ts
418
499
  dom.maybe(".demo")
419
500
  // HTMLElement | undefined
420
501
  ```
421
- - `select` all elements
502
+ - `all` matching elements in an array
422
503
  ```ts
423
504
  dom.all(".demo ul li")
424
505
  // HTMLElement[]
425
506
  ```
426
- - `in` the scope of an element
507
+
508
+ ### 🪄 dom.in scope
509
+ - make a scope
427
510
  ```ts
428
- dom(element).require("li")
429
- // HTMLElement (or throws)
511
+ dom.in(".demo") // selector
512
+ // Dom instance
430
513
  ```
431
514
  ```ts
432
- dom(element).maybe("li")
433
- // HTMLElement | undefined
515
+ dom.in(demoElement) // element
516
+ // Dom instance
434
517
  ```
518
+ - run queries in that scope
435
519
  ```ts
436
- dom(element).all("li")
437
- // 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")
438
527
  ```
439
528
 
440
529
  ### 🪄 dom utilities
441
- - `register` web components
530
+ - `dom.register` web components
442
531
  ```ts
443
532
  dom.register({MyComponent, AnotherCoolComponent})
444
533
  // <my-component>
445
534
  // <another-cool-component>
446
535
  ```
447
536
  - `dom.register` automatically dashes the tag names (`MyComponent` becomes `<my-component>`)
448
- - `render` content into an element
537
+ - `dom.render` content into an element
449
538
  ```ts
450
- dom(element).render(html`<p>hello world</p>`)
539
+ dom.render(element, html`<p>hello world</p>`)
451
540
  ```
452
541
  ```ts
453
542
  dom.in(".demo").render(html`<p>hello world</p>`)
454
543
  ```
544
+ - `dom.el` little element builder
455
545
  ```ts
456
- 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>
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
+ })
457
578
  ```
458
- - `attrs` <a id="dom.attrs"></a> to setup a type-happy html attribute helper
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
459
584
  ```ts
460
585
  const attrs = dom.attrs(element).spec({
461
586
  name: String,
@@ -464,6 +589,13 @@ import {dom} from "@e280/sly"
464
589
  })
465
590
  ```
466
591
  ```ts
592
+ const attrs = dom.in(".demo").attrs.spec({
593
+ name: String,
594
+ count: Number,
595
+ active: Boolean,
596
+ })
597
+ ```
598
+ ```ts
467
599
  attrs.name // "chase"
468
600
  attrs.count // 123
469
601
  attrs.active // true
@@ -477,11 +609,12 @@ import {dom} from "@e280/sly"
477
609
  attrs.name = undefined // removes the attr
478
610
  attrs.count = undefined // removes the attr
479
611
  ```
480
- or if you wanna be more loosey-goosy, skip the spec
612
+ or if you wanna be more loosey-goosey, skip the spec
481
613
  ```ts
482
- dom.attrs(element).string.name = "pimsley"
483
- dom.attrs(element).number.count = 125
484
- dom.attrs(element).boolean.active = true
614
+ const a = dom.in(".demo").attrs
615
+ a.strings.name = "pimsley"
616
+ a.numbers.count = 125
617
+ a.booleans.active = true
485
618
  ```
486
619
 
487
620
 
@@ -489,12 +622,13 @@ import {dom} from "@e280/sly"
489
622
  <br/><br/>
490
623
  <a id="ops"></a>
491
624
 
492
- ## 🦝🫛 sly ops
493
- > *tools for async operations and loading spinners*
625
+ ## 🫛🦝 sly ops
626
+ > `@e280/sly/ops`
627
+ > *tools for async operations and loading spinners*
494
628
 
495
629
  ```ts
496
630
  import {nap} from "@e280/stz"
497
- import {Pod, podium, Op, makeLoader, anims} from "@e280/sly"
631
+ import {Pod, podium, Op, loaders} from "@e280/sly"
498
632
  ```
499
633
 
500
634
  ### 🫛 pods: loading/ready/error
@@ -593,14 +727,29 @@ import {Pod, podium, Op, makeLoader, anims} from "@e280/sly"
593
727
  - loading if any ops are in loading, otherwise
594
728
  - ready if all the ops are ready
595
729
 
596
- ### 🫛 loaders: animated loading spinners
597
- - 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
598
745
  ```ts
599
- const loader = makeLoader(anims.dots)
746
+ const loader = loaders.make(loaders.anims.dots)
600
747
  ```
601
748
  - see all the anims available on the testing page https://sly.e280.org/
602
749
  - ngl, i made too many.. *i was having fun, okay?*
603
- - use the loader to render your op
750
+
751
+ ### ⏳ render an op with it
752
+ - use your loader to render an op
604
753
  ```ts
605
754
  return html`
606
755
  <h2>cool stuff</h2>
@@ -616,11 +765,101 @@ import {Pod, podium, Op, makeLoader, anims} from "@e280/sly"
616
765
 
617
766
 
618
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
+
619
857
  <br/><br/>
620
858
  <a id="loot"></a>
621
859
 
622
- ## 🦝🪙 loot
623
- > *drag-and-drop facilities*
860
+ ## 🪙🦝 loot
861
+ > `@e280/sly/loot`
862
+ > *drag-and-drop facilities*
624
863
 
625
864
  ```ts
626
865
  import {loot, view, dom} from "@e280/sly"
@@ -741,7 +980,7 @@ import {ev} from "@e280/stz"
741
980
  <br/><br/>
742
981
  <a id="e280"></a>
743
982
 
744
- ## 🦝🧑‍💻 sly is by e280
983
+ ## 🧑‍💻🦝 sly is by e280
745
984
  reward us with github stars
746
985
  build with us at https://e280.org/ but only if you're cool
747
986