Skip to content

Commit d7abbc0

Browse files
committed
fix(pat-sortable): Initialize sorting on cloned elements.
Fix sorting behavior on cloned elements, which broke on Patternslib 9.8.0-alpha.0.
1 parent bf25dc4 commit d7abbc0

File tree

3 files changed

+92
-22
lines changed

3 files changed

+92
-22
lines changed

src/pat/sortable/index.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
<script src="/bundle.min.js"></script>
88
</head>
99
<body>
10+
11+
<h2>Vertical sorting</h2>
1012
<ul class="pat-sortable">
1113
<li class="sortable-item">Item 3</li>
1214
<li class="sortable-item">Item 5</li>
@@ -16,6 +18,7 @@
1618
<li class="sortable-item">Item 2</li>
1719
</ul>
1820

21+
<h2>Horizontal sorting</h2>
1922
<ul class="pat-sortable vertical">
2023
<li class="sortable-item">Item 3</li>
2124
<li class="sortable-item">Item 5</li>
@@ -25,6 +28,15 @@
2528
<li class="sortable-item">Item 2</li>
2629
</ul>
2730

31+
<h2>Vertical sorting with pat-clone</h2>
32+
<button class="clone-trigger-1">Add item</button>
33+
<ul class="pat-sortable pat-clone"
34+
data-pat-clone="template: .clone-template-1; trigger-element: .clone-trigger-1">
35+
</ul>
36+
<template class="clone-template-1">
37+
<li class="sortable-item">Item #{1}</li>
38+
</template>
39+
2840

2941
<style>
3042
.sortable-item {

src/pat/sortable/sortable.js

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import $ from "jquery";
22
import Base from "../../core/base";
3+
import events from "../../core/events";
34
import Parser from "../../core/parser";
45

56
export const parser = new Parser("sortable");
@@ -28,12 +29,25 @@ export default Base.extend({
2829
/* Handler which gets called when pat-update is triggered within
2930
* the .pat-sortable element.
3031
*/
31-
if (data?.pattern == "clone") {
32-
this.recordPositions();
33-
data.$el.on("dragstart", this.onDragStart.bind(this));
34-
data.$el.on("dragend", this.onDragEnd.bind(this));
32+
if (data?.pattern !== "clone" || data?.action !== "added" || !data?.dom) {
33+
// Nothing to do.
34+
return;
3535
}
36-
return true;
36+
37+
this.recordPositions();
38+
39+
events.add_event_listener(
40+
data.dom,
41+
"dragstart",
42+
"pat-sortable--dragstart",
43+
this.onDragStart.bind(this)
44+
);
45+
events.add_event_listener(
46+
data.dom,
47+
"dragend",
48+
"pat-sortable--dragend",
49+
this.onDragEnd.bind(this)
50+
);
3751
},
3852

3953
recordPositions: function () {
@@ -49,22 +63,37 @@ export default Base.extend({
4963
},
5064

5165
addHandles: function () {
52-
var $sortables_without_handles = this.$sortables.filter(function () {
53-
return $(this).find(".sortable-handle").length === 0;
54-
});
55-
var $handles = $('<a href="#" class="sortable-handle">⇕</a>').appendTo(
56-
$sortables_without_handles
57-
);
58-
if ("draggable" in document.createElement("span")) {
59-
$handles.attr("draggable", true);
60-
} else {
61-
$handles.on("selectstart", function (ev) {
62-
ev.preventDefault();
66+
for (const sortable of [...this.$sortables].filter(
67+
(it) => !it.querySelector(".sortable-handle")
68+
)) {
69+
// TODO: we should change to a <button>.
70+
const handle = document.createElement("a");
71+
handle.textContent = "⇕";
72+
handle.classList.add("sortable-handle");
73+
handle.setAttribute("draggable", "true");
74+
handle.setAttribute("href", "#");
75+
handle.setAttribute("title", "Drag to reorder");
76+
handle.setAttribute("aria-label", "Drag to reorder");
77+
sortable.insertBefore(handle, sortable.firstChild);
78+
79+
// TODO: remove when element is a button.
80+
events.add_event_listener(handle, "click", "pat-sortable--click", (e) => {
81+
e.preventDefault();
6382
});
83+
84+
events.add_event_listener(
85+
handle,
86+
"dragstart",
87+
"pat-sortable--dragstart",
88+
this.onDragStart.bind(this)
89+
);
90+
events.add_event_listener(
91+
handle,
92+
"dragend",
93+
"pat-sortable--dragend",
94+
this.onDragEnd.bind(this)
95+
);
6496
}
65-
$handles.on("dragstart", this.onDragStart.bind(this));
66-
$handles.on("dragend", this.onDragEnd.bind(this));
67-
return this;
6897
},
6998

7099
initScrolling: function () {
@@ -140,9 +169,9 @@ export default Base.extend({
140169
},
141170

142171
onDragStart: function (ev) {
143-
var $handle = $(ev.target),
144-
$dragged = $handle.parent(),
145-
that = this;
172+
var $handle = $(ev.target);
173+
var $dragged = $handle.parent();
174+
var that = this;
146175
if (ev.originalEvent?.dataTransfer) {
147176
// Firefox seems to need this set to any value
148177
ev.originalEvent.dataTransfer?.setData("Text", "");

src/pat/sortable/sortable.test.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,4 +151,33 @@ describe("pat-sortable", function () {
151151
expect(data.action).toBe("changed");
152152
expect(data.dom).toBe(dragging_element);
153153
});
154+
155+
it("5 - Initializes sorting behavior on pat-clone'd elements.", async function () {
156+
const Clone = (await import("../clone/clone")).default;
157+
158+
document.body.innerHTML = `
159+
<ul class="pat-sortable pat-clone"
160+
data-pat-clone="template: .clone-template; trigger-element: .clone-trigger">
161+
</ul>
162+
<button class="clone-trigger">Clone</button>
163+
<template class="clone-template">
164+
<li>item</li>
165+
</template>
166+
`;
167+
const el = document.querySelector(".pat-sortable");
168+
const sortable = new Sortable(el);
169+
new Clone(el);
170+
171+
const clone_trigger = document.querySelector(".clone-trigger");
172+
clone_trigger.click();
173+
174+
const cloned = el.querySelector("li");
175+
expect(cloned).toBeTruthy();
176+
177+
const drag_handle = cloned.querySelector(".sortable-handle");
178+
expect(drag_handle).toBeTruthy();
179+
drag_handle.dispatchEvent(new Event("dragstart"));
180+
181+
expect(cloned.classList.contains(sortable.options.dragClass)).toBe(true);
182+
});
154183
});

0 commit comments

Comments
 (0)