@e280/sly 0.2.0-1 → 0.2.0-11

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