@jsenv/navi 0.29.493 → 0.29.495
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/dev/jsenv_navi.js +186 -72
- package/dist/dev/jsenv_navi.js.map +24 -24
- package/dist/jsenv_navi.js +182 -68
- package/dist/jsenv_navi.js.map +24 -24
- package/docs/resource.md +2 -1
- package/docs/testid.md +1 -1
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -6,7 +6,7 @@ import { installImportMetaCssBuild, coarsePointerSignal, windowHeightSignal, win
|
|
|
6
6
|
import { createPubSub, createIterableWeakSet, elementIsFocusable, getScrollContainer, dispatchInternalCustomEvent, dispatchCustomEvent, findEvent, mergeOneStyle, findFocusable, getElementSignature, chainEvent, isTouchDrivenEvent, getKeyboardEventDefaultAction, isPressDrivenClick, getVisuallyVisibleInfo, getFirstVisuallyVisibleAncestor, createValueEffect, findFocusDelegateTarget, scrollIntoViewThroughScrollables, allowWheelThrough, dispatchPublicCustomEvent, resolveCSSColor, getScrollContainerSet, ELEMENT_SIZE_CHANGE, findSelfOrAncestorFixedPosition, visibleRectEffect, pickPositionRelativeTo, getBorderSizes, getPaddingSizes, applyNewPosition, measureLongestVisualLineWidth, keepTouchRefusable, waitForTap, waitForPressHeld, suppressClickAfterGesture, startDragToTravel, dragSourceThatStoodDown, markDragSource, refuseDragTo, startDragTo, installPanZoom, createInternalCustomEvent, activeElementSignal, normalizeStyle, getPositionedParent, normalizeStyles, createGroupTransitionController, getBorderRadius, preventIntermediateScrollbar, createOpacityTransition, watchWheelTravel, scrollRoomTowards, trapScrollInside, scrollIntoViewScoped, isPressDisputedByDrag, closestOpenableAncestor, isAncestorOpen, isDisplayedDespiteClosedAncestor, observeAncestorOpenState, getAncestorOpenType, findBefore, findAfter, resolveCSSSize, hasCSSSizeUnit, releaseWheelGesture, getScrollIntoViewScopedOffsets, wheelGestureIsTakenFrom, claimWheelGesture, initFocusGroup, stringifyStyle as stringifyStyle$1, isScrollable, resolveOklchLightness, contrastColor, overflowGoesToViewport, parsePositionArea, snapToPixel, trapFocusInside, onAncestorReopen, canScroll, measureWidestChildRow, performTabNavigation, dragAfterIntent, stickyAsRelativeCoords, createDragToMoveGestureController, getDropTargetInfo, setStyles, useActiveElement, setVirtualKeyboardOverlaysContent, subscribeVirtualKeyboardGeometryChange, getVirtualKeyboardOverlayHeight } from "@jsenv/dom";
|
|
7
7
|
export { chainEvent, clickIsSuppressed, contrastColor, createDragGestureController, dragAfterIntent, findEvent, markDragSource, startDragTo } from "@jsenv/dom";
|
|
8
8
|
import { signal, computed, effect, untracked, batch, useComputed, useSignal } from "@preact/signals";
|
|
9
|
-
import { useState, useLayoutEffect, useErrorBoundary, useContext, useMemo, useRef, useReducer, useEffect, useCallback
|
|
9
|
+
import { useState, useLayoutEffect, useErrorBoundary, useContext, useMemo, useRef, useReducer, useEffect, useCallback } from "preact/hooks";
|
|
10
10
|
import { options, isValidElement, createContext, render as render$1, h, toChildArray, createRef, cloneElement, createElement, Fragment as Fragment$1 } from "preact";
|
|
11
11
|
import { humanizeI18n, setRuntimeLangSource, formatDuration, formatMonth, formatDay, resolveTimeRangePrecision, formatDatePlaceholder, toDate, getRelativeDay, formatDayRelative, formatMonthPlaceholder, formatWeekPlaceholder, formatDatetimePlaceholder, formatDatetime, toTimeOfDay, formatTimeOfDay, formatMinuteDuration, formatTime, formatSecondDuration, formatHourDuration, formatTimeRelative, pluralCategory, formatNumber, interpolateText, installInterpolateJsx } from "@jsenv/humanize";
|
|
12
12
|
export { createI18n, formatByteSize, formatDatetime, formatDay, formatDayRelative, formatDuration, formatHourDuration, formatList, formatMinuteDuration, formatMonth, formatNumber, formatSecondDuration, formatTime, formatTimeOfDay, formatTimeRange, formatTimeRelative, interpolateText, pluralCategory } from "@jsenv/humanize";
|
|
@@ -13457,13 +13457,13 @@ const writePressWritesHere = (pressWrites) => {
|
|
|
13457
13457
|
}
|
|
13458
13458
|
};
|
|
13459
13459
|
|
|
13460
|
-
//
|
|
13461
|
-
//
|
|
13462
|
-
//
|
|
13463
|
-
// same
|
|
13464
|
-
const
|
|
13465
|
-
const
|
|
13466
|
-
return
|
|
13460
|
+
// An id made up at render names one mount: navi's own ("navi_<n>", see
|
|
13461
|
+
// useGeneratedId) counts mounts in the document, and preact's useId(), which
|
|
13462
|
+
// an app may hand a component, gives "P<mask0>-<mask1>" from the render order.
|
|
13463
|
+
// Neither names the same component in two mounts or two documents.
|
|
13464
|
+
const GENERATED_ID_REGEX = /^(?:navi_\d+|P\d+-\d+)/;
|
|
13465
|
+
const isGeneratedId = (id) => {
|
|
13466
|
+
return GENERATED_ID_REGEX.test(id);
|
|
13467
13467
|
};
|
|
13468
13468
|
|
|
13469
13469
|
/**
|
|
@@ -13487,7 +13487,7 @@ const dropGeneratedIdKeys = (state) => {
|
|
|
13487
13487
|
}
|
|
13488
13488
|
let stateWithoutGeneratedIdKeys = state;
|
|
13489
13489
|
for (const key of Object.keys(state)) {
|
|
13490
|
-
if (!
|
|
13490
|
+
if (!isGeneratedId(key)) {
|
|
13491
13491
|
continue;
|
|
13492
13492
|
}
|
|
13493
13493
|
if (stateWithoutGeneratedIdKeys === state) {
|
|
@@ -14776,10 +14776,10 @@ const useNavStateBasic = (
|
|
|
14776
14776
|
|
|
14777
14777
|
let effectiveType = type;
|
|
14778
14778
|
// A push bakes the key into a history entry of its own, and an id naming a
|
|
14779
|
-
// render position (see
|
|
14779
|
+
// render position (see isGeneratedId) can be claimed by two
|
|
14780
14780
|
// mounts of this same page. A replace keeps the key on the entry the document
|
|
14781
14781
|
// is already on, where the id at least names one render.
|
|
14782
|
-
if (type === "push" &&
|
|
14782
|
+
if (type === "push" && isGeneratedId(id)) {
|
|
14783
14783
|
effectiveType = "replace";
|
|
14784
14784
|
}
|
|
14785
14785
|
|
|
@@ -14895,7 +14895,7 @@ const useNavStateBasic = (
|
|
|
14895
14895
|
* - "push": creates a new history entry — pressing the back button removes it and calls onLeave.
|
|
14896
14896
|
* - "replace": updates the current history entry — no extra history entry is created.
|
|
14897
14897
|
* Silently downgraded to "replace" (with a dev-only console.warn) when `id`
|
|
14898
|
-
* looks auto-generated (
|
|
14898
|
+
* looks auto-generated (navi's own, or preact's useId()) — an unstable id baked
|
|
14899
14899
|
* into a pushed history entry won't survive a reload correctly, and could
|
|
14900
14900
|
* even collide with a different component's own auto-generated id. Pass a
|
|
14901
14901
|
* stable, explicit id to actually get "push" behavior.
|
|
@@ -29677,7 +29677,8 @@ ${name}.many("${propertyName}") source location: ${declarationSite}`,
|
|
|
29677
29677
|
* - `DELETE: async ({ id }) => [id, null]`
|
|
29678
29678
|
*
|
|
29679
29679
|
* `ownerId` may also be `{ [uniqueKey]: value }` when the owner is known by an alternate key.
|
|
29680
|
-
* The property is `
|
|
29680
|
+
* The property is `undefined` until an answer speaks for the owner (its row, or a
|
|
29681
|
+
* callback), `null` when that answer holds none; setting it to `null` clears it.
|
|
29681
29682
|
* Mutations apply directly to the owner's signal, so the parent GET is never rerun; an
|
|
29682
29683
|
* owner the store does not hold yet is left alone, its child comes with its next read.
|
|
29683
29684
|
*
|
|
@@ -29733,7 +29734,10 @@ ${name}.many("${propertyName}") source location: ${declarationSite}`,
|
|
|
29733
29734
|
for (const childSetup of childSetupCallbackSet) {
|
|
29734
29735
|
childSetup(childItem);
|
|
29735
29736
|
}
|
|
29736
|
-
|
|
29737
|
+
// `undefined` until something answers for this owner, `null` once an
|
|
29738
|
+
// answer says it holds none: a screen tells « not read yet » from
|
|
29739
|
+
// « nothing » as it does with an action's data.
|
|
29740
|
+
const childSignal = signal(undefined);
|
|
29737
29741
|
// The child keeps its identity while its props change, so the owner's
|
|
29738
29742
|
// getter also reads this version, bumped by each change in place:
|
|
29739
29743
|
// whichever way the props came in — the owner's row, a write of this
|
|
@@ -29742,7 +29746,10 @@ ${name}.many("${propertyName}") source location: ${declarationSite}`,
|
|
|
29742
29746
|
// Returns whether the child changed — what the store asks a relation
|
|
29743
29747
|
// (see assign in array_signal_store.js).
|
|
29744
29748
|
const applyProps = (props) => {
|
|
29745
|
-
if (
|
|
29749
|
+
if (props === undefined) {
|
|
29750
|
+
return false;
|
|
29751
|
+
}
|
|
29752
|
+
if (props === null) {
|
|
29746
29753
|
if (childSignal.peek() === null) {
|
|
29747
29754
|
return false;
|
|
29748
29755
|
}
|
|
@@ -29764,7 +29771,7 @@ ${name}.many("${propertyName}") source location: ${declarationSite}`,
|
|
|
29764
29771
|
}
|
|
29765
29772
|
}
|
|
29766
29773
|
if (childSignal.peek() !== childItem) {
|
|
29767
|
-
childSignal.value = childItem; // first activation
|
|
29774
|
+
childSignal.value = childItem; // first activation
|
|
29768
29775
|
return true;
|
|
29769
29776
|
}
|
|
29770
29777
|
if (changed) {
|
|
@@ -33639,7 +33646,12 @@ const Box = props => {
|
|
|
33639
33646
|
mount,
|
|
33640
33647
|
...computeProps
|
|
33641
33648
|
} = props;
|
|
33642
|
-
|
|
33649
|
+
// The flow of the nearest Box above in the component tree, which lays this
|
|
33650
|
+
// box out unless a plain element sits between the two in the DOM: the box
|
|
33651
|
+
// then reads that element's flow once mounted (see readDomParentFlow).
|
|
33652
|
+
const contextBoxFlow = useContext(BoxFlowContext);
|
|
33653
|
+
const [domParentFlow, setDomParentFlow] = useState(null);
|
|
33654
|
+
const parentBoxFlow = domParentFlow && domParentFlow.contextBoxFlow === contextBoxFlow ? domParentFlow.flow : contextBoxFlow;
|
|
33643
33655
|
// Which interactions this box answers, and with what. Read here rather than
|
|
33644
33656
|
// on the control, so a swipe or a hold can be declared on anything — a row, a
|
|
33645
33657
|
// card, a block of text — and reach the control it belongs to (which is what
|
|
@@ -33686,7 +33698,17 @@ const Box = props => {
|
|
|
33686
33698
|
childForwardedProps,
|
|
33687
33699
|
styleDeps
|
|
33688
33700
|
} = computed;
|
|
33689
|
-
const
|
|
33701
|
+
const verifyParentFlow = parentBoxFlow === contextBoxFlow && FLOW_STYLING_ITS_CHILDREN_SET.has(contextBoxFlow) && readsParentFlow(computeProps);
|
|
33702
|
+
const syncBox = useCallback(verifyParentFlow ? boxEl => {
|
|
33703
|
+
const flow = readDomParentFlow(boxEl);
|
|
33704
|
+
if (flow !== contextBoxFlow) {
|
|
33705
|
+
setDomParentFlow({
|
|
33706
|
+
contextBoxFlow,
|
|
33707
|
+
flow
|
|
33708
|
+
});
|
|
33709
|
+
}
|
|
33710
|
+
return computed.syncBox(boxEl);
|
|
33711
|
+
} : computed.syncBox, styleDeps);
|
|
33690
33712
|
const finalRef = useComposeElementRef(syncBox, ref);
|
|
33691
33713
|
useInteractionsEffect(finalRef, interactionsRef);
|
|
33692
33714
|
let innerChildren = children;
|
|
@@ -33991,6 +34013,13 @@ const computeBox = (props, parentBoxFlow) => {
|
|
|
33991
34013
|
const mergedValue = prepareStyleValue(stylesTarget[name], value, name, styleContext);
|
|
33992
34014
|
const cssVar = readCSSVarName(styleContext.styleCSSVars[name]);
|
|
33993
34015
|
if (cssVar) {
|
|
34016
|
+
if (name === "color" && value === "inherit") {
|
|
34017
|
+
// A token set to `inherit` takes the token of the parent element
|
|
34018
|
+
// (a list's white for its selected items), never the parent's color.
|
|
34019
|
+
// `color: currentColor` IS `color: inherit`, and a token carries it.
|
|
34020
|
+
addCSSVar("currentColor", cssVar, stylesTarget);
|
|
34021
|
+
return true;
|
|
34022
|
+
}
|
|
33994
34023
|
addCSSVar(mergedValue, cssVar, stylesTarget);
|
|
33995
34024
|
if (name === "borderRadius" && value === "inherit") {
|
|
33996
34025
|
// "inherit" cannot be expressed via a CSS variable — a var() reference
|
|
@@ -34302,6 +34331,47 @@ const declaresSomething = value => {
|
|
|
34302
34331
|
}
|
|
34303
34332
|
return true;
|
|
34304
34333
|
};
|
|
34334
|
+
// The flows the styles of a child branch on (see expandX, selfAlignX… in
|
|
34335
|
+
// box_style_util.js): under any other the child writes the same styles
|
|
34336
|
+
// whatever its parent is.
|
|
34337
|
+
const FLOW_STYLING_ITS_CHILDREN_SET = new Set(["flex-x", "flex-y", "inline-flex-x", "inline-flex-y", "grid", "inline-grid"]);
|
|
34338
|
+
const PARENT_FLOW_PROP_NAMES = ["expand", "expandX", "expandY", "selfAlignX", "selfAlignY"];
|
|
34339
|
+
const readsParentFlow = props => {
|
|
34340
|
+
for (const name of PARENT_FLOW_PROP_NAMES) {
|
|
34341
|
+
if (props[name]) {
|
|
34342
|
+
return true;
|
|
34343
|
+
}
|
|
34344
|
+
}
|
|
34345
|
+
return false;
|
|
34346
|
+
};
|
|
34347
|
+
// A Box parent says its flow on its element; a plain element is read from its
|
|
34348
|
+
// computed style, which only a box mounted under one pays for.
|
|
34349
|
+
const readDomParentFlow = boxEl => {
|
|
34350
|
+
let parent = boxEl.parentElement;
|
|
34351
|
+
while (parent) {
|
|
34352
|
+
const flowAttribute = parent.getAttribute("navi-box-flow");
|
|
34353
|
+
if (flowAttribute) {
|
|
34354
|
+
return flowAttribute;
|
|
34355
|
+
}
|
|
34356
|
+
const {
|
|
34357
|
+
display,
|
|
34358
|
+
flexDirection
|
|
34359
|
+
} = window.getComputedStyle(parent);
|
|
34360
|
+
if (display === "contents") {
|
|
34361
|
+
parent = parent.parentElement;
|
|
34362
|
+
continue;
|
|
34363
|
+
}
|
|
34364
|
+
if (display === "flex" || display === "inline-flex") {
|
|
34365
|
+
const axis = flexDirection.startsWith("column") ? "y" : "x";
|
|
34366
|
+
return `${display}-${axis}`;
|
|
34367
|
+
}
|
|
34368
|
+
if (display === "grid" || display === "inline-grid") {
|
|
34369
|
+
return display;
|
|
34370
|
+
}
|
|
34371
|
+
return "block";
|
|
34372
|
+
}
|
|
34373
|
+
return undefined;
|
|
34374
|
+
};
|
|
34305
34375
|
const isScrollingOverflow = value => value === "auto" || value === "scroll";
|
|
34306
34376
|
const mayHoldComponent = children => {
|
|
34307
34377
|
if (children === null || children === undefined) {
|
|
@@ -45314,17 +45384,22 @@ document.body.addEventListener(
|
|
|
45314
45384
|
* on screen. A registry keyed by id then holds the newcomer, and loses the
|
|
45315
45385
|
* first control when the newcomer leaves. navi renders on the client only, so
|
|
45316
45386
|
* a counter is all the uniqueness needs.
|
|
45387
|
+
*
|
|
45388
|
+
* Held in a ref, not in useState: @preact/signals' shouldComponentUpdate
|
|
45389
|
+
* skips the render of a component holding hook state whose props did not
|
|
45390
|
+
* change, so an id kept as state would change when its caller renders.
|
|
45317
45391
|
*/
|
|
45318
45392
|
|
|
45319
45393
|
|
|
45320
45394
|
let generatedIdCount = 0;
|
|
45321
45395
|
|
|
45322
45396
|
const useGeneratedId = () => {
|
|
45323
|
-
const
|
|
45397
|
+
const idRef = useRef(null);
|
|
45398
|
+
if (idRef.current === null) {
|
|
45324
45399
|
generatedIdCount++;
|
|
45325
|
-
|
|
45326
|
-
}
|
|
45327
|
-
return
|
|
45400
|
+
idRef.current = `navi_${generatedIdCount}`;
|
|
45401
|
+
}
|
|
45402
|
+
return idRef.current;
|
|
45328
45403
|
};
|
|
45329
45404
|
|
|
45330
45405
|
/**
|
|
@@ -59788,7 +59863,7 @@ const Expandable = props => {
|
|
|
59788
59863
|
const rootRef = ref || defaultRef;
|
|
59789
59864
|
const uiRef = useRef();
|
|
59790
59865
|
const contentContainerRef = useRef();
|
|
59791
|
-
const contentId =
|
|
59866
|
+
const contentId = useGeneratedId();
|
|
59792
59867
|
const isColumn = layout === "column";
|
|
59793
59868
|
const closedContentSized = isColumn && mount === "always";
|
|
59794
59869
|
// Only the two directions the layout has room for: a column layout reveals
|
|
@@ -61428,7 +61503,7 @@ const ButtonConfirm = ({
|
|
|
61428
61503
|
// it is elsewhere by then, and pulling it back would take it from whatever
|
|
61429
61504
|
// the person moved to.
|
|
61430
61505
|
const restoreFocusRef = useRef(false);
|
|
61431
|
-
const questionId = `${
|
|
61506
|
+
const questionId = `${useGeneratedId()}_question`;
|
|
61432
61507
|
const cancel = restoreFocus => {
|
|
61433
61508
|
restoreFocusRef.current = restoreFocus;
|
|
61434
61509
|
setAsking(false);
|
|
@@ -62213,7 +62288,7 @@ const ControlSwap = props => {
|
|
|
62213
62288
|
const activeSide = sides[activeIndex];
|
|
62214
62289
|
const capRefs = [useRef(), useRef()];
|
|
62215
62290
|
const slotRefs = [useRef(), useRef()];
|
|
62216
|
-
const slotIdPrefix =
|
|
62291
|
+
const slotIdPrefix = useGeneratedId();
|
|
62217
62292
|
const activeNameRef = useRef(floor.name);
|
|
62218
62293
|
activeNameRef.current = activeSide.name;
|
|
62219
62294
|
const swapTo = (name, event, pressedCapIndex = null) => {
|
|
@@ -63063,7 +63138,7 @@ const InputCheckboxFieldInterface = props => {
|
|
|
63063
63138
|
// setting's name — which are its label and its accessible name: a mode one
|
|
63064
63139
|
// toggles is a checkbox, not a button writing a signal on each press.
|
|
63065
63140
|
const hasLabel = variant === "button" && children !== undefined && children !== null && children !== false;
|
|
63066
|
-
const labelId =
|
|
63141
|
+
const labelId = useGeneratedId();
|
|
63067
63142
|
const boxRef = useRef();
|
|
63068
63143
|
useAccentColorAttributes(boxRef, accentColor, {
|
|
63069
63144
|
elementSelector: ".navi_checkbox_accent_probe"
|
|
@@ -74208,7 +74283,7 @@ const PopupTitleContext = createContext(null);
|
|
|
74208
74283
|
const PopupTitle = props => {
|
|
74209
74284
|
import.meta.css = [css$E, "@jsenv/navi/src/layout/popup_title.jsx"];
|
|
74210
74285
|
const registerTitle = useContext(PopupTitleContext);
|
|
74211
|
-
const idDefault =
|
|
74286
|
+
const idDefault = useGeneratedId();
|
|
74212
74287
|
const id = props.id || `popup_title_${idDefault}`;
|
|
74213
74288
|
useLayoutEffect(() => {
|
|
74214
74289
|
if (!registerTitle) {
|
|
@@ -77513,7 +77588,7 @@ const usePopoverProps = props => {
|
|
|
77513
77588
|
// open, two separate invocations of openEffect that don't otherwise share
|
|
77514
77589
|
// any scope, hence the ref.
|
|
77515
77590
|
const disarmBackdropHideRef = useRef(null);
|
|
77516
|
-
const defaultId =
|
|
77591
|
+
const defaultId = useGeneratedId();
|
|
77517
77592
|
const id = rest.id || defaultId;
|
|
77518
77593
|
const backdropId = `${id}-backdrop`;
|
|
77519
77594
|
const debugPopup = useDebugPopup();
|
|
@@ -79103,10 +79178,10 @@ const PickerCustom = props => {
|
|
|
79103
79178
|
// before computing popupId below, so two Pickers without an explicit id never collide.
|
|
79104
79179
|
// Captured before the fallback chain below overwrites props.id — needed to
|
|
79105
79180
|
// know whether the id actually came from the caller (stable) or from
|
|
79106
|
-
//
|
|
79181
|
+
// useGeneratedId()/ControlIdContext (a generated id names one mount: a reload, or a
|
|
79107
79182
|
// return to this page, generates another), see pickerNavType below.
|
|
79108
79183
|
const hasExplicitId = Boolean(props.id);
|
|
79109
|
-
const idDefault =
|
|
79184
|
+
const idDefault = useGeneratedId();
|
|
79110
79185
|
const controlId = useContext(ControlIdContext);
|
|
79111
79186
|
props.id = props.id || controlId || idDefault;
|
|
79112
79187
|
// Given no value, the picker holds what the control in its popup holds (see
|
|
@@ -79207,7 +79282,7 @@ const PickerCustom = props => {
|
|
|
79207
79282
|
const debugPopup = useDebugPopup();
|
|
79208
79283
|
// In "dialog" mode with a stable, caller-provided id, enterExpanded()
|
|
79209
79284
|
// pushes a history entry so the back button closes it. Every other case
|
|
79210
|
-
// (popover mode, or a dialog whose id was auto-generated via
|
|
79285
|
+
// (popover mode, or a dialog whose id was auto-generated via useGeneratedId()/
|
|
79211
79286
|
// ControlIdContext) replaces the current history state instead — a
|
|
79212
79287
|
// generated id names one mount, so pushing it would either leave an entry
|
|
79213
79288
|
// nothing reads or, worse, collide with a different component's own
|
|
@@ -82084,7 +82159,7 @@ const ListSelectable = props => {
|
|
|
82084
82159
|
const Next = useNextResolver();
|
|
82085
82160
|
import.meta.css = [css$x, "@jsenv/navi/src/control/list/list_selectable.jsx"];
|
|
82086
82161
|
// we allow ourselves to auto-generate a name
|
|
82087
|
-
const defaultName =
|
|
82162
|
+
const defaultName = useGeneratedId();
|
|
82088
82163
|
props.name = props.name || `listbox_${defaultName}`;
|
|
82089
82164
|
const {
|
|
82090
82165
|
ref,
|
|
@@ -82433,7 +82508,7 @@ const ListItemSelectableResolver = props => {
|
|
|
82433
82508
|
};
|
|
82434
82509
|
const ListItemSelectable = props => {
|
|
82435
82510
|
const Next = useNextResolver();
|
|
82436
|
-
const defaultId =
|
|
82511
|
+
const defaultId = useGeneratedId();
|
|
82437
82512
|
// Whether the caller SAYS anything about this row's selection. Passing
|
|
82438
82513
|
// `checked: undefined` is not the same as not passing it: a control with the
|
|
82439
82514
|
// key present calls itself controlled, and a controlled row is one the list
|
|
@@ -83423,7 +83498,7 @@ const ListUI = props => {
|
|
|
83423
83498
|
} = props;
|
|
83424
83499
|
// Remembered by name, and a name made up at render (see ListFirstResolver)
|
|
83425
83500
|
// names no list a later mount would recognize.
|
|
83426
|
-
const rememberScroll = !scrollResetOnNavigation && !
|
|
83501
|
+
const rememberScroll = !scrollResetOnNavigation && !isGeneratedId(rest.id);
|
|
83427
83502
|
// Where the list was when its screen was left, when this is the way back
|
|
83428
83503
|
// (see scroll_restoration.js). Read once: `defaultScrolled` is held by
|
|
83429
83504
|
// reference, and a place read again each render would be a list moved each
|
|
@@ -83733,7 +83808,7 @@ const ListFirstResolver = props => {
|
|
|
83733
83808
|
const Next = useNextResolver();
|
|
83734
83809
|
const refDefault = useRef(null);
|
|
83735
83810
|
props.ref = props.ref || refDefault;
|
|
83736
|
-
const idDefault =
|
|
83811
|
+
const idDefault = useGeneratedId();
|
|
83737
83812
|
props.id = props.id || idDefault;
|
|
83738
83813
|
const listItemsRef = useRef(null);
|
|
83739
83814
|
if (!listItemsRef.current) {
|
|
@@ -87147,7 +87222,7 @@ const ListItemUI = props => {
|
|
|
87147
87222
|
if (identityMatters && props.id === undefined) {
|
|
87148
87223
|
console.warn("ListItem is missing an explicit id prop. Provide a stable id so pointed/selected state survives search reordering.");
|
|
87149
87224
|
}
|
|
87150
|
-
const idDefault =
|
|
87225
|
+
const idDefault = useGeneratedId();
|
|
87151
87226
|
props.id = props.id || idDefault;
|
|
87152
87227
|
const listItems = useContext(ListItemsContext);
|
|
87153
87228
|
const groupId = useContext(ListGroupContext);
|
|
@@ -87960,7 +88035,7 @@ const useRunItems = (store, {
|
|
|
87960
88035
|
renderSkeleton,
|
|
87961
88036
|
renderError
|
|
87962
88037
|
}) => {
|
|
87963
|
-
const ownerId =
|
|
88038
|
+
const ownerId = useGeneratedId();
|
|
87964
88039
|
// Dev only: the group `groupOfMissing` put an item not held in, by rank,
|
|
87965
88040
|
// checked against `groupBy` once the item lands.
|
|
87966
88041
|
const missingGroupCheckRef = useRef(null);
|
|
@@ -88515,7 +88590,7 @@ const ListRunSkeletonItem = ({
|
|
|
88515
88590
|
}) => {
|
|
88516
88591
|
const listItems = useContext(ListItemsContext);
|
|
88517
88592
|
const groupId = useContext(ListGroupContext);
|
|
88518
|
-
const entryId =
|
|
88593
|
+
const entryId = useGeneratedId();
|
|
88519
88594
|
listItems.draw(entryId, {
|
|
88520
88595
|
ownerId: runItem.ownerId,
|
|
88521
88596
|
place: runItem.index,
|
|
@@ -89247,7 +89322,7 @@ const ListItemGroup = ({
|
|
|
89247
89322
|
children,
|
|
89248
89323
|
...rest
|
|
89249
89324
|
}) => {
|
|
89250
|
-
const groupId =
|
|
89325
|
+
const groupId = useGeneratedId();
|
|
89251
89326
|
const listItems = useContext(ListItemsContext);
|
|
89252
89327
|
const group = listItems.group(groupId);
|
|
89253
89328
|
useLayoutEffect(() => {
|
|
@@ -93470,7 +93545,7 @@ const Spin = ({
|
|
|
93470
93545
|
} else {
|
|
93471
93546
|
compareValues ??= compareValuesDefault;
|
|
93472
93547
|
}
|
|
93473
|
-
const id =
|
|
93548
|
+
const id = useGeneratedId();
|
|
93474
93549
|
// What the group around it says, when there is one: how big the whole thing
|
|
93475
93550
|
// is written is said once, on the group, and every spin in it follows.
|
|
93476
93551
|
const group = useContext(SpinGroupContext);
|
|
@@ -94688,7 +94763,7 @@ const TimeRangeSpin = ({
|
|
|
94688
94763
|
endTimeProps,
|
|
94689
94764
|
...rest
|
|
94690
94765
|
}) => {
|
|
94691
|
-
const startId =
|
|
94766
|
+
const startId = useGeneratedId();
|
|
94692
94767
|
return jsxs(ControlGroup, {
|
|
94693
94768
|
flex: true,
|
|
94694
94769
|
alignY: "center",
|
|
@@ -94781,7 +94856,7 @@ const css$q = /* css */`.navi_checkbox_group {
|
|
|
94781
94856
|
const CheckboxGroup = props => {
|
|
94782
94857
|
const refDefault = useRef(null);
|
|
94783
94858
|
props.ref = props.ref || refDefault;
|
|
94784
|
-
const defaultName =
|
|
94859
|
+
const defaultName = useGeneratedId();
|
|
94785
94860
|
props.name = props.name || `checkbox_group_${defaultName}`;
|
|
94786
94861
|
const checkboxGroup = jsx(CheckboxGroupInterface, {
|
|
94787
94862
|
...props
|
|
@@ -95546,7 +95621,7 @@ const css$o = /* css */`.navi_radio_group {
|
|
|
95546
95621
|
const RadioGroup = props => {
|
|
95547
95622
|
const refDefault = useRef(null);
|
|
95548
95623
|
props.ref = props.ref || refDefault;
|
|
95549
|
-
const defaultName =
|
|
95624
|
+
const defaultName = useGeneratedId();
|
|
95550
95625
|
props.name = props.name || `radio_group_${defaultName}`;
|
|
95551
95626
|
const radioGroup = jsx(RadioGroupInterface, {
|
|
95552
95627
|
...props
|
|
@@ -95884,7 +95959,7 @@ const SplitButton = props => {
|
|
|
95884
95959
|
if (popupProps.positionArea === undefined && mode === "popover") {
|
|
95885
95960
|
popupProps.positionArea = "bottom-end";
|
|
95886
95961
|
}
|
|
95887
|
-
const idDefault =
|
|
95962
|
+
const idDefault = useGeneratedId();
|
|
95888
95963
|
const idResolved = id || idDefault;
|
|
95889
95964
|
const menuId = `${idResolved}_menu`;
|
|
95890
95965
|
const rootRef = useRef(null);
|
|
@@ -96639,12 +96714,18 @@ const buildMatchInfo = (searchText, items, matchFn) => {
|
|
|
96639
96714
|
* needed. `data.role === "presentation"` keeps it out entirely.
|
|
96640
96715
|
* Returns the visible rank (position among visible items), or -1 when hidden
|
|
96641
96716
|
* or filtered.
|
|
96717
|
+
* An item rendered without an index keeps the position it has, and a new one
|
|
96718
|
+
* goes last: for a host that cannot know the order at render and gives it
|
|
96719
|
+
* afterwards with setOrder.
|
|
96642
96720
|
* Signals and onChange are deferred to a microtask so multiple items updating
|
|
96643
96721
|
* in one commit cause only one notification.
|
|
96644
96722
|
*
|
|
96645
96723
|
* getTrackedItemByIndex(index): synchronous O(1) lookup of a visible item by
|
|
96646
96724
|
* its visible rank. Returns undefined when index is out of range.
|
|
96647
96725
|
*
|
|
96726
|
+
* setOrder(ids): gives each tracked item its position from its rank in
|
|
96727
|
+
* `ids`.
|
|
96728
|
+
*
|
|
96648
96729
|
* peekItems(): the items as they stand right now, without waiting for the
|
|
96649
96730
|
* deferred notification — what a sibling rendering after the items must read
|
|
96650
96731
|
* to paint them in the same commit.
|
|
@@ -96925,8 +97006,14 @@ const createItemTracker = (onChange) => {
|
|
|
96925
97006
|
// that determines this item's position among siblings.
|
|
96926
97007
|
// Returns the item's visible rank among non-hidden items, or -1 when hidden.
|
|
96927
97008
|
const useTrackItem = (data) => {
|
|
96928
|
-
const { id
|
|
97009
|
+
const { id } = data;
|
|
96929
97010
|
const key = keyForId(id);
|
|
97011
|
+
let { index } = data;
|
|
97012
|
+
if (index === undefined) {
|
|
97013
|
+
index = keyToExplicitOrder.has(key)
|
|
97014
|
+
? keyToExplicitOrder.get(key)
|
|
97015
|
+
: Infinity;
|
|
97016
|
+
}
|
|
96930
97017
|
|
|
96931
97018
|
syncItem(key, index, data);
|
|
96932
97019
|
notify();
|
|
@@ -96944,6 +97031,24 @@ const createItemTracker = (onChange) => {
|
|
|
96944
97031
|
return keyToOrderedIndex.get(key) ?? -1;
|
|
96945
97032
|
};
|
|
96946
97033
|
|
|
97034
|
+
const setOrder = (ids) => {
|
|
97035
|
+
let changed = false;
|
|
97036
|
+
for (let i = 0; i < ids.length; i++) {
|
|
97037
|
+
const key = idToKey.get(ids[i]);
|
|
97038
|
+
if (key === undefined || !allRegistrations.has(key)) {
|
|
97039
|
+
continue;
|
|
97040
|
+
}
|
|
97041
|
+
if (keyToExplicitOrder.get(key) === i) {
|
|
97042
|
+
continue;
|
|
97043
|
+
}
|
|
97044
|
+
syncItem(key, i, allRegistrations.get(key));
|
|
97045
|
+
changed = true;
|
|
97046
|
+
}
|
|
97047
|
+
if (changed) {
|
|
97048
|
+
notify();
|
|
97049
|
+
}
|
|
97050
|
+
};
|
|
97051
|
+
|
|
96947
97052
|
const getTrackedItemByIndex = (index) => {
|
|
96948
97053
|
const key = orderedKeys[index];
|
|
96949
97054
|
if (key === undefined) {
|
|
@@ -96977,6 +97082,7 @@ const createItemTracker = (onChange) => {
|
|
|
96977
97082
|
return {
|
|
96978
97083
|
useTrackItem,
|
|
96979
97084
|
getTrackedItemByIndex,
|
|
97085
|
+
setOrder,
|
|
96980
97086
|
peekItems,
|
|
96981
97087
|
itemsSignal,
|
|
96982
97088
|
visibleItemsSignal,
|
|
@@ -97374,8 +97480,8 @@ const getNumericRange = items => {
|
|
|
97374
97480
|
|
|
97375
97481
|
// Wheel.Item registers its {value, label} here so WheelUI knows the full ordered
|
|
97376
97482
|
// list of items regardless of how children are wrapped (providers, fragments…).
|
|
97377
|
-
//
|
|
97378
|
-
//
|
|
97483
|
+
// The position comes after the commit, from the marker each item leaves where it
|
|
97484
|
+
// stands (see WheelUI).
|
|
97379
97485
|
const WheelItemTrackerContext = createContext(null);
|
|
97380
97486
|
|
|
97381
97487
|
// Lets a WheelGroup push shared presentation (glass, orientation) down to every
|
|
@@ -97997,15 +98103,21 @@ function WheelUI(props) {
|
|
|
97997
98103
|
// (children may be wrapped). This ordered list is the source of truth for the
|
|
97998
98104
|
// whole wheel — the rendered rows are a small recycled window over it.
|
|
97999
98105
|
const tracker = useItemTracker();
|
|
98000
|
-
|
|
98001
|
-
|
|
98002
|
-
|
|
98003
|
-
|
|
98004
|
-
|
|
98005
|
-
|
|
98006
|
-
|
|
98007
|
-
|
|
98008
|
-
|
|
98106
|
+
// The order is read from the markers the items leave in the DOM, not from the
|
|
98107
|
+
// order they render in: an item whose props did not change may skip a render
|
|
98108
|
+
// (signals' shouldComponentUpdate), and a render count would then rank the
|
|
98109
|
+
// ones that did render first.
|
|
98110
|
+
useLayoutEffect(() => {
|
|
98111
|
+
const el = ref.current;
|
|
98112
|
+
if (!el) {
|
|
98113
|
+
return;
|
|
98114
|
+
}
|
|
98115
|
+
const ids = [];
|
|
98116
|
+
for (const marker of el.querySelectorAll("[data-navi-wheel-item]")) {
|
|
98117
|
+
ids.push(marker.getAttribute("data-navi-wheel-item"));
|
|
98118
|
+
}
|
|
98119
|
+
tracker.setOrder(ids);
|
|
98120
|
+
});
|
|
98009
98121
|
const trackedItems = tracker.itemsSignal.value;
|
|
98010
98122
|
const trackedItemsRef = useRef(trackedItems);
|
|
98011
98123
|
trackedItemsRef.current = trackedItems;
|
|
@@ -98805,7 +98917,7 @@ function WheelUI(props) {
|
|
|
98805
98917
|
className: "navi_wheel_pane",
|
|
98806
98918
|
"data-side": "start"
|
|
98807
98919
|
}), jsx(WheelItemTrackerContext.Provider, {
|
|
98808
|
-
value:
|
|
98920
|
+
value: tracker,
|
|
98809
98921
|
children: jsx(ControlFacadeChildrenWrapper, {
|
|
98810
98922
|
facadeController: facadeController,
|
|
98811
98923
|
children: children
|
|
@@ -98918,9 +99030,10 @@ const WheelWindow = ({
|
|
|
98918
99030
|
* Wheel.Item — a value in a Wheel. Must be used inside <Wheel>.
|
|
98919
99031
|
*
|
|
98920
99032
|
* Registration only: it records its {value, label, itemProps} with the wheel and
|
|
98921
|
-
* renders
|
|
98922
|
-
*
|
|
98923
|
-
*
|
|
99033
|
+
* renders no row, only a hidden marker the wheel reads its position from. The
|
|
99034
|
+
* wheel is virtualized — it renders a small recycled window of rows and fills
|
|
99035
|
+
* each from this tracked data — so the value list is the source of truth, not a
|
|
99036
|
+
* per-item DOM node. Selection lives on the wheel's value.
|
|
98924
99037
|
*
|
|
98925
99038
|
* @type {import("preact").FunctionComponent<{
|
|
98926
99039
|
* value: any,
|
|
@@ -98934,19 +99047,20 @@ const WheelItem = ({
|
|
|
98934
99047
|
children,
|
|
98935
99048
|
...rest
|
|
98936
99049
|
}) => {
|
|
98937
|
-
const idDefault =
|
|
99050
|
+
const idDefault = useGeneratedId();
|
|
98938
99051
|
const resolvedId = id || idDefault;
|
|
98939
99052
|
// Wheel.Item must live inside a Wheel, so the context is always present.
|
|
98940
|
-
const
|
|
98941
|
-
|
|
98942
|
-
trackerContext.tracker.useTrackItem({
|
|
99053
|
+
const tracker = useContext(WheelItemTrackerContext);
|
|
99054
|
+
tracker.useTrackItem({
|
|
98943
99055
|
id: resolvedId,
|
|
98944
|
-
index,
|
|
98945
99056
|
value,
|
|
98946
99057
|
label: children,
|
|
98947
99058
|
itemProps: rest
|
|
98948
99059
|
});
|
|
98949
|
-
return
|
|
99060
|
+
return jsx("span", {
|
|
99061
|
+
hidden: true,
|
|
99062
|
+
"data-navi-wheel-item": resolvedId
|
|
99063
|
+
});
|
|
98950
99064
|
};
|
|
98951
99065
|
Wheel.Item = WheelItem;
|
|
98952
99066
|
|
|
@@ -99532,7 +99646,7 @@ const TimeRangeWheel = ({
|
|
|
99532
99646
|
...rest
|
|
99533
99647
|
}) => {
|
|
99534
99648
|
import.meta.css = [css$j, "@jsenv/navi/src/control/wheel/wheel_time.jsx"];
|
|
99535
|
-
const startId =
|
|
99649
|
+
const startId = useGeneratedId();
|
|
99536
99650
|
const startRef = useRef(null);
|
|
99537
99651
|
const endRef = useRef(null);
|
|
99538
99652
|
const endHourList = useMemo(() => resolveHourList(hours), [hours ? hours.min : undefined, hours ? hours.max : undefined, hours]);
|
|
@@ -102520,7 +102634,7 @@ const useIsInTableHead = () => useContext(TableSectionContext) === "head";
|
|
|
102520
102634
|
const Table = props => {
|
|
102521
102635
|
installTableCss();
|
|
102522
102636
|
const tableDefaultRef = useRef();
|
|
102523
|
-
const tableDefaultId = `table-${
|
|
102637
|
+
const tableDefaultId = `table-${useGeneratedId()}`;
|
|
102524
102638
|
const {
|
|
102525
102639
|
id = tableDefaultId,
|
|
102526
102640
|
selection = [],
|