@jsenv/dom 0.17.23 → 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.
Files changed (2) hide show
  1. package/dist/jsenv_dom.js +92 -1
  2. 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
  *
@@ -14621,6 +14657,40 @@ if (window.visualViewport) {
14621
14657
  // to the next hides and re-shows the keyboard.
14622
14658
  subscribeVirtualKeyboardGeometryChange(scheduleVisualViewportResize);
14623
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
+
14624
14694
  let windowResizeTimeoutId;
14625
14695
  window.addEventListener("resize", (event) => {
14626
14696
  clearTimeout(windowResizeTimeoutId);
@@ -15092,6 +15162,27 @@ const visibleRectEffect = (
15092
15162
  );
15093
15163
  addTeardown(subscribeWindowResizeSettled(onWindowOrViewportResize));
15094
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
+ }
15095
15186
  on_element_resize: {
15096
15187
  if (skipElementResize) {
15097
15188
  break on_element_resize;
@@ -19659,4 +19750,4 @@ const useResizeStatus = (elementRef, { as = "number" } = {}) => {
19659
19750
  };
19660
19751
  };
19661
19752
 
19662
- 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, 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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/dom",
3
- "version": "0.17.23",
3
+ "version": "0.17.24",
4
4
  "type": "module",
5
5
  "description": "DOM utilities for writing frontend code",
6
6
  "repository": {