@e280/sly 0.2.0-1 → 0.2.0-10
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 +348 -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 +21 -0
- package/s/ui/base-element.ts +76 -0
- package/s/ui/types.ts +33 -0
- package/s/ui/view/make-component.ts +33 -0
- package/s/ui/view/make-view.ts +40 -0
- package/s/{views/utils → ui/view/parts}/apply-attrs.ts +4 -7
- 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 +17 -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 +16 -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 +2 -4
- 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,**
|
|
112
119
|
```ts
|
|
113
|
-
const
|
|
120
|
+
export const GreeterView = view(use => (name: string) => {
|
|
121
|
+
return html`<p>hello ${name}</p>`
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
// view usage:
|
|
125
|
+
// GreeterView("pimsley")
|
|
114
126
|
```
|
|
115
|
-
|
|
116
|
-
- **convert any view into a web component**
|
|
127
|
+
then convert it to a component.
|
|
117
128
|
```ts
|
|
118
|
-
|
|
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>
|
|
119
137
|
```
|
|
120
|
-
-
|
|
121
|
-
|
|
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,**
|
|
140
|
+
```ts
|
|
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>
|
|
151
|
+
```
|
|
152
|
+
then it already has a `.view` ready for you.
|
|
153
|
+
```ts
|
|
154
|
+
// view usage:
|
|
155
|
+
// GreeterComponent.view("pimsley")
|
|
156
|
+
```
|
|
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,87 @@ 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 a class-based approach to create a custom element web component.
|
|
332
|
+
|
|
333
|
+
it lets you expose js properties on the element instance, which helps you setup a better developer experience for people interacting with your element through the dom.
|
|
334
|
+
|
|
335
|
+
base element enjoys the same `use` hooks as views.
|
|
336
|
+
|
|
337
|
+
### 🪵 base element setup
|
|
338
|
+
- **declare your element class**
|
|
339
|
+
```ts
|
|
340
|
+
export class MyElement extends BaseElement {
|
|
341
|
+
static styles = css`span{color:orange}`
|
|
342
|
+
|
|
343
|
+
// custom property
|
|
344
|
+
start = 10
|
|
345
|
+
|
|
346
|
+
// custom attributes
|
|
347
|
+
attrs = dom.attrs(this).spec({
|
|
348
|
+
multiply: Number,
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
// custom methods
|
|
352
|
+
hello() {
|
|
353
|
+
return "world"
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
render(use: Use) {
|
|
357
|
+
const $count = use.signal(1)
|
|
358
|
+
const increment = () => $count.value++
|
|
359
|
+
|
|
360
|
+
const {start} = this
|
|
361
|
+
const {multiply = 1} = this.attrs
|
|
362
|
+
const result = start + (multiply * $count())
|
|
363
|
+
|
|
364
|
+
return html`
|
|
365
|
+
<span>${result}</span>
|
|
366
|
+
<button @click="${increment}">+</button>
|
|
367
|
+
`
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
- **register your element to the dom**
|
|
372
|
+
```ts
|
|
373
|
+
dom.register({MyElement})
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### 🪵 base element usage
|
|
377
|
+
- **place the element in your html body**
|
|
378
|
+
```html
|
|
379
|
+
<body>
|
|
380
|
+
<my-element></my-element>
|
|
381
|
+
</body>
|
|
382
|
+
```
|
|
383
|
+
- **now you can interact with it**
|
|
384
|
+
```ts
|
|
385
|
+
const myElement = dom<MyElement>("my-element")
|
|
386
|
+
|
|
387
|
+
// js property
|
|
388
|
+
myElement.start = 100
|
|
389
|
+
|
|
390
|
+
// html attributes
|
|
391
|
+
myElement.attrs.multiply = 2
|
|
392
|
+
|
|
393
|
+
// methods
|
|
394
|
+
myElement.hello()
|
|
395
|
+
// "world"
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
<br/><br/>
|
|
401
|
+
<a id="dom"></a>
|
|
271
402
|
|
|
272
403
|
## 🦝🪄 sly dom
|
|
273
404
|
> *the "it's not jquery!" multitool*
|
|
@@ -277,56 +408,86 @@ import {dom} from "@e280/sly"
|
|
|
277
408
|
```
|
|
278
409
|
|
|
279
410
|
### 🪄 dom queries
|
|
280
|
-
- require an element
|
|
411
|
+
- `require` an element
|
|
281
412
|
```ts
|
|
282
413
|
dom(".demo")
|
|
283
414
|
// HTMLElement (or throws)
|
|
284
415
|
```
|
|
285
|
-
- maybe get an element
|
|
416
|
+
- `maybe` get an element
|
|
286
417
|
```ts
|
|
287
418
|
dom.maybe(".demo")
|
|
288
419
|
// HTMLElement | undefined
|
|
289
420
|
```
|
|
290
|
-
- select all elements
|
|
421
|
+
- `select` all elements
|
|
291
422
|
```ts
|
|
292
423
|
dom.all(".demo ul li")
|
|
293
424
|
// HTMLElement[]
|
|
294
425
|
```
|
|
295
|
-
-
|
|
426
|
+
- `in` the scope of an element
|
|
296
427
|
```ts
|
|
297
|
-
dom
|
|
428
|
+
dom(element).require("li")
|
|
298
429
|
// HTMLElement (or throws)
|
|
299
430
|
```
|
|
300
431
|
```ts
|
|
301
|
-
dom
|
|
432
|
+
dom(element).maybe("li")
|
|
302
433
|
// HTMLElement | undefined
|
|
303
434
|
```
|
|
304
435
|
```ts
|
|
305
|
-
dom
|
|
436
|
+
dom(element).all("li")
|
|
306
437
|
// HTMLElement[]
|
|
307
438
|
```
|
|
308
439
|
|
|
309
440
|
### 🪄 dom utilities
|
|
310
|
-
- register web components
|
|
441
|
+
- `register` web components
|
|
311
442
|
```ts
|
|
312
443
|
dom.register({MyComponent, AnotherCoolComponent})
|
|
313
444
|
// <my-component>
|
|
314
445
|
// <another-cool-component>
|
|
315
446
|
```
|
|
316
|
-
-
|
|
447
|
+
- `dom.register` automatically dashes the tag names (`MyComponent` becomes `<my-component>`)
|
|
448
|
+
- `render` content into an element
|
|
449
|
+
```ts
|
|
450
|
+
dom(element).render(html`<p>hello world</p>`)
|
|
451
|
+
```
|
|
452
|
+
```ts
|
|
453
|
+
dom.in(".demo").render(html`<p>hello world</p>`)
|
|
454
|
+
```
|
|
317
455
|
```ts
|
|
318
456
|
dom.render(element, html`<p>hello world</p>`)
|
|
319
457
|
```
|
|
458
|
+
- `attrs` <a id="dom.attrs"></a> to setup a type-happy html attribute helper
|
|
459
|
+
```ts
|
|
460
|
+
const attrs = dom.attrs(element).spec({
|
|
461
|
+
name: String,
|
|
462
|
+
count: Number,
|
|
463
|
+
active: Boolean,
|
|
464
|
+
})
|
|
465
|
+
```
|
|
320
466
|
```ts
|
|
321
|
-
|
|
467
|
+
attrs.name // "chase"
|
|
468
|
+
attrs.count // 123
|
|
469
|
+
attrs.active // true
|
|
322
470
|
```
|
|
323
471
|
```ts
|
|
324
|
-
|
|
472
|
+
attrs.name = "zenky"
|
|
473
|
+
attrs.count = 124
|
|
474
|
+
attrs.active = false // removes html attr
|
|
475
|
+
```
|
|
476
|
+
```ts
|
|
477
|
+
attrs.name = undefined // removes the attr
|
|
478
|
+
attrs.count = undefined // removes the attr
|
|
479
|
+
```
|
|
480
|
+
or if you wanna be more loosey-goosy, skip the spec
|
|
481
|
+
```ts
|
|
482
|
+
dom.attrs(element).string.name = "pimsley"
|
|
483
|
+
dom.attrs(element).number.count = 125
|
|
484
|
+
dom.attrs(element).boolean.active = true
|
|
325
485
|
```
|
|
326
486
|
|
|
327
487
|
|
|
328
488
|
|
|
329
489
|
<br/><br/>
|
|
490
|
+
<a id="ops"></a>
|
|
330
491
|
|
|
331
492
|
## 🦝🫛 sly ops
|
|
332
493
|
> *tools for async operations and loading spinners*
|
|
@@ -383,7 +544,7 @@ import {Pod, podium, Op, makeLoader, anims} from "@e280/sly"
|
|
|
383
544
|
```
|
|
384
545
|
- 🔥 create an op that calls and tracks an async fn
|
|
385
546
|
```ts
|
|
386
|
-
const op = Op.
|
|
547
|
+
const op = Op.load(async() => {
|
|
387
548
|
await nap(4000)
|
|
388
549
|
return 123
|
|
389
550
|
})
|
|
@@ -456,8 +617,135 @@ import {Pod, podium, Op, makeLoader, anims} from "@e280/sly"
|
|
|
456
617
|
|
|
457
618
|
|
|
458
619
|
<br/><br/>
|
|
620
|
+
<a id="loot"></a>
|
|
621
|
+
|
|
622
|
+
## 🦝🪙 loot
|
|
623
|
+
> *drag-and-drop facilities*
|
|
624
|
+
|
|
625
|
+
```ts
|
|
626
|
+
import {loot, view, dom} from "@e280/sly"
|
|
627
|
+
import {ev} from "@e280/stz"
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
### 🪙 `loot.Drops`
|
|
631
|
+
> *accept the user dropping stuff like files onto the page*
|
|
632
|
+
- **setup drops**
|
|
633
|
+
```ts
|
|
634
|
+
const drops = new loot.Drops({
|
|
635
|
+
predicate: loot.hasFiles,
|
|
636
|
+
acceptDrop: event => {
|
|
637
|
+
const files = loot.files(event)
|
|
638
|
+
console.log("files dropped", files)
|
|
639
|
+
},
|
|
640
|
+
})
|
|
641
|
+
```
|
|
642
|
+
- **attach event listeners to your dropzone,** one of these ways:
|
|
643
|
+
- **view example**
|
|
644
|
+
```ts
|
|
645
|
+
view(() => () => html`
|
|
646
|
+
<div
|
|
647
|
+
?data-indicator="${drops.$indicator()}"
|
|
648
|
+
@dragover="${drops.dragover}"
|
|
649
|
+
@dragleave="${drops.dragleave}"
|
|
650
|
+
@drop="${drops.drop}">
|
|
651
|
+
my dropzone
|
|
652
|
+
</div>
|
|
653
|
+
`)
|
|
654
|
+
```
|
|
655
|
+
- **vanilla-js whole-page example**
|
|
656
|
+
```ts
|
|
657
|
+
// attach listeners to the body
|
|
658
|
+
ev(document.body, {
|
|
659
|
+
dragover: drops.dragover,
|
|
660
|
+
dragleave: drops.dragleave,
|
|
661
|
+
drop: drops.drop,
|
|
662
|
+
})
|
|
663
|
+
|
|
664
|
+
// sly attribute handler for the body
|
|
665
|
+
const attrs = dom.attrs(document.body).spec({
|
|
666
|
+
"data-indicator": Boolean,
|
|
667
|
+
})
|
|
668
|
+
|
|
669
|
+
// sync the data-indicator attribute
|
|
670
|
+
drops.$indicator.on(bool => attrs["data-indicator"] = bool)
|
|
671
|
+
```
|
|
672
|
+
- **flashy css indicator for the dropzone,** so the user knows your app is eager to accept the drop
|
|
673
|
+
```css
|
|
674
|
+
[data-indicator] {
|
|
675
|
+
border: 0.5em dashed cyan;
|
|
676
|
+
}
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
### 🪙 `loot.DragAndDrops`
|
|
680
|
+
> *setup drag-and-drops between items within your page*
|
|
681
|
+
- **declare types for your draggy and droppy things**
|
|
682
|
+
```ts
|
|
683
|
+
// money that can be picked up and dragged
|
|
684
|
+
type Money = {value: number}
|
|
685
|
+
// dnd will call this a "draggy"
|
|
686
|
+
|
|
687
|
+
// bag that money can be dropped into
|
|
688
|
+
type Bag = {id: number}
|
|
689
|
+
// dnd will call this a "droppy"
|
|
690
|
+
```
|
|
691
|
+
- **make your dnd**
|
|
692
|
+
```ts
|
|
693
|
+
const dnd = new loot.DragAndDrops<Money, Bag>({
|
|
694
|
+
acceptDrop: (event, money, bag) => {
|
|
695
|
+
console.log("drop!", {money, bag})
|
|
696
|
+
},
|
|
697
|
+
})
|
|
698
|
+
```
|
|
699
|
+
- **attach dragzone listeners** (there can be many dragzones...)
|
|
700
|
+
```ts
|
|
701
|
+
view(use => () => {
|
|
702
|
+
const money = use.once((): Money => ({value: 280}))
|
|
703
|
+
const dragzone = use.once(() => dnd.dragzone(() => money))
|
|
704
|
+
|
|
705
|
+
return html`
|
|
706
|
+
<div
|
|
707
|
+
draggable="${dragzone.draggable}"
|
|
708
|
+
@dragstart="${dragzone.dragstart}"
|
|
709
|
+
@dragend="${dragzone.dragend}">
|
|
710
|
+
money ${money.value}
|
|
711
|
+
</div>
|
|
712
|
+
`
|
|
713
|
+
})
|
|
714
|
+
```
|
|
715
|
+
- **attach dropzone listeners** (there can be many dropzones...)
|
|
716
|
+
```ts
|
|
717
|
+
view(use => () => {
|
|
718
|
+
const bag = use.once((): Bag => ({id: 1}))
|
|
719
|
+
const dropzone = use.once(() => dnd.dropzone(() => bag))
|
|
720
|
+
const indicator = !!(dnd.dragging && dnd.hovering === bag)
|
|
721
|
+
|
|
722
|
+
return html`
|
|
723
|
+
<div
|
|
724
|
+
?data-indicator="${indicator}"
|
|
725
|
+
@dragenter="${dropzone.dragenter}"
|
|
726
|
+
@dragleave="${dropzone.dragleave}"
|
|
727
|
+
@dragover="${dropzone.dragover}"
|
|
728
|
+
@drop="${dropzone.drop}">
|
|
729
|
+
bag ${bag.id}
|
|
730
|
+
</div>
|
|
731
|
+
`
|
|
732
|
+
})
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
### 🪙 loot helpers
|
|
736
|
+
- **`loot.hasFiles(event)`** — return true if `DragEvent` contains any files (useful in `predicate`)
|
|
737
|
+
- **`loot.files(event)`** — returns an array of files in a drop's `DragEvent` (useful in `acceptDrop`)
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
<br/><br/>
|
|
742
|
+
<a id="e280"></a>
|
|
459
743
|
|
|
460
744
|
## 🦝🧑💻 sly is by e280
|
|
461
745
|
reward us with github stars
|
|
462
746
|
build with us at https://e280.org/ but only if you're cool
|
|
463
747
|
|
|
748
|
+
|
|
749
|
+
|
|
750
|
+
<br/><br/>
|
|
751
|
+
|
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-10",
|
|
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;
|