@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.
@@ -20295,16 +20295,16 @@ const useComposeElementRef = (syncElement, externalRef) => {
20295
20295
  const cleanupRef = useRef(null);
20296
20296
  const elRef = useRef(null);
20297
20297
  const prevSyncElementRef = useRef(undefined);
20298
- const stableRef = useRef(null);
20298
+ const holderRef = useRef(null);
20299
20299
  const externalRefRef = useRef(externalRef);
20300
20300
  const syncElementRef = useRef(syncElement);
20301
20301
  syncElementRef.current = syncElement;
20302
- // Detect external ref identity change between renders. The refCallback is
20303
- // stable across renders, so when the parent passes a new ref object (or
20304
- // switches from null to a ref), Preact does NOT re-fire the callback while
20305
- // the DOM element is unchanged. We must manually clear the old ref and
20306
- // populate the new one with the current element to avoid leaving the new
20307
- // ref's `.current` stuck at `null`.
20302
+ // Detect external ref identity change between renders. The refCallback keeps
20303
+ // its identity once it holds the element, so when the parent passes a new
20304
+ // ref object (or switches from null to a ref), Preact does NOT re-fire the
20305
+ // callback while the DOM element is unchanged. We must manually clear the
20306
+ // old ref and populate the new one with the current element to avoid
20307
+ // leaving the new ref's `.current` stuck at `null`.
20308
20308
  const prevExternalRefRef = useRef(externalRef);
20309
20309
  if (prevExternalRefRef.current !== externalRef) {
20310
20310
  const previous = prevExternalRefRef.current;
@@ -20322,12 +20322,12 @@ const useComposeElementRef = (syncElement, externalRef) => {
20322
20322
  }
20323
20323
  externalRefRef.current = externalRef;
20324
20324
 
20325
- if (!stableRef.current) {
20326
- // Created once, like the ref callback that calls it, and reading the sync
20327
- // function through a ref for that reason: the element can be replaced long
20328
- // after the first render — a tag that changes, a box hidden then shown —
20329
- // and what the new element gets must be the current render's sync, not
20330
- // the one the first render closed over.
20325
+ if (!holderRef.current) {
20326
+ // Created once, and reading the sync function through a ref for that
20327
+ // reason: the element can be replaced long after the first render — a tag
20328
+ // that changes, a box hidden then shown — and what the new element gets
20329
+ // must be the current render's sync, not the one the first render closed
20330
+ // over.
20331
20331
  const runSync = (el) => {
20332
20332
  if (cleanupRef.current) {
20333
20333
  cleanupRef.current();
@@ -20340,6 +20340,18 @@ const useComposeElementRef = (syncElement, externalRef) => {
20340
20340
  cleanupRef.current = cleanup;
20341
20341
  }
20342
20342
  };
20343
+ holderRef.current = { runSync, refCallback: null };
20344
+ }
20345
+ const { runSync } = holderRef.current;
20346
+
20347
+ // A new callback on every render until Preact has handed it the element,
20348
+ // the same one afterwards. Preact re-applies a ref only when its identity
20349
+ // changes, and a diff that throws with no error boundary above skips
20350
+ // commitRoot: its refs are dropped while its DOM stays and is adopted as
20351
+ // mounted by the next render. A callback that never fired would then never
20352
+ // fire, and a Box would stay raw HTML for the life of the document. Free on
20353
+ // a healthy mount, where the first callback is the only one ever created.
20354
+ if (!elRef.current) {
20343
20355
  const refCallback = (el) => {
20344
20356
  elRef.current = el;
20345
20357
  // Keep .current in sync immediately so useEffect callbacks that read
@@ -20363,9 +20375,9 @@ const useComposeElementRef = (syncElement, externalRef) => {
20363
20375
  prevSyncElementRef.current = undefined;
20364
20376
  }
20365
20377
  };
20366
- stableRef.current = { refCallback, runSync };
20378
+ holderRef.current.refCallback = refCallback;
20367
20379
  }
20368
- const { refCallback, runSync } = stableRef.current;
20380
+ const { refCallback } = holderRef.current;
20369
20381
 
20370
20382
  // If element already mounted, re-sync when syncElement reference changed.
20371
20383
  if (elRef.current && syncElement !== prevSyncElementRef.current) {
@@ -31722,6 +31734,17 @@ registerNaviCommand("--navi-send", (source, event, { requester }) => {
31722
31734
  return sent;
31723
31735
  }
31724
31736
  if (isRunning) {
31737
+ // An optimistic control takes its send as done on its own word: what
31738
+ // follows it runs at once and the run goes on detached — still
31739
+ // watched, still able to fail, its error callout then drawn on what
31740
+ // surrounds the closed surface it was typed in (see openCallout's
31741
+ // anchor resolution in callout.js).
31742
+ const sendController = (findControlHost(target) || target)
31743
+ .__uiStateController__;
31744
+ if (sendController?.optimistic) {
31745
+ runAfterSend();
31746
+ return sent;
31747
+ }
31725
31748
  // The send is committing but has not finished: leaving now would take
31726
31749
  // the form off the screen mid-submission (a popup closing over its own
31727
31750
  // running action, a slide moving on before it is answered). What
@@ -36226,6 +36249,16 @@ const useControlProps = (props, {
36226
36249
  if (!deferredCommand) {
36227
36250
  return;
36228
36251
  }
36252
+ if (uiStateController.optimistic) {
36253
+ // An optimistic button takes its action as done on its own word:
36254
+ // the command runs at once and the run goes on detached, the same
36255
+ // release the form counterpart gives the send (see --navi-send in
36256
+ // commands.js). Only a refused press has nothing to follow.
36257
+ if (completion.result !== false) {
36258
+ deferredCommand();
36259
+ }
36260
+ return;
36261
+ }
36229
36262
  runWhenActionSucceeded(completion, deferredCommand);
36230
36263
  };
36231
36264
  return {
@@ -48083,6 +48116,7 @@ const COMMAND_DEFAULT_PROPS_FACTORIES = {
48083
48116
  * replace?: boolean,
48084
48117
  * actionStandalone?: boolean,
48085
48118
  * actionAbortable?: boolean,
48119
+ * optimistic?: boolean,
48086
48120
  * [key: string]: any,
48087
48121
  * }>}
48088
48122
  * @param {boolean} [replace] Go where the press leads — an `href`, a
@@ -48126,6 +48160,12 @@ const COMMAND_DEFAULT_PROPS_FACTORIES = {
48126
48160
  * running, which the app watches from somewhere else; never for one holding
48127
48161
  * an answer the screen is the only place to read (see
48128
48162
  * docs/interactions.md#the-fourth-question-whose-wait-is-it).
48163
+ * @param {boolean} [optimistic] The action is taken as done on the button's own
48164
+ * word: no busy state, a second press queues behind the run, and its
48165
+ * `command` runs at once — the popup it sits in closes over the run, which
48166
+ * goes on behind. A failure then has no popup to be read in: `resetOnError`
48167
+ * puts the button back and the error callout is drawn on what surrounds the
48168
+ * closed popup (see docs/popup_open.md#the-popup-owns-its-open-state).
48129
48169
  * @param {boolean} [actionAbortable] The person waiting may give up on this
48130
48170
  * button's action: closing the popup the run holds calls it off and goes
48131
48171
  * through, instead of being refused. For a run whose answer may never come —
@@ -48165,7 +48205,7 @@ const COMMAND_DEFAULT_PROPS_FACTORIES = {
48165
48205
  * (it wears it itself) and loses the shrink under the finger, which has
48166
48206
  * nothing left to scale but the interactive area itself.
48167
48207
  */
48168
- const Button = createComponentResolver([ButtonFirstResolver, ButtonRouteResolver, ButtonCommandPropResolver, ButtonConfirmResolver, ButtonUI]);
48208
+ const Button = /*#__PURE__*/createComponentResolver([ButtonFirstResolver, ButtonRouteResolver, ButtonCommandPropResolver, ButtonConfirmResolver, ButtonUI]);
48169
48209
 
48170
48210
  installImportMetaCssBuild(import.meta);
48171
48211
  const css$O = /* css */`.navi_control_swap {
@@ -51533,7 +51573,7 @@ const InputTextualFirstResolver = props => {
51533
51573
  ...props
51534
51574
  });
51535
51575
  };
51536
- const InputTextual = createComponentResolver([InputTextualAsTextResolver, InputTextualFirstResolver, InputWithListResolver, InputWithSuggestionsResolver, InputTypeResolver, InputModeResolver, InputHeadlessResolver, InputTextualUI]);
51576
+ const InputTextual = /*#__PURE__*/createComponentResolver([InputTextualAsTextResolver, InputTextualFirstResolver, InputWithListResolver, InputWithSuggestionsResolver, InputTypeResolver, InputModeResolver, InputHeadlessResolver, InputTextualUI]);
51537
51577
  const RealInput = ({
51538
51578
  maxLength,
51539
51579
  ...domProps
@@ -65070,209 +65110,6 @@ const ListFirstResolver = props => {
65070
65110
  })
65071
65111
  });
65072
65112
  };
65073
-
65074
- /**
65075
- * List — generic virtualized scroll container.
65076
- * Items must use <List.Item> to participate in tracking.
65077
- *
65078
- * @type {import("ignore:preact").FunctionComponent<{
65079
- * selectable?: boolean,
65080
- * multiple?: boolean,
65081
- * deselectable?: boolean,
65082
- * maxLength?: number,
65083
- * maxLengthGuard?: number,
65084
- * parallelGuard?: number,
65085
- * standalone?: boolean,
65086
- * action?: (value: any) => void,
65087
- * uiAction?: (value: any) => void,
65088
- * popover?: boolean,
65089
- * role?: string,
65090
- * renderBudget?: number | string,
65091
- * renderBudgetSkipCheck?: boolean,
65092
- * virtualItemSize?: number,
65093
- * onListVisibleItemsChange?: (visibleItems: any[]) => void,
65094
- * scrolled?: "start" | "end" | number | {id: string, offset?: number},
65095
- * defaultScrolled?: "start" | "end" | number | {id: string, offset?: number},
65096
- * onScrolledChange?: (scrolled: {id: string, index: number, offset: number}) => void,
65097
- * scroller?: "self" | "parent" | "document" | Element | {current: Element},
65098
- * hoverWhileScrolling?: boolean,
65099
- * fallback?: import("ignore:preact").ComponentChildren,
65100
- * searchFallback?: import("ignore:preact").ComponentChildren,
65101
- * searchText?: string,
65102
- * searchNoMatchMode?: "remove" | "invisible_and_inert" | "muted",
65103
- * loading?: boolean,
65104
- * loadingFallback?: "skeleton" | "loader" | import("ignore:preact").ComponentChildren,
65105
- * loadingSkeletonCount?: number,
65106
- * renderSkeleton?: false | ((index: number) => import("ignore:preact").ComponentChildren),
65107
- * error?: boolean | import("ignore:preact").ComponentChildren,
65108
- * separator?: boolean | import("ignore:preact").ComponentChildren,
65109
- * itemTransition?: boolean,
65110
- * lockSize?: boolean,
65111
- * horizontal?: boolean,
65112
- * spacing?: string,
65113
- * columns?: string,
65114
- * itemColumns?: string,
65115
- * alignX?: string,
65116
- * alignY?: string,
65117
- * flexWrap?: boolean,
65118
- * expandX?: boolean,
65119
- * expandY?: boolean,
65120
- * expand?: boolean,
65121
- * children?: import("ignore:preact").ComponentChildren,
65122
- * [key: string]: any,
65123
- * }>}
65124
- * @param {string} [props.columns]
65125
- * The list's own columns: a `grid-template-columns` value the ITEMS are laid
65126
- * into — a sheet of icons (`repeat(auto-fill, minmax(2.5rem, 1fr))`), a row of
65127
- * choices (`repeat(3, minmax(0, 1fr))`). Each item takes one cell, and an item
65128
- * meant to take a whole line says so for itself
65129
- * (`style={{ gridColumn: "1 / -1" }}`). Several items to a line is a shape the
65130
- * list cannot virtualize — its render window and the room it holds for the
65131
- * rows it does not draw both count one item per line — so it is for a set of
65132
- * items the caller renders whole, not for a `<List.Items>` collection.
65133
- * @param {string} [props.itemColumns]
65134
- * The columns inside an ITEM: a `grid-template-columns` value each item fills
65135
- * with its own children, a table whose cells line up down the list. Every item
65136
- * becomes a subgrid row spanning all the columns, so a column is as wide as
65137
- * the widest cell in it among the rows actually in the DOM — real column
65138
- * sizing that stays right as the window moves. Rows of a table, then, where
65139
- * `columns` above is a grid of items; the two cannot both be set.
65140
- * @param {string} [props.alignX]
65141
- * Where the items sit across the track — `alignX="center"` centres a
65142
- * horizontal list's row of items inside a list wider than they are. Together
65143
- * with `alignY`, `align` and `flexWrap`, this reaches the `<ul>` holding the
65144
- * items rather than the frame drawn around it: the frame's only child is the
65145
- * scroll box, which fills it and has nothing to arrange.
65146
- * @param {boolean} [props.flexWrap]
65147
- * Lets a horizontal list's items fall to the next line instead of running
65148
- * past the edge — a row of choices under a `maxWidth`, say. Same caveat as
65149
- * `columns` above: a line holding several items is not virtualizable.
65150
- * @param {string} [props.overflow]
65151
- * `"visible"` lets the items paint outside the list — a check in a row's
65152
- * corner, a badge crossing the edge. A list clips by default, which is what
65153
- * its rounded corners and its scroll box need, and the two cannot both be
65154
- * true: asking for visible gives up the clipping, corners included.
65155
- * @param {boolean} [props.itemTransition]
65156
- * Names each row, so a change the application wraps in
65157
- * `document.startViewTransition` is seen row by row — rows moving to their new
65158
- * place, an arriving row appearing where it lands — instead of the list
65159
- * cross-fading as a block. The rows are drawn inside the list's own picture,
65160
- * so one coming from outside the visible part of the list is cut at the list's
65161
- * edge like any other overflow. Requires nested view transition groups
65162
- * (Chrome/Edge 140+): elsewhere the rows are left unnamed and the change
65163
- * simply happens — but the browser still names the document root, so the page
65164
- * cross-fades as a whole unless the application says otherwise, which is its
65165
- * call and not the list's:
65166
- * ```css
65167
- * @supports not (view-transition-group: contain) {
65168
- * :root { view-transition-name: none; }
65169
- * }
65170
- * ```
65171
- * @param {false|((index: number) => any)} [props.renderSkeleton]
65172
- * What a row on its way looks like — a row of the shape the real ones will
65173
- * have, so nothing moves when they arrive. Used for the rows a `<List.Items>`
65174
- * stands for and does not hold yet, and for the placeholder rows drawn while
65175
- * the whole list is `loading`. Defaults to a bare `<List.Item skeleton>`.
65176
- * @param {"skeleton"|"loader"|import("ignore:preact").ComponentChildren} [props.loadingFallback="skeleton"]
65177
- * What to display in place of the items while `loading` — that is, while
65178
- * there is nothing to show at all: `"skeleton"` renders
65179
- * `loadingSkeletonCount` placeholder rows (look:
65180
- * `renderSkeleton`), `"loader"` a single centered spinner, and
65181
- * anything else is rendered as-is in a row of its own. A falsy value
65182
- * displays nothing. A list that knows how many rows it will have has no use
65183
- * for this — see `<List.Items count>`, whose not-yet-loaded rows are drawn
65184
- * as skeletons in place, one per row, virtualized like the rest.
65185
- * @param {number} [props.loadingSkeletonCount=3]
65186
- * How many placeholder rows `loadingFallback="skeleton"` draws. `0` says the
65187
- * list is already known to be empty: the empty `fallback` shows right away
65188
- * rather than an empty frame, so nothing moves when the response arrives.
65189
- * @param {"start"|"end"|number|{id: string, offset?: number}} [props.defaultScrolled="start"]
65190
- * Where the list opens, after which the user owns the scroll. `"end"` is a
65191
- * thread read backwards — the last rows are the ones to show, and the ones
65192
- * asked for first. A number opens on that row of the collection. `{id,
65193
- * offset}` — what `onScrolledChange` hands out — opens on a NAMED row,
65194
- * `offset` pixels below where the row would land on its own: the row is asked
65195
- * for by name (see the range's own `around`), then put back by MEASURING it,
65196
- * so it lands where it was even if rows were inserted before it, and whatever
65197
- * the screen it was saved on. `offset: 0` is where a `scrollIntoView()` puts
65198
- * it — in front of the fixed bar the scroller gives room for, below the
65199
- * sticky header and the group label the row lives under — so nothing of that
65200
- * room has to be restated as a number by whoever asks.
65201
- * @param {"start"|"end"|number|{id: string, offset?: number}} [props.scrolled]
65202
- * The same, but held: the list goes back there every time this changes, even
65203
- * after the user has scrolled — the caller owns where the list is (see
65204
- * `defaultScrolled` for the uncontrolled form, and `open`/`defaultOpen`
65205
- * elsewhere in navi for the same pair). When the named row turns out not to
65206
- * exist — a message deleted since — the list opens at `defaultScrolled`
65207
- * instead.
65208
- *
65209
- * In every form the list holds itself there while it is still finding out
65210
- * how many rows there are and how tall one is, and lets go the moment the
65211
- * user reaches for the list.
65212
- * @param {(scrolled: {id: string, index: number, offset: number}) => void} [props.onScrolledChange]
65213
- * Where the list is, as the user scrolls: the row at the top of the view and
65214
- * how far below the place a row lands on its own (see `defaultScrolled`) it
65215
- * starts. Keep it to come back to it
65216
- * later through `scrolled`/`defaultScrolled` — an index would not do, since
65217
- * rows get inserted while a list is being read.
65218
- * @param {"self"|"parent"|"document"|Element|{current: Element}} [props.scroller="self"]
65219
- * Which box scrolls. `"self"` gives the list a scroll box of its own;
65220
- * `"parent"` makes it virtualize against the scrollable ancestor it lives in
65221
- * (the page, a panel) — no scroll box nested inside another one, no height
65222
- * to compute.
65223
- *
65224
- * `"parent"` finds that ancestor by measuring: the nearest one whose content
65225
- * actually overflows it, the page if none does. Declaring `overflow` is not
65226
- * enough to be picked (a box with `overflow-x: auto` that grows with its
65227
- * content computes `overflow-y: auto` without ever scrolling), and the
65228
- * answer is taken again as the geometry moves, so an ancestor that starts to
65229
- * scroll once it fills up is picked up then. When that is still not the box
65230
- * you mean, say so: `"document"`, or the element itself (a ref works) —
65231
- * nothing is guessed then.
65232
- * @param {boolean} [props.hoverWhileScrolling=false]
65233
- * Whether the rows still answer the pointer while the scroller they live in
65234
- * is moving. They do not by default: a scroll slides the rows under a
65235
- * motionless pointer, so the browser reports a hover on each of them, and
65236
- * the user asked to scroll, not to hover. The cost of taking them at face
65237
- * value is paid by whatever hover triggers — a highlight elsewhere in the
65238
- * tree, a prefetch, a map — at the worst moment, mid-scroll.
65239
- *
65240
- * Pass `true` for a list whose rows must stay live under the pointer while
65241
- * it scrolls. The trade of the default is the mirror one: right after a
65242
- * scroll, the row under the pointer lights up only once the pointer moves.
65243
- * @param {boolean} [props.deselectable]
65244
- * A single-select list allowed to hold nothing: the selected row, pressed
65245
- * again, lets go. Without it the list is a radio group — a choice, once
65246
- * made, moves to another row but never goes away. A `multiple` list toggles
65247
- * its rows already.
65248
- * @param {number} [props.maxLength]
65249
- * How many items a `selectable multiple` list accepts — the same word, and
65250
- * the same behaviour, as `maxLength` on a text field: a rule the list is
65251
- * judged against, not a wall. A longer selection is allowed to exist and is
65252
- * reported as invalid, which is what lets a value coming from elsewhere (an
65253
- * API, a URL) be shown and then corrected.
65254
- * @param {number} [props.maxLengthGuard]
65255
- * The same limit, enforced as the selection is made: while the list holds as
65256
- * many items as it accepts, the ones not selected go read-only — still
65257
- * pointable, focusable and pressable, answering `"[max] max."` instead of
65258
- * taking — and `uiAction` is not called. The selected ones stay takeable
65259
- * back, so a selection that arrived too long can always be brought back
65260
- * under the limit. Implies `maxLength` for validity.
65261
- * @param {number} [props.parallelGuard=4]
65262
- * How many runs the rows may have in flight at once, for a list whose rows
65263
- * carry their own `action` (a button per row). While that many are out, every
65264
- * row that is not running goes read-only and says how many it is waiting on;
65265
- * the next press is possible again as soon as one comes back. `Infinity`
65266
- * lifts it. Counts runs, not values — `maxLengthGuard` above is the one that
65267
- * says how many things the selection may hold.
65268
- * @param {boolean} [props.standalone]
65269
- * This list answers for itself: it does not register with the control group
65270
- * or picker around it, so its selection stays out of that value and nothing
65271
- * coming down — a distributed value, a reset — reaches it. What a popup whose
65272
- * one answer is spread over several lists says, so that none of them is taken
65273
- * for the answer itself.
65274
- */
65275
- const List = createComponentResolver([ListFirstResolver, ListSelectableResolver, ListUI]);
65276
65113
  const ListContent = ({
65277
65114
  role,
65278
65115
  fallback,
@@ -67489,14 +67326,13 @@ const LIST_ITEM_STYLE_CSS_VARS = {
67489
67326
  * search-driven scroll-to-top-match behavior. `matchRanges` are [start, end]
67490
67327
  * ranges highlighted via the CSS Highlight API.
67491
67328
  */
67492
- const ListItem = createComponentResolver([ListItemFirstResolver, ListItemRowResolver, ListItemSkeletonResolver, ListItemSelectableResolver, ListItemHeaderOrFooterResolver, ListItemPresentationResolver, ListItemUI],
67329
+ const ListItem = /*#__PURE__*/createComponentResolver([ListItemFirstResolver, ListItemRowResolver, ListItemSkeletonResolver, ListItemSelectableResolver, ListItemHeaderOrFooterResolver, ListItemPresentationResolver, ListItemUI],
67493
67330
  // Rendered by the hundred, and almost always with the same primitive props:
67494
67331
  // a selection change re-renders the list, and every row but two has nothing
67495
67332
  // to change.
67496
67333
  {
67497
67334
  pure: true
67498
67335
  });
67499
- List.Item = ListItem;
67500
67336
 
67501
67337
  // Everything the list knows about the collection while its children are being
67502
67338
  // rendered: how many rows it has in total, which of them are actually held, and
@@ -68256,7 +68092,6 @@ const ListItems = ({
68256
68092
  }
68257
68093
  return rows;
68258
68094
  };
68259
- List.Items = ListItems;
68260
68095
 
68261
68096
  // What is drawn where rows were asked for and never came: the sentence and the
68262
68097
  // way out, in the row itself — the rest of the list is fine, so replacing all
@@ -68857,6 +68692,219 @@ const resolveSeparatorVnode = (separator, gapIndex) => {
68857
68692
  }
68858
68693
  return separator;
68859
68694
  };
68695
+ const ListResolved = /*#__PURE__*/createComponentResolver([ListFirstResolver, ListSelectableResolver, ListUI]);
68696
+
68697
+ // Declared last because Item and Items are consts defined above, and they are
68698
+ // folded into the declaration rather than written onto List afterwards
68699
+ // (`List.Item = ListItem`): List is what createComponentResolver returns, an
68700
+ // object a bundler cannot see through, so a property assigned to it later is a
68701
+ // side effect it has to keep — and with it List, its rows and everything they
68702
+ // import, in a bundle that never renders a list. See Picker for the same shape.
68703
+ /**
68704
+ * List — generic virtualized scroll container.
68705
+ * Items must use <List.Item> to participate in tracking.
68706
+ *
68707
+ * @type {import("ignore:preact").FunctionComponent<{
68708
+ * selectable?: boolean,
68709
+ * multiple?: boolean,
68710
+ * deselectable?: boolean,
68711
+ * maxLength?: number,
68712
+ * maxLengthGuard?: number,
68713
+ * parallelGuard?: number,
68714
+ * standalone?: boolean,
68715
+ * action?: (value: any) => void,
68716
+ * uiAction?: (value: any) => void,
68717
+ * popover?: boolean,
68718
+ * role?: string,
68719
+ * renderBudget?: number | string,
68720
+ * renderBudgetSkipCheck?: boolean,
68721
+ * virtualItemSize?: number,
68722
+ * onListVisibleItemsChange?: (visibleItems: any[]) => void,
68723
+ * scrolled?: "start" | "end" | number | {id: string, offset?: number},
68724
+ * defaultScrolled?: "start" | "end" | number | {id: string, offset?: number},
68725
+ * onScrolledChange?: (scrolled: {id: string, index: number, offset: number}) => void,
68726
+ * scroller?: "self" | "parent" | "document" | Element | {current: Element},
68727
+ * hoverWhileScrolling?: boolean,
68728
+ * fallback?: import("ignore:preact").ComponentChildren,
68729
+ * searchFallback?: import("ignore:preact").ComponentChildren,
68730
+ * searchText?: string,
68731
+ * searchNoMatchMode?: "remove" | "invisible_and_inert" | "muted",
68732
+ * loading?: boolean,
68733
+ * loadingFallback?: "skeleton" | "loader" | import("ignore:preact").ComponentChildren,
68734
+ * loadingSkeletonCount?: number,
68735
+ * renderSkeleton?: false | ((index: number) => import("ignore:preact").ComponentChildren),
68736
+ * error?: boolean | import("ignore:preact").ComponentChildren,
68737
+ * separator?: boolean | import("ignore:preact").ComponentChildren,
68738
+ * itemTransition?: boolean,
68739
+ * lockSize?: boolean,
68740
+ * horizontal?: boolean,
68741
+ * spacing?: string,
68742
+ * columns?: string,
68743
+ * itemColumns?: string,
68744
+ * alignX?: string,
68745
+ * alignY?: string,
68746
+ * flexWrap?: boolean,
68747
+ * expandX?: boolean,
68748
+ * expandY?: boolean,
68749
+ * expand?: boolean,
68750
+ * children?: import("ignore:preact").ComponentChildren,
68751
+ * [key: string]: any,
68752
+ * }>}
68753
+ * @param {string} [props.columns]
68754
+ * The list's own columns: a `grid-template-columns` value the ITEMS are laid
68755
+ * into — a sheet of icons (`repeat(auto-fill, minmax(2.5rem, 1fr))`), a row of
68756
+ * choices (`repeat(3, minmax(0, 1fr))`). Each item takes one cell, and an item
68757
+ * meant to take a whole line says so for itself
68758
+ * (`style={{ gridColumn: "1 / -1" }}`). Several items to a line is a shape the
68759
+ * list cannot virtualize — its render window and the room it holds for the
68760
+ * rows it does not draw both count one item per line — so it is for a set of
68761
+ * items the caller renders whole, not for a `<List.Items>` collection.
68762
+ * @param {string} [props.itemColumns]
68763
+ * The columns inside an ITEM: a `grid-template-columns` value each item fills
68764
+ * with its own children, a table whose cells line up down the list. Every item
68765
+ * becomes a subgrid row spanning all the columns, so a column is as wide as
68766
+ * the widest cell in it among the rows actually in the DOM — real column
68767
+ * sizing that stays right as the window moves. Rows of a table, then, where
68768
+ * `columns` above is a grid of items; the two cannot both be set.
68769
+ * @param {string} [props.alignX]
68770
+ * Where the items sit across the track — `alignX="center"` centres a
68771
+ * horizontal list's row of items inside a list wider than they are. Together
68772
+ * with `alignY`, `align` and `flexWrap`, this reaches the `<ul>` holding the
68773
+ * items rather than the frame drawn around it: the frame's only child is the
68774
+ * scroll box, which fills it and has nothing to arrange.
68775
+ * @param {boolean} [props.flexWrap]
68776
+ * Lets a horizontal list's items fall to the next line instead of running
68777
+ * past the edge — a row of choices under a `maxWidth`, say. Same caveat as
68778
+ * `columns` above: a line holding several items is not virtualizable.
68779
+ * @param {string} [props.overflow]
68780
+ * `"visible"` lets the items paint outside the list — a check in a row's
68781
+ * corner, a badge crossing the edge. A list clips by default, which is what
68782
+ * its rounded corners and its scroll box need, and the two cannot both be
68783
+ * true: asking for visible gives up the clipping, corners included.
68784
+ * @param {boolean} [props.itemTransition]
68785
+ * Names each row, so a change the application wraps in
68786
+ * `document.startViewTransition` is seen row by row — rows moving to their new
68787
+ * place, an arriving row appearing where it lands — instead of the list
68788
+ * cross-fading as a block. The rows are drawn inside the list's own picture,
68789
+ * so one coming from outside the visible part of the list is cut at the list's
68790
+ * edge like any other overflow. Requires nested view transition groups
68791
+ * (Chrome/Edge 140+): elsewhere the rows are left unnamed and the change
68792
+ * simply happens — but the browser still names the document root, so the page
68793
+ * cross-fades as a whole unless the application says otherwise, which is its
68794
+ * call and not the list's:
68795
+ * ```css
68796
+ * @supports not (view-transition-group: contain) {
68797
+ * :root { view-transition-name: none; }
68798
+ * }
68799
+ * ```
68800
+ * @param {false|((index: number) => any)} [props.renderSkeleton]
68801
+ * What a row on its way looks like — a row of the shape the real ones will
68802
+ * have, so nothing moves when they arrive. Used for the rows a `<List.Items>`
68803
+ * stands for and does not hold yet, and for the placeholder rows drawn while
68804
+ * the whole list is `loading`. Defaults to a bare `<List.Item skeleton>`.
68805
+ * @param {"skeleton"|"loader"|import("ignore:preact").ComponentChildren} [props.loadingFallback="skeleton"]
68806
+ * What to display in place of the items while `loading` — that is, while
68807
+ * there is nothing to show at all: `"skeleton"` renders
68808
+ * `loadingSkeletonCount` placeholder rows (look:
68809
+ * `renderSkeleton`), `"loader"` a single centered spinner, and
68810
+ * anything else is rendered as-is in a row of its own. A falsy value
68811
+ * displays nothing. A list that knows how many rows it will have has no use
68812
+ * for this — see `<List.Items count>`, whose not-yet-loaded rows are drawn
68813
+ * as skeletons in place, one per row, virtualized like the rest.
68814
+ * @param {number} [props.loadingSkeletonCount=3]
68815
+ * How many placeholder rows `loadingFallback="skeleton"` draws. `0` says the
68816
+ * list is already known to be empty: the empty `fallback` shows right away
68817
+ * rather than an empty frame, so nothing moves when the response arrives.
68818
+ * @param {"start"|"end"|number|{id: string, offset?: number}} [props.defaultScrolled="start"]
68819
+ * Where the list opens, after which the user owns the scroll. `"end"` is a
68820
+ * thread read backwards — the last rows are the ones to show, and the ones
68821
+ * asked for first. A number opens on that row of the collection. `{id,
68822
+ * offset}` — what `onScrolledChange` hands out — opens on a NAMED row,
68823
+ * `offset` pixels below where the row would land on its own: the row is asked
68824
+ * for by name (see the range's own `around`), then put back by MEASURING it,
68825
+ * so it lands where it was even if rows were inserted before it, and whatever
68826
+ * the screen it was saved on. `offset: 0` is where a `scrollIntoView()` puts
68827
+ * it — in front of the fixed bar the scroller gives room for, below the
68828
+ * sticky header and the group label the row lives under — so nothing of that
68829
+ * room has to be restated as a number by whoever asks.
68830
+ * @param {"start"|"end"|number|{id: string, offset?: number}} [props.scrolled]
68831
+ * The same, but held: the list goes back there every time this changes, even
68832
+ * after the user has scrolled — the caller owns where the list is (see
68833
+ * `defaultScrolled` for the uncontrolled form, and `open`/`defaultOpen`
68834
+ * elsewhere in navi for the same pair). When the named row turns out not to
68835
+ * exist — a message deleted since — the list opens at `defaultScrolled`
68836
+ * instead.
68837
+ *
68838
+ * In every form the list holds itself there while it is still finding out
68839
+ * how many rows there are and how tall one is, and lets go the moment the
68840
+ * user reaches for the list.
68841
+ * @param {(scrolled: {id: string, index: number, offset: number}) => void} [props.onScrolledChange]
68842
+ * Where the list is, as the user scrolls: the row at the top of the view and
68843
+ * how far below the place a row lands on its own (see `defaultScrolled`) it
68844
+ * starts. Keep it to come back to it
68845
+ * later through `scrolled`/`defaultScrolled` — an index would not do, since
68846
+ * rows get inserted while a list is being read.
68847
+ * @param {"self"|"parent"|"document"|Element|{current: Element}} [props.scroller="self"]
68848
+ * Which box scrolls. `"self"` gives the list a scroll box of its own;
68849
+ * `"parent"` makes it virtualize against the scrollable ancestor it lives in
68850
+ * (the page, a panel) — no scroll box nested inside another one, no height
68851
+ * to compute.
68852
+ *
68853
+ * `"parent"` finds that ancestor by measuring: the nearest one whose content
68854
+ * actually overflows it, the page if none does. Declaring `overflow` is not
68855
+ * enough to be picked (a box with `overflow-x: auto` that grows with its
68856
+ * content computes `overflow-y: auto` without ever scrolling), and the
68857
+ * answer is taken again as the geometry moves, so an ancestor that starts to
68858
+ * scroll once it fills up is picked up then. When that is still not the box
68859
+ * you mean, say so: `"document"`, or the element itself (a ref works) —
68860
+ * nothing is guessed then.
68861
+ * @param {boolean} [props.hoverWhileScrolling=false]
68862
+ * Whether the rows still answer the pointer while the scroller they live in
68863
+ * is moving. They do not by default: a scroll slides the rows under a
68864
+ * motionless pointer, so the browser reports a hover on each of them, and
68865
+ * the user asked to scroll, not to hover. The cost of taking them at face
68866
+ * value is paid by whatever hover triggers — a highlight elsewhere in the
68867
+ * tree, a prefetch, a map — at the worst moment, mid-scroll.
68868
+ *
68869
+ * Pass `true` for a list whose rows must stay live under the pointer while
68870
+ * it scrolls. The trade of the default is the mirror one: right after a
68871
+ * scroll, the row under the pointer lights up only once the pointer moves.
68872
+ * @param {boolean} [props.deselectable]
68873
+ * A single-select list allowed to hold nothing: the selected row, pressed
68874
+ * again, lets go. Without it the list is a radio group — a choice, once
68875
+ * made, moves to another row but never goes away. A `multiple` list toggles
68876
+ * its rows already.
68877
+ * @param {number} [props.maxLength]
68878
+ * How many items a `selectable multiple` list accepts — the same word, and
68879
+ * the same behaviour, as `maxLength` on a text field: a rule the list is
68880
+ * judged against, not a wall. A longer selection is allowed to exist and is
68881
+ * reported as invalid, which is what lets a value coming from elsewhere (an
68882
+ * API, a URL) be shown and then corrected.
68883
+ * @param {number} [props.maxLengthGuard]
68884
+ * The same limit, enforced as the selection is made: while the list holds as
68885
+ * many items as it accepts, the ones not selected go read-only — still
68886
+ * pointable, focusable and pressable, answering `"[max] max."` instead of
68887
+ * taking — and `uiAction` is not called. The selected ones stay takeable
68888
+ * back, so a selection that arrived too long can always be brought back
68889
+ * under the limit. Implies `maxLength` for validity.
68890
+ * @param {number} [props.parallelGuard=4]
68891
+ * How many runs the rows may have in flight at once, for a list whose rows
68892
+ * carry their own `action` (a button per row). While that many are out, every
68893
+ * row that is not running goes read-only and says how many it is waiting on;
68894
+ * the next press is possible again as soon as one comes back. `Infinity`
68895
+ * lifts it. Counts runs, not values — `maxLengthGuard` above is the one that
68896
+ * says how many things the selection may hold.
68897
+ * @param {boolean} [props.standalone]
68898
+ * This list answers for itself: it does not register with the control group
68899
+ * or picker around it, so its selection stays out of that value and nothing
68900
+ * coming down — a distributed value, a reset — reaches it. What a popup whose
68901
+ * one answer is spread over several lists says, so that none of them is taken
68902
+ * for the answer itself.
68903
+ */
68904
+ const List = /*#__PURE__*/Object.assign(ListResolved, {
68905
+ Item: ListItem,
68906
+ Items: ListItems
68907
+ });
68860
68908
 
68861
68909
  const PickerNaviTime = props => {
68862
68910
  const Next = useNextResolver();
@@ -69789,7 +69837,7 @@ const PickerObject = props => {
69789
69837
  "navi-state-shape": "object"
69790
69838
  });
69791
69839
  };
69792
- const PickerObjectUI = asPickerOwnUI(() => {
69840
+ const PickerObjectUI = /*#__PURE__*/asPickerOwnUI(() => {
69793
69841
  const {
69794
69842
  value,
69795
69843
  placeholder
@@ -69824,7 +69872,7 @@ const PickerArray = props => {
69824
69872
  "navi-state-shape": "array"
69825
69873
  });
69826
69874
  };
69827
- const PickerArrayUI = asPickerOwnUI(() => {
69875
+ const PickerArrayUI = /*#__PURE__*/asPickerOwnUI(() => {
69828
69876
  const {
69829
69877
  value,
69830
69878
  placeholder,
@@ -69907,7 +69955,7 @@ const PickerColor = props => {
69907
69955
  ...props
69908
69956
  });
69909
69957
  };
69910
- const PickerColorUI = asPickerOwnUI(() => {
69958
+ const PickerColorUI = /*#__PURE__*/asPickerOwnUI(() => {
69911
69959
  const {
69912
69960
  value,
69913
69961
  placeholder
@@ -69931,7 +69979,7 @@ const PickerDate = props => {
69931
69979
  type: "date"
69932
69980
  });
69933
69981
  };
69934
- const PickerDateUI = asPickerOwnUI(props => {
69982
+ const PickerDateUI = /*#__PURE__*/asPickerOwnUI(props => {
69935
69983
  const {
69936
69984
  value,
69937
69985
  placeholder
@@ -69963,7 +70011,7 @@ const PickerMonth = props => {
69963
70011
  type: "month"
69964
70012
  });
69965
70013
  };
69966
- const PickerMonthUI = asPickerOwnUI(props => {
70014
+ const PickerMonthUI = /*#__PURE__*/asPickerOwnUI(props => {
69967
70015
  const {
69968
70016
  value,
69969
70017
  placeholder
@@ -69994,7 +70042,7 @@ const PickerWeek = props => {
69994
70042
  type: "week"
69995
70043
  });
69996
70044
  };
69997
- const PickerWeekUI = asPickerOwnUI(props => {
70045
+ const PickerWeekUI = /*#__PURE__*/asPickerOwnUI(props => {
69998
70046
  const {
69999
70047
  value,
70000
70048
  placeholder
@@ -70025,7 +70073,7 @@ const PickerTime = props => {
70025
70073
  type: "time"
70026
70074
  });
70027
70075
  };
70028
- const PickerTimeUI = asPickerOwnUI(props => {
70076
+ const PickerTimeUI = /*#__PURE__*/asPickerOwnUI(props => {
70029
70077
  const {
70030
70078
  value,
70031
70079
  placeholder
@@ -70056,7 +70104,7 @@ const PickerDuration = props => {
70056
70104
  "navi-input-type": "duration"
70057
70105
  });
70058
70106
  };
70059
- const PickerDurationUI = asPickerOwnUI(props => {
70107
+ const PickerDurationUI = /*#__PURE__*/asPickerOwnUI(props => {
70060
70108
  const {
70061
70109
  value,
70062
70110
  placeholder
@@ -70086,7 +70134,7 @@ const PickerDatetime = props => {
70086
70134
  type: "datetime-local"
70087
70135
  });
70088
70136
  };
70089
- const PickerDatetimeUI = asPickerOwnUI(props => {
70137
+ const PickerDatetimeUI = /*#__PURE__*/asPickerOwnUI(props => {
70090
70138
  const {
70091
70139
  value,
70092
70140
  placeholder
@@ -70115,7 +70163,7 @@ const PickerFile = props => {
70115
70163
  ...props
70116
70164
  });
70117
70165
  };
70118
- const PickerFileUI = asPickerOwnUI(() => {
70166
+ const PickerFileUI = /*#__PURE__*/asPickerOwnUI(() => {
70119
70167
  const {
70120
70168
  value,
70121
70169
  placeholder
@@ -71207,26 +71255,37 @@ const PickerFirstResolver = props => {
71207
71255
  ...props
71208
71256
  });
71209
71257
  };
71210
- const Picker = createComponentResolver([PickerFirstResolver, PickerPresetResolver, PickerConfirmResolver, PickerCustomResolver, PickerTypeResolver, PickerButton]);
71211
- Picker.Chip = PickerChip;
71212
- Picker.Clear = PickerClear;
71213
- Picker.UI = PickerDefaultUI;
71214
- Picker.UI.Date = PickerDateUI;
71215
- Picker.UI.Time = PickerTimeUI;
71216
- Picker.UI.Duration = PickerDurationUI;
71217
- Picker.UI.Week = PickerWeekUI;
71218
- Picker.UI.Datetime = PickerDatetimeUI;
71219
- Picker.UI.File = PickerFileUI;
71220
- Picker.UI.Color = PickerColorUI;
71221
- Picker.UI.Object = PickerObjectUI;
71222
- Picker.UI.Multiple = PickerArrayUI;
71223
- Picker.UI.PencilSvg = PencilSvg;
71224
- Picker.UI.ChevronDownSvg = ChevronDownSvg$1;
71225
- Picker.UI.ClockSvg = ClockSvg;
71226
- Picker.UI.DurationSvg = DurationSvg;
71227
- Picker.UI.CalendarSvg = CalendarSvg;
71228
- Picker.UI.FileSvg = FileSvg;
71229
- Picker.UI.ColorSvg = ColorSvg;
71258
+ const PickerResolved = /*#__PURE__*/createComponentResolver([PickerFirstResolver, PickerPresetResolver, PickerConfirmResolver, PickerCustomResolver, PickerTypeResolver, PickerButton]);
71259
+ const PickerUI = /*#__PURE__*/Object.assign(PickerDefaultUI, {
71260
+ Date: PickerDateUI,
71261
+ Time: PickerTimeUI,
71262
+ Duration: PickerDurationUI,
71263
+ Week: PickerWeekUI,
71264
+ Datetime: PickerDatetimeUI,
71265
+ File: PickerFileUI,
71266
+ Color: PickerColorUI,
71267
+ Object: PickerObjectUI,
71268
+ Multiple: PickerArrayUI,
71269
+ PencilSvg,
71270
+ ChevronDownSvg: ChevronDownSvg$1,
71271
+ ClockSvg,
71272
+ DurationSvg,
71273
+ CalendarSvg,
71274
+ FileSvg,
71275
+ ColorSvg
71276
+ });
71277
+
71278
+ // Assembled in pure expressions rather than written onto Picker afterwards
71279
+ // (`Picker.Chip = PickerChip`): what createComponentResolver returns is an object
71280
+ // a bundler cannot see through, so a property assigned to it later is a side
71281
+ // effect it has to keep — and with it Picker, every UI and everything they
71282
+ // import, in a bundle that never renders a picker. The annotations say these
71283
+ // calls only create and return; an unused Picker then disappears whole.
71284
+ const Picker = /*#__PURE__*/Object.assign(PickerResolved, {
71285
+ Chip: PickerChip,
71286
+ Clear: PickerClear,
71287
+ UI: PickerUI
71288
+ });
71230
71289
 
71231
71290
  installImportMetaCssBuild(import.meta);
71232
71291
  const css$s = /* css */`@layer navi {
@@ -77203,7 +77262,7 @@ const useTableDragContextValue = ({
77203
77262
  };
77204
77263
  }, [grabTarget, canChangeColumnOrder]);
77205
77264
  };
77206
- const TableDragCloneContainer = forwardRef((props, ref) => {
77265
+ const TableDragCloneContainer = /*#__PURE__*/forwardRef((props, ref) => {
77207
77266
  import.meta.css = [css$k, "@jsenv/navi/src/control/table/drag/table_drag.jsx"];
77208
77267
  const {
77209
77268
  tableId
@@ -77214,7 +77273,7 @@ const TableDragCloneContainer = forwardRef((props, ref) => {
77214
77273
  "data-overlay-for": tableId
77215
77274
  });
77216
77275
  });
77217
- const TableColumnDropPreview = forwardRef((props, ref) => {
77276
+ const TableColumnDropPreview = /*#__PURE__*/forwardRef((props, ref) => {
77218
77277
  return jsxs("div", {
77219
77278
  ref: ref,
77220
77279
  className: "navi_table_column_drop_preview",
@@ -79310,7 +79369,7 @@ const css$f = /* css */`.navi_table_ui {
79310
79369
  overflow: hidden;
79311
79370
  }
79312
79371
  `;
79313
- const TableUI = forwardRef((props, ref) => {
79372
+ const TableUI = /*#__PURE__*/forwardRef((props, ref) => {
79314
79373
  import.meta.css = [css$f, "@jsenv/navi/src/control/table/table_ui.jsx"];
79315
79374
  const {
79316
79375
  tableRef,