@e280/sly 0.2.4 → 0.3.0-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +302 -614
- package/package.json +6 -8
- package/s/_archive/README.md +1221 -0
- package/s/{base → _archive/base}/element.ts +5 -2
- package/s/_archive/view/index.ts +7 -0
- package/s/_archive/view/types.ts +45 -0
- package/s/{view → _archive/view}/utils/parts/capsule.ts +9 -2
- 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 +24 -18
- package/s/demo/views/loaders.ts +7 -7
- 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 -58
- 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 +23 -18
- 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/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 +6 -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/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 -52
- 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 -374
- 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/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 -50
- 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}/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/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,167 @@
|
|
|
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
|
-
- 🍋 [**#views**](#views) — shadow-dom'd,
|
|
10
|
-
-
|
|
11
|
-
- 🪄 [**#dom**](#dom) — the "it's not jquery" multitool
|
|
13
|
+
- 🍋 [**#views**](#views) — hooks-based, shadow-dom'd, template-literal'd
|
|
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
|
+
> *modern views.. in lightness, or darkness..*
|
|
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** — declarative rendering with [modern hooks](#hooks) familiar to react devs
|
|
29
32
|
|
|
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
|
|
33
|
+
```ts
|
|
34
|
+
import {html} from "lit"
|
|
35
|
+
import {light, shadow, dom} from "@e280/sly"
|
|
35
36
|
|
|
36
|
-
>
|
|
37
|
-
> you can import everything in sly from `@e280/sly`,
|
|
38
|
-
> or from specific subpackages like `@e280/sly/view`, `@e280/sly/dom`, etc...
|
|
37
|
+
export const MyLightView = light(() => html`<p>blinded by the light</p>`)
|
|
39
38
|
|
|
39
|
+
export const MyShadowView = shadow(() => html`<p>shrouded in darkness</p>`)
|
|
40
|
+
```
|
|
40
41
|
|
|
42
|
+
### 🌞 light views
|
|
43
|
+
> *just pretend it's react*
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
- **define a light view**
|
|
46
|
+
```ts
|
|
47
|
+
import {html} from "lit"
|
|
48
|
+
import {light, useSignal} from "@e280/sly"
|
|
44
49
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
export const MyCounter = light((start: number) => {
|
|
51
|
+
const $count = useSignal(start)
|
|
52
|
+
const increment = () => $count.value++
|
|
48
53
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
return html`
|
|
55
|
+
<button @click="${increment}">${$count.value}</button>
|
|
56
|
+
`
|
|
57
|
+
})
|
|
58
|
+
```
|
|
59
|
+
- **render it into the dom**
|
|
60
|
+
```ts
|
|
61
|
+
dom.in(".demo").render(html`
|
|
62
|
+
<h1>my cool counter demo</h1>
|
|
63
|
+
${MyCounter(123)}
|
|
64
|
+
`)
|
|
65
|
+
```
|
|
52
66
|
|
|
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)
|
|
67
|
+
### 🌚 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)*
|
|
59
69
|
|
|
60
|
-
|
|
61
|
-
```ts
|
|
62
|
-
import {view, dom, BaseElement} from "@e280/sly"
|
|
63
|
-
import {html, css} from "lit"
|
|
64
|
-
```
|
|
65
|
-
- **declare view**
|
|
70
|
+
- **define a shadow view**
|
|
66
71
|
```ts
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
import {css, html} from "lit"
|
|
73
|
+
import {shadow, useName, useCss, useSignal} from "@e280/sly"
|
|
74
|
+
|
|
75
|
+
export const MyShadowCounter = shadow((start: number) => {
|
|
76
|
+
useName("shadow-counter")
|
|
77
|
+
useCss(css`button { color: cyan }`)
|
|
69
78
|
|
|
70
|
-
const $count =
|
|
79
|
+
const $count = useSignal(start)
|
|
71
80
|
const increment = () => $count.value++
|
|
72
81
|
|
|
73
82
|
return html`
|
|
74
|
-
<button @click="${increment}">
|
|
75
|
-
|
|
76
|
-
</button>
|
|
83
|
+
<button @click="${increment}">${$count()}</button>
|
|
84
|
+
<slot></slot>
|
|
77
85
|
`
|
|
78
86
|
})
|
|
79
87
|
```
|
|
80
|
-
|
|
81
|
-
- **inject view into dom**
|
|
88
|
+
- **render it into the dom**
|
|
82
89
|
```ts
|
|
83
|
-
dom.in(".
|
|
84
|
-
<h1>cool counter demo</h1>
|
|
85
|
-
${
|
|
90
|
+
dom.in(".demo").render(html`
|
|
91
|
+
<h1>my cool counter demo</h1>
|
|
92
|
+
${MyShadowCounter(234)}
|
|
86
93
|
`)
|
|
87
94
|
```
|
|
88
|
-
-
|
|
95
|
+
- **.with to nest children or set attrs**
|
|
89
96
|
```ts
|
|
90
|
-
dom.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
97
|
+
dom.in(".demo").render(html`
|
|
98
|
+
<h1>my cool counter demo</h1>
|
|
99
|
+
|
|
100
|
+
${MyShadowCounter.with({
|
|
101
|
+
props: [234],
|
|
102
|
+
attrs: {"data-whatever": 555},
|
|
103
|
+
children: html`
|
|
104
|
+
<p>woah, slotting support!</p>
|
|
105
|
+
`,
|
|
106
|
+
})}
|
|
107
|
+
`)
|
|
95
108
|
```
|
|
96
|
-
|
|
97
|
-
|
|
109
|
+
- **oh, you can do custom shadow config if needed**
|
|
110
|
+
```ts
|
|
111
|
+
const MyCustomShadow = shadow.config(() => {
|
|
112
|
+
const host = document.createElement("div")
|
|
113
|
+
const shadow = host.attachShadow({mode: "open"})
|
|
114
|
+
return {host, shadow}
|
|
115
|
+
})(() => html`<p>shrouded in darkness</p>`)
|
|
98
116
|
```
|
|
99
117
|
|
|
100
|
-
|
|
101
|
-
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
<br/><br/>
|
|
121
|
+
<a id="hooks"></a>
|
|
122
|
+
|
|
123
|
+
## 🪝 hooks
|
|
124
|
+
> *composable view state and utilities*
|
|
125
|
+
|
|
126
|
+
### 👮 follow the hooks rules
|
|
127
|
+
|
|
128
|
+
just like [react hooks](https://react.dev/warnings/invalid-hook-call-warning), the execution order of sly's `use` hooks actually matters.
|
|
129
|
+
|
|
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..*
|
|
131
|
+
|
|
132
|
+
### 🌚 shadow-only hooks
|
|
133
|
+
- **useName** — *(shadow only)* — set the "view" attribute value
|
|
102
134
|
```ts
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
.render(use => (greeting: string) => html`😎 ${greeting} <slot></slot>`)
|
|
135
|
+
useName("squarepants")
|
|
136
|
+
// <sly-shadow view="squarepants">
|
|
106
137
|
```
|
|
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
|
|
138
|
+
- **useCss** — *(shadow only)* — attach stylesheets (use lit's `css`!) to the shadow root
|
|
112
139
|
```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
|
-
`)
|
|
140
|
+
useCss(css1, css2, css3)
|
|
121
141
|
```
|
|
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,**
|
|
142
|
+
- **useHost** — *(shadow only)* — get the host element
|
|
129
143
|
```ts
|
|
130
|
-
|
|
131
|
-
return html`<p>hello ${name}</p>`
|
|
132
|
-
})
|
|
144
|
+
const host = useHost()
|
|
133
145
|
```
|
|
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
|
-
})
|
|
146
|
+
- **useShadow** — *(shadow only)* — get the shadow root
|
|
147
|
+
```ts
|
|
148
|
+
const shadow = useShadow()
|
|
215
149
|
```
|
|
216
150
|
|
|
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">`
|
|
151
|
+
### 🌞 universal hooks
|
|
152
|
+
- **useState** — react-like hook to create some reactive state (we prefer signals)
|
|
224
153
|
```ts
|
|
225
|
-
|
|
154
|
+
const [count, setCount] = useState(0)
|
|
155
|
+
|
|
156
|
+
const increment = () => setCount(n => n + 1)
|
|
226
157
|
```
|
|
227
|
-
- **
|
|
158
|
+
- **useRef** — react-like hook to make a non-reactive box for a value
|
|
228
159
|
```ts
|
|
229
|
-
|
|
160
|
+
const ref = useRef(0)
|
|
161
|
+
|
|
162
|
+
ref.current // 0
|
|
163
|
+
ref.current = 1 // does not trigger rerender
|
|
230
164
|
```
|
|
231
|
-
|
|
232
|
-
- **use.signal** — create a [strata signal](https://github.com/e280/strata)
|
|
165
|
+
- **useSignal** — create a [strata](https://github.com/e280/strata) signal
|
|
233
166
|
```ts
|
|
234
|
-
const $count =
|
|
167
|
+
const $count = useSignal(1)
|
|
235
168
|
|
|
236
169
|
// read the signal
|
|
237
170
|
$count()
|
|
@@ -239,391 +172,95 @@ import {html, css} from "lit"
|
|
|
239
172
|
// write the signal
|
|
240
173
|
$count(2)
|
|
241
174
|
```
|
|
242
|
-
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
```ts
|
|
253
|
-
const whatever = use.once(() => {
|
|
254
|
-
console.log("happens only once")
|
|
175
|
+
- see [strata readme](https://github.com/e280/strata)
|
|
176
|
+
- **useDerived** — create a [strata](https://github.com/e280/strata) derived signal
|
|
177
|
+
```ts
|
|
178
|
+
const $product = useDerived(() => $count() * $whatever())
|
|
179
|
+
```
|
|
180
|
+
- see [strata readme](https://github.com/e280/strata)
|
|
181
|
+
- **useOnce** — run fn at initialization, and return a value
|
|
182
|
+
```ts
|
|
183
|
+
const whatever = useOnce(() => {
|
|
184
|
+
console.log("happens one time")
|
|
255
185
|
return 123
|
|
256
186
|
})
|
|
257
187
|
|
|
258
188
|
whatever // 123
|
|
259
189
|
```
|
|
260
|
-
- **
|
|
190
|
+
- **useMount** — setup mount/unmount lifecycle
|
|
261
191
|
```ts
|
|
262
|
-
|
|
263
|
-
console.log("
|
|
264
|
-
|
|
265
|
-
return () => {
|
|
266
|
-
console.log("view unmounted")
|
|
267
|
-
}
|
|
192
|
+
useMount(() => {
|
|
193
|
+
console.log("mounted")
|
|
194
|
+
return () => console.log("unmounted")
|
|
268
195
|
})
|
|
269
196
|
```
|
|
270
|
-
- **
|
|
197
|
+
- **useWake** — run fn each time mounted, and return value
|
|
271
198
|
```ts
|
|
272
|
-
const whatever =
|
|
273
|
-
console.log("
|
|
199
|
+
const whatever = useWake(() => {
|
|
200
|
+
console.log("mounted")
|
|
274
201
|
return 123
|
|
275
202
|
})
|
|
276
203
|
|
|
277
204
|
whatever // 123
|
|
278
205
|
```
|
|
279
|
-
- **
|
|
206
|
+
- **useLife** — mount/unmount lifecycle, but also return a value
|
|
280
207
|
```ts
|
|
281
|
-
const
|
|
208
|
+
const whatever = useLife(() => {
|
|
282
209
|
console.log("mounted")
|
|
283
210
|
const value = 123
|
|
284
211
|
return [value, () => console.log("unmounted")]
|
|
285
212
|
})
|
|
286
213
|
|
|
287
|
-
|
|
214
|
+
whatever // 123
|
|
288
215
|
```
|
|
289
|
-
- **
|
|
216
|
+
- **useRender** — returns a fn to rerender the view (debounced)
|
|
290
217
|
```ts
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
})
|
|
218
|
+
const render = useRender()
|
|
219
|
+
|
|
220
|
+
render().then(() => console.log("render done"))
|
|
295
221
|
```
|
|
296
|
-
- **
|
|
222
|
+
- **useRendered** — get a promise that resolves *after* the next render
|
|
297
223
|
```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
|
-
})
|
|
224
|
+
useRendered().then(() => console.log("rendered"))
|
|
343
225
|
```
|
|
344
|
-
- **
|
|
226
|
+
- **useOp** — start loading an op based on an async fn
|
|
345
227
|
```ts
|
|
346
|
-
const op =
|
|
228
|
+
const op = useOp(async() => {
|
|
347
229
|
await nap(5000)
|
|
348
230
|
return 123
|
|
349
231
|
})
|
|
350
232
|
```
|
|
351
|
-
- **
|
|
233
|
+
- **useOpPromise** — start loading an op based on a promise
|
|
352
234
|
```ts
|
|
353
|
-
const op =
|
|
235
|
+
const op = useOpPromise(doAsyncWork())
|
|
354
236
|
```
|
|
355
237
|
|
|
356
|
-
###
|
|
238
|
+
### 🧑🍳 happy hooks recipes
|
|
357
239
|
- make a ticker — mount, cycle, and nap
|
|
358
240
|
```ts
|
|
359
241
|
import {cycle, nap} from "@e280/stz"
|
|
360
242
|
```
|
|
361
243
|
```ts
|
|
362
|
-
const $seconds =
|
|
244
|
+
const $seconds = useSignal(0)
|
|
363
245
|
|
|
364
|
-
|
|
246
|
+
useMount(() => cycle(async() => {
|
|
365
247
|
await nap(1000)
|
|
366
248
|
$seconds.value++
|
|
367
249
|
}))
|
|
368
250
|
```
|
|
369
251
|
- wake + rendered, to do something after each mount's first render
|
|
370
252
|
```ts
|
|
371
|
-
|
|
253
|
+
useWake(() => useRendered.then(() => {
|
|
372
254
|
console.log("after first render")
|
|
373
255
|
}))
|
|
374
256
|
```
|
|
375
257
|
|
|
376
258
|
|
|
377
259
|
|
|
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
260
|
<br/><br/>
|
|
623
261
|
<a id="ops"></a>
|
|
624
262
|
|
|
625
|
-
##
|
|
626
|
-
> `@e280/sly/ops`
|
|
263
|
+
## 🫛 ops
|
|
627
264
|
> *tools for async operations and loading spinners*
|
|
628
265
|
|
|
629
266
|
```ts
|
|
@@ -732,8 +369,7 @@ import {Pod, podium, Op, loaders} from "@e280/sly"
|
|
|
732
369
|
<br/><br/>
|
|
733
370
|
<a id="loaders"></a>
|
|
734
371
|
|
|
735
|
-
##
|
|
736
|
-
> `@e280/sly/loaders`
|
|
372
|
+
## ⏳ loaders
|
|
737
373
|
> *animated loading spinners for ops*
|
|
738
374
|
|
|
739
375
|
```ts
|
|
@@ -765,100 +401,10 @@ import {loaders} from "@e280/sly"
|
|
|
765
401
|
|
|
766
402
|
|
|
767
403
|
|
|
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
404
|
<br/><br/>
|
|
858
405
|
<a id="loot"></a>
|
|
859
406
|
|
|
860
|
-
##
|
|
861
|
-
> `@e280/sly/loot`
|
|
407
|
+
## 🪙 loot
|
|
862
408
|
> *drag-and-drop facilities*
|
|
863
409
|
|
|
864
410
|
```ts
|
|
@@ -881,7 +427,7 @@ import {ev} from "@e280/stz"
|
|
|
881
427
|
- **attach event listeners to your dropzone,** one of these ways:
|
|
882
428
|
- **view example**
|
|
883
429
|
```ts
|
|
884
|
-
|
|
430
|
+
light(() => html`
|
|
885
431
|
<div
|
|
886
432
|
?data-indicator="${drops.$indicator()}"
|
|
887
433
|
@dragover="${drops.dragover}"
|
|
@@ -937,9 +483,9 @@ import {ev} from "@e280/stz"
|
|
|
937
483
|
```
|
|
938
484
|
- **attach dragzone listeners** (there can be many dragzones...)
|
|
939
485
|
```ts
|
|
940
|
-
|
|
941
|
-
const money =
|
|
942
|
-
const dragzone =
|
|
486
|
+
light(() => {
|
|
487
|
+
const money = useOnce((): Money => ({value: 280}))
|
|
488
|
+
const dragzone = useOnce(() => dnd.dragzone(() => money))
|
|
943
489
|
|
|
944
490
|
return html`
|
|
945
491
|
<div
|
|
@@ -953,9 +499,9 @@ import {ev} from "@e280/stz"
|
|
|
953
499
|
```
|
|
954
500
|
- **attach dropzone listeners** (there can be many dropzones...)
|
|
955
501
|
```ts
|
|
956
|
-
|
|
957
|
-
const bag =
|
|
958
|
-
const dropzone =
|
|
502
|
+
light(() => {
|
|
503
|
+
const bag = useOnce((): Bag => ({id: 1}))
|
|
504
|
+
const dropzone = useOnce(() => dnd.dropzone(() => bag))
|
|
959
505
|
const indicator = !!(dnd.dragging && dnd.hovering === bag)
|
|
960
506
|
|
|
961
507
|
return html`
|
|
@@ -978,13 +524,155 @@ import {ev} from "@e280/stz"
|
|
|
978
524
|
|
|
979
525
|
|
|
980
526
|
<br/><br/>
|
|
981
|
-
<a id="
|
|
527
|
+
<a id="dom"></a>
|
|
982
528
|
|
|
983
|
-
##
|
|
984
|
-
|
|
985
|
-
|
|
529
|
+
## 🪄 dom
|
|
530
|
+
> *the "it's not jquery!" multitool*
|
|
531
|
+
|
|
532
|
+
```ts
|
|
533
|
+
import {dom} from "@e280/sly"
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### 🪄 dom queries
|
|
537
|
+
- `require` an element
|
|
538
|
+
```ts
|
|
539
|
+
dom(".demo")
|
|
540
|
+
// HTMLElement (or throws)
|
|
541
|
+
```
|
|
542
|
+
```ts
|
|
543
|
+
// alias
|
|
544
|
+
dom.require(".demo")
|
|
545
|
+
// HTMLElement (or throws)
|
|
546
|
+
```
|
|
547
|
+
- `maybe` get an element
|
|
548
|
+
```ts
|
|
549
|
+
dom.maybe(".demo")
|
|
550
|
+
// HTMLElement | undefined
|
|
551
|
+
```
|
|
552
|
+
- `all` matching elements in an array
|
|
553
|
+
```ts
|
|
554
|
+
dom.all(".demo ul li")
|
|
555
|
+
// HTMLElement[]
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
### 🪄 dom.in scope
|
|
559
|
+
- make a scope
|
|
560
|
+
```ts
|
|
561
|
+
dom.in(".demo") // selector
|
|
562
|
+
// Dom instance
|
|
563
|
+
```
|
|
564
|
+
```ts
|
|
565
|
+
dom.in(demoElement) // element
|
|
566
|
+
// Dom instance
|
|
567
|
+
```
|
|
568
|
+
- run queries in that scope
|
|
569
|
+
```ts
|
|
570
|
+
dom.in(demoElement).require(".button")
|
|
571
|
+
```
|
|
572
|
+
```ts
|
|
573
|
+
dom.in(demoElement).maybe(".button")
|
|
574
|
+
```
|
|
575
|
+
```ts
|
|
576
|
+
dom.in(demoElement).all("ol li")
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
### 🪄 dom utilities
|
|
580
|
+
- `dom.register` web components
|
|
581
|
+
```ts
|
|
582
|
+
dom.register({MyComponent, AnotherCoolComponent})
|
|
583
|
+
// <my-component>
|
|
584
|
+
// <another-cool-component>
|
|
585
|
+
```
|
|
586
|
+
- `dom.register` automatically dashes the tag names (`MyComponent` becomes `<my-component>`)
|
|
587
|
+
- `dom.render` content into an element
|
|
588
|
+
```ts
|
|
589
|
+
dom.render(element, html`<p>hello world</p>`)
|
|
590
|
+
```
|
|
591
|
+
```ts
|
|
592
|
+
dom.in(".demo").render(html`<p>hello world</p>`)
|
|
593
|
+
```
|
|
594
|
+
- `dom.el` little element builder
|
|
595
|
+
```ts
|
|
596
|
+
const div = dom.el("div", {"data-whatever": 123, "data-active": true})
|
|
597
|
+
// <div data-whatever="123" data-active></div>
|
|
598
|
+
```
|
|
599
|
+
- `dom.elmer` make an element with a fluent chain
|
|
600
|
+
```ts
|
|
601
|
+
const div = dom.elmer("div")
|
|
602
|
+
.attr("data-whatever", 123)
|
|
603
|
+
.attr("data-active")
|
|
604
|
+
.children("hello world")
|
|
605
|
+
.done()
|
|
606
|
+
// HTMLElement
|
|
607
|
+
```
|
|
608
|
+
- `dom.mk` make an element with a lit template (returns the first)
|
|
609
|
+
```ts
|
|
610
|
+
const div = dom.mk(html`
|
|
611
|
+
<div data-whatever="123" data-active>
|
|
612
|
+
hello world
|
|
613
|
+
</div>
|
|
614
|
+
`) // HTMLElement
|
|
615
|
+
```
|
|
616
|
+
- `dom.events` <a id="dom.events"></a> to attach event listeners
|
|
617
|
+
```ts
|
|
618
|
+
const detach = dom.events(element, {
|
|
619
|
+
keydown: (e: KeyboardEvent) => console.log("keydown", e.code),
|
|
620
|
+
keyup: (e: KeyboardEvent) => console.log("keyup", e.code),
|
|
621
|
+
})
|
|
622
|
+
```
|
|
623
|
+
```ts
|
|
624
|
+
const detach = dom.in(".demo").events({
|
|
625
|
+
keydown: (e: KeyboardEvent) => console.log("keydown", e.code),
|
|
626
|
+
keyup: (e: KeyboardEvent) => console.log("keyup", e.code),
|
|
627
|
+
})
|
|
628
|
+
```
|
|
629
|
+
```ts
|
|
630
|
+
// unattach those event listeners when you're done
|
|
631
|
+
detach()
|
|
632
|
+
```
|
|
633
|
+
- `dom.attrs` <a id="dom.attrs"></a> to setup a type-happy html attribute helper
|
|
634
|
+
```ts
|
|
635
|
+
const attrs = dom.attrs(element).spec({
|
|
636
|
+
name: String,
|
|
637
|
+
count: Number,
|
|
638
|
+
active: Boolean,
|
|
639
|
+
})
|
|
640
|
+
```
|
|
641
|
+
```ts
|
|
642
|
+
const attrs = dom.in(".demo").attrs.spec({
|
|
643
|
+
name: String,
|
|
644
|
+
count: Number,
|
|
645
|
+
active: Boolean,
|
|
646
|
+
})
|
|
647
|
+
```
|
|
648
|
+
```ts
|
|
649
|
+
attrs.name // "chase"
|
|
650
|
+
attrs.count // 123
|
|
651
|
+
attrs.active // true
|
|
652
|
+
```
|
|
653
|
+
```ts
|
|
654
|
+
attrs.name = "zenky"
|
|
655
|
+
attrs.count = 124
|
|
656
|
+
attrs.active = false // removes html attr
|
|
657
|
+
```
|
|
658
|
+
```ts
|
|
659
|
+
attrs.name = undefined // removes the attr
|
|
660
|
+
attrs.count = undefined // removes the attr
|
|
661
|
+
```
|
|
662
|
+
or if you wanna be more loosey-goosey, skip the spec
|
|
663
|
+
```ts
|
|
664
|
+
const a = dom.in(".demo").attrs
|
|
665
|
+
a.strings.name = "pimsley"
|
|
666
|
+
a.numbers.count = 125
|
|
667
|
+
a.booleans.active = true
|
|
668
|
+
```
|
|
986
669
|
|
|
987
670
|
|
|
988
671
|
|
|
989
672
|
<br/><br/>
|
|
673
|
+
<a id="e280"></a>
|
|
674
|
+
|
|
675
|
+
## 🧑💻 sly is by e280
|
|
676
|
+
reward us with github stars
|
|
677
|
+
build with us at https://e280.org/ but only if you're cool
|
|
990
678
|
|