@e280/sly 0.3.0-2 → 0.3.0-4

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.
package/README.md CHANGED
@@ -2,21 +2,20 @@
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) light-dom or shadow-dom reactive lit views
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
 
@@ -24,12 +23,13 @@ npm install lit @e280/sly @e280/strata @e280/stz
24
23
  <a id="views"></a>
25
24
 
26
25
  ## 🎭 views
27
- > *reactive views, light or shadow*
26
+ > *"flourish in the light.. or thrive in the darkness"*
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** familiar react-style [hooks](#hooks)
32
- - 🌗 **light or shadow** render directly in the dom, or inside a shadow-dom bubble
28
+ - 🌗 **light or shadow,** render nakedly on the page, or within a cozy shadow bubble
29
+ - 🪝 **hooks-based,** familiar react-style [hooks](#hooks)
30
+ - **auto-reactive,** views magically rerender on [strata](https://github.com/e280/strata)-compatible state changes
31
+ - 🪶 **no compile step,** just god's honest javascript via [lit](https://lit.dev/)-html tagged-template-literals
32
+ - 🧩 **not web components,** no dom registration needed, just vibes and good typings
33
33
 
34
34
  ```ts
35
35
  import {html} from "lit"
@@ -41,7 +41,7 @@ export const MyShadowView = shadow(() => html`<p>shrouded in darkness</p>`)
41
41
  ```
42
42
 
43
43
  ### 🌞 light views
44
- > *just pretend it's react*
44
+ > *"just pretend it's react, without jsx"*
45
45
 
46
46
  - **define a light view**
47
47
  ```ts
@@ -64,9 +64,15 @@ export const MyShadowView = shadow(() => html`<p>shrouded in darkness</p>`)
64
64
  ${MyCounter(123)}
65
65
  `)
66
66
  ```
67
+ - light views have no host element, rendered output looks like:
68
+ ```html
69
+ <h1>my cool counter demo</h1>
70
+ <button>123</button>
71
+ ```
72
+ - **light views are naked,** they don't have a containing host element
67
73
 
68
74
  ### 🌚 shadow views
69
- > *each shadow view gets its own cozy [shadow-dom](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM) bubble and supports [slots](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots)*
75
+ > *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)*
70
76
 
71
77
  - **define a shadow view**
72
78
  ```ts
@@ -74,14 +80,14 @@ export const MyShadowView = shadow(() => html`<p>shrouded in darkness</p>`)
74
80
  import {shadow, useName, useCss, useSignal} from "@e280/sly"
75
81
 
76
82
  export const MyShadowCounter = shadow((start: number) => {
77
- useName("shadow-counter")
83
+ useName("counter")
78
84
  useCss(css`button { color: cyan }`)
79
85
 
80
86
  const $count = useSignal(start)
81
87
  const increment = () => $count.value++
82
88
 
83
89
  return html`
84
- <button @click="${increment}">${$count()}</button>
90
+ <button @click="${increment}">${$count.value}</button>
85
91
  <slot></slot>
86
92
  `
87
93
  })
@@ -93,6 +99,11 @@ export const MyShadowView = shadow(() => html`<p>shrouded in darkness</p>`)
93
99
  ${MyShadowCounter(234)}
94
100
  `)
95
101
  ```
102
+ - shadow views have a host element, rendered output looks like:
103
+ ```html
104
+ <h1>my cool counter demo</h1>
105
+ <sly-shadow view="counter"></sly-shadow>
106
+ ```
96
107
  - **.with to nest children or set attrs**
97
108
  ```ts
98
109
  dom.render(dom(".demo"), html`
@@ -107,13 +118,18 @@ export const MyShadowView = shadow(() => html`<p>shrouded in darkness</p>`)
107
118
  })}
108
119
  `)
109
120
  ```
110
- - **you can do custom shadow config if needed**
121
+ - **you can do custom shadow config if needed** (default shown)
111
122
  ```ts
112
- const MyShadowView = shadow.config(() => {
113
- const host = document.createElement("div")
123
+ import {SlyShadow} from "@e280/sly"
124
+
125
+ const customShadow = shadow.config(() => {
126
+ SlyShadow.register()
127
+ const host = document.createElement("sly-shadow")
114
128
  const shadow = host.attachShadow({mode: "open"})
115
129
  return {host, shadow}
116
- })(() => html`<p>shrouded in darkness</p>`)
130
+ })
131
+
132
+ const MyShadowView = customShadow(() => html`<p>shrouded in darkness</p>`)
117
133
  ```
