@e280/sly 0.0.0-6 → 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 +281 -53
- package/package.json +1 -1
- package/s/demo/demo.bundle.ts +3 -1
- package/s/demo/views/counter.ts +10 -6
- package/s/demo/views/demo.ts +3 -3
- package/s/demo/views/loaders.ts +4 -4
- package/s/{features/dom → dom}/dollar.ts +2 -0
- package/s/index.html.ts +1 -0
- 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}/types.ts +7 -0
- package/s/{features/views → views}/use.ts +24 -7
- package/s/{features/views → views}/view.ts +45 -15
- package/x/demo/demo.bundle.js +3 -1
- package/x/demo/demo.bundle.js.map +1 -1
- package/x/demo/demo.bundle.min.js +23 -19
- package/x/demo/demo.bundle.min.js.map +4 -4
- package/x/demo/views/counter.d.ts +1 -1
- package/x/demo/views/counter.js +10 -6
- package/x/demo/views/counter.js.map +1 -1
- package/x/demo/views/demo.js +3 -3
- 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/{features/dom → dom}/dollar.d.ts +1 -0
- package/x/{features/dom → dom}/dollar.js +2 -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 +3 -2
- package/x/index.html.js +1 -0
- package/x/index.html.js.map +1 -1
- 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.d.ts +6 -0
- package/x/{features/views → views}/types.js.map +1 -1
- package/x/{features/views → views}/use.d.ts +11 -6
- package/x/{features/views → views}/use.js +23 -7
- 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 -1
- package/x/{features/views → views}/view.js +32 -13
- 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}/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}/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}/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.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,56 +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.
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
view(use => () => "hello world")
|
|
37
|
+
```
|
|
26
38
|
|
|
27
|
-
views are
|
|
39
|
+
views are not web components.
|
|
28
40
|
|
|
29
|
-
|
|
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.
|
|
30
42
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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.
|
|
48
|
+
|
|
49
|
+
### 🍋 view example
|
|
50
|
+
- **import some stuff**
|
|
34
51
|
```ts
|
|
35
|
-
import {view} from "@e280/sly"
|
|
52
|
+
import {$, view} from "@e280/sly"
|
|
36
53
|
import {html, css} from "lit"
|
|
37
|
-
|
|
54
|
+
```
|
|
55
|
+
- **declaring a view**
|
|
56
|
+
```ts
|
|
38
57
|
export const CounterView = view(use => (start: number) => {
|
|
39
58
|
use.name("counter")
|
|
40
59
|
use.styles(css`p {color: green}`)
|
|
60
|
+
|
|
41
61
|
const count = use.signal(start)
|
|
62
|
+
const increment = () => { count.value++ }
|
|
42
63
|
|
|
43
64
|
return html`
|
|
44
65
|
<p>count ${count()}</p>
|
|
45
|
-
<button @click="${
|
|
66
|
+
<button @click="${increment}">+</button>
|
|
46
67
|
`
|
|
47
68
|
})
|
|
48
69
|
```
|
|
49
|
-
- each view renders into a `<sly-view>` host
|
|
50
|
-
- **
|
|
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**
|
|
51
72
|
```ts
|
|
52
|
-
|
|
53
|
-
|
|
73
|
+
$.render($(".app"), html`
|
|
74
|
+
<h1>my cool counter demo</h1>
|
|
54
75
|
|
|
55
|
-
const content = html`
|
|
56
|
-
<h1>my demo page</h1>
|
|
57
76
|
${CounterView(1)}
|
|
58
|
-
`
|
|
59
|
-
|
|
60
|
-
|
|
77
|
+
`)
|
|
78
|
+
```
|
|
79
|
+
- 🤯 **register view as a web component**
|
|
80
|
+
```ts
|
|
81
|
+
$.register({MyCounter: CounterView.component(1)})
|
|
82
|
+
// <my-counter></my-counter>
|
|
61
83
|
```
|
|
62
84
|
|
|
63
85
|
### 🍋 view declaration settings
|
|
64
86
|
- special settings for views at declaration-time
|
|
65
87
|
```ts
|
|
66
|
-
import {view} from "@e280/sly"
|
|
67
|
-
import {html} from "lit"
|
|
68
|
-
|
|
69
88
|
export const CoolView = view
|
|
70
89
|
.settings({mode: "open", delegatesFocus: true})
|
|
71
90
|
.view(use => (greeting: string) => {
|
|
@@ -79,24 +98,39 @@ sly views are wired to automatically rerender whenever they're using any state s
|
|
|
79
98
|
### 🍋 view injection options
|
|
80
99
|
- options for views at the template injection site
|
|
81
100
|
```ts
|
|
82
|
-
|
|
83
|
-
import {CoolView} from "./cool-view.js"
|
|
84
|
-
|
|
85
|
-
const content = html`
|
|
101
|
+
$.render($(".app"), html`
|
|
86
102
|
<h2>super cool example</h2>
|
|
87
103
|
${CoolView
|
|
88
104
|
.attr("class", "hero")
|
|
89
105
|
.children(html`<em>spongebob</em>`)
|
|
90
106
|
.props("hello")}
|
|
91
|
-
`
|
|
92
|
-
|
|
93
|
-
render(content, document.querySelector(".app")!)
|
|
107
|
+
`)
|
|
94
108
|
```
|
|
95
109
|
- `attr` — set html attributes on the `<sly-view>` host element
|
|
96
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)
|
|
97
111
|
- `props` — finally inject the view by providing its props
|
|
98
112
|
|
|
99
|
-
### 🍋
|
|
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
|
|
100
134
|
- **use.name** — set the "view" attr value, eg `<sly-view view="squarepants">`
|
|
101
135
|
```ts
|
|
102
136
|
use.name("squarepants")
|
|
@@ -110,19 +144,19 @@ sly views are wired to automatically rerender whenever they're using any state s
|
|
|
110
144
|
const count = use.signal(1)
|
|
111
145
|
|
|
112
146
|
// read the signal
|
|
113
|
-
count()
|
|
147
|
+
count() // 1
|
|
114
148
|
|
|
115
149
|
// write the signal
|
|
116
150
|
count(2)
|
|
117
151
|
```
|
|
118
|
-
- **use.once** — run fn at initialization
|
|
152
|
+
- **use.once** — run fn at initialization, and return a value
|
|
119
153
|
```ts
|
|
120
154
|
const whatever = use.once(() => {
|
|
121
155
|
console.log("happens only once")
|
|
122
156
|
return 123
|
|
123
157
|
})
|
|
124
158
|
|
|
125
|
-
whatever
|
|
159
|
+
whatever // 123
|
|
126
160
|
```
|
|
127
161
|
- **use.mount** — setup mount/unmount lifecycle
|
|
128
162
|
```ts
|
|
@@ -134,6 +168,15 @@ sly views are wired to automatically rerender whenever they're using any state s
|
|
|
134
168
|
}
|
|
135
169
|
})
|
|
136
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
|
+
```
|
|
137
180
|
- **use.life** — mount/unmount lifecycle, but also return a value
|
|
138
181
|
```ts
|
|
139
182
|
const v = use.life(() => {
|
|
@@ -142,22 +185,47 @@ sly views are wired to automatically rerender whenever they're using any state s
|
|
|
142
185
|
return [value, () => console.log("unmounted")]
|
|
143
186
|
})
|
|
144
187
|
|
|
145
|
-
v
|
|
188
|
+
v // 123
|
|
189
|
+
```
|
|
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
|
+
})
|
|
146
197
|
```
|
|
147
|
-
|
|
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)
|
|
148
212
|
```ts
|
|
149
213
|
use.render()
|
|
150
214
|
```
|
|
215
|
+
- **use.renderNow** — rerender the view instantly (not debounced)
|
|
216
|
+
```ts
|
|
217
|
+
use.renderNow()
|
|
218
|
+
```
|
|
151
219
|
- **use.rendered** — promise that resolves *after* the next render
|
|
152
220
|
```ts
|
|
153
221
|
use.rendered.then(() => {
|
|
154
|
-
const slot = use.shadow.querySelector("slot")
|
|
222
|
+
const slot = use.shadow.querySelector("slot")
|
|
155
223
|
console.log(slot)
|
|
156
224
|
})
|
|
157
225
|
```
|
|
158
|
-
- **use.op
|
|
226
|
+
- **use.op** — start with an op based on an async fn
|
|
159
227
|
```ts
|
|
160
|
-
const op = use.op
|
|
228
|
+
const op = use.op(async() => {
|
|
161
229
|
await nap(5000)
|
|
162
230
|
return 123
|
|
163
231
|
})
|
|
@@ -167,7 +235,7 @@ sly views are wired to automatically rerender whenever they're using any state s
|
|
|
167
235
|
const op = use.op.promise(doAsyncWork())
|
|
168
236
|
```
|
|
169
237
|
|
|
170
|
-
### 🍋
|
|
238
|
+
### 🍋 view "use" recipes
|
|
171
239
|
- make a ticker — mount, repeat, and nap
|
|
172
240
|
```ts
|
|
173
241
|
import {repeat, nap} from "@e280/stz"
|
|
@@ -180,23 +248,183 @@ sly views are wired to automatically rerender whenever they're using any state s
|
|
|
180
248
|
seconds.value++
|
|
181
249
|
}))
|
|
182
250
|
```
|
|
183
|
-
-
|
|
251
|
+
- wake + rendered, to do something after each mount's first render
|
|
184
252
|
```ts
|
|
185
|
-
use.
|
|
253
|
+
use.wake(() => use.rendered.then(() => {
|
|
186
254
|
console.log("after first render")
|
|
187
255
|
}))
|
|
188
256
|
```
|
|
189
257
|
|
|
190
258
|
<br/>
|
|
191
259
|
|
|
260
|
+
## 🦝 SLY `$` DOM MULTITOOL
|
|
261
|
+
|
|
262
|
+
### 💲 follow the money
|
|
263
|
+
- import the dollarsign
|
|
264
|
+
```ts
|
|
265
|
+
import {$} from "@e280/sly"
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### 💲 dom queries
|
|
269
|
+
- require an element
|
|
270
|
+
```ts
|
|
271
|
+
$(".demo")
|
|
272
|
+
// HTMLElement (or throws error)
|
|
273
|
+
```
|
|
274
|
+
- request an element
|
|
275
|
+
```ts
|
|
276
|
+
$.maybe(".demo")
|
|
277
|
+
// HTMLElement | undefined
|
|
278
|
+
```
|
|
279
|
+
- query all elements
|
|
280
|
+
```ts
|
|
281
|
+
for (const item of $.all("ul li"))
|
|
282
|
+
console.log(item)
|
|
283
|
+
```
|
|
284
|
+
- specify what element to query under
|
|
285
|
+
```ts
|
|
286
|
+
$("li", listElement)
|
|
287
|
+
// HTMLElement
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### 💲 dom utilities
|
|
291
|
+
- render content into an element
|
|
292
|
+
```ts
|
|
293
|
+
$.render(element, html`hello world`)
|
|
294
|
+
```
|
|
295
|
+
- register web components
|
|
296
|
+
```ts
|
|
297
|
+
$.register({MyComponent, AnotherCoolComponent})
|
|
298
|
+
// <my-component>
|
|
299
|
+
// <another-cool-component>
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
<br/>
|
|
303
|
+
|
|
192
304
|
## 🦝 SLY OPS, PODS, AND LOADERS
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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
|
|
200
428
|
|
|
201
429
|
<br/>
|
|
202
430
|
|
package/package.json
CHANGED
package/s/demo/demo.bundle.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
|
|
2
|
+
import {$} from "../dom/dollar.js"
|
|
2
3
|
import {DemoView} from "./views/demo.js"
|
|
3
|
-
import {
|
|
4
|
+
import {CounterView} from "./views/counter.js"
|
|
4
5
|
|
|
5
6
|
$.render($(".demo"), DemoView())
|
|
7
|
+
$.register({DemoCounter: CounterView.component(1)})
|
|
6
8
|
|
|
7
9
|
console.log("🦝 sly")
|
|
8
10
|
|
package/s/demo/views/counter.ts
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
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
|
-
export const CounterView = view(use => () => {
|
|
8
|
+
export const CounterView = view(use => (initial: number) => {
|
|
9
9
|
use.name("counter")
|
|
10
10
|
use.styles(cssReset, styles)
|
|
11
11
|
|
|
@@ -17,10 +17,11 @@ export const CounterView = view(use => () => {
|
|
|
17
17
|
seconds(Math.floor(since / 1000))
|
|
18
18
|
}))
|
|
19
19
|
|
|
20
|
-
const count = use.signal(
|
|
20
|
+
const count = use.signal(initial)
|
|
21
21
|
const increment = () => count(count() + 1)
|
|
22
22
|
|
|
23
23
|
return html`
|
|
24
|
+
<slot></slot>
|
|
24
25
|
<div>
|
|
25
26
|
<span>${seconds()}</span>
|
|
26
27
|
</div>
|
|
@@ -34,9 +35,12 @@ export const CounterView = view(use => () => {
|
|
|
34
35
|
const styles = css`
|
|
35
36
|
:host {
|
|
36
37
|
display: flex;
|
|
37
|
-
flex-direction: column;
|
|
38
38
|
justify-content: center;
|
|
39
|
-
|
|
39
|
+
gap: 1em;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
button {
|
|
43
|
+
padding: 0.2em 0.5em;
|
|
40
44
|
}
|
|
41
45
|
`
|
|
42
46
|
|
package/s/demo/views/demo.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
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")
|
|
10
10
|
use.styles(cssReset, styles)
|
|
11
11
|
|
|
12
12
|
return html`
|
|
13
|
-
${CounterView()}
|
|
13
|
+
${CounterView.children("view")(2)}
|
|
14
14
|
${LoadersView()}
|
|
15
15
|
`
|
|
16
16
|
})
|
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")
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import {render} from "lit"
|
|
3
|
+
import {register} from "./register.js"
|
|
3
4
|
import {Content} from "../views/types.js"
|
|
4
5
|
|
|
5
6
|
export type Container = HTMLElement | ShadowRoot | DocumentFragment
|
|
@@ -22,4 +23,5 @@ $.maybe = <E extends HTMLElement = HTMLElement>(selector: string, context: Query
|
|
|
22
23
|
$.all = all
|
|
23
24
|
|
|
24
25
|
$.render = (container: Container, ...content: Content[]) => render(content, container)
|
|
26
|
+
$.register = register
|
|
25
27
|
|
package/s/index.html.ts
CHANGED
|
@@ -29,6 +29,7 @@ export default ssg.page(import.meta.url, async orb => ({
|
|
|
29
29
|
<h1>sly testing page</h1>
|
|
30
30
|
<p><a href="https://github.com/e280/sly">github.com/e280/sly</a></p>
|
|
31
31
|
<p class=lil>v${orb.packageVersion()}</p>
|
|
32
|
+
<demo-counter>component</demo-counter>
|
|
32
33
|
<div class=demo></div>
|
|
33
34
|
`,
|
|
34
35
|
}))
|
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
|
|