@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.
Files changed (117) hide show
  1. package/README.md +281 -53
  2. package/package.json +1 -1
  3. package/s/demo/demo.bundle.ts +3 -1
  4. package/s/demo/views/counter.ts +10 -6
  5. package/s/demo/views/demo.ts +3 -3
  6. package/s/demo/views/loaders.ts +4 -4
  7. package/s/{features/dom → dom}/dollar.ts +2 -0
  8. package/s/index.html.ts +1 -0
  9. package/s/index.ts +15 -14
  10. package/s/{features/op → ops}/op.ts +15 -8
  11. package/s/views/attributes.ts +89 -0
  12. package/s/{features/views → views}/types.ts +7 -0
  13. package/s/{features/views → views}/use.ts +24 -7
  14. package/s/{features/views → views}/view.ts +45 -15
  15. package/x/demo/demo.bundle.js +3 -1
  16. package/x/demo/demo.bundle.js.map +1 -1
  17. package/x/demo/demo.bundle.min.js +23 -19
  18. package/x/demo/demo.bundle.min.js.map +4 -4
  19. package/x/demo/views/counter.d.ts +1 -1
  20. package/x/demo/views/counter.js +10 -6
  21. package/x/demo/views/counter.js.map +1 -1
  22. package/x/demo/views/demo.js +3 -3
  23. package/x/demo/views/demo.js.map +1 -1
  24. package/x/demo/views/loaders.js +4 -4
  25. package/x/demo/views/loaders.js.map +1 -1
  26. package/x/dom/dashify.js.map +1 -0
  27. package/x/{features/dom → dom}/dollar.d.ts +1 -0
  28. package/x/{features/dom → dom}/dollar.js +2 -0
  29. package/x/dom/dollar.js.map +1 -0
  30. package/x/dom/register.js.map +1 -0
  31. package/x/{features/op → dom}/types.js.map +1 -1
  32. package/x/index.d.ts +15 -14
  33. package/x/index.html +3 -2
  34. package/x/index.html.js +1 -0
  35. package/x/index.html.js.map +1 -1
  36. package/x/index.js +15 -14
  37. package/x/index.js.map +1 -1
  38. package/x/ops/loaders/make-loader.js.map +1 -0
  39. package/x/ops/loaders/parts/anims.js.map +1 -0
  40. package/x/ops/loaders/parts/ascii-anim.js.map +1 -0
  41. package/x/ops/loaders/parts/error-display.d.ts +1 -0
  42. package/x/ops/loaders/parts/error-display.js.map +1 -0
  43. package/x/{features/op → ops}/op.d.ts +6 -3
  44. package/x/{features/op → ops}/op.js +13 -7
  45. package/x/ops/op.js.map +1 -0
  46. package/x/ops/podium.js.map +1 -0
  47. package/x/{features/dom → ops}/types.js.map +1 -1
  48. package/x/views/attributes.d.ts +10 -0
  49. package/x/views/attributes.js +46 -0
  50. package/x/views/attributes.js.map +1 -0
  51. package/x/views/css-reset.js.map +1 -0
  52. package/x/{features/views → views}/types.d.ts +6 -0
  53. package/x/{features/views → views}/types.js.map +1 -1
  54. package/x/{features/views → views}/use.d.ts +11 -6
  55. package/x/{features/views → views}/use.js +23 -7
  56. package/x/views/use.js.map +1 -0
  57. package/x/views/utils/apply-attrs.js.map +1 -0
  58. package/x/views/utils/apply-styles.js.map +1 -0
  59. package/x/views/utils/mounts.js.map +1 -0
  60. package/x/{features/views → views}/view.d.ts +2 -1
  61. package/x/{features/views → views}/view.js +32 -13
  62. package/x/views/view.js.map +1 -0
  63. package/x/features/dom/dashify.js.map +0 -1
  64. package/x/features/dom/dollar.js.map +0 -1
  65. package/x/features/dom/register.js.map +0 -1
  66. package/x/features/op/loaders/make-loader.js.map +0 -1
  67. package/x/features/op/loaders/parts/anims.js.map +0 -1
  68. package/x/features/op/loaders/parts/ascii-anim.js.map +0 -1
  69. package/x/features/op/loaders/parts/error-display.d.ts +0 -1
  70. package/x/features/op/loaders/parts/error-display.js.map +0 -1
  71. package/x/features/op/op.js.map +0 -1
  72. package/x/features/op/podium.js.map +0 -1
  73. package/x/features/views/css-reset.js.map +0 -1
  74. package/x/features/views/use.js.map +0 -1
  75. package/x/features/views/utils/apply-attrs.js.map +0 -1
  76. package/x/features/views/utils/apply-styles.js.map +0 -1
  77. package/x/features/views/utils/mounts.js.map +0 -1
  78. package/x/features/views/view.js.map +0 -1
  79. /package/s/{features/dom → dom}/dashify.ts +0 -0
  80. /package/s/{features/dom → dom}/register.ts +0 -0
  81. /package/s/{features/dom → dom}/types.ts +0 -0
  82. /package/s/{features/op → ops}/loaders/make-loader.ts +0 -0
  83. /package/s/{features/op → ops}/loaders/parts/anims.ts +0 -0
  84. /package/s/{features/op → ops}/loaders/parts/ascii-anim.ts +0 -0
  85. /package/s/{features/op → ops}/loaders/parts/error-display.ts +0 -0
  86. /package/s/{features/op → ops}/podium.ts +0 -0
  87. /package/s/{features/op → ops}/types.ts +0 -0
  88. /package/s/{features/views → views}/css-reset.ts +0 -0
  89. /package/s/{features/views → views}/utils/apply-attrs.ts +0 -0
  90. /package/s/{features/views → views}/utils/apply-styles.ts +0 -0
  91. /package/s/{features/views → views}/utils/mounts.ts +0 -0
  92. /package/x/{features/dom → dom}/dashify.d.ts +0 -0
  93. /package/x/{features/dom → dom}/dashify.js +0 -0
  94. /package/x/{features/dom → dom}/register.d.ts +0 -0
  95. /package/x/{features/dom → dom}/register.js +0 -0
  96. /package/x/{features/dom → dom}/types.d.ts +0 -0
  97. /package/x/{features/dom → dom}/types.js +0 -0
  98. /package/x/{features/op → ops}/loaders/make-loader.d.ts +0 -0
  99. /package/x/{features/op → ops}/loaders/make-loader.js +0 -0
  100. /package/x/{features/op → ops}/loaders/parts/anims.d.ts +0 -0
  101. /package/x/{features/op → ops}/loaders/parts/anims.js +0 -0
  102. /package/x/{features/op → ops}/loaders/parts/ascii-anim.d.ts +0 -0
  103. /package/x/{features/op → ops}/loaders/parts/ascii-anim.js +0 -0
  104. /package/x/{features/op → ops}/loaders/parts/error-display.js +0 -0
  105. /package/x/{features/op → ops}/podium.d.ts +0 -0
  106. /package/x/{features/op → ops}/podium.js +0 -0
  107. /package/x/{features/op → ops}/types.d.ts +0 -0
  108. /package/x/{features/op → ops}/types.js +0 -0
  109. /package/x/{features/views → views}/css-reset.d.ts +0 -0
  110. /package/x/{features/views → views}/css-reset.js +0 -0
  111. /package/x/{features/views → views}/types.js +0 -0
  112. /package/x/{features/views → views}/utils/apply-attrs.d.ts +0 -0
  113. /package/x/{features/views → views}/utils/apply-attrs.js +0 -0
  114. /package/x/{features/views → views}/utils/apply-styles.d.ts +0 -0
  115. /package/x/{features/views → views}/utils/apply-styles.js +0 -0
  116. /package/x/{features/views → views}/utils/mounts.d.ts +0 -0
  117. /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
