@e280/sly 0.0.0-3 → 0.0.0-5

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 (85) hide show
  1. package/README.md +108 -87
  2. package/package.json +3 -3
  3. package/s/demo/demo.bundle.ts +3 -47
  4. package/s/demo/demo.css +13 -6
  5. package/s/demo/views/counter.ts +42 -0
  6. package/s/demo/views/demo.ts +25 -0
  7. package/s/demo/views/loaders.ts +58 -0
  8. package/s/features/dom/dollar.ts +13 -9
  9. package/s/features/op/loaders/make-loader.ts +18 -0
  10. package/s/features/op/loaders/parts/anims.ts +277 -0
  11. package/s/features/op/loaders/parts/ascii-anim.ts +38 -0
  12. package/s/features/{loady → op/loaders}/parts/error-display.ts +10 -10
  13. package/s/features/op/op.ts +42 -20
  14. package/s/features/op/podium.ts +67 -0
  15. package/s/features/views/types.ts +9 -2
  16. package/s/features/views/use.ts +20 -9
  17. package/s/features/views/view.ts +15 -8
  18. package/s/index.html.ts +3 -3
  19. package/s/index.ts +4 -3
  20. package/x/demo/demo.bundle.js +2 -36
  21. package/x/demo/demo.bundle.js.map +1 -1
  22. package/x/demo/demo.bundle.min.js +61 -12
  23. package/x/demo/demo.bundle.min.js.map +4 -4
  24. package/x/demo/demo.css +13 -6
  25. package/x/demo/views/counter.d.ts +1 -0
  26. package/x/demo/views/counter.js +34 -0
  27. package/x/demo/views/counter.js.map +1 -0
  28. package/x/demo/views/demo.d.ts +1 -0
  29. package/x/demo/views/demo.js +21 -0
  30. package/x/demo/views/demo.js.map +1 -0
  31. package/x/demo/views/loaders.d.ts +1 -0
  32. package/x/demo/views/loaders.js +50 -0
  33. package/x/demo/views/loaders.js.map +1 -0
  34. package/x/features/dom/dollar.d.ts +6 -5
  35. package/x/features/dom/dollar.js +8 -7
  36. package/x/features/dom/dollar.js.map +1 -1
  37. package/x/features/op/loaders/make-loader.d.ts +5 -0
  38. package/x/features/op/loaders/make-loader.js +7 -0
  39. package/x/features/op/loaders/make-loader.js.map +1 -0
  40. package/x/features/op/loaders/parts/anims.d.ts +24 -0
  41. package/x/features/op/loaders/parts/anims.js +251 -0
  42. package/x/features/op/loaders/parts/anims.js.map +1 -0
  43. package/x/features/op/loaders/parts/ascii-anim.d.ts +6 -0
  44. package/x/features/op/loaders/parts/ascii-anim.js +26 -0
  45. package/x/features/op/loaders/parts/ascii-anim.js.map +1 -0
  46. package/x/features/op/loaders/parts/error-display.d.ts +1 -0
  47. package/x/features/{loady → op/loaders}/parts/error-display.js +9 -9
  48. package/x/features/op/loaders/parts/error-display.js.map +1 -0
  49. package/x/features/op/op.d.ts +12 -3
  50. package/x/features/op/op.js +33 -17
  51. package/x/features/op/op.js.map +1 -1
  52. package/x/features/op/podium.d.ts +9 -0
  53. package/x/features/op/podium.js +53 -0
  54. package/x/features/op/podium.js.map +1 -0
  55. package/x/features/views/types.d.ts +9 -2
  56. package/x/features/views/use.d.ts +8 -6
  57. package/x/features/views/use.js +21 -10
  58. package/x/features/views/use.js.map +1 -1
  59. package/x/features/views/view.d.ts +2 -10
  60. package/x/features/views/view.js +12 -6
  61. package/x/features/views/view.js.map +1 -1
  62. package/x/index.d.ts +4 -3
  63. package/x/index.html +18 -11
  64. package/x/index.html.js +3 -3
  65. package/x/index.html.js.map +1 -1
  66. package/x/index.js +4 -3
  67. package/x/index.js.map +1 -1
  68. package/s/features/loady/ascii-loader.ts +0 -38
  69. package/s/features/loady/parts/ascii-anim.ts +0 -27
  70. package/s/features/loady/parts/ascii-loader.ts +0 -14
  71. package/s/features/op/pod.ts +0 -19
  72. package/x/features/loady/ascii-loader.d.ts +0 -5
  73. package/x/features/loady/ascii-loader.js +0 -33
  74. package/x/features/loady/ascii-loader.js.map +0 -1
  75. package/x/features/loady/parts/ascii-anim.d.ts +0 -8
  76. package/x/features/loady/parts/ascii-anim.js +0 -21
  77. package/x/features/loady/parts/ascii-anim.js.map +0 -1
  78. package/x/features/loady/parts/ascii-loader.d.ts +0 -3
  79. package/x/features/loady/parts/ascii-loader.js +0 -10
  80. package/x/features/loady/parts/ascii-loader.js.map +0 -1
  81. package/x/features/loady/parts/error-display.d.ts +0 -8
  82. package/x/features/loady/parts/error-display.js.map +0 -1
  83. package/x/features/op/pod.d.ts +0 -5
  84. package/x/features/op/pod.js +0 -16
  85. package/x/features/op/pod.js.map +0 -1
