Skip to content

Commit 49902b7

Browse files
committed
work
1 parent 0bb7868 commit 49902b7

5 files changed

Lines changed: 108 additions & 4 deletions

File tree

packages/frontend/dom/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@jsenv/dom",
3-
"version": "0.17.53",
3+
"version": "0.17.54",
44
"type": "module",
55
"description": "DOM utilities for writing frontend code",
66
"repository": {

packages/frontend/dom/src/interaction/drag/drag_after_intent.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,10 @@ const css = /* css */ `
106106
still is answered by nobody but the browser, whose long press selects the
107107
word under the thumb (see the top of this file). Everywhere else the
108108
selection is only refused for the length of the gesture, from the gesture
109-
itself (see the selectstart refused in drag_gesture.js).
109+
itself (see the selectstart refused in drag_gesture.js) — which a mouse is
110+
in time for and a finger is not: the browser answers the held finger AFTER
111+
the wait, so a source whose label must not be picked out says
112+
user-select: none in its own stylesheet (see interactions.md).
110113
Prefixed too: Safari only took the property unprefixed at 17, and an older
111114
iPhone is exactly what this long press comes from. */
112115
user-select: none;

packages/frontend/dom/src/interaction/press_held.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,20 @@ export const waitForPressHeld = (
6565
) => {
6666
const { pointerId, clientX, clientY } = pressEvent;
6767

68+
/* The one capture that says nothing about the press. A pointer that cannot
69+
hover is given a capture the moment it lands, by the browser and to the
70+
element it landed on, without anybody asking — and it is announced like any
71+
other, just before the first pointer event that follows. EVERY touch press
72+
has it, so it cannot be read as this one having been settled: read that way,
73+
the wait dies at the first pixel of a finger that was only resting on the
74+
glass, which is what a finger does. That is the hold that fails one time in
75+
two, on the pointer this whole module exists for.
76+
Asked rather than assumed, and asked HERE: the press is the one moment where
77+
a capture can only be the browser's, since nothing has taken one yet. */
78+
const implicitCaptureHolder = pressEvent.target.hasPointerCapture?.(pointerId)
79+
? pressEvent.target
80+
: null;
81+
6882
const pressCleanupCallbacks = [];
6983
// Who is still holding the refusal. The finger holds it because the finger is
7084
// what the system's menu answers; the caller holds it from the moment the press
@@ -188,6 +202,12 @@ export const waitForPressHeld = (
188202
if (captureEvent.pointerId !== pointerId) {
189203
return;
190204
}
205+
if (captureEvent.target === implicitCaptureHolder) {
206+
// The capture the press was born with, not one somebody took (see above).
207+
// A gesture taking that same element instead changes nothing for the
208+
// browser, so it announces nothing, and there is nothing to miss here.
209+
return;
210+
}
191211
cancelPress(captureEvent);
192212
};
193213
// On window rather than on the element: the finger can leave it, and the

packages/frontend/navi/docs/interactions.md

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -951,14 +951,43 @@ Almost nothing, on purpose. A **handle** (`data-drag-handle`) exists only to dra
951951
so it shows the hand; a **source** does not — it drags only once the intent shows,
952952
a plain click on it stays a click, and it is usually something else FIRST (a link,
953953
a card one opens). The cursor says what an element IS, and the gesture is not the
954-
one who knows, so it leaves it alone (`default`, not an I-beam: the text inside
955-
cannot be selected either).
954+
one who knows, so it leaves it alone (`default`, not an I-beam: with a mouse the
955+
text inside cannot be selected either — a finger is another matter, see below).
956956
957957
So on a board where a piece is also clickable, the cursor is already spoken for and
958958
the affordance has to be said in the piece itself — a grip mark in a corner, a
959959
shadow appearing under the pointer, a handle. It is worth deciding, not defaulting:
960960
a board one may drag on is worth nothing if nobody tries.
961961
962+
### The text inside: `user-select: none`
963+
964+
A press on a source belongs to the drag, and the selection that press would have
965+
made is refused for as long as it lasts — so with a mouse, dragging across the
966+
label of a card never paints it blue.
967+
968+
A finger is another matter. It is asked to hold still, and the browser answers
969+
that same held finger a moment later with a gesture of its own: it picks out the
970+
word under the thumb and keeps the touch for the handles it puts around it. The
971+
grab is then lost, and nothing done at the end of the wait takes it back —
972+
whether a finger MAY select is settled when it lands. Like the iOS callout, it is
973+
a stylesheet, and it has to be true before the press:
974+
975+
```css
976+
.token {
977+
user-select: none;
978+
-webkit-user-select: none; /* Safari only took it unprefixed at 17 */
979+
}
980+
```
981+
982+
**Most of the time that is what you want.** What can be picked up carries a
983+
label rather than a text to read — a token, a row, a card one moves — and the
984+
press is there for the drag: nobody selects the word inside a thing they are
985+
carrying. Keep the selection where the text IS the point (a note one copies from)
986+
and give that one a `data-drag-handle`: the grip drags, the text stays text.
987+
988+
navi says it on its own in one place only, inside `[data-drag-on-contact]`, where
989+
there is no wait left to answer the finger with.
990+
962991
## Tuning
963992
964993
Read off the element or any ancestor carrying the attribute, so a whole list is

packages/frontend/navi/src/control/demos/38_interactions_demo.html

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,14 @@
322322
.token[data-grabbed] {
323323
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.3);
324324
}
325+
/* The word inside is not text to read, it is the label of a thing one
326+
carries — see the "user-select: none" section. Prefixed too: Safari only
327+
took the property unprefixed at 17, and an older iPhone is exactly the
328+
phone this matters on. */
329+
.token_unselectable {
330+
user-select: none;
331+
-webkit-user-select: none;
332+
}
325333

326334
/* A surface drawn from three numbers the hand writes: where it has been
327335
panned to and how far it is zoomed. The grid is the surface's own
@@ -1510,6 +1518,50 @@ <h2 style="margin: 0 0 4px">Contents</h2>
15101518
</Box>
15111519
</Box>
15121520

1521+
<Heading level={4} id="move-selection">
1522+
user-select: none
1523+
</Heading>
1524+
<p class="legend">
1525+
A finger held on a word is the browser's own gesture too: it picks
1526+
the word out, keeps the touch for its selection handles, and the
1527+
grab never happens. Whether it may is settled when the finger
1528+
LANDS, so nothing the gesture does at the end of the wait takes it
1529+
back — it is a stylesheet, written before the press, exactly like
1530+
the callout iOS raises. The second token says so; the first is
1531+
left to the browser. A mouse sees no difference between the two:
1532+
there the selection is already refused for the length of the
1533+
press.
1534+
</p>
1535+
<Box flex="x" spacing="l" alignY="start">
1536+
<Box flex="y" spacing="xs">
1537+
<Text size="s" color="#666">
1538+
left to the browser
1539+
</Text>
1540+
<Box className="board board_small">
1541+
<Token
1542+
id="token_selectable"
1543+
label="text"
1544+
style={{ left: "16px", top: "16px" }}
1545+
interactions={{ move: logMove("selectable") }}
1546+
/>
1547+
</Box>
1548+
</Box>
1549+
<Box flex="y" spacing="xs">
1550+
<Text size="s" color="#666">
1551+
user-select: none
1552+
</Text>
1553+
<Box className="board board_small">
1554+
<Token
1555+
id="token_unselectable"
1556+
className="token token_unselectable"
1557+
label="text"
1558+
style={{ left: "16px", top: "16px" }}
1559+
interactions={{ move: logMove("unselectable") }}
1560+
/>
1561+
</Box>
1562+
</Box>
1563+
</Box>
1564+
15131565
<Heading level={4} id="move-on-contact">
15141566
data-drag-on-contact
15151567
</Heading>

0 commit comments

Comments
 (0)