[IMP] web: draggable_hook_builder option to ignore selectively
The way the `ignore` property is implemented means that nested draggable elements under a single element matching the `ignore` selector will all be disabled. In Knowledge, the sidebar allowing to move articles around has nested draggable elements that the user should be able to move around, under other draggable elements that should stay in place (depending on access rights). Therefore, this commit introduces another way to disable dragging with the `preventDrag` property which is a function that receives the dragging candidate and returns a boolean to indicate whether the element can effectively be dragged or not in its current situation. task-3456741 closes odoo/odoo#135981 X-original-commit: eb302dbf48500b2eee61a0efb865868866fb454b Related: odoo/enterprise#47640 Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com> Signed-off-by: Damien Abeloos (abd) <abd@odoo.com>
This commit is contained in:
@@ -34,6 +34,7 @@ import { hasTouch, isBrowserFirefox, isIOS } from "../browser/feature_detection"
|
||||
* @property {boolean} [followCursor=true]
|
||||
* @property {string | null} [cursor=null]
|
||||
* @property {() => boolean} [enable=() => false]
|
||||
* @property {(HTMLElement) => boolean} [preventDrag=(el) => false]
|
||||
* @property {Position} [pointer={ x: 0, y: 0 }]
|
||||
* @property {EdgeScrollingOptions} [edgeScrolling]
|
||||
* @property {number} [delay]
|
||||
@@ -77,6 +78,7 @@ const DRAGGED_CLASS = "o_dragged";
|
||||
|
||||
const DEFAULT_ACCEPTED_PARAMS = {
|
||||
enable: [Boolean, Function],
|
||||
preventDrag: [Function],
|
||||
ref: [Object],
|
||||
elements: [String],
|
||||
handle: [String, Function],
|
||||
@@ -89,6 +91,7 @@ const DEFAULT_ACCEPTED_PARAMS = {
|
||||
const DEFAULT_DEFAULT_PARAMS = {
|
||||
elements: `.${DRAGGABLE_CLASS}`,
|
||||
enable: true,
|
||||
preventDrag: () => false,
|
||||
edgeScrolling: {
|
||||
speed: 10,
|
||||
threshold: 30,
|
||||
@@ -658,11 +661,13 @@ export function makeDraggableHook(hookParams) {
|
||||
// outside of the window.
|
||||
dragEnd(null);
|
||||
|
||||
const fullSelectorEl = ev.target.closest(ctx.fullSelector);
|
||||
if (
|
||||
ev.button !== LEFT_CLICK ||
|
||||
!ctx.enable() ||
|
||||
!ev.target.closest(ctx.fullSelector) ||
|
||||
(ctx.ignoreSelector && ev.target.closest(ctx.ignoreSelector))
|
||||
!fullSelectorEl ||
|
||||
(ctx.ignoreSelector && ev.target.closest(ctx.ignoreSelector)) ||
|
||||
ctx.preventDrag(fullSelectorEl)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@@ -886,6 +891,7 @@ export function makeDraggableHook(hookParams) {
|
||||
/** @type {DraggableHookContext} */
|
||||
const ctx = {
|
||||
enable: () => false,
|
||||
preventDrag: () => false,
|
||||
ref: params.ref,
|
||||
ignoreSelector: null,
|
||||
fullSelector: null,
|
||||
@@ -911,6 +917,11 @@ export function makeDraggableHook(hookParams) {
|
||||
// Enable getter
|
||||
ctx.enable = actualParams.enable;
|
||||
|
||||
// Dragging constraint
|
||||
if (actualParams.preventDrag) {
|
||||
ctx.preventDrag = actualParams.preventDrag;
|
||||
}
|
||||
|
||||
// Selectors
|
||||
ctx.elementSelector = actualParams.elements;
|
||||
if (!ctx.elementSelector) {
|
||||
|
||||
@@ -22,7 +22,11 @@ import { makeDraggableHook } from "@web/core/utils/draggable_hook_builder";
|
||||
* @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 targeting elements that must
|
||||
* initiate a drag.
|
||||
* initiate a drag (recursive in depth: all draggable elements under an
|
||||
* ignored element will be locked in place)
|
||||
* @property {(HTMLElement) => boolean} [preventDrag] function receiving a
|
||||
* the current target for dragging (element) and returning a boolean, whether
|
||||
* the element can be effectively dragged or not.
|
||||
* @property {boolean | () => boolean} [connectGroups] whether elements can be dragged
|
||||
* across different parent groups. Note that it requires a `groups` param to work.
|
||||
* @property {string | () => string} [cursor] cursor style during the dragging sequence.
|
||||
|
||||
@@ -215,6 +215,71 @@ QUnit.module("Draggable", ({ beforeEach }) => {
|
||||
assert.verifySteps([]);
|
||||
});
|
||||
|
||||
QUnit.test("Ignore specific elements in a nested draggable", async (assert) => {
|
||||
assert.expect(7);
|
||||
|
||||
class List extends Component {
|
||||
static components = { List };
|
||||
static template = xml`
|
||||
<div t-ref="root" class="root">
|
||||
<ul class="list">
|
||||
<li t-foreach="[0, 1]" t-as="i" t-key="i"
|
||||
t-attf-class="item parent #{ i % 2 ? 'ignored' : 'not-ignored' }">
|
||||
<span t-esc="'parent' + i" />
|
||||
<ul class="list">
|
||||
<li t-foreach="[0, 1]" t-as="j" t-key="j"
|
||||
t-attf-class="item child #{ j % 2 ? 'ignored' : 'not-ignored' }">
|
||||
<span t-esc="'child' + j" />
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>`;
|
||||
setup() {
|
||||
useDraggable({
|
||||
ref: useRef("root"),
|
||||
elements: ".item",
|
||||
preventDrag: (el) => el.classList.contains('ignored'),
|
||||
onDragStart() {
|
||||
assert.step("drag");
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await mount(List, target);
|
||||
|
||||
assert.verifySteps([]);
|
||||
|
||||
// Drag ignored under non-ignored -> block
|
||||
await dragAndDrop(
|
||||
".not-ignored.parent .ignored.child",
|
||||
".not-ignored.parent .not-ignored.child"
|
||||
);
|
||||
assert.verifySteps([]);
|
||||
|
||||
// Drag not-ignored-under not-ignored -> succeed
|
||||
await dragAndDrop(
|
||||
".not-ignored.parent .not-ignored.child",
|
||||
".not-ignored.parent .ignored.child"
|
||||
);
|
||||
assert.verifySteps(["drag"]);
|
||||
|
||||
// Drag ignored under ignored -> block
|
||||
await dragAndDrop(
|
||||
".ignored.parent .ignored.child",
|
||||
".ignored.parent .not-ignored.child"
|
||||
);
|
||||
assert.verifySteps([]);
|
||||
|
||||
// Drag not-ignored under ignored -> succeed
|
||||
await dragAndDrop(
|
||||
".ignored.parent .not-ignored.child",
|
||||
".ignored.parent .ignored.child"
|
||||
);
|
||||
assert.verifySteps(["drag"]);
|
||||
});
|
||||
|
||||
QUnit.test("Dragging element with touch event", async (assert) => {
|
||||
assert.expect(10);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user