[IMP] web: Add 'ignore' param to useSortable

This commit adds a new parameter to the 'useSortable' hook that allows
HTML elements to prevent a drag sequence. This 'ignore' param can be a
string or a function returning a string, that will be a selector
targetting the elements that need to prevent drag & drop sequences.

Part-of: odoo/odoo#98791
This commit is contained in:
Julien Mougenot
2022-08-25 12:05:15 +02:00
parent 3bac0f9d68
commit cf4a8f198f
2 changed files with 66 additions and 4 deletions
+19 -3
View File
@@ -22,6 +22,8 @@ const { useEffect, useEnv, useExternalListener, onWillUnmount } = owl;
* work on group elements during the dragging sequence.
* @property {string | () => string} [handle] additional selector for when the dragging
* sequence must be initiated when dragging on a certain part of the element.
* @property {string | () => string} [ignore] selector targetting elements that must
* initiate a drag.
* @property {boolean | () => boolean} [connectGroups] whether elements can be dragged
* accross different parent groups. Note that it requires a `groups` param to work.
* @property {string | () => string} [cursor] cursor style during the dragging sequence.
@@ -63,6 +65,7 @@ const SORTABLE_PARAMS = {
elements: ["string"],
groups: ["string", "function"],
handle: ["string", "function"],
ignore: ["string", "function"],
connectGroups: ["boolean", "function"],
cursor: ["string"],
};
@@ -155,6 +158,11 @@ export function useSortable(params) {
* @type {string | null}
*/
let elementSelector = null;
/**
* Stores the full selector used to initiate a drag sequence.
* @type {string | null}
*/
let ignoreSelector = null;
/**
* Stores the full selector used to initiate a drag sequence.
* @type {string | null}
@@ -339,7 +347,12 @@ export function useSortable(params) {
// outside of the window.
dragStop(true);
if (ev.button !== LEFT_CLICK || !enabled || !ev.target.closest(fullSelector)) {
if (
ev.button !== LEFT_CLICK ||
!enabled ||
!ev.target.closest(fullSelector) ||
(ignoreSelector && ev.target.closest(ignoreSelector))
) {
return;
}
@@ -407,8 +420,8 @@ export function useSortable(params) {
currentContainerRect.height -= ptop + pbottom;
// Prepares the ghost element
ghost = currentElement.cloneNode(true);
ghost.style.visibility = "hidden";
ghost = currentElement.cloneNode(false);
ghost.style = `visibility: hidden; display: block; width: ${width}px; height:${height}px;`;
cleanups.push(() => ghost.remove());
// Binds handlers on eligible groups, if the elements are not confined to
@@ -526,6 +539,9 @@ export function useSortable(params) {
if (actualParams.handle) {
allSelectors.push(actualParams.handle);
}
if (actualParams.ignore) {
ignoreSelector = actualParams.ignore;
}
fullSelector = allSelectors.join(" ");
// Connection accross groups
+47 -1
View File
@@ -201,7 +201,7 @@ QUnit.module("UI", ({ beforeEach }) => {
QUnit.test("Dynamically disable sortable feature", async (assert) => {
assert.expect(4);
let state = reactive({ enableSortable: true });
const state = reactive({ enableSortable: true });
class List extends Component {
setup() {
this.state = useState(state);
@@ -274,4 +274,50 @@ QUnit.module("UI", ({ beforeEach }) => {
assert.ok(true, "No drag sequence should have been initiated");
});
QUnit.test("Ignore specified elements elements", async (assert) => {
assert.expect(6);
class List extends Component {
setup() {
useSortable({
ref: useRef("root"),
elements: ".item",
ignore: ".ignored",
onStart() {
assert.step("drag");
},
});
}
}
List.template = xml`
<div t-ref="root" class="root">
<ul class="list">
<li t-foreach="[1, 2, 3]" t-as="i" t-key="i" class="item">
<span class="ignored" t-esc="i" />
<span class="not-ignored" t-esc="i" />
</li>
</ul>
</div>`;
await mount(List, target);
assert.verifySteps([]);
// Drag root item element
await dragAndDrop(".item:first-child", ".item:nth-child(2)");
assert.verifySteps(["drag"]);
// Drag ignored element
await dragAndDrop(".item:first-child .not-ignored", ".item:nth-child(2)");
assert.verifySteps(["drag"]);
// Drag non-ignored element
await dragAndDrop(".item:first-child .ignored", ".item:nth-child(2)");
assert.verifySteps([]);
});
});