@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.
- package/README.md +344 -60
- package/package.json +3 -3
- package/s/demo/demo.bundle.ts +9 -5
- package/s/demo/views/counter.ts +22 -24
- package/s/demo/views/demo.ts +10 -6
- package/s/demo/views/fastcount.ts +29 -0
- package/s/demo/views/loaders.ts +2 -2
- package/s/dom/attrs/attrs.ts +21 -0
- package/s/dom/attrs/parts/attr-fns.ts +38 -0
- package/s/dom/attrs/parts/attr-proxies.ts +35 -0
- package/s/dom/attrs/parts/attr-spec.ts +29 -0
- package/s/dom/attrs/parts/on-attrs.ts +8 -0
- package/s/dom/dom.ts +38 -16
- package/s/dom/{register.ts → parts/register.ts} +2 -7
- package/s/dom/types.ts +39 -2
- package/s/index.html.ts +4 -2
- package/s/index.ts +10 -8
- package/s/loot/drag-and-drops.ts +82 -0
- package/s/loot/drops.ts +35 -0
- package/s/loot/helpers.ts +31 -0
- package/s/loot/index.ts +5 -0
- package/s/ops/loaders/make-loader.ts +3 -3
- package/s/ops/loaders/parts/anims.ts +1 -1
- package/s/ops/loaders/parts/ascii-anim.ts +3 -3
- package/s/ops/loaders/parts/error-display.ts +2 -2
- package/s/ops/op.ts +2 -2
- package/s/{views → ui/base}/use.ts +19 -19
- package/s/ui/base/utils/attr-watcher.ts +22 -0
- package/s/ui/base/utils/reactor.ts +32 -0
- package/s/ui/base-element.ts +76 -0
- package/s/ui/types.ts +33 -0
- package/s/ui/view/make-component.ts +34 -0
- package/s/ui/view/make-view.ts +40 -0
- package/s/{views/utils → ui/view/parts}/apply-attrs.ts +5 -8
- package/s/ui/view/parts/capsule.ts +67 -0
- package/s/ui/view/parts/chain.ts +33 -0
- package/s/ui/view/parts/context.ts +10 -0
- package/s/ui/view/parts/directive.ts +29 -0
- package/s/ui/view/parts/sly-view.ts +15 -0
- package/s/ui/view.ts +24 -0
- package/x/demo/demo.bundle.js +8 -4
- package/x/demo/demo.bundle.js.map +1 -1
- package/x/demo/demo.bundle.min.js +19 -22
- package/x/demo/demo.bundle.min.js.map +4 -4
- package/x/demo/views/counter.d.ts +374 -1
- package/x/demo/views/counter.js +19 -22
- package/x/demo/views/counter.js.map +1 -1
- package/x/demo/views/demo.d.ts +4 -1
- package/x/demo/views/demo.js +10 -5
- package/x/demo/views/demo.js.map +1 -1
- package/x/demo/views/fastcount.d.ts +12 -0
- package/x/demo/views/fastcount.js +21 -0
- package/x/demo/views/fastcount.js.map +1 -0
- package/x/demo/views/loaders.js +2 -2
- package/x/demo/views/loaders.js.map +1 -1
- package/x/dom/attrs/attrs.d.ts +20 -0
- package/x/dom/attrs/attrs.js +17 -0
- package/x/dom/attrs/attrs.js.map +1 -0
- package/x/dom/attrs/parts/attr-fns.d.ts +13 -0
- package/x/dom/attrs/parts/attr-fns.js +42 -0
- package/x/dom/attrs/parts/attr-fns.js.map +1 -0
- package/x/dom/attrs/parts/attr-proxies.d.ts +8 -0
- package/x/dom/attrs/parts/attr-proxies.js +21 -0
- package/x/dom/attrs/parts/attr-proxies.js.map +1 -0
- package/x/dom/attrs/parts/attr-spec.d.ts +3 -0
- package/x/dom/attrs/parts/attr-spec.js +21 -0
- package/x/dom/attrs/parts/attr-spec.js.map +1 -0
- package/x/dom/attrs/parts/on-attrs.d.ts +2 -0
- package/x/dom/attrs/parts/on-attrs.js +7 -0
- package/x/dom/attrs/parts/on-attrs.js.map +1 -0
- package/x/dom/dom.d.ts +18 -7
- package/x/dom/dom.js +25 -12
- package/x/dom/dom.js.map +1 -1
- package/x/dom/parts/dashify.js.map +1 -0
- package/x/dom/{register.d.ts → parts/register.d.ts} +2 -6
- package/x/dom/parts/register.js.map +1 -0
- package/x/dom/types.d.ts +14 -2
- package/x/index.d.ts +9 -7
- package/x/index.html +6 -4
- package/x/index.html.js +4 -2
- package/x/index.html.js.map +1 -1
- package/x/index.js +9 -7
- package/x/index.js.map +1 -1
- package/x/loot/drag-and-drops.d.ts +30 -0
- package/x/loot/drag-and-drops.js +63 -0
- package/x/loot/drag-and-drops.js.map +1 -0
- package/x/loot/drops.d.ts +14 -0
- package/x/loot/drops.js +25 -0
- package/x/loot/drops.js.map +1 -0
- package/x/loot/helpers.d.ts +3 -0
- package/x/loot/helpers.js +21 -0
- package/x/loot/helpers.js.map +1 -0
- package/x/loot/index.d.ts +3 -0
- package/x/loot/index.js +4 -0
- package/x/loot/index.js.map +1 -0
- package/x/ops/loaders/make-loader.d.ts +1 -1
- package/x/ops/loaders/make-loader.js +2 -2
- package/x/ops/loaders/make-loader.js.map +1 -1
- package/x/ops/loaders/parts/anims.d.ts +1 -1
- package/x/ops/loaders/parts/ascii-anim.d.ts +2 -2
- package/x/ops/loaders/parts/ascii-anim.js +2 -2
- package/x/ops/loaders/parts/ascii-anim.js.map +1 -1
- package/x/ops/loaders/parts/error-display.js +2 -2
- package/x/ops/loaders/parts/error-display.js.map +1 -1
- package/x/ops/op.d.ts +2 -2
- package/x/ops/op.js +2 -2
- package/x/ops/op.js.map +1 -1
- package/x/ui/base/css-reset.js.map +1 -0
- package/x/{views → ui/base}/use.d.ts +6 -6
- package/x/{views → ui/base}/use.js +10 -12
- package/x/ui/base/use.js.map +1 -0
- package/x/ui/base/utils/apply-styles.js.map +1 -0
- package/x/ui/base/utils/attr-watcher.d.ts +8 -0
- package/x/ui/base/utils/attr-watcher.js +20 -0
- package/x/ui/base/utils/attr-watcher.js.map +1 -0
- package/x/ui/base/utils/mounts.js.map +1 -0
- package/x/ui/base/utils/reactor.d.ts +5 -0
- package/x/ui/base/utils/reactor.js +25 -0
- package/x/ui/base/utils/reactor.js.map +1 -0
- package/x/ui/base-element.d.ts +19 -0
- package/x/ui/base-element.js +52 -0
- package/x/ui/base-element.js.map +1 -0
- package/x/ui/types.d.ts +20 -0
- package/x/{views → ui}/types.js.map +1 -1
- package/x/ui/view/make-component.d.ts +5 -0
- package/x/ui/view/make-component.js +17 -0
- package/x/ui/view/make-component.js.map +1 -0
- package/x/ui/view/make-view.d.ts +2 -0
- package/x/ui/view/make-view.js +16 -0
- package/x/ui/view/make-view.js.map +1 -0
- package/x/ui/view/parts/apply-attrs.d.ts +2 -0
- package/x/{views/utils → ui/view/parts}/apply-attrs.js +3 -5
- package/x/ui/view/parts/apply-attrs.js.map +1 -0
- package/x/ui/view/parts/capsule.d.ts +13 -0
- package/x/ui/view/parts/capsule.js +49 -0
- package/x/ui/view/parts/capsule.js.map +1 -0
- package/x/ui/view/parts/chain.d.ts +11 -0
- package/x/ui/view/parts/chain.js +21 -0
- package/x/ui/view/parts/chain.js.map +1 -0
- package/x/ui/view/parts/context.d.ts +8 -0
- package/x/ui/view/parts/context.js +10 -0
- package/x/ui/view/parts/context.js.map +1 -0
- package/x/ui/view/parts/directive.d.ts +5 -0
- package/x/ui/view/parts/directive.js +18 -0
- package/x/ui/view/parts/directive.js.map +1 -0
- package/x/ui/view/parts/sly-view.d.ts +5 -0
- package/x/ui/view/parts/sly-view.js +13 -0
- package/x/ui/view/parts/sly-view.js.map +1 -0
- package/x/ui/view.d.ts +11 -0
- package/x/ui/view.js +15 -0
- package/x/ui/view.js.map +1 -0
- package/s/views/attributes.ts +0 -89
- package/s/views/types.ts +0 -40
- package/s/views/view.ts +0 -150
- package/x/dom/dashify.js.map +0 -1
- package/x/dom/register.js.map +0 -1
- package/x/views/attributes.d.ts +0 -10
- package/x/views/attributes.js +0 -46
- package/x/views/attributes.js.map +0 -1
- package/x/views/css-reset.js.map +0 -1
- package/x/views/types.d.ts +0 -31
- package/x/views/use.js.map +0 -1
- package/x/views/utils/apply-attrs.d.ts +0 -2
- package/x/views/utils/apply-attrs.js.map +0 -1
- package/x/views/utils/apply-styles.js.map +0 -1
- package/x/views/utils/mounts.js.map +0 -1
- package/x/views/view.d.ts +0 -9
- package/x/views/view.js +0 -116
- package/x/views/view.js.map +0 -1
- /package/s/dom/{dashify.ts → parts/dashify.ts} +0 -0
- /package/s/{views → ui/base}/css-reset.ts +0 -0
- /package/s/{views → ui/base}/utils/apply-styles.ts +0 -0
- /package/s/{views → ui/base}/utils/mounts.ts +0 -0
- /package/x/dom/{dashify.d.ts → parts/dashify.d.ts} +0 -0
- /package/x/dom/{dashify.js → parts/dashify.js} +0 -0
- /package/x/dom/{register.js → parts/register.js} +0 -0
- /package/x/{views → ui/base}/css-reset.d.ts +0 -0
- /package/x/{views → ui/base}/css-reset.js +0 -0
- /package/x/{views → ui/base}/utils/apply-styles.d.ts +0 -0
- /package/x/{views → ui/base}/utils/apply-styles.js +0 -0
- /package/x/{views → ui/base}/utils/mounts.d.ts +0 -0
- /package/x/{views → ui/base}/utils/mounts.js +0 -0
- /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
|
|
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
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
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)
|
|
29
|
-
> - 🐢 [scute](https://github.com/e280/scute)
|
|
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
|
-
-
|
|
43
|
-
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
<
|
|
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({
|
|
79
|
-
|
|
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
|
-
.
|
|
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
|
|
111
|
-
- **
|
|
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
|
-
|
|
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
|
-
|
|
116
|
-
- **convert any view into a web component**
|
|
152
|
+
then it already has a `.view` ready for you.
|
|
117
153
|
```ts
|
|
118
|
-
|
|
154
|
+
// view usage:
|
|
155
|
+
// GreeterComponent.view("pimsley")
|
|
119
156
|
```
|
|
120
|
-
|
|
121
|
-
-
|
|
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({
|
|
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
|
-
### 🍋
|
|
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
|
-
- `
|
|
210
|
+
- `derived` signals
|
|
154
211
|
```ts
|
|
155
|
-
const $product = use.
|
|
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
|
-
### 🍋
|
|
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
|
-
-
|
|
422
|
+
- `in` the scope of an element
|
|
296
423
|
```ts
|
|
297
|
-
dom
|
|
424
|
+
dom(element).require("li")
|
|
298
425
|
// HTMLElement (or throws)
|
|
299
426
|
```
|
|
300
427
|
```ts
|
|
301
|
-
dom
|
|
428
|
+
dom(element).maybe("li")
|
|
302
429
|
// HTMLElement | undefined
|
|
303
430
|
```
|
|
304
431
|
```ts
|
|
305
|
-
dom
|
|
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
|
-
-
|
|
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
|
-
|
|
463
|
+
attrs.name // "chase"
|
|
464
|
+
attrs.count // 123
|
|
465
|
+
attrs.active // true
|
|
322
466
|
```
|
|
323
467
|
```ts
|
|
324
|
-
|
|
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.
|
|
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-
|
|
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-
|
|
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.
|
|
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"
|
package/s/demo/demo.bundle.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
|
|
2
2
|
import {dom} from "../dom/dom.js"
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
dom.register({
|
|
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
|
|
package/s/demo/views/counter.ts
CHANGED
|
@@ -1,44 +1,42 @@
|
|
|
1
1
|
|
|
2
2
|
import {css, html} from "lit"
|
|
3
|
-
import {repeat} from "@e280/stz"
|
|
4
3
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
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 => (
|
|
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 $
|
|
13
|
-
const
|
|
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>${$
|
|
29
|
-
</div>
|
|
30
|
-
<div>
|
|
31
|
-
<span>${$count.get()}</span>
|
|
19
|
+
<span>${$count()}</span>
|
|
32
20
|
</div>
|
|
33
21
|
<div>
|
|
34
|
-
<
|
|
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;
|