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