@e280/sly 0.2.0-4 → 0.2.0-6
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 +181 -36
- package/package.json +1 -1
- package/s/demo/views/incredi.ts +2 -2
- package/s/dom/dom.ts +10 -1
- package/s/dom/register.ts +4 -1
- package/s/index.ts +2 -2
- package/s/loot/drag-and-drops.ts +82 -0
- package/s/loot/{drop.ts → drops.ts} +8 -17
- package/s/loot/helpers.ts +3 -3
- package/s/loot/index.ts +2 -2
- package/s/views/base-element.ts +1 -1
- package/s/views/use.ts +3 -2
- package/x/demo/demo.bundle.min.js +13 -13
- package/x/demo/demo.bundle.min.js.map +4 -4
- package/x/demo/views/incredi.d.ts +1 -1
- package/x/demo/views/incredi.js +2 -2
- package/x/demo/views/incredi.js.map +1 -1
- package/x/dom/attributes.js.map +1 -0
- package/x/dom/dom.d.ts +5 -1
- package/x/dom/dom.js +5 -0
- package/x/dom/dom.js.map +1 -1
- package/x/dom/register.d.ts +5 -1
- package/x/dom/register.js.map +1 -1
- package/x/index.d.ts +2 -2
- package/x/index.html +2 -2
- package/x/index.js +2 -2
- 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/{drop.d.ts → drops.d.ts} +3 -5
- package/x/loot/drops.js +25 -0
- package/x/loot/drops.js.map +1 -0
- package/x/loot/helpers.d.ts +3 -3
- package/x/loot/helpers.js +3 -3
- package/x/loot/helpers.js.map +1 -1
- package/x/loot/index.d.ts +2 -2
- package/x/loot/index.js +2 -2
- package/x/loot/index.js.map +1 -1
- package/x/views/base-element.js +1 -1
- package/x/views/base-element.js.map +1 -1
- package/x/views/use.d.ts +2 -2
- package/x/views/use.js +3 -2
- package/x/views/use.js.map +1 -1
- package/s/dom/types.ts +0 -8
- package/s/loot/drag-drop.ts +0 -76
- package/x/dom/types.d.ts +0 -7
- package/x/dom/types.js +0 -2
- package/x/dom/types.js.map +0 -1
- package/x/loot/drag-drop.d.ts +0 -29
- package/x/loot/drag-drop.js +0 -54
- package/x/loot/drag-drop.js.map +0 -1
- package/x/loot/drop.js +0 -32
- package/x/loot/drop.js.map +0 -1
- package/x/views/attributes.js.map +0 -1
- /package/s/{views → dom}/attributes.ts +0 -0
- /package/x/{views → dom}/attributes.d.ts +0 -0
- /package/x/{views → dom}/attributes.js +0 -0
package/README.md
CHANGED
|
@@ -4,14 +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
|
-
- 🫛 **ops** — tools for async operations and loading spinners
|
|
14
|
-
-
|
|
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/
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
|
|
@@ -24,14 +24,15 @@ npm install @e280/sly lit
|
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
> [!NOTE]
|
|
27
|
-
> - 🔥 [lit](https://lit.dev/) for html rendering
|
|
27
|
+
> - 🔥 [lit](https://lit.dev/), for html rendering
|
|
28
28
|
> - ⛏️ [@e280/strata](https://github.com/e280/strata), for state management (signals, state trees)
|
|
29
|
-
> - 🏂 [@e280/stz](https://github.com/e280/stz)
|
|
30
|
-
> - 🐢 [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
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
<br/><br/>
|
|
35
|
+
<a id="views"></a>
|
|
35
36
|
|
|
36
37
|
## 🦝🍋 sly views
|
|
37
38
|
> *views are the crown jewel of sly.. shadow-dom'd.. hooks-based.. "ergonomics"..*
|
|
@@ -46,11 +47,10 @@ view(use => () => html`<p>hello world</p>`)
|
|
|
46
47
|
- views automatically rerender whenever any [strata-compatible](https://github.com/e280/strata) state changes
|
|
47
48
|
|
|
48
49
|
### 🍋 view example
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
```
|
|
50
|
+
```ts
|
|
51
|
+
import {view, dom} from "@e280/sly"
|
|
52
|
+
import {html, css} from "lit"
|
|
53
|
+
```
|
|
54
54
|
- **declare a view**
|
|
55
55
|
```ts
|
|
56
56
|
export const CounterView = view(use => (start: number) => {
|
|
@@ -198,7 +198,8 @@ view(use => () => html`<p>hello world</p>`)
|
|
|
198
198
|
|
|
199
199
|
v // 123
|
|
200
200
|
```
|
|
201
|
-
- **use.attrs** — ergonomic typed html attribute access
|
|
201
|
+
- **use.attrs** — ergonomic typed html attribute access
|
|
202
|
+
*(see [dom.attrs](#dom.attrs) for more details)*
|
|
202
203
|
```ts
|
|
203
204
|
const attrs = use.attrs({
|
|
204
205
|
name: String,
|
|
@@ -211,14 +212,6 @@ view(use => () => html`<p>hello world</p>`)
|
|
|
211
212
|
attrs.count // 123
|
|
212
213
|
attrs.active // true
|
|
213
214
|
```
|
|
214
|
-
```ts
|
|
215
|
-
attrs.name = "zenky"
|
|
216
|
-
attrs.count = 124
|
|
217
|
-
attrs.active = false // removes html attr
|
|
218
|
-
```
|
|
219
|
-
```ts
|
|
220
|
-
attrs.name = undefined // removes the attr
|
|
221
|
-
```
|
|
222
215
|
- **use.render** — rerender the view (debounced)
|
|
223
216
|
```ts
|
|
224
217
|
use.render()
|
|
@@ -269,22 +262,23 @@ view(use => () => html`<p>hello world</p>`)
|
|
|
269
262
|
|
|
270
263
|
|
|
271
264
|
<br/><br/>
|
|
265
|
+
<a id="base-element"></a>
|
|
272
266
|
|
|
273
267
|
## 🦝🪵 sly base element
|
|
274
268
|
> *the classic experience*
|
|
275
269
|
|
|
276
|
-
|
|
277
|
-
|
|
270
|
+
```ts
|
|
271
|
+
import {BaseElement, Use, dom} from "@e280/sly"
|
|
272
|
+
import {html, css} from "lit"
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
`BaseElement` is a class-based approach to create a custom element web component.
|
|
278
276
|
|
|
279
|
-
|
|
280
|
-
|
|
277
|
+
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.
|
|
278
|
+
|
|
279
|
+
base element enjoys the same `use` hooks as views.
|
|
281
280
|
|
|
282
281
|
### 🪵 base element setup
|
|
283
|
-
- **import stuff**
|
|
284
|
-
```ts
|
|
285
|
-
import {BaseElement, Use, attributes, dom} from "@e280/sly"
|
|
286
|
-
import {html, css} from "lit"
|
|
287
|
-
```
|
|
288
282
|
- **declare your element class**
|
|
289
283
|
```ts
|
|
290
284
|
export class MyElement extends BaseElement {
|
|
@@ -294,8 +288,8 @@ it's a proper class-based element, but it lets you have the same `use` hooks tha
|
|
|
294
288
|
start = 10
|
|
295
289
|
|
|
296
290
|
// custom attributes
|
|
297
|
-
attrs =
|
|
298
|
-
multiply: Number
|
|
291
|
+
attrs = dom.attrs(this, {
|
|
292
|
+
multiply: Number,
|
|
299
293
|
})
|
|
300
294
|
|
|
301
295
|
// custom methods
|
|
@@ -338,7 +332,7 @@ it's a proper class-based element, but it lets you have the same `use` hooks tha
|
|
|
338
332
|
myElement.start = 100
|
|
339
333
|
|
|
340
334
|
// html attributes
|
|
341
|
-
myElement.
|
|
335
|
+
myElement.attrs.multiply = 2
|
|
342
336
|
|
|
343
337
|
// methods
|
|
344
338
|
myElement.hello()
|
|
@@ -348,6 +342,7 @@ it's a proper class-based element, but it lets you have the same `use` hooks tha
|
|
|
348
342
|
|
|
349
343
|
|
|
350
344
|
<br/><br/>
|
|
345
|
+
<a id="dom"></a>
|
|
351
346
|
|
|
352
347
|
## 🦝🪄 sly dom
|
|
353
348
|
> *the "it's not jquery!" multitool*
|
|
@@ -403,10 +398,33 @@ import {dom} from "@e280/sly"
|
|
|
403
398
|
```ts
|
|
404
399
|
dom.render(element, html`<p>hello world</p>`)
|
|
405
400
|
```
|
|
401
|
+
- `attrs` <a id="dom.attrs"></a> to setup a type-happy html attribute helper
|
|
402
|
+
```ts
|
|
403
|
+
const attrs = dom.attrs({
|
|
404
|
+
name: String,
|
|
405
|
+
count: Number,
|
|
406
|
+
active: Boolean,
|
|
407
|
+
})
|
|
408
|
+
```
|
|
409
|
+
```ts
|
|
410
|
+
attrs.name // "chase"
|
|
411
|
+
attrs.count // 123
|
|
412
|
+
attrs.active // true
|
|
413
|
+
```
|
|
414
|
+
```ts
|
|
415
|
+
attrs.name = "zenky"
|
|
416
|
+
attrs.count = 124
|
|
417
|
+
attrs.active = false // removes html attr
|
|
418
|
+
```
|
|
419
|
+
```ts
|
|
420
|
+
attrs.name = undefined // removes the attr
|
|
421
|
+
attrs.count = undefined // removes the attr
|
|
422
|
+
```
|
|
406
423
|
|
|
407
424
|
|
|
408
425
|
|
|
409
426
|
<br/><br/>
|
|
427
|
+
<a id="ops"></a>
|
|
410
428
|
|
|
411
429
|
## 🦝🫛 sly ops
|
|
412
430
|
> *tools for async operations and loading spinners*
|
|
@@ -536,8 +554,135 @@ import {Pod, podium, Op, makeLoader, anims} from "@e280/sly"
|
|
|
536
554
|
|
|
537
555
|
|
|
538
556
|
<br/><br/>
|
|
557
|
+
<a id="loot"></a>
|
|
558
|
+
|
|
559
|
+
## 🦝🪙 loot
|
|
560
|
+
> *drag-and-drop facilities*
|
|
561
|
+
|
|
562
|
+
```ts
|
|
563
|
+
import {loot, view, dom} from "@e280/sly"
|
|
564
|
+
import {ev} from "@e280/stz"
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
### 🪙 `loot.Drops`
|
|
568
|
+
> *accept the user dropping stuff like files onto the page*
|
|
569
|
+
- **setup drops**
|
|
570
|
+
```ts
|
|
571
|
+
const drops = new loot.Drops({
|
|
572
|
+
predicate: loot.hasFiles,
|
|
573
|
+
acceptDrop: event => {
|
|
574
|
+
const files = loot.files(event)
|
|
575
|
+
console.log("files dropped", files)
|
|
576
|
+
},
|
|
577
|
+
})
|
|
578
|
+
```
|
|
579
|
+
- **attach event listeners to your dropzone,** one of these ways:
|
|
580
|
+
- **view example**
|
|
581
|
+
```ts
|
|
582
|
+
view(() => () => html`
|
|
583
|
+
<div
|
|
584
|
+
?data-indicator="${drops.$indicator()}"
|
|
585
|
+
@dragover="${drops.dragover}"
|
|
586
|
+
@dragleave="${drops.dragleave}"
|
|
587
|
+
@drop="${drops.drop}">
|
|
588
|
+
my dropzone
|
|
589
|
+
</div>
|
|
590
|
+
`)
|
|
591
|
+
```
|
|
592
|
+
- **vanilla-js whole-page example**
|
|
593
|
+
```ts
|
|
594
|
+
// attach listeners to the body
|
|
595
|
+
ev(document.body, {
|
|
596
|
+
dragover: drops.dragover,
|
|
597
|
+
dragleave: drops.dragleave,
|
|
598
|
+
drop: drops.drop,
|
|
599
|
+
})
|
|
600
|
+
|
|
601
|
+
// sly attribute handler for the body
|
|
602
|
+
const attrs = dom.attrs(document.body, {
|
|
603
|
+
"data-indicator": Boolean,
|
|
604
|
+
})
|
|
605
|
+
|
|
606
|
+
// sync the data-indicator attribute
|
|
607
|
+
drops.$indicator.on(bool => attrs["data-indicator"] = bool)
|
|
608
|
+
```
|
|
609
|
+
- **flashy css indicator for the dropzone,** so the user knows your app is eager to accept the drop
|
|
610
|
+
```css
|
|
611
|
+
[data-indicator] {
|
|
612
|
+
border: 0.5em dashed cyan;
|
|
613
|
+
}
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
### 🪙 `loot.DragAndDrops`
|
|
617
|
+
> *setup drag-and-drops between items within your page*
|
|
618
|
+
- **declare types for your draggy and droppy things**
|
|
619
|
+
```ts
|
|
620
|
+
// money that can be picked up and dragged
|
|
621
|
+
type Money = {value: number}
|
|
622
|
+
// dnd will call this a "draggy"
|
|
623
|
+
|
|
624
|
+
// bag that money can be dropped into
|
|
625
|
+
type Bag = {id: number}
|
|
626
|
+
// dnd will call this a "droppy"
|
|
627
|
+
```
|
|
628
|
+
- **make your dnd**
|
|
629
|
+
```ts
|
|
630
|
+
const dnd = new loot.DragAndDrops<Money, Bag>({
|
|
631
|
+
acceptDrop: (event, money, bag) => {
|
|
632
|
+
console.log("drop!", {money, bag})
|
|
633
|
+
},
|
|
634
|
+
})
|
|
635
|
+
```
|
|
636
|
+
- **attach dragzone listeners** (there can be many dragzones...)
|
|
637
|
+
```ts
|
|
638
|
+
view(use => () => {
|
|
639
|
+
const money = use.once((): Money => ({value: 280}))
|
|
640
|
+
const dragzone = use.once(() => dnd.dragzone(() => money))
|
|
641
|
+
|
|
642
|
+
return html`
|
|
643
|
+
<div
|
|
644
|
+
draggable="${dragzone.draggable}"
|
|
645
|
+
@dragstart="${dragzone.dragstart}"
|
|
646
|
+
@dragend="${dragzone.dragend}">
|
|
647
|
+
money ${money.value}
|
|
648
|
+
</div>
|
|
649
|
+
`
|
|
650
|
+
})
|
|
651
|
+
```
|
|
652
|
+
- **attach dropzone listeners** (there can be many dropzones...)
|
|
653
|
+
```ts
|
|
654
|
+
view(use => () => {
|
|
655
|
+
const bag = use.once((): Bag => ({id: 1}))
|
|
656
|
+
const dropzone = use.once(() => dnd.dropzone(() => bag))
|
|
657
|
+
const indicator = !!(dnd.dragging && dnd.hovering === bag)
|
|
658
|
+
|
|
659
|
+
return html`
|
|
660
|
+
<div
|
|
661
|
+
?data-indicator="${indicator}"
|
|
662
|
+
@dragenter="${dropzone.dragenter}"
|
|
663
|
+
@dragleave="${dropzone.dragleave}"
|
|
664
|
+
@dragover="${dropzone.dragover}"
|
|
665
|
+
@drop="${dropzone.drop}">
|
|
666
|
+
bag ${bag.id}
|
|
667
|
+
</div>
|
|
668
|
+
`
|
|
669
|
+
})
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
### 🪙 loot helpers
|
|
673
|
+
- **`loot.hasFiles(event)`** — return true if `DragEvent` contains any files (useful in `predicate`)
|
|
674
|
+
- **`loot.files(event)`** — returns an array of files in a drop's `DragEvent` (useful in `acceptDrop`)
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
<br/><br/>
|
|
679
|
+
<a id="e280"></a>
|
|
539
680
|
|
|
540
681
|
## 🦝🧑💻 sly is by e280
|
|
541
682
|
reward us with github stars
|
|
542
683
|
build with us at https://e280.org/ but only if you're cool
|
|
543
684
|
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
<br/><br/>
|
|
688
|
+
|
package/package.json
CHANGED
package/s/demo/views/incredi.ts
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
import {css, html} from "lit"
|
|
3
3
|
import {nap, repeat} from "@e280/stz"
|
|
4
4
|
|
|
5
|
+
import {dom} from "../../dom/dom.js"
|
|
5
6
|
import {Use} from "../../views/use.js"
|
|
6
|
-
import {attributes} from "../../views/attributes.js"
|
|
7
7
|
import {BaseElement} from "../../views/base-element.js"
|
|
8
8
|
|
|
9
9
|
export class IncrediElement extends BaseElement {
|
|
10
10
|
static styles = css`span{color:orange}`
|
|
11
|
-
attrs =
|
|
11
|
+
attrs = dom.attrs(this, {value: Number})
|
|
12
12
|
something = {whatever: "rofl"}
|
|
13
13
|
|
|
14
14
|
render(use: Use) {
|
package/s/dom/dom.ts
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
import {render} from "lit"
|
|
3
3
|
import {register} from "./register.js"
|
|
4
4
|
import {Content} from "../views/types.js"
|
|
5
|
-
import {
|
|
5
|
+
import {attributes, AttrSpec, AttrTypes} from "./attributes.js"
|
|
6
|
+
|
|
7
|
+
export type Renderable = HTMLElement | ShadowRoot | DocumentFragment
|
|
8
|
+
export type Queryable = HTMLElement | ShadowRoot | Element | Document | DocumentFragment
|
|
6
9
|
|
|
7
10
|
function require<E extends Element>(
|
|
8
11
|
container: Queryable,
|
|
@@ -41,6 +44,10 @@ export class Dom<C extends Queryable> {
|
|
|
41
44
|
render(...content: Content[]) {
|
|
42
45
|
return render(content, this.element as Renderable)
|
|
43
46
|
}
|
|
47
|
+
|
|
48
|
+
attrs<A extends AttrSpec>(spec: A) {
|
|
49
|
+
return attributes(this.element as HTMLElement, spec)
|
|
50
|
+
}
|
|
44
51
|
}
|
|
45
52
|
|
|
46
53
|
export function dom<E extends Queryable>(selector: string): E
|
|
@@ -56,6 +63,8 @@ dom.in = doc.in.bind(doc)
|
|
|
56
63
|
dom.require = doc.require.bind(doc)
|
|
57
64
|
dom.maybe = doc.maybe.bind(doc)
|
|
58
65
|
dom.all = doc.all.bind(doc)
|
|
66
|
+
|
|
67
|
+
dom.attrs = attributes
|
|
59
68
|
dom.register = register
|
|
60
69
|
dom.render = (container: Renderable, ...content: Content[]) => {
|
|
61
70
|
return render(content, container)
|
package/s/dom/register.ts
CHANGED
package/s/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
|
+
export * from "./dom/attributes.js"
|
|
2
3
|
export * from "./dom/dashify.js"
|
|
3
4
|
export * from "./dom/dom.js"
|
|
4
5
|
export * from "./dom/register.js"
|
|
5
|
-
export * from "./dom/types.js"
|
|
6
6
|
|
|
7
7
|
export * from "./ops/loaders/make-loader.js"
|
|
8
8
|
export * from "./ops/loaders/parts/ascii-anim.js"
|
|
@@ -13,7 +13,7 @@ export * from "./ops/types.js"
|
|
|
13
13
|
|
|
14
14
|
export * as loot from "./loot/index.js"
|
|
15
15
|
|
|
16
|
-
export * from "./
|
|
16
|
+
export * from "./dom/attributes.js"
|
|
17
17
|
export * from "./views/base-element.js"
|
|
18
18
|
export * from "./views/css-reset.js"
|
|
19
19
|
export * from "./views/types.js"
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
|
|
2
|
+
import {signal} from "@e280/strata"
|
|
3
|
+
import {Drops} from "./drops.js"
|
|
4
|
+
import {outsideCurrentTarget} from "./helpers.js"
|
|
5
|
+
|
|
6
|
+
/** respond to user dragging-and-dropping things around on a webpage */
|
|
7
|
+
export class DragAndDrops<Draggy, Droppy> {
|
|
8
|
+
|
|
9
|
+
/** what is currently being dragged */
|
|
10
|
+
$draggy = signal<Draggy | undefined>(undefined)
|
|
11
|
+
|
|
12
|
+
/** what dropzone are we curently hovering over */
|
|
13
|
+
$droppy = signal<Droppy | undefined>(undefined)
|
|
14
|
+
|
|
15
|
+
constructor(private params: {
|
|
16
|
+
|
|
17
|
+
/** accept a dropped item that was declared within this system */
|
|
18
|
+
acceptDrop: (event: DragEvent, draggy: Draggy, droppy: Droppy) => void
|
|
19
|
+
|
|
20
|
+
/** also accept drops on the side */
|
|
21
|
+
backchannelDrops?: Drops
|
|
22
|
+
}) {}
|
|
23
|
+
|
|
24
|
+
get dragging() {
|
|
25
|
+
return this.$draggy()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get hovering() {
|
|
29
|
+
return this.$droppy()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** make event listeners to attach to your dragzone(s) */
|
|
33
|
+
dragzone = (getDraggy: () => Draggy) => ({
|
|
34
|
+
draggable: "true",
|
|
35
|
+
|
|
36
|
+
dragstart: (_: DragEvent) => {
|
|
37
|
+
this.$draggy.value = getDraggy()
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
dragend: (_: DragEvent) => {
|
|
41
|
+
this.$draggy.value = undefined
|
|
42
|
+
this.$droppy.value = undefined
|
|
43
|
+
},
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
/** make event listeners to attach to your dropzones(s) */
|
|
47
|
+
dropzone = (getDroppy: () => Droppy) => ({
|
|
48
|
+
dragenter: (_: DragEvent) => {},
|
|
49
|
+
|
|
50
|
+
dragover: (event: DragEvent) => {
|
|
51
|
+
event.preventDefault()
|
|
52
|
+
if (this.$draggy())
|
|
53
|
+
this.$droppy.value = getDroppy()
|
|
54
|
+
else
|
|
55
|
+
this.params.backchannelDrops?.dragover(event)
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
dragleave: (event: DragEvent) => {
|
|
59
|
+
if (outsideCurrentTarget(event))
|
|
60
|
+
this.$droppy.value = undefined
|
|
61
|
+
this.params.backchannelDrops?.dragleave(event)
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
drop: (event: DragEvent) => {
|
|
65
|
+
event.preventDefault()
|
|
66
|
+
const {acceptDrop} = this.params
|
|
67
|
+
const draggy = this.$draggy()
|
|
68
|
+
const droppy = this.$droppy()
|
|
69
|
+
try {
|
|
70
|
+
if (draggy && droppy)
|
|
71
|
+
acceptDrop(event, draggy, droppy)
|
|
72
|
+
else
|
|
73
|
+
this.params.backchannelDrops?.drop(event)
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
this.$draggy.value = undefined
|
|
77
|
+
this.$droppy.value = undefined
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
|
|
2
2
|
import {signal} from "@e280/strata"
|
|
3
|
-
import {
|
|
3
|
+
import {outsideCurrentTarget} from "./helpers.js"
|
|
4
4
|
|
|
5
|
-
/** dropzone that accepts dropped stuff like files */
|
|
6
|
-
export class
|
|
7
|
-
|
|
5
|
+
/** dropzone that accepts user-dropped stuff like files */
|
|
6
|
+
export class Drops {
|
|
7
|
+
$indicator = signal(false)
|
|
8
8
|
|
|
9
9
|
constructor(private params: {
|
|
10
10
|
|
|
@@ -15,28 +15,19 @@ export class Drop {
|
|
|
15
15
|
acceptDrop: (event: DragEvent) => void
|
|
16
16
|
}) {}
|
|
17
17
|
|
|
18
|
-
get indicator() {
|
|
19
|
-
return this.#$indicator.value
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
resetIndicator = () => {
|
|
23
|
-
this.#$indicator.value = false
|
|
24
|
-
}
|
|
25
|
-
|
|
26
18
|
dragover = (event: DragEvent) => {
|
|
27
19
|
event.preventDefault()
|
|
28
|
-
|
|
29
|
-
this.#$indicator.value = true
|
|
20
|
+
this.$indicator.value = this.params.predicate(event)
|
|
30
21
|
}
|
|
31
22
|
|
|
32
23
|
dragleave = (event: DragEvent) => {
|
|
33
|
-
if (
|
|
34
|
-
this
|
|
24
|
+
if (outsideCurrentTarget(event))
|
|
25
|
+
this.$indicator.value = false
|
|
35
26
|
}
|
|
36
27
|
|
|
37
28
|
drop = (event: DragEvent) => {
|
|
38
29
|
event.preventDefault()
|
|
39
|
-
this
|
|
30
|
+
this.$indicator.value = false
|
|
40
31
|
if (this.params.predicate(event))
|
|
41
32
|
this.params.acceptDrop(event)
|
|
42
33
|
}
|
package/s/loot/helpers.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
|
|
2
|
-
export function
|
|
2
|
+
export function hasFiles(event: DragEvent) {
|
|
3
3
|
return !!(
|
|
4
4
|
event.dataTransfer &&
|
|
5
5
|
event.dataTransfer.types.includes("Files")
|
|
6
6
|
)
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
export function
|
|
9
|
+
export function files(event: DragEvent) {
|
|
10
10
|
return event.dataTransfer
|
|
11
11
|
? Array.from(event.dataTransfer.files)
|
|
12
12
|
: []
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export function
|
|
15
|
+
export function outsideCurrentTarget(event: DragEvent) {
|
|
16
16
|
const isCursorOutsideViewport = !event.relatedTarget || (
|
|
17
17
|
event.clientX === 0 &&
|
|
18
18
|
event.clientY === 0
|
package/s/loot/index.ts
CHANGED
package/s/views/base-element.ts
CHANGED
|
@@ -5,7 +5,7 @@ import {debounce, MapG} from "@e280/stz"
|
|
|
5
5
|
|
|
6
6
|
import {dom} from "../dom/dom.js"
|
|
7
7
|
import {Content} from "./types.js"
|
|
8
|
-
import {onAttrChange} from "
|
|
8
|
+
import {onAttrChange} from "../dom/attributes.js"
|
|
9
9
|
import {applyStyles} from "./utils/apply-styles.js"
|
|
10
10
|
import {Use, _disconnect, _reconnect, _wrap} from "./use.js"
|
|
11
11
|
|
package/s/views/use.ts
CHANGED
|
@@ -4,9 +4,10 @@ import {defer, MapG} from "@e280/stz"
|
|
|
4
4
|
import {signal, SignalOptions} from "@e280/strata/signals"
|
|
5
5
|
|
|
6
6
|
import {Op} from "../ops/op.js"
|
|
7
|
+
import {dom} from "../dom/dom.js"
|
|
7
8
|
import {Mounts} from "./utils/mounts.js"
|
|
8
9
|
import {applyStyles} from "./utils/apply-styles.js"
|
|
9
|
-
import {
|
|
10
|
+
import {AttrSpec, onAttrChange} from "../dom/attributes.js"
|
|
10
11
|
|
|
11
12
|
export const _wrap = Symbol()
|
|
12
13
|
export const _disconnect = Symbol()
|
|
@@ -66,7 +67,7 @@ export class Use {
|
|
|
66
67
|
|
|
67
68
|
attrs<A extends AttrSpec>(spec: A) {
|
|
68
69
|
this.mount(() => onAttrChange(this.element, this.render))
|
|
69
|
-
return this.once(() =>
|
|
70
|
+
return this.once(() => dom.attrs(this.element, spec))
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
once<V>(fn: () => V) {
|