@tillsc/progressive-web-components 0.1.0 → 0.1.2
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 +5 -2
- package/dist/all-bs5.js +212 -0
- package/dist/all.js +212 -0
- package/dist/zone-transfer.js +315 -0
- package/package.json +5 -5
- package/src/core/pwc-children-observer-element.js +53 -0
- package/src/core/pwc-element.js +76 -0
- package/src/core/pwc-sentinel-init-element.js +56 -0
- package/src/core/pwc-simple-init-element.js +25 -0
- package/src/core/utils.js +9 -0
- package/src/dialog-opener/INTERNALS.md +47 -0
- package/src/dialog-opener/README.md +145 -0
- package/src/dialog-opener/base.js +226 -0
- package/src/dialog-opener/bs5/dialog-opener.js +73 -0
- package/src/dialog-opener/bs5/index.js +10 -0
- package/src/dialog-opener/dialog-opener.css +21 -0
- package/src/dialog-opener/dialog-opener.js +41 -0
- package/src/dialog-opener/index.html +24 -0
- package/src/dialog-opener/index.js +12 -0
- package/src/dialog-opener/test/basic.html +109 -0
- package/src/dialog-opener/test/bs5-basic.html +100 -0
- package/src/dialog-opener/test/bs5-iframe-target.html +41 -0
- package/src/dialog-opener/test/iframe-target.html +28 -0
- package/src/index-bs5.js +5 -0
- package/src/index.js +4 -0
- package/src/modal-dialog/INTERNALS.md +55 -0
- package/src/modal-dialog/README.md +139 -0
- package/src/modal-dialog/base.js +117 -0
- package/src/modal-dialog/bs5/index.js +7 -0
- package/src/modal-dialog/bs5/modal-dialog.js +109 -0
- package/src/modal-dialog/index.html +24 -0
- package/src/modal-dialog/index.js +9 -0
- package/src/modal-dialog/modal-dialog.css +103 -0
- package/src/modal-dialog/modal-dialog.js +97 -0
- package/src/modal-dialog/test/basic.html +84 -0
- package/src/modal-dialog/test/bs5-basic.html +123 -0
- package/src/multiselect-dual-list/INTERNALS.md +101 -0
- package/src/multiselect-dual-list/README.md +191 -0
- package/src/multiselect-dual-list/base.js +215 -0
- package/src/multiselect-dual-list/bs5/index.js +10 -0
- package/src/multiselect-dual-list/bs5/multiselect-dual-list.js +103 -0
- package/src/multiselect-dual-list/index.html +26 -0
- package/src/multiselect-dual-list/index.js +9 -0
- package/src/multiselect-dual-list/multiselect-dual-list.css +123 -0
- package/src/multiselect-dual-list/multiselect-dual-list.js +100 -0
- package/src/multiselect-dual-list/test/basic.html +115 -0
- package/src/multiselect-dual-list/test/bs5-basic.html +106 -0
- package/src/multiselect-dual-list/test/dynamic-options.html +70 -0
- package/src/multiselect-dual-list/test/filter-api.html +66 -0
- package/src/zone-transfer/INTERNALS.md +80 -0
- package/src/zone-transfer/README.md +166 -0
- package/src/zone-transfer/index.html +24 -0
- package/src/zone-transfer/index.js +9 -0
- package/src/zone-transfer/test/basic.html +78 -0
- package/src/zone-transfer/test/keyboard.html +111 -0
- package/src/zone-transfer/zone-transfer.css +12 -0
- package/src/zone-transfer/zone-transfer.js +292 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<title>zone-transfer: move between zones</title>
|
|
6
|
+
<script type="module" src="../../../dist/zone-transfer.js"></script>
|
|
7
|
+
</head>
|
|
8
|
+
|
|
9
|
+
<body>
|
|
10
|
+
<pwc-zone-transfer id="zt">
|
|
11
|
+
<pwc-zone-transfer-zone name="available">
|
|
12
|
+
<div data-pwc-item="a">Alice</div>
|
|
13
|
+
<div data-pwc-item="b">Bob</div>
|
|
14
|
+
</pwc-zone-transfer-zone>
|
|
15
|
+
|
|
16
|
+
<pwc-zone-transfer-zone name="selected">
|
|
17
|
+
<!-- empty -->
|
|
18
|
+
</pwc-zone-transfer-zone>
|
|
19
|
+
</pwc-zone-transfer>
|
|
20
|
+
|
|
21
|
+
<style>
|
|
22
|
+
pwc-zone-transfer-zone {
|
|
23
|
+
display: block;
|
|
24
|
+
border: 1px solid red;
|
|
25
|
+
padding: 0.4rem;
|
|
26
|
+
margin: 0.8rem;
|
|
27
|
+
min-height: 2rem;
|
|
28
|
+
}
|
|
29
|
+
pwc-zone-transfer-zone[name="selected"] {
|
|
30
|
+
border-color: blue;
|
|
31
|
+
}
|
|
32
|
+
</style>
|
|
33
|
+
|
|
34
|
+
<script type="module">
|
|
35
|
+
import { run } from "../../../static/test-harness.js";
|
|
36
|
+
|
|
37
|
+
run(async ({ assert, equal, waitFor, log, nextTick }) => {
|
|
38
|
+
log("checking element definition");
|
|
39
|
+
assert(customElements.get("pwc-zone-transfer"), "pwc-zone-transfer not defined");
|
|
40
|
+
|
|
41
|
+
const el = document.getElementById("zt");
|
|
42
|
+
assert(el, "missing <pwc-zone-transfer>");
|
|
43
|
+
|
|
44
|
+
const zoneAvailable = el.querySelector('pwc-zone-transfer-zone[name="available"]');
|
|
45
|
+
const zoneSelected = el.querySelector('pwc-zone-transfer-zone[name="selected"]');
|
|
46
|
+
assert(zoneAvailable, "missing available zone");
|
|
47
|
+
assert(zoneSelected, "missing selected zone");
|
|
48
|
+
|
|
49
|
+
const alice = zoneAvailable.querySelector('[data-pwc-item="a"]');
|
|
50
|
+
assert(alice, "missing item a");
|
|
51
|
+
|
|
52
|
+
log("precondition: item is in available zone");
|
|
53
|
+
equal(zoneAvailable.querySelectorAll("[data-pwc-item]").length, 2, "expected 2 items in available");
|
|
54
|
+
equal(zoneSelected.querySelectorAll("[data-pwc-item]").length, 0, "expected 0 items in selected");
|
|
55
|
+
|
|
56
|
+
log("simulating drag&drop: Alice -> selected");
|
|
57
|
+
|
|
58
|
+
alice.dispatchEvent(new DragEvent("dragstart", { bubbles: true, cancelable: true }));
|
|
59
|
+
zoneSelected.dispatchEvent(new DragEvent("dragover", { bubbles: true, cancelable: true, clientY: 0 }));
|
|
60
|
+
zoneSelected.dispatchEvent(new DragEvent("drop", { bubbles: true, cancelable: true }));
|
|
61
|
+
alice.dispatchEvent(new DragEvent("dragend", { bubbles: true, cancelable: true }));
|
|
62
|
+
|
|
63
|
+
await nextTick("after drop");
|
|
64
|
+
|
|
65
|
+
log("waiting for DOM move to complete");
|
|
66
|
+
await waitFor(() => {
|
|
67
|
+
return zoneSelected.querySelector('[data-pwc-item="a"]') && !zoneAvailable.querySelector('[data-pwc-item="a"]');
|
|
68
|
+
}, { label: "alice moved to selected" });
|
|
69
|
+
|
|
70
|
+
log("asserting final counts");
|
|
71
|
+
equal(zoneAvailable.querySelectorAll("[data-pwc-item]").length, 1, "expected 1 item remaining in available");
|
|
72
|
+
equal(zoneSelected.querySelectorAll("[data-pwc-item]").length, 1, "expected 1 item in selected");
|
|
73
|
+
|
|
74
|
+
log("done");
|
|
75
|
+
});
|
|
76
|
+
</script>
|
|
77
|
+
</body>
|
|
78
|
+
</html>
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<title>zone-transfer: keyboard (3 zones)</title>
|
|
6
|
+
<script type="module" src="../../../dist/zone-transfer.js"></script>
|
|
7
|
+
</head>
|
|
8
|
+
|
|
9
|
+
<body>
|
|
10
|
+
<pwc-zone-transfer id="zt">
|
|
11
|
+
<pwc-zone-transfer-zone name="a" data-pwc-zone-key="1">
|
|
12
|
+
<pwc-zone-transfer-item id="a1">A1</pwc-zone-transfer-item>
|
|
13
|
+
<pwc-zone-transfer-item id="a2">A2</pwc-zone-transfer-item>
|
|
14
|
+
<pwc-zone-transfer-item id="a3">A3</pwc-zone-transfer-item>
|
|
15
|
+
</pwc-zone-transfer-zone>
|
|
16
|
+
|
|
17
|
+
<pwc-zone-transfer-zone name="b" data-pwc-zone-key="2">
|
|
18
|
+
<pwc-zone-transfer-item id="b1">B1</pwc-zone-transfer-item>
|
|
19
|
+
</pwc-zone-transfer-zone>
|
|
20
|
+
|
|
21
|
+
<pwc-zone-transfer-zone name="c" data-pwc-zone-key="3">
|
|
22
|
+
<!-- empty -->
|
|
23
|
+
</pwc-zone-transfer-zone>
|
|
24
|
+
</pwc-zone-transfer>
|
|
25
|
+
|
|
26
|
+
<style>
|
|
27
|
+
pwc-zone-transfer-zone {
|
|
28
|
+
display: block;
|
|
29
|
+
border: 1px solid #999;
|
|
30
|
+
padding: 0.4rem;
|
|
31
|
+
margin: 0.8rem;
|
|
32
|
+
min-height: 2rem;
|
|
33
|
+
}
|
|
34
|
+
pwc-zone-transfer-item {
|
|
35
|
+
display: block;
|
|
36
|
+
padding: 0.2rem 0.3rem;
|
|
37
|
+
border: 1px solid #ddd;
|
|
38
|
+
margin: 0.2rem 0;
|
|
39
|
+
}
|
|
40
|
+
</style>
|
|
41
|
+
|
|
42
|
+
<script type="module">
|
|
43
|
+
import { run } from "../../../static/test-harness.js";
|
|
44
|
+
|
|
45
|
+
function key(el, k, opts = {}) {
|
|
46
|
+
el.dispatchEvent(new KeyboardEvent("keydown", {
|
|
47
|
+
key: k,
|
|
48
|
+
bubbles: true,
|
|
49
|
+
cancelable: true,
|
|
50
|
+
ctrlKey: Boolean(opts.ctrlKey),
|
|
51
|
+
metaKey: Boolean(opts.metaKey)
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
run(async ({ assert, equal, waitFor, log, nextTick }) => {
|
|
56
|
+
log("checking element definition");
|
|
57
|
+
assert(customElements.get("pwc-zone-transfer"), "pwc-zone-transfer not defined");
|
|
58
|
+
|
|
59
|
+
const el = document.getElementById("zt");
|
|
60
|
+
assert(el, "missing <pwc-zone-transfer>");
|
|
61
|
+
|
|
62
|
+
const zoneA = el.querySelector('pwc-zone-transfer-zone[name="a"]');
|
|
63
|
+
const zoneB = el.querySelector('pwc-zone-transfer-zone[name="b"]');
|
|
64
|
+
const zoneC = el.querySelector('pwc-zone-transfer-zone[name="c"]');
|
|
65
|
+
assert(zoneA && zoneB && zoneC, "missing zones");
|
|
66
|
+
|
|
67
|
+
const a2 = zoneA.querySelector("#a2");
|
|
68
|
+
assert(a2, "missing a2");
|
|
69
|
+
|
|
70
|
+
log("focus a2 (roving tabindex)");
|
|
71
|
+
a2.tabIndex = 0;
|
|
72
|
+
a2.focus();
|
|
73
|
+
await nextTick("after focus");
|
|
74
|
+
equal(document.activeElement, a2, "a2 should be focused");
|
|
75
|
+
|
|
76
|
+
log("keyboard reorder within zone: Ctrl+ArrowDown moves a2 below a3");
|
|
77
|
+
key(a2, "ArrowDown", { ctrlKey: true });
|
|
78
|
+
await nextTick("after reorder");
|
|
79
|
+
|
|
80
|
+
{
|
|
81
|
+
const ids = Array.from(zoneA.querySelectorAll("pwc-zone-transfer-item")).map((n) => n.id);
|
|
82
|
+
equal(ids[0], "a1", "a1 stays first");
|
|
83
|
+
equal(ids[1], "a3", "a3 becomes second");
|
|
84
|
+
equal(ids[2], "a2", "a2 becomes third");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
log("keyboard move to zone via hotkey: '2' moves focused item to zone b");
|
|
88
|
+
zoneA.querySelector("#a2").focus();
|
|
89
|
+
await nextTick("after refocus");
|
|
90
|
+
|
|
91
|
+
key(document.activeElement, "2");
|
|
92
|
+
await nextTick("after hotkey move");
|
|
93
|
+
|
|
94
|
+
await waitFor(() => zoneB.querySelector("#a2"), { label: "a2 moved to zone b" });
|
|
95
|
+
equal(Boolean(zoneA.querySelector("#a2")), false, "a2 removed from zone a");
|
|
96
|
+
|
|
97
|
+
log("keyboard move to empty zone via hotkey: '3' moves focused item to zone c");
|
|
98
|
+
zoneB.querySelector("#a2").focus();
|
|
99
|
+
await nextTick("after focus in b");
|
|
100
|
+
|
|
101
|
+
key(document.activeElement, "3");
|
|
102
|
+
await nextTick("after hotkey move 2");
|
|
103
|
+
|
|
104
|
+
await waitFor(() => zoneC.querySelector("#a2"), { label: "a2 moved to zone c" });
|
|
105
|
+
equal(Boolean(zoneB.querySelector("#a2")), false, "a2 removed from zone b");
|
|
106
|
+
|
|
107
|
+
log("done");
|
|
108
|
+
});
|
|
109
|
+
</script>
|
|
110
|
+
</body>
|
|
111
|
+
</html>
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { PwcChildrenObserverElement } from "../core/pwc-children-observer-element.js";
|
|
2
|
+
import { defineOnce } from "../core/utils.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* <pwc-zone-transfer>
|
|
6
|
+
*
|
|
7
|
+
* Drag&drop + keyboard sort, optional keyboard move across zones via per-zone hotkeys.
|
|
8
|
+
*
|
|
9
|
+
* Hooks (default):
|
|
10
|
+
* - Zones: pwc-zone-transfer-zone, [data-pwc-zone]
|
|
11
|
+
* - Items: pwc-zone-transfer-item, [data-pwc-item]
|
|
12
|
+
* - Handle: pwc-zone-transfer-handle, [data-pwc-handle]
|
|
13
|
+
*
|
|
14
|
+
* Zone hotkeys (optional):
|
|
15
|
+
* - data-pwc-zone-key="Enter" on a zone element
|
|
16
|
+
* Pressing that key moves the focused item to that zone.
|
|
17
|
+
*
|
|
18
|
+
* Keyboard:
|
|
19
|
+
* - ArrowUp/ArrowDown: move focus within current zone
|
|
20
|
+
* - Ctrl+ArrowUp/Ctrl+ArrowDown: reorder focused item within zone
|
|
21
|
+
* - Zone hotkey: move focused item to that zone (only if any zone hotkeys are defined)
|
|
22
|
+
*
|
|
23
|
+
* Events:
|
|
24
|
+
* - pwc-zone-transfer:change { itemId, fromZone, toZone, index, method }
|
|
25
|
+
*/
|
|
26
|
+
export class PwcZoneTransfer extends PwcChildrenObserverElement {
|
|
27
|
+
static events = ["dragstart", "dragover", "drop", "dragend", "keydown"];
|
|
28
|
+
static observeMode = "tree";
|
|
29
|
+
|
|
30
|
+
static zoneSelector = "pwc-zone-transfer-zone, [data-pwc-zone]";
|
|
31
|
+
static itemSelector = "pwc-zone-transfer-item, [data-pwc-item]";
|
|
32
|
+
static handleSelector = "pwc-zone-transfer-handle, [data-pwc-handle]";
|
|
33
|
+
|
|
34
|
+
onChildrenChanged() {
|
|
35
|
+
const items = this._items();
|
|
36
|
+
for (const item of items) {
|
|
37
|
+
if (!item.hasAttribute("draggable")) item.setAttribute("draggable", "true");
|
|
38
|
+
if (!item.hasAttribute("tabindex")) item.tabIndex = -1;
|
|
39
|
+
if (!item.hasAttribute("role")) item.setAttribute("role", "option");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
for (const zone of this._zones()) {
|
|
43
|
+
if (!zone.hasAttribute("role")) zone.setAttribute("role", "listbox");
|
|
44
|
+
if (!zone.hasAttribute("tabindex")) zone.tabIndex = -1;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const active = items.find((it) => it.tabIndex === 0) || items[0] || null;
|
|
48
|
+
for (const it of items) it.tabIndex = it === active ? 0 : -1;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
handleEvent(e) {
|
|
52
|
+
if (e.type === "dragstart") return this._onDragStart(e);
|
|
53
|
+
if (e.type === "dragover") return this._onDragOver(e);
|
|
54
|
+
if (e.type === "drop") return this._onDrop(e);
|
|
55
|
+
if (e.type === "dragend") return this._onDragEnd();
|
|
56
|
+
if (e.type === "keydown") return this._onKeyDown(e);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
_zones() {
|
|
60
|
+
return Array.from(this.querySelectorAll(this.constructor.zoneSelector));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
_items(zoneEl) {
|
|
64
|
+
return Array.from((zoneEl || this).querySelectorAll(this.constructor.itemSelector));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
_onDragStart(e) {
|
|
68
|
+
const item = this._closestItem(e.target);
|
|
69
|
+
if (!item) return;
|
|
70
|
+
|
|
71
|
+
if (item.querySelector(this.constructor.handleSelector) && !this._closestHandle(e.target)) {
|
|
72
|
+
e.preventDefault();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const zone = this._closestZone(item);
|
|
77
|
+
if (!zone) return;
|
|
78
|
+
|
|
79
|
+
this._drag = { item, fromZone: zone };
|
|
80
|
+
|
|
81
|
+
// Optional UX hint for native DnD
|
|
82
|
+
if (e.dataTransfer) e.dataTransfer.effectAllowed = "move";
|
|
83
|
+
|
|
84
|
+
item.classList.add("pwc-zone-transfer-dragging");
|
|
85
|
+
this._ensurePlaceholder(item);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
_onDragOver(e) {
|
|
89
|
+
if (!this._drag?.item) return;
|
|
90
|
+
|
|
91
|
+
const zone = this._closestZone(e.target);
|
|
92
|
+
if (!zone) return;
|
|
93
|
+
|
|
94
|
+
e.preventDefault();
|
|
95
|
+
|
|
96
|
+
// Optional UX hint for native DnD
|
|
97
|
+
if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
|
|
98
|
+
|
|
99
|
+
this._ensurePlaceholder(this._drag.item);
|
|
100
|
+
|
|
101
|
+
const beforeEl = this._beforeFromPointer(zone, e, this._drag.item);
|
|
102
|
+
this._movePlaceholder(zone, beforeEl);
|
|
103
|
+
|
|
104
|
+
this._drag.overZone = zone;
|
|
105
|
+
this._drag.overMethod = beforeEl ? "before" : "append";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
_onDrop(e) {
|
|
109
|
+
if (!this._drag?.item) return;
|
|
110
|
+
|
|
111
|
+
const zone = this._closestZone(e.target);
|
|
112
|
+
if (!zone) return;
|
|
113
|
+
|
|
114
|
+
e.preventDefault();
|
|
115
|
+
|
|
116
|
+
this._applyMove(this._drag.item, this._drag.fromZone, zone, this._drag.overMethod || "append");
|
|
117
|
+
this._clearPlaceholder();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
_onDragEnd() {
|
|
121
|
+
if (this._drag?.item) this._drag.item.classList.remove("pwc-zone-transfer-dragging");
|
|
122
|
+
this._drag = null;
|
|
123
|
+
this._clearPlaceholder();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
_onKeyDown(e) {
|
|
127
|
+
if (e.target?.closest?.("input,textarea,select,button,[contenteditable]")) return;
|
|
128
|
+
|
|
129
|
+
const item = this._closestItem(e.target);
|
|
130
|
+
if (!item) return;
|
|
131
|
+
|
|
132
|
+
if (e.key === "ArrowUp" || e.key === "ArrowDown") {
|
|
133
|
+
e.preventDefault();
|
|
134
|
+
const dir = e.key === "ArrowDown" ? 1 : -1;
|
|
135
|
+
if (e.ctrlKey || e.metaKey) this._keyboardReorder(item, dir);
|
|
136
|
+
else this._focusSibling(item, dir);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const zone = this._zoneByHotkey(e.key);
|
|
141
|
+
if (!zone) return;
|
|
142
|
+
|
|
143
|
+
e.preventDefault();
|
|
144
|
+
this._keyboardMoveToZone(item, zone);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
_keyboardReorder(item, dir) {
|
|
148
|
+
const zone = this._closestZone(item);
|
|
149
|
+
if (!zone) return;
|
|
150
|
+
|
|
151
|
+
const items = this._items(zone);
|
|
152
|
+
const i = items.indexOf(item);
|
|
153
|
+
if (i < 0) return;
|
|
154
|
+
|
|
155
|
+
const j = i + dir;
|
|
156
|
+
if (j < 0 || j >= items.length) return;
|
|
157
|
+
|
|
158
|
+
zone.insertBefore(item, dir > 0 ? items[j].nextElementSibling : items[j]);
|
|
159
|
+
|
|
160
|
+
for (const it of this._items()) it.tabIndex = it === item ? 0 : -1;
|
|
161
|
+
item.focus();
|
|
162
|
+
|
|
163
|
+
this._emitChange(item, zone, zone, this._indexInZone(item, zone), "before");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
_keyboardMoveToZone(item, zone) {
|
|
167
|
+
const fromZone = this._closestZone(item);
|
|
168
|
+
if (!fromZone || fromZone === zone) return;
|
|
169
|
+
|
|
170
|
+
zone.appendChild(item);
|
|
171
|
+
|
|
172
|
+
for (const it of this._items()) it.tabIndex = it === item ? 0 : -1;
|
|
173
|
+
item.focus();
|
|
174
|
+
|
|
175
|
+
this._emitChange(item, fromZone, zone, this._indexInZone(item, zone), "append");
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
_zoneByHotkey(key) {
|
|
179
|
+
const zones = this._zones();
|
|
180
|
+
if (!zones.some((z) => z.hasAttribute("data-pwc-zone-key"))) return null;
|
|
181
|
+
return zones.find((z) => z.getAttribute("data-pwc-zone-key") === key) || null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
_emitChange(item, fromZone, toZone, index, method) {
|
|
185
|
+
this.dispatchEvent(
|
|
186
|
+
new CustomEvent("pwc-zone-transfer:change", {
|
|
187
|
+
bubbles: true,
|
|
188
|
+
detail: {
|
|
189
|
+
itemId: this._itemId(item),
|
|
190
|
+
fromZone: this._zoneName(fromZone),
|
|
191
|
+
toZone: this._zoneName(toZone),
|
|
192
|
+
index,
|
|
193
|
+
method
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
_applyMove(item, fromZone, toZone, method) {
|
|
200
|
+
if (this._placeholder?.parentNode === toZone) toZone.insertBefore(item, this._placeholder);
|
|
201
|
+
else toZone.appendChild(item);
|
|
202
|
+
|
|
203
|
+
for (const it of this._items()) it.tabIndex = it === item ? 0 : -1;
|
|
204
|
+
|
|
205
|
+
this._emitChange(item, fromZone, toZone, this._indexInZone(item, toZone), method);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
_focusSibling(item, dir) {
|
|
209
|
+
const zone = this._closestZone(item);
|
|
210
|
+
if (!zone) return;
|
|
211
|
+
|
|
212
|
+
const items = this._items(zone);
|
|
213
|
+
const i = items.indexOf(item);
|
|
214
|
+
if (i < 0) return;
|
|
215
|
+
|
|
216
|
+
const next = items[i + dir];
|
|
217
|
+
if (!next) return;
|
|
218
|
+
|
|
219
|
+
item.tabIndex = -1;
|
|
220
|
+
next.tabIndex = 0;
|
|
221
|
+
next.focus();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
_ensurePlaceholder(itemEl) {
|
|
225
|
+
if (this._placeholder?.isConnected) return;
|
|
226
|
+
|
|
227
|
+
this._placeholder = document.createElement("div");
|
|
228
|
+
this._placeholder.className = "pwc-zone-transfer-placeholder";
|
|
229
|
+
this._placeholder.setAttribute("aria-hidden", "true");
|
|
230
|
+
this._placeholder.style.height = `${Math.max(8, Math.round(itemEl.getBoundingClientRect().height || 0))}px`;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
_movePlaceholder(zoneEl, beforeEl) {
|
|
234
|
+
if (!this._placeholder) return;
|
|
235
|
+
if (beforeEl && beforeEl.parentNode === zoneEl) zoneEl.insertBefore(this._placeholder, beforeEl);
|
|
236
|
+
else zoneEl.appendChild(this._placeholder);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
_clearPlaceholder() {
|
|
240
|
+
if (this._placeholder?.parentNode) this._placeholder.parentNode.removeChild(this._placeholder);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
_beforeFromPointer(zoneEl, e, draggedItem) {
|
|
244
|
+
const y = e.clientY;
|
|
245
|
+
for (const it of this._items(zoneEl)) {
|
|
246
|
+
if (it === draggedItem || it === this._placeholder) continue;
|
|
247
|
+
const r = it.getBoundingClientRect();
|
|
248
|
+
if (y <= r.top + r.height / 2) return it;
|
|
249
|
+
}
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
_closestZone(node) {
|
|
254
|
+
if (!(node instanceof Element)) return null;
|
|
255
|
+
const zone = node.closest(this.constructor.zoneSelector);
|
|
256
|
+
return zone && this.contains(zone) ? zone : null;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
_closestItem(node) {
|
|
260
|
+
if (!(node instanceof Element)) return null;
|
|
261
|
+
const item = node.closest(this.constructor.itemSelector);
|
|
262
|
+
return item && this.contains(item) ? item : null;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
_closestHandle(node) {
|
|
266
|
+
if (!(node instanceof Element)) return null;
|
|
267
|
+
const handle = node.closest(this.constructor.handleSelector);
|
|
268
|
+
return handle && this.contains(handle) ? handle : null;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
_zoneName(zoneEl) {
|
|
272
|
+
if (!zoneEl) return "";
|
|
273
|
+
return zoneEl.tagName.toLowerCase() === "pwc-zone-transfer-zone"
|
|
274
|
+
? zoneEl.getAttribute("name") || ""
|
|
275
|
+
: zoneEl.getAttribute("data-pwc-zone") || "";
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
_itemId(itemEl) {
|
|
279
|
+
if (!itemEl) return "";
|
|
280
|
+
return itemEl.tagName.toLowerCase() === "pwc-zone-transfer-item"
|
|
281
|
+
? itemEl.getAttribute("id") || itemEl.getAttribute("data-id") || ""
|
|
282
|
+
: itemEl.getAttribute("data-pwc-item") || itemEl.getAttribute("id") || "";
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
_indexInZone(itemEl, zoneEl) {
|
|
286
|
+
return Math.max(0, this._items(zoneEl).indexOf(itemEl));
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export function define() {
|
|
291
|
+
defineOnce("pwc-zone-transfer", PwcZoneTransfer);
|
|
292
|
+
}
|