@jsenv/navi 0.27.50 → 0.27.52
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 +187 -123
- package/dist/jsenv_navi.js.map +14 -15
- package/dist/jsenv_navi_side_effects.js +3 -0
- package/dist/jsenv_navi_side_effects.js.map +2 -2
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -8869,11 +8869,11 @@ import.meta.css = [/* css */`
|
|
|
8869
8869
|
}
|
|
8870
8870
|
}
|
|
8871
8871
|
/*
|
|
8872
|
-
|
|
8873
|
-
It's very common to declare a display on component as follow
|
|
8872
|
+
To set display on component, code usually do something like:
|
|
8874
8873
|
.component_class { display: component_display; }
|
|
8875
8874
|
|
|
8876
|
-
|
|
8875
|
+
It overrides the default behavior of [hidden] attribute!
|
|
8876
|
+
This needs to be explicitly handled with:
|
|
8877
8877
|
.component_class[hidden] { display: none; }
|
|
8878
8878
|
|
|
8879
8879
|
To avoid this extra work and potential mistakes we force the default behavior of [hidden] attribute.
|
|
@@ -22459,7 +22459,7 @@ const useUIStateController = (
|
|
|
22459
22459
|
const currentUIState = uiStateController.uiState;
|
|
22460
22460
|
const stateIsTheSame = compareTwoJsValues(newUIState, currentUIState);
|
|
22461
22461
|
if (stateIsTheSame) {
|
|
22462
|
-
if (controlType === "button") {
|
|
22462
|
+
if (controlType === "button" || controlType === "link") {
|
|
22463
22463
|
if (!isInternalEvent(e)) {
|
|
22464
22464
|
uiStateController.onUIAction(e);
|
|
22465
22465
|
}
|
|
@@ -23071,6 +23071,9 @@ const useUIGroupStateController = (
|
|
|
23071
23071
|
if (childUIStateController.controlType === "button") {
|
|
23072
23072
|
return false;
|
|
23073
23073
|
}
|
|
23074
|
+
if (childUIStateController.controlType === "link") {
|
|
23075
|
+
return false;
|
|
23076
|
+
}
|
|
23074
23077
|
return true;
|
|
23075
23078
|
};
|
|
23076
23079
|
|
|
@@ -23367,6 +23370,9 @@ const useUIGroupStateController = (
|
|
|
23367
23370
|
if (childUIStateController.controlType === "button") {
|
|
23368
23371
|
continue;
|
|
23369
23372
|
}
|
|
23373
|
+
if (childUIStateController.controlType === "link") {
|
|
23374
|
+
continue;
|
|
23375
|
+
}
|
|
23370
23376
|
childUIStateController.clearUIState(propagateDownClearEvent);
|
|
23371
23377
|
}
|
|
23372
23378
|
onChange(e, { notifyExternal: true });
|
|
@@ -23465,6 +23471,9 @@ const useUIFacadeStateController = (props, realUIStateController) => {
|
|
|
23465
23471
|
if (childController.controlType === "button") {
|
|
23466
23472
|
return false;
|
|
23467
23473
|
}
|
|
23474
|
+
if (childController.controlType === "link") {
|
|
23475
|
+
return false;
|
|
23476
|
+
}
|
|
23468
23477
|
if (childController.controlType === "facade") {
|
|
23469
23478
|
return false;
|
|
23470
23479
|
}
|
|
@@ -32091,8 +32100,12 @@ const getNowHoursRoundedToStep = (stepMinutes, offsetMinutes = 0) => {
|
|
|
32091
32100
|
return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`;
|
|
32092
32101
|
};
|
|
32093
32102
|
|
|
32094
|
-
// Maps validity type names → navi input type names
|
|
32103
|
+
// Maps validity type names → navi input type names.
|
|
32104
|
+
// Numeric signal types must not fall through to the native type="number"
|
|
32105
|
+
// (which adds spinner buttons and has poor UX) — they map to navi_number instead.
|
|
32095
32106
|
const VALIDITY_TYPE_TO_INPUT_TYPE = {
|
|
32107
|
+
number: "navi_number",
|
|
32108
|
+
integer: "navi_number",
|
|
32096
32109
|
percentage: "navi_percentage",
|
|
32097
32110
|
};
|
|
32098
32111
|
|
|
@@ -36318,6 +36331,10 @@ const PickerCustom = props => {
|
|
|
36318
36331
|
Object.assign(pickerProps, {
|
|
36319
36332
|
eventReactionDefinitions: {
|
|
36320
36333
|
mouseDown: e => {
|
|
36334
|
+
const popupEl = popupRef.current;
|
|
36335
|
+
if (popupEl && popupEl.contains(e.target)) {
|
|
36336
|
+
return null;
|
|
36337
|
+
}
|
|
36321
36338
|
if (expandedRef.current) {
|
|
36322
36339
|
return {
|
|
36323
36340
|
name: "mousedown to close picker",
|
|
@@ -36334,6 +36351,10 @@ const PickerCustom = props => {
|
|
|
36334
36351
|
};
|
|
36335
36352
|
},
|
|
36336
36353
|
click: e => {
|
|
36354
|
+
const popupEl = popupRef.current;
|
|
36355
|
+
if (popupEl && popupEl.contains(e.target)) {
|
|
36356
|
+
return null;
|
|
36357
|
+
}
|
|
36337
36358
|
// When a label is clicked it transfers focus to the select
|
|
36338
36359
|
// in that case we want to open it (otherwise we have already opened on mousedown interaction)
|
|
36339
36360
|
return {
|
|
@@ -36352,40 +36373,6 @@ const PickerCustom = props => {
|
|
|
36352
36373
|
}
|
|
36353
36374
|
}
|
|
36354
36375
|
});
|
|
36355
|
-
Object.assign(popupProps, {
|
|
36356
|
-
onMouseDown: e => {
|
|
36357
|
-
if (e.button !== 0) {
|
|
36358
|
-
return;
|
|
36359
|
-
}
|
|
36360
|
-
// mousedown inside popover should not bubble to the select (would re-open it if that mousedown closes it)
|
|
36361
|
-
debugPopup(e, `"mousedown" received on popup -> prevent bubbling to picker (e.stopPropagation())`);
|
|
36362
|
-
e.stopPropagation();
|
|
36363
|
-
},
|
|
36364
|
-
onClick: e => {
|
|
36365
|
-
if (e.button !== 0) {
|
|
36366
|
-
return;
|
|
36367
|
-
}
|
|
36368
|
-
// click inside popover should not bubble to the picker (would re-open it if that click closes it)
|
|
36369
|
-
debugPopup(e, `"click" received on popup -> prevent bubbling to picker (e.stopPropagation())`);
|
|
36370
|
-
e.stopPropagation();
|
|
36371
|
-
// Here we can't preventDefault because the click might be needed to check a radio for instance.
|
|
36372
|
-
// As a result we have to let it go through which means it could trigger form submission
|
|
36373
|
-
// but we've put type="button" on the picker to ensure it can't submit the form
|
|
36374
|
-
// so browser won't submit eventual form for clicks inside the popover/dialog
|
|
36375
|
-
// e.preventDefault();
|
|
36376
|
-
},
|
|
36377
|
-
onKeyDown: e => {
|
|
36378
|
-
// some keys pressed inside popover should not reach the picker button
|
|
36379
|
-
// (like enter that would try to request action of closest form otherwise for instance)
|
|
36380
|
-
if (e.key === "Enter") {
|
|
36381
|
-
debugPopup(e, `"enter" received on popup -> prevent bubbling to picker (e.stopPropagation())`);
|
|
36382
|
-
e.stopPropagation();
|
|
36383
|
-
// preventDefault prevents the browser from dispatching a "click" on the
|
|
36384
|
-
// picker button when focus moves to it synchronously during enterEffect
|
|
36385
|
-
e.preventDefault();
|
|
36386
|
-
}
|
|
36387
|
-
}
|
|
36388
|
-
});
|
|
36389
36376
|
}
|
|
36390
36377
|
}
|
|
36391
36378
|
if (mode === "popover") {
|
|
@@ -37257,7 +37244,7 @@ const createItemTracker = (onChange) => {
|
|
|
37257
37244
|
const visibleItemsSignal = signal([]);
|
|
37258
37245
|
const countSignal = signal(0);
|
|
37259
37246
|
const visibleCountSignal = signal(0);
|
|
37260
|
-
const
|
|
37247
|
+
const noMatchCountSignal = signal(0);
|
|
37261
37248
|
|
|
37262
37249
|
let notifyScheduled = false;
|
|
37263
37250
|
const runNotify = () => {
|
|
@@ -37279,7 +37266,7 @@ const createItemTracker = (onChange) => {
|
|
|
37279
37266
|
let visibleItemsChanged = false;
|
|
37280
37267
|
const allItems = [];
|
|
37281
37268
|
const visibleItems = [];
|
|
37282
|
-
let
|
|
37269
|
+
let newNoMatchCount = 0;
|
|
37283
37270
|
for (let i = 0; i < allOrderedKeys.length; i++) {
|
|
37284
37271
|
const key = allOrderedKeys[i];
|
|
37285
37272
|
const item = allRegistrations.get(key);
|
|
@@ -37288,12 +37275,12 @@ const createItemTracker = (onChange) => {
|
|
|
37288
37275
|
if (!allItemsChanged && item !== prevAllItems[i]) {
|
|
37289
37276
|
allItemsChanged = true;
|
|
37290
37277
|
}
|
|
37291
|
-
if (
|
|
37278
|
+
if (item.match === false) {
|
|
37279
|
+
newNoMatchCount++;
|
|
37280
|
+
}
|
|
37281
|
+
if (!item.hidden) {
|
|
37292
37282
|
const visibleIdx = visibleItems.length;
|
|
37293
37283
|
visibleItems.push(item);
|
|
37294
|
-
if (item.matchScore > 0) {
|
|
37295
|
-
newMatchCount++;
|
|
37296
|
-
}
|
|
37297
37284
|
if (!visibleItemsChanged && item !== prevVisibleItems[visibleIdx]) {
|
|
37298
37285
|
visibleItemsChanged = true;
|
|
37299
37286
|
}
|
|
@@ -37315,9 +37302,9 @@ const createItemTracker = (onChange) => {
|
|
|
37315
37302
|
visibleItemsSignal.value = visibleItems;
|
|
37316
37303
|
someChange = true;
|
|
37317
37304
|
}
|
|
37318
|
-
const
|
|
37319
|
-
if (
|
|
37320
|
-
|
|
37305
|
+
const noMatchCountModified = noMatchCountSignal.peek() !== newNoMatchCount;
|
|
37306
|
+
if (noMatchCountModified) {
|
|
37307
|
+
noMatchCountSignal.value = newNoMatchCount;
|
|
37321
37308
|
someChange = true;
|
|
37322
37309
|
}
|
|
37323
37310
|
if (someChange) {
|
|
@@ -37516,7 +37503,7 @@ const createItemTracker = (onChange) => {
|
|
|
37516
37503
|
visibleItemsSignal,
|
|
37517
37504
|
countSignal,
|
|
37518
37505
|
visibleCountSignal,
|
|
37519
|
-
|
|
37506
|
+
noMatchCountSignal,
|
|
37520
37507
|
_flushSync,
|
|
37521
37508
|
};
|
|
37522
37509
|
};
|
|
@@ -37583,8 +37570,8 @@ installImportMetaCssBuild(import.meta);const css$n = /* css */`
|
|
|
37583
37570
|
--list-item-outline-color: var(--navi-focus-outline-color);
|
|
37584
37571
|
/* Focus outline end */
|
|
37585
37572
|
--list-item-border-color: var(--navi-control-border-color);
|
|
37586
|
-
--list-item-padding-x-default: var(--navi-
|
|
37587
|
-
--list-item-padding-y-default: var(--navi-
|
|
37573
|
+
--list-item-padding-x-default: var(--navi-list-item-padding-x-default);
|
|
37574
|
+
--list-item-padding-y-default: var(--navi-list-item-padding-y-default);
|
|
37588
37575
|
|
|
37589
37576
|
/* Hover (mouse) */
|
|
37590
37577
|
--list-item-background-color-hover: light-dark(#f5f5f5, #2a2a2a);
|
|
@@ -37633,6 +37620,14 @@ installImportMetaCssBuild(import.meta);const css$n = /* css */`
|
|
|
37633
37620
|
}
|
|
37634
37621
|
}
|
|
37635
37622
|
|
|
37623
|
+
.navi_list_container[navi-selectable] {
|
|
37624
|
+
.navi_list_fallback,
|
|
37625
|
+
.navi_list_no_match_fallback {
|
|
37626
|
+
--list-item-padding-x-default: inherit;
|
|
37627
|
+
--list-item-padding-y-default: inherit;
|
|
37628
|
+
}
|
|
37629
|
+
}
|
|
37630
|
+
|
|
37636
37631
|
.navi_list_item[navi-selectable] {
|
|
37637
37632
|
--list-item-padding-x-default: inherit;
|
|
37638
37633
|
--list-item-padding-y-default: inherit;
|
|
@@ -37642,6 +37637,13 @@ installImportMetaCssBuild(import.meta);const css$n = /* css */`
|
|
|
37642
37637
|
outline-offset: var(--list-item-outline-offset);
|
|
37643
37638
|
cursor: var(--x-list-item-cursor);
|
|
37644
37639
|
|
|
37640
|
+
.navi_checkbox {
|
|
37641
|
+
--margin: 0;
|
|
37642
|
+
}
|
|
37643
|
+
.navi_radio {
|
|
37644
|
+
--margin: 0;
|
|
37645
|
+
}
|
|
37646
|
+
|
|
37645
37647
|
&[navi-selectable] {
|
|
37646
37648
|
user-select: none;
|
|
37647
37649
|
}
|
|
@@ -38253,6 +38255,11 @@ const applySearchHighlight = (el, highlight) => {
|
|
|
38253
38255
|
installImportMetaCssBuild(import.meta);const ListItemTrackerContext = createContext(null);
|
|
38254
38256
|
const GroupItemTrackerContext = createContext(null);
|
|
38255
38257
|
const PendingScrollRefContext = createContext(null);
|
|
38258
|
+
// Controls how List.Item behaves when match=false (set via List searchNoMatchMode prop):
|
|
38259
|
+
// "remove" — remove from DOM (default)
|
|
38260
|
+
// "invisible_and_inert" — keep in DOM, invisible and non-interactive (preserves layout, no content visible)
|
|
38261
|
+
// "muted" — keep in DOM, visible but opacified and still interactive
|
|
38262
|
+
const SearchNoMatchModeContext = createContext("remove");
|
|
38256
38263
|
|
|
38257
38264
|
// When total rendered items exceeds renderBudget, a render window [start, end)
|
|
38258
38265
|
// is activated to cap the number of DOM nodes. Items outside the window return
|
|
@@ -38454,6 +38461,14 @@ const css$m = /* css */`
|
|
|
38454
38461
|
scroll-margin-right: var(--x-list-scroll-spacing-right);
|
|
38455
38462
|
scroll-margin-bottom: var(--x-list-scroll-spacing-bottom);
|
|
38456
38463
|
scroll-margin-left: var(--x-list-scroll-spacing-left);
|
|
38464
|
+
|
|
38465
|
+
&[aria-hidden="true"] {
|
|
38466
|
+
opacity: 0;
|
|
38467
|
+
}
|
|
38468
|
+
|
|
38469
|
+
&[navi-muted] {
|
|
38470
|
+
opacity: 0.35;
|
|
38471
|
+
}
|
|
38457
38472
|
}
|
|
38458
38473
|
|
|
38459
38474
|
/* Virtual scroll fillers — must remain invisible.
|
|
@@ -38479,10 +38494,10 @@ const css$m = /* css */`
|
|
|
38479
38494
|
Using order ensures fallbacks always appear after items regardless of DOM order.
|
|
38480
38495
|
matchFallback intentionally shares the same order as fallback so it appears
|
|
38481
38496
|
at the same visual position — after an input if present but before any items
|
|
38482
|
-
still displayed (non-matching items remain in DOM
|
|
38497
|
+
still displayed (non-matching items remain in DOM, invisible_and_inert or muted):
|
|
38483
38498
|
1. Input (sticky header, order: -2)
|
|
38484
|
-
2.
|
|
38485
|
-
3.
|
|
38499
|
+
2. searchFallback (order: -1)
|
|
38500
|
+
3. invisible/dim items (regular order, after DOM flow)
|
|
38486
38501
|
4. HOT FIX OF THE DEAD for bottom filler + preact issue: order: 1
|
|
38487
38502
|
5. sticky footer (order: 2)
|
|
38488
38503
|
*/
|
|
@@ -38496,12 +38511,39 @@ const css$m = /* css */`
|
|
|
38496
38511
|
order: -2;
|
|
38497
38512
|
}
|
|
38498
38513
|
.navi_list_fallback,
|
|
38499
|
-
.
|
|
38514
|
+
.navi_list_search_fallback {
|
|
38500
38515
|
order: -1;
|
|
38501
38516
|
color: light-dark(#888, #aaa);
|
|
38502
38517
|
&[navi-default] {
|
|
38503
38518
|
display: inline;
|
|
38504
|
-
padding: var(
|
|
38519
|
+
padding-top: var(
|
|
38520
|
+
--list-item-padding-top,
|
|
38521
|
+
var(
|
|
38522
|
+
--list-item-padding-y,
|
|
38523
|
+
var(--list-item-padding, var(--list-item-padding-y-default))
|
|
38524
|
+
)
|
|
38525
|
+
);
|
|
38526
|
+
padding-right: var(
|
|
38527
|
+
--list-item-padding-right,
|
|
38528
|
+
var(
|
|
38529
|
+
--list-item-padding-x,
|
|
38530
|
+
var(--list-item-padding, var(--list-item-padding-x-default))
|
|
38531
|
+
)
|
|
38532
|
+
);
|
|
38533
|
+
padding-bottom: var(
|
|
38534
|
+
--list-item-padding-bottom,
|
|
38535
|
+
var(
|
|
38536
|
+
--list-item-padding-y,
|
|
38537
|
+
var(--list-item-padding, var(--list-item-padding-y-default))
|
|
38538
|
+
)
|
|
38539
|
+
);
|
|
38540
|
+
padding-left: var(
|
|
38541
|
+
--list-item-padding-left,
|
|
38542
|
+
var(
|
|
38543
|
+
--list-item-padding-x,
|
|
38544
|
+
var(--list-item-padding, var(--list-item-padding-x-default))
|
|
38545
|
+
)
|
|
38546
|
+
);
|
|
38505
38547
|
text-align: center;
|
|
38506
38548
|
user-select: none;
|
|
38507
38549
|
}
|
|
@@ -38575,37 +38617,6 @@ const css$m = /* css */`
|
|
|
38575
38617
|
}
|
|
38576
38618
|
}
|
|
38577
38619
|
`;
|
|
38578
|
-
|
|
38579
|
-
/**
|
|
38580
|
-
* List — generic virtualized scroll container.
|
|
38581
|
-
*
|
|
38582
|
-
* Renders children inside a scrollable container with an optional render budget
|
|
38583
|
-
* for virtual scrolling. Items must use <ListItem> to participate in tracking.
|
|
38584
|
-
*
|
|
38585
|
-
* Props:
|
|
38586
|
-
* keyboardInteractions — when true, attaches arrow/enter/escape keyboard shortcuts
|
|
38587
|
-
* that dispatch navi_list_nav / navi_list_confirm / navi_list_clear
|
|
38588
|
-
* to the list container. Pair with uiAction for a full keyboard-
|
|
38589
|
-
* navigable list.
|
|
38590
|
-
* uiAction — called with the selected value on confirm. When provided
|
|
38591
|
-
* the list becomes interactive: tracks hover and keyboard-
|
|
38592
|
-
* pointed state, handles navi_list_nav / navi_list_clear /
|
|
38593
|
-
* navi_list_confirm custom events via ListInteractionContext.
|
|
38594
|
-
* popover — when true, renders as a managed popover positioned near
|
|
38595
|
-
* an anchor element via navi_list_open / navi_list_close events.
|
|
38596
|
-
* renderBudget — max items in DOM at once (default 100, virtual scroll when exceeded)
|
|
38597
|
-
* virtualItemSize — fixed px size per item (width if horizontal, height otherwise) when all items have the same size.
|
|
38598
|
-
* Enables precise virtual-scroll filler sizing without a DOM
|
|
38599
|
-
* measurement pass. Required when renderBudget is active and
|
|
38600
|
-
* item height is known up-front.
|
|
38601
|
-
* fallback — content shown when no items exist at all
|
|
38602
|
-
* matchFallback — content shown when items exist but all are hidden (e.g. no search match)
|
|
38603
|
-
* separator — element or function(index, { previousItem, currentItem }) inserted between visible items
|
|
38604
|
-
* lockSize — when true, captures the container's dimensions on first render
|
|
38605
|
-
* (always in unfiltered state). Those values become min-width/
|
|
38606
|
-
* min-height so filtering cannot collapse the layout.
|
|
38607
|
-
* ...rest — forwarded to the outer scroll container <Box>
|
|
38608
|
-
*/
|
|
38609
38620
|
const ListUI = props => {
|
|
38610
38621
|
import.meta.css = [css$m, "@jsenv/navi/src/control/list/list.jsx"];
|
|
38611
38622
|
const {
|
|
@@ -38614,7 +38625,7 @@ const ListUI = props => {
|
|
|
38614
38625
|
renderBudgetSkipCheck,
|
|
38615
38626
|
role,
|
|
38616
38627
|
fallback,
|
|
38617
|
-
|
|
38628
|
+
searchFallback,
|
|
38618
38629
|
separator,
|
|
38619
38630
|
children,
|
|
38620
38631
|
popover,
|
|
@@ -38625,6 +38636,7 @@ const ListUI = props => {
|
|
|
38625
38636
|
virtualItemSize,
|
|
38626
38637
|
lockSize,
|
|
38627
38638
|
searchText,
|
|
38639
|
+
searchNoMatchMode = "remove",
|
|
38628
38640
|
horizontal,
|
|
38629
38641
|
spacing,
|
|
38630
38642
|
...rest
|
|
@@ -38714,8 +38726,8 @@ const ListUI = props => {
|
|
|
38714
38726
|
children: jsx(ListContent, {
|
|
38715
38727
|
role: role,
|
|
38716
38728
|
fallback: fallback,
|
|
38717
|
-
|
|
38718
|
-
|
|
38729
|
+
searchFallback: searchFallback,
|
|
38730
|
+
searchNoMatchMode: searchNoMatchMode,
|
|
38719
38731
|
separator: separator,
|
|
38720
38732
|
expandX: expandX,
|
|
38721
38733
|
expand: expand,
|
|
@@ -38739,12 +38751,39 @@ const ListFirstResolver = props => {
|
|
|
38739
38751
|
...props
|
|
38740
38752
|
});
|
|
38741
38753
|
};
|
|
38754
|
+
|
|
38755
|
+
/**
|
|
38756
|
+
* List — generic virtualized scroll container.
|
|
38757
|
+
* Items must use <List.Item> to participate in tracking.
|
|
38758
|
+
*
|
|
38759
|
+
* @type {import("ignore:preact").FunctionComponent<{
|
|
38760
|
+
* selectable?: boolean,
|
|
38761
|
+
* action?: (value: any) => void,
|
|
38762
|
+
* uiAction?: (value: any) => void,
|
|
38763
|
+
* popover?: boolean,
|
|
38764
|
+
* renderBudget?: number,
|
|
38765
|
+
* virtualItemSize?: number,
|
|
38766
|
+
* fallback?: import("ignore:preact").ComponentChildren,
|
|
38767
|
+
* searchFallback?: import("ignore:preact").ComponentChildren,
|
|
38768
|
+
* searchText?: string,
|
|
38769
|
+
* searchNoMatchMode?: "remove" | "invisible_and_inert" | "muted",
|
|
38770
|
+
* separator?: boolean | import("ignore:preact").ComponentChildren,
|
|
38771
|
+
* lockSize?: boolean,
|
|
38772
|
+
* horizontal?: boolean,
|
|
38773
|
+
* spacing?: string,
|
|
38774
|
+
* expandX?: boolean,
|
|
38775
|
+
* expand?: boolean,
|
|
38776
|
+
* maxHeight?: string | number,
|
|
38777
|
+
* children?: import("ignore:preact").ComponentChildren,
|
|
38778
|
+
* [key: string]: any,
|
|
38779
|
+
* }>}
|
|
38780
|
+
*/
|
|
38742
38781
|
const List = createComponentResolver([ListFirstResolver, ListSelectableResolver, ListUI]);
|
|
38743
38782
|
const ListContent = ({
|
|
38744
38783
|
role,
|
|
38745
38784
|
fallback,
|
|
38746
|
-
|
|
38747
|
-
|
|
38785
|
+
searchFallback,
|
|
38786
|
+
searchNoMatchMode,
|
|
38748
38787
|
separator,
|
|
38749
38788
|
expandX,
|
|
38750
38789
|
expand,
|
|
@@ -38762,8 +38801,8 @@ const ListContent = ({
|
|
|
38762
38801
|
children: jsx(UnorderedList, {
|
|
38763
38802
|
role: role,
|
|
38764
38803
|
fallback: fallback,
|
|
38765
|
-
|
|
38766
|
-
|
|
38804
|
+
searchFallback: searchFallback,
|
|
38805
|
+
searchNoMatchMode: searchNoMatchMode,
|
|
38767
38806
|
separator: separator === true ? jsx(Separator, {
|
|
38768
38807
|
margin: "0"
|
|
38769
38808
|
}) : separator,
|
|
@@ -39190,8 +39229,8 @@ const UnorderedList = ({
|
|
|
39190
39229
|
renderWindow,
|
|
39191
39230
|
virtualItemSizeSignal,
|
|
39192
39231
|
fallback,
|
|
39193
|
-
|
|
39194
|
-
|
|
39232
|
+
searchFallback,
|
|
39233
|
+
searchNoMatchMode,
|
|
39195
39234
|
separator,
|
|
39196
39235
|
horizontal,
|
|
39197
39236
|
spacing,
|
|
@@ -39208,20 +39247,22 @@ const UnorderedList = ({
|
|
|
39208
39247
|
children: [jsx(BeforeFiller, {
|
|
39209
39248
|
virtualItemSizeSignal: virtualItemSizeSignal,
|
|
39210
39249
|
renderWindowStart: renderWindow.start
|
|
39211
|
-
}), jsx(
|
|
39212
|
-
|
|
39213
|
-
tracker: tracker
|
|
39214
|
-
searchText: searchText
|
|
39250
|
+
}), jsx(SearchFallback, {
|
|
39251
|
+
searchFallback: searchFallback,
|
|
39252
|
+
tracker: tracker
|
|
39215
39253
|
}), jsx(Fallback, {
|
|
39216
39254
|
fallback: fallback,
|
|
39217
39255
|
tracker: tracker
|
|
39218
|
-
}), jsx(
|
|
39219
|
-
value:
|
|
39220
|
-
children: jsx(
|
|
39221
|
-
value:
|
|
39222
|
-
children: jsx(
|
|
39223
|
-
value:
|
|
39224
|
-
children:
|
|
39256
|
+
}), jsx(SearchNoMatchModeContext.Provider, {
|
|
39257
|
+
value: searchNoMatchMode,
|
|
39258
|
+
children: jsx(RenderWindowContext.Provider, {
|
|
39259
|
+
value: renderWindow,
|
|
39260
|
+
children: jsx(SeparatorContext.Provider, {
|
|
39261
|
+
value: separator ?? null,
|
|
39262
|
+
children: jsx(ListItemTrackerContext.Provider, {
|
|
39263
|
+
value: tracker,
|
|
39264
|
+
children: children
|
|
39265
|
+
})
|
|
39225
39266
|
})
|
|
39226
39267
|
})
|
|
39227
39268
|
}), jsx(AfterFiller, {
|
|
@@ -39231,31 +39272,33 @@ const UnorderedList = ({
|
|
|
39231
39272
|
})]
|
|
39232
39273
|
});
|
|
39233
39274
|
};
|
|
39234
|
-
|
|
39275
|
+
|
|
39276
|
+
// Show when all matchable items (those with a match prop) are non-matching.
|
|
39277
|
+
// The match prop on List.Item signals participation in a matching system
|
|
39278
|
+
// (search, filter, etc.). searchFallback appears when every such item has match=false.
|
|
39279
|
+
const SearchFallback = ({
|
|
39235
39280
|
tracker,
|
|
39236
|
-
|
|
39237
|
-
searchText
|
|
39281
|
+
searchFallback
|
|
39238
39282
|
}) => {
|
|
39239
39283
|
const itemCount = tracker.countSignal.value;
|
|
39240
|
-
const
|
|
39241
|
-
const
|
|
39242
|
-
|
|
39243
|
-
|
|
39244
|
-
|
|
39245
|
-
|
|
39246
|
-
|
|
39247
|
-
|
|
39248
|
-
noMatchFallback = allHidden ? naviI18n("list.no_match") : naviI18n("list.no_match_rest_shown");
|
|
39284
|
+
const noMatchCount = tracker.noMatchCountSignal.value;
|
|
39285
|
+
const showMatchFallback = noMatchCount > 0 && noMatchCount === itemCount;
|
|
39286
|
+
if (searchFallback === undefined) {
|
|
39287
|
+
searchFallback = naviI18n("list.no_match");
|
|
39288
|
+
}
|
|
39289
|
+
if (!searchFallback) {
|
|
39290
|
+
// explicitely disabled by user (<List searchFallback={false|null|''}>)
|
|
39291
|
+
return null;
|
|
39249
39292
|
}
|
|
39250
39293
|
if (!showMatchFallback) {
|
|
39251
39294
|
return null;
|
|
39252
39295
|
}
|
|
39253
39296
|
return jsx(ListItem, {
|
|
39254
39297
|
role: "presentation",
|
|
39255
|
-
className: "navi_list_item
|
|
39298
|
+
className: "navi_list_item navi_list_search_fallback",
|
|
39256
39299
|
hidden: !showMatchFallback,
|
|
39257
|
-
"navi-default": typeof
|
|
39258
|
-
children:
|
|
39300
|
+
"navi-default": typeof searchFallback === "string" ? "" : undefined,
|
|
39301
|
+
children: searchFallback
|
|
39259
39302
|
});
|
|
39260
39303
|
};
|
|
39261
39304
|
const Fallback = ({
|
|
@@ -39360,6 +39403,18 @@ const ListItemUI = props => {
|
|
|
39360
39403
|
props.id = props.id || idDefault;
|
|
39361
39404
|
const renderWindow = useContext(RenderWindowContext);
|
|
39362
39405
|
const tracker = useContext(ListItemTrackerContext);
|
|
39406
|
+
const searchNoMatchMode = useContext(SearchNoMatchModeContext);
|
|
39407
|
+
// Derive filtered/hidden/nonMatching from the `match` prop + searchNoMatchMode context.
|
|
39408
|
+
// The `match` prop replaces the older `filtered`/`hidden` per-item props.
|
|
39409
|
+
if (props.match === false) {
|
|
39410
|
+
if (searchNoMatchMode === "remove") {
|
|
39411
|
+
props.filtered = true;
|
|
39412
|
+
} else if (searchNoMatchMode === "invisible_and_inert") {
|
|
39413
|
+
props.hidden = true;
|
|
39414
|
+
} else if (searchNoMatchMode === "muted") {
|
|
39415
|
+
props.muted = true;
|
|
39416
|
+
}
|
|
39417
|
+
}
|
|
39363
39418
|
const item = props;
|
|
39364
39419
|
const visibleIndex = tracker.useTrackItem(item);
|
|
39365
39420
|
const groupTracker = useContext(GroupItemTrackerContext);
|
|
@@ -39419,6 +39474,7 @@ const ListItemReal = props => {
|
|
|
39419
39474
|
ref,
|
|
39420
39475
|
id,
|
|
39421
39476
|
hidden,
|
|
39477
|
+
muted,
|
|
39422
39478
|
highlight,
|
|
39423
39479
|
children,
|
|
39424
39480
|
...rest
|
|
@@ -39451,7 +39507,15 @@ const ListItemReal = props => {
|
|
|
39451
39507
|
index: undefined,
|
|
39452
39508
|
selected: undefined,
|
|
39453
39509
|
matchScore: undefined,
|
|
39454
|
-
|
|
39510
|
+
match: undefined
|
|
39511
|
+
// We use aria-hidden and not hidden because hidden would be forced to
|
|
39512
|
+
// display: none while here we want to keep it in the DOM to avoid layout shift
|
|
39513
|
+
// but visually hidden
|
|
39514
|
+
,
|
|
39515
|
+
|
|
39516
|
+
"aria-hidden": hidden,
|
|
39517
|
+
inert: hidden ? true : undefined,
|
|
39518
|
+
"navi-muted": muted ? "" : undefined,
|
|
39455
39519
|
ref: ref,
|
|
39456
39520
|
children: children
|
|
39457
39521
|
});
|