@jsenv/navi 0.29.238 → 0.29.299

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.
@@ -20677,16 +20677,16 @@ const useComposeElementRef = (syncElement, externalRef) => {
20677
20677
  const cleanupRef = useRef(null);
20678
20678
  const elRef = useRef(null);
20679
20679
  const prevSyncElementRef = useRef(undefined);
20680
- const stableRef = useRef(null);
20680
+ const holderRef = useRef(null);
20681
20681
  const externalRefRef = useRef(externalRef);
20682
20682
  const syncElementRef = useRef(syncElement);
20683
20683
  syncElementRef.current = syncElement;
20684
- // Detect external ref identity change between renders. The refCallback is
20685
- // stable across renders, so when the parent passes a new ref object (or
20686
- // switches from null to a ref), Preact does NOT re-fire the callback while
20687
- // the DOM element is unchanged. We must manually clear the old ref and
20688
- // populate the new one with the current element to avoid leaving the new
20689
- // ref's `.current` stuck at `null`.
20684
+ // Detect external ref identity change between renders. The refCallback keeps
20685
+ // its identity once it holds the element, so when the parent passes a new
20686
+ // ref object (or switches from null to a ref), Preact does NOT re-fire the
20687
+ // callback while the DOM element is unchanged. We must manually clear the
20688
+ // old ref and populate the new one with the current element to avoid
20689
+ // leaving the new ref's `.current` stuck at `null`.
20690
20690
  const prevExternalRefRef = useRef(externalRef);
20691
20691
  if (prevExternalRefRef.current !== externalRef) {
20692
20692
  const previous = prevExternalRefRef.current;
@@ -20704,12 +20704,12 @@ const useComposeElementRef = (syncElement, externalRef) => {
20704
20704
  }
20705
20705
  externalRefRef.current = externalRef;
20706
20706
 
20707
- if (!stableRef.current) {
20708
- // Created once, like the ref callback that calls it, and reading the sync
20709
- // function through a ref for that reason: the element can be replaced long
20710
- // after the first render — a tag that changes, a box hidden then shown —
20711
- // and what the new element gets must be the current render's sync, not
20712
- // the one the first render closed over.
20707
+ if (!holderRef.current) {
20708
+ // Created once, and reading the sync function through a ref for that
20709
+ // reason: the element can be replaced long after the first render — a tag
20710
+ // that changes, a box hidden then shown — and what the new element gets
20711
+ // must be the current render's sync, not the one the first render closed
20712
+ // over.
20713
20713
  const runSync = (el) => {
20714
20714
  if (cleanupRef.current) {
20715
20715
  cleanupRef.current();
@@ -20722,6 +20722,18 @@ const useComposeElementRef = (syncElement, externalRef) => {
20722
20722
  cleanupRef.current = cleanup;
20723
20723
  }
20724
20724
  };
20725
+ holderRef.current = { runSync, refCallback: null };
20726
+ }
20727
+ const { runSync } = holderRef.current;
20728
+
20729
+ // A new callback on every render until Preact has handed it the element,
20730
+ // the same one afterwards. Preact re-applies a ref only when its identity
20731
+ // changes, and a diff that throws with no error boundary above skips
20732
+ // commitRoot: its refs are dropped while its DOM stays and is adopted as
20733
+ // mounted by the next render. A callback that never fired would then never
20734
+ // fire, and a Box would stay raw HTML for the life of the document. Free on
20735
+ // a healthy mount, where the first callback is the only one ever created.
20736
+ if (!elRef.current) {
20725
20737
  const refCallback = (el) => {
20726
20738
  elRef.current = el;
20727
20739
  // Keep .current in sync immediately so useEffect callbacks that read
@@ -20745,9 +20757,9 @@ const useComposeElementRef = (syncElement, externalRef) => {
20745
20757
  prevSyncElementRef.current = undefined;
20746
20758
  }
20747
20759
  };
20748
- stableRef.current = { refCallback, runSync };
20760
+ holderRef.current.refCallback = refCallback;
20749
20761
  }
20750
- const { refCallback, runSync } = stableRef.current;
20762
+ const { refCallback } = holderRef.current;
20751
20763
 
20752
20764
  // If element already mounted, re-sync when syncElement reference changed.
20753
20765
  if (elRef.current && syncElement !== prevSyncElementRef.current) {
@@ -32258,6 +32270,17 @@ registerNaviCommand("--navi-send", (source, event, { requester }) => {
32258
32270
  return sent;
32259
32271
  }
32260
32272
  if (isRunning) {
32273
+ // An optimistic control takes its send as done on its own word: what
32274
+ // follows it runs at once and the run goes on detached — still
32275
+ // watched, still able to fail, its error callout then drawn on what
32276
+ // surrounds the closed surface it was typed in (see openCallout's
32277
+ // anchor resolution in callout.js).
32278
+ const sendController = (findControlHost(target) || target)
32279
+ .__uiStateController__;
32280
+ if (sendController?.optimistic) {
32281
+ runAfterSend();
32282
+ return sent;
32283
+ }
32261
32284
  // The send is committing but has not finished: leaving now would take
32262
32285
  // the form off the screen mid-submission (a popup closing over its own
32263
32286
  // running action, a slide moving on before it is answered). What
@@ -36849,6 +36872,16 @@ const useControlProps = (props, {
36849
36872
  if (!deferredCommand) {
36850
36873
  return;
36851
36874
  }
36875
+ if (uiStateController.optimistic) {
36876
+ // An optimistic button takes its action as done on its own word:
36877
+ // the command runs at once and the run goes on detached, the same
36878
+ // release the form counterpart gives the send (see --navi-send in
36879
+ // commands.js). Only a refused press has nothing to follow.
36880
+ if (completion.result !== false) {
36881
+ deferredCommand();
36882
+ }
36883
+ return;
36884
+ }
36852
36885
  runWhenActionSucceeded(completion, deferredCommand);
36853
36886
  };
36854
36887
  return {
@@ -48795,6 +48828,7 @@ const COMMAND_DEFAULT_PROPS_FACTORIES = {
48795
48828
  * replace?: boolean,
48796
48829
  * actionStandalone?: boolean,
48797
48830
  * actionAbortable?: boolean,
48831
+ * optimistic?: boolean,
48798
48832
  * [key: string]: any,
48799
48833
  * }>}
48800
48834
  * @param {boolean} [replace] Go where the press leads — an `href`, a
@@ -48838,6 +48872,12 @@ const COMMAND_DEFAULT_PROPS_FACTORIES = {
48838
48872
  * running, which the app watches from somewhere else; never for one holding
48839
48873
  * an answer the screen is the only place to read (see
48840
48874
  * docs/interactions.md#the-fourth-question-whose-wait-is-it).
48875
+ * @param {boolean} [optimistic] The action is taken as done on the button's own
48876
+ * word: no busy state, a second press queues behind the run, and its
48877
+ * `command` runs at once — the popup it sits in closes over the run, which
48878
+ * goes on behind. A failure then has no popup to be read in: `resetOnError`
48879
+ * puts the button back and the error callout is drawn on what surrounds the
48880
+ * closed popup (see docs/popup_open.md#the-popup-owns-its-open-state).
48841
48881
  * @param {boolean} [actionAbortable] The person waiting may give up on this
48842
48882
  * button's action: closing the popup the run holds calls it off and goes
48843
48883
  * through, instead of being refused. For a run whose answer may never come —
@@ -48877,7 +48917,7 @@ const COMMAND_DEFAULT_PROPS_FACTORIES = {
48877
48917
  * (it wears it itself) and loses the shrink under the finger, which has
48878
48918
  * nothing left to scale but the interactive area itself.
48879
48919
  */
48880
- const Button = createComponentResolver([ButtonFirstResolver, ButtonRouteResolver, ButtonCommandPropResolver, ButtonConfirmResolver, ButtonUI]);
48920
+ const Button = /*#__PURE__*/createComponentResolver([ButtonFirstResolver, ButtonRouteResolver, ButtonCommandPropResolver, ButtonConfirmResolver, ButtonUI]);
48881
48921
 
48882
48922
  installImportMetaCssBuild(import.meta);
48883
48923
  const css$O = /* css */`.navi_control_swap {
@@ -52248,7 +52288,7 @@ const InputTextualFirstResolver = props => {
52248
52288
  ...props
52249
52289
  });
52250
52290
  };
52251
- const InputTextual = createComponentResolver([InputTextualAsTextResolver, InputTextualFirstResolver, InputWithListResolver, InputWithSuggestionsResolver, InputTypeResolver, InputModeResolver, InputHeadlessResolver, InputTextualUI]);
52291
+ const InputTextual = /*#__PURE__*/createComponentResolver([InputTextualAsTextResolver, InputTextualFirstResolver, InputWithListResolver, InputWithSuggestionsResolver, InputTypeResolver, InputModeResolver, InputHeadlessResolver, InputTextualUI]);
52252
52292
  const RealInput = ({
52253
52293
  maxLength,
52254
52294
  ...domProps
@@ -65850,209 +65890,6 @@ const ListFirstResolver = props => {
65850
65890
  })
65851
65891
  });
65852
65892
  };
65853
-
65854
- /**
65855
- * List — generic virtualized scroll container.
65856
- * Items must use <List.Item> to participate in tracking.
65857
- *
65858
- * @type {import("ignore:preact").FunctionComponent<{
65859
- * selectable?: boolean,
65860
- * multiple?: boolean,
65861
- * deselectable?: boolean,
65862
- * maxLength?: number,
65863
- * maxLengthGuard?: number,
65864
- * parallelGuard?: number,
65865
- * standalone?: boolean,
65866
- * action?: (value: any) => void,
65867
- * uiAction?: (value: any) => void,
65868
- * popover?: boolean,
65869
- * role?: string,
65870
- * renderBudget?: number | string,
65871
- * renderBudgetSkipCheck?: boolean,
65872
- * virtualItemSize?: number,
65873
- * onListVisibleItemsChange?: (visibleItems: any[]) => void,
65874
- * scrolled?: "start" | "end" | number | {id: string, offset?: number},
65875
- * defaultScrolled?: "start" | "end" | number | {id: string, offset?: number},
65876
- * onScrolledChange?: (scrolled: {id: string, index: number, offset: number}) => void,
65877
- * scroller?: "self" | "parent" | "document" | Element | {current: Element},
65878
- * hoverWhileScrolling?: boolean,
65879
- * fallback?: import("ignore:preact").ComponentChildren,
65880
- * searchFallback?: import("ignore:preact").ComponentChildren,
65881
- * searchText?: string,
65882
- * searchNoMatchMode?: "remove" | "invisible_and_inert" | "muted",
65883
- * loading?: boolean,
65884
- * loadingFallback?: "skeleton" | "loader" | import("ignore:preact").ComponentChildren,
65885
- * loadingSkeletonCount?: number,
65886
- * renderSkeleton?: false | ((index: number) => import("ignore:preact").ComponentChildren),
65887
- * error?: boolean | import("ignore:preact").ComponentChildren,
65888
- * separator?: boolean | import("ignore:preact").ComponentChildren,
65889
- * itemTransition?: boolean,
65890
- * lockSize?: boolean,
65891
- * horizontal?: boolean,
65892
- * spacing?: string,
65893
- * columns?: string,
65894
- * itemColumns?: string,
65895
- * alignX?: string,
65896
- * alignY?: string,
65897
- * flexWrap?: boolean,
65898
- * expandX?: boolean,
65899
- * expandY?: boolean,
65900
- * expand?: boolean,
65901
- * children?: import("ignore:preact").ComponentChildren,
65902
- * [key: string]: any,
65903
- * }>}
65904
- * @param {string} [props.columns]
65905
- * The list's own columns: a `grid-template-columns` value the ITEMS are laid
65906
- * into — a sheet of icons (`repeat(auto-fill, minmax(2.5rem, 1fr))`), a row of
65907
- * choices (`repeat(3, minmax(0, 1fr))`). Each item takes one cell, and an item
65908
- * meant to take a whole line says so for itself
65909
- * (`style={{ gridColumn: "1 / -1" }}`). Several items to a line is a shape the
65910
- * list cannot virtualize — its render window and the room it holds for the
65911
- * rows it does not draw both count one item per line — so it is for a set of
65912
- * items the caller renders whole, not for a `<List.Items>` collection.
65913
- * @param {string} [props.itemColumns]
65914
- * The columns inside an ITEM: a `grid-template-columns` value each item fills
65915
- * with its own children, a table whose cells line up down the list. Every item
65916
- * becomes a subgrid row spanning all the columns, so a column is as wide as
65917
- * the widest cell in it among the rows actually in the DOM — real column
65918
- * sizing that stays right as the window moves. Rows of a table, then, where
65919
- * `columns` above is a grid of items; the two cannot both be set.
65920
- * @param {string} [props.alignX]
65921
- * Where the items sit across the track — `alignX="center"` centres a
65922
- * horizontal list's row of items inside a list wider than they are. Together
65923
- * with `alignY`, `align` and `flexWrap`, this reaches the `<ul>` holding the
65924
- * items rather than the frame drawn around it: the frame's only child is the
65925
- * scroll box, which fills it and has nothing to arrange.
65926
- * @param {boolean} [props.flexWrap]
65927
- * Lets a horizontal list's items fall to the next line instead of running
65928
- * past the edge — a row of choices under a `maxWidth`, say. Same caveat as
65929
- * `columns` above: a line holding several items is not virtualizable.
65930
- * @param {string} [props.overflow]
65931
- * `"visible"` lets the items paint outside the list — a check in a row's
65932
- * corner, a badge crossing the edge. A list clips by default, which is what
65933
- * its rounded corners and its scroll box need, and the two cannot both be
65934
- * true: asking for visible gives up the clipping, corners included.
65935
- * @param {boolean} [props.itemTransition]
65936
- * Names each row, so a change the application wraps in
65937
- * `document.startViewTransition` is seen row by row — rows moving to their new
65938
- * place, an arriving row appearing where it lands — instead of the list
65939
- * cross-fading as a block. The rows are drawn inside the list's own picture,
65940
- * so one coming from outside the visible part of the list is cut at the list's
65941
- * edge like any other overflow. Requires nested view transition groups
65942
- * (Chrome/Edge 140+): elsewhere the rows are left unnamed and the change
65943
- * simply happens — but the browser still names the document root, so the page
65944
- * cross-fades as a whole unless the application says otherwise, which is its
65945
- * call and not the list's:
65946
- * ```css
65947
- * @supports not (view-transition-group: contain) {
65948
- * :root { view-transition-name: none; }
65949
- * }
65950
- * ```
65951
- * @param {false|((index: number) => any)} [props.renderSkeleton]
65952
- * What a row on its way looks like — a row of the shape the real ones will
65953
- * have, so nothing moves when they arrive. Used for the rows a `<List.Items>`
65954
- * stands for and does not hold yet, and for the placeholder rows drawn while
65955
- * the whole list is `loading`. Defaults to a bare `<List.Item skeleton>`.
65956
- * @param {"skeleton"|"loader"|import("ignore:preact").ComponentChildren} [props.loadingFallback="skeleton"]
65957
- * What to display in place of the items while `loading` — that is, while
65958
- * there is nothing to show at all: `"skeleton"` renders
65959
- * `loadingSkeletonCount` placeholder rows (look:
65960
- * `renderSkeleton`), `"loader"` a single centered spinner, and
65961
- * anything else is rendered as-is in a row of its own. A falsy value
65962
- * displays nothing. A list that knows how many rows it will have has no use
65963
- * for this — see `<List.Items count>`, whose not-yet-loaded rows are drawn
65964
- * as skeletons in place, one per row, virtualized like the rest.
65965
- * @param {number} [props.loadingSkeletonCount=3]
65966
- * How many placeholder rows `loadingFallback="skeleton"` draws. `0` says the
65967
- * list is already known to be empty: the empty `fallback` shows right away
65968
- * rather than an empty frame, so nothing moves when the response arrives.
65969
- * @param {"start"|"end"|number|{id: string, offset?: number}} [props.defaultScrolled="start"]
65970
- * Where the list opens, after which the user owns the scroll. `"end"` is a
65971
- * thread read backwards — the last rows are the ones to show, and the ones
65972
- * asked for first. A number opens on that row of the collection. `{id,
65973
- * offset}` — what `onScrolledChange` hands out — opens on a NAMED row,
65974
- * `offset` pixels below where the row would land on its own: the row is asked
65975
- * for by name (see the range's own `around`), then put back by MEASURING it,
65976
- * so it lands where it was even if rows were inserted before it, and whatever
65977
- * the screen it was saved on. `offset: 0` is where a `scrollIntoView()` puts
65978
- * it — in front of the fixed bar the scroller gives room for, below the
65979
- * sticky header and the group label the row lives under — so nothing of that
65980
- * room has to be restated as a number by whoever asks.
65981
- * @param {"start"|"end"|number|{id: string, offset?: number}} [props.scrolled]
65982
- * The same, but held: the list goes back there every time this changes, even
65983
- * after the user has scrolled — the caller owns where the list is (see
65984
- * `defaultScrolled` for the uncontrolled form, and `open`/`defaultOpen`
65985
- * elsewhere in navi for the same pair). When the named row turns out not to
65986
- * exist — a message deleted since — the list opens at `defaultScrolled`
65987
- * instead.
65988
- *
65989
- * In every form the list holds itself there while it is still finding out
65990
- * how many rows there are and how tall one is, and lets go the moment the
65991
- * user reaches for the list.
65992
- * @param {(scrolled: {id: string, index: number, offset: number}) => void} [props.onScrolledChange]
65993
- * Where the list is, as the user scrolls: the row at the top of the view and
65994
- * how far below the place a row lands on its own (see `defaultScrolled`) it
65995
- * starts. Keep it to come back to it
65996
- * later through `scrolled`/`defaultScrolled` — an index would not do, since
65997
- * rows get inserted while a list is being read.
65998
- * @param {"self"|"parent"|"document"|Element|{current: Element}} [props.scroller="self"]
65999
- * Which box scrolls. `"self"` gives the list a scroll box of its own;
66000
- * `"parent"` makes it virtualize against the scrollable ancestor it lives in
66001
- * (the page, a panel) — no scroll box nested inside another one, no height
66002
- * to compute.
66003
- *
66004
- * `"parent"` finds that ancestor by measuring: the nearest one whose content
66005
- * actually overflows it, the page if none does. Declaring `overflow` is not
66006
- * enough to be picked (a box with `overflow-x: auto` that grows with its
66007
- * content computes `overflow-y: auto` without ever scrolling), and the
66008
- * answer is taken again as the geometry moves, so an ancestor that starts to
66009
- * scroll once it fills up is picked up then. When that is still not the box
66010
- * you mean, say so: `"document"`, or the element itself (a ref works) —
66011
- * nothing is guessed then.
66012
- * @param {boolean} [props.hoverWhileScrolling=false]
66013
- * Whether the rows still answer the pointer while the scroller they live in
66014
- * is moving. They do not by default: a scroll slides the rows under a
66015
- * motionless pointer, so the browser reports a hover on each of them, and
66016
- * the user asked to scroll, not to hover. The cost of taking them at face
66017
- * value is paid by whatever hover triggers — a highlight elsewhere in the
66018
- * tree, a prefetch, a map — at the worst moment, mid-scroll.
66019
- *
66020
- * Pass `true` for a list whose rows must stay live under the pointer while
66021
- * it scrolls. The trade of the default is the mirror one: right after a
66022
- * scroll, the row under the pointer lights up only once the pointer moves.
66023
- * @param {boolean} [props.deselectable]
66024
- * A single-select list allowed to hold nothing: the selected row, pressed
66025
- * again, lets go. Without it the list is a radio group — a choice, once
66026
- * made, moves to another row but never goes away. A `multiple` list toggles
66027
- * its rows already.
66028
- * @param {number} [props.maxLength]
66029
- * How many items a `selectable multiple` list accepts — the same word, and
66030
- * the same behaviour, as `maxLength` on a text field: a rule the list is
66031
- * judged against, not a wall. A longer selection is allowed to exist and is
66032
- * reported as invalid, which is what lets a value coming from elsewhere (an
66033
- * API, a URL) be shown and then corrected.
66034
- * @param {number} [props.maxLengthGuard]
66035
- * The same limit, enforced as the selection is made: while the list holds as
66036
- * many items as it accepts, the ones not selected go read-only — still
66037
- * pointable, focusable and pressable, answering `"[max] max."` instead of
66038
- * taking — and `uiAction` is not called. The selected ones stay takeable
66039
- * back, so a selection that arrived too long can always be brought back
66040
- * under the limit. Implies `maxLength` for validity.
66041
- * @param {number} [props.parallelGuard=4]
66042
- * How many runs the rows may have in flight at once, for a list whose rows
66043
- * carry their own `action` (a button per row). While that many are out, every
66044
- * row that is not running goes read-only and says how many it is waiting on;
66045
- * the next press is possible again as soon as one comes back. `Infinity`
66046
- * lifts it. Counts runs, not values — `maxLengthGuard` above is the one that
66047
- * says how many things the selection may hold.
66048
- * @param {boolean} [props.standalone]
66049
- * This list answers for itself: it does not register with the control group
66050
- * or picker around it, so its selection stays out of that value and nothing
66051
- * coming down — a distributed value, a reset — reaches it. What a popup whose
66052
- * one answer is spread over several lists says, so that none of them is taken
66053
- * for the answer itself.
66054
- */
66055
- const List = createComponentResolver([ListFirstResolver, ListSelectableResolver, ListUI]);
66056
65893
  const ListContent = ({
66057
65894
  role,
66058
65895
  fallback,
@@ -68318,14 +68155,13 @@ const LIST_ITEM_STYLE_CSS_VARS = {
68318
68155
  * search-driven scroll-to-top-match behavior. `matchRanges` are [start, end]
68319
68156
  * ranges highlighted via the CSS Highlight API.
68320
68157
  */
68321
- const ListItem = createComponentResolver([ListItemFirstResolver, ListItemRowResolver, ListItemSkeletonResolver, ListItemSelectableResolver, ListItemHeaderOrFooterResolver, ListItemPresentationResolver, ListItemUI],
68158
+ const ListItem = /*#__PURE__*/createComponentResolver([ListItemFirstResolver, ListItemRowResolver, ListItemSkeletonResolver, ListItemSelectableResolver, ListItemHeaderOrFooterResolver, ListItemPresentationResolver, ListItemUI],
68322
68159
  // Rendered by the hundred, and almost always with the same primitive props:
68323
68160
  // a selection change re-renders the list, and every row but two has nothing
68324
68161
  // to change.
68325
68162
  {
68326
68163
  pure: true
68327
68164
  });
68328
- List.Item = ListItem;
68329
68165
 
68330
68166
  // Everything the list knows about the collection while its children are being
68331
68167
  // rendered: how many rows it has in total, which of them are actually held, and
@@ -69085,7 +68921,6 @@ const ListItems = ({
69085
68921
  }
69086
68922
  return rows;
69087
68923
  };
69088
- List.Items = ListItems;
69089
68924
 
69090
68925
  // What is drawn where rows were asked for and never came: the sentence and the
69091
68926
  // way out, in the row itself — the rest of the list is fine, so replacing all
@@ -69695,6 +69530,219 @@ const resolveSeparatorVnode = (separator, gapIndex) => {
69695
69530
  }
69696
69531
  return separator;
69697
69532
  };
69533
+ const ListResolved = /*#__PURE__*/createComponentResolver([ListFirstResolver, ListSelectableResolver, ListUI]);
69534
+
69535
+ // Declared last because Item and Items are consts defined above, and they are
69536
+ // folded into the declaration rather than written onto List afterwards
69537
+ // (`List.Item = ListItem`): List is what createComponentResolver returns, an
69538
+ // object a bundler cannot see through, so a property assigned to it later is a
69539
+ // side effect it has to keep — and with it List, its rows and everything they
69540
+ // import, in a bundle that never renders a list. See Picker for the same shape.
69541
+ /**
69542
+ * List — generic virtualized scroll container.
69543
+ * Items must use <List.Item> to participate in tracking.
69544
+ *
69545
+ * @type {import("ignore:preact").FunctionComponent<{
69546
+ * selectable?: boolean,
69547
+ * multiple?: boolean,
69548
+ * deselectable?: boolean,
69549
+ * maxLength?: number,
69550
+ * maxLengthGuard?: number,
69551
+ * parallelGuard?: number,
69552
+ * standalone?: boolean,
69553
+ * action?: (value: any) => void,
69554
+ * uiAction?: (value: any) => void,
69555
+ * popover?: boolean,
69556
+ * role?: string,
69557
+ * renderBudget?: number | string,
69558
+ * renderBudgetSkipCheck?: boolean,
69559
+ * virtualItemSize?: number,
69560
+ * onListVisibleItemsChange?: (visibleItems: any[]) => void,
69561
+ * scrolled?: "start" | "end" | number | {id: string, offset?: number},
69562
+ * defaultScrolled?: "start" | "end" | number | {id: string, offset?: number},
69563
+ * onScrolledChange?: (scrolled: {id: string, index: number, offset: number}) => void,
69564
+ * scroller?: "self" | "parent" | "document" | Element | {current: Element},
69565
+ * hoverWhileScrolling?: boolean,
69566
+ * fallback?: import("ignore:preact").ComponentChildren,
69567
+ * searchFallback?: import("ignore:preact").ComponentChildren,
69568
+ * searchText?: string,
69569
+ * searchNoMatchMode?: "remove" | "invisible_and_inert" | "muted",
69570
+ * loading?: boolean,
69571
+ * loadingFallback?: "skeleton" | "loader" | import("ignore:preact").ComponentChildren,
69572
+ * loadingSkeletonCount?: number,
69573
+ * renderSkeleton?: false | ((index: number) => import("ignore:preact").ComponentChildren),
69574
+ * error?: boolean | import("ignore:preact").ComponentChildren,
69575
+ * separator?: boolean | import("ignore:preact").ComponentChildren,
69576
+ * itemTransition?: boolean,
69577
+ * lockSize?: boolean,
69578
+ * horizontal?: boolean,
69579
+ * spacing?: string,
69580
+ * columns?: string,
69581
+ * itemColumns?: string,
69582
+ * alignX?: string,
69583
+ * alignY?: string,
69584
+ * flexWrap?: boolean,
69585
+ * expandX?: boolean,
69586
+ * expandY?: boolean,
69587
+ * expand?: boolean,
69588
+ * children?: import("ignore:preact").ComponentChildren,
69589
+ * [key: string]: any,
69590
+ * }>}
69591
+ * @param {string} [props.columns]
69592
+ * The list's own columns: a `grid-template-columns` value the ITEMS are laid
69593
+ * into — a sheet of icons (`repeat(auto-fill, minmax(2.5rem, 1fr))`), a row of
69594
+ * choices (`repeat(3, minmax(0, 1fr))`). Each item takes one cell, and an item
69595
+ * meant to take a whole line says so for itself
69596
+ * (`style={{ gridColumn: "1 / -1" }}`). Several items to a line is a shape the
69597
+ * list cannot virtualize — its render window and the room it holds for the
69598
+ * rows it does not draw both count one item per line — so it is for a set of
69599
+ * items the caller renders whole, not for a `<List.Items>` collection.
69600
+ * @param {string} [props.itemColumns]
69601
+ * The columns inside an ITEM: a `grid-template-columns` value each item fills
69602
+ * with its own children, a table whose cells line up down the list. Every item
69603
+ * becomes a subgrid row spanning all the columns, so a column is as wide as
69604
+ * the widest cell in it among the rows actually in the DOM — real column
69605
+ * sizing that stays right as the window moves. Rows of a table, then, where
69606
+ * `columns` above is a grid of items; the two cannot both be set.
69607
+ * @param {string} [props.alignX]
69608
+ * Where the items sit across the track — `alignX="center"` centres a
69609
+ * horizontal list's row of items inside a list wider than they are. Together
69610
+ * with `alignY`, `align` and `flexWrap`, this reaches the `<ul>` holding the
69611
+ * items rather than the frame drawn around it: the frame's only child is the
69612
+ * scroll box, which fills it and has nothing to arrange.
69613
+ * @param {boolean} [props.flexWrap]
69614
+ * Lets a horizontal list's items fall to the next line instead of running
69615
+ * past the edge — a row of choices under a `maxWidth`, say. Same caveat as
69616
+ * `columns` above: a line holding several items is not virtualizable.
69617
+ * @param {string} [props.overflow]
69618
+ * `"visible"` lets the items paint outside the list — a check in a row's
69619
+ * corner, a badge crossing the edge. A list clips by default, which is what
69620
+ * its rounded corners and its scroll box need, and the two cannot both be
69621
+ * true: asking for visible gives up the clipping, corners included.
69622
+ * @param {boolean} [props.itemTransition]
69623
+ * Names each row, so a change the application wraps in
69624
+ * `document.startViewTransition` is seen row by row — rows moving to their new
69625
+ * place, an arriving row appearing where it lands — instead of the list
69626
+ * cross-fading as a block. The rows are drawn inside the list's own picture,
69627
+ * so one coming from outside the visible part of the list is cut at the list's
69628
+ * edge like any other overflow. Requires nested view transition groups
69629
+ * (Chrome/Edge 140+): elsewhere the rows are left unnamed and the change
69630
+ * simply happens — but the browser still names the document root, so the page
69631
+ * cross-fades as a whole unless the application says otherwise, which is its
69632
+ * call and not the list's:
69633
+ * ```css
69634
+ * @supports not (view-transition-group: contain) {
69635
+ * :root { view-transition-name: none; }
69636
+ * }
69637
+ * ```
69638
+ * @param {false|((index: number) => any)} [props.renderSkeleton]
69639
+ * What a row on its way looks like — a row of the shape the real ones will
69640
+ * have, so nothing moves when they arrive. Used for the rows a `<List.Items>`
69641
+ * stands for and does not hold yet, and for the placeholder rows drawn while
69642
+ * the whole list is `loading`. Defaults to a bare `<List.Item skeleton>`.
69643
+ * @param {"skeleton"|"loader"|import("ignore:preact").ComponentChildren} [props.loadingFallback="skeleton"]
69644
+ * What to display in place of the items while `loading` — that is, while
69645
+ * there is nothing to show at all: `"skeleton"` renders
69646
+ * `loadingSkeletonCount` placeholder rows (look:
69647
+ * `renderSkeleton`), `"loader"` a single centered spinner, and
69648
+ * anything else is rendered as-is in a row of its own. A falsy value
69649
+ * displays nothing. A list that knows how many rows it will have has no use
69650
+ * for this — see `<List.Items count>`, whose not-yet-loaded rows are drawn
69651
+ * as skeletons in place, one per row, virtualized like the rest.
69652
+ * @param {number} [props.loadingSkeletonCount=3]
69653
+ * How many placeholder rows `loadingFallback="skeleton"` draws. `0` says the
69654
+ * list is already known to be empty: the empty `fallback` shows right away
69655
+ * rather than an empty frame, so nothing moves when the response arrives.
69656
+ * @param {"start"|"end"|number|{id: string, offset?: number}} [props.defaultScrolled="start"]
69657
+ * Where the list opens, after which the user owns the scroll. `"end"` is a
69658
+ * thread read backwards — the last rows are the ones to show, and the ones
69659
+ * asked for first. A number opens on that row of the collection. `{id,
69660
+ * offset}` — what `onScrolledChange` hands out — opens on a NAMED row,
69661
+ * `offset` pixels below where the row would land on its own: the row is asked
69662
+ * for by name (see the range's own `around`), then put back by MEASURING it,
69663
+ * so it lands where it was even if rows were inserted before it, and whatever
69664
+ * the screen it was saved on. `offset: 0` is where a `scrollIntoView()` puts
69665
+ * it — in front of the fixed bar the scroller gives room for, below the
69666
+ * sticky header and the group label the row lives under — so nothing of that
69667
+ * room has to be restated as a number by whoever asks.
69668
+ * @param {"start"|"end"|number|{id: string, offset?: number}} [props.scrolled]
69669
+ * The same, but held: the list goes back there every time this changes, even
69670
+ * after the user has scrolled — the caller owns where the list is (see
69671
+ * `defaultScrolled` for the uncontrolled form, and `open`/`defaultOpen`
69672
+ * elsewhere in navi for the same pair). When the named row turns out not to
69673
+ * exist — a message deleted since — the list opens at `defaultScrolled`
69674
+ * instead.
69675
+ *
69676
+ * In every form the list holds itself there while it is still finding out
69677
+ * how many rows there are and how tall one is, and lets go the moment the
69678
+ * user reaches for the list.
69679
+ * @param {(scrolled: {id: string, index: number, offset: number}) => void} [props.onScrolledChange]
69680
+ * Where the list is, as the user scrolls: the row at the top of the view and
69681
+ * how far below the place a row lands on its own (see `defaultScrolled`) it
69682
+ * starts. Keep it to come back to it
69683
+ * later through `scrolled`/`defaultScrolled` — an index would not do, since
69684
+ * rows get inserted while a list is being read.
69685
+ * @param {"self"|"parent"|"document"|Element|{current: Element}} [props.scroller="self"]
69686
+ * Which box scrolls. `"self"` gives the list a scroll box of its own;
69687
+ * `"parent"` makes it virtualize against the scrollable ancestor it lives in
69688
+ * (the page, a panel) — no scroll box nested inside another one, no height
69689
+ * to compute.
69690
+ *
69691
+ * `"parent"` finds that ancestor by measuring: the nearest one whose content
69692
+ * actually overflows it, the page if none does. Declaring `overflow` is not
69693
+ * enough to be picked (a box with `overflow-x: auto` that grows with its
69694
+ * content computes `overflow-y: auto` without ever scrolling), and the
69695
+ * answer is taken again as the geometry moves, so an ancestor that starts to
69696
+ * scroll once it fills up is picked up then. When that is still not the box
69697
+ * you mean, say so: `"document"`, or the element itself (a ref works) —
69698
+ * nothing is guessed then.
69699
+ * @param {boolean} [props.hoverWhileScrolling=false]
69700
+ * Whether the rows still answer the pointer while the scroller they live in
69701
+ * is moving. They do not by default: a scroll slides the rows under a
69702
+ * motionless pointer, so the browser reports a hover on each of them, and
69703
+ * the user asked to scroll, not to hover. The cost of taking them at face
69704
+ * value is paid by whatever hover triggers — a highlight elsewhere in the
69705
+ * tree, a prefetch, a map — at the worst moment, mid-scroll.
69706
+ *
69707
+ * Pass `true` for a list whose rows must stay live under the pointer while
69708
+ * it scrolls. The trade of the default is the mirror one: right after a
69709
+ * scroll, the row under the pointer lights up only once the pointer moves.
69710
+ * @param {boolean} [props.deselectable]
69711
+ * A single-select list allowed to hold nothing: the selected row, pressed
69712
+ * again, lets go. Without it the list is a radio group — a choice, once
69713
+ * made, moves to another row but never goes away. A `multiple` list toggles
69714
+ * its rows already.
69715
+ * @param {number} [props.maxLength]
69716
+ * How many items a `selectable multiple` list accepts — the same word, and
69717
+ * the same behaviour, as `maxLength` on a text field: a rule the list is
69718
+ * judged against, not a wall. A longer selection is allowed to exist and is
69719
+ * reported as invalid, which is what lets a value coming from elsewhere (an
69720
+ * API, a URL) be shown and then corrected.
69721
+ * @param {number} [props.maxLengthGuard]
69722
+ * The same limit, enforced as the selection is made: while the list holds as
69723
+ * many items as it accepts, the ones not selected go read-only — still
69724
+ * pointable, focusable and pressable, answering `"[max] max."` instead of
69725
+ * taking — and `uiAction` is not called. The selected ones stay takeable
69726
+ * back, so a selection that arrived too long can always be brought back
69727
+ * under the limit. Implies `maxLength` for validity.
69728
+ * @param {number} [props.parallelGuard=4]
69729
+ * How many runs the rows may have in flight at once, for a list whose rows
69730
+ * carry their own `action` (a button per row). While that many are out, every
69731
+ * row that is not running goes read-only and says how many it is waiting on;
69732
+ * the next press is possible again as soon as one comes back. `Infinity`
69733
+ * lifts it. Counts runs, not values — `maxLengthGuard` above is the one that
69734
+ * says how many things the selection may hold.
69735
+ * @param {boolean} [props.standalone]
69736
+ * This list answers for itself: it does not register with the control group
69737
+ * or picker around it, so its selection stays out of that value and nothing
69738
+ * coming down — a distributed value, a reset — reaches it. What a popup whose
69739
+ * one answer is spread over several lists says, so that none of them is taken
69740
+ * for the answer itself.
69741
+ */
69742
+ const List = /*#__PURE__*/Object.assign(ListResolved, {
69743
+ Item: ListItem,
69744
+ Items: ListItems
69745
+ });
69698
69746
 
69699
69747
  const PickerNaviTime = props => {
69700
69748
  const Next = useNextResolver();
@@ -70627,7 +70675,7 @@ const PickerObject = props => {
70627
70675
  "navi-state-shape": "object"
70628
70676
  });
70629
70677
  };
70630
- const PickerObjectUI = asPickerOwnUI(() => {
70678
+ const PickerObjectUI = /*#__PURE__*/asPickerOwnUI(() => {
70631
70679
  const {
70632
70680
  value,
70633
70681
  placeholder
@@ -70662,7 +70710,7 @@ const PickerArray = props => {
70662
70710
  "navi-state-shape": "array"
70663
70711
  });
70664
70712
  };
70665
- const PickerArrayUI = asPickerOwnUI(() => {
70713
+ const PickerArrayUI = /*#__PURE__*/asPickerOwnUI(() => {
70666
70714
  const {
70667
70715
  value,
70668
70716
  placeholder,
@@ -70745,7 +70793,7 @@ const PickerColor = props => {
70745
70793
  ...props
70746
70794
  });
70747
70795
  };
70748
- const PickerColorUI = asPickerOwnUI(() => {
70796
+ const PickerColorUI = /*#__PURE__*/asPickerOwnUI(() => {
70749
70797
  const {
70750
70798
  value,
70751
70799
  placeholder
@@ -70769,7 +70817,7 @@ const PickerDate = props => {
70769
70817
  type: "date"
70770
70818
  });
70771
70819
  };
70772
- const PickerDateUI = asPickerOwnUI(props => {
70820
+ const PickerDateUI = /*#__PURE__*/asPickerOwnUI(props => {
70773
70821
  const {
70774
70822
  value,
70775
70823
  placeholder
@@ -70801,7 +70849,7 @@ const PickerMonth = props => {
70801
70849
  type: "month"
70802
70850
  });
70803
70851
  };
70804
- const PickerMonthUI = asPickerOwnUI(props => {
70852
+ const PickerMonthUI = /*#__PURE__*/asPickerOwnUI(props => {
70805
70853
  const {
70806
70854
  value,
70807
70855
  placeholder
@@ -70832,7 +70880,7 @@ const PickerWeek = props => {
70832
70880
  type: "week"
70833
70881
  });
70834
70882
  };
70835
- const PickerWeekUI = asPickerOwnUI(props => {
70883
+ const PickerWeekUI = /*#__PURE__*/asPickerOwnUI(props => {
70836
70884
  const {
70837
70885
  value,
70838
70886
  placeholder
@@ -70863,7 +70911,7 @@ const PickerTime = props => {
70863
70911
  type: "time"
70864
70912
  });
70865
70913
  };
70866
- const PickerTimeUI = asPickerOwnUI(props => {
70914
+ const PickerTimeUI = /*#__PURE__*/asPickerOwnUI(props => {
70867
70915
  const {
70868
70916
  value,
70869
70917
  placeholder
@@ -70894,7 +70942,7 @@ const PickerDuration = props => {
70894
70942
  "navi-input-type": "duration"
70895
70943
  });
70896
70944
  };
70897
- const PickerDurationUI = asPickerOwnUI(props => {
70945
+ const PickerDurationUI = /*#__PURE__*/asPickerOwnUI(props => {
70898
70946
  const {
70899
70947
  value,
70900
70948
  placeholder
@@ -70924,7 +70972,7 @@ const PickerDatetime = props => {
70924
70972
  type: "datetime-local"
70925
70973
  });
70926
70974
  };
70927
- const PickerDatetimeUI = asPickerOwnUI(props => {
70975
+ const PickerDatetimeUI = /*#__PURE__*/asPickerOwnUI(props => {
70928
70976
  const {
70929
70977
  value,
70930
70978
  placeholder
@@ -70953,7 +71001,7 @@ const PickerFile = props => {
70953
71001
  ...props
70954
71002
  });
70955
71003
  };
70956
- const PickerFileUI = asPickerOwnUI(() => {
71004
+ const PickerFileUI = /*#__PURE__*/asPickerOwnUI(() => {
70957
71005
  const {
70958
71006
  value,
70959
71007
  placeholder
@@ -72509,26 +72557,37 @@ const warnOnUnknownPickerType = props => {
72509
72557
  pickerTypeWarnedSet.add(type);
72510
72558
  console.warn(`[navi] <Picker type="${type}"> — "${type}" is not a picker type. ` + `The picker holds a single text value instead, so an object given to it is written as "[object Object]". ` + `Types are: date, month, week, time, datetime, duration, color, file, text, array, object, confirm — ` + `"object" being the one for a popup holding several named controls.`);
72511
72559
  };
72512
- const Picker = createComponentResolver([PickerFirstResolver, PickerPresetResolver, PickerConfirmResolver, PickerCustomResolver, PickerTypeResolver, PickerButton]);
72513
- Picker.Chip = PickerChip;
72514
- Picker.Clear = PickerClear;
72515
- Picker.UI = PickerDefaultUI;
72516
- Picker.UI.Date = PickerDateUI;
72517
- Picker.UI.Time = PickerTimeUI;
72518
- Picker.UI.Duration = PickerDurationUI;
72519
- Picker.UI.Week = PickerWeekUI;
72520
- Picker.UI.Datetime = PickerDatetimeUI;
72521
- Picker.UI.File = PickerFileUI;
72522
- Picker.UI.Color = PickerColorUI;
72523
- Picker.UI.Object = PickerObjectUI;
72524
- Picker.UI.Multiple = PickerArrayUI;
72525
- Picker.UI.PencilSvg = PencilSvg;
72526
- Picker.UI.ChevronDownSvg = ChevronDownSvg$1;
72527
- Picker.UI.ClockSvg = ClockSvg;
72528
- Picker.UI.DurationSvg = DurationSvg;
72529
- Picker.UI.CalendarSvg = CalendarSvg;
72530
- Picker.UI.FileSvg = FileSvg;
72531
- Picker.UI.ColorSvg = ColorSvg;
72560
+ const PickerResolved = /*#__PURE__*/createComponentResolver([PickerFirstResolver, PickerPresetResolver, PickerConfirmResolver, PickerCustomResolver, PickerTypeResolver, PickerButton]);
72561
+ const PickerUI = /*#__PURE__*/Object.assign(PickerDefaultUI, {
72562
+ Date: PickerDateUI,
72563
+ Time: PickerTimeUI,
72564
+ Duration: PickerDurationUI,
72565
+ Week: PickerWeekUI,
72566
+ Datetime: PickerDatetimeUI,
72567
+ File: PickerFileUI,
72568
+ Color: PickerColorUI,
72569
+ Object: PickerObjectUI,
72570
+ Multiple: PickerArrayUI,
72571
+ PencilSvg,
72572
+ ChevronDownSvg: ChevronDownSvg$1,
72573
+ ClockSvg,
72574
+ DurationSvg,
72575
+ CalendarSvg,
72576
+ FileSvg,
72577
+ ColorSvg
72578
+ });
72579
+
72580
+ // Assembled in pure expressions rather than written onto Picker afterwards
72581
+ // (`Picker.Chip = PickerChip`): what createComponentResolver returns is an object
72582
+ // a bundler cannot see through, so a property assigned to it later is a side
72583
+ // effect it has to keep — and with it Picker, every UI and everything they
72584
+ // import, in a bundle that never renders a picker. The annotations say these
72585
+ // calls only create and return; an unused Picker then disappears whole.
72586
+ const Picker = /*#__PURE__*/Object.assign(PickerResolved, {
72587
+ Chip: PickerChip,
72588
+ Clear: PickerClear,
72589
+ UI: PickerUI
72590
+ });
72532
72591
 
72533
72592
  installImportMetaCssBuild(import.meta);
72534
72593
  const css$s = /* css */`@layer navi {
@@ -78508,7 +78567,7 @@ const useTableDragContextValue = ({
78508
78567
  };
78509
78568
  }, [grabTarget, canChangeColumnOrder]);
78510
78569
  };
78511
- const TableDragCloneContainer = forwardRef((props, ref) => {
78570
+ const TableDragCloneContainer = /*#__PURE__*/forwardRef((props, ref) => {
78512
78571
  import.meta.css = [css$k, "@jsenv/navi/src/control/table/drag/table_drag.jsx"];
78513
78572
  const {
78514
78573
  tableId
@@ -78519,7 +78578,7 @@ const TableDragCloneContainer = forwardRef((props, ref) => {
78519
78578
  "data-overlay-for": tableId
78520
78579
  });
78521
78580
  });
78522
- const TableColumnDropPreview = forwardRef((props, ref) => {
78581
+ const TableColumnDropPreview = /*#__PURE__*/forwardRef((props, ref) => {
78523
78582
  return jsxs("div", {
78524
78583
  ref: ref,
78525
78584
  className: "navi_table_column_drop_preview",
@@ -80615,7 +80674,7 @@ const css$f = /* css */`.navi_table_ui {
80615
80674
  overflow: hidden;
80616
80675
  }
80617
80676
  `;
80618
- const TableUI = forwardRef((props, ref) => {
80677
+ const TableUI = /*#__PURE__*/forwardRef((props, ref) => {
80619
80678
  import.meta.css = [css$f, "@jsenv/navi/src/control/table/table_ui.jsx"];
80620
80679
  const {
80621
80680
  tableRef,