@e280/sly 0.2.5 → 0.3.0-10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +356 -626
- package/package.json +22 -30
- 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 +5 -8
- 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 -20
- package/s/demo/views/loaders.ts +9 -9
- package/s/demo/views/time-light.ts +19 -0
- package/s/demo/views/time-shadow.ts +22 -0
- package/s/dom/attrs/attrs.ts +3 -3
- package/s/dom/attrs/parts/attr-spec.ts +17 -5
- package/s/dom/attrs/parts/on-attrs.ts +1 -1
- package/s/dom/dom.ts +1 -1
- package/s/index.html.ts +30 -33
- package/s/index.ts +2 -4
- package/s/{loaders → loader}/index.barrel.ts +0 -1
- package/s/{loaders → loader}/index.ts +0 -1
- package/s/{loaders → loader}/make.ts +1 -1
- package/s/{loaders → loader}/parts/ascii-anim.ts +6 -8
- package/s/loader/parts/error-display.ts +26 -0
- package/s/{loaders → loader}/types.ts +1 -1
- package/s/test.ts +5 -0
- package/s/view/common/css-reset.ts +19 -0
- package/s/view/common/sly-shadow.ts +14 -0
- package/s/view/elements/light.ts +14 -0
- package/s/view/elements/shadow.ts +52 -0
- package/s/view/hooks/plumbing/hooks.ts +28 -0
- package/s/view/hooks/plumbing/hookscope.ts +12 -0
- package/s/view/hooks/use-attrs.ts +28 -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 +22 -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/shadow.ts +93 -0
- package/s/view/types.ts +15 -34
- package/x/demo/demo.bundle.js +5 -8
- package/x/demo/demo.bundle.js.map +1 -1
- package/x/demo/demo.bundle.min.js +52 -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 +24 -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 +9 -9
- package/x/demo/views/loaders.js.map +1 -1
- package/x/demo/views/{counter.d.ts → time-light.d.ts} +3 -19
- package/x/demo/views/time-light.js +16 -0
- package/x/demo/views/time-light.js.map +1 -0
- package/x/demo/views/time-shadow.d.ts +365 -0
- package/x/demo/views/time-shadow.js +18 -0
- package/x/demo/views/time-shadow.js.map +1 -0
- package/x/dom/attrs/attrs.d.ts +3 -2
- package/x/dom/attrs/attrs.js +2 -2
- package/x/dom/attrs/attrs.js.map +1 -1
- package/x/dom/attrs/parts/attr-spec.d.ts +5 -1
- package/x/dom/attrs/parts/attr-spec.js +12 -6
- package/x/dom/attrs/parts/attr-spec.js.map +1 -1
- package/x/dom/attrs/parts/on-attrs.d.ts +1 -1
- package/x/dom/attrs/parts/on-attrs.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 +2 -4
- 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 +2 -4
- package/x/index.js.map +1 -1
- package/x/{loaders → loader}/index.barrel.d.ts +0 -1
- package/x/loader/index.barrel.js.map +1 -0
- package/x/{loaders → loader}/index.d.ts +0 -1
- package/x/loader/index.js.map +1 -0
- package/x/{loaders → loader}/make.d.ts +1 -1
- package/x/loader/make.js.map +1 -0
- package/x/loader/mock.js.map +1 -0
- package/x/loader/parts/anims.js.map +1 -0
- package/x/{loaders → loader}/parts/ascii-anim.d.ts +1 -1
- package/x/{loaders → loader}/parts/ascii-anim.js +6 -7
- package/x/loader/parts/ascii-anim.js.map +1 -0
- package/x/loader/parts/error-display.d.ts +1 -0
- package/x/loader/parts/error-display.js +20 -0
- package/x/loader/parts/error-display.js.map +1 -0
- package/x/{loaders → loader}/types.d.ts +1 -1
- package/x/{loaders → loader}/types.js.map +1 -1
- package/x/loot/drag-and-drops.d.ts +2 -2
- package/x/loot/drops.d.ts +1 -1
- package/x/op/index.js.map +1 -0
- package/x/op/op.js.map +1 -0
- package/x/op/podium.js.map +1 -0
- package/x/{ops → op}/types.js.map +1 -1
- package/x/test.js +3 -0
- package/x/test.js.map +1 -0
- package/x/view/common/css-reset.js +17 -0
- package/x/view/common/css-reset.js.map +1 -0
- package/x/view/common/sly-shadow.d.ts +4 -0
- package/x/view/common/sly-shadow.js +11 -0
- package/x/view/common/sly-shadow.js.map +1 -0
- package/x/view/elements/light.d.ts +357 -0
- package/x/view/elements/light.js +10 -0
- package/x/view/elements/light.js.map +1 -0
- package/x/view/elements/shadow.d.ts +366 -0
- package/x/view/elements/shadow.js +42 -0
- package/x/view/elements/shadow.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-attrs.d.ts +2 -0
- package/x/view/hooks/use-attrs.js +23 -0
- package/x/view/hooks/use-attrs.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 +19 -4
- package/x/view/index.js +19 -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/shadow.d.ts +6 -0
- package/x/view/shadow.js +71 -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/s/loaders/parts/error-display.ts +0 -26
- package/s/tests.test.ts +0 -8
- 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.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/loaders/index.barrel.js.map +0 -1
- package/x/loaders/index.js.map +0 -1
- package/x/loaders/make.js.map +0 -1
- package/x/loaders/mock.js.map +0 -1
- package/x/loaders/parts/anims.js.map +0 -1
- package/x/loaders/parts/ascii-anim.js.map +0 -1
- package/x/loaders/parts/error-display.d.ts +0 -1
- package/x/loaders/parts/error-display.js +0 -20
- package/x/loaders/parts/error-display.js.map +0 -1
- package/x/ops/index.js.map +0 -1
- package/x/ops/op.js.map +0 -1
- package/x/ops/podium.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/tests.test.js +0 -6
- package/x/tests.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/s/{loaders → loader}/mock.ts +0 -0
- /package/s/{loaders → loader}/parts/anims.ts +0 -0
- /package/s/{ops → op}/index.ts +0 -0
- /package/s/{ops → op}/op.ts +0 -0
- /package/s/{ops → op}/podium.ts +0 -0
- /package/s/{ops → op}/types.ts +0 -0
- /package/x/{loaders → loader}/index.barrel.js +0 -0
- /package/x/{loaders → loader}/index.js +0 -0
- /package/x/{loaders → loader}/make.js +0 -0
- /package/x/{loaders → loader}/mock.d.ts +0 -0
- /package/x/{loaders → loader}/mock.js +0 -0
- /package/x/{loaders → loader}/parts/anims.d.ts +0 -0
- /package/x/{loaders → loader}/parts/anims.js +0 -0
- /package/x/{loaders → loader}/types.js +0 -0
- /package/x/{ops → op}/index.d.ts +0 -0
- /package/x/{ops → op}/index.js +0 -0
- /package/x/{ops → op}/op.d.ts +0 -0
- /package/x/{ops → op}/op.js +0 -0
- /package/x/{ops → op}/podium.d.ts +0 -0
- /package/x/{ops → op}/podium.js +0 -0
- /package/x/{ops → op}/types.d.ts +0 -0
- /package/x/{ops → op}/types.js +0 -0
- /package/x/{tests.test.d.ts → test.d.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
|
@@ -2,236 +2,212 @@
|
|
|
2
2
|
<div align="center"><img alt="" width="256" src="./assets/favicon.png"/></div>
|
|
3
3
|
|
|
4
4
|
# 🦝 sly
|
|
5
|
-
> *mischievous shadow views*
|
|
6
|
-
|
|
7
|
-
[@e280](https://e280.org/)'s new [lit](https://lit.dev/)-based frontend webdev library.
|
|
8
|
-
|
|
9
|
-
- 🍋 [**#views**](#views) — shadow-dom'd, hooks-based, componentizable
|
|
10
|
-
- 🪵 [**#base-element**](#base-element) — for a more classical experience
|
|
11
|
-
- 🪄 [**#dom**](#dom) — the "it's not jquery" multitool
|
|
12
|
-
- 🫛 [**#ops**](#ops) — reactive tooling for async operations
|
|
13
|
-
- ⏳ [**#loaders**](#loaders) — animated loading spinners for rendering ops
|
|
14
|
-
- 💅 [**#spa**](#spa) — hash routing for your spa-day
|
|
15
|
-
- 🪙 [**#loot**](#loot) — drag-and-drop facilities
|
|
16
|
-
- 🧪 https://sly.e280.org/ — our testing page
|
|
17
|
-
- **✨[shiny](https://shiny.e280.org/)✨** — our wip component library
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<br/><br/>
|
|
22
|
-
|
|
23
|
-
## 🦝 sly and friends
|
|
24
|
-
> `@e280/sly`
|
|
25
5
|
|
|
26
6
|
```sh
|
|
27
|
-
npm install @e280/sly
|
|
7
|
+
npm install lit @e280/sly @e280/strata @e280/stz
|
|
28
8
|
```
|
|
29
9
|
|
|
30
|
-
|
|
31
|
-
> - 🔥 [lit](https://lit.dev/), for html rendering
|
|
32
|
-
> - ⛏️ [@e280/strata](https://github.com/e280/strata), for state management (signals, state trees)
|
|
33
|
-
> - 🏂 [@e280/stz](https://github.com/e280/stz), our ts standard library
|
|
34
|
-
> - 🐢 [@e280/scute](https://github.com/e280/scute), our buildy-bundly-buddy
|
|
10
|
+
#### [@e280](https://e280.org/)'s [lit-based](https://lit.dev/) web library for reactive light or shadow views
|
|
35
11
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
12
|
+
- 🎭 [**#views,**](#views) reactive lit views, light-dom or shadow-dom
|
|
13
|
+
- 🪝 [**#hooks,**](#hooks) react-like composable hooks
|
|
14
|
+
- 🫛 [**#ops,**](#ops) tooling for async operations ui
|
|
15
|
+
- ⏳ [**#loaders,**](#loaders) render ops with animated loading spinners
|
|
16
|
+
- 🪙 [**#loot,**](#loot) drag-and-drop facilities
|
|
17
|
+
- 🪄 [**#dom,**](#dom) the "it's not jquery" multitool
|
|
18
|
+
- 🧪 **https://sly.e280.org/** sly's testing page
|
|
39
19
|
|
|
40
20
|
|
|
41
21
|
|
|
42
22
|
<br/><br/>
|
|
43
23
|
<a id="views"></a>
|
|
44
24
|
|
|
45
|
-
##
|
|
46
|
-
>
|
|
47
|
-
|
|
25
|
+
## 🎭 views
|
|
26
|
+
> *reactive lit-html views*
|
|
27
|
+
|
|
28
|
+
- 🔮 [**see codepen demo,**](https://codepen.io/editor/ChaseMoskal/pen/019cd681-722b-7f51-a961-bc16e3d524a9) plain html (no build!)
|
|
29
|
+
- 🌗 **light or shadow,** render nakedly on the page, or within a cozy shadow bubble
|
|
30
|
+
- 🪝 **hooks-based,** familiar react-style [hooks](#hooks)
|
|
31
|
+
- ⚡ **auto-reactive,** views magically rerender on [strata](https://github.com/e280/strata)-compatible state changes
|
|
32
|
+
- 🪶 **no compile step,** just god's honest javascript via [lit](https://lit.dev/)-html tagged-templates
|
|
33
|
+
- 🧩 **not web components,** no dom registration needed, just vibes and good typings
|
|
48
34
|
|
|
49
35
|
```ts
|
|
50
|
-
|
|
51
|
-
|
|
36
|
+
import {html} from "lit"
|
|
37
|
+
import {light, shadow, dom} from "@e280/sly"
|
|
52
38
|
|
|
53
|
-
|
|
54
|
-
- 🥷 **shadow dom'd** — each view gets its own cozy [shadow](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM) bubble, and supports [slots](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots)
|
|
55
|
-
- 🪝 **hooks-based** — declarative rendering with the [`use`](#use) family of ergonomic hooks
|
|
56
|
-
- ⚡ **reactive** — they auto-rerender whenever any [strata](https://github.com/e280/strata)-compatible state changes
|
|
57
|
-
- 🧐 **not components, per se** — they're comfy typescript-native ui building blocks [(technically, lit directives)](https://lit.dev/docs/templates/custom-directives/)
|
|
58
|
-
- 🧩 **componentizable** — any view can be magically converted into a proper [web component](https://developer.mozilla.org/en-US/docs/Web/API/Web_components)
|
|
39
|
+
export const MyLightView = light(() => html`<p>blinded by the light</p>`)
|
|
59
40
|
|
|
60
|
-
|
|
61
|
-
```ts
|
|
62
|
-
import {view, dom, BaseElement} from "@e280/sly"
|
|
63
|
-
import {html, css} from "lit"
|
|
41
|
+
export const MyShadowView = shadow(() => html`<p>shrouded in darkness</p>`)
|
|
64
42
|
```
|
|
65
|
-
|
|
43
|
+
|
|
44
|
+
### 🌞 light views
|
|
45
|
+
> *just pretend it's like react!*
|
|
46
|
+
|
|
47
|
+
- **define a light view**
|
|
66
48
|
```ts
|
|
67
|
-
|
|
68
|
-
|
|
49
|
+
import {html} from "lit"
|
|
50
|
+
import {light, useSignal} from "@e280/sly"
|
|
69
51
|
|
|
70
|
-
|
|
52
|
+
export const MyCounter = light((start: number) => {
|
|
53
|
+
const $count = useSignal(start)
|
|
71
54
|
const increment = () => $count.value++
|
|
72
55
|
|
|
73
56
|
return html`
|
|
74
|
-
<button @click="${increment}">
|
|
75
|
-
${$count.value}
|
|
76
|
-
</button>
|
|
57
|
+
<button @click="${increment}">${$count.value}</button>
|
|
77
58
|
`
|
|
78
59
|
})
|
|
79
60
|
```
|
|
80
|
-
|
|
81
|
-
- **inject view into dom**
|
|
61
|
+
- **render it into the dom**
|
|
82
62
|
```ts
|
|
83
|
-
dom.
|
|
84
|
-
<h1>cool counter demo</h1>
|
|
85
|
-
${
|
|
63
|
+
dom.render(dom(".demo"), html`
|
|
64
|
+
<h1>my cool counter demo</h1>
|
|
65
|
+
${MyCounter(123)}
|
|
86
66
|
`)
|
|
87
67
|
```
|
|
88
|
-
-
|
|
68
|
+
- **remember, light views are naked.**
|
|
69
|
+
so they don't have a containing host element,
|
|
70
|
+
and they can't have their own styles.
|
|
71
|
+
|
|
72
|
+
### 🌚 shadow views
|
|
73
|
+
> *each shadow view gets its own cozy [shadow-dom](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM) bubble and supports [slotting](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots)*
|
|
74
|
+
|
|
75
|
+
- **define a shadow view**
|
|
89
76
|
```ts
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
77
|
+
import {css, html} from "lit"
|
|
78
|
+
import {shadow, useName, useCss, useSignal} from "@e280/sly"
|
|
79
|
+
|
|
80
|
+
export const MyShadowCounter = shadow((start: number) => {
|
|
81
|
+
useName("counter")
|
|
82
|
+
useCss(css`button { color: cyan }`)
|
|
83
|
+
|
|
84
|
+
const $count = useSignal(start)
|
|
85
|
+
const increment = () => $count.value++
|
|
86
|
+
|
|
87
|
+
return html`
|
|
88
|
+
<button @click="${increment}">${$count.value}</button>
|
|
89
|
+
<slot></slot>
|
|
90
|
+
`
|
|
94
91
|
})
|
|
95
92
|
```
|
|
96
|
-
|
|
97
|
-
|
|
93
|
+
- **render it into the dom**
|
|
94
|
+
```ts
|
|
95
|
+
dom.render(dom(".demo"), html`
|
|
96
|
+
<h1>my cool counter demo</h1>
|
|
97
|
+
${MyShadowCounter(234)}
|
|
98
|
+
`)
|
|
98
99
|
```
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
- shadow views have a host element, rendered output looks like:
|
|
101
|
+
```html
|
|
102
|
+
<h1>my cool counter demo</h1>
|
|
103
|
+
<sly-shadow view="counter"></sly-shadow>
|
|
104
|
+
```
|
|
105
|
+
- **.with to nest children or set attrs**
|
|
106
|
+
```ts
|
|
107
|
+
dom.render(dom(".demo"), html`
|
|
108
|
+
<h1>my cool counter demo</h1>
|
|
109
|
+
|
|
110
|
+
${MyShadowCounter.with({
|
|
111
|
+
props: [234],
|
|
112
|
+
attrs: {"data-whatever": 555},
|
|
113
|
+
children: html`
|
|
114
|
+
<p>woah, slotting support!</p>
|
|
115
|
+
`,
|
|
116
|
+
})}
|
|
117
|
+
`)
|
|
118
|
+
```
|
|
119
|
+
- **you can do custom shadow setup if needed** (default shown)
|
|
102
120
|
```ts
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
121
|
+
import {SlyShadow} from "@e280/sly"
|
|
122
|
+
|
|
123
|
+
const customShadow = shadow.setup(() => {
|
|
124
|
+
SlyShadow.register()
|
|
125
|
+
const host = document.createElement("sly-shadow")
|
|
126
|
+
const shadow = host.attachShadow({mode: "open"})
|
|
127
|
+
return {host, shadow}
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
const MyShadowView = customShadow(() => html`<p>shrouded in darkness</p>`)
|
|
106
131
|
```
|
|
107
|
-
- all [attachShadow params](https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow#parameters) (like `mode` and `delegatesFocus`) are valid `settings`
|
|
108
|
-
- note the `<slot></slot>` we'll use in the next example lol
|
|
109
132
|
|
|
110
|
-
###
|
|
111
|
-
-
|
|
133
|
+
### 🍨 web components
|
|
134
|
+
> *web-native custom elements*
|
|
135
|
+
|
|
136
|
+
- **they use hooks like the views, but they don't take props**
|
|
112
137
|
```ts
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
`)
|
|
138
|
+
import {html} from "lit"
|
|
139
|
+
import {lightElement, shadowElement} from "@e280/sly"
|
|
140
|
+
|
|
141
|
+
const MyLight = lightElement(() => html`hello`)
|
|
142
|
+
const MyShadow = shadowElement(() => html`hello`)
|
|
143
|
+
|
|
144
|
+
dom.register({MyLight, MyShadow})
|
|
121
145
|
```
|
|
122
|
-
|
|
123
|
-
-
|
|
124
|
-
-
|
|
125
|
-
|
|
146
|
+
```html
|
|
147
|
+
<my-light></my-light>
|
|
148
|
+
<my-shadow></my-shadow>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
<br/><br/>
|
|
154
|
+
<a id="hooks"></a>
|
|
155
|
+
|
|
156
|
+
## 🪝 hooks
|
|
157
|
+
> *composable view state and utilities*
|
|
126
158
|
|
|
127
|
-
###
|
|
128
|
-
|
|
159
|
+
### 👮 follow the hooks rules
|
|
160
|
+
|
|
161
|
+
just like [react hooks](https://react.dev/warnings/invalid-hook-call-warning), the execution order of hooks seriously matters.
|
|
162
|
+
|
|
163
|
+
you must not call these hooks under if-conditionals, or for-loops, or inside callback functions, or after a conditional return statement, or anything like that.. *otherwise, heed my warning: weird bad stuff will happen..*
|
|
164
|
+
|
|
165
|
+
### 🌚 shadow-only hooks
|
|
166
|
+
- **useName,** set the "view" attribute value
|
|
129
167
|
```ts
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
})
|
|
168
|
+
useName("squarepants")
|
|
169
|
+
// <sly-shadow view="squarepants">
|
|
133
170
|
```
|
|
134
|
-
|
|
135
|
-
```ts
|
|
136
|
-
GreeterView("pimsley")
|
|
137
|
-
```
|
|
138
|
-
**then you can convert it to a component.**
|
|
171
|
+
- **useCss,** attach stylesheets (use lit's `css`!) to the shadow root
|
|
139
172
|
```ts
|
|
140
|
-
|
|
141
|
-
GreeterView
|
|
142
|
-
.component()
|
|
143
|
-
.props(component => [component.getAttribute("name") ?? "unknown"])
|
|
144
|
-
) {}
|
|
173
|
+
useCss(css1, css2, css3)
|
|
145
174
|
```
|
|
146
|
-
|
|
147
|
-
```html
|
|
148
|
-
<greeter-component name="pimsley"></greeter-component>
|
|
149
|
-
```
|
|
150
|
-
- **you can start with a component,**
|
|
175
|
+
- **useHost,** get the host element
|
|
151
176
|
```ts
|
|
152
|
-
|
|
153
|
-
view(use => (name: string) => {
|
|
154
|
-
return html`<p>hello ${name}</p>`
|
|
155
|
-
})
|
|
156
|
-
.component()
|
|
157
|
-
.props(component => [component.getAttribute("name") ?? "unknown"])
|
|
158
|
-
) {}
|
|
177
|
+
const host = useHost()
|
|
159
178
|
```
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
})
|
|
179
|
+
- **useShadow,** get the shadow root
|
|
180
|
+
```ts
|
|
181
|
+
const shadow = useShadow()
|
|
215
182
|
```
|
|
183
|
+
- **useAttrs,** access host element attributes (and rerender on attr changes)
|
|
184
|
+
```ts
|
|
185
|
+
const attrs = useAttrs({
|
|
186
|
+
name: String,
|
|
187
|
+
count: Number,
|
|
188
|
+
active: Boolean,
|
|
189
|
+
})
|
|
216
190
|
|
|
217
|
-
|
|
191
|
+
attrs.count = 123 // set the attr
|
|
192
|
+
```
|
|
218
193
|
|
|
219
|
-
###
|
|
220
|
-
-
|
|
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">`
|
|
194
|
+
### 🌞 universal hooks
|
|
195
|
+
- **useState,** react-like hook to create some reactive state (we prefer signals)
|
|
224
196
|
```ts
|
|
225
|
-
|
|
197
|
+
const [count, setCount] = useState(0)
|
|
198
|
+
|
|
199
|
+
const increment = () => setCount(n => n + 1)
|
|
226
200
|
```
|
|
227
|
-
- **
|
|
201
|
+
- **useRef,** react-like hook to make a non-reactive box for a value
|
|
228
202
|
```ts
|
|
229
|
-
|
|
203
|
+
const ref = useRef(0)
|
|
204
|
+
|
|
205
|
+
ref.current // 0
|
|
206
|
+
ref.current = 1 // does not trigger rerender
|
|
230
207
|
```
|
|
231
|
-
|
|
232
|
-
- **use.signal** — create a [strata signal](https://github.com/e280/strata)
|
|
208
|
+
- **useSignal,** create a [strata](https://github.com/e280/strata) signal
|
|
233
209
|
```ts
|
|
234
|
-
const $count =
|
|
210
|
+
const $count = useSignal(1)
|
|
235
211
|
|
|
236
212
|
// read the signal
|
|
237
213
|
$count()
|
|
@@ -239,392 +215,96 @@ import {html, css} from "lit"
|
|
|
239
215
|
// write the signal
|
|
240
216
|
$count(2)
|
|
241
217
|
```
|
|
242
|
-
|
|
243
|
-
```ts
|
|
244
|
-
const $product = use.derived(() => $count() * $whatever())
|
|
245
|
-
```
|
|
246
|
-
- `lazy` signals
|
|
247
|
-
```ts
|
|
248
|
-
const $product = use.lazy(() => $count() * $whatever())
|
|
249
|
-
```
|
|
250
|
-
- go read the [strata readme](https://github.com/e280/strata) about this stuff
|
|
251
|
-
- **use.once** — run fn at initialization, and return a value
|
|
218
|
+
- **useDerived,** create a [strata](https://github.com/e280/strata) derived signal
|
|
252
219
|
```ts
|
|
253
|
-
const
|
|
254
|
-
|
|
220
|
+
const $product = useDerived(() => $count() * $whatever())
|
|
221
|
+
```
|
|
222
|
+
- **useOnce,** run fn at initialization, and return a value
|
|
223
|
+
```ts
|
|
224
|
+
const whatever = useOnce(() => {
|
|
225
|
+
console.log("happens one time")
|
|
255
226
|
return 123
|
|
256
227
|
})
|
|
257
228
|
|
|
258
229
|
whatever // 123
|
|
259
230
|
```
|
|
260
|
-
- **
|
|
231
|
+
- **useMount,** setup mount/unmount lifecycle
|
|
261
232
|
```ts
|
|
262
|
-
|
|
263
|
-
console.log("
|
|
264
|
-
|
|
265
|
-
return () => {
|
|
266
|
-
console.log("view unmounted")
|
|
267
|
-
}
|
|
233
|
+
useMount(() => {
|
|
234
|
+
console.log("mounted")
|
|
235
|
+
return () => console.log("unmounted")
|
|
268
236
|
})
|
|
269
237
|
```
|
|
270
|
-
- **
|
|
238
|
+
- **useWake,** run fn each time mounted, and return value
|
|
271
239
|
```ts
|
|
272
|
-
const whatever =
|
|
273
|
-
console.log("
|
|
240
|
+
const whatever = useWake(() => {
|
|
241
|
+
console.log("mounted")
|
|
274
242
|
return 123
|
|
275
243
|
})
|
|
276
244
|
|
|
277
245
|
whatever // 123
|
|
278
246
|
```
|
|
279
|
-
- **
|
|
247
|
+
- **useLife,** mount/unmount lifecycle, but also return a value
|
|
280
248
|
```ts
|
|
281
|
-
const
|
|
249
|
+
const whatever = useLife(() => {
|
|
282
250
|
console.log("mounted")
|
|
283
251
|
const value = 123
|
|
284
252
|
return [value, () => console.log("unmounted")]
|
|
285
253
|
})
|
|
286
254
|
|
|
287
|
-
|
|
288
|
-
```
|
|
289
|
-
- **use.events** — attach event listeners to the element (auto-cleaned up)
|
|
290
|
-
```ts
|
|
291
|
-
use.events({
|
|
292
|
-
keydown: (e: KeyboardEvent) => console.log("keydown", e.code),
|
|
293
|
-
keyup: (e: KeyboardEvent) => console.log("keyup", e.code),
|
|
294
|
-
})
|
|
295
|
-
```
|
|
296
|
-
- **use.states** — [internal states](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/states) helper
|
|
297
|
-
```ts
|
|
298
|
-
const states = use.states()
|
|
299
|
-
states.assign("active", "cool")
|
|
300
|
-
```
|
|
301
|
-
```css
|
|
302
|
-
[view="my-view"]::state(active) { color: yellow; }
|
|
303
|
-
[view="my-view"]::state(cool) { outline: 1px solid cyan; }
|
|
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()
|
|
255
|
+
whatever // 123
|
|
332
256
|
```
|
|
333
|
-
- **
|
|
257
|
+
- **useRender,** returns a fn to rerender the view (debounced)
|
|
334
258
|
```ts
|
|
335
|
-
|
|
259
|
+
const render = useRender()
|
|
260
|
+
|
|
261
|
+
render().then(() => console.log("render done"))
|
|
336
262
|
```
|
|
337
|
-
- **
|
|
263
|
+
- **useRendered,** get a promise that resolves *after* the next render
|
|
338
264
|
```ts
|
|
339
|
-
|
|
340
|
-
const slot = use.shadow.querySelector("slot")
|
|
341
|
-
console.log(slot)
|
|
342
|
-
})
|
|
265
|
+
useRendered().then(() => console.log("rendered"))
|
|
343
266
|
```
|
|
344
|
-
- **
|
|
267
|
+
- **useOp,** start loading an op based on an async fn
|
|
345
268
|
```ts
|
|
346
|
-
const op =
|
|
269
|
+
const op = useOp(async() => {
|
|
347
270
|
await nap(5000)
|
|
348
271
|
return 123
|
|
349
272
|
})
|
|
350
273
|
```
|
|
351
|
-
- **
|
|
274
|
+
- **useOpPromise,** start loading an op based on a promise
|
|
352
275
|
```ts
|
|
353
|
-
const op =
|
|
276
|
+
const op = useOpPromise(doAsyncWork())
|
|
354
277
|
```
|
|
355
278
|
|
|
356
|
-
###
|
|
357
|
-
- make a ticker
|
|
279
|
+
### 🧑🍳 happy hooks recipes
|
|
280
|
+
- make a ticker, mount, cycle, and nap
|
|
358
281
|
```ts
|
|
359
282
|
import {cycle, nap} from "@e280/stz"
|
|
360
283
|
```
|
|
361
284
|
```ts
|
|
362
|
-
const $seconds =
|
|
285
|
+
const $seconds = useSignal(0)
|
|
363
286
|
|
|
364
|
-
|
|
287
|
+
useMount(() => cycle(async() => {
|
|
365
288
|
await nap(1000)
|
|
366
289
|
$seconds.value++
|
|
367
290
|
}))
|
|
368
291
|
```
|
|
369
292
|
- wake + rendered, to do something after each mount's first render
|
|
370
293
|
```ts
|
|
371
|
-
|
|
294
|
+
const rendered = useRendered()
|
|
295
|
+
|
|
296
|
+
useWake(() => rendered.then(() => {
|
|
372
297
|
console.log("after first render")
|
|
373
298
|
}))
|
|
374
299
|
```
|
|
375
300
|
|
|
376
301
|
|
|
377
302
|
|
|
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
303
|
<br/><br/>
|
|
623
304
|
<a id="ops"></a>
|
|
624
305
|
|
|
625
|
-
##
|
|
626
|
-
>
|
|
627
|
-
> *tools for async operations and loading spinners*
|
|
306
|
+
## 🫛 ops
|
|
307
|
+
> *helpers for async operations*
|
|
628
308
|
|
|
629
309
|
```ts
|
|
630
310
|
import {nap} from "@e280/stz"
|
|
@@ -659,22 +339,22 @@ import {Pod, podium, Op, loaders} from "@e280/sly"
|
|
|
659
339
|
podium.value(["ready", 123])
|
|
660
340
|
// 123
|
|
661
341
|
```
|
|
662
|
-
- see more at [podium.ts](./s/
|
|
342
|
+
- see more at [podium.ts](./s/op/podium.ts)
|
|
663
343
|
|
|
664
344
|
### 🫛 ops: nice pod ergonomics
|
|
665
|
-
- an `Op<V>` wraps a pod with a signal for reactivity
|
|
345
|
+
- an `Op<V>` wraps a pod with a strata signal for reactivity
|
|
666
346
|
- create an op
|
|
667
347
|
```ts
|
|
668
|
-
|
|
348
|
+
new Op<number>() // loading status by default
|
|
669
349
|
```
|
|
670
350
|
```ts
|
|
671
|
-
|
|
351
|
+
Op.loading<number>()
|
|
672
352
|
```
|
|
673
353
|
```ts
|
|
674
|
-
|
|
354
|
+
Op.ready<number>(123)
|
|
675
355
|
```
|
|
676
356
|
```ts
|
|
677
|
-
|
|
357
|
+
Op.error<number>(new Error())
|
|
678
358
|
```
|
|
679
359
|
- 🔥 create an op that calls and tracks an async fn
|
|
680
360
|
```ts
|
|
@@ -701,13 +381,12 @@ import {Pod, podium, Op, loaders} from "@e280/sly"
|
|
|
701
381
|
- select executes a fn based on the status
|
|
702
382
|
```ts
|
|
703
383
|
const result = op.select({
|
|
704
|
-
loading: () => "
|
|
384
|
+
loading: () => "still loading...",
|
|
705
385
|
ready: value => `dude, it's ready! ${value}`,
|
|
706
|
-
error: err => `
|
|
386
|
+
error: err => `ack! an error!`,
|
|
707
387
|
})
|
|
708
388
|
|
|
709
|
-
result
|
|
710
|
-
// "dude, it's ready! 123"
|
|
389
|
+
result // "dude, it's ready! 123"
|
|
711
390
|
```
|
|
712
391
|
- morph returns a new pod, transforming the value if ready
|
|
713
392
|
```ts
|
|
@@ -732,9 +411,8 @@ import {Pod, podium, Op, loaders} from "@e280/sly"
|
|
|
732
411
|
<br/><br/>
|
|
733
412
|
<a id="loaders"></a>
|
|
734
413
|
|
|
735
|
-
##
|
|
736
|
-
>
|
|
737
|
-
> *animated loading spinners for ops*
|
|
414
|
+
## ⏳ loaders
|
|
415
|
+
> *animated loading spinners for ops*
|
|
738
416
|
|
|
739
417
|
```ts
|
|
740
418
|
import {loaders} from "@e280/sly"
|
|
@@ -765,100 +443,10 @@ import {loaders} from "@e280/sly"
|
|
|
765
443
|
|
|
766
444
|
|
|
767
445
|
|
|
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
446
|
<br/><br/>
|
|
858
447
|
<a id="loot"></a>
|
|
859
448
|
|
|
860
|
-
##
|
|
861
|
-
> `@e280/sly/loot`
|
|
449
|
+
## 🪙 loot
|
|
862
450
|
> *drag-and-drop facilities*
|
|
863
451
|
|
|
864
452
|
```ts
|
|
@@ -881,7 +469,7 @@ import {ev} from "@e280/stz"
|
|
|
881
469
|
- **attach event listeners to your dropzone,** one of these ways:
|
|
882
470
|
- **view example**
|
|
883
471
|
```ts
|
|
884
|
-
|
|
472
|
+
light(() => html`
|
|
885
473
|
<div
|
|
886
474
|
?data-indicator="${drops.$indicator()}"
|
|
887
475
|
@dragover="${drops.dragover}"
|
|
@@ -937,9 +525,9 @@ import {ev} from "@e280/stz"
|
|
|
937
525
|
```
|
|
938
526
|
- **attach dragzone listeners** (there can be many dragzones...)
|
|
939
527
|
```ts
|
|
940
|
-
|
|
941
|
-
const money =
|
|
942
|
-
const dragzone =
|
|
528
|
+
light(() => {
|
|
529
|
+
const money = useOnce((): Money => ({value: 280}))
|
|
530
|
+
const dragzone = useOnce(() => dnd.dragzone(() => money))
|
|
943
531
|
|
|
944
532
|
return html`
|
|
945
533
|
<div
|
|
@@ -953,9 +541,9 @@ import {ev} from "@e280/stz"
|
|
|
953
541
|
```
|
|
954
542
|
- **attach dropzone listeners** (there can be many dropzones...)
|
|
955
543
|
```ts
|
|
956
|
-
|
|
957
|
-
const bag =
|
|
958
|
-
const dropzone =
|
|
544
|
+
light(() => {
|
|
545
|
+
const bag = useOnce((): Bag => ({id: 1}))
|
|
546
|
+
const dropzone = useOnce(() => dnd.dropzone(() => bag))
|
|
959
547
|
const indicator = !!(dnd.dragging && dnd.hovering === bag)
|
|
960
548
|
|
|
961
549
|
return html`
|
|
@@ -978,13 +566,155 @@ import {ev} from "@e280/stz"
|
|
|
978
566
|
|
|
979
567
|
|
|
980
568
|
<br/><br/>
|
|
981
|
-
<a id="
|
|
569
|
+
<a id="dom"></a>
|
|
982
570
|
|
|
983
|
-
##
|
|
984
|
-
|
|
985
|
-
|
|
571
|
+
## 🪄 dom
|
|
572
|
+
> *the "it's not jquery!" multitool*
|
|
573
|
+
|
|
574
|
+
```ts
|
|
575
|
+
import {dom} from "@e280/sly"
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
### 🪄 dom queries
|
|
579
|
+
- `require` an element
|
|
580
|
+
```ts
|
|
581
|
+
dom(".demo")
|
|
582
|
+
// HTMLElement (or throws)
|
|
583
|
+
```
|
|
584
|
+
```ts
|
|
585
|
+
// alias
|
|
586
|
+
dom.require(".demo")
|
|
587
|
+
// HTMLElement (or throws)
|
|
588
|
+
```
|
|
589
|
+
- `maybe` get an element
|
|
590
|
+
```ts
|
|
591
|
+
dom.maybe(".demo")
|
|
592
|
+
// HTMLElement | undefined
|
|
593
|
+
```
|
|
594
|
+
- `all` matching elements in an array
|
|
595
|
+
```ts
|
|
596
|
+
dom.all(".demo ul li")
|
|
597
|
+
// HTMLElement[]
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
### 🪄 dom.in scope
|
|
601
|
+
- make a scope
|
|
602
|
+
```ts
|
|
603
|
+
dom.in(".demo") // selector
|
|
604
|
+
// Dom instance
|
|
605
|
+
```
|
|
606
|
+
```ts
|
|
607
|
+
dom.in(demoElement) // element
|
|
608
|
+
// Dom instance
|
|
609
|
+
```
|
|
610
|
+
- run queries in that scope
|
|
611
|
+
```ts
|
|
612
|
+
dom.in(demoElement).require(".button")
|
|
613
|
+
```
|
|
614
|
+
```ts
|
|
615
|
+
dom.in(demoElement).maybe(".button")
|
|
616
|
+
```
|
|
617
|
+
```ts
|
|
618
|
+
dom.in(demoElement).all("ol li")
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
### 🪄 dom utilities
|
|
622
|
+
- `dom.register` web components
|
|
623
|
+
```ts
|
|
624
|
+
dom.register({MyComponent, AnotherCoolComponent})
|
|
625
|
+
// <my-component>
|
|
626
|
+
// <another-cool-component>
|
|
627
|
+
```
|
|
628
|
+
- `dom.register` automatically dashes the tag names (`MyComponent` becomes `<my-component>`)
|
|
629
|
+
- `dom.render` content into an element
|
|
630
|
+
```ts
|
|
631
|
+
dom.render(element, html`<p>hello world</p>`)
|
|
632
|
+
```
|
|
633
|
+
```ts
|
|
634
|
+
dom.in(".demo").render(html`<p>hello world</p>`)
|
|
635
|
+
```
|
|
636
|
+
- `dom.el` little element builder
|
|
637
|
+
```ts
|
|
638
|
+
const div = dom.el("div", {"data-whatever": 123, "data-active": true})
|
|
639
|
+
// <div data-whatever="123" data-active></div>
|
|
640
|
+
```
|
|
641
|
+
- `dom.elmer` make an element with a fluent chain
|
|
642
|
+
```ts
|
|
643
|
+
const div = dom.elmer("div")
|
|
644
|
+
.attr("data-whatever", 123)
|
|
645
|
+
.attr("data-active")
|
|
646
|
+
.children("hello world")
|
|
647
|
+
.done()
|
|
648
|
+
// HTMLElement
|
|
649
|
+
```
|
|
650
|
+
- `dom.mk` make an element with a lit template (returns the first)
|
|
651
|
+
```ts
|
|
652
|
+
const div = dom.mk(html`
|
|
653
|
+
<div data-whatever="123" data-active>
|
|
654
|
+
hello world
|
|
655
|
+
</div>
|
|
656
|
+
`) // HTMLElement
|
|
657
|
+
```
|
|
658
|
+
- `dom.events` <a id="dom.events"></a> to attach event listeners
|
|
659
|
+
```ts
|
|
660
|
+
const detach = dom.events(element, {
|
|
661
|
+
keydown: (e: KeyboardEvent) => console.log("keydown", e.code),
|
|
662
|
+
keyup: (e: KeyboardEvent) => console.log("keyup", e.code),
|
|
663
|
+
})
|
|
664
|
+
```
|
|
665
|
+
```ts
|
|
666
|
+
const detach = dom.in(".demo").events({
|
|
667
|
+
keydown: (e: KeyboardEvent) => console.log("keydown", e.code),
|
|
668
|
+
keyup: (e: KeyboardEvent) => console.log("keyup", e.code),
|
|
669
|
+
})
|
|
670
|
+
```
|
|
671
|
+
```ts
|
|
672
|
+
// unattach those event listeners when you're done
|
|
673
|
+
detach()
|
|
674
|
+
```
|
|
675
|
+
- `dom.attrs` <a id="dom.attrs"></a> to setup a type-happy html attribute helper
|
|
676
|
+
```ts
|
|
677
|
+
const attrs = dom.attrs(element).spec({
|
|
678
|
+
name: String,
|
|
679
|
+
count: Number,
|
|
680
|
+
active: Boolean,
|
|
681
|
+
})
|
|
682
|
+
```
|
|
683
|
+
```ts
|
|
684
|
+
const attrs = dom.in(".demo").attrs.spec({
|
|
685
|
+
name: String,
|
|
686
|
+
count: Number,
|
|
687
|
+
active: Boolean,
|
|
688
|
+
})
|
|
689
|
+
```
|
|
690
|
+
```ts
|
|
691
|
+
attrs.name // "chase"
|
|
692
|
+
attrs.count // 123
|
|
693
|
+
attrs.active // true
|
|
694
|
+
```
|
|
695
|
+
```ts
|
|
696
|
+
attrs.name = "zenky"
|
|
697
|
+
attrs.count = 124
|
|
698
|
+
attrs.active = false // removes html attr
|
|
699
|
+
```
|
|
700
|
+
```ts
|
|
701
|
+
attrs.name = undefined // removes the attr
|
|
702
|
+
attrs.count = undefined // removes the attr
|
|
703
|
+
```
|
|
704
|
+
or if you wanna be more loosey-goosey, skip the spec
|
|
705
|
+
```ts
|
|
706
|
+
const {attrs} = dom.in(".demo")
|
|
707
|
+
attrs.strings.name = "pimsley"
|
|
708
|
+
attrs.numbers.count = 125
|
|
709
|
+
attrs.booleans.active = true
|
|
710
|
+
```
|
|
986
711
|
|
|
987
712
|
|
|
988
713
|
|
|
989
714
|
<br/><br/>
|
|
715
|
+
<a id="e280"></a>
|
|
716
|
+
|
|
717
|
+
## 🧑💻 sly is by e280
|
|
718
|
+
reward us with github stars
|
|
719
|
+
build with us at https://e280.org/ but only if you're cool
|
|
990
720
|
|