@jsenv/navi 0.29.59 → 0.29.60
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_navi.js +103 -47
- package/dist/jsenv_navi.js.map +4 -4
- package/package.json +2 -2
package/dist/jsenv_navi.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { installImportMetaCssBuild, windowHeightSignal, windowWidthSignal, visualViewportHeightSignal, visualViewportWidthSignal, getAppHeight, getAppWidth, smallTouchScreenSignal } from "./jsenv_navi_side_effects.js";
|
|
6
6
|
export { coarsePointerSignal } from "./jsenv_navi_side_effects.js";
|
|
7
|
-
import { elementIsFocusable, createPubSub, dispatchInternalCustomEvent, dispatchCustomEvent,
|
|
7
|
+
import { elementIsFocusable, createPubSub, dispatchInternalCustomEvent, dispatchCustomEvent, getVisuallyVisibleInfo, getFirstVisuallyVisibleAncestor, getElementSignature, findEvent, createValueEffect, findFocusDelegateTarget, findFocusable, allowWheelThrough, dispatchPublicCustomEvent, resolveCSSColor, ELEMENT_SIZE_CHANGE, findSelfOrAncestorFixedPosition, visibleRectEffect, pickPositionRelativeTo, getBorderSizes, getPaddingSizes, applyNewPosition, measureLongestVisualLineWidth, chainEvent, waitForPressHeld, suppressClickAfterGesture, startDragToTravel, markDragSource, startDragTo, createIterableWeakSet, createEventGroupLogger, getKeyboardEventDefaultAction, activeElementSignal, normalizeStyle, mergeOneStyle, getPositionedParent, mergeTwoStyles, normalizeStyles, resolveCSSSize, hasCSSSizeUnit, resolveOklchLightness, contrastColor, closestOpenableAncestor, isAncestorOpen, observeAncestorOpenState, getAncestorOpenType, scrollRoomTowards, parsePositionArea, snapToPixel, trapFocusInside, trapScrollInside, onAncestorReopen, createGroupTransitionController, getBorderRadius, preventIntermediateScrollbar, createOpacityTransition, watchWheelTravel, findBefore, findAfter, initFocusGroup, scrollIntoViewScoped, getScrollContainer, canScroll, measureWidestChildRow, performTabNavigation, wheelGestureIsTakenFrom, releaseWheelGesture, claimWheelGesture, dragAfterIntent, stickyAsRelativeCoords, createDragToMoveGestureController, getDropTargetInfo, setStyles, useActiveElement, stringifyStyle as stringifyStyle$1 } from "@jsenv/dom";
|
|
8
8
|
export { contrastColor, findEvent, startDragTo } from "@jsenv/dom";
|
|
9
9
|
import { signal, computed, effect, batch, untracked, useSignal } from "@preact/signals";
|
|
10
10
|
import { createContext, isValidElement, h, Fragment, render, toChildArray, options, cloneElement } from "preact";
|
|
@@ -7924,6 +7924,9 @@ const css$$ = /* css */`
|
|
|
7924
7924
|
* @param {string} [options.status=""] - Callout status: "info" | "warning" | "error" | "success"
|
|
7925
7925
|
* @param {Function} [options.onClose] - Callback when callout is closed
|
|
7926
7926
|
* @param {boolean} [options.closeOnClickOutside] - Whether to close on outside clicks (defaults to true for "info" status)
|
|
7927
|
+
* @param {string} [options.reopen="toggle"] - What to do when the anchor already has an open callout:
|
|
7928
|
+
* "toggle" closes it (a second press on what opened it closes it), "update" replaces its message
|
|
7929
|
+
* in place, "replace" tears it down and opens a new one
|
|
7927
7930
|
* @param {boolean} [options.debug=false] - Enable debug logging
|
|
7928
7931
|
*
|
|
7929
7932
|
* Positioning is also driven by attributes read on the anchor element itself
|
|
@@ -7964,6 +7967,7 @@ const openCallout = (message, {
|
|
|
7964
7967
|
closeOnClickOutside = status === "info",
|
|
7965
7968
|
closeOnFocusLeave = closeOnClickOutside,
|
|
7966
7969
|
openingEvent,
|
|
7970
|
+
reopen = "toggle",
|
|
7967
7971
|
showErrorStack,
|
|
7968
7972
|
skipFocus = false,
|
|
7969
7973
|
debug = () => {}
|
|
@@ -7972,6 +7976,45 @@ const openCallout = (message, {
|
|
|
7972
7976
|
if (debug === true) {
|
|
7973
7977
|
debug = (e, ...args) => console.debug(`"${e.type}" -> `, ...args);
|
|
7974
7978
|
}
|
|
7979
|
+
const originalAnchorElement = anchorElement;
|
|
7980
|
+
if (anchorElement) {
|
|
7981
|
+
const proxyElement = findControlProxy(anchorElement);
|
|
7982
|
+
if (proxyElement) {
|
|
7983
|
+
anchorElement = proxyElement;
|
|
7984
|
+
}
|
|
7985
|
+
const controlRoot = findControlRoot(anchorElement);
|
|
7986
|
+
if (controlRoot) {
|
|
7987
|
+
anchorElement = controlRoot;
|
|
7988
|
+
}
|
|
7989
|
+
const anchorVisuallyVisibleInfo = getVisuallyVisibleInfo(anchorElement, {
|
|
7990
|
+
countOffscreenAsVisible: true
|
|
7991
|
+
});
|
|
7992
|
+
if (!anchorVisuallyVisibleInfo.visible) {
|
|
7993
|
+
anchorElement = getFirstVisuallyVisibleAncestor(anchorElement);
|
|
7994
|
+
if (!anchorElement) {
|
|
7995
|
+
// anchorElement is not in the DOM anymore, fallback to body
|
|
7996
|
+
anchorElement = document.body;
|
|
7997
|
+
}
|
|
7998
|
+
console.warn(`anchor is not visually visible (${anchorVisuallyVisibleInfo.reason}) -> callout will anchor to first visually visible ancestor (${getElementSignature(anchorElement)})`);
|
|
7999
|
+
}
|
|
8000
|
+
}
|
|
8001
|
+
if (anchorElement) {
|
|
8002
|
+
const calloutAlreadyThere = anchorElement.callout;
|
|
8003
|
+
if (calloutAlreadyThere && calloutAlreadyThere.opened) {
|
|
8004
|
+
if (reopen === "toggle") {
|
|
8005
|
+
debug(openingEvent, `callout already open on ${getElementSignature(anchorElement)} -> close it (reopen: "toggle")`);
|
|
8006
|
+
calloutAlreadyThere.requestClose(openingEvent, "reopen_toggle");
|
|
8007
|
+
return calloutAlreadyThere;
|
|
8008
|
+
}
|
|
8009
|
+
if (reopen === "update") {
|
|
8010
|
+
debug(openingEvent, `callout already open on ${getElementSignature(anchorElement)} -> update it (reopen: "update")`);
|
|
8011
|
+
calloutAlreadyThere.update(message, {
|
|
8012
|
+
status
|
|
8013
|
+
});
|
|
8014
|
+
return calloutAlreadyThere;
|
|
8015
|
+
}
|
|
8016
|
+
}
|
|
8017
|
+
}
|
|
7975
8018
|
const callout = {
|
|
7976
8019
|
opened: true,
|
|
7977
8020
|
close: null,
|
|
@@ -8155,28 +8198,7 @@ const openCallout = (message, {
|
|
|
8155
8198
|
callout.updatePosition();
|
|
8156
8199
|
}
|
|
8157
8200
|
};
|
|
8158
|
-
|
|
8159
|
-
if (anchorElement) {
|
|
8160
|
-
const proxyElement = findControlProxy(anchorElement);
|
|
8161
|
-
if (proxyElement) {
|
|
8162
|
-
anchorElement = proxyElement;
|
|
8163
|
-
}
|
|
8164
|
-
const controlRoot = findControlRoot(anchorElement);
|
|
8165
|
-
if (controlRoot) {
|
|
8166
|
-
anchorElement = controlRoot;
|
|
8167
|
-
}
|
|
8168
|
-
const anchorVisuallyVisibleInfo = getVisuallyVisibleInfo(anchorElement, {
|
|
8169
|
-
countOffscreenAsVisible: true
|
|
8170
|
-
});
|
|
8171
|
-
if (!anchorVisuallyVisibleInfo.visible) {
|
|
8172
|
-
anchorElement = getFirstVisuallyVisibleAncestor(anchorElement);
|
|
8173
|
-
if (!anchorElement) {
|
|
8174
|
-
// anchorElement is not in the DOM anymore, fallback to body
|
|
8175
|
-
anchorElement = document.body;
|
|
8176
|
-
}
|
|
8177
|
-
console.warn(`anchor is not visually visible (${anchorVisuallyVisibleInfo.reason}) -> callout will anchor to first visually visible ancestor (${getElementSignature(anchorElement)})`);
|
|
8178
|
-
}
|
|
8179
|
-
}
|
|
8201
|
+
|
|
8180
8202
|
// Resolve the visual anchor for positioning: when data-callout-anchor is set,
|
|
8181
8203
|
// use the inner element it points to. anchorElement remains the container
|
|
8182
8204
|
// that receives data-callout and CSS vars.
|
|
@@ -8212,6 +8234,14 @@ const openCallout = (message, {
|
|
|
8212
8234
|
return anchorElement.parentNode || document.body;
|
|
8213
8235
|
})();
|
|
8214
8236
|
{
|
|
8237
|
+
// document.body as anchor means "no anchor" (the callout is docked in the
|
|
8238
|
+
// viewport); everything would be inside it.
|
|
8239
|
+
const isInsideAnchor = target => {
|
|
8240
|
+
if (!anchorElement || anchorElement === document.body) {
|
|
8241
|
+
return false;
|
|
8242
|
+
}
|
|
8243
|
+
return anchorElement === target || anchorElement.contains(target);
|
|
8244
|
+
};
|
|
8215
8245
|
const handleClickOutside = event => {
|
|
8216
8246
|
if (event.button !== 0) {
|
|
8217
8247
|
// right click
|
|
@@ -8221,6 +8251,16 @@ const openCallout = (message, {
|
|
|
8221
8251
|
if (clickTarget === calloutElement || calloutElement.contains(clickTarget)) {
|
|
8222
8252
|
return;
|
|
8223
8253
|
}
|
|
8254
|
+
if (isInsideAnchor(clickTarget)) {
|
|
8255
|
+
// Pressing the anchor is not "outside": this listener is on document in
|
|
8256
|
+
// the capture phase, so closing here would destroy the callout before
|
|
8257
|
+
// the event reaches the handler that owns it — and that handler,
|
|
8258
|
+
// opening a callout on the very anchor it was just removed from, would
|
|
8259
|
+
// create a second one within the same click. Left open, openCallout's
|
|
8260
|
+
// `reopen` decides (toggle by default).
|
|
8261
|
+
debug(event, `click on anchor, let the anchor handler decide`);
|
|
8262
|
+
return;
|
|
8263
|
+
}
|
|
8224
8264
|
requestClose(event, "click_outside");
|
|
8225
8265
|
};
|
|
8226
8266
|
const handleSpaceOutside = event => {
|
|
@@ -8231,6 +8271,12 @@ const openCallout = (message, {
|
|
|
8231
8271
|
if (keyTarget === calloutElement || calloutElement.contains(keyTarget)) {
|
|
8232
8272
|
return;
|
|
8233
8273
|
}
|
|
8274
|
+
if (isInsideAnchor(keyTarget)) {
|
|
8275
|
+
// Space on the anchor produces a click afterwards — same reasoning as
|
|
8276
|
+
// handleClickOutside above.
|
|
8277
|
+
debug(event, `space on anchor, let the anchor handler decide`);
|
|
8278
|
+
return;
|
|
8279
|
+
}
|
|
8234
8280
|
requestClose(event, "space_outside");
|
|
8235
8281
|
};
|
|
8236
8282
|
const registerClickOutsideListener = () => {
|
|
@@ -8403,7 +8449,9 @@ const openCallout = (message, {
|
|
|
8403
8449
|
});
|
|
8404
8450
|
allowWheelThrough(calloutElement, visualAnchorElement);
|
|
8405
8451
|
anchorElement.setAttribute("data-callout", calloutId);
|
|
8406
|
-
addTeardown((
|
|
8452
|
+
addTeardown(({
|
|
8453
|
+
event
|
|
8454
|
+
}) => {
|
|
8407
8455
|
anchorElement.removeAttribute("data-callout");
|
|
8408
8456
|
});
|
|
8409
8457
|
const visualElement = (() => {
|
|
@@ -29086,12 +29134,13 @@ const CLOSE_DIRECTION_BY_SIDE = { left: -1, right: 1, top: -1, bottom: 1 };
|
|
|
29086
29134
|
/**
|
|
29087
29135
|
* Builds the `pointerdown` handler a popup docked to `side` answers with.
|
|
29088
29136
|
*
|
|
29089
|
-
* `grip`
|
|
29090
|
-
*
|
|
29091
|
-
*
|
|
29092
|
-
*
|
|
29093
|
-
*
|
|
29094
|
-
*
|
|
29137
|
+
* `grip` says where the gesture may start, as a selector the pressed element is
|
|
29138
|
+
* matched against with `closest`. A popup that names one is held THERE and
|
|
29139
|
+
* nowhere else: everything else it holds is content the finger came to operate,
|
|
29140
|
+
* and a press on it is that content's — including gestures of its own, which
|
|
29141
|
+
* this file has no way of knowing about. A popup that names no grip is pushed
|
|
29142
|
+
* from its whole surface, which only suits one made of nothing else (a side
|
|
29143
|
+
* panel showing a page).
|
|
29095
29144
|
*/
|
|
29096
29145
|
const createSwipeToClose = (side, { grip } = {}) => {
|
|
29097
29146
|
const axis = SWIPE_AXIS_BY_SIDE[side];
|
|
@@ -29103,8 +29152,11 @@ const createSwipeToClose = (side, { grip } = {}) => {
|
|
|
29103
29152
|
return;
|
|
29104
29153
|
}
|
|
29105
29154
|
if (grip) {
|
|
29106
|
-
|
|
29107
|
-
|
|
29155
|
+
// Read from the press outwards rather than by looking the grip up in the
|
|
29156
|
+
// panel: a popup may hold several (a header and whatever else was marked
|
|
29157
|
+
// as one), and where they sit in it is the application's business.
|
|
29158
|
+
const gripEl = pointerDownEvent.target.closest(grip);
|
|
29159
|
+
if (!gripEl || !panelEl.contains(gripEl)) {
|
|
29108
29160
|
return;
|
|
29109
29161
|
}
|
|
29110
29162
|
}
|
|
@@ -29492,13 +29544,14 @@ const css$W = /* css */`
|
|
|
29492
29544
|
&:has(> [data-focus-outline-delegate][data-focus-visible]) {
|
|
29493
29545
|
outline-style: solid;
|
|
29494
29546
|
}
|
|
29495
|
-
/*
|
|
29496
|
-
not a piece of the scroll: a finger on it moves the sheet, and
|
|
29497
|
-
the browser scroll the sheet under the same gesture would show
|
|
29498
|
-
movements answering one drag.
|
|
29499
|
-
|
|
29500
|
-
|
|
29501
|
-
&[data-swipe-to-close]
|
|
29547
|
+
/* What a sheet that closes by being pushed back down is held by is a
|
|
29548
|
+
handle, not a piece of the scroll: a finger on it moves the sheet, and
|
|
29549
|
+
letting the browser scroll the sheet under the same gesture would show
|
|
29550
|
+
two movements answering one drag. The same parts swipe_to_close.js takes
|
|
29551
|
+
hold of, said again here because CSS is the only place it can be said
|
|
29552
|
+
before the finger lands. */
|
|
29553
|
+
&[data-swipe-to-close] [data-header],
|
|
29554
|
+
&[data-swipe-to-close] [data-swipe-grip] {
|
|
29502
29555
|
touch-action: none;
|
|
29503
29556
|
}
|
|
29504
29557
|
|
|
@@ -29647,8 +29700,10 @@ const css$W = /* css */`
|
|
|
29647
29700
|
* edge docking would bring it to, so docking could only take away the shape
|
|
29648
29701
|
* the caller asked for. Re-resolves live as the pointer
|
|
29649
29702
|
* type or the window size changes. A sheet resting on the bottom edge is also
|
|
29650
|
-
* pushed back down to close it, held by its header (a
|
|
29651
|
-
*
|
|
29703
|
+
* pushed back down to close it, held by its header (a `Box` with the `header`
|
|
29704
|
+
* prop) and by anything else carrying `data-swipe-grip`. The rest of the sheet
|
|
29705
|
+
* is left to what it holds, so a board something is dragged across keeps its
|
|
29706
|
+
* own gestures. See `swipe_to_close.js`.
|
|
29652
29707
|
* @param {string} [props.positionArea="center"] - Where to dock the dialog
|
|
29653
29708
|
* within its container (the viewport for `layer="top"`, the positioned
|
|
29654
29709
|
* ancestor for `layer="local"`) — Dialog is never anchored to a real
|
|
@@ -29902,12 +29957,13 @@ const DOCKED = {
|
|
|
29902
29957
|
scrollCapture: true
|
|
29903
29958
|
};
|
|
29904
29959
|
|
|
29905
|
-
// Where a bottom sheet is held to push it back down: the strip a
|
|
29906
|
-
//
|
|
29907
|
-
//
|
|
29908
|
-
//
|
|
29909
|
-
//
|
|
29910
|
-
|
|
29960
|
+
// Where a bottom sheet is held to push it back down: the strip a Box declares
|
|
29961
|
+
// with `header`, plus anything the application marked as one more. Everything
|
|
29962
|
+
// else in the sheet is content the finger came to operate — a board a piece is
|
|
29963
|
+
// dragged across, a list, a map — and a press there belongs to it. A sheet with
|
|
29964
|
+
// no header and nothing marked is not pushed down at all; it is closed by its
|
|
29965
|
+
// own controls, by the backdrop and by Escape.
|
|
29966
|
+
const DOCKED_SWIPE_GRIP = "[data-header],[data-swipe-grip]";
|
|
29911
29967
|
|
|
29912
29968
|
// The first control inside `dialogEl` that is mid-action, if any. Walks the
|
|
29913
29969
|
// controls rather than reading an attribute off the dialog: a dialog carries no
|