@jsenv/navi 0.29.51 → 0.29.52

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.
@@ -55587,10 +55587,13 @@ const ListFirstResolver = props => {
55587
55587
  * thread read backwards — the last rows are the ones to show, and the ones
55588
55588
  * asked for first. A number opens on that row of the collection. `{id,
55589
55589
  * offset}` — what `onScrolledChange` hands out — opens on a NAMED row,
55590
- * `offset` pixels below the top of the view: the row is asked for by name
55591
- * (see the range's own `around`), then put back by MEASURING it, so it lands
55592
- * where it was even if rows were inserted before it, and whatever the screen
55593
- * it was saved on.
55590
+ * `offset` pixels below where the row would land on its own: the row is asked
55591
+ * for by name (see the range's own `around`), then put back by MEASURING it,
55592
+ * so it lands where it was even if rows were inserted before it, and whatever
55593
+ * the screen it was saved on. `offset: 0` is where a `scrollIntoView()` puts
55594
+ * it — in front of the fixed bar the scroller gives room for, below the
55595
+ * sticky header and the group label the row lives under — so nothing of that
55596
+ * room has to be restated as a number by whoever asks.
55594
55597
  * @param {"start"|"end"|number|{id: string, offset?: number}} [props.scrolled]
55595
55598
  * The same, but held: the list goes back there every time this changes, even
55596
55599
  * after the user has scrolled — the caller owns where the list is (see
@@ -55604,7 +55607,8 @@ const ListFirstResolver = props => {
55604
55607
  * user reaches for the list.
55605
55608
  * @param {(scrolled: {id: string, index: number, offset: number}) => void} [props.onScrolledChange]
55606
55609
  * Where the list is, as the user scrolls: the row at the top of the view and
55607
- * how far below the top of the view it starts. Keep it to come back to it
55610
+ * how far below the place a row lands on its own (see `defaultScrolled`) it
55611
+ * starts. Keep it to come back to it
55608
55612
  * later through `scrolled`/`defaultScrolled` — an index would not do, since
55609
55613
  * rows get inserted while a list is being read.
55610
55614
  * @param {"self"|"parent"|"document"|Element|{current: Element}} [props.scroller="self"]
@@ -56215,7 +56219,7 @@ const useListScrollSync = ({
56215
56219
  }
56216
56220
  const viewportRect = getScrollerViewportRect(scrollerEl);
56217
56221
  const rowRect = rowEl.getBoundingClientRect();
56218
- const offsetWanted = resolveOpenOffset(openAt.offset || 0, horizontal ? viewportRect.width : viewportRect.height, horizontal ? rowRect.width : rowRect.height);
56222
+ const offsetWanted = resolveOpenOffset(getRowScrollInset(scrollerEl, rowEl, horizontal) + (openAt.offset || 0), horizontal ? viewportRect.width : viewportRect.height, horizontal ? rowRect.width : rowRect.height);
56219
56223
  const offsetNow = horizontal ? rowRect.left - viewportRect.left : rowRect.top - viewportRect.top;
56220
56224
  const delta = offsetNow - offsetWanted;
56221
56225
  if (delta > -0.5 && delta < 0.5) {
@@ -56292,10 +56296,14 @@ const useListScrollSync = ({
56292
56296
  return;
56293
56297
  }
56294
56298
  positionRef.current = position;
56295
- onScrolledChangeRef.current?.({
56299
+ if (!onScrolledChangeRef.current) {
56300
+ return;
56301
+ }
56302
+ const rowEl = findRowElement(getListEl(), position.id);
56303
+ onScrolledChangeRef.current({
56296
56304
  id: position.id,
56297
56305
  index: position.index,
56298
- offset: position.offset
56306
+ offset: position.offset - getRowScrollInset(getScroller(), rowEl, horizontal)
56299
56307
  });
56300
56308
  };
56301
56309
 
@@ -56760,6 +56768,38 @@ const resolveOpenOffset = (offset, viewportSize, rowSize) => {
56760
56768
  return offset;
56761
56769
  };
56762
56770
 
56771
+ // The room a row must be given at the top (or left) of the view: the
56772
+ // scroller's own scroll-padding — where a fixed bar publishes the space it
56773
+ // takes — plus the row's scroll-margin, where the list puts its sticky header
56774
+ // and the height of the group label it lives under. `scrollIntoView()` on a row
56775
+ // lands past both; a position given as `{id, offset}` means the same place, so
56776
+ // `offset` is the caller's own few pixels and not a number restating what the
56777
+ // CSS already measures.
56778
+ const getRowScrollInset = (scrollerEl, rowEl, horizontal) => {
56779
+ if (!rowEl) {
56780
+ return 0;
56781
+ }
56782
+ const viewportRect = getScrollerViewportRect(scrollerEl);
56783
+ const viewportSize = horizontal ? viewportRect.width : viewportRect.height;
56784
+ const scrollerStyle = window.getComputedStyle(scrollerEl);
56785
+ const rowStyle = window.getComputedStyle(rowEl);
56786
+ const scrollPadding = resolveScrollInset(horizontal ? scrollerStyle.scrollPaddingLeft : scrollerStyle.scrollPaddingTop, viewportSize);
56787
+ const scrollMargin = resolveScrollInset(horizontal ? rowStyle.scrollMarginLeft : rowStyle.scrollMarginTop, viewportSize);
56788
+ return scrollPadding + scrollMargin;
56789
+ };
56790
+ // scroll-padding is a length, a percentage of the scrollport, or "auto" (the
56791
+ // browser decides, which for placing a row means nothing).
56792
+ const resolveScrollInset = (value, viewportSize) => {
56793
+ const number = parseFloat(value);
56794
+ if (!number) {
56795
+ return 0;
56796
+ }
56797
+ if (value.endsWith("%")) {
56798
+ return number * viewportSize / 100;
56799
+ }
56800
+ return number;
56801
+ };
56802
+
56763
56803
  // The row with that id, IN THIS LIST. Not document.getElementById: an id is
56764
56804
  // only ever unique within a list — two lists on the same page can be showing
56765
56805
  // the same collection — and a list acting on a row that belongs to another one
@@ -57893,6 +57933,7 @@ const VISIBILITY_HIDDEN_STYLE = {
57893
57933
  * count?: number,
57894
57934
  * groupBy?: (item: any, index: number) => any,
57895
57935
  * renderGroupLabel?: (item: any, index: number) => import("ignore:preact").ComponentChildren,
57936
+ * groupLabelProps?: (item: any, index: number) => object,
57896
57937
  * pageSize?: number,
57897
57938
  * memoryBudget?: number,
57898
57939
  * renderSkeleton?: false | ((index: number) => import("ignore:preact").ComponentChildren),
@@ -57910,6 +57951,12 @@ const VISIBILITY_HIDDEN_STYLE = {
57910
57951
  * the only way a list that discovers its rows page by page can have any.
57911
57952
  * @param {(item: any, index: number) => any} [props.renderGroupLabel]
57912
57953
  * The label of the group a row opens, given that row.
57954
+ * @param {(item: any, index: number) => object} [props.groupLabelProps]
57955
+ * The props the label of the group a row opens carries — `class`,
57956
+ * `data-*`, anything a `<span>` takes. For a label that says something about
57957
+ * its group (a day behind us, today, one ahead) rather than just naming it:
57958
+ * the state then sits on the element the CSS styles, instead of being read
57959
+ * back from a child.
57913
57960
  * @param {number} [props.pageSize]
57914
57961
  * How many rows to ask for at a time. A turn of the wheel opens a hole three
57915
57962
  * rows wide; asking for exactly that would ask again at the next turn.
@@ -57937,6 +57984,7 @@ const ListItems = ({
57937
57984
  memoryBudget,
57938
57985
  groupBy,
57939
57986
  renderGroupLabel,
57987
+ groupLabelProps,
57940
57988
  renderSkeleton,
57941
57989
  renderError
57942
57990
  }) => {
@@ -58076,6 +58124,7 @@ const ListItems = ({
58076
58124
  }
58077
58125
  rows.push(jsx(ListItemGroup, {
58078
58126
  label: group.label,
58127
+ labelProps: group.labelProps,
58079
58128
  children: group.children
58080
58129
  }, `${ownerId}_group_${group.key}`));
58081
58130
  group = null;
@@ -58092,6 +58141,7 @@ const ListItems = ({
58092
58141
  group = {
58093
58142
  key: groupKey,
58094
58143
  label: renderGroupLabel ? renderGroupLabel(item, rowIndex) : groupKey,
58144
+ labelProps: groupLabelProps ? groupLabelProps(item, rowIndex) : undefined,
58095
58145
  children: []
58096
58146
  };
58097
58147
  }
@@ -58532,6 +58582,7 @@ const useItemStore = ({
58532
58582
  */
58533
58583
  const ListItemGroup = ({
58534
58584
  label,
58585
+ labelProps,
58535
58586
  hiddenWhileEmpty,
58536
58587
  children,
58537
58588
  ...rest
@@ -58557,6 +58608,11 @@ const ListItemGroup = ({
58557
58608
  groupEl.style.setProperty("--list-group-label-height", `${rect.height}px`);
58558
58609
  groupEl.style.setProperty("--list-group-label-width", `${rect.width}px`);
58559
58610
  }, []);
58611
+ const {
58612
+ className: labelClassName,
58613
+ class: labelClass,
58614
+ ...labelRest
58615
+ } = labelProps || {};
58560
58616
  return jsxs(ListItem, {
58561
58617
  ...rest,
58562
58618
  ref: groupRef,
@@ -58564,9 +58620,10 @@ const ListItemGroup = ({
58564
58620
  role: "presentation",
58565
58621
  "data-hidden-while-empty": hiddenWhileEmpty ? "" : undefined,
58566
58622
  children: [jsx("span", {
58623
+ ...labelRest,
58567
58624
  ref: labelRef,
58568
58625
  id: groupId,
58569
- className: "navi_list_item_group_label",
58626
+ className: withPropsClassName("navi_list_item_group_label", labelClassName || labelClass),
58570
58627
  role: "presentation",
58571
58628
  "aria-hidden": labelHidden ? "true" : undefined,
58572
58629
  inert: labelHidden ? true : undefined