@jsenv/navi 0.29.39 → 0.29.41

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.
@@ -52142,6 +52142,82 @@ const PickerNaviMinute = props => {
52142
52142
  });
52143
52143
  };
52144
52144
 
52145
+ /*
52146
+ * Who is scrolling right now, told to the DOM: while an element scrolls it
52147
+ * carries `navi-scrolling`, so anyone concerned reacts in CSS, without a
52148
+ * listener and without a subscription.
52149
+ *
52150
+ * The one this exists for is hover. A scroll moves the content under a
52151
+ * motionless pointer, so the browser dispatches mouseenter/mouseleave for every
52152
+ * element crossing the cursor — a dozen per wheel tick. Those hovers are noise:
52153
+ * the user asked to scroll, not to hover. Anything doing real work on hover (a
52154
+ * map highlight, a preview, a prefetch) then pays for that noise on the main
52155
+ * thread, exactly while a scroll animation is running. A rule matching
52156
+ * `[navi-scrolling] *` takes the rows out of hit-testing and the browser
52157
+ * suppresses it all, at no cost per element.
52158
+ *
52159
+ * One capturing listener on the document sees them all: "scroll" does not
52160
+ * bubble, but it does propagate in the capture phase.
52161
+ */
52162
+
52163
+
52164
+ // A scroller is still moving after its last "scroll" event (momentum, smooth
52165
+ // scrolling, the gap between two wheel ticks), so "still scrolling" is a
52166
+ // timeout: long enough to bridge that gap, short enough that hover comes back
52167
+ // as soon as the user stops.
52168
+ const SCROLL_IDLE_DELAY = 120;
52169
+
52170
+ /**
52171
+ * True while anything in the page is being scrolled.
52172
+ */
52173
+ const scrollActivitySignal = signal(false);
52174
+
52175
+ /**
52176
+ * @param {Element} [element] The scroller to ask about; any scroller when omitted.
52177
+ * @returns {boolean}
52178
+ */
52179
+ const isScrolling = (element) => {
52180
+ if (element === undefined) {
52181
+ return scrollActivitySignal.peek();
52182
+ }
52183
+ return asScroller(element).hasAttribute("navi-scrolling");
52184
+ };
52185
+
52186
+ // The page scroll is dispatched on the document; the element carrying the
52187
+ // attribute is the one CSS can reach, document.scrollingElement.
52188
+ const asScroller = (eventTargetOrElement) => {
52189
+ if (eventTargetOrElement === document || eventTargetOrElement === window) {
52190
+ return document.scrollingElement;
52191
+ }
52192
+ return eventTargetOrElement;
52193
+ };
52194
+
52195
+ const idleTimeoutMap = new Map();
52196
+ document.addEventListener(
52197
+ "scroll",
52198
+ (e) => {
52199
+ const scroller = asScroller(e.target);
52200
+ const idleTimeout = idleTimeoutMap.get(scroller);
52201
+ if (idleTimeout !== undefined) {
52202
+ clearTimeout(idleTimeout);
52203
+ } else {
52204
+ scroller.setAttribute("navi-scrolling", "");
52205
+ scrollActivitySignal.value = true;
52206
+ }
52207
+ idleTimeoutMap.set(
52208
+ scroller,
52209
+ setTimeout(() => {
52210
+ idleTimeoutMap.delete(scroller);
52211
+ scroller.removeAttribute("navi-scrolling");
52212
+ if (idleTimeoutMap.size === 0) {
52213
+ scrollActivitySignal.value = false;
52214
+ }
52215
+ }, SCROLL_IDLE_DELAY),
52216
+ );
52217
+ },
52218
+ { capture: true, passive: true },
52219
+ );
52220
+
52145
52221
  const LoadingDotsSvg = () => {
52146
52222
  return jsxs("svg", {
52147
52223
  viewBox: "0 0 200 200",
@@ -53764,6 +53840,17 @@ const css$v = /* css */`
53764
53840
  }
53765
53841
  }
53766
53842
 
53843
+ /* A scroll moves the rows under a motionless pointer: the browser then
53844
+ fires mouseenter/mouseleave for every row crossing the cursor, and
53845
+ whoever reacts to hover (a highlight elsewhere, a prefetch, a map) pays
53846
+ for those while the scroll animation runs. Out of hit-testing, the
53847
+ browser suppresses them all — see utils/scroll_activity.js for who
53848
+ writes navi-scrolling. The scroller itself keeps its own hit-testing, so
53849
+ the wheel and the scrollbar go on reaching it. */
53850
+ &:not([navi-hover-while-scrolling]) .navi_list:is([navi-scrolling] *) {
53851
+ pointer-events: none;
53852
+ }
53853
+
53767
53854
  /* Scrolling with the page means sticking to the viewport, and a FixedBar
53768
53855
  is in front of that viewport: without the offset a sticky label lands
53769
53856
  behind the bar. The bar publishes the room it takes (see
@@ -54239,6 +54326,7 @@ const ListUI = props => {
54239
54326
  defaultScrolled = "start",
54240
54327
  onScrolledChange,
54241
54328
  scroller = "self",
54329
+ hoverWhileScrolling = false,
54242
54330
  lockSize,
54243
54331
  columns,
54244
54332
  searchText,
@@ -54432,6 +54520,7 @@ const ListUI = props => {
54432
54520
  popover: popover,
54433
54521
  "data-horizontal": horizontal ? "" : undefined,
54434
54522
  "data-scroller": getScrollerAttribute(scroller),
54523
+ "navi-hover-while-scrolling": hoverWhileScrolling ? "" : undefined,
54435
54524
  "data-expand-x": expandX || expand ? "" : undefined,
54436
54525
  "data-expand-y": expandY || expand ? "" : undefined,
54437
54526
  expandX: expandX,
@@ -54515,6 +54604,7 @@ const ListFirstResolver = props => {
54515
54604
  * defaultScrolled?: "start" | "end" | number | {id: string, offset?: number},
54516
54605
  * onScrolledChange?: (scrolled: {id: string, index: number, offset: number}) => void,
54517
54606
  * scroller?: "self" | "parent" | "document" | Element | {current: Element},
54607
+ * hoverWhileScrolling?: boolean,
54518
54608
  * fallback?: import("ignore:preact").ComponentChildren,
54519
54609
  * searchFallback?: import("ignore:preact").ComponentChildren,
54520
54610
  * searchText?: string,
@@ -54609,6 +54699,17 @@ const ListFirstResolver = props => {
54609
54699
  * scroll once it fills up is picked up then. When that is still not the box
54610
54700
  * you mean, say so: `"document"`, or the element itself (a ref works) —
54611
54701
  * nothing is guessed then.
54702
+ * @param {boolean} [props.hoverWhileScrolling=false]
54703
+ * Whether the rows still answer the pointer while the scroller they live in
54704
+ * is moving. They do not by default: a scroll slides the rows under a
54705
+ * motionless pointer, so the browser reports a hover on each of them, and
54706
+ * the user asked to scroll, not to hover. The cost of taking them at face
54707
+ * value is paid by whatever hover triggers — a highlight elsewhere in the
54708
+ * tree, a prefetch, a map — at the worst moment, mid-scroll.
54709
+ *
54710
+ * Pass `true` for a list whose rows must stay live under the pointer while
54711
+ * it scrolls. The trade of the default is the mirror one: right after a
54712
+ * scroll, the row under the pointer lights up only once the pointer moves.
54612
54713
  * @param {number} [props.maxLength]
54613
54714
  * How many items a `selectable multiple` list accepts — the same word, and
54614
54715
  * the same behaviour, as `maxLength` on a text field: a rule the list is
@@ -70485,5 +70586,5 @@ const UserSvg = () => jsx("svg", {
70485
70586
  })
70486
70587
  });
70487
70588
 
70488
- export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Binder, Box, Button, ButtonCopyToClipboard, Caption, CardLayout, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, DaySpin, Details, Dialog, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, EyeClosedSvg, EyeSvg, Field, FixedBar, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, ListItems, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, NumberSpin, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RouteTravel, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, Select, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, Slide, SlideContainer, Spin, SpinGroup, StarSvg, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Textarea, TextareaCharCount, Thead, Time, TimeRangeSpin, TimeSpin, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, coarsePointerSignal, compareTwoJsValues, createAction, createAvailableConstraint, createI18n, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, defineInteractionDetector, defineNaviConfirmPopupOptions, detectHorizontalOverflow, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isRowSelected, isToday, languagesSignal, localStorageSignal, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, setBaseUrl, setPreferredLanguage, setSupportedLanguages, setUrlTargetOptions, setupRoutes, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, triggerNaviCommand, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutRequestClose, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useInputGroup, useKeyboardShortcuts, useNavState, useOrderedColumns, usePopupMode, useRouteStatus, useRunOnMount, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useSlideValue, useStateArray, useTitleLevel, useUrlSearchParam, useUrlTargetId, valueInLocalStorage, windowWidthSignal };
70589
+ export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Binder, Box, Button, ButtonCopyToClipboard, Caption, CardLayout, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, DaySpin, Details, Dialog, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, EyeClosedSvg, EyeSvg, Field, FixedBar, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, ListItems, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, NumberSpin, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RouteTravel, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, Select, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, Slide, SlideContainer, Spin, SpinGroup, StarSvg, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Textarea, TextareaCharCount, Thead, Time, TimeRangeSpin, TimeSpin, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, coarsePointerSignal, compareTwoJsValues, createAction, createAvailableConstraint, createI18n, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, defineInteractionDetector, defineNaviConfirmPopupOptions, detectHorizontalOverflow, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isRowSelected, isScrolling, isToday, languagesSignal, localStorageSignal, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, scrollActivitySignal, setBaseUrl, setPreferredLanguage, setSupportedLanguages, setUrlTargetOptions, setupRoutes, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, triggerNaviCommand, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutRequestClose, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useInputGroup, useKeyboardShortcuts, useNavState, useOrderedColumns, usePopupMode, useRouteStatus, useRunOnMount, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useSlideValue, useStateArray, useTitleLevel, useUrlSearchParam, useUrlTargetId, valueInLocalStorage, windowWidthSignal };
70489
70590
  //# sourceMappingURL=jsenv_navi.js.map