@jsenv/dom 0.17.22 → 0.17.24
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 +149 -22
- package/package.json +1 -1
package/dist/jsenv_dom.js
CHANGED
|
@@ -290,6 +290,42 @@ const chainEvent = (customEvent, parentEvent) => {
|
|
|
290
290
|
return customEvent;
|
|
291
291
|
};
|
|
292
292
|
|
|
293
|
+
/**
|
|
294
|
+
* Whether `event` was caused by a finger — a predicate for findEvent, so a
|
|
295
|
+
* question about a whole interaction reads as
|
|
296
|
+
* `findEvent(openEvent, isTouchDrivenEvent)`.
|
|
297
|
+
*
|
|
298
|
+
* Asked of the interaction and not of the device (a media query, a pointer:
|
|
299
|
+
* coarse signal) on purpose: a hybrid tablet has both a touchscreen and a
|
|
300
|
+
* trackpad, and answers "coarse" whichever one was just used. What matters is
|
|
301
|
+
* which one WAS used — a tap brings the on-screen keyboard up, the trackpad
|
|
302
|
+
* next to it does not.
|
|
303
|
+
*
|
|
304
|
+
* Three readings, because no single one covers every path from a finger to an
|
|
305
|
+
* event:
|
|
306
|
+
* - a touch* event says it outright;
|
|
307
|
+
* - `pointerType` says it on a PointerEvent, which "click" also is in some
|
|
308
|
+
* engines and not in others — hence not the only reading;
|
|
309
|
+
* - `sourceCapabilities.firesTouchEvents` is what is left for the compatibility
|
|
310
|
+
* mouse events a tap synthesizes, where nothing else remembers the finger.
|
|
311
|
+
* Absent outside Chromium, where it costs nothing: the readings above have
|
|
312
|
+
* already answered by then, or there was no pointer event to answer about.
|
|
313
|
+
*/
|
|
314
|
+
const isTouchDrivenEvent = (event) => {
|
|
315
|
+
if (!event) {
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
if (typeof event.type === "string" && event.type.startsWith("touch")) {
|
|
319
|
+
return true;
|
|
320
|
+
}
|
|
321
|
+
// "" on a pointer event the engine could not attribute — not an answer, so
|
|
322
|
+
// it falls through to the last reading rather than being read as "not touch".
|
|
323
|
+
if (event.pointerType) {
|
|
324
|
+
return event.pointerType === "touch";
|
|
325
|
+
}
|
|
326
|
+
return event.sourceCapabilities?.firesTouchEvents === true;
|
|
327
|
+
};
|
|
328
|
+
|
|
293
329
|
/**
|
|
294
330
|
* Returns true if the event itself or any event in its chain matches the predicate.
|
|
295
331
|
*
|
|
@@ -4295,41 +4331,77 @@ const createPreviousNodeIterator = (fromNode, rootNode, skipRoot = null) => {
|
|
|
4295
4331
|
* link or a button that click means "follow me", which is not what the hand
|
|
4296
4332
|
* asked for: the press was already answered, by the gesture.
|
|
4297
4333
|
*
|
|
4298
|
-
* So it is swallowed, once,
|
|
4299
|
-
*
|
|
4334
|
+
* So it is swallowed, once, before any other listener sees it. Being first is
|
|
4335
|
+
* earned twice, because both orderings matter:
|
|
4336
|
+
*
|
|
4337
|
+
* - on `window`, the first target of the capture phase — a listener anywhere
|
|
4338
|
+
* lower (document included) comes after, no matter when it was registered.
|
|
4339
|
+
* - registered at module load — among listeners on the same target and phase,
|
|
4340
|
+
* registration order decides. A listener added when the gesture ends would
|
|
4341
|
+
* lose to any window-capture listener registered at startup (@jsenv/navi's
|
|
4342
|
+
* link interception is one), so the listener is permanent and merely armed
|
|
4343
|
+
* by each gesture.
|
|
4344
|
+
*
|
|
4345
|
+
* A window-capture listener that this module's evaluation cannot be proven to
|
|
4346
|
+
* precede must not bet on that order: it checks `clickIsSuppressed()` and
|
|
4347
|
+
* stands aside on its own.
|
|
4300
4348
|
*/
|
|
4301
4349
|
|
|
4350
|
+
let suppressing = false;
|
|
4351
|
+
let disarmAtNextPress = false;
|
|
4352
|
+
|
|
4353
|
+
const suppressClick = (clickEvent) => {
|
|
4354
|
+
if (!suppressing) {
|
|
4355
|
+
return;
|
|
4356
|
+
}
|
|
4357
|
+
suppressing = false;
|
|
4358
|
+
disarmAtNextPress = false;
|
|
4359
|
+
clickEvent.preventDefault();
|
|
4360
|
+
clickEvent.stopImmediatePropagation();
|
|
4361
|
+
};
|
|
4362
|
+
const onPointerDown = () => {
|
|
4363
|
+
if (disarmAtNextPress) {
|
|
4364
|
+
suppressing = false;
|
|
4365
|
+
disarmAtNextPress = false;
|
|
4366
|
+
}
|
|
4367
|
+
};
|
|
4368
|
+
window.addEventListener("click", suppressClick, { capture: true });
|
|
4369
|
+
window.addEventListener("pointerdown", onPointerDown, { capture: true });
|
|
4370
|
+
|
|
4302
4371
|
/**
|
|
4303
4372
|
* Swallows the next click, for a gesture that has just answered the press.
|
|
4304
4373
|
*
|
|
4305
|
-
* @returns {() => void} the gesture is over. The
|
|
4306
|
-
*
|
|
4374
|
+
* @returns {() => void} the gesture is over. The suppression cannot be lifted
|
|
4375
|
+
* with it — the click is dispatched AFTER the pointerup that ends the
|
|
4307
4376
|
* gesture, so it would be gone one event too early, and the drag would end on
|
|
4308
|
-
* the link it started from being followed. It
|
|
4377
|
+
* the link it started from being followed. It lifts once it has swallowed a
|
|
4309
4378
|
* click, or at the next press if the gesture produced none: a click is always
|
|
4310
|
-
* preceded by a press, so a
|
|
4379
|
+
* preceded by a press, so a suppression that outlives one press can never
|
|
4311
4380
|
* reach the click of another.
|
|
4312
4381
|
*/
|
|
4313
4382
|
const suppressClickAfterGesture = () => {
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
clickEvent.preventDefault();
|
|
4317
|
-
stopSuppressing();
|
|
4318
|
-
};
|
|
4319
|
-
const stopSuppressing = () => {
|
|
4320
|
-
document.removeEventListener("click", suppressClick, { capture: true });
|
|
4321
|
-
document.removeEventListener("pointerdown", stopSuppressing, {
|
|
4322
|
-
capture: true,
|
|
4323
|
-
});
|
|
4324
|
-
};
|
|
4325
|
-
document.addEventListener("click", suppressClick, { capture: true });
|
|
4383
|
+
suppressing = true;
|
|
4384
|
+
disarmAtNextPress = false;
|
|
4326
4385
|
return () => {
|
|
4327
|
-
|
|
4328
|
-
capture: true,
|
|
4329
|
-
});
|
|
4386
|
+
disarmAtNextPress = true;
|
|
4330
4387
|
};
|
|
4331
4388
|
};
|
|
4332
4389
|
|
|
4390
|
+
/**
|
|
4391
|
+
* Whether the click being dispatched is one a gesture left behind — armed by
|
|
4392
|
+
* `suppressClickAfterGesture`, waiting to be swallowed by this module.
|
|
4393
|
+
*
|
|
4394
|
+
* A last resort, not a convenience. The suppressor already swallows the click
|
|
4395
|
+
* before anyone else sees it; the one listener that legitimately needs to ask
|
|
4396
|
+
* is a `click` listener in capture on `window` whose registration cannot be
|
|
4397
|
+
* proven to come after this module's evaluation — that one may run before the
|
|
4398
|
+
* suppressor and must stand aside on its own. Everywhere else (an element,
|
|
4399
|
+
* `document`, the bubble phase) the click never arrives and checking this is
|
|
4400
|
+
* dead code. Reach for it only when you are sure that is your situation and
|
|
4401
|
+
* no other ordering is available.
|
|
4402
|
+
*/
|
|
4403
|
+
const clickIsSuppressed = () => suppressing;
|
|
4404
|
+
|
|
4333
4405
|
/**
|
|
4334
4406
|
* A press that says something by NOT moving.
|
|
4335
4407
|
*
|
|
@@ -14585,6 +14657,40 @@ if (window.visualViewport) {
|
|
|
14585
14657
|
// to the next hides and re-shows the keyboard.
|
|
14586
14658
|
subscribeVirtualKeyboardGeometryChange(scheduleVisualViewportResize);
|
|
14587
14659
|
|
|
14660
|
+
// A focus change is not a resize, and yet: on a phone, giving focus to a field
|
|
14661
|
+
// is the moment the browser decides what to put over the page — the on-screen
|
|
14662
|
+
// keyboard, and above it the suggestion/autofill strip whose height NOTHING
|
|
14663
|
+
// reports. No event describes that strip: visualViewport stays silent about
|
|
14664
|
+
// it, and so does the keyboard's own geometrychange. So the focus itself is
|
|
14665
|
+
// taken as the only hint there is, and whoever sizes against the viewport
|
|
14666
|
+
// re-measures while the furniture settles.
|
|
14667
|
+
//
|
|
14668
|
+
// Several delays rather than one because there is nothing to wait for: the
|
|
14669
|
+
// strip comes up on its own schedule, after the keyboard, sometimes after a
|
|
14670
|
+
// round-trip to the IME. Polling is what is left when the platform describes
|
|
14671
|
+
// nothing — bounded, and free whenever it finds nothing: a re-measure that
|
|
14672
|
+
// reads the same numbers does nothing at all (visible_rect.js's own check()
|
|
14673
|
+
// dedupes on exactly that).
|
|
14674
|
+
const FOCUS_SETTLE_DELAYS = [250, 350, 700];
|
|
14675
|
+
const [publishFocusSettled, subscribeFocusSettled] = createPubSub();
|
|
14676
|
+
let focusSettleTimeoutIds = [];
|
|
14677
|
+
document.addEventListener(
|
|
14678
|
+
"focusin",
|
|
14679
|
+
(event) => {
|
|
14680
|
+
for (const timeoutId of focusSettleTimeoutIds) {
|
|
14681
|
+
clearTimeout(timeoutId);
|
|
14682
|
+
}
|
|
14683
|
+
focusSettleTimeoutIds = FOCUS_SETTLE_DELAYS.map((delay) =>
|
|
14684
|
+
setTimeout(() => {
|
|
14685
|
+
publishFocusSettled(event);
|
|
14686
|
+
}, delay),
|
|
14687
|
+
);
|
|
14688
|
+
},
|
|
14689
|
+
// Capture: a focus moving into something that stops the event on its way up
|
|
14690
|
+
// still moved the furniture.
|
|
14691
|
+
{ capture: true },
|
|
14692
|
+
);
|
|
14693
|
+
|
|
14588
14694
|
let windowResizeTimeoutId;
|
|
14589
14695
|
window.addEventListener("resize", (event) => {
|
|
14590
14696
|
clearTimeout(windowResizeTimeoutId);
|
|
@@ -15056,6 +15162,27 @@ const visibleRectEffect = (
|
|
|
15056
15162
|
);
|
|
15057
15163
|
addTeardown(subscribeWindowResizeSettled(onWindowOrViewportResize));
|
|
15058
15164
|
}
|
|
15165
|
+
{
|
|
15166
|
+
// The room left on screen can change with nothing announcing it — see
|
|
15167
|
+
// subscribeFocusSettled in window_size.js for what does that and why a
|
|
15168
|
+
// focus change is the only hint available. Same guard as the resize
|
|
15169
|
+
// reaction just above, for the same reason.
|
|
15170
|
+
//
|
|
15171
|
+
// Not routed through onWindowOrViewportResize despite doing the same
|
|
15172
|
+
// thing: the event reaching autoCheck is what decides whether the move
|
|
15173
|
+
// is animated (pickPositionRelativeTo's own shouldTransition reads
|
|
15174
|
+
// event.type === "resize"), and a focus change must not animate. It is
|
|
15175
|
+
// a correction of a measurement that went stale unannounced, not a
|
|
15176
|
+
// viewport the user watched change — and it fires on every focus, where
|
|
15177
|
+
// an animated slide of a popup nobody touched would be the bug.
|
|
15178
|
+
const onFocusSettled = (event) => {
|
|
15179
|
+
if (ancestorRepositioningCount > 0) {
|
|
15180
|
+
return;
|
|
15181
|
+
}
|
|
15182
|
+
autoCheck(event);
|
|
15183
|
+
};
|
|
15184
|
+
addTeardown(subscribeFocusSettled(onFocusSettled));
|
|
15185
|
+
}
|
|
15059
15186
|
on_element_resize: {
|
|
15060
15187
|
if (skipElementResize) {
|
|
15061
15188
|
break on_element_resize;
|
|
@@ -19623,4 +19750,4 @@ const useResizeStatus = (elementRef, { as = "number" } = {}) => {
|
|
|
19623
19750
|
};
|
|
19624
19751
|
};
|
|
19625
19752
|
|
|
19626
|
-
export { EASING, ELEMENT_SIZE_CHANGE, activeElementSignal, addActiveElementEffect, addAttributeEffect, allowWheelThrough, appendStyles, applyNewPosition, canScroll, captureScrollState, chainEvent, claimWheelGesture, closestOpenableAncestor, contrastColor, createBackgroundColorTransition, createBackgroundTransition, createBorderRadiusTransition, createBorderTransition, createDragGestureController, createDragToMoveGestureController, createEventGroupLogger, createGroupTransitionController, createHeightTransition, createIterableWeakSet, createOpacityTransition, createPubSub, createStyleController, createTimelineTransition, createTransition, createTranslateXTransition, createValueEffect, createWidthTransition, cubicBezier, dispatchCustomEvent, dispatchInternalCustomEvent, dispatchPublicCustomEvent, dragAfterIntent, elementIsFocusable, elementIsVisibleForFocus, elementIsVisuallyVisible, findAfter, findAncestor, findBefore, findDescendant, findEvent, findFocusDelegateTarget, findFocusable, findSelfOrAncestorFixedPosition, formatEventSideEffect, getAncestorOpenType, getAvailableHeight, getAvailableWidth, getBackground, getBackgroundColor, getBorder, getBorderRadius, getBorderSizes, getContrastRatio, getDefaultStyles, getDragCoordinates, getDropTargetInfo, getElementSignature, getFirstVisuallyVisibleAncestor, getFocusVisibilityInfo, getHeight, getHeightWithoutTransition, getInnerHeight, getInnerWidth, getKeyboardEventDefaultAction, getLuminance, getMarginSizes, getMaxHeight, getMaxWidth, getMinHeight, getMinWidth, getOpacity, getOpacityWithoutTransition, getPaddingSizes, getPositionedParent, getPositioningScrollOffset, getPreferedColorScheme, getScrollBox, getScrollContainer, getScrollContainerSet, getScrollRelativeRect, getSelfAndAncestorScrolls, getStyle, getTranslateX, getTranslateXWithoutTransition, getTranslateY, getVirtualKeyboardOverlayHeight, getVisuallyVisibleInfo, getWidth, getWidthWithoutTransition, hasCSSSizeUnit, initFlexDetailsSet, initFocusGroup, initPositionSticky, isAncestorOpen, isPrimaryButtonEvent, isSameColor, isScrollable, markDragSource, measureLongestVisualLineWidth, measureScrollbar, measureWidestChildRow, mergeOneStyle, mergeTwoStyles, normalizeKeyboardKey, normalizeStyle, normalizeStyles, observeAncestorOpenState, onAncestorReopen, parsePositionArea, parseStyle, performTabNavigation, pickPositionRelativeTo, prefersDarkColors, prefersLightColors, preventFocusNav, preventFocusNavViaKeyboard, preventIntermediateScrollbar, releaseWheelGesture, resolveCSSColor, resolveCSSSize, resolveColorLuminance, resolveOklchLightness, scrollIntoViewScoped, scrollIntoViewWithStickyAwareness, scrollRoomTowards, setAttribute, setAttributes, setStyles, setVirtualKeyboardOverlaysContent, snapToPixel, startDragTo, startDragToResizeGesture, startDragToTravel, stickyAsRelativeCoords, stringifyStyle, subscribeVirtualKeyboardGeometryChange, subscribeVisualViewportResizeSettled, subscribeWindowResizeSettled, suppressClickAfterGesture, trapFocusInside, trapScrollInside, useActiveElement, useAvailableHeight, useAvailableWidth, useMaxHeight, useMaxWidth, useResizeStatus, visibleRectEffect, waitForPressHeld, watchWheelTravel, wheelGestureIsTakenFrom };
|
|
19753
|
+
export { EASING, ELEMENT_SIZE_CHANGE, activeElementSignal, addActiveElementEffect, addAttributeEffect, allowWheelThrough, appendStyles, applyNewPosition, canScroll, captureScrollState, chainEvent, claimWheelGesture, clickIsSuppressed, closestOpenableAncestor, contrastColor, createBackgroundColorTransition, createBackgroundTransition, createBorderRadiusTransition, createBorderTransition, createDragGestureController, createDragToMoveGestureController, createEventGroupLogger, createGroupTransitionController, createHeightTransition, createIterableWeakSet, createOpacityTransition, createPubSub, createStyleController, createTimelineTransition, createTransition, createTranslateXTransition, createValueEffect, createWidthTransition, cubicBezier, dispatchCustomEvent, dispatchInternalCustomEvent, dispatchPublicCustomEvent, dragAfterIntent, elementIsFocusable, elementIsVisibleForFocus, elementIsVisuallyVisible, findAfter, findAncestor, findBefore, findDescendant, findEvent, findFocusDelegateTarget, findFocusable, findSelfOrAncestorFixedPosition, formatEventSideEffect, getAncestorOpenType, getAvailableHeight, getAvailableWidth, getBackground, getBackgroundColor, getBorder, getBorderRadius, getBorderSizes, getContrastRatio, getDefaultStyles, getDragCoordinates, getDropTargetInfo, getElementSignature, getFirstVisuallyVisibleAncestor, getFocusVisibilityInfo, getHeight, getHeightWithoutTransition, getInnerHeight, getInnerWidth, getKeyboardEventDefaultAction, getLuminance, getMarginSizes, getMaxHeight, getMaxWidth, getMinHeight, getMinWidth, getOpacity, getOpacityWithoutTransition, getPaddingSizes, getPositionedParent, getPositioningScrollOffset, getPreferedColorScheme, getScrollBox, getScrollContainer, getScrollContainerSet, getScrollRelativeRect, getSelfAndAncestorScrolls, getStyle, getTranslateX, getTranslateXWithoutTransition, getTranslateY, getVirtualKeyboardOverlayHeight, getVisuallyVisibleInfo, getWidth, getWidthWithoutTransition, hasCSSSizeUnit, initFlexDetailsSet, initFocusGroup, initPositionSticky, isAncestorOpen, isPrimaryButtonEvent, isSameColor, isScrollable, isTouchDrivenEvent, markDragSource, measureLongestVisualLineWidth, measureScrollbar, measureWidestChildRow, mergeOneStyle, mergeTwoStyles, normalizeKeyboardKey, normalizeStyle, normalizeStyles, observeAncestorOpenState, onAncestorReopen, parsePositionArea, parseStyle, performTabNavigation, pickPositionRelativeTo, prefersDarkColors, prefersLightColors, preventFocusNav, preventFocusNavViaKeyboard, preventIntermediateScrollbar, releaseWheelGesture, resolveCSSColor, resolveCSSSize, resolveColorLuminance, resolveOklchLightness, scrollIntoViewScoped, scrollIntoViewWithStickyAwareness, scrollRoomTowards, setAttribute, setAttributes, setStyles, setVirtualKeyboardOverlaysContent, snapToPixel, startDragTo, startDragToResizeGesture, startDragToTravel, stickyAsRelativeCoords, stringifyStyle, subscribeVirtualKeyboardGeometryChange, subscribeVisualViewportResizeSettled, subscribeWindowResizeSettled, suppressClickAfterGesture, trapFocusInside, trapScrollInside, useActiveElement, useAvailableHeight, useAvailableWidth, useMaxHeight, useMaxWidth, useResizeStatus, visibleRectEffect, waitForPressHeld, watchWheelTravel, wheelGestureIsTakenFrom };
|