@e280/sly 0.2.0-0 → 0.2.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/README.md +414 -97
- package/package.json +4 -4
- package/s/demo/demo.bundle.ts +10 -6
- package/s/demo/views/counter.ts +22 -17
- package/s/demo/views/demo.ts +10 -6
- package/s/demo/views/fastcount.ts +29 -0
- package/s/demo/views/loaders.ts +2 -2
- 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 +73 -0
- package/s/dom/{register.ts → parts/register.ts} +2 -7
- package/s/dom/types.ts +40 -0
- package/s/index.html.ts +4 -2
- package/s/index.ts +10 -9
- package/s/loot/drag-and-drops.ts +82 -0
- package/s/loot/drops.ts +35 -0
- package/s/loot/helpers.ts +31 -0
- package/s/loot/index.ts +5 -0
- package/s/ops/loaders/make-loader.ts +3 -3
- package/s/ops/loaders/parts/anims.ts +1 -1
- package/s/ops/loaders/parts/ascii-anim.ts +3 -3
- package/s/ops/loaders/parts/error-display.ts +2 -2
- package/s/ops/op.ts +2 -2
- package/s/{views → ui/base}/use.ts +35 -17
- package/s/ui/base/utils/attr-watcher.ts +22 -0
- package/s/ui/base/utils/reactor.ts +21 -0
- package/s/ui/base-element.ts +76 -0
- package/s/ui/types.ts +33 -0
- package/s/ui/view/make-component.ts +33 -0
- package/s/ui/view/make-view.ts +40 -0
- package/s/{views/utils → ui/view/parts}/apply-attrs.ts +4 -7
- package/s/ui/view/parts/capsule.ts +67 -0
- package/s/ui/view/parts/chain.ts +33 -0
- package/s/ui/view/parts/context.ts +10 -0
- package/s/ui/view/parts/directive.ts +29 -0
- package/s/ui/view/parts/sly-view.ts +15 -0
- package/s/ui/view.ts +24 -0
- package/x/demo/demo.bundle.js +9 -5
- package/x/demo/demo.bundle.js.map +1 -1
- package/x/demo/demo.bundle.min.js +19 -17
- 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 -15
- 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 +2 -2
- 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 +32 -0
- package/x/dom/dom.js +54 -0
- package/x/dom/dom.js.map +1 -0
- package/x/dom/parts/dashify.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 -0
- package/x/index.d.ts +9 -8
- 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 +9 -8
- package/x/index.js.map +1 -1
- 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/drops.d.ts +14 -0
- package/x/loot/drops.js +25 -0
- package/x/loot/drops.js.map +1 -0
- package/x/loot/helpers.d.ts +3 -0
- package/x/loot/helpers.js +21 -0
- package/x/loot/helpers.js.map +1 -0
- package/x/loot/index.d.ts +3 -0
- package/x/loot/index.js +4 -0
- package/x/loot/index.js.map +1 -0
- package/x/ops/loaders/make-loader.d.ts +1 -1
- package/x/ops/loaders/make-loader.js +2 -2
- package/x/ops/loaders/make-loader.js.map +1 -1
- package/x/ops/loaders/parts/anims.d.ts +1 -1
- package/x/ops/loaders/parts/ascii-anim.d.ts +2 -2
- package/x/ops/loaders/parts/ascii-anim.js +2 -2
- package/x/ops/loaders/parts/ascii-anim.js.map +1 -1
- package/x/ops/loaders/parts/error-display.js +2 -2
- package/x/ops/loaders/parts/error-display.js.map +1 -1
- package/x/ops/op.d.ts +2 -2
- package/x/ops/op.js +2 -2
- package/x/ops/op.js.map +1 -1
- package/x/ui/base/css-reset.js.map +1 -0
- package/x/{views → ui/base}/use.d.ts +12 -5
- package/x/{views → ui/base}/use.js +24 -10
- package/x/ui/base/use.js.map +1 -0
- package/x/ui/base/utils/apply-styles.js.map +1 -0
- package/x/ui/base/utils/attr-watcher.d.ts +8 -0
- package/x/ui/base/utils/attr-watcher.js +20 -0
- package/x/ui/base/utils/attr-watcher.js.map +1 -0
- package/x/ui/base/utils/mounts.js.map +1 -0
- package/x/ui/base/utils/reactor.d.ts +5 -0
- package/x/ui/base/utils/reactor.js +17 -0
- package/x/ui/base/utils/reactor.js.map +1 -0
- package/x/ui/base-element.d.ts +19 -0
- package/x/ui/base-element.js +52 -0
- package/x/ui/base-element.js.map +1 -0
- package/x/ui/types.d.ts +20 -0
- package/x/{views → ui}/types.js.map +1 -1
- package/x/ui/view/make-component.d.ts +5 -0
- package/x/ui/view/make-component.js +16 -0
- package/x/ui/view/make-component.js.map +1 -0
- package/x/ui/view/make-view.d.ts +2 -0
- package/x/ui/view/make-view.js +16 -0
- package/x/ui/view/make-view.js.map +1 -0
- package/x/ui/view/parts/apply-attrs.d.ts +2 -0
- package/x/{views/utils → ui/view/parts}/apply-attrs.js +2 -4
- package/x/ui/view/parts/apply-attrs.js.map +1 -0
- package/x/ui/view/parts/capsule.d.ts +13 -0
- package/x/ui/view/parts/capsule.js +49 -0
- package/x/ui/view/parts/capsule.js.map +1 -0
- package/x/ui/view/parts/chain.d.ts +11 -0
- package/x/ui/view/parts/chain.js +21 -0
- package/x/ui/view/parts/chain.js.map +1 -0
- package/x/ui/view/parts/context.d.ts +8 -0
- package/x/ui/view/parts/context.js +10 -0
- package/x/ui/view/parts/context.js.map +1 -0
- package/x/ui/view/parts/directive.d.ts +5 -0
- package/x/ui/view/parts/directive.js +18 -0
- package/x/ui/view/parts/directive.js.map +1 -0
- package/x/ui/view/parts/sly-view.d.ts +5 -0
- package/x/ui/view/parts/sly-view.js +13 -0
- package/x/ui/view/parts/sly-view.js.map +1 -0
- package/x/ui/view.d.ts +11 -0
- package/x/ui/view.js +15 -0
- package/x/ui/view.js.map +1 -0
- package/s/dom/dollar.ts +0 -27
- package/s/views/attributes.ts +0 -89
- package/s/views/types.ts +0 -40
- package/s/views/view.ts +0 -150
- package/x/dom/dashify.js.map +0 -1
- package/x/dom/dollar.d.ts +0 -10
- package/x/dom/dollar.js +0 -18
- package/x/dom/dollar.js.map +0 -1
- package/x/dom/register.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.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/dom/{dashify.ts → parts/dashify.ts} +0 -0
- /package/s/{views → ui/base}/css-reset.ts +0 -0
- /package/s/{views → ui/base}/utils/apply-styles.ts +0 -0
- /package/s/{views → ui/base}/utils/mounts.ts +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/{views → ui/base}/css-reset.d.ts +0 -0
- /package/x/{views → ui/base}/css-reset.js +0 -0
- /package/x/{views → ui/base}/utils/apply-styles.d.ts +0 -0
- /package/x/{views → ui/base}/utils/apply-styles.js +0 -0
- /package/x/{views → ui/base}/utils/mounts.d.ts +0 -0
- /package/x/{views → ui/base}/utils/mounts.js +0 -0
- /package/x/{views → ui}/types.js +0 -0
package/README.md
CHANGED
|
@@ -1,63 +1,67 @@
|
|
|
1
1
|
|
|
2
2
|
<div align="center"><img alt="" width="256" src="./assets/favicon.png"/></div>
|
|
3
3
|
|
|
4
|
-
# 🦝 sly
|
|
5
|
-
>
|
|
4
|
+
# 🦝 sly
|
|
5
|
+
> *mischievous shadow views*
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
- 🥷 leverage shadow-dom and slots
|
|
9
|
-
- 🤯 register any view as a web component
|
|
10
|
-
- 💲 handy little dom multitool
|
|
11
|
-
- 🫛 ops for fancy loading spinners
|
|
12
|
-
- 🧙♂️ took many years to get it right
|
|
13
|
-
- 🌅 sly is the successor that replaces [@benev/slate](https://github.com/benevolent-games/slate)
|
|
14
|
-
- 🧑💻 project by [@e280](https://e280.org/)
|
|
7
|
+
[@e280](https://e280.org/)'s shiny new [lit](https://lit.dev/)-based frontend lib for webdevs. *(sly replaces its predecessor, [slate](https://github.com/benevolent-games/slate))*
|
|
15
8
|
|
|
16
|
-
|
|
9
|
+
- 🍋 [**views**](#views) — hooks-based, shadow-dom'd, componentizable
|
|
10
|
+
- 🪵 [**base element**](#base-element) — for a more classical experience
|
|
11
|
+
- 🪄 [**dom**](#dom) — the "it's not jquery" multitool
|
|
12
|
+
- 🫛 [**ops**](#ops) — tools for async operations and loading spinners
|
|
13
|
+
- 🪙 [**loot**](#loot) — drag-and-drop facilities
|
|
14
|
+
- 🧪 testing page — https://sly.e280.org/
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
<br/><br/>
|
|
17
19
|
|
|
18
20
|
## 🦝 sly and friends
|
|
19
21
|
|
|
20
22
|
```sh
|
|
21
|
-
npm install @e280/sly lit
|
|
23
|
+
npm install @e280/sly lit @e280/strata @e280/stz
|
|
22
24
|
```
|
|
23
25
|
|
|
24
26
|
> [!NOTE]
|
|
25
|
-
> - 🔥 [lit](https://lit.dev/) for html rendering
|
|
26
|
-
> - ⛏️ [@e280/strata](https://github.com/e280/strata) for state management (signals, state trees)
|
|
27
|
-
> - 🏂 [@e280/stz](https://github.com/e280/stz)
|
|
28
|
-
> - 🐢 [scute](https://github.com/e280/scute)
|
|
27
|
+
> - 🔥 [lit](https://lit.dev/), for html rendering
|
|
28
|
+
> - ⛏️ [@e280/strata](https://github.com/e280/strata), for state management (signals, state trees)
|
|
29
|
+
> - 🏂 [@e280/stz](https://github.com/e280/stz), our ts standard library
|
|
30
|
+
> - 🐢 [@e280/scute](https://github.com/e280/scute), our buildy-bundly-buddy
|
|
31
|
+
|
|
32
|
+
|
|
29
33
|
|
|
30
|
-
<br/>
|
|
34
|
+
<br/><br/>
|
|
35
|
+
<a id="views"></a>
|
|
31
36
|
|
|
32
|
-
##
|
|
37
|
+
## 🦝🍋 sly views and components
|
|
33
38
|
> *views are the crown jewel of sly.. shadow-dom'd.. hooks-based.. "ergonomics"..*
|
|
34
39
|
|
|
35
40
|
```ts
|
|
36
41
|
view(use => () => html`<p>hello world</p>`)
|
|
37
42
|
```
|
|
38
43
|
|
|
39
|
-
-
|
|
40
|
-
-
|
|
44
|
+
- any view can be converted into a web component
|
|
45
|
+
- views are not [web components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components) per se, but they do have [shadow roots](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM) and support [slots](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots)
|
|
41
46
|
- views are typescript-native and comfy for webdevs building apps
|
|
42
47
|
- views automatically rerender whenever any [strata-compatible](https://github.com/e280/strata) state changes
|
|
43
48
|
|
|
44
49
|
### 🍋 view example
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
+
```ts
|
|
51
|
+
import {view, dom, BaseElement} from "@e280/sly"
|
|
52
|
+
import {html, css} from "lit"
|
|
53
|
+
```
|
|
50
54
|
- **declare a view**
|
|
51
55
|
```ts
|
|
52
56
|
export const CounterView = view(use => (start: number) => {
|
|
53
57
|
use.name("counter")
|
|
54
58
|
use.styles(css`p {color: green}`)
|
|
55
59
|
|
|
56
|
-
const count = use.signal(start)
|
|
57
|
-
const increment = () =>
|
|
60
|
+
const $count = use.signal(start)
|
|
61
|
+
const increment = () => $count.value++
|
|
58
62
|
|
|
59
63
|
return html`
|
|
60
|
-
<
|
|
64
|
+
<span>${$count.value}</span>
|
|
61
65
|
<button @click="${increment}">+</button>
|
|
62
66
|
`
|
|
63
67
|
})
|
|
@@ -65,16 +69,21 @@ view(use => () => html`<p>hello world</p>`)
|
|
|
65
69
|
- each view renders into a `<sly-view view="counter">` host (where "counter" is the `use.name` you provided)
|
|
66
70
|
- **inject a view into the dom**
|
|
67
71
|
```ts
|
|
68
|
-
|
|
69
|
-
<h1>
|
|
70
|
-
|
|
72
|
+
dom.in(".app").render(html`
|
|
73
|
+
<h1>cool counter demo</h1>
|
|
71
74
|
${CounterView(1)}
|
|
72
75
|
`)
|
|
73
76
|
```
|
|
74
77
|
- 🤯 **register a view as a web component**
|
|
75
78
|
```ts
|
|
76
|
-
|
|
77
|
-
|
|
79
|
+
dom.register({
|
|
80
|
+
MyCounter: CounterView
|
|
81
|
+
.component(BaseElement)
|
|
82
|
+
.props(component => [dom.attrs(component).number.start ?? 0]),
|
|
83
|
+
})
|
|
84
|
+
```
|
|
85
|
+
```html
|
|
86
|
+
<my-counter start="1"></my-counter>
|
|
78
87
|
```
|
|
79
88
|
|
|
80
89
|
### 🍋 view declaration settings
|
|
@@ -82,7 +91,7 @@ view(use => () => html`<p>hello world</p>`)
|
|
|
82
91
|
```ts
|
|
83
92
|
export const CoolView = view
|
|
84
93
|
.settings({mode: "open", delegatesFocus: true})
|
|
85
|
-
.
|
|
94
|
+
.render(use => (greeting: string) => {
|
|
86
95
|
return html`😎 ${greeting} <slot></slot>`
|
|
87
96
|
})
|
|
88
97
|
```
|
|
@@ -92,9 +101,8 @@ view(use => () => html`<p>hello world</p>`)
|
|
|
92
101
|
### 🍋 view injection options
|
|
93
102
|
- options for views at the template injection site
|
|
94
103
|
```ts
|
|
95
|
-
|
|
96
|
-
<h2>
|
|
97
|
-
|
|
104
|
+
dom.in(".app").render(html`
|
|
105
|
+
<h2>cool example</h2>
|
|
98
106
|
${CoolView.props("hello")
|
|
99
107
|
.attr("class", "hero")
|
|
100
108
|
.children(html`<em>spongebob</em>`)
|
|
@@ -106,27 +114,77 @@ view(use => () => html`<p>hello world</p>`)
|
|
|
106
114
|
- `children` — nested content in the host element, can be [slotted](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots)
|
|
107
115
|
- `render` — end the view chain and render the lit directive
|
|
108
116
|
|
|
109
|
-
### 🍋
|
|
110
|
-
- **
|
|
117
|
+
### 🍋 view/component universality
|
|
118
|
+
- **you can start with a view,**
|
|
111
119
|
```ts
|
|
112
|
-
const
|
|
120
|
+
export const GreeterView = view(use => (name: string) => {
|
|
121
|
+
return html`<p>hello ${name}</p>`
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
// view usage:
|
|
125
|
+
// GreeterView("pimsley")
|
|
113
126
|
```
|
|
114
|
-
|
|
115
|
-
- **convert any view into a web component**
|
|
127
|
+
then convert it to a component.
|
|
116
128
|
```ts
|
|
117
|
-
|
|
129
|
+
export class GreeterComponent extends (
|
|
130
|
+
GreeterView
|
|
131
|
+
.component(BaseElement)
|
|
132
|
+
.props(component => [component.getAttribute("name") ?? "unknown"])
|
|
133
|
+
) {}
|
|
134
|
+
|
|
135
|
+
// html usage:
|
|
136
|
+
// <greeter-component name="pimsley"></greeter-component>
|
|
118
137
|
```
|
|
119
|
-
-
|
|
120
|
-
|
|
138
|
+
- this trick with `class` and `extends` is amazing because typescript exports both the value of your component class, but also its type (doesn't work if you use `const`)
|
|
139
|
+
- **you can start with a component,**
|
|
140
|
+
```ts
|
|
141
|
+
export class GreeterComponent extends (
|
|
142
|
+
view(use => (name: string) => {
|
|
143
|
+
return html`<p>hello ${name}</p>`
|
|
144
|
+
})
|
|
145
|
+
.component(BaseElement)
|
|
146
|
+
.props(component => [component.getAttribute("name") ?? "unknown"])
|
|
147
|
+
) {}
|
|
148
|
+
|
|
149
|
+
// html usage:
|
|
150
|
+
// <greeter-component name="pimsley"></greeter-component>
|
|
151
|
+
```
|
|
152
|
+
then it already has a `.view` ready for you.
|
|
153
|
+
```ts
|
|
154
|
+
// view usage:
|
|
155
|
+
// GreeterComponent.view("pimsley")
|
|
156
|
+
```
|
|
157
|
+
- **understanding `.component(C)` and `.props(fn)`**
|
|
158
|
+
- `.props` takes a fn that is called every render, which returns the props given to the view
|
|
159
|
+
```ts
|
|
160
|
+
.component(BaseElement)
|
|
161
|
+
.props(() => ["pimsley"])
|
|
162
|
+
```
|
|
163
|
+
the props fn receives the component instance, so you can query html attributes or instance properties
|
|
164
|
+
```ts
|
|
165
|
+
.component(BaseElement)
|
|
166
|
+
.props(component => [component.getAttribute("name") ?? "unknown"])
|
|
167
|
+
```
|
|
168
|
+
- `.component` accepts a subclass of `BaseElement`, which lets you define your own properties and methods for your component class
|
|
169
|
+
```ts
|
|
170
|
+
.component(class extends BaseElement {
|
|
171
|
+
$name = signal("jim raynor")
|
|
172
|
+
updateName(name: string) {
|
|
173
|
+
this.$name.value = name
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
.props(component => [component.$name.value])
|
|
177
|
+
```
|
|
178
|
+
- `.component` lets devs interacting with your component get nice types
|
|
179
|
+
```ts
|
|
180
|
+
dom<GreeterComponent>("my-component").updateName("mortimer")
|
|
181
|
+
```
|
|
121
182
|
- **register web components to the dom**
|
|
122
183
|
```ts
|
|
123
|
-
|
|
124
|
-
// <my-component></my-component>
|
|
125
|
-
// <my-counter></my-counter>
|
|
184
|
+
dom.register({GreeterComponent})
|
|
126
185
|
```
|
|
127
|
-
- `$.register` automatically dashes the tag names (`MyComponent` becomes `<my-component>`)
|
|
128
186
|
|
|
129
|
-
### 🍋
|
|
187
|
+
### 🍋 "use" hooks reference
|
|
130
188
|
- 👮 **follow the hooks rules**
|
|
131
189
|
> just like [react hooks](https://react.dev/warnings/invalid-hook-call-warning), the execution order of sly's `use` hooks actually matters..
|
|
132
190
|
> 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..*
|
|
@@ -138,16 +196,26 @@ view(use => () => html`<p>hello world</p>`)
|
|
|
138
196
|
```ts
|
|
139
197
|
use.styles(css1, css2, css3)
|
|
140
198
|
```
|
|
199
|
+
*(alias `use.css`)*
|
|
141
200
|
- **use.signal** — create a [strata signal](https://github.com/e280/strata)
|
|
142
201
|
```ts
|
|
143
|
-
const count = use.signal(1)
|
|
202
|
+
const $count = use.signal(1)
|
|
144
203
|
|
|
145
204
|
// read the signal
|
|
146
|
-
count()
|
|
205
|
+
$count()
|
|
147
206
|
|
|
148
207
|
// write the signal
|
|
149
|
-
count(2)
|
|
150
|
-
```
|
|
208
|
+
$count(2)
|
|
209
|
+
```
|
|
210
|
+
- `derived` signals
|
|
211
|
+
```ts
|
|
212
|
+
const $product = use.derived(() => $count() * $whatever())
|
|
213
|
+
```
|
|
214
|
+
- `lazy` signals
|
|
215
|
+
```ts
|
|
216
|
+
const $product = use.lazy(() => $count() * $whatever())
|
|
217
|
+
```
|
|
218
|
+
- go read the [strata readme](https://github.com/e280/strata) about this stuff
|
|
151
219
|
- **use.once** — run fn at initialization, and return a value
|
|
152
220
|
```ts
|
|
153
221
|
const whatever = use.once(() => {
|
|
@@ -186,9 +254,10 @@ view(use => () => html`<p>hello world</p>`)
|
|
|
186
254
|
|
|
187
255
|
v // 123
|
|
188
256
|
```
|
|
189
|
-
- **use.attrs** — ergonomic typed html attribute access
|
|
257
|
+
- **use.attrs** — ergonomic typed html attribute access
|
|
258
|
+
*(see [dom.attrs](#dom.attrs) for more details)*
|
|
190
259
|
```ts
|
|
191
|
-
const attrs = use.attrs({
|
|
260
|
+
const attrs = use.attrs.spec({
|
|
192
261
|
name: String,
|
|
193
262
|
count: Number,
|
|
194
263
|
active: Boolean,
|
|
@@ -199,14 +268,6 @@ view(use => () => html`<p>hello world</p>`)
|
|
|
199
268
|
attrs.count // 123
|
|
200
269
|
attrs.active // true
|
|
201
270
|
```
|
|
202
|
-
```ts
|
|
203
|
-
attrs.name = "zenky"
|
|
204
|
-
attrs.count = 124
|
|
205
|
-
attrs.active = false // removes html attr
|
|
206
|
-
```
|
|
207
|
-
```ts
|
|
208
|
-
attrs.name = undefined // removes the attr
|
|
209
|
-
```
|
|
210
271
|
- **use.render** — rerender the view (debounced)
|
|
211
272
|
```ts
|
|
212
273
|
use.render()
|
|
@@ -234,17 +295,17 @@ view(use => () => html`<p>hello world</p>`)
|
|
|
234
295
|
const op = use.op.promise(doAsyncWork())
|
|
235
296
|
```
|
|
236
297
|
|
|
237
|
-
### 🍋
|
|
298
|
+
### 🍋 "use" recipes
|
|
238
299
|
- make a ticker — mount, repeat, and nap
|
|
239
300
|
```ts
|
|
240
301
|
import {repeat, nap} from "@e280/stz"
|
|
241
302
|
```
|
|
242
303
|
```ts
|
|
243
|
-
const seconds = use.signal(0)
|
|
304
|
+
const $seconds = use.signal(0)
|
|
244
305
|
|
|
245
306
|
use.mount(() => repeat(async() => {
|
|
246
307
|
await nap(1000)
|
|
247
|
-
seconds.value++
|
|
308
|
+
$seconds.value++
|
|
248
309
|
}))
|
|
249
310
|
```
|
|
250
311
|
- wake + rendered, to do something after each mount's first render
|
|
@@ -254,55 +315,182 @@ view(use => () => html`<p>hello world</p>`)
|
|
|
254
315
|
}))
|
|
255
316
|
```
|
|
256
317
|
|
|
257
|
-
<br/>
|
|
258
318
|
|
|
259
|
-
## 🦝 sly dom multitool
|
|
260
|
-
> *"it's not jquery!"*
|
|
261
319
|
|
|
262
|
-
|
|
263
|
-
-
|
|
320
|
+
<br/><br/>
|
|
321
|
+
<a id="base-element"></a>
|
|
322
|
+
|
|
323
|
+
## 🦝🪵 sly base element
|
|
324
|
+
> *the classic experience*
|
|
325
|
+
|
|
326
|
+
```ts
|
|
327
|
+
import {BaseElement, Use, dom} from "@e280/sly"
|
|
328
|
+
import {html, css} from "lit"
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
`BaseElement` is a class-based approach to create a custom element web component.
|
|
332
|
+
|
|
333
|
+
it lets you expose js properties on the element instance, which helps you setup a better developer experience for people interacting with your element through the dom.
|
|
334
|
+
|
|
335
|
+
base element enjoys the same `use` hooks as views.
|
|
336
|
+
|
|
337
|
+
### 🪵 base element setup
|
|
338
|
+
- **declare your element class**
|
|
264
339
|
```ts
|
|
265
|
-
|
|
340
|
+
export class MyElement extends BaseElement {
|
|
341
|
+
static styles = css`span{color:orange}`
|
|
342
|
+
|
|
343
|
+
// custom property
|
|
344
|
+
start = 10
|
|
345
|
+
|
|
346
|
+
// custom attributes
|
|
347
|
+
attrs = dom.attrs(this).spec({
|
|
348
|
+
multiply: Number,
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
// custom methods
|
|
352
|
+
hello() {
|
|
353
|
+
return "world"
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
render(use: Use) {
|
|
357
|
+
const $count = use.signal(1)
|
|
358
|
+
const increment = () => $count.value++
|
|
359
|
+
|
|
360
|
+
const {start} = this
|
|
361
|
+
const {multiply = 1} = this.attrs
|
|
362
|
+
const result = start + (multiply * $count())
|
|
363
|
+
|
|
364
|
+
return html`
|
|
365
|
+
<span>${result}</span>
|
|
366
|
+
<button @click="${increment}">+</button>
|
|
367
|
+
`
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
- **register your element to the dom**
|
|
372
|
+
```ts
|
|
373
|
+
dom.register({MyElement})
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### 🪵 base element usage
|
|
377
|
+
- **place the element in your html body**
|
|
378
|
+
```html
|
|
379
|
+
<body>
|
|
380
|
+
<my-element></my-element>
|
|
381
|
+
</body>
|
|
382
|
+
```
|
|
383
|
+
- **now you can interact with it**
|
|
384
|
+
```ts
|
|
385
|
+
const myElement = dom<MyElement>("my-element")
|
|
386
|
+
|
|
387
|
+
// js property
|
|
388
|
+
myElement.start = 100
|
|
389
|
+
|
|
390
|
+
// html attributes
|
|
391
|
+
myElement.attrs.multiply = 2
|
|
392
|
+
|
|
393
|
+
// methods
|
|
394
|
+
myElement.hello()
|
|
395
|
+
// "world"
|
|
266
396
|
```
|
|
267
397
|
|
|
268
|
-
|
|
269
|
-
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
<br/><br/>
|
|
401
|
+
<a id="dom"></a>
|
|
402
|
+
|
|
403
|
+
## 🦝🪄 sly dom
|
|
404
|
+
> *the "it's not jquery!" multitool*
|
|
405
|
+
|
|
406
|
+
```ts
|
|
407
|
+
import {dom} from "@e280/sly"
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### 🪄 dom queries
|
|
411
|
+
- `require` an element
|
|
270
412
|
```ts
|
|
271
|
-
|
|
272
|
-
// HTMLElement (or throws
|
|
413
|
+
dom(".demo")
|
|
414
|
+
// HTMLElement (or throws)
|
|
273
415
|
```
|
|
274
|
-
-
|
|
416
|
+
- `maybe` get an element
|
|
275
417
|
```ts
|
|
276
|
-
|
|
418
|
+
dom.maybe(".demo")
|
|
277
419
|
// HTMLElement | undefined
|
|
278
420
|
```
|
|
279
|
-
-
|
|
421
|
+
- `select` all elements
|
|
280
422
|
```ts
|
|
281
|
-
|
|
282
|
-
|
|
423
|
+
dom.all(".demo ul li")
|
|
424
|
+
// HTMLElement[]
|
|
283
425
|
```
|
|
284
|
-
-
|
|
426
|
+
- `in` the scope of an element
|
|
285
427
|
```ts
|
|
286
|
-
|
|
287
|
-
// HTMLElement
|
|
428
|
+
dom(element).require("li")
|
|
429
|
+
// HTMLElement (or throws)
|
|
430
|
+
```
|
|
431
|
+
```ts
|
|
432
|
+
dom(element).maybe("li")
|
|
433
|
+
// HTMLElement | undefined
|
|
288
434
|
```
|
|
289
|
-
|
|
290
|
-
### 💲 dom utilities
|
|
291
|
-
- render content into an element
|
|
292
435
|
```ts
|
|
293
|
-
|
|
436
|
+
dom(element).all("li")
|
|
437
|
+
// HTMLElement[]
|
|
294
438
|
```
|
|
295
|
-
|
|
439
|
+
|
|
440
|
+
### 🪄 dom utilities
|
|
441
|
+
- `register` web components
|
|
296
442
|
```ts
|
|
297
|
-
|
|
443
|
+
dom.register({MyComponent, AnotherCoolComponent})
|
|
298
444
|
// <my-component>
|
|
299
445
|
// <another-cool-component>
|
|
300
446
|
```
|
|
447
|
+
- `dom.register` automatically dashes the tag names (`MyComponent` becomes `<my-component>`)
|
|
448
|
+
- `render` content into an element
|
|
449
|
+
```ts
|
|
450
|
+
dom(element).render(html`<p>hello world</p>`)
|
|
451
|
+
```
|
|
452
|
+
```ts
|
|
453
|
+
dom.in(".demo").render(html`<p>hello world</p>`)
|
|
454
|
+
```
|
|
455
|
+
```ts
|
|
456
|
+
dom.render(element, html`<p>hello world</p>`)
|
|
457
|
+
```
|
|
458
|
+
- `attrs` <a id="dom.attrs"></a> to setup a type-happy html attribute helper
|
|
459
|
+
```ts
|
|
460
|
+
const attrs = dom.attrs(element).spec({
|
|
461
|
+
name: String,
|
|
462
|
+
count: Number,
|
|
463
|
+
active: Boolean,
|
|
464
|
+
})
|
|
465
|
+
```
|
|
466
|
+
```ts
|
|
467
|
+
attrs.name // "chase"
|
|
468
|
+
attrs.count // 123
|
|
469
|
+
attrs.active // true
|
|
470
|
+
```
|
|
471
|
+
```ts
|
|
472
|
+
attrs.name = "zenky"
|
|
473
|
+
attrs.count = 124
|
|
474
|
+
attrs.active = false // removes html attr
|
|
475
|
+
```
|
|
476
|
+
```ts
|
|
477
|
+
attrs.name = undefined // removes the attr
|
|
478
|
+
attrs.count = undefined // removes the attr
|
|
479
|
+
```
|
|
480
|
+
or if you wanna be more loosey-goosy, skip the spec
|
|
481
|
+
```ts
|
|
482
|
+
dom.attrs(element).string.name = "pimsley"
|
|
483
|
+
dom.attrs(element).number.count = 125
|
|
484
|
+
dom.attrs(element).boolean.active = true
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
|
|
301
488
|
|
|
302
|
-
<br/>
|
|
489
|
+
<br/><br/>
|
|
490
|
+
<a id="ops"></a>
|
|
303
491
|
|
|
304
|
-
##
|
|
305
|
-
> *async operations and
|
|
492
|
+
## 🦝🫛 sly ops
|
|
493
|
+
> *tools for async operations and loading spinners*
|
|
306
494
|
|
|
307
495
|
```ts
|
|
308
496
|
import {nap} from "@e280/stz"
|
|
@@ -356,7 +544,7 @@ import {Pod, podium, Op, makeLoader, anims} from "@e280/sly"
|
|
|
356
544
|
```
|
|
357
545
|
- 🔥 create an op that calls and tracks an async fn
|
|
358
546
|
```ts
|
|
359
|
-
const op = Op.
|
|
547
|
+
const op = Op.load(async() => {
|
|
360
548
|
await nap(4000)
|
|
361
549
|
return 123
|
|
362
550
|
})
|
|
@@ -426,9 +614,138 @@ import {Pod, podium, Op, makeLoader, anims} from "@e280/sly"
|
|
|
426
614
|
- when the op is in error, the error will be displayed
|
|
427
615
|
- when the op is ready, your fn is called and given the value
|
|
428
616
|
|
|
429
|
-
<br/>
|
|
430
617
|
|
|
431
|
-
|
|
618
|
+
|
|
619
|
+
<br/><br/>
|
|
620
|
+
<a id="loot"></a>
|
|
621
|
+
|
|
622
|
+
## 🦝🪙 loot
|
|
623
|
+
> *drag-and-drop facilities*
|
|
624
|
+
|
|
625
|
+
```ts
|
|
626
|
+
import {loot, view, dom} from "@e280/sly"
|
|
627
|
+
import {ev} from "@e280/stz"
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
### 🪙 `loot.Drops`
|
|
631
|
+
> *accept the user dropping stuff like files onto the page*
|
|
632
|
+
- **setup drops**
|
|
633
|
+
```ts
|
|
634
|
+
const drops = new loot.Drops({
|
|
635
|
+
predicate: loot.hasFiles,
|
|
636
|
+
acceptDrop: event => {
|
|
637
|
+
const files = loot.files(event)
|
|
638
|
+
console.log("files dropped", files)
|
|
639
|
+
},
|
|
640
|
+
})
|
|
641
|
+
```
|
|
642
|
+
- **attach event listeners to your dropzone,** one of these ways:
|
|
643
|
+
- **view example**
|
|
644
|
+
```ts
|
|
645
|
+
view(() => () => html`
|
|
646
|
+
<div
|
|
647
|
+
?data-indicator="${drops.$indicator()}"
|
|
648
|
+
@dragover="${drops.dragover}"
|
|
649
|
+
@dragleave="${drops.dragleave}"
|
|
650
|
+
@drop="${drops.drop}">
|
|
651
|
+
my dropzone
|
|
652
|
+
</div>
|
|
653
|
+
`)
|
|
654
|
+
```
|
|
655
|
+
- **vanilla-js whole-page example**
|
|
656
|
+
```ts
|
|
657
|
+
// attach listeners to the body
|
|
658
|
+
ev(document.body, {
|
|
659
|
+
dragover: drops.dragover,
|
|
660
|
+
dragleave: drops.dragleave,
|
|
661
|
+
drop: drops.drop,
|
|
662
|
+
})
|
|
663
|
+
|
|
664
|
+
// sly attribute handler for the body
|
|
665
|
+
const attrs = dom.attrs(document.body).spec({
|
|
666
|
+
"data-indicator": Boolean,
|
|
667
|
+
})
|
|
668
|
+
|
|
669
|
+
// sync the data-indicator attribute
|
|
670
|
+
drops.$indicator.on(bool => attrs["data-indicator"] = bool)
|
|
671
|
+
```
|
|
672
|
+
- **flashy css indicator for the dropzone,** so the user knows your app is eager to accept the drop
|
|
673
|
+
```css
|
|
674
|
+
[data-indicator] {
|
|
675
|
+
border: 0.5em dashed cyan;
|
|
676
|
+
}
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
### 🪙 `loot.DragAndDrops`
|
|
680
|
+
> *setup drag-and-drops between items within your page*
|
|
681
|
+
- **declare types for your draggy and droppy things**
|
|
682
|
+
```ts
|
|
683
|
+
// money that can be picked up and dragged
|
|
684
|
+
type Money = {value: number}
|
|
685
|
+
// dnd will call this a "draggy"
|
|
686
|
+
|
|
687
|
+
// bag that money can be dropped into
|
|
688
|
+
type Bag = {id: number}
|
|
689
|
+
// dnd will call this a "droppy"
|
|
690
|
+
```
|
|
691
|
+
- **make your dnd**
|
|
692
|
+
```ts
|
|
693
|
+
const dnd = new loot.DragAndDrops<Money, Bag>({
|
|
694
|
+
acceptDrop: (event, money, bag) => {
|
|
695
|
+
console.log("drop!", {money, bag})
|
|
696
|
+
},
|
|
697
|
+
})
|
|
698
|
+
```
|
|
699
|
+
- **attach dragzone listeners** (there can be many dragzones...)
|
|
700
|
+
```ts
|
|
701
|
+
view(use => () => {
|
|
702
|
+
const money = use.once((): Money => ({value: 280}))
|
|
703
|
+
const dragzone = use.once(() => dnd.dragzone(() => money))
|
|
704
|
+
|
|
705
|
+
return html`
|
|
706
|
+
<div
|
|
707
|
+
draggable="${dragzone.draggable}"
|
|
708
|
+
@dragstart="${dragzone.dragstart}"
|
|
709
|
+
@dragend="${dragzone.dragend}">
|
|
710
|
+
money ${money.value}
|
|
711
|
+
</div>
|
|
712
|
+
`
|
|
713
|
+
})
|
|
714
|
+
```
|
|
715
|
+
- **attach dropzone listeners** (there can be many dropzones...)
|
|
716
|
+
```ts
|
|
717
|
+
view(use => () => {
|
|
718
|
+
const bag = use.once((): Bag => ({id: 1}))
|
|
719
|
+
const dropzone = use.once(() => dnd.dropzone(() => bag))
|
|
720
|
+
const indicator = !!(dnd.dragging && dnd.hovering === bag)
|
|
721
|
+
|
|
722
|
+
return html`
|
|
723
|
+
<div
|
|
724
|
+
?data-indicator="${indicator}"
|
|
725
|
+
@dragenter="${dropzone.dragenter}"
|
|
726
|
+
@dragleave="${dropzone.dragleave}"
|
|
727
|
+
@dragover="${dropzone.dragover}"
|
|
728
|
+
@drop="${dropzone.drop}">
|
|
729
|
+
bag ${bag.id}
|
|
730
|
+
</div>
|
|
731
|
+
`
|
|
732
|
+
})
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
### 🪙 loot helpers
|
|
736
|
+
- **`loot.hasFiles(event)`** — return true if `DragEvent` contains any files (useful in `predicate`)
|
|
737
|
+
- **`loot.files(event)`** — returns an array of files in a drop's `DragEvent` (useful in `acceptDrop`)
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
<br/><br/>
|
|
742
|
+
<a id="e280"></a>
|
|
743
|
+
|
|
744
|
+
## 🦝🧑💻 sly is by e280
|
|
432
745
|
reward us with github stars
|
|
433
746
|
build with us at https://e280.org/ but only if you're cool
|
|
434
747
|
|
|
748
|
+
|
|
749
|
+
|
|
750
|
+
<br/><br/>
|
|
751
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@e280/sly",
|
|
3
|
-
"version": "0.2.0-
|
|
3
|
+
"version": "0.2.0-10",
|
|
4
4
|
"description": "web shadow views",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -16,12 +16,12 @@
|
|
|
16
16
|
"lit": "^3.3.1"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@e280/strata": "^0.2.0-
|
|
19
|
+
"@e280/strata": "^0.2.0-8",
|
|
20
20
|
"@e280/stz": "^0.2.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@e280/science": "^0.1.
|
|
24
|
-
"@e280/scute": "^0.
|
|
23
|
+
"@e280/science": "^0.1.2",
|
|
24
|
+
"@e280/scute": "^0.1.0",
|
|
25
25
|
"http-server": "^14.1.1",
|
|
26
26
|
"npm-run-all": "^4.1.5",
|
|
27
27
|
"typescript": "^5.9.2"
|