@e280/sly 0.2.0-2 → 0.2.0-21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +552 -98
- package/package.json +12 -5
- package/s/base/element.ts +76 -0
- package/s/base/index.ts +5 -0
- package/s/{views → base}/use.ts +17 -18
- package/s/base/utils/attr-watcher.ts +22 -0
- package/s/base/utils/reactor.ts +32 -0
- package/s/base/utils/use-attrs.ts +27 -0
- package/s/demo/demo.bundle.ts +9 -5
- package/s/demo/views/counter.ts +21 -24
- package/s/demo/views/demo.ts +10 -6
- package/s/demo/views/fastcount.ts +29 -0
- package/s/demo/views/loaders.ts +7 -7
- package/s/dom/attrs/attrs.ts +21 -0
- package/s/dom/attrs/parts/attr-fns.ts +38 -0
- package/s/dom/attrs/parts/attr-proxies.ts +35 -0
- package/s/dom/attrs/parts/attr-spec.ts +29 -0
- package/s/dom/attrs/parts/on-attrs.ts +8 -0
- package/s/dom/dom.ts +46 -16
- package/s/dom/index.ts +4 -0
- package/s/dom/parts/el.ts +14 -0
- package/s/dom/parts/eve.ts +24 -0
- package/s/dom/{register.ts → parts/register.ts} +2 -7
- package/s/dom/types.ts +39 -2
- package/s/index.html.ts +4 -2
- package/s/index.ts +7 -18
- package/s/loaders/index.barrel.ts +9 -0
- package/s/loaders/index.ts +3 -0
- package/s/loaders/make.ts +14 -0
- package/s/loaders/mock.ts +11 -0
- package/s/{ops/loaders → loaders}/parts/anims.ts +1 -1
- package/s/{ops/loaders → loaders}/parts/ascii-anim.ts +4 -3
- package/s/{ops/loaders → loaders}/parts/error-display.ts +2 -2
- package/s/loaders/types.ts +6 -0
- package/s/loot/drag-and-drops.ts +82 -0
- package/s/loot/{drop.ts → drops.ts} +8 -17
- package/s/loot/helpers.ts +3 -3
- package/s/loot/index.barrel.ts +5 -0
- package/s/loot/index.ts +1 -3
- package/s/ops/index.ts +5 -0
- package/s/ops/op.ts +3 -2
- package/s/spa/index.barrel.ts +6 -0
- package/s/spa/index.ts +3 -0
- package/s/spa/plumbing/braces.ts +76 -0
- package/s/spa/plumbing/primitives.ts +85 -0
- package/s/spa/plumbing/router-core.ts +49 -0
- package/s/spa/plumbing/types.ts +45 -0
- package/s/spa/router.ts +49 -0
- package/s/spa/spa.test.ts +91 -0
- package/s/tests.test.ts +4 -1
- package/s/view/index.ts +6 -0
- package/s/view/types.ts +40 -0
- package/s/view/utils/make-component.ts +34 -0
- package/s/view/utils/make-view.ts +48 -0
- package/s/view/utils/parts/capsule.ts +67 -0
- package/s/view/utils/parts/chain.ts +33 -0
- package/s/view/utils/parts/context.ts +10 -0
- package/s/view/utils/parts/directive.ts +29 -0
- package/s/view/utils/parts/set-attrs.ts +33 -0
- package/s/view/utils/parts/sly-view.ts +15 -0
- package/s/view/view.ts +24 -0
- package/x/base/css-reset.js.map +1 -0
- package/x/base/element.d.ts +19 -0
- package/x/base/element.js +52 -0
- package/x/base/element.js.map +1 -0
- package/x/base/index.d.ts +3 -0
- package/x/base/index.js +4 -0
- package/x/base/index.js.map +1 -0
- package/x/{views → base}/use.d.ts +5 -5
- package/x/{views → base}/use.js +9 -11
- package/x/base/use.js.map +1 -0
- package/x/base/utils/apply-styles.js.map +1 -0
- package/x/base/utils/attr-watcher.d.ts +8 -0
- package/x/base/utils/attr-watcher.js +20 -0
- package/x/base/utils/attr-watcher.js.map +1 -0
- package/x/base/utils/mounts.js.map +1 -0
- package/x/base/utils/reactor.d.ts +5 -0
- package/x/base/utils/reactor.js +25 -0
- package/x/base/utils/reactor.js.map +1 -0
- package/x/base/utils/use-attrs.d.ts +11 -0
- package/x/base/utils/use-attrs.js +19 -0
- package/x/base/utils/use-attrs.js.map +1 -0
- package/x/demo/demo.bundle.js +8 -4
- package/x/demo/demo.bundle.js.map +1 -1
- package/x/demo/demo.bundle.min.js +19 -22
- package/x/demo/demo.bundle.min.js.map +4 -4
- package/x/demo/views/counter.d.ts +374 -1
- package/x/demo/views/counter.js +19 -22
- package/x/demo/views/counter.js.map +1 -1
- package/x/demo/views/demo.d.ts +4 -1
- package/x/demo/views/demo.js +10 -5
- package/x/demo/views/demo.js.map +1 -1
- package/x/demo/views/fastcount.d.ts +12 -0
- package/x/demo/views/fastcount.js +21 -0
- package/x/demo/views/fastcount.js.map +1 -0
- package/x/demo/views/loaders.js +6 -6
- package/x/demo/views/loaders.js.map +1 -1
- package/x/dom/attrs/attrs.d.ts +20 -0
- package/x/dom/attrs/attrs.js +17 -0
- package/x/dom/attrs/attrs.js.map +1 -0
- package/x/dom/attrs/parts/attr-fns.d.ts +13 -0
- package/x/dom/attrs/parts/attr-fns.js +42 -0
- package/x/dom/attrs/parts/attr-fns.js.map +1 -0
- package/x/dom/attrs/parts/attr-proxies.d.ts +8 -0
- package/x/dom/attrs/parts/attr-proxies.js +21 -0
- package/x/dom/attrs/parts/attr-proxies.js.map +1 -0
- package/x/dom/attrs/parts/attr-spec.d.ts +3 -0
- package/x/dom/attrs/parts/attr-spec.js +21 -0
- package/x/dom/attrs/parts/attr-spec.js.map +1 -0
- package/x/dom/attrs/parts/on-attrs.d.ts +2 -0
- package/x/dom/attrs/parts/on-attrs.js +7 -0
- package/x/dom/attrs/parts/on-attrs.js.map +1 -0
- package/x/dom/dom.d.ts +22 -7
- package/x/dom/dom.js +32 -12
- package/x/dom/dom.js.map +1 -1
- package/x/dom/index.d.ts +2 -0
- package/x/dom/index.js +3 -0
- package/x/dom/index.js.map +1 -0
- package/x/dom/parts/dashify.js.map +1 -0
- package/x/dom/parts/el.d.ts +2 -0
- package/x/dom/parts/el.js +7 -0
- package/x/dom/parts/el.js.map +1 -0
- package/x/dom/parts/eve.d.ts +7 -0
- package/x/dom/parts/eve.js +16 -0
- package/x/dom/parts/eve.js.map +1 -0
- package/x/dom/{register.d.ts → parts/register.d.ts} +2 -6
- package/x/dom/parts/register.js.map +1 -0
- package/x/dom/types.d.ts +14 -2
- package/x/index.d.ts +7 -15
- package/x/index.html +6 -4
- package/x/index.html.js +4 -2
- package/x/index.html.js.map +1 -1
- package/x/index.js +7 -15
- package/x/index.js.map +1 -1
- package/x/loaders/index.barrel.d.ts +6 -0
- package/x/loaders/index.barrel.js +7 -0
- package/x/loaders/index.barrel.js.map +1 -0
- package/x/loaders/index.d.ts +1 -0
- package/x/loaders/index.js +2 -0
- package/x/loaders/index.js.map +1 -0
- package/x/loaders/make.d.ts +3 -0
- package/x/loaders/make.js +6 -0
- package/x/loaders/make.js.map +1 -0
- package/x/loaders/mock.d.ts +2 -0
- package/x/loaders/mock.js +8 -0
- package/x/loaders/mock.js.map +1 -0
- package/x/{ops/loaders → loaders}/parts/anims.d.ts +1 -1
- package/x/loaders/parts/anims.js.map +1 -0
- package/x/{ops/loaders → loaders}/parts/ascii-anim.d.ts +2 -2
- package/x/{ops/loaders → loaders}/parts/ascii-anim.js +2 -2
- package/x/loaders/parts/ascii-anim.js.map +1 -0
- package/x/loaders/parts/error-display.d.ts +1 -0
- package/x/{ops/loaders → loaders}/parts/error-display.js +2 -2
- package/x/loaders/parts/error-display.js.map +1 -0
- package/x/loaders/types.d.ts +3 -0
- package/x/loaders/types.js.map +1 -0
- package/x/loot/drag-and-drops.d.ts +30 -0
- package/x/loot/drag-and-drops.js +63 -0
- package/x/loot/drag-and-drops.js.map +1 -0
- package/x/loot/{drop.d.ts → drops.d.ts} +3 -5
- package/x/loot/drops.js +25 -0
- package/x/loot/drops.js.map +1 -0
- package/x/loot/helpers.d.ts +3 -3
- package/x/loot/helpers.js +3 -3
- package/x/loot/helpers.js.map +1 -1
- package/x/loot/index.barrel.d.ts +3 -0
- package/x/loot/index.barrel.js +4 -0
- package/x/loot/index.barrel.js.map +1 -0
- package/x/loot/index.d.ts +1 -3
- package/x/loot/index.js +1 -3
- package/x/loot/index.js.map +1 -1
- package/x/ops/index.d.ts +3 -0
- package/x/ops/index.js +4 -0
- package/x/ops/index.js.map +1 -0
- package/x/ops/op.d.ts +2 -2
- package/x/ops/op.js +3 -2
- package/x/ops/op.js.map +1 -1
- package/x/spa/index.barrel.d.ts +4 -0
- package/x/spa/index.barrel.js +3 -0
- package/x/spa/index.barrel.js.map +1 -0
- package/x/spa/index.d.ts +1 -0
- package/x/spa/index.js +2 -0
- package/x/spa/index.js.map +1 -0
- package/x/spa/plumbing/braces.d.ts +12 -0
- package/x/spa/plumbing/braces.js +55 -0
- package/x/spa/plumbing/braces.js.map +1 -0
- package/x/spa/plumbing/primitives.d.ts +22 -0
- package/x/spa/plumbing/primitives.js +65 -0
- package/x/spa/plumbing/primitives.js.map +1 -0
- package/x/spa/plumbing/router-core.d.ts +13 -0
- package/x/spa/plumbing/router-core.js +38 -0
- package/x/spa/plumbing/router-core.js.map +1 -0
- package/x/spa/plumbing/types.d.ts +35 -0
- package/x/spa/plumbing/types.js +2 -0
- package/x/spa/plumbing/types.js.map +1 -0
- package/x/spa/router.d.ts +16 -0
- package/x/spa/router.js +39 -0
- package/x/spa/router.js.map +1 -0
- package/x/spa/spa.test.d.ts +15 -0
- package/x/spa/spa.test.js +78 -0
- package/x/spa/spa.test.js.map +1 -0
- package/x/tests.test.js +4 -1
- package/x/tests.test.js.map +1 -1
- package/x/view/index.d.ts +4 -0
- package/x/view/index.js +5 -0
- package/x/view/index.js.map +1 -0
- package/x/view/types.d.ts +22 -0
- package/x/view/types.js +2 -0
- package/x/{views → view}/types.js.map +1 -1
- package/x/view/utils/make-component.d.ts +5 -0
- package/x/view/utils/make-component.js +17 -0
- package/x/view/utils/make-component.js.map +1 -0
- package/x/view/utils/make-view.d.ts +2 -0
- package/x/view/utils/make-view.js +24 -0
- package/x/view/utils/make-view.js.map +1 -0
- package/x/view/utils/parts/capsule.d.ts +13 -0
- package/x/view/utils/parts/capsule.js +49 -0
- package/x/view/utils/parts/capsule.js.map +1 -0
- package/x/view/utils/parts/chain.d.ts +11 -0
- package/x/view/utils/parts/chain.js +21 -0
- package/x/view/utils/parts/chain.js.map +1 -0
- package/x/view/utils/parts/context.d.ts +8 -0
- package/x/view/utils/parts/context.js +10 -0
- package/x/view/utils/parts/context.js.map +1 -0
- package/x/view/utils/parts/directive.d.ts +5 -0
- package/x/view/utils/parts/directive.js +18 -0
- package/x/view/utils/parts/directive.js.map +1 -0
- package/x/view/utils/parts/set-attrs.d.ts +3 -0
- package/x/view/utils/parts/set-attrs.js +21 -0
- package/x/view/utils/parts/set-attrs.js.map +1 -0
- package/x/view/utils/parts/sly-view.d.ts +5 -0
- package/x/view/utils/parts/sly-view.js +13 -0
- package/x/view/utils/parts/sly-view.js.map +1 -0
- package/x/view/view.d.ts +11 -0
- package/x/view/view.js +15 -0
- package/x/view/view.js.map +1 -0
- package/s/loot/drag-drop.ts +0 -76
- package/s/ops/loaders/make-loader.ts +0 -18
- package/s/views/attributes.ts +0 -89
- package/s/views/types.ts +0 -40
- package/s/views/utils/apply-attrs.ts +0 -33
- package/s/views/view.ts +0 -150
- package/x/dom/dashify.js.map +0 -1
- package/x/dom/register.js.map +0 -1
- package/x/loot/drag-drop.d.ts +0 -29
- package/x/loot/drag-drop.js +0 -54
- package/x/loot/drag-drop.js.map +0 -1
- package/x/loot/drop.js +0 -32
- package/x/loot/drop.js.map +0 -1
- package/x/ops/loaders/make-loader.d.ts +0 -5
- package/x/ops/loaders/make-loader.js +0 -7
- package/x/ops/loaders/make-loader.js.map +0 -1
- package/x/ops/loaders/parts/anims.js.map +0 -1
- package/x/ops/loaders/parts/ascii-anim.js.map +0 -1
- package/x/ops/loaders/parts/error-display.d.ts +0 -1
- package/x/ops/loaders/parts/error-display.js.map +0 -1
- package/x/views/attributes.d.ts +0 -10
- package/x/views/attributes.js +0 -46
- package/x/views/attributes.js.map +0 -1
- package/x/views/css-reset.js.map +0 -1
- package/x/views/types.d.ts +0 -31
- package/x/views/use.js.map +0 -1
- package/x/views/utils/apply-attrs.d.ts +0 -2
- package/x/views/utils/apply-attrs.js +0 -21
- package/x/views/utils/apply-attrs.js.map +0 -1
- package/x/views/utils/apply-styles.js.map +0 -1
- package/x/views/utils/mounts.js.map +0 -1
- package/x/views/view.d.ts +0 -9
- package/x/views/view.js +0 -116
- package/x/views/view.js.map +0 -1
- /package/s/{views → base}/css-reset.ts +0 -0
- /package/s/{views → base}/utils/apply-styles.ts +0 -0
- /package/s/{views → base}/utils/mounts.ts +0 -0
- /package/s/dom/{dashify.ts → parts/dashify.ts} +0 -0
- /package/x/{views → base}/css-reset.d.ts +0 -0
- /package/x/{views → base}/css-reset.js +0 -0
- /package/x/{views → base}/utils/apply-styles.d.ts +0 -0
- /package/x/{views → base}/utils/apply-styles.js +0 -0
- /package/x/{views → base}/utils/mounts.d.ts +0 -0
- /package/x/{views → base}/utils/mounts.js +0 -0
- /package/x/dom/{dashify.d.ts → parts/dashify.d.ts} +0 -0
- /package/x/dom/{dashify.js → parts/dashify.js} +0 -0
- /package/x/dom/{register.js → parts/register.js} +0 -0
- /package/x/{ops/loaders → loaders}/parts/anims.js +0 -0
- /package/x/{views → loaders}/types.js +0 -0
package/README.md
CHANGED
|
@@ -4,99 +4,115 @@
|
|
|
4
4
|
# 🦝 sly
|
|
5
5
|
> *mischievous shadow views*
|
|
6
6
|
|
|
7
|
-
[@e280](https://e280.org/)'s shiny
|
|
8
|
-
sly replaces its predecessor, [slate](https://github.com/benevolent-games/slate).
|
|
7
|
+
[@e280](https://e280.org/)'s shiny new [lit](https://lit.dev/)-based frontend webdev library. *(sly replaces its predecessor, [slate](https://github.com/benevolent-games/slate))*
|
|
9
8
|
|
|
10
|
-
- 🍋 **views
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
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
|
+
- 🧪 testing page — https://sly.e280.org/
|
|
14
17
|
|
|
15
18
|
|
|
16
19
|
|
|
17
20
|
<br/><br/>
|
|
18
21
|
|
|
19
22
|
## 🦝 sly and friends
|
|
23
|
+
> `@e280/sly`
|
|
20
24
|
|
|
21
25
|
```sh
|
|
22
|
-
npm install @e280/sly lit
|
|
26
|
+
npm install @e280/sly lit @e280/strata @e280/stz
|
|
23
27
|
```
|
|
24
28
|
|
|
25
29
|
> [!NOTE]
|
|
26
|
-
> - 🔥 [lit](https://lit.dev/) for html rendering
|
|
30
|
+
> - 🔥 [lit](https://lit.dev/), for html rendering
|
|
27
31
|
> - ⛏️ [@e280/strata](https://github.com/e280/strata), for state management (signals, state trees)
|
|
28
|
-
> - 🏂 [@e280/stz](https://github.com/e280/stz)
|
|
29
|
-
> - 🐢 [scute](https://github.com/e280/scute)
|
|
32
|
+
> - 🏂 [@e280/stz](https://github.com/e280/stz), our ts standard library
|
|
33
|
+
> - 🐢 [@e280/scute](https://github.com/e280/scute), our buildy-bundly-buddy
|
|
34
|
+
|
|
35
|
+
> [!TIP]
|
|
36
|
+
> you can import everything in sly from `@e280/sly`,
|
|
37
|
+
> or from specific subpackages like `@e280/sly/view`, `@e280/sly/dom`, etc...
|
|
30
38
|
|
|
31
39
|
|
|
32
40
|
|
|
33
41
|
<br/><br/>
|
|
42
|
+
<a id="views"></a>
|
|
34
43
|
|
|
35
|
-
##
|
|
36
|
-
>
|
|
44
|
+
## 🍋🦝 sly views
|
|
45
|
+
> `@e280/sly/view`
|
|
46
|
+
> *the crown jewel of sly*
|
|
37
47
|
|
|
38
48
|
```ts
|
|
39
49
|
view(use => () => html`<p>hello world</p>`)
|
|
40
50
|
```
|
|
41
51
|
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
52
|
+
- 🪶 **no compile step** — just god's honest javascript, via [lit](https://lit.dev/)-html tagged-template-literals
|
|
53
|
+
- 🥷 **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)
|
|
54
|
+
- 🪝 **hooks-based** — declarative rendering with the [`use`](#use) family of ergonomic hooks
|
|
55
|
+
- ⚡ **reactive** — they auto-rerender whenever any [strata](https://github.com/e280/strata)-compatible state changes
|
|
56
|
+
- 🧐 **not components, per se** — they're comfy typescript-native ui building blocks [(technically, lit directives)](https://lit.dev/docs/templates/custom-directives/)
|
|
57
|
+
- 🧩 **componentizable** — any view can be magically converted into a proper [web component](https://developer.mozilla.org/en-US/docs/Web/API/Web_components)
|
|
46
58
|
|
|
47
59
|
### 🍋 view example
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
- **declare a view**
|
|
60
|
+
```ts
|
|
61
|
+
import {view, dom, BaseElement} from "@e280/sly"
|
|
62
|
+
import {html, css} from "lit"
|
|
63
|
+
```
|
|
64
|
+
- **declare view**
|
|
54
65
|
```ts
|
|
55
66
|
export const CounterView = view(use => (start: number) => {
|
|
56
|
-
use.name("counter")
|
|
57
67
|
use.styles(css`p {color: green}`)
|
|
58
68
|
|
|
59
69
|
const $count = use.signal(start)
|
|
60
70
|
const increment = () => $count.value++
|
|
61
71
|
|
|
62
72
|
return html`
|
|
63
|
-
<
|
|
64
|
-
|
|
73
|
+
<button @click="${increment}">
|
|
74
|
+
${$count.value}
|
|
75
|
+
</button>
|
|
65
76
|
`
|
|
66
77
|
})
|
|
67
78
|
```
|
|
68
|
-
-
|
|
69
|
-
- **inject
|
|
79
|
+
- `$count` is a [strata signal](https://github.com/e280/strata#readme) *(we like those)*
|
|
80
|
+
- **inject view into dom**
|
|
70
81
|
```ts
|
|
71
82
|
dom.in(".app").render(html`
|
|
72
83
|
<h1>cool counter demo</h1>
|
|
73
84
|
${CounterView(1)}
|
|
74
85
|
`)
|
|
75
86
|
```
|
|
76
|
-
- 🤯 **register
|
|
87
|
+
- 🤯 **register view as web component**
|
|
77
88
|
```ts
|
|
78
|
-
dom.register({
|
|
79
|
-
|
|
89
|
+
dom.register({
|
|
90
|
+
MyCounter: CounterView
|
|
91
|
+
.component()
|
|
92
|
+
.props(() => [1]),
|
|
93
|
+
})
|
|
94
|
+
```
|
|
95
|
+
```html
|
|
96
|
+
<my-counter></my-counter>
|
|
80
97
|
```
|
|
81
98
|
|
|
82
|
-
### 🍋 view
|
|
83
|
-
-
|
|
99
|
+
### 🍋 view settings
|
|
100
|
+
- optional settings for views you should know about
|
|
84
101
|
```ts
|
|
85
102
|
export const CoolView = view
|
|
86
103
|
.settings({mode: "open", delegatesFocus: true})
|
|
87
|
-
.
|
|
88
|
-
return html`😎 ${greeting} <slot></slot>`
|
|
89
|
-
})
|
|
104
|
+
.render(use => (greeting: string) => html`😎 ${greeting} <slot></slot>`)
|
|
90
105
|
```
|
|
91
106
|
- all [attachShadow params](https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow#parameters) (like `mode` and `delegatesFocus`) are valid `settings`
|
|
92
107
|
- note the `<slot></slot>` we'll use in the next example lol
|
|
93
108
|
|
|
94
|
-
### 🍋 view
|
|
95
|
-
-
|
|
109
|
+
### 🍋 view chains
|
|
110
|
+
- views have this sick chaining syntax for supplying more stuff at the template injection site
|
|
96
111
|
```ts
|
|
97
112
|
dom.in(".app").render(html`
|
|
98
113
|
<h2>cool example</h2>
|
|
99
|
-
${CoolView
|
|
114
|
+
${CoolView
|
|
115
|
+
.props("hello")
|
|
100
116
|
.attr("class", "hero")
|
|
101
117
|
.children(html`<em>spongebob</em>`)
|
|
102
118
|
.render()}
|
|
@@ -104,30 +120,102 @@ view(use => () => html`<p>hello world</p>`)
|
|
|
104
120
|
```
|
|
105
121
|
- `props` — provide props and start a view chain
|
|
106
122
|
- `attr` — set html attributes on the `<sly-view>` host element
|
|
107
|
-
- `children` — nested
|
|
123
|
+
- `children` — add nested [slottable](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots) content
|
|
108
124
|
- `render` — end the view chain and render the lit directive
|
|
109
125
|
|
|
110
|
-
### 🍋 view
|
|
111
|
-
- **
|
|
126
|
+
### 🍋 view/component universality
|
|
127
|
+
- **you can start with a view,**
|
|
128
|
+
```ts
|
|
129
|
+
export const GreeterView = view(use => (name: string) => {
|
|
130
|
+
return html`<p>hello ${name}</p>`
|
|
131
|
+
})
|
|
132
|
+
```
|
|
133
|
+
- view usage
|
|
134
|
+
```ts
|
|
135
|
+
GreeterView("pimsley")
|
|
136
|
+
```
|
|
137
|
+
**then you can convert it to a component.**
|
|
112
138
|
```ts
|
|
113
|
-
|
|
139
|
+
export class GreeterComponent extends (
|
|
140
|
+
GreeterView
|
|
141
|
+
.component()
|
|
142
|
+
.props(component => [component.getAttribute("name") ?? "unknown"])
|
|
143
|
+
) {}
|
|
114
144
|
```
|
|
115
|
-
-
|
|
116
|
-
|
|
145
|
+
- html usage
|
|
146
|
+
```html
|
|
147
|
+
<greeter-component name="pimsley"></greeter-component>
|
|
148
|
+
```
|
|
149
|
+
- **you can start with a component,**
|
|
117
150
|
```ts
|
|
118
|
-
|
|
151
|
+
export class GreeterComponent extends (
|
|
152
|
+
view(use => (name: string) => {
|
|
153
|
+
return html`<p>hello ${name}</p>`
|
|
154
|
+
})
|
|
155
|
+
.component()
|
|
156
|
+
.props(component => [component.getAttribute("name") ?? "unknown"])
|
|
157
|
+
) {}
|
|
119
158
|
```
|
|
120
|
-
-
|
|
121
|
-
|
|
159
|
+
- html usage
|
|
160
|
+
```html
|
|
161
|
+
<greeter-component name="pimsley"></greeter-component>
|
|
162
|
+
```
|
|
163
|
+
**and it already has `.view` ready for you.**
|
|
164
|
+
- view usage
|
|
165
|
+
```ts
|
|
166
|
+
GreeterComponent.view("pimsley")
|
|
167
|
+
```
|
|
168
|
+
- **understanding `.component(BaseElement)` and `.props(fn)`**
|
|
169
|
+
- `.props` takes a fn that is called every render, which returns the props given to the view
|
|
170
|
+
```ts
|
|
171
|
+
.props(() => ["pimsley"])
|
|
172
|
+
```
|
|
173
|
+
the props fn receives the component instance, so you can query html attributes or instance properties
|
|
174
|
+
```ts
|
|
175
|
+
.props(component => [component.getAttribute("name") ?? "unknown"])
|
|
176
|
+
```
|
|
177
|
+
- `.component` accepts a subclass of `BaseElement`, so you can define your own properties and methods for your component class
|
|
178
|
+
```ts
|
|
179
|
+
const GreeterComponent = GreeterView
|
|
180
|
+
|
|
181
|
+
// declare your own custom class
|
|
182
|
+
.component(class extends BaseElement {
|
|
183
|
+
$name = signal("jim raynor")
|
|
184
|
+
updateName(name: string) {
|
|
185
|
+
this.$name.value = name
|
|
186
|
+
}
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
// props gets the right types on 'component'
|
|
190
|
+
.props(component => [component.$name.value])
|
|
191
|
+
```
|
|
192
|
+
- `.component` provides the devs interacting with your component, with noice typings
|
|
193
|
+
```ts
|
|
194
|
+
dom<GreeterComponent>("greeter-component").updateName("mortimer")
|
|
195
|
+
```
|
|
196
|
+
- typescript class wizardry
|
|
197
|
+
- ❌ smol-brain approach exports class value, but NOT the typings
|
|
198
|
+
```ts
|
|
199
|
+
export const GreeterComponent = (...)
|
|
200
|
+
```
|
|
201
|
+
- ✅ giga-brain approach exports class value AND the typings
|
|
202
|
+
```ts
|
|
203
|
+
export class GreeterComponent extends (...) {}
|
|
204
|
+
```
|
|
122
205
|
- **register web components to the dom**
|
|
123
206
|
```ts
|
|
124
|
-
dom.register({
|
|
125
|
-
|
|
126
|
-
|
|
207
|
+
dom.register({GreeterComponent})
|
|
208
|
+
```
|
|
209
|
+
- **oh and don't miss out on the insta-component shorthand**
|
|
210
|
+
```ts
|
|
211
|
+
dom.register({
|
|
212
|
+
QuickComponent: view.component(use => html`⚡ incredi`),
|
|
213
|
+
})
|
|
127
214
|
```
|
|
128
|
-
- `dom.register` automatically dashes the tag names (`MyComponent` becomes `<my-component>`)
|
|
129
215
|
|
|
130
|
-
|
|
216
|
+
<a id="use"></a>
|
|
217
|
+
|
|
218
|
+
### 🍋 "use" hooks reference
|
|
131
219
|
- 👮 **follow the hooks rules**
|
|
132
220
|
> just like [react hooks](https://react.dev/warnings/invalid-hook-call-warning), the execution order of sly's `use` hooks actually matters..
|
|
133
221
|
> 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..*
|
|
@@ -150,9 +238,9 @@ view(use => () => html`<p>hello world</p>`)
|
|
|
150
238
|
// write the signal
|
|
151
239
|
$count(2)
|
|
152
240
|
```
|
|
153
|
-
- `
|
|
241
|
+
- `derived` signals
|
|
154
242
|
```ts
|
|
155
|
-
const $product = use.
|
|
243
|
+
const $product = use.derived(() => $count() * $whatever())
|
|
156
244
|
```
|
|
157
245
|
- `lazy` signals
|
|
158
246
|
```ts
|
|
@@ -197,27 +285,31 @@ view(use => () => html`<p>hello world</p>`)
|
|
|
197
285
|
|
|
198
286
|
v // 123
|
|
199
287
|
```
|
|
200
|
-
- **use.attrs** — ergonomic typed html attribute access
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
attrs.
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
288
|
+
- **use.attrs** — ergonomic typed html attribute access
|
|
289
|
+
- `use.attrs` is a bit different than `dom.attrs`, just to properly memoize .spec and .on for the render fn
|
|
290
|
+
- use.attrs.spec
|
|
291
|
+
```ts
|
|
292
|
+
const attrs = use.attrs.spec({
|
|
293
|
+
name: String,
|
|
294
|
+
count: Number,
|
|
295
|
+
active: Boolean,
|
|
296
|
+
})
|
|
297
|
+
```
|
|
298
|
+
```ts
|
|
299
|
+
attrs.name // "chase"
|
|
300
|
+
attrs.count // 123
|
|
301
|
+
attrs.active // true
|
|
302
|
+
```
|
|
303
|
+
- use.attrs.(strings/numbers/booleans)
|
|
304
|
+
```ts
|
|
305
|
+
use.attrs.strings.name // "chase"
|
|
306
|
+
use.attrs.numbers.count // 123
|
|
307
|
+
use.attrs.booleans.active // true
|
|
308
|
+
```
|
|
309
|
+
- use.attrs.on
|
|
310
|
+
```ts
|
|
311
|
+
use.attrs.on(() => console.log("an attribute changed"))
|
|
312
|
+
```
|
|
221
313
|
- **use.render** — rerender the view (debounced)
|
|
222
314
|
```ts
|
|
223
315
|
use.render()
|
|
@@ -245,7 +337,7 @@ view(use => () => html`<p>hello world</p>`)
|
|
|
245
337
|
const op = use.op.promise(doAsyncWork())
|
|
246
338
|
```
|
|
247
339
|
|
|
248
|
-
### 🍋
|
|
340
|
+
### 🍋 "use" recipes
|
|
249
341
|
- make a ticker — mount, repeat, and nap
|
|
250
342
|
```ts
|
|
251
343
|
import {repeat, nap} from "@e280/stz"
|
|
@@ -268,72 +360,202 @@ view(use => () => html`<p>hello world</p>`)
|
|
|
268
360
|
|
|
269
361
|
|
|
270
362
|
<br/><br/>
|
|
363
|
+
<a id="base-element"></a>
|
|
364
|
+
|
|
365
|
+
## 🪵🦝 sly base element
|
|
366
|
+
> `@e280/sly/base`
|
|
367
|
+
> *the classic experience*
|
|
368
|
+
|
|
369
|
+
```ts
|
|
370
|
+
import {BaseElement, Use, dom} from "@e280/sly"
|
|
371
|
+
import {html, css} from "lit"
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
`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.
|
|
375
|
+
|
|
376
|
+
👮 a *BaseElement* is not a *View*, and cannot be converted into a *View*.
|
|
377
|
+
|
|
378
|
+
### 🪵 let's clarify some sly terminology
|
|
379
|
+
- "Element"
|
|
380
|
+
- an html element; any subclass of the browser's HTMLElement
|
|
381
|
+
- all genuine ["web components"](https://developer.mozilla.org/en-US/docs/Web/API/Web_components) are elements
|
|
382
|
+
- "BaseElement"
|
|
383
|
+
- sly's own subclass of the browser-native HTMLElement
|
|
384
|
+
- is a true element and web component (can be registered to the dom)
|
|
385
|
+
- "View"
|
|
386
|
+
- sly's own magic concept that uses a lit-directive to render stuff
|
|
387
|
+
- NOT an element or web component (can NOT be registered to the dom)
|
|
388
|
+
- NOT related to BaseElement
|
|
389
|
+
- can be converted into a Component via `view.component().props(() => [])`
|
|
390
|
+
- "Component"
|
|
391
|
+
- a sly view that has been converted into an element
|
|
392
|
+
- is a true element and web component (can be registered to the dom)
|
|
393
|
+
- actually a subclass of BaseElement
|
|
394
|
+
- actually contains the view on `Component.view`
|
|
395
|
+
|
|
396
|
+
### 🪵 base element setup
|
|
397
|
+
- **declare your element class**
|
|
398
|
+
```ts
|
|
399
|
+
export class MyElement extends BaseElement {
|
|
400
|
+
static styles = css`span{color:orange}`
|
|
401
|
+
|
|
402
|
+
// custom property
|
|
403
|
+
$start = signal(10)
|
|
404
|
+
|
|
405
|
+
// custom attributes
|
|
406
|
+
attrs = dom.attrs(this).spec({
|
|
407
|
+
multiply: Number,
|
|
408
|
+
})
|
|
409
|
+
|
|
410
|
+
// custom methods
|
|
411
|
+
hello() {
|
|
412
|
+
return "world"
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
render(use: Use) {
|
|
416
|
+
const $count = use.signal(1)
|
|
417
|
+
const increment = () => $count.value++
|
|
418
|
+
|
|
419
|
+
const {$start} = this
|
|
420
|
+
const {multiply = 1} = this.attrs
|
|
421
|
+
const result = $start() + (multiply * $count())
|
|
422
|
+
|
|
423
|
+
return html`
|
|
424
|
+
<span>${result}</span>
|
|
425
|
+
<button @click="${increment}">+</button>
|
|
426
|
+
`
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
```
|
|
430
|
+
- **register your element to the dom**
|
|
431
|
+
```ts
|
|
432
|
+
dom.register({MyElement})
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### 🪵 base element usage
|
|
436
|
+
- **place the element in your html body**
|
|
437
|
+
```html
|
|
438
|
+
<body>
|
|
439
|
+
<my-element></my-element>
|
|
440
|
+
</body>
|
|
441
|
+
```
|
|
442
|
+
- **now you can interact with it**
|
|
443
|
+
```ts
|
|
444
|
+
const myElement = dom<MyElement>("my-element")
|
|
445
|
+
|
|
446
|
+
// js property
|
|
447
|
+
myElement.$start(100)
|
|
448
|
+
|
|
449
|
+
// html attributes
|
|
450
|
+
myElement.attrs.multiply = 2
|
|
451
|
+
|
|
452
|
+
// methods
|
|
453
|
+
myElement.hello()
|
|
454
|
+
// "world"
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
<br/><br/>
|
|
460
|
+
<a id="dom"></a>
|
|
271
461
|
|
|
272
|
-
##
|
|
273
|
-
>
|
|
462
|
+
## 🪄🦝 sly dom
|
|
463
|
+
> `@e280/sly/dom`
|
|
464
|
+
> *the "it's not jquery!" multitool*
|
|
274
465
|
|
|
275
466
|
```ts
|
|
276
467
|
import {dom} from "@e280/sly"
|
|
277
468
|
```
|
|
278
469
|
|
|
279
470
|
### 🪄 dom queries
|
|
280
|
-
- require an element
|
|
471
|
+
- `require` an element
|
|
281
472
|
```ts
|
|
282
473
|
dom(".demo")
|
|
283
474
|
// HTMLElement (or throws)
|
|
284
475
|
```
|
|
285
|
-
- maybe get an element
|
|
476
|
+
- `maybe` get an element
|
|
286
477
|
```ts
|
|
287
478
|
dom.maybe(".demo")
|
|
288
479
|
// HTMLElement | undefined
|
|
289
480
|
```
|
|
290
|
-
-
|
|
481
|
+
- `all` matching elements in an array
|
|
291
482
|
```ts
|
|
292
483
|
dom.all(".demo ul li")
|
|
293
484
|
// HTMLElement[]
|
|
294
485
|
```
|
|
295
|
-
-
|
|
486
|
+
- scoped to an element
|
|
296
487
|
```ts
|
|
297
|
-
dom
|
|
488
|
+
dom(element).require("li")
|
|
298
489
|
// HTMLElement (or throws)
|
|
299
490
|
```
|
|
300
491
|
```ts
|
|
301
|
-
dom
|
|
492
|
+
dom(element).maybe("li")
|
|
302
493
|
// HTMLElement | undefined
|
|
303
494
|
```
|
|
304
495
|
```ts
|
|
305
|
-
dom
|
|
496
|
+
dom(element).all("li")
|
|
306
497
|
// HTMLElement[]
|
|
307
498
|
```
|
|
308
499
|
|
|
309
500
|
### 🪄 dom utilities
|
|
310
|
-
- register web components
|
|
501
|
+
- `register` web components
|
|
311
502
|
```ts
|
|
312
503
|
dom.register({MyComponent, AnotherCoolComponent})
|
|
313
504
|
// <my-component>
|
|
314
505
|
// <another-cool-component>
|
|
315
506
|
```
|
|
316
|
-
-
|
|
507
|
+
- `dom.register` automatically dashes the tag names (`MyComponent` becomes `<my-component>`)
|
|
508
|
+
- `render` content into an element
|
|
509
|
+
```ts
|
|
510
|
+
dom(element).render(html`<p>hello world</p>`)
|
|
511
|
+
```
|
|
512
|
+
```ts
|
|
513
|
+
dom.in(".demo").render(html`<p>hello world</p>`)
|
|
514
|
+
```
|
|
317
515
|
```ts
|
|
318
516
|
dom.render(element, html`<p>hello world</p>`)
|
|
319
517
|
```
|
|
518
|
+
- `attrs` <a id="dom.attrs"></a> to setup a type-happy html attribute helper
|
|
320
519
|
```ts
|
|
321
|
-
dom.
|
|
520
|
+
const attrs = dom.attrs(element).spec({
|
|
521
|
+
name: String,
|
|
522
|
+
count: Number,
|
|
523
|
+
active: Boolean,
|
|
524
|
+
})
|
|
322
525
|
```
|
|
323
526
|
```ts
|
|
324
|
-
|
|
527
|
+
attrs.name // "chase"
|
|
528
|
+
attrs.count // 123
|
|
529
|
+
attrs.active // true
|
|
530
|
+
```
|
|
531
|
+
```ts
|
|
532
|
+
attrs.name = "zenky"
|
|
533
|
+
attrs.count = 124
|
|
534
|
+
attrs.active = false // removes html attr
|
|
535
|
+
```
|
|
536
|
+
```ts
|
|
537
|
+
attrs.name = undefined // removes the attr
|
|
538
|
+
attrs.count = undefined // removes the attr
|
|
539
|
+
```
|
|
540
|
+
or if you wanna be more loosey-goosy, skip the spec
|
|
541
|
+
```ts
|
|
542
|
+
dom.attrs(element).strings.name = "pimsley"
|
|
543
|
+
dom.attrs(element).numbers.count = 125
|
|
544
|
+
dom.attrs(element).booleans.active = true
|
|
325
545
|
```
|
|
326
546
|
|
|
327
547
|
|
|
328
548
|
|
|
329
549
|
<br/><br/>
|
|
550
|
+
<a id="ops"></a>
|
|
330
551
|
|
|
331
|
-
##
|
|
332
|
-
>
|
|
552
|
+
## 🫛🦝 sly ops
|
|
553
|
+
> `@e280/sly/ops`
|
|
554
|
+
> *tools for async operations and loading spinners*
|
|
333
555
|
|
|
334
556
|
```ts
|
|
335
557
|
import {nap} from "@e280/stz"
|
|
336
|
-
import {Pod, podium, Op,
|
|
558
|
+
import {Pod, podium, Op, loaders} from "@e280/sly"
|
|
337
559
|
```
|
|
338
560
|
|
|
339
561
|
### 🫛 pods: loading/ready/error
|
|
@@ -383,7 +605,7 @@ import {Pod, podium, Op, makeLoader, anims} from "@e280/sly"
|
|
|
383
605
|
```
|
|
384
606
|
- 🔥 create an op that calls and tracks an async fn
|
|
385
607
|
```ts
|
|
386
|
-
const op = Op.
|
|
608
|
+
const op = Op.load(async() => {
|
|
387
609
|
await nap(4000)
|
|
388
610
|
return 123
|
|
389
611
|
})
|
|
@@ -432,14 +654,29 @@ import {Pod, podium, Op, makeLoader, anims} from "@e280/sly"
|
|
|
432
654
|
- loading if any ops are in loading, otherwise
|
|
433
655
|
- ready if all the ops are ready
|
|
434
656
|
|
|
435
|
-
|
|
436
|
-
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
<br/><br/>
|
|
660
|
+
<a id="loaders"></a>
|
|
661
|
+
|
|
662
|
+
## ⏳🦝 sly loaders
|
|
663
|
+
> `@e280/sly/loaders`
|
|
664
|
+
> *animated loading spinners for ops*
|
|
665
|
+
|
|
666
|
+
```ts
|
|
667
|
+
import {loaders} from "@e280/sly"
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
### ⏳ make a loader, choose an anim
|
|
671
|
+
- create a loader fn
|
|
437
672
|
```ts
|
|
438
|
-
const loader =
|
|
673
|
+
const loader = loaders.make(loaders.anims.dots)
|
|
439
674
|
```
|
|
440
675
|
- see all the anims available on the testing page https://sly.e280.org/
|
|
441
676
|
- ngl, i made too many.. *i was having fun, okay?*
|
|
442
|
-
|
|
677
|
+
|
|
678
|
+
### ⏳ render an op with it
|
|
679
|
+
- use your loader to render an op
|
|
443
680
|
```ts
|
|
444
681
|
return html`
|
|
445
682
|
<h2>cool stuff</h2>
|
|
@@ -456,8 +693,225 @@ import {Pod, podium, Op, makeLoader, anims} from "@e280/sly"
|
|
|
456
693
|
|
|
457
694
|
|
|
458
695
|
<br/><br/>
|
|
696
|
+
<a id="spa"></a>
|
|
697
|
+
|
|
698
|
+
## 💅🦝 sly spa
|
|
699
|
+
> `@e280/sly/spa`
|
|
700
|
+
> *hash router for single-page-apps*
|
|
701
|
+
|
|
702
|
+
```ts
|
|
703
|
+
import {spa, html} from "@e280/sly"
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
### 💅 spa.Router basics
|
|
707
|
+
- **make a spa router**
|
|
708
|
+
```ts
|
|
709
|
+
const router = new spa.Router({
|
|
710
|
+
routes: {
|
|
711
|
+
home: spa.route("#/", async() => html`home`),
|
|
712
|
+
settings: spa.route("#/settings", async() => html`settings`),
|
|
713
|
+
user: spa.route("#/user/{userId}", async({userId}) => html`user ${userId}`),
|
|
714
|
+
},
|
|
715
|
+
})
|
|
716
|
+
```
|
|
717
|
+
- all route strings must start with `#/`
|
|
718
|
+
- use braces like `{userId}` to accept string params
|
|
719
|
+
- home-equivalent hashes like `""` and `"#"` are normalized to `"#/"`
|
|
720
|
+
- 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*
|
|
721
|
+
- you can provide `loader` option if you want to specify the loading spinner (defaults to `loaders.make()`)
|
|
722
|
+
- you can provide `notFound` option, if you want to specify what is shown on invalid routes (defaults to `() => null`)
|
|
723
|
+
- when `auto` is true (default), the router calls `.refresh()` and `.listen()` in the constructor.. set it to `false` if you want manual control
|
|
724
|
+
- you can set `auto` option false if you want to omit the default initial refresh and listen calls
|
|
725
|
+
- **render your current page**
|
|
726
|
+
```ts
|
|
727
|
+
return html`
|
|
728
|
+
<div class="my-page">
|
|
729
|
+
${router.render()}
|
|
730
|
+
</div>
|
|
731
|
+
`
|
|
732
|
+
```
|
|
733
|
+
- returns lit content
|
|
734
|
+
- shows a loading spinner when pages are loading
|
|
735
|
+
- will display the notFound content for invalid routes (defaults to null)
|
|
736
|
+
- **perform navigations**
|
|
737
|
+
- go to settings page
|
|
738
|
+
```ts
|
|
739
|
+
await router.nav.settings.go()
|
|
740
|
+
// goes to "#/settings"
|
|
741
|
+
```
|
|
742
|
+
- go to user page
|
|
743
|
+
```ts
|
|
744
|
+
await router.nav.user.go("123")
|
|
745
|
+
// goes to "#/user/123"
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
### 💅 spa.Router advanced
|
|
749
|
+
- **generate a route's hash string**
|
|
750
|
+
```ts
|
|
751
|
+
const hash = router.nav.user.hash("123")
|
|
752
|
+
// "#/user/123"
|
|
753
|
+
|
|
754
|
+
html`<a href="${hash}">user 123</a>`
|
|
755
|
+
```
|
|
756
|
+
- **check if a route is the currently-active one**
|
|
757
|
+
```ts
|
|
758
|
+
const hash = router.nav.user.active
|
|
759
|
+
// true
|
|
760
|
+
```
|
|
761
|
+
- **force-refresh the router**
|
|
762
|
+
```ts
|
|
763
|
+
await router.refresh()
|
|
764
|
+
```
|
|
765
|
+
- **force-navigate the router by hash**
|
|
766
|
+
```ts
|
|
767
|
+
await router.refresh("#/user/123")
|
|
768
|
+
```
|
|
769
|
+
- **get the current hash string (normalized)**
|
|
770
|
+
```ts
|
|
771
|
+
router.hash
|
|
772
|
+
// "#/user/123"
|
|
773
|
+
```
|
|
774
|
+
- **the `route(...)` helper fn enables the braces-params syntax**
|
|
775
|
+
- but, if you wanna do it differently, you *can* implement your own hash parser to do your own funky syntax
|
|
776
|
+
- **dispose the router when you're done with it**
|
|
777
|
+
```ts
|
|
778
|
+
router.dispose()
|
|
779
|
+
// stop listening to hashchange events
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
|
|
783
|
+
|
|
784
|
+
<br/><br/>
|
|
785
|
+
<a id="loot"></a>
|
|
786
|
+
|
|
787
|
+
## 🪙🦝 loot
|
|
788
|
+
> `@e280/sly/loot`
|
|
789
|
+
> *drag-and-drop facilities*
|
|
790
|
+
|
|
791
|
+
```ts
|
|
792
|
+
import {loot, view, dom} from "@e280/sly"
|
|
793
|
+
import {ev} from "@e280/stz"
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
### 🪙 `loot.Drops`
|
|
797
|
+
> *accept the user dropping stuff like files onto the page*
|
|
798
|
+
- **setup drops**
|
|
799
|
+
```ts
|
|
800
|
+
const drops = new loot.Drops({
|
|
801
|
+
predicate: loot.hasFiles,
|
|
802
|
+
acceptDrop: event => {
|
|
803
|
+
const files = loot.files(event)
|
|
804
|
+
console.log("files dropped", files)
|
|
805
|
+
},
|
|
806
|
+
})
|
|
807
|
+
```
|
|
808
|
+
- **attach event listeners to your dropzone,** one of these ways:
|
|
809
|
+
- **view example**
|
|
810
|
+
```ts
|
|
811
|
+
view(() => () => html`
|
|
812
|
+
<div
|
|
813
|
+
?data-indicator="${drops.$indicator()}"
|
|
814
|
+
@dragover="${drops.dragover}"
|
|
815
|
+
@dragleave="${drops.dragleave}"
|
|
816
|
+
@drop="${drops.drop}">
|
|
817
|
+
my dropzone
|
|
818
|
+
</div>
|
|
819
|
+
`)
|
|
820
|
+
```
|
|
821
|
+
- **vanilla-js whole-page example**
|
|
822
|
+
```ts
|
|
823
|
+
// attach listeners to the body
|
|
824
|
+
ev(document.body, {
|
|
825
|
+
dragover: drops.dragover,
|
|
826
|
+
dragleave: drops.dragleave,
|
|
827
|
+
drop: drops.drop,
|
|
828
|
+
})
|
|
829
|
+
|
|
830
|
+
// sly attribute handler for the body
|
|
831
|
+
const attrs = dom.attrs(document.body).spec({
|
|
832
|
+
"data-indicator": Boolean,
|
|
833
|
+
})
|
|
834
|
+
|
|
835
|
+
// sync the data-indicator attribute
|
|
836
|
+
drops.$indicator.on(bool => attrs["data-indicator"] = bool)
|
|
837
|
+
```
|
|
838
|
+
- **flashy css indicator for the dropzone,** so the user knows your app is eager to accept the drop
|
|
839
|
+
```css
|
|
840
|
+
[data-indicator] {
|
|
841
|
+
border: 0.5em dashed cyan;
|
|
842
|
+
}
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
### 🪙 `loot.DragAndDrops`
|
|
846
|
+
> *setup drag-and-drops between items within your page*
|
|
847
|
+
- **declare types for your draggy and droppy things**
|
|
848
|
+
```ts
|
|
849
|
+
// money that can be picked up and dragged
|
|
850
|
+
type Money = {value: number}
|
|
851
|
+
// dnd will call this a "draggy"
|
|
852
|
+
|
|
853
|
+
// bag that money can be dropped into
|
|
854
|
+
type Bag = {id: number}
|
|
855
|
+
// dnd will call this a "droppy"
|
|
856
|
+
```
|
|
857
|
+
- **make your dnd**
|
|
858
|
+
```ts
|
|
859
|
+
const dnd = new loot.DragAndDrops<Money, Bag>({
|
|
860
|
+
acceptDrop: (event, money, bag) => {
|
|
861
|
+
console.log("drop!", {money, bag})
|
|
862
|
+
},
|
|
863
|
+
})
|
|
864
|
+
```
|
|
865
|
+
- **attach dragzone listeners** (there can be many dragzones...)
|
|
866
|
+
```ts
|
|
867
|
+
view(use => () => {
|
|
868
|
+
const money = use.once((): Money => ({value: 280}))
|
|
869
|
+
const dragzone = use.once(() => dnd.dragzone(() => money))
|
|
870
|
+
|
|
871
|
+
return html`
|
|
872
|
+
<div
|
|
873
|
+
draggable="${dragzone.draggable}"
|
|
874
|
+
@dragstart="${dragzone.dragstart}"
|
|
875
|
+
@dragend="${dragzone.dragend}">
|
|
876
|
+
money ${money.value}
|
|
877
|
+
</div>
|
|
878
|
+
`
|
|
879
|
+
})
|
|
880
|
+
```
|
|
881
|
+
- **attach dropzone listeners** (there can be many dropzones...)
|
|
882
|
+
```ts
|
|
883
|
+
view(use => () => {
|
|
884
|
+
const bag = use.once((): Bag => ({id: 1}))
|
|
885
|
+
const dropzone = use.once(() => dnd.dropzone(() => bag))
|
|
886
|
+
const indicator = !!(dnd.dragging && dnd.hovering === bag)
|
|
887
|
+
|
|
888
|
+
return html`
|
|
889
|
+
<div
|
|
890
|
+
?data-indicator="${indicator}"
|
|
891
|
+
@dragenter="${dropzone.dragenter}"
|
|
892
|
+
@dragleave="${dropzone.dragleave}"
|
|
893
|
+
@dragover="${dropzone.dragover}"
|
|
894
|
+
@drop="${dropzone.drop}">
|
|
895
|
+
bag ${bag.id}
|
|
896
|
+
</div>
|
|
897
|
+
`
|
|
898
|
+
})
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
### 🪙 loot helpers
|
|
902
|
+
- **`loot.hasFiles(event)`** — return true if `DragEvent` contains any files (useful in `predicate`)
|
|
903
|
+
- **`loot.files(event)`** — returns an array of files in a drop's `DragEvent` (useful in `acceptDrop`)
|
|
904
|
+
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
<br/><br/>
|
|
908
|
+
<a id="e280"></a>
|
|
459
909
|
|
|
460
|
-
##
|
|
910
|
+
## 🧑💻🦝 sly is by e280
|
|
461
911
|
reward us with github stars
|
|
462
912
|
build with us at https://e280.org/ but only if you're cool
|
|
463
913
|
|
|
914
|
+
|
|
915
|
+
|
|
916
|
+
<br/><br/>
|
|
917
|
+
|