Skip to content

Commit

Permalink
Merge pull request #1120 from Patternslib/pat-sortable-clone
Browse files Browse the repository at this point in the history
fix(pat-sortable): Initialize sorting on cloned elements.
  • Loading branch information
thet authored Dec 22, 2022
2 parents 7197d37 + d7abbc0 commit 283712d
Show file tree
Hide file tree
Showing 5 changed files with 242 additions and 100 deletions.
16 changes: 16 additions & 0 deletions src/core/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,20 @@ const submit_event = () => {
});
};

const dragstart_event = () => {
return new Event("dragstart", {
bubbles: true,
cancelable: true,
});
};

const dragend_event = () => {
return new Event("dragend", {
bubbles: true,
cancelable: true,
});
};

export default {
add_event_listener: add_event_listener,
remove_event_listener: remove_event_listener,
Expand All @@ -206,4 +220,6 @@ export default {
mouseup_event: mouseup_event,
scroll_event: scroll_event,
submit_event: submit_event,
dragstart_event: dragstart_event,
dragend_event: dragend_event,
};
18 changes: 18 additions & 0 deletions src/core/events.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,24 @@ describe("core.events tests", () => {
await utils.timeout(1);
expect(catched).toBe("outer");
});

it("dragstart event", async () => {
outer.addEventListener("dragstart", () => {
catched = "outer";
});
inner.dispatchEvent(events.dragstart_event());
await utils.timeout(1);
expect(catched).toBe("outer");
});

it("dragend event", async () => {
outer.addEventListener("dragend", () => {
catched = "outer";
});
inner.dispatchEvent(events.dragend_event());
await utils.timeout(1);
expect(catched).toBe("outer");
});
});

describe("3 - jQuery vs native", () => {
Expand Down
12 changes: 12 additions & 0 deletions src/pat/sortable/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
<script src="/bundle.min.js"></script>
</head>
<body>

<h2>Vertical sorting</h2>
<ul class="pat-sortable">
<li class="sortable-item">Item 3</li>
<li class="sortable-item">Item 5</li>
Expand All @@ -16,6 +18,7 @@
<li class="sortable-item">Item 2</li>
</ul>

<h2>Horizontal sorting</h2>
<ul class="pat-sortable vertical">
<li class="sortable-item">Item 3</li>
<li class="sortable-item">Item 5</li>
Expand All @@ -25,6 +28,15 @@
<li class="sortable-item">Item 2</li>
</ul>

<h2>Vertical sorting with pat-clone</h2>
<button class="clone-trigger-1">Add item</button>
<ul class="pat-sortable pat-clone"
data-pat-clone="template: .clone-template-1; trigger-element: .clone-trigger-1">
</ul>
<template class="clone-template-1">
<li class="sortable-item">Item #{1}</li>
</template>


<style>
.sortable-item {
Expand Down
77 changes: 53 additions & 24 deletions src/pat/sortable/sortable.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import $ from "jquery";
import Base from "../../core/base";
import events from "../../core/events";
import Parser from "../../core/parser";

export const parser = new Parser("sortable");
Expand Down Expand Up @@ -28,12 +29,25 @@ export default Base.extend({
/* Handler which gets called when pat-update is triggered within
* the .pat-sortable element.
*/
if (data?.pattern == "clone") {
this.recordPositions();
data.$el.on("dragstart", this.onDragStart.bind(this));
data.$el.on("dragend", this.onDragEnd.bind(this));
if (data?.pattern !== "clone" || data?.action !== "added" || !data?.dom) {
// Nothing to do.
return;
}
return true;

this.recordPositions();

events.add_event_listener(
data.dom,
"dragstart",
"pat-sortable--dragstart",
this.onDragStart.bind(this)
);
events.add_event_listener(
data.dom,
"dragend",
"pat-sortable--dragend",
this.onDragEnd.bind(this)
);
},

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

addHandles: function () {
var $sortables_without_handles = this.$sortables.filter(function () {
return $(this).find(".sortable-handle").length === 0;
});
var $handles = $('<a href="#" class="sortable-handle">⇕</a>').appendTo(
$sortables_without_handles
);
if ("draggable" in document.createElement("span")) {
$handles.attr("draggable", true);
} else {
$handles.on("selectstart", function (ev) {
ev.preventDefault();
for (const sortable of [...this.$sortables].filter(
(it) => !it.querySelector(".sortable-handle")
)) {
// TODO: we should change to a <button>.
const handle = document.createElement("a");
handle.textContent = "⇕";
handle.classList.add("sortable-handle");
handle.setAttribute("draggable", "true");
handle.setAttribute("href", "#");
handle.setAttribute("title", "Drag to reorder");
handle.setAttribute("aria-label", "Drag to reorder");
sortable.insertBefore(handle, sortable.firstChild);

// TODO: remove when element is a button.
events.add_event_listener(handle, "click", "pat-sortable--click", (e) => {
e.preventDefault();
});

events.add_event_listener(
handle,
"dragstart",
"pat-sortable--dragstart",
this.onDragStart.bind(this)
);
events.add_event_listener(
handle,
"dragend",
"pat-sortable--dragend",
this.onDragEnd.bind(this)
);
}
$handles.on("dragstart", this.onDragStart.bind(this));
$handles.on("dragend", this.onDragEnd.bind(this));
return this;
},

initScrolling: function () {
Expand Down Expand Up @@ -140,12 +169,12 @@ export default Base.extend({
},

onDragStart: function (ev) {
var $handle = $(ev.target),
$dragged = $handle.parent(),
that = this;
if (typeof ev.originalEvent !== "undefined") {
var $handle = $(ev.target);
var $dragged = $handle.parent();
var that = this;
if (ev.originalEvent?.dataTransfer) {
// Firefox seems to need this set to any value
ev.originalEvent.dataTransfer.setData("Text", "");
ev.originalEvent.dataTransfer?.setData("Text", "");
ev.originalEvent.dataTransfer.effectAllowed = ["move"];
if ("setDragImage" in ev.originalEvent.dataTransfer) {
ev.originalEvent.dataTransfer.setDragImage($dragged[0], 0, 0);
Expand Down
Loading

0 comments on commit 283712d

Please sign in to comment.