@e280/sly 0.3.0-1 → 0.3.0-10

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 (145) hide show
  1. package/README.md +107 -65
  2. package/package.json +20 -26
  3. package/s/demo/demo.bundle.ts +5 -1
  4. package/s/demo/views/demo.ts +3 -0
  5. package/s/demo/views/loaders.ts +2 -2
  6. package/s/demo/views/time-light.ts +19 -0
  7. package/s/demo/views/time-shadow.ts +22 -0
  8. package/s/dom/attrs/attrs.ts +3 -3
  9. package/s/dom/attrs/parts/attr-spec.ts +17 -5
  10. package/s/dom/attrs/parts/on-attrs.ts +1 -1
  11. package/s/dom/dom.ts +1 -1
  12. package/s/index.ts +2 -2
  13. package/s/{loaders → loader}/index.barrel.ts +0 -1
  14. package/s/{loaders → loader}/index.ts +0 -1
  15. package/s/{loaders → loader}/types.ts +1 -1
  16. package/s/view/{parts → common}/sly-shadow.ts +6 -0
  17. package/s/view/elements/light.ts +14 -0
  18. package/s/view/elements/shadow.ts +52 -0
  19. package/s/view/hooks/use-attrs.ts +28 -0
  20. package/s/view/hooks/use-life.ts +2 -2
  21. package/s/view/hooks/use-op.ts +1 -1
  22. package/s/view/hooks/use-ref.ts +3 -3
  23. package/s/view/hooks/use-state.ts +4 -4
  24. package/s/view/index.ts +5 -0
  25. package/s/view/shadow.ts +12 -12
  26. package/x/demo/demo.bundle.js +4 -1
  27. package/x/demo/demo.bundle.js.map +1 -1
  28. package/x/demo/demo.bundle.min.js +22 -15
  29. package/x/demo/demo.bundle.min.js.map +4 -4
  30. package/x/demo/views/demo.js +3 -0
  31. package/x/demo/views/demo.js.map +1 -1
  32. package/x/demo/views/loaders.js +2 -2
  33. package/x/demo/views/loaders.js.map +1 -1
  34. package/x/demo/views/time-light.d.ts +359 -0
  35. package/x/demo/views/time-light.js +16 -0
  36. package/x/demo/views/time-light.js.map +1 -0
  37. package/x/demo/views/time-shadow.d.ts +365 -0
  38. package/x/demo/views/time-shadow.js +18 -0
  39. package/x/demo/views/time-shadow.js.map +1 -0
  40. package/x/dom/attrs/attrs.d.ts +3 -2
  41. package/x/dom/attrs/attrs.js +2 -2
  42. package/x/dom/attrs/attrs.js.map +1 -1
  43. package/x/dom/attrs/parts/attr-spec.d.ts +5 -1
  44. package/x/dom/attrs/parts/attr-spec.js +12 -6
  45. package/x/dom/attrs/parts/attr-spec.js.map +1 -1
  46. package/x/dom/attrs/parts/on-attrs.d.ts +1 -1
  47. package/x/dom/attrs/parts/on-attrs.js.map +1 -1
  48. package/x/dom/dom.d.ts +1 -1
  49. package/x/dom/dom.js.map +1 -1
  50. package/x/index.d.ts +2 -2
  51. package/x/index.html +2 -2
  52. package/x/index.js +2 -2
  53. package/x/index.js.map +1 -1
  54. package/x/{loaders → loader}/index.barrel.d.ts +0 -1
  55. package/x/loader/index.barrel.js.map +1 -0
  56. package/x/{loaders → loader}/index.d.ts +0 -1
  57. package/x/loader/index.js.map +1 -0
  58. package/x/loader/make.js.map +1 -0
  59. package/x/loader/mock.js.map +1 -0
  60. package/x/loader/parts/anims.js.map +1 -0
  61. package/x/loader/parts/ascii-anim.js.map +1 -0
  62. package/x/loader/parts/error-display.js.map +1 -0
  63. package/x/{loaders → loader}/types.d.ts +1 -1
  64. package/x/{loaders → loader}/types.js.map +1 -1
  65. package/x/loot/drag-and-drops.d.ts +2 -2
  66. package/x/loot/drops.d.ts +1 -1
  67. package/x/op/index.js.map +1 -0
  68. package/x/op/op.js.map +1 -0
  69. package/x/op/podium.js.map +1 -0
  70. package/x/{ops → op}/types.js.map +1 -1
  71. package/x/{tests.test.js → test.js} +1 -1
  72. package/x/test.js.map +1 -0
  73. package/x/view/{parts → common}/sly-shadow.d.ts +1 -0
  74. package/x/view/{parts → common}/sly-shadow.js +4 -0
  75. package/x/view/common/sly-shadow.js.map +1 -0
  76. package/x/view/elements/light.d.ts +357 -0
  77. package/x/view/elements/light.js +10 -0
  78. package/x/view/elements/light.js.map +1 -0
  79. package/x/view/elements/shadow.d.ts +366 -0
  80. package/x/view/elements/shadow.js +42 -0
  81. package/x/view/elements/shadow.js.map +1 -0
  82. package/x/view/hooks/use-attrs.d.ts +2 -0
  83. package/x/view/hooks/use-attrs.js +23 -0
  84. package/x/view/hooks/use-attrs.js.map +1 -0
  85. package/x/view/hooks/use-life.js +2 -2
  86. package/x/view/hooks/use-life.js.map +1 -1
  87. package/x/view/hooks/use-op.d.ts +1 -1
  88. package/x/view/hooks/use-op.js +1 -1
  89. package/x/view/hooks/use-op.js.map +1 -1
  90. package/x/view/hooks/use-ref.d.ts +3 -3
  91. package/x/view/hooks/use-ref.js +5 -5
  92. package/x/view/hooks/use-ref.js.map +1 -1
  93. package/x/view/hooks/use-signal.d.ts +3 -3
  94. package/x/view/hooks/use-state.js +4 -4
  95. package/x/view/hooks/use-state.js.map +1 -1
  96. package/x/view/index.d.ts +4 -0
  97. package/x/view/index.js +4 -0
  98. package/x/view/index.js.map +1 -1
  99. package/x/view/shadow.d.ts +2 -2
  100. package/x/view/shadow.js +10 -11
  101. package/x/view/shadow.js.map +1 -1
  102. package/x/loaders/index.barrel.js.map +0 -1
  103. package/x/loaders/index.js.map +0 -1
  104. package/x/loaders/make.js.map +0 -1
  105. package/x/loaders/mock.js.map +0 -1
  106. package/x/loaders/parts/anims.js.map +0 -1
  107. package/x/loaders/parts/ascii-anim.js.map +0 -1
  108. package/x/loaders/parts/error-display.js.map +0 -1
  109. package/x/ops/index.js.map +0 -1
  110. package/x/ops/op.js.map +0 -1
  111. package/x/ops/podium.js.map +0 -1
  112. package/x/tests.test.js.map +0 -1
  113. package/x/view/parts/sly-shadow.js.map +0 -1
  114. /package/s/{loaders → loader}/make.ts +0 -0
  115. /package/s/{loaders → loader}/mock.ts +0 -0
  116. /package/s/{loaders → loader}/parts/anims.ts +0 -0
  117. /package/s/{loaders → loader}/parts/ascii-anim.ts +0 -0
  118. /package/s/{loaders → loader}/parts/error-display.ts +0 -0
  119. /package/s/{ops → op}/index.ts +0 -0
  120. /package/s/{ops → op}/op.ts +0 -0
  121. /package/s/{ops → op}/podium.ts +0 -0
  122. /package/s/{ops → op}/types.ts +0 -0
  123. /package/s/{tests.test.ts → test.ts} +0 -0
  124. /package/x/{loaders → loader}/index.barrel.js +0 -0
  125. /package/x/{loaders → loader}/index.js +0 -0
  126. /package/x/{loaders → loader}/make.d.ts +0 -0
  127. /package/x/{loaders → loader}/make.js +0 -0
  128. /package/x/{loaders → loader}/mock.d.ts +0 -0
  129. /package/x/{loaders → loader}/mock.js +0 -0
  130. /package/x/{loaders → loader}/parts/anims.d.ts +0 -0
  131. /package/x/{loaders → loader}/parts/anims.js +0 -0
  132. /package/x/{loaders → loader}/parts/ascii-anim.d.ts +0 -0
  133. /package/x/{loaders → loader}/parts/ascii-anim.js +0 -0
  134. /package/x/{loaders → loader}/parts/error-display.d.ts +0 -0
  135. /package/x/{loaders → loader}/parts/error-display.js +0 -0
  136. /package/x/{loaders → loader}/types.js +0 -0
  137. /package/x/{ops → op}/index.d.ts +0 -0
  138. /package/x/{ops → op}/index.js +0 -0
  139. /package/x/{ops → op}/op.d.ts +0 -0
  140. /package/x/{ops → op}/op.js +0 -0
  141. /package/x/{ops → op}/podium.d.ts +0 -0
  142. /package/x/{ops → op}/podium.js +0 -0
  143. /package/x/{ops → op}/types.d.ts +0 -0
  144. /package/x/{ops → op}/types.js +0 -0
  145. /package/x/{tests.test.d.ts → test.d.ts} +0 -0