- - 🪒 lean view framework for [lit](https://lit.dev/) web devs
8
- - 🌅 sly is the successor to [@benev/slate](https://github.com/benevolent-games/slate)
9
- - 🏂 commonly used with stz standard library [@e280/stz](https://github.com/e280/stz)
10
- - ⛏️ integrates signals and state trees from [@e280/strata](https://github.com/e280/strata)
11
- - 🐢 if you need a buildy-bundler-buddy, try [scute](https://github.com/e280/scute)
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/stz @e280/strata lit
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. not components.
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 leaner than web components.. no dom registration, no string tag names.. just import 'em, and the types work.. web components are fine, but they're for providing html authors with entrypoints to your cool widgets.. whereas views are the building blocks for frontend app devs.
39
+ views are not web components.
28
40
 
29
- sly views are wired to automatically rerender whenever they're using any state stuff from [@e280/strata](https://github.com/e280/strata).
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
- ### 🍋 basic view example
32
- - views are hooks-based functional components with a [shadow root](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM)
33
- - **declaring a view**
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="${() => { count.value++ }}">+</button>
66
+ <button @click="${increment}">+</button>
46
67
  `
47
68
  })
48
69
  ```
49
- - each view renders into a `<sly-view>` host, with the provided `name` set as its view attribute, eg `<sly-view view="counter">`
50
- - **injecting a view into the dom**
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
- import {render, html} from "lit"
53
- import {CounterView} from "./my-counter.js"
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
- render(content, document.querySelector(".app")!)
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
- import {render, html} from "lit"
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
- ### 🍋 view `use` reference
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() //-> 1
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 //-> 123
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 //-> 123
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
- - **use.render** — force a hard render (not debounced)
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.fn** — start with an op based on an async fn
226
+ - **use.op** — start with an op based on an async fn
159
227
  ```ts
160
- const op = use.op.fn(async() => {
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
- ### 🍋 neat tricks to impress the ladies
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
- - once+rendered to do an action after the first render
251
+ - wake + rendered, to do something after each mount's first render
184
252
  ```ts
185
- use.once(() => use.rendered.then(() => {
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
- > ***TODO*** *we need to write real docs for this, lol*
194
- - `Pod` is a type for loading/ready/error states
195
- - `podium` is a tool with fns for working with pods
196
- - `Op` class wraps a pod signal and has some ergonomic fns
197
- - `makeLoader(anims.bar2)` makes it easy to create a loader
198
- - see the available `anims` on the testing page: https://sly.e280.org/
199
- - a loader's job is to render an op, with a nice loading anim and error display view
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@e280/sly",
3
- "version": "0.0.0-6",
3
+ "version": "0.0.0-8",
4
4
  "description": "web shadow views",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,8 +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
+ 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
 
@@ -2,10 +2,10 @@
2
2
  import {css, html} from "lit"
3
3
  import {repeat} from "@e280/stz"
4
4
 
5
- import {view} from "../../features/views/view.js"
6
- import {cssReset} from "../../features/views/css-reset.js"
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(0)
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
- text-align: center;
39
+ gap: 1em;
40
+ }
41
+
42
+ button {
43
+ padding: 0.2em 0.5em;
40
44
  }
41
45
  `
42
46
 
@@ -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 {view} from "../../features/views/view.js"
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
  })
@@ -1,9 +1,9 @@
1
1
 
2
2
  import {css, html} from "lit"
3
- import {Op} from "../../features/op/op.js"
4
- import {view} from "../../features/views/view.js"
5
- import {cssReset} from "../../features/views/css-reset.js"
6
- import {anims, makeLoader} from "../../features/op/loaders/make-loader.js"
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 "./features/dom/dashify.js"
3
- export * from "./features/dom/dollar.js"
4
- export * from "./features/dom/register.js"
5
- export * from "./features/dom/types.js"
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 "./features/op/loaders/make-loader.js"
8
- export * from "./features/op/loaders/parts/ascii-anim.js"
9
- export * from "./features/op/loaders/parts/error-display.js"
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 "./features/op/op.js"
12
- export * from "./features/op/podium.js"
13
- export * from "./features/op/types.js"
11
+ export * from "./ops/op.js"
12
+ export * from "./ops/podium.js"
13
+ export * from "./ops/types.js"
14
14
 
15
- export * from "./features/views/css-reset.js"
16
- export * from "./features/views/types.js"
17
- export * from "./features/views/use.js"
18
- export * from "./features/views/view.js"
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
- const pod = podium.all(...pods)
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
- await this.setReady(value)
67
+ if (count === this.#count)
68
+ await this.setReady(value)
63
69
  return value
64
70
  }
65
71
  catch (error) {
66
- await this.setError(error)
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 new Op(podium.morph(this.pod, fn))
124
+ return podium.morph(this.pod, fn)
118
125
  }
119
126
  }
120
127