@e280/sly 0.0.0-7 → 0.0.0-8
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 +254 -98
- package/package.json +1 -1
- package/s/demo/demo.bundle.ts +2 -5
- package/s/demo/views/counter.ts +2 -2
- package/s/demo/views/demo.ts +2 -2
- package/s/demo/views/loaders.ts +4 -4
- package/s/index.ts +15 -14
- package/s/{features/op → ops}/op.ts +15 -8
- package/s/views/attributes.ts +89 -0
- package/s/{features/views → views}/use.ts +21 -5
- package/s/{features/views → views}/view.ts +15 -10
- package/x/demo/demo.bundle.js +2 -4
- package/x/demo/demo.bundle.js.map +1 -1
- package/x/demo/demo.bundle.min.js +12 -12
- package/x/demo/demo.bundle.min.js.map +4 -4
- package/x/demo/views/counter.js +2 -2
- package/x/demo/views/counter.js.map +1 -1
- package/x/demo/views/demo.js +2 -2
- package/x/demo/views/demo.js.map +1 -1
- package/x/demo/views/loaders.js +4 -4
- package/x/demo/views/loaders.js.map +1 -1
- package/x/dom/dashify.js.map +1 -0
- package/x/dom/dollar.js.map +1 -0
- package/x/dom/register.js.map +1 -0
- package/x/{features/op → dom}/types.js.map +1 -1
- package/x/index.d.ts +15 -14
- package/x/index.html +2 -2
- package/x/index.js +15 -14
- package/x/index.js.map +1 -1
- package/x/ops/loaders/make-loader.js.map +1 -0
- package/x/ops/loaders/parts/anims.js.map +1 -0
- package/x/ops/loaders/parts/ascii-anim.js.map +1 -0
- package/x/ops/loaders/parts/error-display.d.ts +1 -0
- package/x/ops/loaders/parts/error-display.js.map +1 -0
- package/x/{features/op → ops}/op.d.ts +6 -3
- package/x/{features/op → ops}/op.js +13 -7
- package/x/ops/op.js.map +1 -0
- package/x/ops/podium.js.map +1 -0
- package/x/{features/dom → ops}/types.js.map +1 -1
- package/x/views/attributes.d.ts +10 -0
- package/x/views/attributes.js +46 -0
- package/x/views/attributes.js.map +1 -0
- package/x/views/css-reset.js.map +1 -0
- package/x/{features/views → views}/types.js.map +1 -1
- package/x/{features/views → views}/use.d.ts +10 -5
- package/x/{features/views → views}/use.js +21 -6
- package/x/views/use.js.map +1 -0
- package/x/views/utils/apply-attrs.js.map +1 -0
- package/x/views/utils/apply-styles.js.map +1 -0
- package/x/views/utils/mounts.js.map +1 -0
- package/x/{features/views → views}/view.d.ts +2 -3
- package/x/{features/views → views}/view.js +7 -7
- package/x/views/view.js.map +1 -0
- package/x/features/dom/dashify.js.map +0 -1
- package/x/features/dom/dollar.js.map +0 -1
- package/x/features/dom/register.js.map +0 -1
- package/x/features/op/loaders/make-loader.js.map +0 -1
- package/x/features/op/loaders/parts/anims.js.map +0 -1
- package/x/features/op/loaders/parts/ascii-anim.js.map +0 -1
- package/x/features/op/loaders/parts/error-display.d.ts +0 -1
- package/x/features/op/loaders/parts/error-display.js.map +0 -1
- package/x/features/op/op.js.map +0 -1
- package/x/features/op/podium.js.map +0 -1
- package/x/features/views/css-reset.js.map +0 -1
- package/x/features/views/use.js.map +0 -1
- package/x/features/views/utils/apply-attrs.js.map +0 -1
- package/x/features/views/utils/apply-styles.js.map +0 -1
- package/x/features/views/utils/mounts.js.map +0 -1
- package/x/features/views/view.js.map +0 -1
- /package/s/{features/dom → dom}/dashify.ts +0 -0
- /package/s/{features/dom → dom}/dollar.ts +0 -0
- /package/s/{features/dom → dom}/register.ts +0 -0
- /package/s/{features/dom → dom}/types.ts +0 -0
- /package/s/{features/op → ops}/loaders/make-loader.ts +0 -0
- /package/s/{features/op → ops}/loaders/parts/anims.ts +0 -0
- /package/s/{features/op → ops}/loaders/parts/ascii-anim.ts +0 -0
- /package/s/{features/op → ops}/loaders/parts/error-display.ts +0 -0
- /package/s/{features/op → ops}/podium.ts +0 -0
- /package/s/{features/op → ops}/types.ts +0 -0
- /package/s/{features/views → views}/css-reset.ts +0 -0
- /package/s/{features/views → views}/types.ts +0 -0
- /package/s/{features/views → views}/utils/apply-attrs.ts +0 -0
- /package/s/{features/views → views}/utils/apply-styles.ts +0 -0
- /package/s/{features/views → views}/utils/mounts.ts +0 -0
- /package/x/{features/dom → dom}/dashify.d.ts +0 -0
- /package/x/{features/dom → dom}/dashify.js +0 -0
- /package/x/{features/dom → dom}/dollar.d.ts +0 -0
- /package/x/{features/dom → dom}/dollar.js +0 -0
- /package/x/{features/dom → dom}/register.d.ts +0 -0
- /package/x/{features/dom → dom}/register.js +0 -0
- /package/x/{features/dom → dom}/types.d.ts +0 -0
- /package/x/{features/dom → dom}/types.js +0 -0
- /package/x/{features/op → ops}/loaders/make-loader.d.ts +0 -0
- /package/x/{features/op → ops}/loaders/make-loader.js +0 -0
- /package/x/{features/op → ops}/loaders/parts/anims.d.ts +0 -0
- /package/x/{features/op → ops}/loaders/parts/anims.js +0 -0
- /package/x/{features/op → ops}/loaders/parts/ascii-anim.d.ts +0 -0
- /package/x/{features/op → ops}/loaders/parts/ascii-anim.js +0 -0
- /package/x/{features/op → ops}/loaders/parts/error-display.js +0 -0
- /package/x/{features/op → ops}/podium.d.ts +0 -0
- /package/x/{features/op → ops}/podium.js +0 -0
- /package/x/{features/op → ops}/types.d.ts +0 -0
- /package/x/{features/op → ops}/types.js +0 -0
- /package/x/{features/views → views}/css-reset.d.ts +0 -0
- /package/x/{features/views → views}/css-reset.js +0 -0
- /package/x/{features/views → views}/types.d.ts +0 -0
- /package/x/{features/views → views}/types.js +0 -0
- /package/x/{features/views → views}/utils/apply-attrs.d.ts +0 -0
- /package/x/{features/views → views}/utils/apply-attrs.js +0 -0
- /package/x/{features/views → views}/utils/apply-styles.d.ts +0 -0
- /package/x/{features/views → views}/utils/apply-styles.js +0 -0
- /package/x/{features/views → views}/utils/mounts.d.ts +0 -0
- /package/x/{features/views → views}/utils/mounts.js +0 -0
package/README.md
CHANGED
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
# 🦝 sly — mischievous shadow views
|
|
5
5
|
> testing page at https://sly.e280.org/
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
7
|
+
- 🍋 web app view library with taste
|
|
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 of iteration and suffering
|
|
13
|
+
- 🌅 sly is the successor that replaces [@benev/slate](https://github.com/benevolent-games/slate)
|
|
12
14
|
- 🧑💻 project by [@e280](https://e280.org/)
|
|
13
15
|
|
|
14
16
|
<br/>
|
|
@@ -16,63 +18,73 @@
|
|
|
16
18
|
## 🦝 INSTALL SLY AND PALS
|
|
17
19
|
|
|
18
20
|
```sh
|
|
19
|
-
npm install @e280/sly @e280/
|
|
21
|
+
npm install @e280/sly lit @e280/strata @e280/stz
|
|
20
22
|
```
|
|
21
23
|
|
|
24
|
+
> [!NOTE]
|
|
25
|
+
> - 🔥 [lit](https://lit.dev/) for html rendering
|
|
26
|
+
> - ⛏️ [@e280/strata](https://github.com/e280/strata) for state management (signals, state trees)
|
|
27
|
+
> - 🏂 *(optional)* [@e280/stz](https://github.com/e280/stz) stz is our ts standard library
|
|
28
|
+
> - 🐢 *(optional)* [scute](https://github.com/e280/scute) is our buildy-bundly-buddy
|
|
29
|
+
|
|
22
30
|
<br/>
|
|
23
31
|
|
|
24
32
|
## 🦝 SLY VIEWS
|
|
25
|
-
views are the crown jewel of sly. shadow-dom'd. hooks-based. fancy ergonomics.
|
|
33
|
+
views are the crown jewel of sly. shadow-dom'd. hooks-based. fancy ergonomics.
|
|
26
34
|
|
|
27
|
-
|
|
35
|
+
```ts
|
|
36
|
+
view(use => () => "hello world")
|
|
37
|
+
```
|
|
28
38
|
|
|
29
|
-
|
|
39
|
+
views are not web components.
|
|
30
40
|
|
|
31
|
-
-
|
|
32
|
-
```ts
|
|
33
|
-
import {view} from "@e280/sly"
|
|
41
|
+
where web components are html-native, views are typescript-native — with views, there's no dom registration or string tag names, you just import them and the types work.
|
|
34
42
|
|
|
35
|
-
|
|
36
|
-
|
|
43
|
+
web components are best for giving html authors access to your cool widgets.. and that's cool, because any sly view can be registered as a web component.
|
|
44
|
+
|
|
45
|
+
views automatically rerender whenever any state stuff from [@e280/strata](https://github.com/e280/strata) changes.
|
|
46
|
+
|
|
47
|
+
🥷 views have a [shadow root](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).. views have the good parts of web components, but they aren't cumbersome.
|
|
37
48
|
|
|
38
49
|
### 🍋 view example
|
|
39
|
-
-
|
|
40
|
-
- **declaring a view**
|
|
50
|
+
- **import some stuff**
|
|
41
51
|
```ts
|
|
42
|
-
import {view} from "@e280/sly"
|
|
52
|
+
import {$, view} from "@e280/sly"
|
|
43
53
|
import {html, css} from "lit"
|
|
44
|
-
|
|
54
|
+
```
|
|
55
|
+
- **declaring a view**
|
|
56
|
+
```ts
|
|
45
57
|
export const CounterView = view(use => (start: number) => {
|
|
46
58
|
use.name("counter")
|
|
47
59
|
use.styles(css`p {color: green}`)
|
|
60
|
+
|
|
48
61
|
const count = use.signal(start)
|
|
62
|
+
const increment = () => { count.value++ }
|
|
49
63
|
|
|
50
64
|
return html`
|
|
51
65
|
<p>count ${count()}</p>
|
|
52
|
-
<button @click="${
|
|
66
|
+
<button @click="${increment}">+</button>
|
|
53
67
|
`
|
|
54
68
|
})
|
|
55
69
|
```
|
|
56
|
-
- each view renders into a `<sly-view>` host
|
|
57
|
-
- **
|
|
70
|
+
- each view renders into a `<sly-view view="counter">` host (where "counter" is the `use.name` you provided)
|
|
71
|
+
- **inject a view into the dom**
|
|
58
72
|
```ts
|
|
59
|
-
|
|
60
|
-
|
|
73
|
+
$.render($(".app"), html`
|
|
74
|
+
<h1>my cool counter demo</h1>
|
|
61
75
|
|
|
62
|
-
const content = html`
|
|
63
|
-
<h1>my demo page</h1>
|
|
64
76
|
${CounterView(1)}
|
|
65
|
-
`
|
|
66
|
-
|
|
67
|
-
|
|
77
|
+
`)
|
|
78
|
+
```
|
|
79
|
+
- 🤯 **register view as a web component**
|
|
80
|
+
```ts
|
|
81
|
+
$.register({MyCounter: CounterView.component(1)})
|
|
82
|
+
// <my-counter></my-counter>
|
|
68
83
|
```
|
|
69
84
|
|
|
70
85
|
### 🍋 view declaration settings
|
|
71
86
|
- special settings for views at declaration-time
|
|
72
87
|
```ts
|
|
73
|
-
import {view} from "@e280/sly"
|
|
74
|
-
import {html} from "lit"
|
|
75
|
-
|
|
76
88
|
export const CoolView = view
|
|
77
89
|
.settings({mode: "open", delegatesFocus: true})
|
|
78
90
|
.view(use => (greeting: string) => {
|
|
@@ -86,24 +98,39 @@ sly views are wired to automatically rerender whenever they're using any state s
|
|
|
86
98
|
### 🍋 view injection options
|
|
87
99
|
- options for views at the template injection site
|
|
88
100
|
```ts
|
|
89
|
-
|
|
90
|
-
import {CoolView} from "./cool-view.js"
|
|
91
|
-
|
|
92
|
-
const content = html`
|
|
101
|
+
$.render($(".app"), html`
|
|
93
102
|
<h2>super cool example</h2>
|
|
94
103
|
${CoolView
|
|
95
104
|
.attr("class", "hero")
|
|
96
105
|
.children(html`<em>spongebob</em>`)
|
|
97
106
|
.props("hello")}
|
|
98
|
-
`
|
|
99
|
-
|
|
100
|
-
render(content, document.querySelector(".app")!)
|
|
107
|
+
`)
|
|
101
108
|
```
|
|
102
109
|
- `attr` — set html attributes on the `<sly-view>` host element
|
|
103
110
|
- `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)
|
|
104
111
|
- `props` — finally inject the view by providing its props
|
|
105
112
|
|
|
106
|
-
### 🍋
|
|
113
|
+
### 🍋 web components
|
|
114
|
+
- **build a component directly**
|
|
115
|
+
```ts
|
|
116
|
+
const MyComponent = view.component(use => html`hello world`)
|
|
117
|
+
```
|
|
118
|
+
- notice that components don't take props
|
|
119
|
+
- **convert any view into a web component**
|
|
120
|
+
```ts
|
|
121
|
+
const MyCounter = CounterView.component(1)
|
|
122
|
+
```
|
|
123
|
+
- to convert a view to a component, you provide props
|
|
124
|
+
- note that the component instance has a render method like `element.render(2)` which can take new props
|
|
125
|
+
- **register web components to the dom**
|
|
126
|
+
```ts
|
|
127
|
+
$.register({MyComponent, MyCounter})
|
|
128
|
+
// <my-component></my-component>
|
|
129
|
+
// <my-counter></my-counter>
|
|
130
|
+
```
|
|
131
|
+
- `$.register` automatically dashes the tag names (`MyComponent` becomes `<my-component>`)
|
|
132
|
+
|
|
133
|
+
### 🍋 view "use" reference
|
|
107
134
|
- **use.name** — set the "view" attr value, eg `<sly-view view="squarepants">`
|
|
108
135
|
```ts
|
|
109
136
|
use.name("squarepants")
|
|
@@ -117,19 +144,19 @@ sly views are wired to automatically rerender whenever they're using any state s
|
|
|
117
144
|
const count = use.signal(1)
|
|
118
145
|
|
|
119
146
|
// read the signal
|
|
120
|
-
count()
|
|
147
|
+
count() // 1
|
|
121
148
|
|
|
122
149
|
// write the signal
|
|
123
150
|
count(2)
|
|
124
151
|
```
|
|
125
|
-
- **use.once** — run fn at initialization
|
|
152
|
+
- **use.once** — run fn at initialization, and return a value
|
|
126
153
|
```ts
|
|
127
154
|
const whatever = use.once(() => {
|
|
128
155
|
console.log("happens only once")
|
|
129
156
|
return 123
|
|
130
157
|
})
|
|
131
158
|
|
|
132
|
-
whatever
|
|
159
|
+
whatever // 123
|
|
133
160
|
```
|
|
134
161
|
- **use.mount** — setup mount/unmount lifecycle
|
|
135
162
|
```ts
|
|
@@ -141,6 +168,15 @@ sly views are wired to automatically rerender whenever they're using any state s
|
|
|
141
168
|
}
|
|
142
169
|
})
|
|
143
170
|
```
|
|
171
|
+
- **use.wake** — run fn each time mounted, and return value
|
|
172
|
+
```ts
|
|
173
|
+
const whatever = use.wake(() => {
|
|
174
|
+
console.log("view mounted")
|
|
175
|
+
return 123
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
whatever // 123
|
|
179
|
+
```
|
|
144
180
|
- **use.life** — mount/unmount lifecycle, but also return a value
|
|
145
181
|
```ts
|
|
146
182
|
const v = use.life(() => {
|
|
@@ -149,22 +185,47 @@ sly views are wired to automatically rerender whenever they're using any state s
|
|
|
149
185
|
return [value, () => console.log("unmounted")]
|
|
150
186
|
})
|
|
151
187
|
|
|
152
|
-
v
|
|
188
|
+
v // 123
|
|
153
189
|
```
|
|
154
|
-
- **use.
|
|
190
|
+
- **use.attrs** — ergonomic typed html attribute access
|
|
191
|
+
```ts
|
|
192
|
+
const attrs = use.attrs({
|
|
193
|
+
name: String,
|
|
194
|
+
count: Number,
|
|
195
|
+
active: Boolean,
|
|
196
|
+
})
|
|
197
|
+
```
|
|
198
|
+
```ts
|
|
199
|
+
attrs.name // "chase"
|
|
200
|
+
attrs.count // 123
|
|
201
|
+
attrs.active // true
|
|
202
|
+
```
|
|
203
|
+
```ts
|
|
204
|
+
attrs.name = "zenky"
|
|
205
|
+
attrs.count = 124
|
|
206
|
+
attrs.active = false // removes html attr
|
|
207
|
+
```
|
|
208
|
+
```ts
|
|
209
|
+
attrs.name = undefined // removes the attr
|
|
210
|
+
```
|
|
211
|
+
- **use.render** — rerender the view (debounced)
|
|
155
212
|
```ts
|
|
156
213
|
use.render()
|
|
157
214
|
```
|
|
215
|
+
- **use.renderNow** — rerender the view instantly (not debounced)
|
|
216
|
+
```ts
|
|
217
|
+
use.renderNow()
|
|
218
|
+
```
|
|
158
219
|
- **use.rendered** — promise that resolves *after* the next render
|
|
159
220
|
```ts
|
|
160
221
|
use.rendered.then(() => {
|
|
161
|
-
const slot = use.shadow.querySelector("slot")
|
|
222
|
+
const slot = use.shadow.querySelector("slot")
|
|
162
223
|
console.log(slot)
|
|
163
224
|
})
|
|
164
225
|
```
|
|
165
|
-
- **use.op
|
|
226
|
+
- **use.op** — start with an op based on an async fn
|
|
166
227
|
```ts
|
|
167
|
-
const op = use.op
|
|
228
|
+
const op = use.op(async() => {
|
|
168
229
|
await nap(5000)
|
|
169
230
|
return 123
|
|
170
231
|
})
|
|
@@ -174,30 +235,7 @@ sly views are wired to automatically rerender whenever they're using any state s
|
|
|
174
235
|
const op = use.op.promise(doAsyncWork())
|
|
175
236
|
```
|
|
176
237
|
|
|
177
|
-
### 🍋
|
|
178
|
-
- convert any view into a proper web component
|
|
179
|
-
```ts
|
|
180
|
-
CounterView.component(1)
|
|
181
|
-
```
|
|
182
|
-
- or build a component directly
|
|
183
|
-
```ts
|
|
184
|
-
const MyComponent = view(use => html`hello world`)
|
|
185
|
-
```
|
|
186
|
-
- register web components to the dom like this
|
|
187
|
-
```ts
|
|
188
|
-
import {$} from "@e280/sly"
|
|
189
|
-
|
|
190
|
-
$.register({
|
|
191
|
-
MyCounter: CounterView.component(1),
|
|
192
|
-
MyComponent,
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
// <my-counter></my-counter>
|
|
196
|
-
// <my-component></my-component>
|
|
197
|
-
```
|
|
198
|
-
- `$.register` automatically dashes the tag names (`MyComponent` becomes `<my-component>`)
|
|
199
|
-
|
|
200
|
-
### 🍋 neat tricks to impress the ladies
|
|
238
|
+
### 🍋 view "use" recipes
|
|
201
239
|
- make a ticker — mount, repeat, and nap
|
|
202
240
|
```ts
|
|
203
241
|
import {repeat, nap} from "@e280/stz"
|
|
@@ -210,65 +248,183 @@ sly views are wired to automatically rerender whenever they're using any state s
|
|
|
210
248
|
seconds.value++
|
|
211
249
|
}))
|
|
212
250
|
```
|
|
213
|
-
-
|
|
251
|
+
- wake + rendered, to do something after each mount's first render
|
|
214
252
|
```ts
|
|
215
|
-
use.
|
|
253
|
+
use.wake(() => use.rendered.then(() => {
|
|
216
254
|
console.log("after first render")
|
|
217
255
|
}))
|
|
218
256
|
```
|
|
219
257
|
|
|
220
258
|
<br/>
|
|
221
259
|
|
|
222
|
-
## 🦝 SLY
|
|
223
|
-
|
|
260
|
+
## 🦝 SLY `$` DOM MULTITOOL
|
|
261
|
+
|
|
262
|
+
### 💲 follow the money
|
|
263
|
+
- import the dollarsign
|
|
224
264
|
```ts
|
|
225
265
|
import {$} from "@e280/sly"
|
|
226
266
|
```
|
|
227
|
-
|
|
267
|
+
|
|
268
|
+
### 💲 dom queries
|
|
269
|
+
- require an element
|
|
228
270
|
```ts
|
|
229
271
|
$(".demo")
|
|
230
|
-
// HTMLElement
|
|
272
|
+
// HTMLElement (or throws error)
|
|
231
273
|
```
|
|
232
|
-
|
|
233
|
-
### queries
|
|
234
|
-
- query an element (undefined if not found)
|
|
274
|
+
- request an element
|
|
235
275
|
```ts
|
|
236
276
|
$.maybe(".demo")
|
|
237
277
|
// HTMLElement | undefined
|
|
238
278
|
```
|
|
239
|
-
- query all elements
|
|
279
|
+
- query all elements
|
|
240
280
|
```ts
|
|
241
|
-
$.
|
|
242
|
-
|
|
281
|
+
for (const item of $.all("ul li"))
|
|
282
|
+
console.log(item)
|
|
243
283
|
```
|
|
244
|
-
-
|
|
284
|
+
- specify what element to query under
|
|
245
285
|
```ts
|
|
246
|
-
|
|
247
|
-
// HTMLElement
|
|
286
|
+
$("li", listElement)
|
|
287
|
+
// HTMLElement
|
|
248
288
|
```
|
|
249
289
|
|
|
250
|
-
### dom
|
|
290
|
+
### 💲 dom utilities
|
|
291
|
+
- render content into an element
|
|
292
|
+
```ts
|
|
293
|
+
$.render(element, html`hello world`)
|
|
294
|
+
```
|
|
251
295
|
- register web components
|
|
252
296
|
```ts
|
|
253
297
|
$.register({MyComponent, AnotherCoolComponent})
|
|
254
298
|
// <my-component>
|
|
255
299
|
// <another-cool-component>
|
|
256
300
|
```
|
|
257
|
-
- render content into an element
|
|
258
|
-
```ts
|
|
259
|
-
$.render(element, html`hello world`)
|
|
260
|
-
```
|
|
261
301
|
|
|
262
302
|
<br/>
|
|
263
303
|
|
|
264
304
|
## 🦝 SLY OPS, PODS, AND LOADERS
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
305
|
+
async operations and displaying loading spinners.
|
|
306
|
+
|
|
307
|
+
```ts
|
|
308
|
+
import {nap} from "@e280/stz"
|
|
309
|
+
import {Pod, podium, Op, makeLoader, anims} from "@e280/sly"
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### 🫛 pods: loading/ready/error data
|
|
313
|
+
- a pod represents an async operation
|
|
314
|
+
- pods are simple json-serializable data
|
|
315
|
+
- there are three kinds of `Pod<V>`
|
|
316
|
+
```ts
|
|
317
|
+
// loading pod
|
|
318
|
+
["loading"]
|
|
319
|
+
|
|
320
|
+
// ready pod contains value 123
|
|
321
|
+
["ready", 123]
|
|
322
|
+
|
|
323
|
+
// error pod contains an error
|
|
324
|
+
["error", new Error()]
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### 🫛 podium: helps you work with pods
|
|
328
|
+
- get pod status
|
|
329
|
+
```ts
|
|
330
|
+
podium.status(["ready", 123])
|
|
331
|
+
// "ready"
|
|
332
|
+
```
|
|
333
|
+
- get pod ready value (or undefined)
|
|
334
|
+
```ts
|
|
335
|
+
podium.value(["loading"])
|
|
336
|
+
// undefined
|
|
337
|
+
|
|
338
|
+
podium.value(["ready", 123])
|
|
339
|
+
// 123
|
|
340
|
+
```
|
|
341
|
+
- see more at [podium.ts](./s/ops/podium.ts)
|
|
342
|
+
|
|
343
|
+
### 🫛 ops: nice pod ergonomics
|
|
344
|
+
- an `Op<V>` wraps a pod with a signal for reactivity
|
|
345
|
+
- create an op
|
|
346
|
+
```ts
|
|
347
|
+
const op = new Op<number>() // loading status by default
|
|
348
|
+
```
|
|
349
|
+
```ts
|
|
350
|
+
const op = Op.loading<number>()
|
|
351
|
+
```
|
|
352
|
+
```ts
|
|
353
|
+
const op = Op.ready<number>(123)
|
|
354
|
+
```
|
|
355
|
+
```ts
|
|
356
|
+
const op = Op.error<number>(new Error())
|
|
357
|
+
```
|
|
358
|
+
- 🔥 create an op that calls and tracks an async fn
|
|
359
|
+
```ts
|
|
360
|
+
const op = Op.fn(async() => {
|
|
361
|
+
await nap(4000)
|
|
362
|
+
return 123
|
|
363
|
+
})
|
|
364
|
+
```
|
|
365
|
+
- await for the next ready value (or thrown error)
|
|
366
|
+
```ts
|
|
367
|
+
await op // 123
|
|
368
|
+
```
|
|
369
|
+
- get pod info
|
|
370
|
+
```ts
|
|
371
|
+
op.status // "loading"
|
|
372
|
+
op.pod // ["loading"]
|
|
373
|
+
op.value // undefined (or value if ready)
|
|
374
|
+
```
|
|
375
|
+
```ts
|
|
376
|
+
op.isLoading // true
|
|
377
|
+
op.isReady // false
|
|
378
|
+
op.isError // false
|
|
379
|
+
```
|
|
380
|
+
- select executes a fn based on the status
|
|
381
|
+
```ts
|
|
382
|
+
const result = op.select({
|
|
383
|
+
loading: () => "it's loading...",
|
|
384
|
+
ready: value => `dude, it's ready! ${value}`,
|
|
385
|
+
error: err => `dude, there's an error!`,
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
result
|
|
389
|
+
// "dude, it's ready! 123"
|
|
390
|
+
```
|
|
391
|
+
- morph returns a new pod, transforming the value if ready
|
|
392
|
+
```ts
|
|
393
|
+
op.morph(n => n + 1)
|
|
394
|
+
// ["ready", 124]
|
|
395
|
+
```
|
|
396
|
+
- you can combine a number of ops into a single pod like this
|
|
397
|
+
```ts
|
|
398
|
+
Op.all(Op.ready(123), Op.loading())
|
|
399
|
+
// ["loading"]
|
|
400
|
+
```
|
|
401
|
+
```ts
|
|
402
|
+
Op.all(Op.ready(1), Op.ready(2), Op.ready(3))
|
|
403
|
+
// ["ready", [1, 2, 3]]
|
|
404
|
+
```
|
|
405
|
+
- error if any ops are in error, otherwise
|
|
406
|
+
- loading if any ops are in loading, otherwise
|
|
407
|
+
- ready if all the ops are ready
|
|
408
|
+
|
|
409
|
+
### 🫛 loaders: animated loading spinners
|
|
410
|
+
- create a `loader` using `makeLoader`
|
|
411
|
+
```ts
|
|
412
|
+
const loader = makeLoader(anims.dots)
|
|
413
|
+
```
|
|
414
|
+
- see all the anims available on the testing page https://sly.e280.org/
|
|
415
|
+
- use the loader to render your op
|
|
416
|
+
```ts
|
|
417
|
+
return html`
|
|
418
|
+
<h2>cool stuff</h2>
|
|
419
|
+
|
|
420
|
+
${loader(op, value => html`
|
|
421
|
+
<div>${value}</div>
|
|
422
|
+
`)}
|
|
423
|
+
`
|
|
424
|
+
```
|
|
425
|
+
- when the op is loading, the loading spinner will animate
|
|
426
|
+
- when the op is in error, the error will be displayed
|
|
427
|
+
- when the op is ready, your fn is called and given the value
|
|
272
428
|
|
|
273
429
|
<br/>
|
|
274
430
|
|
package/package.json
CHANGED
package/s/demo/demo.bundle.ts
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
|
|
2
|
+
import {$} from "../dom/dollar.js"
|
|
2
3
|
import {DemoView} from "./views/demo.js"
|
|
3
|
-
import {$} from "../features/dom/dollar.js"
|
|
4
4
|
import {CounterView} from "./views/counter.js"
|
|
5
5
|
|
|
6
6
|
$.render($(".demo"), DemoView())
|
|
7
|
-
|
|
8
|
-
$.register({
|
|
9
|
-
DemoCounter: CounterView.component(1),
|
|
10
|
-
})
|
|
7
|
+
$.register({DemoCounter: CounterView.component(1)})
|
|
11
8
|
|
|
12
9
|
console.log("🦝 sly")
|
|
13
10
|
|
package/s/demo/views/counter.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import {css, html} from "lit"
|
|
3
3
|
import {repeat} from "@e280/stz"
|
|
4
4
|
|
|
5
|
-
import {view} from "../../
|
|
6
|
-
import {cssReset} from "../../
|
|
5
|
+
import {view} from "../../views/view.js"
|
|
6
|
+
import {cssReset} from "../../views/css-reset.js"
|
|
7
7
|
|
|
8
8
|
export const CounterView = view(use => (initial: number) => {
|
|
9
9
|
use.name("counter")
|
package/s/demo/views/demo.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
|
|
2
2
|
import {css, html} from "lit"
|
|
3
|
+
import {view} from "../../views/view.js"
|
|
3
4
|
import {CounterView} from "./counter.js"
|
|
4
5
|
import {LoadersView} from "./loaders.js"
|
|
5
|
-
import {
|
|
6
|
-
import {cssReset} from "../../features/views/css-reset.js"
|
|
6
|
+
import {cssReset} from "../../views/css-reset.js"
|
|
7
7
|
|
|
8
8
|
export const DemoView = view(use => () => {
|
|
9
9
|
use.name("demo")
|
package/s/demo/views/loaders.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
|
|
2
2
|
import {css, html} from "lit"
|
|
3
|
-
import {Op} from "../../
|
|
4
|
-
import {view} from "../../
|
|
5
|
-
import {cssReset} from "../../
|
|
6
|
-
import {anims, makeLoader} from "../../
|
|
3
|
+
import {Op} from "../../ops/op.js"
|
|
4
|
+
import {view} from "../../views/view.js"
|
|
5
|
+
import {cssReset} from "../../views/css-reset.js"
|
|
6
|
+
import {anims, makeLoader} from "../../ops/loaders/make-loader.js"
|
|
7
7
|
|
|
8
8
|
export const LoadersView = view(use => () => {
|
|
9
9
|
use.name("loaders")
|
package/s/index.ts
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
|
|
2
|
-
export * from "./
|
|
3
|
-
export * from "./
|
|
4
|
-
export * from "./
|
|
5
|
-
export * from "./
|
|
2
|
+
export * from "./dom/dashify.js"
|
|
3
|
+
export * from "./dom/dollar.js"
|
|
4
|
+
export * from "./dom/register.js"
|
|
5
|
+
export * from "./dom/types.js"
|
|
6
6
|
|
|
7
|
-
export * from "./
|
|
8
|
-
export * from "./
|
|
9
|
-
export * from "./
|
|
7
|
+
export * from "./ops/loaders/make-loader.js"
|
|
8
|
+
export * from "./ops/loaders/parts/ascii-anim.js"
|
|
9
|
+
export * from "./ops/loaders/parts/error-display.js"
|
|
10
10
|
|
|
11
|
-
export * from "./
|
|
12
|
-
export * from "./
|
|
13
|
-
export * from "./
|
|
11
|
+
export * from "./ops/op.js"
|
|
12
|
+
export * from "./ops/podium.js"
|
|
13
|
+
export * from "./ops/types.js"
|
|
14
14
|
|
|
15
|
-
export * from "./
|
|
16
|
-
export * from "./
|
|
17
|
-
export * from "./
|
|
18
|
-
export * from "./
|
|
15
|
+
export * from "./views/attributes.js"
|
|
16
|
+
export * from "./views/css-reset.js"
|
|
17
|
+
export * from "./views/types.js"
|
|
18
|
+
export * from "./views/use.js"
|
|
19
|
+
export * from "./views/view.js"
|
|
19
20
|
|
|
@@ -22,11 +22,11 @@ export class Op<V> {
|
|
|
22
22
|
|
|
23
23
|
static all<V>(...ops: Op<V>[]) {
|
|
24
24
|
const pods = ops.map(op => op.pod)
|
|
25
|
-
|
|
26
|
-
return new this(pod)
|
|
25
|
+
return podium.all(...pods)
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
readonly signal: Signal<Pod<V>>
|
|
29
|
+
#count = 0
|
|
30
30
|
#resolve = pub<[V]>()
|
|
31
31
|
#reject = pub<[any]>()
|
|
32
32
|
|
|
@@ -35,12 +35,16 @@ export class Op<V> {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
get wait() {
|
|
38
|
-
return new Promise((resolve, reject) => {
|
|
39
|
-
this.#resolve.next().then(resolve)
|
|
40
|
-
this.#reject.next().then(reject)
|
|
38
|
+
return new Promise<V>((resolve, reject) => {
|
|
39
|
+
this.#resolve.next().then(([v]) => resolve(v))
|
|
40
|
+
this.#reject.next().then(([e]) => reject(e))
|
|
41
41
|
})
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
get then() { return this.wait.then.bind(this.wait) }
|
|
45
|
+
get catch() { return this.wait.catch.bind(this.wait) }
|
|
46
|
+
get finally() { return this.wait.finally.bind(this.wait) }
|
|
47
|
+
|
|
44
48
|
async setLoading() {
|
|
45
49
|
await this.signal(["loading"])
|
|
46
50
|
}
|
|
@@ -56,14 +60,17 @@ export class Op<V> {
|
|
|
56
60
|
}
|
|
57
61
|
|
|
58
62
|
async promise(promise: Promise<V>) {
|
|
63
|
+
const count = ++this.#count
|
|
59
64
|
await this.setLoading()
|
|
60
65
|
try {
|
|
61
66
|
const value = await promise
|
|
62
|
-
|
|
67
|
+
if (count === this.#count)
|
|
68
|
+
await this.setReady(value)
|
|
63
69
|
return value
|
|
64
70
|
}
|
|
65
71
|
catch (error) {
|
|
66
|
-
|
|
72
|
+
if (count === this.#count)
|
|
73
|
+
await this.setError(error)
|
|
67
74
|
}
|
|
68
75
|
}
|
|
69
76
|
|
|
@@ -114,7 +121,7 @@ export class Op<V> {
|
|
|
114
121
|
}
|
|
115
122
|
|
|
116
123
|
morph<V2>(fn: (value: V) => V2) {
|
|
117
|
-
return
|
|
124
|
+
return podium.morph(this.pod, fn)
|
|
118
125
|
}
|
|
119
126
|
}
|
|
120
127
|
|