package/README.md CHANGED
@@ -2,33 +2,35 @@
2
2
  <div align="center"><img alt="" width="256" src="./assets/favicon.png"/></div>
3
3
 
4
4
  # 🦝 sly
5
- > *mischievous shadow views*
6
5
 
7
6
  ```sh
8
7
  npm install lit @e280/sly @e280/strata @e280/stz
9
8
  ```
10
9
 
11
- [@e280](https://e280.org/)'s new [lit](https://lit.dev/)-based frontend webdev library.
10
+ #### [@e280](https://e280.org/)'s [lit-based](https://lit.dev/) web library for reactive light or shadow views
12
11
 
13
- - 🍋 [**#views**](#views) hooks-based, shadow-dom'd, template-literal'd
14
- - 🪝 [**#hooks**](#hooks) full reference of available view hooks
15
- - 🫛 [**#ops**](#ops) — reactive tooling for async operations
16
- - ⏳ [**#loaders**](#loaders) animated loading spinners for rendering ops
17
- - 🪙 [**#loot**](#loot) drag-and-drop facilities
18
- - 🪄 [**#dom**](#dom) the "it's not jquery" multitool
19
- - 🧪 https://sly.e280.org/ our testing page
12
+ - 🎭 [**#views,**](#views) reactive lit views, light-dom or shadow-dom
13
+ - 🪝 [**#hooks,**](#hooks) react-like composable hooks
14
+ - 🫛 [**#ops,**](#ops) tooling for async operations ui
15
+ - ⏳ [**#loaders,**](#loaders) render ops with animated loading spinners
16
+ - 🪙 [**#loot,**](#loot) drag-and-drop facilities
17
+ - 🪄 [**#dom,**](#dom) the "it's not jquery" multitool
18
+ - 🧪 **https://sly.e280.org/** sly's testing page
20
19
 
21
20
 
22
21
 
23
22
  <br/><br/>
24
23
  <a id="views"></a>
25
24
 
26
- ## 🍋 views
27
- > *modern views.. in lightness, or darkness..*
25
+ ## 🎭 views
26
+ > *reactive lit-html views*
28
27
 
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** declarative rendering with [modern hooks](#hooks) familiar to react devs
28
+ - 🔮 [**see codepen demo,**](https://codepen.io/editor/ChaseMoskal/pen/019cd681-722b-7f51-a961-bc16e3d524a9) plain html (no build!)
29
+ - 🌗 **light or shadow,** render nakedly on the page, or within a cozy shadow bubble
30
+ - 🪝 **hooks-based,** familiar react-style [hooks](#hooks)
31
+ - ⚡ **auto-reactive,** views magically rerender on [strata](https://github.com/e280/strata)-compatible state changes
32
+ - 🪶 **no compile step,** just god's honest javascript via [lit](https://lit.dev/)-html tagged-templates
33
+ - 🧩 **not web components,** no dom registration needed, just vibes and good typings
32
34
 
33
35
  ```ts
34
36
  import {html} from "lit"
@@ -40,7 +42,7 @@ export const MyShadowView = shadow(() => html`<p>shrouded in darkness</p>`)
40
42
  ```
41
43
 
42
44
  ### 🌞 light views
43
- > *just pretend it's react*
45
+ > *just pretend it's like react!*
44
46
 
45
47
  - **define a light view**
46
48
  ```ts
@@ -58,14 +60,17 @@ export const MyShadowView = shadow(() => html`<p>shrouded in darkness</p>`)
58
60
  ```
59
61
  - **render it into the dom**
60
62
  ```ts
61
- dom.in(".demo").render(html`
63
+ dom.render(dom(".demo"), html`
62
64
  <h1>my cool counter demo</h1>
63
65
  ${MyCounter(123)}
64
66
  `)
65
67
  ```
68
+ - **remember, light views are naked.**
69
+ so they don't have a containing host element,
70
+ and they can't have their own styles.
66
71
 
67
72
  ### 🌚 shadow views
68
- > *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)*
73
+ > *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 [slotting](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots)*
69
74
 
70
75
  - **define a shadow view**
71
76
  ```ts
@@ -73,28 +78,33 @@ export const MyShadowView = shadow(() => html`<p>shrouded in darkness</p>`)
73
78
  import {shadow, useName, useCss, useSignal} from "@e280/sly"
74
79
 
75
80
  export const MyShadowCounter = shadow((start: number) => {
76
- useName("shadow-counter")
81
+ useName("counter")
77
82
  useCss(css`button { color: cyan }`)
78
83
 
79
84
  const $count = useSignal(start)
80
85
  const increment = () => $count.value++
81
86
 
82
87
  return html`
83
- <button @click="${increment}">${$count()}</button>
88
+ <button @click="${increment}">${$count.value}</button>
84
89
  <slot></slot>
85
90
  `
86
91
  })
87
92
  ```
88
93
  - **render it into the dom**
89
94
  ```ts
90
- dom.in(".demo").render(html`
95
+ dom.render(dom(".demo"), html`
91
96
  <h1>my cool counter demo</h1>
92
97
  ${MyShadowCounter(234)}
93
98
  `)
94
99
  ```
100
+ - shadow views have a host element, rendered output looks like:
101
+ ```html
102
+ <h1>my cool counter demo</h1>
103
+ <sly-shadow view="counter"></sly-shadow>
104
+ ```
95
105
  - **.with to nest children or set attrs**
96
106
  ```ts
97
- dom.in(".demo").render(html`
107
+ dom.render(dom(".demo"), html`
98
108
  <h1>my cool counter demo</h1>
99
109
 
100
110
  ${MyShadowCounter.with({
@@ -106,13 +116,36 @@ export const MyShadowView = shadow(() => html`<p>shrouded in darkness</p>`)
106
116
  })}
107
117
  `)
108
118
  ```
109
- - **oh, you can do custom shadow config if needed**
119
+ - **you can do custom shadow setup if needed** (default shown)
110
120
  ```ts
111
- const MyCustomShadow = shadow.config(() => {
112
- const host = document.createElement("div")
121
+ import {SlyShadow} from "@e280/sly"
122
+
123
+ const customShadow = shadow.setup(() => {
124
+ SlyShadow.register()
125
+ const host = document.createElement("sly-shadow")
113
126
  const shadow = host.attachShadow({mode: "open"})
114
127
  return {host, shadow}
115
- })(() => html`<p>shrouded in darkness</p>`)
128
+ })
129
+
130
+ const MyShadowView = customShadow(() => html`<p>shrouded in darkness</p>`)
131
+ ```
132
+
133
+ ### 🍨 web components
134
+ > *web-native custom elements*
135
+
136
+ - **they use hooks like the views, but they don't take props**
137
+ ```ts
138
+ import {html} from "lit"
139
+ import {lightElement, shadowElement} from "@e280/sly"
140
+
141
+ const MyLight = lightElement(() => html`hello`)
142
+ const MyShadow = shadowElement(() => html`hello`)
143
+
144
+ dom.register({MyLight, MyShadow})
145
+ ```
146
+ ```html
147
+ <my-light></my-light>
148
+ <my-shadow></my-shadow>
116
149
  ```
117
150
 
118
151
 
@@ -121,48 +154,58 @@ export const MyShadowView = shadow(() => html`<p>shrouded in darkness</p>`)
121
154
  <a id="hooks"></a>
122
155
 
123
156
  ## 🪝 hooks
124
- > *composable view state and utilities*
157
+ > *composable view state and utilities*
125
158
 
126
159
  ### 👮 follow the hooks rules
127
160
 
128
- just like [react hooks](https://react.dev/warnings/invalid-hook-call-warning), the execution order of sly's `use` hooks actually matters.
161
+ just like [react hooks](https://react.dev/warnings/invalid-hook-call-warning), the execution order of hooks seriously matters.
129
162
 
130
- 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..*
163
+ you must not call these hooks under if-conditionals, or for-loops, or inside callback functions, or after a conditional return statement, or anything like that.. *otherwise, heed my warning: weird bad stuff will happen..*
131
164
 
132
165
  ### 🌚 shadow-only hooks
133
- - **useName** — *(shadow only)* — set the "view" attribute value
166
+ - **useName,** set the "view" attribute value
134
167
  ```ts
135
168
  useName("squarepants")
136
169
  // <sly-shadow view="squarepants">
137
170
  ```
138
- - **useCss** — *(shadow only)* — attach stylesheets (use lit's `css`!) to the shadow root
171
+ - **useCss,** attach stylesheets (use lit's `css`!) to the shadow root
139
172
  ```ts
140
173
  useCss(css1, css2, css3)
141
174
  ```
142
- - **useHost** — *(shadow only)* — get the host element
175
+ - **useHost,** get the host element
143
176
  ```ts
144
177
  const host = useHost()
145
178
  ```
146
- - **useShadow** — *(shadow only)* — get the shadow root
179
+ - **useShadow,** get the shadow root
147
180
  ```ts
148
181
  const shadow = useShadow()
149
182
  ```
183
+ - **useAttrs,** access host element attributes (and rerender on attr changes)
184
+ ```ts
185
+ const attrs = useAttrs({
186
+ name: String,
187
+ count: Number,
188
+ active: Boolean,
189
+ })
190
+
191
+ attrs.count = 123 // set the attr
192
+ ```
150
193
 
151
194
  ### 🌞 universal hooks
152
- - **useState** react-like hook to create some reactive state (we prefer signals)
195
+ - **useState,** react-like hook to create some reactive state (we prefer signals)
153
196
  ```ts
154
197
  const [count, setCount] = useState(0)
155
198
 
156
199
  const increment = () => setCount(n => n + 1)
157
200
  ```
158
- - **useRef** react-like hook to make a non-reactive box for a value
201
+ - **useRef,** react-like hook to make a non-reactive box for a value
159
202
  ```ts
160
203
  const ref = useRef(0)
161
204
 
162
205
  ref.current // 0
163
206
  ref.current = 1 // does not trigger rerender
164
207
  ```
165
- - **useSignal** create a [strata](https://github.com/e280/strata) signal
208
+ - **useSignal,** create a [strata](https://github.com/e280/strata) signal
166
209
  ```ts
167
210
  const $count = useSignal(1)
168
211
 
@@ -172,13 +215,11 @@ you must not call these hooks under `if` conditionals, or `for` loops, or in cal
172
215
  // write the signal
173
216
  $count(2)
174
217
  ```
175
- - see [strata readme](https://github.com/e280/strata)
176
- - **useDerived** — create a [strata](https://github.com/e280/strata) derived signal
218
+ - **useDerived,** create a [strata](https://github.com/e280/strata) derived signal
177
219
  ```ts
178
220
  const $product = useDerived(() => $count() * $whatever())
179
221
  ```
180
- - see [strata readme](https://github.com/e280/strata)
181
- - **useOnce** — run fn at initialization, and return a value
222
+ - **useOnce,** run fn at initialization, and return a value
182
223
  ```ts
183
224
  const whatever = useOnce(() => {
184
225
  console.log("happens one time")
@@ -187,14 +228,14 @@ you must not call these hooks under `if` conditionals, or `for` loops, or in cal
187
228
 
188
229
  whatever // 123
189
230
  ```
190
- - **useMount** setup mount/unmount lifecycle
231
+ - **useMount,** setup mount/unmount lifecycle
191
232
  ```ts
192
233
  useMount(() => {
193
234
  console.log("mounted")
194
235
  return () => console.log("unmounted")
195
236
  })
196
237
  ```
197
- - **useWake** run fn each time mounted, and return value
238
+ - **useWake,** run fn each time mounted, and return value
198
239
  ```ts
199
240
  const whatever = useWake(() => {
200
241
  console.log("mounted")
@@ -203,7 +244,7 @@ you must not call these hooks under `if` conditionals, or `for` loops, or in cal
203
244
 
204
245
  whatever // 123
205
246
  ```
206
- - **useLife** mount/unmount lifecycle, but also return a value
247
+ - **useLife,** mount/unmount lifecycle, but also return a value
207
248
  ```ts
208
249
  const whatever = useLife(() => {
209
250
  console.log("mounted")
@@ -213,30 +254,30 @@ you must not call these hooks under `if` conditionals, or `for` loops, or in cal
213
254
 
214
255
  whatever // 123
215
256
  ```
216
- - **useRender** returns a fn to rerender the view (debounced)
257
+ - **useRender,** returns a fn to rerender the view (debounced)
217
258
  ```ts
218
259
  const render = useRender()
219
260
 
220
261
  render().then(() => console.log("render done"))
221
262
  ```
222
- - **useRendered** get a promise that resolves *after* the next render
263
+ - **useRendered,** get a promise that resolves *after* the next render
223
264
  ```ts
224
265
  useRendered().then(() => console.log("rendered"))
225
266
  ```
226
- - **useOp** start loading an op based on an async fn
267
+ - **useOp,** start loading an op based on an async fn
227
268
  ```ts
228
269
  const op = useOp(async() => {
229
270
  await nap(5000)
230
271
  return 123
231
272
  })
232
273
  ```
233
- - **useOpPromise** start loading an op based on a promise
274
+ - **useOpPromise,** start loading an op based on a promise
234
275
  ```ts
235
276
  const op = useOpPromise(doAsyncWork())
236
277
  ```
237
278
 
238
279
  ### 🧑‍🍳 happy hooks recipes
239
- - make a ticker mount, cycle, and nap
280
+ - make a ticker, mount, cycle, and nap
240
281
  ```ts
241
282
  import {cycle, nap} from "@e280/stz"
242
283
  ```
@@ -250,7 +291,9 @@ you must not call these hooks under `if` conditionals, or `for` loops, or in cal
250
291
  ```
251
292
  - wake + rendered, to do something after each mount's first render
252
293
  ```ts
253
- useWake(() => useRendered.then(() => {
294
+ const rendered = useRendered()
295
+
296
+ useWake(() => rendered.then(() => {
254
297
  console.log("after first render")
255
298
  }))
256
299
  ```
@@ -261,7 +304,7 @@ you must not call these hooks under `if` conditionals, or `for` loops, or in cal
261
304
  <a id="ops"></a>
262
305
 
263
306
  ## 🫛 ops
264
- > *tools for async operations and loading spinners*
307
+ > *helpers for async operations*
265
308
 
266
309
  ```ts
267
310
  import {nap} from "@e280/stz"
@@ -296,22 +339,22 @@ import {Pod, podium, Op, loaders} from "@e280/sly"
296
339
  podium.value(["ready", 123])
297
340
  // 123
298
341
  ```
299
- - see more at [podium.ts](./s/ops/podium.ts)
342
+ - see more at [podium.ts](./s/op/podium.ts)
300
343
 
301
344
  ### 🫛 ops: nice pod ergonomics
302
- - an `Op<V>` wraps a pod with a signal for reactivity
345
+ - an `Op<V>` wraps a pod with a strata signal for reactivity
303
346
  - create an op
304
347
  ```ts
305
- const op = new Op<number>() // loading status by default
348
+ new Op<number>() // loading status by default
306
349
  ```
307
350
  ```ts
308
- const op = Op.loading<number>()
351
+ Op.loading<number>()
309
352
  ```
310
353
  ```ts
311
- const op = Op.ready<number>(123)
354
+ Op.ready<number>(123)
312
355
  ```
313
356
  ```ts
314
- const op = Op.error<number>(new Error())
357
+ Op.error<number>(new Error())
315
358
  ```
316
359
  - 🔥 create an op that calls and tracks an async fn
317
360
  ```ts
@@ -338,13 +381,12 @@ import {Pod, podium, Op, loaders} from "@e280/sly"
338
381
  - select executes a fn based on the status
339
382
  ```ts
340
383
  const result = op.select({
341
- loading: () => "it's loading...",
384
+ loading: () => "still loading...",
342
385
  ready: value => `dude, it's ready! ${value}`,
343
- error: err => `dude, there's an error!`,
386
+ error: err => `ack! an error!`,
344
387
  })
345
388
 
346
- result
347
- // "dude, it's ready! 123"
389
+ result // "dude, it's ready! 123"
348
390
  ```
349
391
  - morph returns a new pod, transforming the value if ready
350
392
  ```ts
@@ -370,7 +412,7 @@ import {Pod, podium, Op, loaders} from "@e280/sly"
370
412
  <a id="loaders"></a>
371
413
 
372
414
  ## ⏳ loaders
373
- > *animated loading spinners for ops*
415
+ > *animated loading spinners for ops*
374
416
 
375
417
  ```ts
376
418
  import {loaders} from "@e280/sly"
@@ -527,7 +569,7 @@ import {ev} from "@e280/stz"
527
569
  <a id="dom"></a>
528
570
 
529
571
  ## 🪄 dom
530
- > *the "it's not jquery!" multitool*
572
+ > *the "it's not jquery!" multitool*
531
573
 
532
574
  ```ts
533
575
  import {dom} from "@e280/sly"
@@ -661,10 +703,10 @@ import {dom} from "@e280/sly"
661
703
  ```
662
704
  or if you wanna be more loosey-goosey, skip the spec
663
705
  ```ts
664
- const a = dom.in(".demo").attrs
665
- a.strings.name = "pimsley"
666
- a.numbers.count = 125
667
- a.booleans.active = true
706
+ const {attrs} = dom.in(".demo")
707
+ attrs.strings.name = "pimsley"
708
+ attrs.numbers.count = 125
709
+ attrs.booleans.active = true
668
710
  ```
669
711
 
670
712
 
package/package.json CHANGED
@@ -1,50 +1,43 @@
1
1
  {
2
2
  "name": "@e280/sly",
3
- "version": "0.3.0-1",
3
+ "version": "0.3.0-10",
4
4
  "description": "web shadow views",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
+ "files": [
8
+ "x",
9
+ "s"
10
+ ],
7
11
  "main": "./x/index.js",
8
12
  "exports": {
9
13
  ".": "./x/index.js",
10
14
  "./dom": "./x/dom/index.js",
11
- "./loaders": "./x/loaders/index.js",
15
+ "./loader": "./x/loader/index.js",
12
16
  "./loot": "./x/loot/index.js",
13
- "./ops": "./x/ops/index.js",
17
+ "./op": "./x/op/index.js",
14
18
  "./view": "./x/view/index.js"
15
19
  },
16
- "files": [
17
- "x",
18
- "s"
19
- ],
20
+ "scripts": {
21
+ "build": "rm -rf x && mkdir x && ln -s '$(realpath s)' x/s && ln -s '$(realpath assets)' x/assets && tsc && scute -v",
22
+ "start": "octo 'tsc -w' 'scute -wv' 'node --watch x/test.js' 'http-server x'",
23
+ "count": "find s -path '*/_archive' -prune -o -name '*.ts' -exec wc -l {} +",
24
+ "test": "node x/test.js"
25
+ },
20
26
  "peerDependencies": {
21
27
  "lit": "^3.3.2"
22
28
  },
23
29
  "dependencies": {
24
- "@e280/strata": "^0.2.8",
25
- "@e280/stz": "^0.2.21"
30
+ "@e280/strata": "^0.3.0-2",
31
+ "@e280/stz": "^0.2.24"
26
32
  },
27
33
  "devDependencies": {
34
+ "@e280/octo": "^0.1.0-12",
28
35
  "@e280/science": "^0.1.8",
29
- "@e280/scute": "^0.2.2",
36
+ "@e280/scute": "^0.3.0-1",
30
37
  "http-server": "^14.1.1",
31
38
  "npm-run-all": "^4.1.5",
32
39
  "typescript": "^5.9.3"
33
40
  },
34
- "scripts": {
35
- "build": "run-s _clean _ln _tsc _scute",
36
- "start": "octo 'scute -vw' 'tsc -w' 'node --watch x/tests.test.js' 'http-server x'",
37
- "count": "find s -path '*/_archive' -prune -o -name '*.ts' -exec wc -l {} +",
38
- "test": "node x/tests.test.js",
39
- "test-inspect": "node inspect x/tests.test.js",
40
- "_clean": "rm -rf x && mkdir x",
41
- "_tsc": "tsc",
42
- "_scute": "scute -v",
43
- "_ln": "run-s _ln:s _ln:assets",
44
- "_ln:s": "ln -s \"$(realpath s)\" x/s",
45
- "_ln:assets": "ln -s \"$(realpath assets)\" x/assets",
46
- "_lnx:stz": "rm -rf node_modules/@e280/stz && ln -s \"$(realpath ../stz)\" node_modules/@e280/stz"
47
- },
48
41
  "author": "Chase Moskal <chasemoskal@gmail.com>",
49
42
  "homepage": "https://github.com/e280/sly#readme",
50
43
  "repository": {
@@ -55,9 +48,10 @@
55
48
  "url": "https://github.com/e280/sly/issues"
56
49
  },
57
50
  "keywords": [
51
+ "lit",
52
+ "views",
58
53
  "shadow views",
59
54
  "shadow dom",
60
- "components",
61
- "lit"
55
+ "components"
62
56
  ]
63
57
  }
@@ -1,7 +1,11 @@
1
1
 
2
2
  import {dom} from "../dom/dom.js"
3
3
  import {Demo} from "./views/demo.js"
4
+ import {TimeLight} from "./views/time-light.js"
5
+ import {TimeShadow} from "./views/time-shadow.js"
4
6
 
5
- dom.in(".demo").render(Demo())
7
+ dom.register({TimeShadow, TimeLight})
8
+
9
+ dom.render(dom(".demo"), Demo())
6
10
  console.log("🦝 sly")
7
11
 
@@ -20,6 +20,9 @@ export const Demo = shadow(() => {
20
20
  })}
21
21
  </p>
22
22
 
23
+ <time-shadow></time-shadow>
24
+ <time-light></time-light>
25
+
23
26
  ${LoadersView()}
24
27
  `
25
28
  })
@@ -1,8 +1,8 @@
1
1
 
2
2
  import {css, html} from "lit"
3
- import {Op} from "../../ops/op.js"
3
+ import {Op} from "../../op/op.js"
4
4
  import {shadow} from "../../view/shadow.js"
5
- import {loaders} from "../../loaders/index.js"
5
+ import {loaders} from "../../loader/index.js"
6
6
  import {cssReset, useName, useOnce, useStyles} from "../../view/index.js"
7
7
 
8
8
  export const LoadersView = shadow(() => {
@@ -0,0 +1,19 @@
1
+
2
+ import {html} from "lit"
3
+ import {cycle, nap} from "@e280/stz"
4
+ import {useMount, useSignal} from "../../view/index.js"
5
+ import {lightElement} from "../../view/elements/light.js"
6
+
7
+ export class TimeLight extends lightElement(() => {
8
+ const $time = useSignal(Date.now())
9
+
10
+ useMount(() => cycle(async() => {
11
+ await nap(100)
12
+ $time(Date.now())
13
+ }))
14
+
15
+ return html`
16
+ <p>${$time()}</p>
17
+ `
18
+ }) {}
19
+
@@ -0,0 +1,22 @@
1
+
2
+ import {css, html} from "lit"
3
+ import {cycle, nap} from "@e280/stz"
4
+ import {shadowElement} from "../../view/elements/shadow.js"
5
+ import {useCss, useMount, useName, useSignal} from "../../view/index.js"
6
+
7
+ export class TimeShadow extends shadowElement(() => {
8
+ useName("time-shadow")
9
+ useCss(css`:host{display:inline-block} button{color:cyan}`)
10
+
11
+ const $time = useSignal(Date.now())
12
+
13
+ useMount(() => cycle(async() => {
14
+ await nap(100)
15
+ $time(Date.now())
16
+ }))
17
+
18
+ return html`
19
+ <p>${$time()}</p>
20
+ `
21
+ }) {}
22
+
@@ -1,9 +1,9 @@
1
1
 
2
2
  import {AttrSpec} from "../types.js"
3
3
  import {onAttrs} from "./parts/on-attrs.js"
4
- import {attrSpec} from "./parts/attr-spec.js"
5
4
  import {AttrProxies} from "./parts/attr-proxies.js"
6
5
  import {attrGet, attrSet} from "./parts/attr-fns.js"
6
+ import {attrSpec, AttrSpecOptions} from "./parts/attr-spec.js"
7
7
 
8
8
  export function attrs(element: HTMLElement) {
9
9
  const proxies = new AttrProxies(element)
@@ -11,8 +11,8 @@ export function attrs(element: HTMLElement) {
11
11
  strings: proxies.strings,
12
12
  numbers: proxies.numbers,
13
13
  booleans: proxies.booleans,
14
- on: (fn: () => void) => onAttrs(element, fn),
15
- spec: <A extends AttrSpec>(spec: A) => attrSpec(element, spec),
14
+ on: (fn: MutationCallback) => onAttrs(element, fn),
15
+ spec: <A extends AttrSpec>(spec: A, options: AttrSpecOptions = {}) => attrSpec(element, spec, options),
16
16
  }
17
17
  }
18
18
 
@@ -2,10 +2,16 @@
2
2
  import {attrGet, attrSet} from "./attr-fns.js"
3
3
  import {AttrSpec, AttrTypes} from "../../types.js"
4
4
 
5
+ export type AttrSpecOptions = {
6
+ beforeSet?: (attrKey: string) => void
7
+ afterSet?: (attrKey: string) => void
8
+ }
9
+
5
10
  /** specify available html attributes and their types and create a proxy accessor */
6
11
  export const attrSpec = <A extends AttrSpec>(
7
12
  e: HTMLElement,
8
13
  spec: A,
14
+ options: AttrSpecOptions = {},
9
15
  ) => new Proxy(spec, {
10
16
 
11
17
  get: (_target, key: string) => {
@@ -18,11 +24,17 @@ export const attrSpec = <A extends AttrSpec>(
18
24
  },
19
25
 
20
26
  set: (_target, key: string, value: any) => {
21
- switch (spec[key]) {
22
- case String: return attrSet.string(e, key, value)
23
- case Number: return attrSet.number(e, key, value)
24
- case Boolean: return attrSet.boolean(e, key, value)
25
- default: throw new Error(`invalid attribute type for "${key}"`)
27
+ try {
28
+ options.beforeSet?.(key)
29
+ switch (spec[key]) {
30
+ case String: return attrSet.string(e, key, value)
31
+ case Number: return attrSet.number(e, key, value)
32
+ case Boolean: return attrSet.boolean(e, key, value)
33
+ default: throw new Error(`invalid attribute type for "${key}"`)
34
+ }
35
+ }
36
+ finally {
37
+ options.afterSet?.(key)
26
38
  }
27
39
  },
28
40
  }) as any as AttrTypes<A>
@@ -1,6 +1,6 @@
1
1
 
2
2
  /** respond when any attribute changes on the html element */
3
- export function onAttrs(element: HTMLElement, fn: () => void) {
3
+ export function onAttrs(element: HTMLElement, fn: MutationCallback) {
4
4
  const observer = new MutationObserver(fn)
5
5
  observer.observe(element, {attributes: true})
6
6
  return () => observer.disconnect()
package/s/dom/dom.ts CHANGED
@@ -11,7 +11,7 @@ import {register} from "./parts/register.js"
11
11
  import {Queryable, Renderable} from "./types.js"
12
12
  import {queryAll, queryMaybe, queryRequire} from "./parts/queries.js"
13
13
 
14
- export function dom<E extends Element>(selector: string, container: Queryable = document): E {
14
+ export function dom<E extends HTMLElement>(selector: string, container: Queryable = document) {
15
15
  return queryRequire<E>(selector, container)
16
16
  }
17
17
 
package/s/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  export * from "./dom/index.js"
3
- export * from "./loaders/index.js"
3
+ export * from "./loader/index.js"
4
4
  export * from "./loot/index.js"
5
- export * from "./ops/index.js"
5
+ export * from "./op/index.js"
6
6
  export * from "./view/index.js"
7
7
 
@@ -1,6 +1,5 @@
1
1
 
2
2
  export * as anims from "./parts/anims.js"
3
- export type * from "./parts/anims.js"
4
3
  export * from "./parts/ascii-anim.js"
5
4
  export * from "./parts/error-display.js"
6
5
  export * from "./make.js"
@@ -1,4 +1,3 @@
1
1
 
2
2
  export * as loaders from "./index.barrel.js"
3
- export type * from "./index.barrel.js"
4
3