118
134
 
119
135
 
@@ -122,48 +138,48 @@ export const MyShadowView = shadow(() => html`<p>shrouded in darkness</p>`)
122
138
  <a id="hooks"></a>
123
139
 
124
140
  ## 🪝 hooks
125
- > *composable view state and utilities*
141
+ > *composable view state and utilities*
126
142
 
127
143
  ### 👮 follow the hooks rules
128
144
 
129
- just like [react hooks](https://react.dev/warnings/invalid-hook-call-warning), the execution order of sly's `use` hooks actually matters.
145
+ just like [react hooks](https://react.dev/warnings/invalid-hook-call-warning), the execution order of hooks seriously matters.
130
146
 
131
- you must not call these hooks under `if` conditionals, or `for` loops, or in callbacks, or after a conditional `return` statement, or anything like that.. *otherwise, heed my warning: weird bad stuff will happen..*
147
+ 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..*
132
148
 
133
149
  ### 🌚 shadow-only hooks
134
- - **useName** — *(shadow only)* — set the "view" attribute value
150
+ - **useName,** set the "view" attribute value
135
151
  ```ts
136
152
  useName("squarepants")
137
153
  // <sly-shadow view="squarepants">
138
154
  ```
139
- - **useCss** — *(shadow only)* — attach stylesheets (use lit's `css`!) to the shadow root
155
+ - **useCss,** attach stylesheets (use lit's `css`!) to the shadow root
140
156
  ```ts
141
157
  useCss(css1, css2, css3)
142
158
  ```
143
- - **useHost** — *(shadow only)* — get the host element
159
+ - **useHost,** get the host element
144
160
  ```ts
145
161
  const host = useHost()
146
162
  ```
147
- - **useShadow** — *(shadow only)* — get the shadow root
163
+ - **useShadow,** get the shadow root
148
164
  ```ts
149
165
  const shadow = useShadow()
150
166
  ```
151
167
 
152
168
  ### 🌞 universal hooks
153
- - **useState** react-like hook to create some reactive state (we prefer signals)
169
+ - **useState,** react-like hook to create some reactive state (we prefer signals)
154
170
  ```ts
155
171
  const [count, setCount] = useState(0)
156
172
 
157
173
  const increment = () => setCount(n => n + 1)
158
174
  ```
159
- - **useRef** react-like hook to make a non-reactive box for a value
175
+ - **useRef,** react-like hook to make a non-reactive box for a value
160
176
  ```ts
161
177
  const ref = useRef(0)
162
178
 
163
179
  ref.current // 0
164
180
  ref.current = 1 // does not trigger rerender
165
181
  ```
166
- - **useSignal** create a [strata](https://github.com/e280/strata) signal
182
+ - **useSignal,** create a [strata](https://github.com/e280/strata) signal
167
183
  ```ts
168
184
  const $count = useSignal(1)
169
185
 
@@ -173,13 +189,11 @@ you must not call these hooks under `if` conditionals, or `for` loops, or in cal
173
189
  // write the signal
174
190
  $count(2)
175
191
  ```
176
- - see [strata readme](https://github.com/e280/strata)
177
- - **useDerived** — create a [strata](https://github.com/e280/strata) derived signal
192
+ - **useDerived,** create a [strata](https://github.com/e280/strata) derived signal
178
193
  ```ts
179
194
  const $product = useDerived(() => $count() * $whatever())
180
195
  ```
181
- - see [strata readme](https://github.com/e280/strata)
182
- - **useOnce** — run fn at initialization, and return a value
196
+ - **useOnce,** run fn at initialization, and return a value
183
197
  ```ts
184
198
  const whatever = useOnce(() => {
185
199
  console.log("happens one time")
@@ -188,14 +202,14 @@ you must not call these hooks under `if` conditionals, or `for` loops, or in cal
188
202
 
189
203
  whatever // 123
190
204
  ```
191
- - **useMount** setup mount/unmount lifecycle
205
+ - **useMount,** setup mount/unmount lifecycle
192
206
  ```ts
193
207
  useMount(() => {
194
208
  console.log("mounted")
195
209
  return () => console.log("unmounted")
196
210
  })
197
211
  ```
198
- - **useWake** run fn each time mounted, and return value
212
+ - **useWake,** run fn each time mounted, and return value
199
213
  ```ts
200
214
  const whatever = useWake(() => {
201
215
  console.log("mounted")
@@ -204,7 +218,7 @@ you must not call these hooks under `if` conditionals, or `for` loops, or in cal
204
218
 
205
219
  whatever // 123
206
220
  ```
207
- - **useLife** mount/unmount lifecycle, but also return a value
221
+ - **useLife,** mount/unmount lifecycle, but also return a value
208
222
  ```ts
209
223
  const whatever = useLife(() => {
210
224
  console.log("mounted")
@@ -214,30 +228,30 @@ you must not call these hooks under `if` conditionals, or `for` loops, or in cal
214
228
 
215
229
  whatever // 123
216
230
  ```
217
- - **useRender** returns a fn to rerender the view (debounced)
231
+ - **useRender,** returns a fn to rerender the view (debounced)
218
232
  ```ts
219
233
  const render = useRender()
220
234
 
221
235
  render().then(() => console.log("render done"))
222
236
  ```
223
- - **useRendered** get a promise that resolves *after* the next render
237
+ - **useRendered,** get a promise that resolves *after* the next render
224
238
  ```ts
225
239
  useRendered().then(() => console.log("rendered"))
226
240
  ```
227
- - **useOp** start loading an op based on an async fn
241
+ - **useOp,** start loading an op based on an async fn
228
242
  ```ts
229
243
  const op = useOp(async() => {
230
244
  await nap(5000)
231
245
  return 123
232
246
  })
233
247
  ```
234
- - **useOpPromise** start loading an op based on a promise
248
+ - **useOpPromise,** start loading an op based on a promise
235
249
  ```ts
236
250
  const op = useOpPromise(doAsyncWork())
237
251
  ```
238
252
 
239
253
  ### 🧑‍🍳 happy hooks recipes
240
- - make a ticker mount, cycle, and nap
254
+ - make a ticker, mount, cycle, and nap
241
255
  ```ts
242
256
  import {cycle, nap} from "@e280/stz"
243
257
  ```
@@ -251,7 +265,9 @@ you must not call these hooks under `if` conditionals, or `for` loops, or in cal
251
265
  ```
252
266
  - wake + rendered, to do something after each mount's first render
253
267
  ```ts
254
- useWake(() => useRendered.then(() => {
268
+ const rendered = useRendered()
269
+
270
+ useWake(() => rendered.then(() => {
255
271
  console.log("after first render")
256
272
  }))
257
273
  ```
@@ -262,7 +278,7 @@ you must not call these hooks under `if` conditionals, or `for` loops, or in cal
262
278
  <a id="ops"></a>
263
279
 
264
280
  ## 🫛 ops
265
- > *tools for async operations and loading spinners*
281
+ > *helpers for async operations*
266
282
 
267
283
  ```ts
268
284
  import {nap} from "@e280/stz"
@@ -300,19 +316,19 @@ import {Pod, podium, Op, loaders} from "@e280/sly"
300
316
  - see more at [podium.ts](./s/ops/podium.ts)
301
317
 
302
318
  ### 🫛 ops: nice pod ergonomics
303
- - an `Op<V>` wraps a pod with a signal for reactivity
319
+ - an `Op<V>` wraps a pod with a strata signal for reactivity
304
320
  - create an op
305
321
  ```ts
306
- const op = new Op<number>() // loading status by default
322
+ new Op<number>() // loading status by default
307
323
  ```
308
324
  ```ts
309
- const op = Op.loading<number>()
325
+ Op.loading<number>()
310
326
  ```
311
327
  ```ts
312
- const op = Op.ready<number>(123)
328
+ Op.ready<number>(123)
313
329
  ```
314
330
  ```ts
315
- const op = Op.error<number>(new Error())
331
+ Op.error<number>(new Error())
316
332
  ```
317
333
  - 🔥 create an op that calls and tracks an async fn
318
334
  ```ts
@@ -339,13 +355,12 @@ import {Pod, podium, Op, loaders} from "@e280/sly"
339
355
  - select executes a fn based on the status
340
356
  ```ts
341
357
  const result = op.select({
342
- loading: () => "it's loading...",
358
+ loading: () => "still loading...",
343
359
  ready: value => `dude, it's ready! ${value}`,
344
- error: err => `dude, there's an error!`,
360
+ error: err => `ack! an error!`,
345
361
  })
346
362
 
347
- result
348
- // "dude, it's ready! 123"
363
+ result // "dude, it's ready! 123"
349
364
  ```
350
365
  - morph returns a new pod, transforming the value if ready
351
366
  ```ts
@@ -371,7 +386,7 @@ import {Pod, podium, Op, loaders} from "@e280/sly"
371
386
  <a id="loaders"></a>
372
387
 
373
388
  ## ⏳ loaders
374
- > *animated loading spinners for ops*
389
+ > *animated loading spinners for ops*
375
390
 
376
391
  ```ts
377
392
  import {loaders} from "@e280/sly"
@@ -528,7 +543,7 @@ import {ev} from "@e280/stz"
528
543
  <a id="dom"></a>
529
544
 
530
545
  ## 🪄 dom
531
- > *the "it's not jquery!" multitool*
546
+ > *the "it's not jquery!" multitool*
532
547
 
533
548
  ```ts
534
549
  import {dom} from "@e280/sly"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@e280/sly",
3
- "version": "0.3.0-2",
3
+ "version": "0.3.0-4",
4
4
  "description": "web shadow views",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,5 +1,11 @@
1
1
 
2
+ import {dom} from "../../dom/dom.js"
3
+
2
4
  export class SlyShadow extends HTMLElement {
5
+ static register() {
6
+ dom.register({SlyShadow}, {soft: true})
7
+ }
8
+
3
9
  connectedCallback() {
4
10
  if (!this.hasAttribute("view"))
5
11
  this.setAttribute("view", "")
package/s/view/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
 
2
2
  export * from "./common/css-reset.js"
3
+ export * from "./common/sly-shadow.js"
3
4
 
4
5
  export * from "./hooks/use-css.js"
5
6
  export * from "./hooks/use-cx.js"
package/s/view/shadow.ts CHANGED
@@ -2,29 +2,29 @@
2
2
  import {microbounce} from "@e280/stz"
3
3
  import {render as litRender} from "lit"
4
4
 
5
- import {dom} from "../dom/dom.js"
6
5
  import {ShadowCx} from "./parts/cx.js"
7
- import {SlyShadow} from "./parts/sly-shadow.js"
8
6
  import {hooks} from "./hooks/plumbing/hooks.js"
7
+ import {SlyShadow} from "./common/sly-shadow.js"
9
8
  import {Reactivity} from "./parts/reactivity.js"
10
9
  import {applyAttrs} from "./parts/apply-attrs.js"
11
10
  import {Hookscope} from "./hooks/plumbing/hookscope.js"
12
11
  import {View, Placement, ShadowSetup, ShadowView} from "./types.js"
13
12
  import {AsyncDirective, directive, PartInfo} from "lit/async-directive.js"
14
13
 
14
+ export function shadowSetup(): ShadowSetup {
15
+ SlyShadow.register()
16
+ const host = document.createElement("sly-shadow")
17
+ const shadow = host.attachShadow({mode: "open"})
18
+ return {host, shadow}
19
+ }
20
+
15
21
  export function shadow<Props extends any[]>(view: View<Props>) {
16
- const setupFn = (): ShadowSetup => {
17
- dom.register({SlyShadow}, {soft: true})
18
- const host = document.createElement("sly-shadow")
19
- const shadow = host.attachShadow({mode: "open"})
20
- return {host, shadow}
21
- }
22
- return rawShadow(setupFn, view)
22
+ return rawShadow(shadowSetup, view)
23
23
  }
24
24
 
25
- shadow.config = (setupFn: () => ShadowSetup) => (
25
+ shadow.setup = (setup: () => ShadowSetup) => (
26
26
  <Props extends any[]>(view: View<Props>) => (
27
- rawShadow(setupFn, view)
27
+ rawShadow(setup, view)
28
28
  )
29
29
  )
30
30