@jsenv/dom 0.17.19 → 0.17.20
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/dist/jsenv_dom.js +114 -26
- package/package.json +1 -1
package/dist/jsenv_dom.js
CHANGED
|
@@ -261,11 +261,22 @@ const chainEvent = (customEvent, parentEvent) => {
|
|
|
261
261
|
if (!parentEvent) {
|
|
262
262
|
return customEvent;
|
|
263
263
|
}
|
|
264
|
-
if (!customEvent.detail) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
264
|
+
if (!customEvent.detail || typeof customEvent.detail !== "object") {
|
|
265
|
+
// A native event has nowhere to hang the chain: `Event` has no detail at
|
|
266
|
+
// all and `UIEvent` (so `InputEvent` too) exposes it as a readonly number.
|
|
267
|
+
// Give it an own detail object, shadowing the prototype getter, so a
|
|
268
|
+
// synthetic event dispatched on behalf of a gesture can still say what
|
|
269
|
+
// caused it.
|
|
270
|
+
if (nativeDetailHasMeaning(customEvent)) {
|
|
271
|
+
console.warn(
|
|
272
|
+
`Chaining "${customEvent.type}" to "${parentEvent.type}" replaces its native detail (${customEvent.detail}), which carries the click count on this event type. Chain a custom event instead, or read the click count before chaining.`,
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
Object.defineProperty(customEvent, "detail", {
|
|
276
|
+
value: {},
|
|
277
|
+
configurable: true,
|
|
278
|
+
enumerable: true,
|
|
279
|
+
});
|
|
269
280
|
}
|
|
270
281
|
// Always build eventChain from the first wrapping so callers can rely on it
|
|
271
282
|
// being present whenever `parentEvent` is set.
|
|
@@ -309,6 +320,24 @@ const findEvent = (event, predicate) => {
|
|
|
309
320
|
return undefined;
|
|
310
321
|
};
|
|
311
322
|
|
|
323
|
+
// `detail` is a click count on the pointer events that define one, and 0
|
|
324
|
+
// everywhere else (`input`, `focus`, `wheel`…). Overwriting it there loses the
|
|
325
|
+
// only way to tell a real click from a keyboard/programmatic one (detail === 0),
|
|
326
|
+
// so those events must not be chained.
|
|
327
|
+
const EVENT_TYPES_WITH_MEANINGFUL_DETAIL = new Set([
|
|
328
|
+
"click",
|
|
329
|
+
"auxclick",
|
|
330
|
+
"dblclick",
|
|
331
|
+
"mousedown",
|
|
332
|
+
"mouseup",
|
|
333
|
+
]);
|
|
334
|
+
const nativeDetailHasMeaning = (event) => {
|
|
335
|
+
if (EVENT_TYPES_WITH_MEANINGFUL_DETAIL.has(event.type)) {
|
|
336
|
+
return true;
|
|
337
|
+
}
|
|
338
|
+
return typeof event.detail === "number" && event.detail !== 0;
|
|
339
|
+
};
|
|
340
|
+
|
|
312
341
|
const resolveEventPredicate = (predicate) => {
|
|
313
342
|
if (typeof predicate === "string") {
|
|
314
343
|
return (e) => e.type === predicate;
|
|
@@ -9117,12 +9146,16 @@ const css$4 = /* css */`
|
|
|
9117
9146
|
/* A source taken by long press must let the scroll through until the grab —
|
|
9118
9147
|
which is exactly what the long press is there to tell apart. Zoom has
|
|
9119
9148
|
nothing to do with the gesture and nobody should lose it by resting a
|
|
9120
|
-
finger on a word.
|
|
9149
|
+
finger on a word.
|
|
9150
|
+
|
|
9151
|
+
Vertical, because that is the way the page and the lists in it go: a
|
|
9152
|
+
source dragged along one axis is surrounded by something scrolling along
|
|
9153
|
+
that same axis (a row of a list runs the way the list scrolls), and a
|
|
9154
|
+
source dragged both ways sits on the usual vertical page. */
|
|
9121
9155
|
touch-action: pan-y pinch-zoom;
|
|
9122
9156
|
}
|
|
9123
9157
|
[data-drag-source="x"] {
|
|
9124
|
-
/*
|
|
9125
|
-
knows which way what surrounds the source scrolls. */
|
|
9158
|
+
/* …and the sideways one, for the same reason read the other way. */
|
|
9126
9159
|
touch-action: pan-x pinch-zoom;
|
|
9127
9160
|
}
|
|
9128
9161
|
[data-drag-on-contact] [data-drag-source],
|
|
@@ -9158,23 +9191,36 @@ import.meta.css = [css$4, "@jsenv/dom/src/interaction/drag/drag_after_intent.js"
|
|
|
9158
9191
|
*
|
|
9159
9192
|
* On the element and not on the window, so the rest of the page keeps its
|
|
9160
9193
|
* touches on the compositor's fast path.
|
|
9194
|
+
*
|
|
9195
|
+
* Exported because a drag does not always begin on a drag source: a copy caught
|
|
9196
|
+
* on its way home is pressed through the pictures of a view transition, and the
|
|
9197
|
+
* touch lands on the document root (see letCopyBeCaught in drag_to.js). Same
|
|
9198
|
+
* rule, other element — and it has to be the same function, or the listener put
|
|
9199
|
+
* down is not the one taken back off.
|
|
9161
9200
|
*/
|
|
9162
9201
|
const keepTouchRefusable = () => {
|
|
9163
9202
|
// Being registered IS the whole of it — see above.
|
|
9164
9203
|
};
|
|
9165
9204
|
|
|
9166
9205
|
/**
|
|
9167
|
-
* Says an element is something a drag can start from
|
|
9206
|
+
* Says an element is something a drag can start from, and which way that drag
|
|
9207
|
+
* goes.
|
|
9208
|
+
*
|
|
9209
|
+
* The axes are written in the DOM rather than kept here because they are what
|
|
9210
|
+
* someone ELSE reads: a box above this one that travels under the same finger
|
|
9211
|
+
* (a row of slides, a sheet pushed down to close it) has to know which axes are
|
|
9212
|
+
* already spoken for before it answers the press — the same thing a travel says
|
|
9213
|
+
* about itself with `data-travel-by-drag`. It is also what leaves the browser
|
|
9214
|
+
* the pan it may still do until the grab (see the stylesheet above).
|
|
9168
9215
|
*
|
|
9169
9216
|
* @param {Element} element
|
|
9170
|
-
* @param {
|
|
9171
|
-
* Which way the
|
|
9172
|
-
*
|
|
9173
|
-
* else for the usual vertical page.
|
|
9217
|
+
* @param {"x"|"y"|"xy"} [axes="xy"]
|
|
9218
|
+
* Which way the drag walks. A list reordered along its own line says `"y"`;
|
|
9219
|
+
* something carried across a board, or thrown, goes both ways.
|
|
9174
9220
|
* @returns {function} Takes the mark back off.
|
|
9175
9221
|
*/
|
|
9176
|
-
const markDragSource = (element, axes) => {
|
|
9177
|
-
element.setAttribute("data-drag-source", axes
|
|
9222
|
+
const markDragSource = (element, axes = "xy") => {
|
|
9223
|
+
element.setAttribute("data-drag-source", axes);
|
|
9178
9224
|
element.addEventListener("touchmove", keepTouchRefusable, {
|
|
9179
9225
|
passive: false
|
|
9180
9226
|
});
|
|
@@ -11569,6 +11615,18 @@ const css$1 = /* css */`
|
|
|
11569
11615
|
pointer-events: auto;
|
|
11570
11616
|
}
|
|
11571
11617
|
|
|
11618
|
+
/* …and a FINGER reaching for it does not land on it: the pictures of the
|
|
11619
|
+
transition cover the page, so as far as the browser is concerned the touch
|
|
11620
|
+
began on the document root. What a touch may do is decided there and at that
|
|
11621
|
+
moment, so the root says it for as long as the copy can be caught — the pan
|
|
11622
|
+
is ours (nothing should scroll while something is landing), zoom stays the
|
|
11623
|
+
reader's. Half of a pair: without the non-passive listener put down at the
|
|
11624
|
+
same moment (see letCopyBeCaught) every touchmove arrives already
|
|
11625
|
+
non-cancelable and refusing it does nothing. */
|
|
11626
|
+
[data-drag-catchable] {
|
|
11627
|
+
touch-action: pinch-zoom;
|
|
11628
|
+
}
|
|
11629
|
+
|
|
11572
11630
|
/* Ce qui a été lancé: il continue dans la direction du geste jusqu'à sortir de
|
|
11573
11631
|
l'écran, et revient par le même chemin si la réponse refuse. */
|
|
11574
11632
|
[navi-drag-clone-wrapper][data-tossed] {
|
|
@@ -12514,7 +12572,15 @@ const startDragToCarryCopy = (event, {
|
|
|
12514
12572
|
return dragGesture;
|
|
12515
12573
|
}, {
|
|
12516
12574
|
threshold,
|
|
12517
|
-
|
|
12575
|
+
// A copy caught on its way home is not an ambiguous press: the hand
|
|
12576
|
+
// reached for something moving, and the press was already matched
|
|
12577
|
+
// against the copy's own box before it got here. The wait a finger is
|
|
12578
|
+
// asked for elsewhere tells a scroll from a drag, and there is no scroll
|
|
12579
|
+
// to tell it from — the copy covers that spot from the top layer. Asked
|
|
12580
|
+
// for anyway it cannot even be answered: the wait is about as long as the
|
|
12581
|
+
// journey, so the thing is home before the proof is done, while a mouse
|
|
12582
|
+
// takes it in five pixels.
|
|
12583
|
+
longPress: cloneWrapperCaught ? false : longPress,
|
|
12518
12584
|
longPressDelay,
|
|
12519
12585
|
longPressSlop,
|
|
12520
12586
|
onPressStart,
|
|
@@ -12704,6 +12770,17 @@ const letCopyBeCaught = (cloneWrapper, carryAgain) => {
|
|
|
12704
12770
|
// gesture holds what it grabbed rather than whatever was behind.
|
|
12705
12771
|
cloneWrapper.setAttribute("data-catchable", "");
|
|
12706
12772
|
document.addEventListener("pointerdown", onPointerDown, true);
|
|
12773
|
+
// The touch half of the same reach, said on the root because that is where a
|
|
12774
|
+
// finger pressing through the pictures lands (see the stylesheet). Both go
|
|
12775
|
+
// down before the copy sets off, since what a touch may do is settled when it
|
|
12776
|
+
// begins: put down later, the press is still read, the carry still starts, and
|
|
12777
|
+
// the browser cancels the pointer one move afterwards — a copy that cannot be
|
|
12778
|
+
// caught with a finger and can with a mouse.
|
|
12779
|
+
const root = document.documentElement;
|
|
12780
|
+
root.setAttribute("data-drag-catchable", "");
|
|
12781
|
+
root.addEventListener("touchmove", keepTouchRefusable, {
|
|
12782
|
+
passive: false
|
|
12783
|
+
});
|
|
12707
12784
|
return {
|
|
12708
12785
|
settled: async () => {
|
|
12709
12786
|
// A hand that lets go and presses again while the copy is still there is
|
|
@@ -12715,6 +12792,8 @@ const letCopyBeCaught = (cloneWrapper, carryAgain) => {
|
|
|
12715
12792
|
}
|
|
12716
12793
|
cloneWrapper.removeAttribute("data-catchable");
|
|
12717
12794
|
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
12795
|
+
root.removeAttribute("data-drag-catchable");
|
|
12796
|
+
root.removeEventListener("touchmove", keepTouchRefusable);
|
|
12718
12797
|
return caught;
|
|
12719
12798
|
}
|
|
12720
12799
|
};
|
|
@@ -13006,12 +13085,13 @@ const DRAG_FLICK_DISTANCE = 8;
|
|
|
13006
13085
|
// way. Let go and it comes back — a wall one can lean on, never walk through.
|
|
13007
13086
|
const DRAG_RESISTANCE = 0.3;
|
|
13008
13087
|
|
|
13009
|
-
// What a drag must not start on: something that reads the pointer itself
|
|
13010
|
-
// button or a link is not in the list — dragging
|
|
13011
|
-
// click it would have made is swallowed on the way
|
|
13012
|
-
//
|
|
13013
|
-
//
|
|
13014
|
-
|
|
13088
|
+
// What a drag must not start on: something that reads the pointer itself, whole,
|
|
13089
|
+
// with no axis left to share. A button or a link is not in the list — dragging
|
|
13090
|
+
// from one travels, and the click it would have made is swallowed on the way
|
|
13091
|
+
// out. A drag SOURCE is not either: it says which way it goes and only takes
|
|
13092
|
+
// that (see DRAG_SOURCE_AXES_ATTRIBUTE) — but a dedicated handle is, being a
|
|
13093
|
+
// place whose only purpose is to be taken hold of, from the first pixel.
|
|
13094
|
+
const DRAG_EXCLUDED_SELECTOR = ["input", "textarea", "select", '[contenteditable=""]', '[contenteditable="true"]', "[data-drag-handle]", "[data-no-drag-travel]"].join(",");
|
|
13015
13095
|
|
|
13016
13096
|
// Which axes a box travels on, one attribute per gesture, said in the DOM by
|
|
13017
13097
|
// whoever owns the box: it is what a box ABOVE another reads to know the
|
|
@@ -13019,6 +13099,12 @@ const DRAG_EXCLUDED_SELECTOR = ["input", "textarea", "select", '[contenteditable
|
|
|
13019
13099
|
// from the outside.
|
|
13020
13100
|
const DRAG_AXES_ATTRIBUTE = "data-travel-by-drag";
|
|
13021
13101
|
const WHEEL_AXES_ATTRIBUTE = "data-travel-by-wheel";
|
|
13102
|
+
// The same thing said by something that is PICKED UP rather than travelled: a
|
|
13103
|
+
// row taken out of a list, a card carried across a board (see markDragSource).
|
|
13104
|
+
// It holds the pointer from the press exactly as a nested travel does, so it is
|
|
13105
|
+
// read exactly as one — a list reordered along its own line takes the axis it
|
|
13106
|
+
// runs on and leaves the other to whoever is above.
|
|
13107
|
+
const DRAG_SOURCE_AXES_ATTRIBUTE = "data-drag-source";
|
|
13022
13108
|
|
|
13023
13109
|
// A surface the browser paints in the top layer: it is still a DOM descendant
|
|
13024
13110
|
// of whatever it was written in, and it is nowhere near it on screen — it
|
|
@@ -13234,10 +13320,12 @@ const startDragToTravel = (pointerDownEvent, {
|
|
|
13234
13320
|
if (!target.closest || target.closest(DRAG_EXCLUDED_SELECTOR)) {
|
|
13235
13321
|
return null;
|
|
13236
13322
|
}
|
|
13237
|
-
// A box between the finger and this one that travels the same way
|
|
13238
|
-
//
|
|
13239
|
-
//
|
|
13240
|
-
|
|
13323
|
+
// A box between the finger and this one that travels the same way, and then
|
|
13324
|
+
// anything between them that is picked up and carried the same way: the
|
|
13325
|
+
// gesture is theirs, and this one is left with the axes none of them walks —
|
|
13326
|
+
// none at all, most of the time, and then there is no gesture here to read.
|
|
13327
|
+
const axesLeftByTravels = axesLeftBy(axes, target, element, DRAG_AXES_ATTRIBUTE);
|
|
13328
|
+
const axesLeft = axesLeftByTravels && axesLeftBy(axesLeftByTravels, target, element, DRAG_SOURCE_AXES_ATTRIBUTE);
|
|
13241
13329
|
if (!axesLeft) {
|
|
13242
13330
|
return null;
|
|
13243
13331
|
}
|