@jsenv/navi 0.29.494 → 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.
@@ -45384,17 +45384,22 @@ document.body.addEventListener(
45384
45384
  * on screen. A registry keyed by id then holds the newcomer, and loses the
45385
45385
  * first control when the newcomer leaves. navi renders on the client only, so
45386
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.
45387
45391
  */
45388
45392
 
45389
45393
 
45390
45394
  let generatedIdCount = 0;
45391
45395
 
45392
45396
  const useGeneratedId = () => {
45393
- const [id] = useState(() => {
45397
+ const idRef = useRef(null);
45398
+ if (idRef.current === null) {
45394
45399
  generatedIdCount++;
45395
- return `navi_${generatedIdCount}`;
45396
- });
45397
- return id;
45400
+ idRef.current = `navi_${generatedIdCount}`;
45401
+ }
45402
+ return idRef.current;
45398
45403
  };
45399
45404
 
45400
45405
  /**
@@ -96709,12 +96714,18 @@ const buildMatchInfo = (searchText, items, matchFn) => {
96709
96714
  * needed. `data.role === "presentation"` keeps it out entirely.
96710
96715
  * Returns the visible rank (position among visible items), or -1 when hidden
96711
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.
96712
96720
  * Signals and onChange are deferred to a microtask so multiple items updating
96713
96721
  * in one commit cause only one notification.
96714
96722
  *
96715
96723
  * getTrackedItemByIndex(index): synchronous O(1) lookup of a visible item by
96716
96724
  * its visible rank. Returns undefined when index is out of range.
96717
96725
  *
96726
+ * setOrder(ids): gives each tracked item its position from its rank in
96727
+ * `ids`.
96728
+ *
96718
96729
  * peekItems(): the items as they stand right now, without waiting for the
96719
96730
  * deferred notification — what a sibling rendering after the items must read
96720
96731
  * to paint them in the same commit.
@@ -96995,8 +97006,14 @@ const createItemTracker = (onChange) => {
96995
97006
  // that determines this item's position among siblings.
96996
97007
  // Returns the item's visible rank among non-hidden items, or -1 when hidden.
96997
97008
  const useTrackItem = (data) => {
96998
- const { id, index } = data;
97009
+ const { id } = data;
96999
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
+ }
97000
97017
 
97001
97018
  syncItem(key, index, data);
97002
97019
  notify();
@@ -97014,6 +97031,24 @@ const createItemTracker = (onChange) => {
97014
97031
  return keyToOrderedIndex.get(key) ?? -1;
97015
97032
  };
97016
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
+
97017
97052
  const getTrackedItemByIndex = (index) => {
97018
97053
  const key = orderedKeys[index];
97019
97054
  if (key === undefined) {
@@ -97047,6 +97082,7 @@ const createItemTracker = (onChange) => {
97047
97082
  return {
97048
97083
  useTrackItem,
97049
97084
  getTrackedItemByIndex,
97085
+ setOrder,
97050
97086
  peekItems,
97051
97087
  itemsSignal,
97052
97088
  visibleItemsSignal,
@@ -97444,8 +97480,8 @@ const getNumericRange = items => {
97444
97480
 
97445
97481
  // Wheel.Item registers its {value, label} here so WheelUI knows the full ordered
97446
97482
  // list of items regardless of how children are wrapped (providers, fragments…).
97447
- // indexRef gives each item its position: WheelUI resets it to 0 every render and
97448
- // each item reads-and-increments it as it renders, in document order.
97483
+ // The position comes after the commit, from the marker each item leaves where it
97484
+ // stands (see WheelUI).
97449
97485
  const WheelItemTrackerContext = createContext(null);
97450
97486
 
97451
97487
  // Lets a WheelGroup push shared presentation (glass, orientation) down to every
@@ -98067,15 +98103,21 @@ function WheelUI(props) {
98067
98103
  // (children may be wrapped). This ordered list is the source of truth for the
98068
98104
  // whole wheel — the rendered rows are a small recycled window over it.
98069
98105
  const tracker = useItemTracker();
98070
- const indexRef = useRef(0);
98071
- indexRef.current = 0;
98072
- const trackerContextRef = useRef(null);
98073
- if (!trackerContextRef.current) {
98074
- trackerContextRef.current = {
98075
- tracker,
98076
- indexRef
98077
- };
98078
- }
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
+ });
98079
98121
  const trackedItems = tracker.itemsSignal.value;
98080
98122
  const trackedItemsRef = useRef(trackedItems);
98081
98123
  trackedItemsRef.current = trackedItems;
@@ -98875,7 +98917,7 @@ function WheelUI(props) {
98875
98917
  className: "navi_wheel_pane",
98876
98918
  "data-side": "start"
98877
98919
  }), jsx(WheelItemTrackerContext.Provider, {
98878
- value: trackerContextRef.current,
98920
+ value: tracker,
98879
98921
  children: jsx(ControlFacadeChildrenWrapper, {
98880
98922
  facadeController: facadeController,
98881
98923
  children: children
@@ -98988,9 +99030,10 @@ const WheelWindow = ({
98988
99030
  * Wheel.Item — a value in a Wheel. Must be used inside <Wheel>.
98989
99031
  *
98990
99032
  * Registration only: it records its {value, label, itemProps} with the wheel and
98991
- * renders NOTHING. The wheel is virtualized — it renders a small recycled window
98992
- * of rows and fills each from this tracked data — so the value list is the source
98993
- * of truth, not a per-item DOM node. Selection lives on the wheel's value.
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.
98994
99037
  *
98995
99038
  * @type {import("preact").FunctionComponent<{
98996
99039
  * value: any,
@@ -99007,16 +99050,17 @@ const WheelItem = ({
99007
99050
  const idDefault = useGeneratedId();
99008
99051
  const resolvedId = id || idDefault;
99009
99052
  // Wheel.Item must live inside a Wheel, so the context is always present.
99010
- const trackerContext = useContext(WheelItemTrackerContext);
99011
- const index = trackerContext.indexRef.current++;
99012
- trackerContext.tracker.useTrackItem({
99053
+ const tracker = useContext(WheelItemTrackerContext);
99054
+ tracker.useTrackItem({
99013
99055
  id: resolvedId,
99014
- index,
99015
99056
  value,
99016
99057
  label: children,
99017
99058
  itemProps: rest
99018
99059
  });
99019
- return null;
99060
+ return jsx("span", {
99061
+ hidden: true,
99062
+ "data-navi-wheel-item": resolvedId
99063
+ });
99020
99064
  };
99021
99065
  Wheel.Item = WheelItem;
99022
99066