@jsenv/dom 0.17.16 → 0.17.18
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 +105 -20
- package/package.json +1 -1
package/dist/jsenv_dom.js
CHANGED
|
@@ -8651,43 +8651,64 @@ const createDragGestureController = (options = {}) => {
|
|
|
8651
8651
|
document.removeEventListener("selectstart", preventSelectStart);
|
|
8652
8652
|
});
|
|
8653
8653
|
/*
|
|
8654
|
-
* Refusing every selection also refuses the
|
|
8655
|
-
* make: a double click selects the word under it,
|
|
8656
|
-
* over before the pointer has gone
|
|
8657
|
-
*
|
|
8654
|
+
* Refusing every selection also refuses the ones a press is entitled to
|
|
8655
|
+
* make: a double click selects the word under it, a triple click the
|
|
8656
|
+
* paragraph around it, and both are over before the pointer has gone
|
|
8657
|
+
* anywhere. They are made here instead, spelled out (see
|
|
8658
|
+
* selectWordAtPoint, selectParagraphAtPoint) rather than left to a browser
|
|
8658
8659
|
* heuristic that cannot tell a drag from a click.
|
|
8659
8660
|
*
|
|
8661
|
+
* Read from `click` rather than `dblclick`, because a triple click has no
|
|
8662
|
+
* event of its own: what tells the clicks apart is `detail`, the count the
|
|
8663
|
+
* browser keeps of how many presses landed in the same place in a row — 2
|
|
8664
|
+
* for a word, 3 and beyond for a paragraph (a fourth click keeps the
|
|
8665
|
+
* paragraph, the way the browser does).
|
|
8666
|
+
*
|
|
8660
8667
|
* On the document and outliving the gesture, because the gesture is
|
|
8661
|
-
* already over when the second click completes:
|
|
8662
|
-
*
|
|
8663
|
-
*
|
|
8664
|
-
*
|
|
8668
|
+
* already over when the second click completes: click comes after mouseup,
|
|
8669
|
+
* the gesture ends at pointerup. Kept installed after it fires, since the
|
|
8670
|
+
* click that selects a word is also the one the next click turns into a
|
|
8671
|
+
* paragraph; it goes when the next press installs its own — a listener
|
|
8672
|
+
* waiting for a click that never comes costs nothing until then.
|
|
8665
8673
|
*/
|
|
8666
|
-
|
|
8667
|
-
const
|
|
8668
|
-
|
|
8674
|
+
removePendingMultiClickListener();
|
|
8675
|
+
const onClick = clickEvent => {
|
|
8676
|
+
const clickCount = clickEvent.detail;
|
|
8677
|
+
if (clickCount < 2) {
|
|
8678
|
+
return;
|
|
8679
|
+
}
|
|
8669
8680
|
// A drag that happened is a gesture, not a click: the second press of a
|
|
8670
8681
|
// double click can be the one that drags, and what it drags must not end
|
|
8671
8682
|
// up selected too.
|
|
8672
8683
|
if (dragGesture.gestureInfo.started) {
|
|
8673
8684
|
return;
|
|
8674
8685
|
}
|
|
8686
|
+
// Only the clicks that continue THIS press: the listener outlives the
|
|
8687
|
+
// gesture and the page keeps being clicked elsewhere, where the browser
|
|
8688
|
+
// is doing its own selecting — a second opinion there would only fight
|
|
8689
|
+
// it.
|
|
8690
|
+
const clickTarget = clickEvent.target;
|
|
8691
|
+
if (clickTarget !== grabEvent.target && !clickTarget.contains(grabEvent.target)) {
|
|
8692
|
+
return;
|
|
8693
|
+
}
|
|
8675
8694
|
// Text the page says is not selectable stays not selectable: a
|
|
8676
8695
|
// programmatic selection goes through `user-select: none` in every
|
|
8677
8696
|
// engine — it is a rule about what the USER may start, and the browser
|
|
8678
8697
|
// does not read it back when asked directly. Read here so that doing the
|
|
8679
8698
|
// browser's work does not also undo what the page asked of it.
|
|
8680
|
-
if (!isSelectable(
|
|
8699
|
+
if (!isSelectable(clickEvent.target)) {
|
|
8700
|
+
return;
|
|
8701
|
+
}
|
|
8702
|
+
if (clickCount === 2) {
|
|
8703
|
+
selectWordAtPoint(clickEvent.clientX, clickEvent.clientY);
|
|
8681
8704
|
return;
|
|
8682
8705
|
}
|
|
8683
|
-
|
|
8706
|
+
selectParagraphAtPoint(clickEvent.clientX, clickEvent.clientY);
|
|
8684
8707
|
};
|
|
8685
|
-
document.addEventListener("
|
|
8686
|
-
|
|
8687
|
-
|
|
8688
|
-
|
|
8689
|
-
removePendingDoubleClickListener = NOOP;
|
|
8690
|
-
document.removeEventListener("dblclick", onDoubleClick);
|
|
8708
|
+
document.addEventListener("click", onClick);
|
|
8709
|
+
removePendingMultiClickListener = () => {
|
|
8710
|
+
removePendingMultiClickListener = NOOP;
|
|
8711
|
+
document.removeEventListener("click", onClick);
|
|
8691
8712
|
};
|
|
8692
8713
|
const cleanup = initializer({
|
|
8693
8714
|
onMove: dragViaPointer,
|
|
@@ -8882,7 +8903,7 @@ const definePropertyAsReadOnly = (object, propertyName) => {
|
|
|
8882
8903
|
});
|
|
8883
8904
|
};
|
|
8884
8905
|
const NOOP = () => {};
|
|
8885
|
-
let
|
|
8906
|
+
let removePendingMultiClickListener = NOOP;
|
|
8886
8907
|
|
|
8887
8908
|
// What a double click selects when the browser is allowed to do it itself: the
|
|
8888
8909
|
// word around the caret the click lands on.
|
|
@@ -8902,6 +8923,46 @@ const selectWordAtPoint = (x, y) => {
|
|
|
8902
8923
|
selection.modify("extend", "forward", "word");
|
|
8903
8924
|
}
|
|
8904
8925
|
};
|
|
8926
|
+
|
|
8927
|
+
// What a triple click selects when the browser is allowed to do it itself: the
|
|
8928
|
+
// paragraph around the caret the click lands on — in the DOM, the contents of
|
|
8929
|
+
// the closest block the caret sits in.
|
|
8930
|
+
//
|
|
8931
|
+
// Drawn from the box tree rather than from `selection.modify("extend",
|
|
8932
|
+
// "forward", "paragraph")`: "paragraph" is a granularity Gecko never
|
|
8933
|
+
// implemented (it stops at word and line), so the modify route would select a
|
|
8934
|
+
// line in Firefox and a paragraph in Chrome.
|
|
8935
|
+
const selectParagraphAtPoint = (x, y) => {
|
|
8936
|
+
const caretRange = createCaretRange(x, y);
|
|
8937
|
+
if (!caretRange) {
|
|
8938
|
+
return;
|
|
8939
|
+
}
|
|
8940
|
+
const block = getClosestBlock(caretRange.startContainer);
|
|
8941
|
+
if (!block) {
|
|
8942
|
+
return;
|
|
8943
|
+
}
|
|
8944
|
+
const selection = window.getSelection();
|
|
8945
|
+
selection.removeAllRanges();
|
|
8946
|
+
const paragraphRange = document.createRange();
|
|
8947
|
+
paragraphRange.selectNodeContents(block);
|
|
8948
|
+
selection.addRange(paragraphRange);
|
|
8949
|
+
};
|
|
8950
|
+
const getClosestBlock = node => {
|
|
8951
|
+
let element = node.nodeType === 1 ? node : node.parentElement;
|
|
8952
|
+
while (element) {
|
|
8953
|
+
const {
|
|
8954
|
+
display
|
|
8955
|
+
} = window.getComputedStyle(element);
|
|
8956
|
+
// Everything that is not laid out as a line inside another line: the first
|
|
8957
|
+
// ancestor that breaks the flow is the one whose text reads as a paragraph
|
|
8958
|
+
// of its own.
|
|
8959
|
+
if (!display.startsWith("inline") && display !== "contents") {
|
|
8960
|
+
return element;
|
|
8961
|
+
}
|
|
8962
|
+
element = element.parentElement;
|
|
8963
|
+
}
|
|
8964
|
+
return null;
|
|
8965
|
+
};
|
|
8905
8966
|
const createCaretRange = (x, y) => {
|
|
8906
8967
|
if (document.caretPositionFromPoint) {
|
|
8907
8968
|
const caretPosition = document.caretPositionFromPoint(x, y);
|
|
@@ -12894,6 +12955,16 @@ const DRAG_EXCLUDED_SELECTOR = ["input", "textarea", "select", '[contenteditable
|
|
|
12894
12955
|
const DRAG_AXES_ATTRIBUTE = "data-travel-by-drag";
|
|
12895
12956
|
const WHEEL_AXES_ATTRIBUTE = "data-travel-by-wheel";
|
|
12896
12957
|
|
|
12958
|
+
// A surface the browser paints in the top layer: it is still a DOM descendant
|
|
12959
|
+
// of whatever it was written in, and it is nowhere near it on screen — it
|
|
12960
|
+
// covers everything. So a gesture that happened on it is not the gesture of any
|
|
12961
|
+
// box it merely sits on top of, and every walk up the tree from the pointer
|
|
12962
|
+
// ends here: the boxes above are behind, and behind is not under the finger.
|
|
12963
|
+
const TOP_LAYER_SELECTOR = [":popover-open", "dialog:modal", ":fullscreen"].join(",");
|
|
12964
|
+
const isTopLayer = element => {
|
|
12965
|
+
return element.matches(TOP_LAYER_SELECTOR);
|
|
12966
|
+
};
|
|
12967
|
+
|
|
12897
12968
|
/**
|
|
12898
12969
|
* What is left for this box of the axes it travels, once the boxes it CONTAINS
|
|
12899
12970
|
* have taken theirs: a row of slides inside a page that walks between pages, a
|
|
@@ -12908,6 +12979,10 @@ const WHEEL_AXES_ATTRIBUTE = "data-travel-by-wheel";
|
|
|
12908
12979
|
* the browser for the pointer LAST, which is the outermost box — the wrong one,
|
|
12909
12980
|
* and past that point the inner one stops being told anything. So the box that
|
|
12910
12981
|
* does not own the gesture must never ask for it.
|
|
12982
|
+
*
|
|
12983
|
+
* A box lifted into the top layer on the way up takes everything: a popover or a
|
|
12984
|
+
* modal dialog is written inside a slide and painted over the whole screen, so
|
|
12985
|
+
* the slides are nowhere near the finger and none of the axes are left.
|
|
12911
12986
|
*/
|
|
12912
12987
|
const axesLeftBy = (axes, fromElement, stopElement, attribute) => {
|
|
12913
12988
|
if (!stopElement.contains(fromElement)) {
|
|
@@ -12919,6 +12994,11 @@ const axesLeftBy = (axes, fromElement, stopElement, attribute) => {
|
|
|
12919
12994
|
let left = axes;
|
|
12920
12995
|
let element = fromElement;
|
|
12921
12996
|
while (element && element !== stopElement && element.nodeType === 1) {
|
|
12997
|
+
if (isTopLayer(element)) {
|
|
12998
|
+
// The gesture happened on a surface painted over this box, not in it:
|
|
12999
|
+
// there is nothing left of it here, whatever axes are still unclaimed.
|
|
13000
|
+
return "";
|
|
13001
|
+
}
|
|
12922
13002
|
const taken = element.getAttribute(attribute);
|
|
12923
13003
|
if (taken) {
|
|
12924
13004
|
let rest = "";
|
|
@@ -12946,6 +13026,11 @@ const axesLeftBy = (axes, fromElement, stopElement, attribute) => {
|
|
|
12946
13026
|
const scrollRoomTowards = (fromElement, stopElement, axis, sign) => {
|
|
12947
13027
|
let element = fromElement;
|
|
12948
13028
|
while (element && element !== stopElement && element.nodeType === 1) {
|
|
13029
|
+
if (isTopLayer(element)) {
|
|
13030
|
+
// A scroller above a top-layer surface is painted behind it: whatever
|
|
13031
|
+
// room it has left is not room the finger is asking for.
|
|
13032
|
+
return false;
|
|
13033
|
+
}
|
|
12949
13034
|
const size = axis === "x" ? element.clientWidth : element.clientHeight;
|
|
12950
13035
|
const scrollSize = axis === "x" ? element.scrollWidth : element.scrollHeight;
|
|
12951
13036
|
if (scrollSize > size + 1) {
|