@jsenv/navi 0.29.152 → 0.29.154
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/jsenv_navi.js +118 -30
- package/dist/jsenv_navi.js.map +10 -7
- package/docs/error_handling.md +52 -1
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -5354,6 +5354,26 @@ const extractMessageAndRemainingProps = (props) => {
|
|
|
5354
5354
|
return [ownMessages, remaining];
|
|
5355
5355
|
};
|
|
5356
5356
|
|
|
5357
|
+
/**
|
|
5358
|
+
* The one-line form of a constraint message, or `undefined` when it has none.
|
|
5359
|
+
*
|
|
5360
|
+
* A message is not always a sentence: it can be an `Error`, or a whole element
|
|
5361
|
+
* (an `errorMapping` returning JSX to put a link inside the callout). Those
|
|
5362
|
+
* still render fine in the callout, but they have no one-line form, and
|
|
5363
|
+
* anything that needs one — a `title` attribute, a caller drawing its own
|
|
5364
|
+
* summary — must be given nothing rather than `String(message)`, which writes
|
|
5365
|
+
* "[object Object]" on the screen.
|
|
5366
|
+
*/
|
|
5367
|
+
const getMessageString = (message) => {
|
|
5368
|
+
if (typeof message === "string") {
|
|
5369
|
+
return message;
|
|
5370
|
+
}
|
|
5371
|
+
if (Error.isError(message)) {
|
|
5372
|
+
return message.message;
|
|
5373
|
+
}
|
|
5374
|
+
return undefined;
|
|
5375
|
+
};
|
|
5376
|
+
|
|
5357
5377
|
const getConstraintMessage = (
|
|
5358
5378
|
controller,
|
|
5359
5379
|
constraint,
|
|
@@ -12147,7 +12167,9 @@ const createControlValidation = (
|
|
|
12147
12167
|
typeof checkResult === "string"
|
|
12148
12168
|
? { message: checkResult }
|
|
12149
12169
|
: checkResult;
|
|
12150
|
-
constraintValidityInfo.messageString =
|
|
12170
|
+
constraintValidityInfo.messageString = getMessageString(
|
|
12171
|
+
constraintValidityInfo.message,
|
|
12172
|
+
);
|
|
12151
12173
|
debugUIState(
|
|
12152
12174
|
`${elementSig} constraint "${constraint.name}" failed -> ${constraintValidityInfo.message}`,
|
|
12153
12175
|
);
|
|
@@ -12189,10 +12211,15 @@ const createControlValidation = (
|
|
|
12189
12211
|
if (titleLess) {
|
|
12190
12212
|
const element = controller.ref.current;
|
|
12191
12213
|
if (element) {
|
|
12192
|
-
|
|
12193
|
-
|
|
12194
|
-
|
|
12195
|
-
|
|
12214
|
+
const { messageString } = activeFailedConstraintInfo;
|
|
12215
|
+
if (messageString) {
|
|
12216
|
+
element.setAttribute("title", messageString);
|
|
12217
|
+
} else {
|
|
12218
|
+
// The message has no one-line form (an element, typically): the
|
|
12219
|
+
// callout shows it whole, and the tooltip stays empty rather than
|
|
12220
|
+
// saying "[object Object]".
|
|
12221
|
+
element.removeAttribute("title");
|
|
12222
|
+
}
|
|
12196
12223
|
}
|
|
12197
12224
|
}
|
|
12198
12225
|
} else {
|
|
@@ -12589,6 +12616,13 @@ const useExecuteAction = (
|
|
|
12589
12616
|
let message;
|
|
12590
12617
|
if (errorMapping) {
|
|
12591
12618
|
const errorMappingResult = errorMapping(error);
|
|
12619
|
+
// Anything the chain below does not recognize — nothing returned, above
|
|
12620
|
+
// all — leaves `message` undefined and the control displays nothing. That
|
|
12621
|
+
// is the contract, and both halves of it are useful:
|
|
12622
|
+
// - returning nothing says "don't show this one", per error, where
|
|
12623
|
+
// actionErrorEffect="none" says it once for the whole control;
|
|
12624
|
+
// - not handling an error means returning it untouched (`return error`),
|
|
12625
|
+
// and it is then shown exactly as if there were no mapping at all.
|
|
12592
12626
|
if (typeof errorMappingResult === "string") {
|
|
12593
12627
|
message = errorMappingResult;
|
|
12594
12628
|
} else if (Error.isError(errorMappingResult)) {
|
|
@@ -61126,12 +61160,14 @@ const SKELETON_LIST_ITEM_CLASS = "navi_list_item_skeleton";
|
|
|
61126
61160
|
// inside it (see ListItems): a run draws the rows it frames and holds the room
|
|
61127
61161
|
// of the others.
|
|
61128
61162
|
const RenderWindowContext = createContext(null);
|
|
61129
|
-
// Carries List's own `
|
|
61163
|
+
// Carries List's own `itemColumns` prop (a grid-template-columns value, e.g.
|
|
61130
61164
|
// "1fr auto auto") down to each ListItem/filler/fallback so they can render
|
|
61131
61165
|
// as a subgrid row instead of a flex row — see ListItem's own use of this
|
|
61132
|
-
// context, and List's own `
|
|
61166
|
+
// context, and List's own `itemColumns` doc, for the full rationale (table-like
|
|
61133
61167
|
// column sizing that stays correct across a virtualized, windowed item set).
|
|
61134
|
-
|
|
61168
|
+
// List's own `columns` never reaches here: it lays the items themselves into
|
|
61169
|
+
// the track's columns, and an item then occupies a cell like anything else.
|
|
61170
|
+
const ListItemColumnsContext = createContext(null);
|
|
61135
61171
|
// Carries the separator element/function down to each ListItem so separators
|
|
61136
61172
|
// are only rendered between items that actually mount (post-filter, post-window).
|
|
61137
61173
|
const SeparatorContext = createContext(null);
|
|
@@ -61217,6 +61253,8 @@ const css$x = /* css */`@layer navi {
|
|
|
61217
61253
|
--x-list-scroll-spacing-bottom: calc(var(--list-footer-height, 0px) + var(--list-scroll-padding-bottom, 0px));
|
|
61218
61254
|
--x-list-scroll-spacing-left: calc(var(--list-header-width, 0px) + var(--list-scroll-padding-left, 0px));
|
|
61219
61255
|
--x-list-scroll-spacing-right: calc(var(--list-footer-width, 0px) + var(--list-scroll-padding-right, 0px));
|
|
61256
|
+
min-width: 0;
|
|
61257
|
+
max-width: var(--list-max-width, 100%);
|
|
61220
61258
|
background-color: var(--x-list-background-color);
|
|
61221
61259
|
border: var(--x-list-border-width) solid var(--x-list-border-color);
|
|
61222
61260
|
border-top-left-radius: var(--x-corner-top-left-radius, var(--x-list-border-radius));
|
|
@@ -61224,8 +61262,6 @@ const css$x = /* css */`@layer navi {
|
|
|
61224
61262
|
border-bottom-right-radius: var(--x-corner-bottom-right-radius, var(--x-list-border-radius));
|
|
61225
61263
|
border-bottom-left-radius: var(--x-corner-bottom-left-radius, var(--x-list-border-radius));
|
|
61226
61264
|
flex-direction: column;
|
|
61227
|
-
min-width: 0;
|
|
61228
|
-
max-width: 100%;
|
|
61229
61265
|
transition: opacity .2s;
|
|
61230
61266
|
display: flex;
|
|
61231
61267
|
overflow: hidden;
|
|
@@ -61251,7 +61287,6 @@ const css$x = /* css */`@layer navi {
|
|
|
61251
61287
|
min-width: inherit;
|
|
61252
61288
|
max-width: var(--list-max-width, inherit);
|
|
61253
61289
|
max-height: var(--list-max-height, inherit);
|
|
61254
|
-
flex-wrap: inherit;
|
|
61255
61290
|
overflow-anchor: none;
|
|
61256
61291
|
overscroll-behavior: inherit;
|
|
61257
61292
|
scrollbar-width: inherit;
|
|
@@ -61313,7 +61348,6 @@ const css$x = /* css */`@layer navi {
|
|
|
61313
61348
|
|
|
61314
61349
|
.navi_list {
|
|
61315
61350
|
box-sizing: border-box;
|
|
61316
|
-
flex-wrap: inherit;
|
|
61317
61351
|
outline: none;
|
|
61318
61352
|
margin: 0;
|
|
61319
61353
|
padding: 0;
|
|
@@ -61596,6 +61630,7 @@ const ListUI = props => {
|
|
|
61596
61630
|
hoverWhileScrolling = false,
|
|
61597
61631
|
lockSize,
|
|
61598
61632
|
columns,
|
|
61633
|
+
itemColumns,
|
|
61599
61634
|
searchText,
|
|
61600
61635
|
searchNoMatchMode = "remove",
|
|
61601
61636
|
loading,
|
|
@@ -61830,6 +61865,7 @@ const ListUI = props => {
|
|
|
61830
61865
|
styleCSSVars: LIST_STYLE_CSS_VARS,
|
|
61831
61866
|
pseudoClasses: LIST_PSEUDO_CLASSES,
|
|
61832
61867
|
hasChildUsingForwardedProps: true,
|
|
61868
|
+
childPropSet: LIST_TRACK_PROP_SET,
|
|
61833
61869
|
onnavi_request_scroll: e => {
|
|
61834
61870
|
if (!Object.hasOwn(e.detail, "id")) {
|
|
61835
61871
|
console.warn(`navi_request_scroll event is missing the "id" property in its detail.`, e);
|
|
@@ -61859,6 +61895,7 @@ const ListUI = props => {
|
|
|
61859
61895
|
horizontal: horizontal,
|
|
61860
61896
|
spacing: spacing,
|
|
61861
61897
|
columns: columns,
|
|
61898
|
+
itemColumns: itemColumns,
|
|
61862
61899
|
tracker: tracker,
|
|
61863
61900
|
renderWindow: renderWindow,
|
|
61864
61901
|
virtual: virtual,
|
|
@@ -61924,12 +61961,47 @@ const ListFirstResolver = props => {
|
|
|
61924
61961
|
* horizontal?: boolean,
|
|
61925
61962
|
* spacing?: string,
|
|
61926
61963
|
* columns?: string,
|
|
61964
|
+
* itemColumns?: string,
|
|
61965
|
+
* alignX?: string,
|
|
61966
|
+
* alignY?: string,
|
|
61967
|
+
* flexWrap?: boolean,
|
|
61927
61968
|
* expandX?: boolean,
|
|
61928
61969
|
* expandY?: boolean,
|
|
61929
61970
|
* expand?: boolean,
|
|
61930
61971
|
* children?: import("ignore:preact").ComponentChildren,
|
|
61931
61972
|
* [key: string]: any,
|
|
61932
61973
|
* }>}
|
|
61974
|
+
* @param {string} [props.columns]
|
|
61975
|
+
* The list's own columns: a `grid-template-columns` value the ITEMS are laid
|
|
61976
|
+
* into — a sheet of icons (`repeat(auto-fill, minmax(2.5rem, 1fr))`), a row of
|
|
61977
|
+
* choices (`repeat(3, minmax(0, 1fr))`). Each item takes one cell, and an item
|
|
61978
|
+
* meant to take a whole line says so for itself
|
|
61979
|
+
* (`style={{ gridColumn: "1 / -1" }}`). Several items to a line is a shape the
|
|
61980
|
+
* list cannot virtualize — its render window and the room it holds for the
|
|
61981
|
+
* rows it does not draw both count one item per line — so it is for a set of
|
|
61982
|
+
* items the caller renders whole, not for a `<List.Items>` collection.
|
|
61983
|
+
* @param {string} [props.itemColumns]
|
|
61984
|
+
* The columns inside an ITEM: a `grid-template-columns` value each item fills
|
|
61985
|
+
* with its own children, a table whose cells line up down the list. Every item
|
|
61986
|
+
* becomes a subgrid row spanning all the columns, so a column is as wide as
|
|
61987
|
+
* the widest cell in it among the rows actually in the DOM — real column
|
|
61988
|
+
* sizing that stays right as the window moves. Rows of a table, then, where
|
|
61989
|
+
* `columns` above is a grid of items; the two cannot both be set.
|
|
61990
|
+
* @param {string} [props.alignX]
|
|
61991
|
+
* Where the items sit across the track — `alignX="center"` centres a
|
|
61992
|
+
* horizontal list's row of items inside a list wider than they are. Together
|
|
61993
|
+
* with `alignY`, `align` and `flexWrap`, this reaches the `<ul>` holding the
|
|
61994
|
+
* items rather than the frame drawn around it: the frame's only child is the
|
|
61995
|
+
* scroll box, which fills it and has nothing to arrange.
|
|
61996
|
+
* @param {boolean} [props.flexWrap]
|
|
61997
|
+
* Lets a horizontal list's items fall to the next line instead of running
|
|
61998
|
+
* past the edge — a row of choices under a `maxWidth`, say. Same caveat as
|
|
61999
|
+
* `columns` above: a line holding several items is not virtualizable.
|
|
62000
|
+
* @param {string} [props.overflow]
|
|
62001
|
+
* `"visible"` lets the items paint outside the list — a check in a row's
|
|
62002
|
+
* corner, a badge crossing the edge. A list clips by default, which is what
|
|
62003
|
+
* its rounded corners and its scroll box need, and the two cannot both be
|
|
62004
|
+
* true: asking for visible gives up the clipping, corners included.
|
|
61933
62005
|
* @param {boolean} [props.itemTransition]
|
|
61934
62006
|
* Names each row, so a change the application wraps in
|
|
61935
62007
|
* `document.startViewTransition` is seen row by row — rows moving to their new
|
|
@@ -62053,6 +62125,7 @@ const ListContent = ({
|
|
|
62053
62125
|
horizontal,
|
|
62054
62126
|
spacing,
|
|
62055
62127
|
columns,
|
|
62128
|
+
itemColumns,
|
|
62056
62129
|
tracker,
|
|
62057
62130
|
renderWindow,
|
|
62058
62131
|
virtual,
|
|
@@ -62098,6 +62171,7 @@ const ListContent = ({
|
|
|
62098
62171
|
horizontal: horizontal,
|
|
62099
62172
|
spacing: spacing,
|
|
62100
62173
|
columns: columns,
|
|
62174
|
+
itemColumns: itemColumns,
|
|
62101
62175
|
...listProps,
|
|
62102
62176
|
tracker: tracker,
|
|
62103
62177
|
renderWindow: renderWindow,
|
|
@@ -62109,6 +62183,12 @@ const ListContent = ({
|
|
|
62109
62183
|
})
|
|
62110
62184
|
});
|
|
62111
62185
|
};
|
|
62186
|
+
// Where the items sit is a question about the track (the <ul>), not about the
|
|
62187
|
+
// frame around it: the frame's only child is the scroll box, which fills it and
|
|
62188
|
+
// has nothing to arrange. Box moves these out of the container's own props and
|
|
62189
|
+
// into BoxForwardedPropsContext, which ListContent reads and hands to the <ul>
|
|
62190
|
+
// — the same route `horizontal`, `spacing` and the column props take by hand.
|
|
62191
|
+
const LIST_TRACK_PROP_SET = new Set(["align", "alignX", "alignY", "flexWrap"]);
|
|
62112
62192
|
const LIST_STYLE_CSS_VARS = {
|
|
62113
62193
|
maxHeight: "--list-max-height",
|
|
62114
62194
|
maxWidth: "--list-max-width",
|
|
@@ -63580,6 +63660,7 @@ const UnorderedList = ({
|
|
|
63580
63660
|
horizontal,
|
|
63581
63661
|
spacing,
|
|
63582
63662
|
columns,
|
|
63663
|
+
itemColumns,
|
|
63583
63664
|
children,
|
|
63584
63665
|
...rest
|
|
63585
63666
|
}) => {
|
|
@@ -63588,11 +63669,19 @@ const UnorderedList = ({
|
|
|
63588
63669
|
// A loading state drawing nothing keeps the message: an announced count of 0
|
|
63589
63670
|
// already tells us the list is empty (see ListUI's loadingPlaceholderShown).
|
|
63590
63671
|
const suppressFallback = loadingPlaceholderShown || Boolean(error);
|
|
63672
|
+
|
|
63673
|
+
// One track, one template: the two column props are the same
|
|
63674
|
+
// grid-template-columns seen from either end, and only differ in what an
|
|
63675
|
+
// item does with it (see useItemColumnsOverrideProps).
|
|
63676
|
+
if (columns && itemColumns) {
|
|
63677
|
+
console.warn(`List: "columns" and "itemColumns" cannot both be set — they define the same track. "columns" lays the items into the columns; "itemColumns" gives each item the columns its own children fill.`);
|
|
63678
|
+
}
|
|
63679
|
+
const trackColumns = columns || itemColumns;
|
|
63591
63680
|
return jsxs(Box, {
|
|
63592
63681
|
as: "ul",
|
|
63593
|
-
flex:
|
|
63594
|
-
grid:
|
|
63595
|
-
gridTemplateColumns:
|
|
63682
|
+
flex: trackColumns ? undefined : horizontal ? "x" : "y",
|
|
63683
|
+
grid: trackColumns ? true : undefined,
|
|
63684
|
+
gridTemplateColumns: trackColumns,
|
|
63596
63685
|
...rest,
|
|
63597
63686
|
spacing: spacing,
|
|
63598
63687
|
baseClassName: "navi_list",
|
|
@@ -63614,8 +63703,8 @@ const UnorderedList = ({
|
|
|
63614
63703
|
value: virtual,
|
|
63615
63704
|
children: jsx(ListRowContext.Provider, {
|
|
63616
63705
|
value: null,
|
|
63617
|
-
children: jsx(
|
|
63618
|
-
value: columns || null,
|
|
63706
|
+
children: jsx(ListItemColumnsContext.Provider, {
|
|
63707
|
+
value: columns ? null : itemColumns || null,
|
|
63619
63708
|
children: jsx(ListDeclaredChildren, {
|
|
63620
63709
|
children: children
|
|
63621
63710
|
})
|
|
@@ -63684,8 +63773,8 @@ const VirtualFiller = ({
|
|
|
63684
63773
|
});
|
|
63685
63774
|
};
|
|
63686
63775
|
|
|
63687
|
-
// List's own `
|
|
63688
|
-
// a subgrid row instead of a flex row: its own children become direct grid
|
|
63776
|
+
// List's own `itemColumns` prop (see ListItemColumnsContext) turns a list item
|
|
63777
|
+
// into a subgrid row instead of a flex row: its own children become direct grid
|
|
63689
63778
|
// items of List's own <ul>, so column widths are computed from whichever
|
|
63690
63779
|
// rows are actually in the DOM (the currently-windowed items plus the
|
|
63691
63780
|
// always-mounted header/footer) — real grid/table column sizing, not a
|
|
@@ -63695,11 +63784,10 @@ const VirtualFiller = ({
|
|
|
63695
63784
|
// this they'd silently stay flex rows and break column alignment against
|
|
63696
63785
|
// the rest of the grid). `flex` is force-cleared here because Box picks
|
|
63697
63786
|
// flex over grid when both are set (see box.jsx's own boxFlow resolution),
|
|
63698
|
-
// so a caller-provided `flex` prop
|
|
63699
|
-
|
|
63700
|
-
const
|
|
63701
|
-
|
|
63702
|
-
if (!columns) {
|
|
63787
|
+
// so a caller-provided `flex` prop would otherwise silently win over this.
|
|
63788
|
+
const useItemColumnsOverrideProps = callerStyle => {
|
|
63789
|
+
const itemColumns = useContext(ListItemColumnsContext);
|
|
63790
|
+
if (!itemColumns) {
|
|
63703
63791
|
return undefined;
|
|
63704
63792
|
}
|
|
63705
63793
|
return {
|
|
@@ -63763,11 +63851,11 @@ const ListItemPresentationResolver = props => {
|
|
|
63763
63851
|
});
|
|
63764
63852
|
};
|
|
63765
63853
|
const ListItemPresentation = props => {
|
|
63766
|
-
const
|
|
63854
|
+
const itemColumnsOverrideProps = useItemColumnsOverrideProps(props.style);
|
|
63767
63855
|
return jsx(Box, {
|
|
63768
63856
|
as: "li",
|
|
63769
63857
|
...props,
|
|
63770
|
-
...
|
|
63858
|
+
...itemColumnsOverrideProps
|
|
63771
63859
|
});
|
|
63772
63860
|
};
|
|
63773
63861
|
// A <List.Item skeleton> — a non-interactive placeholder row shown while a list
|
|
@@ -63797,14 +63885,14 @@ const ListItemSkeleton = props => {
|
|
|
63797
63885
|
paddingY = "s",
|
|
63798
63886
|
...rest
|
|
63799
63887
|
} = props;
|
|
63800
|
-
const
|
|
63888
|
+
const itemColumnsOverrideProps = useItemColumnsOverrideProps(rest.style);
|
|
63801
63889
|
return jsx(Box, {
|
|
63802
63890
|
as: "li",
|
|
63803
63891
|
role: "presentation",
|
|
63804
63892
|
"aria-hidden": "true",
|
|
63805
63893
|
paddingY: paddingY,
|
|
63806
63894
|
...rest,
|
|
63807
|
-
...
|
|
63895
|
+
...itemColumnsOverrideProps,
|
|
63808
63896
|
baseClassName: `navi_list_item ${SKELETON_LIST_ITEM_CLASS}`,
|
|
63809
63897
|
children: children ?? jsx(Text, {
|
|
63810
63898
|
loading: true
|
|
@@ -63965,7 +64053,7 @@ const ListItemReal = props => {
|
|
|
63965
64053
|
// CSS Highlight API: mark matching text ranges from matchInfo.matchRanges,
|
|
63966
64054
|
// if any (there is no standalone highlight prop — see ListItem's own doc).
|
|
63967
64055
|
useSearchHighlight(ref, matchInfo?.matchRanges, [children, hidden]);
|
|
63968
|
-
const
|
|
64056
|
+
const itemColumnsOverrideProps = useItemColumnsOverrideProps(rest.style);
|
|
63969
64057
|
// <List itemTransition>: the row carries the name it is to be paired by, and
|
|
63970
64058
|
// the stylesheet turns it into a view-transition-name where a browser can
|
|
63971
64059
|
// draw it inside the list (see the @supports block in the css above).
|
|
@@ -64034,7 +64122,7 @@ const ListItemReal = props => {
|
|
|
64034
64122
|
id: id,
|
|
64035
64123
|
"navi-list-item-real": "",
|
|
64036
64124
|
...rest,
|
|
64037
|
-
...
|
|
64125
|
+
...itemColumnsOverrideProps,
|
|
64038
64126
|
index: undefined,
|
|
64039
64127
|
selected: undefined
|
|
64040
64128
|
// We use aria-hidden and not hidden because hidden would be forced to
|