@jsenv/navi 0.29.6 → 0.29.7
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 +71 -21
- package/dist/jsenv_navi.js.map +7 -6
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -7,7 +7,7 @@ import { createContext, isValidElement, h, Fragment, toChildArray, render, clone
|
|
|
7
7
|
import { useContext, useLayoutEffect, useRef, useEffect, useCallback, useState, useMemo, useId, useErrorBoundary } from "preact/hooks";
|
|
8
8
|
import { jsx, jsxs, Fragment as Fragment$1 } from "preact/jsx-runtime";
|
|
9
9
|
import { computed, signal, effect, batch, useSignal } from "@preact/signals";
|
|
10
|
-
import { createPubSub, normalizeStyle, mergeOneStyle, getPositionedParent, findEvent, dispatchInternalCustomEvent, mergeTwoStyles, normalizeStyles, resolveCSSSize, measureLongestVisualLineWidth, hasCSSSizeUnit, resolveOklchLightness, contrastColor, createIterableWeakSet, dispatchCustomEvent, getElementSignature, createValueEffect, getVisuallyVisibleInfo, getFirstVisuallyVisibleAncestor, findFocusDelegateTarget, findFocusable, allowWheelThrough, dispatchPublicCustomEvent, resolveCSSColor, ELEMENT_SIZE_CHANGE, findSelfOrAncestorFixedPosition, visibleRectEffect, pickPositionRelativeTo, getBorderSizes, getPaddingSizes, applyNewPosition, createEventGroupLogger, closestOpenableAncestor, isAncestorOpen, observeAncestorOpenState, getAncestorOpenType, getKeyboardEventDefaultAction, chainEvent, activeElementSignal, parsePositionArea, snapToPixel, trapFocusInside, trapScrollInside, onAncestorReopen, createGroupTransitionController, getBorderRadius, preventIntermediateScrollbar, createOpacityTransition, findBefore, findAfter, initFocusGroup, elementIsFocusable, scrollIntoViewScoped, measureWidestChildRow, performTabNavigation, dragAfterThreshold,
|
|
10
|
+
import { createPubSub, normalizeStyle, mergeOneStyle, getPositionedParent, findEvent, dispatchInternalCustomEvent, mergeTwoStyles, normalizeStyles, resolveCSSSize, measureLongestVisualLineWidth, hasCSSSizeUnit, resolveOklchLightness, contrastColor, createIterableWeakSet, dispatchCustomEvent, getElementSignature, createValueEffect, getVisuallyVisibleInfo, getFirstVisuallyVisibleAncestor, findFocusDelegateTarget, findFocusable, allowWheelThrough, dispatchPublicCustomEvent, resolveCSSColor, ELEMENT_SIZE_CHANGE, findSelfOrAncestorFixedPosition, visibleRectEffect, pickPositionRelativeTo, getBorderSizes, getPaddingSizes, applyNewPosition, createEventGroupLogger, closestOpenableAncestor, isAncestorOpen, observeAncestorOpenState, getAncestorOpenType, getKeyboardEventDefaultAction, chainEvent, activeElementSignal, parsePositionArea, snapToPixel, trapFocusInside, trapScrollInside, onAncestorReopen, createGroupTransitionController, getBorderRadius, preventIntermediateScrollbar, createOpacityTransition, findBefore, findAfter, initFocusGroup, elementIsFocusable, scrollIntoViewScoped, getScrollContainer, canScroll, measureWidestChildRow, performTabNavigation, dragAfterThreshold, stickyAsRelativeCoords, createDragToMoveGestureController, getDropTargetInfo, setStyles, useActiveElement, stringifyStyle as stringifyStyle$1 } from "@jsenv/dom";
|
|
11
11
|
export { contrastColor, startDragToReorder } from "@jsenv/dom";
|
|
12
12
|
import { createValidity, parseDuration, durationContainsNaN, compareTwoDurations, durationToSeconds, durationToISOString } from "@jsenv/validity";
|
|
13
13
|
export { compareTwoDurations, durationContainsNaN, durationToHours, durationToISOString, durationToMinutes, durationToNumber, durationToSeconds, durationToString, parseDuration } from "@jsenv/validity";
|
|
@@ -46952,10 +46952,10 @@ const css$u = /* css */`
|
|
|
46952
46952
|
scrollbar-width: inherit;
|
|
46953
46953
|
}
|
|
46954
46954
|
|
|
46955
|
-
/* scroller
|
|
46956
|
-
does. Its own scroll box must then be transparent to
|
|
46957
|
-
it would cap the list at a height of its own and
|
|
46958
|
-
scroll inside the page's. */
|
|
46955
|
+
/* Any scroller other than "self": the list does not scroll, something
|
|
46956
|
+
around it does. Its own scroll box must then be transparent to
|
|
46957
|
+
layout — otherwise it would cap the list at a height of its own and
|
|
46958
|
+
start a second, nested scroll inside the page's. */
|
|
46959
46959
|
&[data-scroller="parent"] {
|
|
46960
46960
|
max-height: none;
|
|
46961
46961
|
overflow: visible;
|
|
@@ -47567,7 +47567,7 @@ const ListUI = props => {
|
|
|
47567
47567
|
baseClassName: "navi_list_container",
|
|
47568
47568
|
popover: popover,
|
|
47569
47569
|
"data-horizontal": horizontal ? "" : undefined,
|
|
47570
|
-
"data-scroller": scroller === "
|
|
47570
|
+
"data-scroller": scroller === "self" ? undefined : "parent",
|
|
47571
47571
|
"data-expand-x": expandX || expand ? "" : undefined,
|
|
47572
47572
|
"data-expand-y": expandY || expand ? "" : undefined,
|
|
47573
47573
|
expandX: expandX,
|
|
@@ -47644,7 +47644,7 @@ const ListFirstResolver = props => {
|
|
|
47644
47644
|
* scrolled?: "start" | "end" | number | {id: string, offset?: number},
|
|
47645
47645
|
* defaultScrolled?: "start" | "end" | number | {id: string, offset?: number},
|
|
47646
47646
|
* onScrolledChange?: (scrolled: {id: string, index: number, offset: number}) => void,
|
|
47647
|
-
* scroller?: "self" | "parent",
|
|
47647
|
+
* scroller?: "self" | "parent" | "document" | Element | {current: Element},
|
|
47648
47648
|
* fallback?: import("ignore:preact").ComponentChildren,
|
|
47649
47649
|
* searchFallback?: import("ignore:preact").ComponentChildren,
|
|
47650
47650
|
* searchText?: string,
|
|
@@ -47705,11 +47705,20 @@ const ListFirstResolver = props => {
|
|
|
47705
47705
|
* how far below the top of the view it starts. Keep it to come back to it
|
|
47706
47706
|
* later through `scrolled`/`defaultScrolled` — an index would not do, since
|
|
47707
47707
|
* rows get inserted while a list is being read.
|
|
47708
|
-
* @param {"self"|"parent"} [props.scroller="self"]
|
|
47708
|
+
* @param {"self"|"parent"|"document"|Element|{current: Element}} [props.scroller="self"]
|
|
47709
47709
|
* Which box scrolls. `"self"` gives the list a scroll box of its own;
|
|
47710
47710
|
* `"parent"` makes it virtualize against the scrollable ancestor it lives in
|
|
47711
47711
|
* (the page, a panel) — no scroll box nested inside another one, no height
|
|
47712
47712
|
* to compute.
|
|
47713
|
+
*
|
|
47714
|
+
* `"parent"` finds that ancestor by measuring: the nearest one whose content
|
|
47715
|
+
* actually overflows it, the page if none does. Declaring `overflow` is not
|
|
47716
|
+
* enough to be picked (a box with `overflow-x: auto` that grows with its
|
|
47717
|
+
* content computes `overflow-y: auto` without ever scrolling), and the
|
|
47718
|
+
* answer is taken again as the geometry moves, so an ancestor that starts to
|
|
47719
|
+
* scroll once it fills up is picked up then. When that is still not the box
|
|
47720
|
+
* you mean, say so: `"document"`, or the element itself (a ref works) —
|
|
47721
|
+
* nothing is guessed then.
|
|
47713
47722
|
*/
|
|
47714
47723
|
const List = createComponentResolver([ListFirstResolver, ListSelectableResolver, ListUI]);
|
|
47715
47724
|
const ListContent = ({
|
|
@@ -47803,6 +47812,20 @@ const useListScrollSync = ({
|
|
|
47803
47812
|
const virtualItemSizeSignal = useVirtualItemSizeSignal(ref, virtualItemSize, horizontal);
|
|
47804
47813
|
const getScroller = () => getScrollerEl(ref.current, scroller, horizontal);
|
|
47805
47814
|
const getListEl = () => ref.current.querySelector(".navi_list");
|
|
47815
|
+
// Which box scrolls is measured (see getScrollerEl), so the answer holds
|
|
47816
|
+
// only for the geometry it was taken on: an ancestor that is bounded but
|
|
47817
|
+
// still waiting for its first rows looks like it will never scroll. It is
|
|
47818
|
+
// taken again on every commit and every resize; the state exists so the
|
|
47819
|
+
// effects below reattach their listeners to whatever it lands on.
|
|
47820
|
+
const [scrollerElResolved, setScrollerElResolved] = useState(null);
|
|
47821
|
+
const resolveScroller = () => {
|
|
47822
|
+
if (!ref.current) {
|
|
47823
|
+
return;
|
|
47824
|
+
}
|
|
47825
|
+
const scrollerElNow = getScroller();
|
|
47826
|
+
setScrollerElResolved(current => current === scrollerElNow ? current : scrollerElNow);
|
|
47827
|
+
};
|
|
47828
|
+
useLayoutEffect(resolveScroller);
|
|
47806
47829
|
|
|
47807
47830
|
// The row the scroll holds onto across a change of geometry, and where it
|
|
47808
47831
|
// sat when that change was decided. Captured at the two moments the list
|
|
@@ -48310,7 +48333,7 @@ const useListScrollSync = ({
|
|
|
48310
48333
|
scrollerEl.removeEventListener(type, takeOver);
|
|
48311
48334
|
}
|
|
48312
48335
|
};
|
|
48313
|
-
}, [scrolledWanted,
|
|
48336
|
+
}, [scrolledWanted, scrollerElResolved]);
|
|
48314
48337
|
|
|
48315
48338
|
// Where the list is, said the way it can be given back to it: the row at the
|
|
48316
48339
|
// top of what is on screen, and how far above the fold it sits. An index
|
|
@@ -48353,6 +48376,11 @@ const useListScrollSync = ({
|
|
|
48353
48376
|
const scrollerEl = getScroller();
|
|
48354
48377
|
const listEl = getListEl();
|
|
48355
48378
|
const observer = new ResizeObserver(entries => {
|
|
48379
|
+
// A box that grows may be a box that starts to scroll — the scroller is
|
|
48380
|
+
// resolved again before anything is done about the resize, so that a
|
|
48381
|
+
// list which has outgrown a bounded ancestor stops holding on to the
|
|
48382
|
+
// page.
|
|
48383
|
+
resolveScroller();
|
|
48356
48384
|
// Two things resize here, and they call for opposite answers. The LIST
|
|
48357
48385
|
// growing is its own content settling: only a list holding itself
|
|
48358
48386
|
// somewhere cares (the end it aims at has moved), and a list the user is
|
|
@@ -48394,7 +48422,7 @@ const useListScrollSync = ({
|
|
|
48394
48422
|
return () => {
|
|
48395
48423
|
observer.disconnect();
|
|
48396
48424
|
};
|
|
48397
|
-
}, [
|
|
48425
|
+
}, [scrollerElResolved]);
|
|
48398
48426
|
|
|
48399
48427
|
// Inserting rows above what the user is looking at must not move it by a
|
|
48400
48428
|
// single pixel. The browser will not do it for us — overflow-anchor gives up
|
|
@@ -48547,7 +48575,7 @@ const useListScrollSync = ({
|
|
|
48547
48575
|
return () => {
|
|
48548
48576
|
scrollEventTarget.removeEventListener("scroll", onScroll);
|
|
48549
48577
|
};
|
|
48550
|
-
}, [renderBudget,
|
|
48578
|
+
}, [renderBudget, scrollerElResolved]);
|
|
48551
48579
|
holdWindow();
|
|
48552
48580
|
return {
|
|
48553
48581
|
virtualItemSizeSignal,
|
|
@@ -48576,21 +48604,43 @@ const getScrollerViewportRect = scrollerEl => {
|
|
|
48576
48604
|
return scrollerEl.getBoundingClientRect();
|
|
48577
48605
|
};
|
|
48578
48606
|
// scroller="parent": the list virtualizes against the scroll box it lives in
|
|
48579
|
-
// instead of one of its own.
|
|
48607
|
+
// instead of one of its own. Which box that is can only be measured, and a
|
|
48608
|
+
// measurement holds for the geometry it was taken on — see resolveScroller in
|
|
48609
|
+
// useListScrollSync, which takes it again whenever the geometry moves.
|
|
48580
48610
|
const getScrollerEl = (listContainerEl, scroller, horizontal) => {
|
|
48611
|
+
if (scroller === "document") {
|
|
48612
|
+
return document.scrollingElement;
|
|
48613
|
+
}
|
|
48614
|
+
if (scroller && typeof scroller === "object") {
|
|
48615
|
+
// The caller names the scroller: an element, or a ref holding one.
|
|
48616
|
+
// Detection is a heuristic forever; being told is not.
|
|
48617
|
+
const el = scroller.nodeType === 1 ? scroller : scroller.current;
|
|
48618
|
+
return el || document.scrollingElement;
|
|
48619
|
+
}
|
|
48581
48620
|
if (scroller !== "parent") {
|
|
48582
48621
|
return listContainerEl.querySelector(`.navi_list_scroll_container`);
|
|
48583
48622
|
}
|
|
48584
|
-
|
|
48585
|
-
|
|
48586
|
-
|
|
48587
|
-
|
|
48588
|
-
|
|
48589
|
-
|
|
48623
|
+
const axis = horizontal ? "x" : "y";
|
|
48624
|
+
let element = listContainerEl;
|
|
48625
|
+
let nearestScrollContainer = null;
|
|
48626
|
+
while (true) {
|
|
48627
|
+
const scrollContainer = getScrollContainer(element);
|
|
48628
|
+
if (!scrollContainer || scrollContainer === element || scrollContainer === document.documentElement || scrollContainer === document.scrollingElement) {
|
|
48629
|
+
break;
|
|
48630
|
+
}
|
|
48631
|
+
// A scroll container that declares an overflow it does not have is not the
|
|
48632
|
+
// one to listen to (see canScroll): the box that scrolls is above it.
|
|
48633
|
+
if (canScroll(scrollContainer, axis)) {
|
|
48634
|
+
return scrollContainer;
|
|
48590
48635
|
}
|
|
48591
|
-
|
|
48636
|
+
nearestScrollContainer = nearestScrollContainer || scrollContainer;
|
|
48637
|
+
element = scrollContainer;
|
|
48592
48638
|
}
|
|
48593
|
-
|
|
48639
|
+
// Nothing scrolls yet. A bounded box waiting for its first rows is the one
|
|
48640
|
+
// that will, and until it does, holding it changes nothing — where holding
|
|
48641
|
+
// the page instead would drag the whole document to the end of a list that
|
|
48642
|
+
// is not even scrollable.
|
|
48643
|
+
return nearestScrollContainer || document.scrollingElement;
|
|
48594
48644
|
};
|
|
48595
48645
|
// A row must be worth looking at once put back where it was. The offset comes
|
|
48596
48646
|
// from wherever the position was taken — another screen, another window size,
|
|
@@ -52581,7 +52631,7 @@ const renderDayDefault = (day, {
|
|
|
52581
52631
|
// saying "you may wrap" there would undo the truncation instead.
|
|
52582
52632
|
,
|
|
52583
52633
|
|
|
52584
|
-
noWrap: maxLines === 1 ? undefined : false,
|
|
52634
|
+
noWrap: maxLines === 1 || maxLines === "1" ? undefined : false,
|
|
52585
52635
|
maxLines: maxLines,
|
|
52586
52636
|
children: day
|
|
52587
52637
|
});
|