package/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
 
2
2
  <div align="center"><img alt="" width="256" src="./assets/favicon.png"/></div>
3
3
 
4
- # 🦝 sly — mischievious frontend web framework
5
- - 🪒 lean view framework for [lit](https://lit.dev/)
4
+ # 🦝 sly — mischievous shadow views
5
+ > testing page at https://sly.e280.org/
6
+
7
+ - 🪒 lean view framework for [lit](https://lit.dev/) web devs
6
8
  - 🌅 sly is the successor to [@benev/slate](https://github.com/benevolent-games/slate)
7
9
  - 🏂 commonly used with stz standard library [@e280/stz](https://github.com/e280/stz)
8
10
  - ⛏️ integrates signals and state trees from [@e280/strata](https://github.com/e280/strata)
@@ -11,8 +13,7 @@
11
13
 
12
14
  <br/>
13
15
 
14
- ## 🦝 INSTALL SLY AND PEERS
15
- they all super work together.
16
+ ## 🦝 INSTALL SLY AND PALS
16
17
 
17
18
  ```sh
18
19
  npm install @e280/sly @e280/stz @e280/strata lit
@@ -20,50 +21,90 @@ npm install @e280/sly @e280/stz @e280/strata lit
20
21
 
21
22
  <br/>
22
23
 
23
- ## 🦝 VIEWS ARE LEAN
24
+ ## 🦝 SLY VIEWS
24
25
  views are the crown jewel of sly. shadow-dom'd. hooks-based. fancy ergonomics. not components.
25
26
 
26
- views are leaner than web components.. no dom registration, 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.
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.
27
28
 
28
29
  sly views are wired to automatically rerender whenever they're using any state stuff from [@e280/strata](https://github.com/e280/strata).
29
30
 
30
31
  ### 🍋 basic view example
31
- > views are hooks-based functional components. they are *not* web components nor *custom elements*, yet they are encapsulated within a [shadow root](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM).
32
-
33
- #### declaring a view
34
- ```ts
35
- import {view} from "@e280/sly"
36
- import {html, css} from "lit"
37
-
38
- export const CounterView = view(use => (start: number) => {
39
- use.name("counter")
40
- use.styles(css`p {color: green}`)
41
- const count = use.signal(start)
42
-
43
- return html`
44
- <p>count ${count()}</p>
45
- <button @click="${() => { count.value++ }}"></button>
46
- `
47
- })
48
- ```
49
- - each view renders into a `<sly-view>` host, with the provided `name` set as its view attribute, eg `<sly-view view="counter">`
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**
34
+ ```ts
35
+ import {view} from "@e280/sly"
36
+ import {html, css} from "lit"
37
+
38
+ export const CounterView = view(use => (start: number) => {
39
+ use.name("counter")
40
+ use.styles(css`p {color: green}`)
41
+ const count = use.signal(start)
42
+
43
+ return html`
44
+ <p>count ${count()}</p>
45
+ <button @click="${() => { count.value++ }}"></button>
46
+ `
47
+ })
48
+ ```
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**
51
+ ```ts
52
+ import {render, html} from "lit"
53
+ import {CounterView} from "./my-counter.js"
50
54
 
51
- #### injecting a view into the dom
52
- ```ts
53
- import {render, html} from "lit"
54
- import {CounterView} from "./my-counter.js"
55
+ const content = html`
56
+ <h1>my demo page</h1>
57
+ ${CounterView(1)}
58
+ `
55
59
 
56
- const content = html`
57
- <h1>my demo page</h1>
58
- ${CounterView(1)}
59
- `
60
+ render(content, document.querySelector(".app")!)
61
+ ```
60
62
 
61
- render(content, document.body)
62
- ```
63
+ ### 🍋 view declaration settings
64
+ - special settings for views at declaration-time
65
+ ```ts
66
+ import {view} from "@e280/sly"
67
+ import {html} from "lit"
63
68
 
64
- ### 🍋 view `use`
65
- > super special view helper, with hooks and other goodies
69
+ export const CoolView = view
70
+ .settings({mode: "open", delegatesFocus: true})
71
+ .view(use => (greeting: string) => {
72
+
73
+ return html`😎 ${greeting} <slot></slot>`
74
+ })
75
+ ```
76
+ - these `settings` like `mode` and `delegatesFocus` are [attachShadow params](https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow#parameters)
77
+ - note the `<slot></slot>` we'll use in the next example lol
66
78
 
79
+ ### 🍋 view injection options
80
+ - options for views at the template injection site
81
+ ```ts
82
+ import {render, html} from "lit"
83
+ import {CoolView} from "./cool-view.js"
84
+
85
+ const content = html`
86
+ <h2>super cool example</h2>
87
+ ${CoolView
88
+ .attr("class", "hero")
89
+ .children(html`<em>spongebob</em>`)
90
+ .props("hello")}
91
+ `
92
+
93
+ render(content, document.querySelector(".app")!)
94
+ ```
95
+ - `attr` — set html attributes on the `<sly-view>` host element
96
+ - `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
+ - `props` — finally inject the view by providing its props
98
+
99
+ ### 🍋 view `use` reference
100
+ - **use.name** — set the "view" attr value, eg `<sly-view view="squarepants">`
101
+ ```ts
102
+ use.name("squarepants")
103
+ ```
104
+ - **use.styles** — attach stylesheets into the view's shadow dom
105
+ ```ts
106
+ use.styles(css1, css2, css3)
107
+ ```
67
108
  - **use.signal** — create a [strata signal](https://github.com/e280/strata)
68
109
  ```ts
69
110
  const count = use.signal(1)
@@ -93,25 +134,40 @@ render(content, document.body)
93
134
  }
94
135
  })
95
136
  ```
96
- - **use.name** — set the "view" attr value, eg `<sly-view view="squarepants">`
137
+ - **use.life** — mount/unmount lifecycle, but also return a value
97
138
  ```ts
98
- use.name("squarepants")
139
+ const v = use.life(() => {
140
+ console.log("mounted")
141
+ const value = 123
142
+ return [value, () => console.log("unmounted")]
143
+ })
144
+
145
+ v //-> 123
99
146
  ```
100
- - **use.styles** — attach stylesheets into the view's shadow dom
147
+ - **use.render** — force a hard render (not debounced)
101
148
  ```ts
102
- use.styles(css1, css2, css3)
149
+ use.render()
103
150
  ```
104
- - **use.rendered** — promise that resolves *after* view has rendered
151
+ - **use.rendered** — promise that resolves *after* the next render
105
152
  ```ts
106
153
  use.rendered.then(() => {
107
154
  const slot = use.shadow.querySelector("slot")!
108
155
  console.log(slot)
109
156
  })
110
157
  ```
158
+ - **use.op.fn** — start with an op based on an async fn
159
+ ```ts
160
+ const op = use.op.fn(async() => {
161
+ await nap(5000)
162
+ return 123
163
+ })
164
+ ```
165
+ - **use.op.promise** — start with an op based on a promise
166
+ ```ts
167
+ const op = use.op.promise(doAsyncWork())
168
+ ```
111
169
 
112
170
  ### 🍋 neat tricks to impress the ladies
113
- > common patterns and snippets
114
-
115
171
  - make a ticker — mount, repeat, and nap
116
172
  ```ts
117
173
  import {repeat, nap} from "@e280/stz"
@@ -131,55 +187,20 @@ render(content, document.body)
131
187
  }))
132
188
  ```
133
189
 
134
- ### 🍋 view declaration settings
135
- > special settings for views at declaration-time
136
-
137
- ```ts
138
- import {view} from "@e280/sly"
139
- import {html} from "lit"
140
-
141
- export const CoolView = view
142
- .settings({mode: "open", delegatesFocus: true})
143
- .view(use => (greeting: string) => {
144
-
145
- return html`😎 ${greeting} <slot></slot>`
146
- })
147
- ```
148
- - these `settings` like `mode` and `delegatesFocus` are [attachShadow params](https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow#parameters)
149
- - note the `<slot></slot>` we'll use in the next example lol
150
-
151
- ### 🍋 view injection options
152
- > options for views at the template injection site
153
-
154
- ```ts
155
- import {render, html} from "lit"
156
- import {CoolView} from "./cool-view.js"
157
-
158
- const content = html`
159
- <h2>super cool example</h2>
160
- ${CoolView
161
- .attr("class", "hero")
162
- .children(html`<em>spongebob</em>`)
163
- .props("hello")}
164
- `
165
-
166
- render(content, document.body)
167
- ```
168
- - `attr` — set html attributes on the `<sly-view>` host element
169
- - `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)
170
- - `props` — finally inject the view by providing its props
171
-
172
190
  <br/>
173
191
 
174
- ## 🦝 OPS LOADING INDICATORS
175
- > ***TODO*** *implemented but not yet documented, lol*
176
- - `Pod` is a type for (loading/ready/error states)
192
+ ## 🦝 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
177
196
  - `Op` class wraps a pod signal and has some ergonomic fns
178
- - `loady` has various loading indicators for dealing with ops
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
179
200
 
180
201
  <br/>
181
202
 
182
- ## 🧑‍💻 PROJECT BY e280
203
+ ## 🧑‍💻 SLY BY E280
183
204
  reward us with github stars
184
205
  build with us at https://e280.org/ but only if you're cool
185
206
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@e280/sly",
3
- "version": "0.0.0-3",
3
+ "version": "0.0.0-5",
4
4
  "description": "web shadow views",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -13,8 +13,8 @@
13
13
  "s"
14
14
  ],
15
15
  "peerDependencies": {
16
- "@e280/stz": "^0.0.0-35",
17
16
  "@e280/strata": "^0.0.0-10",
17
+ "@e280/stz": "^0.0.0-36",
18
18
  "lit": "^3.3.1"
19
19
  },
20
20
  "devDependencies": {
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "scripts": {
28
28
  "build": "run-s _clean _ln _tsc _scute",
29
- "watch": "octo 'scute -vw' 'tsc -w' 'node --watch x/tests.test.js' 'http-server x'",
29
+ "start": "octo 'scute -vw' 'tsc -w' 'node --watch x/tests.test.js' 'http-server x'",
30
30
  "count": "find s -path '*/_archive' -prune -o -name '*.ts' -exec wc -l {} +",
31
31
  "test": "node x/tests.test.js",
32
32
  "test-inspect": "node inspect x/tests.test.js",
@@ -1,52 +1,8 @@
1
1
 
2
- import {nap, repeat} from "@e280/stz"
3
- import {css, html, render} from "lit"
4
-
2
+ import {DemoView} from "./views/demo.js"
5
3
  import {$} from "../features/dom/dollar.js"
6
- import {view} from "../features/views/view.js"
7
- import {cssReset} from "../features/views/css-reset.js"
8
- import {loady} from "../features/loady/ascii-loader.js"
9
-
10
- console.log("🦝 sly")
11
-
12
- const styles = css`
13
- :host {
14
- display: flex;
15
- flex-direction: column;
16
- justify-content: center;
17
- text-align: center;
18
- }
19
- `
20
-
21
- const MyView = view(use => (greeting: string) => {
22
- use.name("my-view")
23
- use.styles(cssReset, styles)
24
- const count = use.signal(0)
25
4
 
26
- use.mount(() => repeat(async() => {
27
- await nap(1000)
28
- count.value++
29
- }))
5
+ $.render($(".demo"), DemoView())
30
6
 
31
- use.once(() => use.rendered.then(() => {
32
- console.log("slot", $("slot", use.shadow))
33
- }))
34
-
35
- const op = use.op.fn(async() => {
36
- await nap(5000)
37
- })
38
-
39
- return html`
40
- <p>${greeting} <slot></slot> ${count()}</p>
41
- <p>${loady.dots(op, () => "op loaded")}</p>
42
- `
43
- })
44
-
45
- render(
46
- MyView
47
- .attr("class", "incredi")
48
- .children("world")
49
- .props("hello"),
50
- $(".demo"),
51
- )
7
+ console.log("🦝 sly")
52
8
 
package/s/demo/demo.css CHANGED
@@ -5,6 +5,7 @@
5
5
  :root {
6
6
  --prime: #1eff00;
7
7
  --accent: #387d42;
8
+ --pill: #12763959;
8
9
  --bg: #0e2316;
9
10
  --link: cyan;
10
11
  }
@@ -51,15 +52,12 @@
51
52
 
52
53
  @layer page {
53
54
  :root {
54
- color-scheme: dark;
55
-
56
- padding: 5vw 0.5em;
57
- min-height: 100%;
58
- scrollbar-gutter: stable;
59
-
60
55
  font-size: 21px;
61
56
  font-family: sans-serif;
62
57
 
58
+ color-scheme: dark;
59
+ scrollbar-gutter: stable;
60
+
63
61
  color: color-mix(in lch, var(--prime), #fff8 50%);
64
62
  background: radial-gradient(
65
63
  circle,
@@ -69,6 +67,11 @@
69
67
  }
70
68
 
71
69
  body {
70
+ width: 100%;
71
+ max-width: 32em;
72
+ margin: auto;
73
+ padding: 1em;
74
+
72
75
  display: flex;
73
76
  flex-direction: column;
74
77
  align-items: center;
@@ -88,6 +91,10 @@
88
91
  font-size: 0.8em;
89
92
  }
90
93
 
94
+ .demo {
95
+ width: 100%;
96
+ }
97
+
91
98
  :not(:defined) {
92
99
  display: none;
93
100
  }
@@ -0,0 +1,42 @@
1
+
2
+ import {css, html} from "lit"
3
+ import {repeat} from "@e280/stz"
4
+
5
+ import {view} from "../../features/views/view.js"
6
+ import {cssReset} from "../../features/views/css-reset.js"
7
+
8
+ export const CounterView = view(use => () => {
9
+ use.name("counter")
10
+ use.styles(cssReset, styles)
11
+
12
+ const start = use.once(() => Date.now())
13
+ const seconds = use.signal(0)
14
+
15
+ use.mount(() => repeat(async() => {
16
+ const since = Date.now() - start
17
+ seconds(Math.floor(since / 1000))
18
+ }))
19
+
20
+ const count = use.signal(0)
21
+ const increment = () => count(count() + 1)
22
+
23
+ return html`
24
+ <div>
25
+ <span>${seconds()}</span>
26
+ </div>
27
+ <div>
28
+ <span>${count()}</span>
29
+ <button @click="${increment}">+</button>
30
+ </div>
31
+ `
32
+ })
33
+
34
+ const styles = css`
35
+ :host {
36
+ display: flex;
37
+ flex-direction: column;
38
+ justify-content: center;
39
+ text-align: center;
40
+ }
41
+ `
42
+
@@ -0,0 +1,25 @@
1
+
2
+ import {css, html} from "lit"
3
+ import {CounterView} from "./counter.js"
4
+ import {LoadersView} from "./loaders.js"
5
+ import {view} from "../../features/views/view.js"
6
+ import {cssReset} from "../../features/views/css-reset.js"
7
+
8
+ export const DemoView = view(use => () => {
9
+ use.name("demo")
10
+ use.styles(cssReset, styles)
11
+
12
+ return html`
13
+ ${CounterView()}
14
+ ${LoadersView()}
15
+ `
16
+ })
17
+
18
+ const styles = css`
19
+ :host {
20
+ display: flex;
21
+ flex-direction: column;
22
+ gap: 1em;
23
+ }
24
+ `
25
+
@@ -0,0 +1,58 @@
1
+
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"
7
+
8
+ export const LoadersView = view(use => () => {
9
+ use.name("loaders")
10
+ use.styles(cssReset, styles)
11
+
12
+ const op = use.once(() => Op.loading())
13
+
14
+ const loaders = use.once(() =>
15
+ Object.entries(anims).map(([key, anim]) => ({
16
+ key,
17
+ loader: makeLoader(anim)
18
+ }))
19
+ )
20
+
21
+ return loaders.map(({key, loader}) => html`
22
+ <div data-anim="${key}">
23
+ <span>${key}</span>
24
+ <span>${loader(op, () => null)}</span>
25
+ </div>
26
+ `)
27
+ })
28
+
29
+ const styles = css`
30
+ :host {
31
+ display: flex;
32
+ flex-direction: row;
33
+ justify-content: center;
34
+ flex-wrap: wrap;
35
+
36
+ gap: 0.2em;
37
+ padding: 1em;
38
+
39
+ width: 100%;
40
+ }
41
+
42
+ div {
43
+ font-family: monospace;
44
+
45
+ display: flex;
46
+ flex-direction: column;
47
+ align-items: center;
48
+ gap: 0.4em;
49
+
50
+ padding: 0.2em 0.5em;
51
+ background: var(--pill);
52
+ border-radius: 0.5em;
53
+
54
+ span:nth-child(1) { font-size: 0.6em; }
55
+ span:nth-child(2) { font-size: 1.2em; }
56
+ }
57
+ `
58
+
@@ -1,21 +1,25 @@
1
1
 
2
- export const $ = one
2
+ import {render} from "lit"
3
+ import {Content} from "../views/types.js"
3
4
 
4
- export type Queryable = HTMLElement | Document | ShadowRoot | Element
5
+ export type Container = HTMLElement | ShadowRoot | DocumentFragment
6
+ export type Queryable = HTMLElement | ShadowRoot | Element | Document | DocumentFragment
5
7
 
6
- function all<E extends HTMLElement = HTMLElement>(selector: string, context: Queryable = document) {
7
- return Array.from(context.querySelectorAll<E>(selector))
8
- }
9
-
10
- function one<E extends HTMLElement = HTMLElement>(selector: string, context: Queryable = document) {
8
+ export function $<E extends HTMLElement = HTMLElement>(selector: string, context: Queryable = document) {
11
9
  const e = context.querySelector<E>(selector)
12
10
  if (!e) throw new Error(`$1 ${selector} not found`)
13
11
  return e
14
12
  }
15
13
 
16
- one.maybe = <E extends HTMLElement = HTMLElement>(selector: string, context: Queryable = document) => {
14
+ function all<E extends HTMLElement = HTMLElement>(selector: string, context: Queryable = document) {
15
+ return Array.from(context.querySelectorAll<E>(selector))
16
+ }
17
+
18
+ $.maybe = <E extends HTMLElement = HTMLElement>(selector: string, context: Queryable = document) => {
17
19
  return context.querySelector<E>(selector)
18
20
  }
19
21
 
20
- one.all = all
22
+ $.all = all
23
+
24
+ $.render = (container: Container, ...content: Content[]) => render(content, container)
21
25
 
@@ -0,0 +1,18 @@
1
+
2
+ import {Op} from "../op.js"
3
+ import {braille} from "./parts/anims.js"
4
+ import {Content} from "../../views/types.js"
5
+ import {ErrorDisplay} from "./parts/error-display.js"
6
+
7
+ export * as anims from "./parts/anims.js"
8
+
9
+ export type Loader = <V>(op: Op<V>, ready: (value: V) => Content) => Content
10
+
11
+ export function makeLoader(
12
+ loading: () => Content = braille,
13
+ error: (error: any) => Content = (error: any) => ErrorDisplay(error),
14
+ ): Loader {
15
+
16
+ return (op, ready) => op.select({loading, ready, error})
17
+ }
18
+