@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.
@@ -46773,17 +46773,22 @@ document.body.addEventListener(
46773
46773
  * on screen. A registry keyed by id then holds the newcomer, and loses the
46774
46774
  * first control when the newcomer leaves. navi renders on the client only, so
46775
46775
  * a counter is all the uniqueness needs.
46776
+ *
46777
+ * Held in a ref, not in useState: @preact/signals' shouldComponentUpdate
46778
+ * skips the render of a component holding hook state whose props did not
46779
+ * change, so an id kept as state would change when its caller renders.
46776
46780
  */
46777
46781
 
46778
46782
 
46779
46783
  let generatedIdCount = 0;
46780
46784
 
46781
46785
  const useGeneratedId = () => {
46782
- const [id] = useState(() => {
46786
+ const idRef = useRef(null);
46787
+ if (idRef.current === null) {
46783
46788
  generatedIdCount++;
46784
- return `navi_${generatedIdCount}`;
46785
- });
46786
- return id;
46789
+ idRef.current = `navi_${generatedIdCount}`;
46790
+ }
46791
+ return idRef.current;
46787
46792
  };
46788
46793
 
46789
46794
  /**
@@ -98829,12 +98834,18 @@ const buildMatchInfo = (searchText, items, matchFn) => {
98829
98834
  * needed. `data.role === "presentation"` keeps it out entirely.
98830
98835
  * Returns the visible rank (position among visible items), or -1 when hidden
98831
98836
  * or filtered.
98837
+ * An item rendered without an index keeps the position it has, and a new one
98838
+ * goes last: for a host that cannot know the order at render and gives it
98839
+ * afterwards with setOrder.
98832
98840
  * Signals and onChange are deferred to a microtask so multiple items updating
98833
98841
  * in one commit cause only one notification.
98834
98842
  *
98835
98843
  * getTrackedItemByIndex(index): synchronous O(1) lookup of a visible item by
98836
98844
  * its visible rank. Returns undefined when index is out of range.
98837
98845
  *
98846
+ * setOrder(ids): gives each tracked item its position from its rank in
98847
+ * `ids`.
98848
+ *
98838
98849
  * peekItems(): the items as they stand right now, without waiting for the
98839
98850
  * deferred notification — what a sibling rendering after the items must read
98840
98851
  * to paint them in the same commit.
@@ -99115,8 +99126,14 @@ const createItemTracker = (onChange) => {
99115
99126
  // that determines this item's position among siblings.
99116
99127
  // Returns the item's visible rank among non-hidden items, or -1 when hidden.
99117
99128
  const useTrackItem = (data) => {
99118
- const { id, index } = data;
99129
+ const { id } = data;
99119
99130
  const key = keyForId(id);
99131
+ let { index } = data;
99132
+ if (index === undefined) {
99133
+ index = keyToExplicitOrder.has(key)
99134
+ ? keyToExplicitOrder.get(key)
99135
+ : Infinity;
99136
+ }
99120
99137
 
99121
99138
  syncItem(key, index, data);
99122
99139
  notify();
@@ -99134,6 +99151,24 @@ const createItemTracker = (onChange) => {
99134
99151
  return keyToOrderedIndex.get(key) ?? -1;
99135
99152
  };
99136
99153
 
99154
+ const setOrder = (ids) => {
99155
+ let changed = false;
99156
+ for (let i = 0; i < ids.length; i++) {
99157
+ const key = idToKey.get(ids[i]);
99158
+ if (key === undefined || !allRegistrations.has(key)) {
99159
+ continue;
99160
+ }
99161
+ if (keyToExplicitOrder.get(key) === i) {
99162
+ continue;
99163
+ }
99164
+ syncItem(key, i, allRegistrations.get(key));
99165
+ changed = true;
99166
+ }
99167
+ if (changed) {
99168
+ notify();
99169
+ }
99170
+ };
99171
+
99137
99172
  const getTrackedItemByIndex = (index) => {
99138
99173
  const key = orderedKeys[index];
99139
99174
  if (key === undefined) {
@@ -99167,6 +99202,7 @@ const createItemTracker = (onChange) => {
99167
99202
  return {
99168
99203
  useTrackItem,
99169
99204
  getTrackedItemByIndex,
99205
+ setOrder,
99170
99206
  peekItems,
99171
99207
  itemsSignal,
99172
99208
  visibleItemsSignal,
@@ -99564,8 +99600,8 @@ const getNumericRange = items => {
99564
99600
 
99565
99601
  // Wheel.Item registers its {value, label} here so WheelUI knows the full ordered
99566
99602
  // list of items regardless of how children are wrapped (providers, fragments…).
99567
- // indexRef gives each item its position: WheelUI resets it to 0 every render and
99568
- // each item reads-and-increments it as it renders, in document order.
99603
+ // The position comes after the commit, from the marker each item leaves where it
99604
+ // stands (see WheelUI).
99569
99605
  const WheelItemTrackerContext = createContext(null);
99570
99606
 
99571
99607
  // Lets a WheelGroup push shared presentation (glass, orientation) down to every
@@ -100188,15 +100224,21 @@ function WheelUI(props) {
100188
100224
  // (children may be wrapped). This ordered list is the source of truth for the
100189
100225
  // whole wheel — the rendered rows are a small recycled window over it.
100190
100226
  const tracker = useItemTracker();
100191
- const indexRef = useRef(0);
100192
- indexRef.current = 0;
100193
- const trackerContextRef = useRef(null);
100194
- if (!trackerContextRef.current) {
100195
- trackerContextRef.current = {
100196
- tracker,
100197
- indexRef
100198
- };
100199
- }
100227
+ // The order is read from the markers the items leave in the DOM, not from the
100228
+ // order they render in: an item whose props did not change may skip a render
100229
+ // (signals' shouldComponentUpdate), and a render count would then rank the
100230
+ // ones that did render first.
100231
+ useLayoutEffect(() => {
100232
+ const el = ref.current;
100233
+ if (!el) {
100234
+ return;
100235
+ }
100236
+ const ids = [];
100237
+ for (const marker of el.querySelectorAll("[data-navi-wheel-item]")) {
100238
+ ids.push(marker.getAttribute("data-navi-wheel-item"));
100239
+ }
100240
+ tracker.setOrder(ids);
100241
+ });
100200
100242
  const trackedItems = tracker.itemsSignal.value;
100201
100243
  const trackedItemsRef = useRef(trackedItems);
100202
100244
  trackedItemsRef.current = trackedItems;
@@ -101003,7 +101045,7 @@ function WheelUI(props) {
101003
101045
  className: "navi_wheel_pane",
101004
101046
  "data-side": "start"
101005
101047
  }), jsx(WheelItemTrackerContext.Provider, {
101006
- value: trackerContextRef.current,
101048
+ value: tracker,
101007
101049
  children: jsx(ControlFacadeChildrenWrapper, {
101008
101050
  facadeController: facadeController,
101009
101051
  children: children
@@ -101116,9 +101158,10 @@ const WheelWindow = ({
101116
101158
  * Wheel.Item — a value in a Wheel. Must be used inside <Wheel>.
101117
101159
  *
101118
101160
  * Registration only: it records its {value, label, itemProps} with the wheel and
101119
- * renders NOTHING. The wheel is virtualized — it renders a small recycled window
101120
- * of rows and fills each from this tracked data — so the value list is the source
101121
- * of truth, not a per-item DOM node. Selection lives on the wheel's value.
101161
+ * renders no row, only a hidden marker the wheel reads its position from. The
101162
+ * wheel is virtualized — it renders a small recycled window of rows and fills
101163
+ * each from this tracked data — so the value list is the source of truth, not a
101164
+ * per-item DOM node. Selection lives on the wheel's value.
101122
101165
  *
101123
101166
  * @type {import("preact").FunctionComponent<{
101124
101167
  * value: any,
@@ -101135,16 +101178,17 @@ const WheelItem = ({
101135
101178
  const idDefault = useGeneratedId();
101136
101179
  const resolvedId = id || idDefault;
101137
101180
  // Wheel.Item must live inside a Wheel, so the context is always present.
101138
- const trackerContext = useContext(WheelItemTrackerContext);
101139
- const index = trackerContext.indexRef.current++;
101140
- trackerContext.tracker.useTrackItem({
101181
+ const tracker = useContext(WheelItemTrackerContext);
101182
+ tracker.useTrackItem({
101141
101183
  id: resolvedId,
101142
- index,
101143
101184
  value,
101144
101185
  label: children,
101145
101186
  itemProps: rest
101146
101187
  });
101147
- return null;
101188
+ return jsx("span", {
101189
+ hidden: true,
101190
+ "data-navi-wheel-item": resolvedId
101191
+ });
101148
101192
  };
101149
101193
  Wheel.Item = WheelItem;
101150
101194