@jsenv/navi 0.16.26 → 0.16.28
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 +122 -49
- package/dist/jsenv_navi.js.map +4 -4
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -2401,15 +2401,18 @@ const generateSignalId = () => {
|
|
|
2401
2401
|
* 2. When explicitly set (programmatically or via localStorage), the explicit value takes precedence
|
|
2402
2402
|
* 3. When default signal changes, it only updates if no explicit value was ever set
|
|
2403
2403
|
* 4. Calling reset() or setting to undefined makes the signal use the dynamic default again
|
|
2404
|
+
* 5. If dynamic default is undefined and options.default is provided, uses the static fallback
|
|
2404
2405
|
*
|
|
2405
2406
|
* This is useful for:
|
|
2406
2407
|
* - Backend data that can change but shouldn't override user preferences
|
|
2407
2408
|
* - Route parameters with dynamic defaults based on other state
|
|
2408
2409
|
* - Cascading configuration where defaults can be updated without losing user customizations
|
|
2410
|
+
* - Having a static fallback when dynamic defaults might be undefined
|
|
2409
2411
|
*
|
|
2410
2412
|
* @param {any|import("@preact/signals").Signal} defaultValue - Static default value OR signal for dynamic default behavior
|
|
2411
2413
|
* @param {Object} [options={}] - Configuration options
|
|
2412
2414
|
* @param {string|number} [options.id] - Custom ID for the signal. If not provided, an auto-generated ID will be used. Used for localStorage key and route pattern detection.
|
|
2415
|
+
* @param {any} [options.default] - Static fallback value used when defaultValue is a signal and that signal's value is undefined
|
|
2413
2416
|
* @param {boolean} [options.persists=false] - Whether to persist the signal value in localStorage using the signal ID as key
|
|
2414
2417
|
* @param {"string" | "number" | "boolean" | "object"} [options.type="string"] - Type for localStorage serialization/deserialization
|
|
2415
2418
|
* @param {Array} [options.oneOf] - Array of valid values for validation. Signal will be marked invalid if value is not in this array
|
|
@@ -2450,6 +2453,21 @@ const generateSignalId = () => {
|
|
|
2450
2453
|
* // Reset: userTheme.value = undefined; // Now follows dynamic default again
|
|
2451
2454
|
*
|
|
2452
2455
|
* @example
|
|
2456
|
+
* // Dynamic default with static fallback
|
|
2457
|
+
* const backendValue = signal(undefined); // might be undefined initially
|
|
2458
|
+
* const userValue = stateSignal(backendValue, {
|
|
2459
|
+
* default: "fallback",
|
|
2460
|
+
* persists: true
|
|
2461
|
+
* });
|
|
2462
|
+
*
|
|
2463
|
+
* // Initially: userValue.value = "fallback" (static fallback since dynamic is undefined)
|
|
2464
|
+
* // Backend loads: backendValue.value = "loaded"; userValue.value = "loaded" (follows dynamic)
|
|
2465
|
+
* // User sets: userValue.value = "custom" (explicit choice, persisted)
|
|
2466
|
+
* // Backend changes: backendValue.value = "updated"
|
|
2467
|
+
* // Result: userValue.value = "custom" (user choice preserved)
|
|
2468
|
+
* // Reset: userValue.value = undefined; userValue.value = "updated" (follows dynamic again)
|
|
2469
|
+
*
|
|
2470
|
+
* @example
|
|
2453
2471
|
* // Route parameter with dynamic default from parent route
|
|
2454
2472
|
* const parentTab = signal("overview");
|
|
2455
2473
|
* const childTab = stateSignal(parentTab);
|
|
@@ -2464,6 +2482,7 @@ const stateSignal = (defaultValue, options = {}) => {
|
|
|
2464
2482
|
autoFix,
|
|
2465
2483
|
persists = false,
|
|
2466
2484
|
debug,
|
|
2485
|
+
default: staticFallback,
|
|
2467
2486
|
} = options;
|
|
2468
2487
|
|
|
2469
2488
|
// Check if defaultValue is a signal (dynamic default) or static value
|
|
@@ -2473,7 +2492,7 @@ const stateSignal = (defaultValue, options = {}) => {
|
|
|
2473
2492
|
"value" in defaultValue &&
|
|
2474
2493
|
"peek" in defaultValue;
|
|
2475
2494
|
const dynamicDefaultSignal = isDynamicDefault ? defaultValue : null;
|
|
2476
|
-
const staticDefaultValue = isDynamicDefault ?
|
|
2495
|
+
const staticDefaultValue = isDynamicDefault ? staticFallback : defaultValue;
|
|
2477
2496
|
const signalId = id || generateSignalId();
|
|
2478
2497
|
// Convert numeric IDs to strings for consistency
|
|
2479
2498
|
const signalIdString = String(signalId);
|
|
@@ -2505,7 +2524,15 @@ const stateSignal = (defaultValue, options = {}) => {
|
|
|
2505
2524
|
if (dynamicDefaultSignal) {
|
|
2506
2525
|
const dynamicValue = dynamicDefaultSignal.peek();
|
|
2507
2526
|
if (dynamicValue === undefined) {
|
|
2508
|
-
|
|
2527
|
+
if (staticDefaultValue === undefined) {
|
|
2528
|
+
return undefined;
|
|
2529
|
+
}
|
|
2530
|
+
if (debug) {
|
|
2531
|
+
console.debug(
|
|
2532
|
+
`[stateSignal:${signalIdString}] dynamic default is undefined, using static default=${staticDefaultValue}`,
|
|
2533
|
+
);
|
|
2534
|
+
}
|
|
2535
|
+
return staticDefaultValue;
|
|
2509
2536
|
}
|
|
2510
2537
|
if (debug) {
|
|
2511
2538
|
console.debug(
|
|
@@ -2526,7 +2553,11 @@ const stateSignal = (defaultValue, options = {}) => {
|
|
|
2526
2553
|
return false;
|
|
2527
2554
|
}
|
|
2528
2555
|
if (dynamicDefaultSignal) {
|
|
2529
|
-
|
|
2556
|
+
const dynamicValue = dynamicDefaultSignal.peek();
|
|
2557
|
+
if (dynamicValue === undefined) {
|
|
2558
|
+
return value !== staticDefaultValue;
|
|
2559
|
+
}
|
|
2560
|
+
return value !== dynamicValue;
|
|
2530
2561
|
}
|
|
2531
2562
|
return value !== staticDefaultValue;
|
|
2532
2563
|
};
|
|
@@ -2577,21 +2608,40 @@ const stateSignal = (defaultValue, options = {}) => {
|
|
|
2577
2608
|
dynamicDefaultPreviousValue = dynamicDefaultValue;
|
|
2578
2609
|
return;
|
|
2579
2610
|
}
|
|
2580
|
-
if
|
|
2611
|
+
// Check if current signal value matches the PREVIOUS dynamic default
|
|
2612
|
+
// If so, it was following the dynamic default and should update
|
|
2613
|
+
// Special case: if previous was undefined and we were using static fallback
|
|
2614
|
+
let wasFollowingDefault = false;
|
|
2615
|
+
if (
|
|
2616
|
+
dynamicDefaultPreviousValue === undefined &&
|
|
2617
|
+
staticDefaultValue !== undefined
|
|
2618
|
+
) {
|
|
2619
|
+
// Signal might have been using static fallback
|
|
2620
|
+
wasFollowingDefault = value === staticDefaultValue;
|
|
2621
|
+
} else {
|
|
2622
|
+
// Signal was following the previous dynamic default
|
|
2623
|
+
wasFollowingDefault = value === dynamicDefaultPreviousValue;
|
|
2624
|
+
}
|
|
2625
|
+
|
|
2626
|
+
if (!wasFollowingDefault) {
|
|
2627
|
+
// Signal has a custom value, don't update even if dynamic default changes
|
|
2581
2628
|
dynamicDefaultPreviousValue = dynamicDefaultValue;
|
|
2582
2629
|
return;
|
|
2583
2630
|
}
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2631
|
+
|
|
2632
|
+
// Signal was using default value, update to new default
|
|
2633
|
+
const newDefaultValue = getDefaultValue();
|
|
2634
|
+
if (newDefaultValue === value) {
|
|
2635
|
+
dynamicDefaultPreviousValue = dynamicDefaultValue;
|
|
2587
2636
|
return;
|
|
2588
2637
|
}
|
|
2589
2638
|
if (debug) {
|
|
2590
2639
|
console.debug(
|
|
2591
|
-
`[stateSignal:${signalIdString}] dynamic default updated, update to ${
|
|
2640
|
+
`[stateSignal:${signalIdString}] dynamic default updated, update to ${newDefaultValue}`,
|
|
2592
2641
|
);
|
|
2593
2642
|
}
|
|
2594
|
-
|
|
2643
|
+
dynamicDefaultPreviousValue = dynamicDefaultValue;
|
|
2644
|
+
advancedSignal.value = newDefaultValue;
|
|
2595
2645
|
});
|
|
2596
2646
|
}
|
|
2597
2647
|
persist_in_local_storage: {
|
|
@@ -2664,6 +2714,7 @@ const stateSignal = (defaultValue, options = {}) => {
|
|
|
2664
2714
|
getDefaultValue,
|
|
2665
2715
|
defaultValue: staticDefaultValue,
|
|
2666
2716
|
dynamicDefaultSignal,
|
|
2717
|
+
isCustomValue,
|
|
2667
2718
|
type,
|
|
2668
2719
|
persists,
|
|
2669
2720
|
localStorageKey,
|
|
@@ -7847,21 +7898,26 @@ const createRoutePattern = (pattern) => {
|
|
|
7847
7898
|
|
|
7848
7899
|
/**
|
|
7849
7900
|
* Helper: Filter out default values from parameters for cleaner URLs
|
|
7901
|
+
*
|
|
7902
|
+
* This function removes parameters that match their default values (static or dynamic)
|
|
7903
|
+
* while preserving custom values and inherited parameters from ancestor routes.
|
|
7904
|
+
* Parameter inheritance from parent routes is intentional - only default values
|
|
7905
|
+
* for the current route's own parameters are filtered out.
|
|
7850
7906
|
*/
|
|
7851
7907
|
const removeDefaultValues = (params) => {
|
|
7852
7908
|
const filtered = { ...params };
|
|
7853
7909
|
|
|
7854
7910
|
for (const connection of connections) {
|
|
7855
|
-
const { paramName, signal } = connection;
|
|
7856
|
-
const defaultValue = parameterDefaults.get(paramName);
|
|
7911
|
+
const { paramName, signal, options } = connection;
|
|
7857
7912
|
|
|
7858
|
-
if (paramName in filtered
|
|
7859
|
-
|
|
7860
|
-
|
|
7861
|
-
|
|
7862
|
-
|
|
7863
|
-
|
|
7864
|
-
) {
|
|
7913
|
+
if (paramName in filtered) {
|
|
7914
|
+
// Parameter is explicitly provided - check if we should remove it
|
|
7915
|
+
if (!options.isCustomValue?.(filtered[paramName])) {
|
|
7916
|
+
// Parameter value is not custom (matches default) - remove it
|
|
7917
|
+
delete filtered[paramName];
|
|
7918
|
+
}
|
|
7919
|
+
} else if (options.isCustomValue?.(signal.value)) {
|
|
7920
|
+
// Parameter not provided but signal has custom value - add it
|
|
7865
7921
|
filtered[paramName] = signal.value;
|
|
7866
7922
|
}
|
|
7867
7923
|
}
|
|
@@ -7880,7 +7936,7 @@ const createRoutePattern = (pattern) => {
|
|
|
7880
7936
|
const effectiveValue = userValue !== undefined ? userValue : signalValue;
|
|
7881
7937
|
return (
|
|
7882
7938
|
effectiveValue === literalValue &&
|
|
7883
|
-
|
|
7939
|
+
conn.options.isCustomValue?.(effectiveValue)
|
|
7884
7940
|
);
|
|
7885
7941
|
});
|
|
7886
7942
|
|
|
@@ -7918,7 +7974,7 @@ const createRoutePattern = (pattern) => {
|
|
|
7918
7974
|
const signalValue = conn.signal?.value;
|
|
7919
7975
|
return (
|
|
7920
7976
|
signalValue === literalValue &&
|
|
7921
|
-
|
|
7977
|
+
conn.options.isCustomValue?.(signalValue)
|
|
7922
7978
|
);
|
|
7923
7979
|
});
|
|
7924
7980
|
|
|
@@ -8061,10 +8117,10 @@ const createRoutePattern = (pattern) => {
|
|
|
8061
8117
|
} else {
|
|
8062
8118
|
const { paramName: name, signal, options } = item;
|
|
8063
8119
|
paramName = name;
|
|
8064
|
-
// Only include
|
|
8120
|
+
// Only include custom parent signal values (not using defaults)
|
|
8065
8121
|
if (
|
|
8066
8122
|
signal?.value === undefined ||
|
|
8067
|
-
signal.value
|
|
8123
|
+
!options.isCustomValue?.(signal.value)
|
|
8068
8124
|
) {
|
|
8069
8125
|
return { isCompatible: true, shouldInclude: false };
|
|
8070
8126
|
}
|
|
@@ -8214,7 +8270,6 @@ const createRoutePattern = (pattern) => {
|
|
|
8214
8270
|
|
|
8215
8271
|
for (const connection of childPatternObj.connections) {
|
|
8216
8272
|
const { paramName, signal, options } = connection;
|
|
8217
|
-
const defaultValue = options.defaultValue;
|
|
8218
8273
|
|
|
8219
8274
|
// Check if parameter was explicitly provided by user
|
|
8220
8275
|
const hasExplicitParam = paramName in params;
|
|
@@ -8223,13 +8278,16 @@ const createRoutePattern = (pattern) => {
|
|
|
8223
8278
|
if (hasExplicitParam) {
|
|
8224
8279
|
// User explicitly provided this parameter - use their value
|
|
8225
8280
|
childParams[paramName] = explicitValue;
|
|
8226
|
-
if (
|
|
8281
|
+
if (
|
|
8282
|
+
explicitValue !== undefined &&
|
|
8283
|
+
options.isCustomValue?.(explicitValue)
|
|
8284
|
+
) {
|
|
8227
8285
|
hasActiveParams = true;
|
|
8228
8286
|
}
|
|
8229
8287
|
} else if (signal?.value !== undefined) {
|
|
8230
8288
|
// No explicit override - use signal value
|
|
8231
8289
|
childParams[paramName] = signal.value;
|
|
8232
|
-
if (signal.value
|
|
8290
|
+
if (options.isCustomValue?.(signal.value)) {
|
|
8233
8291
|
hasActiveParams = true;
|
|
8234
8292
|
}
|
|
8235
8293
|
}
|
|
@@ -8309,7 +8367,7 @@ const createRoutePattern = (pattern) => {
|
|
|
8309
8367
|
// Check if parameters that determine child selection are non-default
|
|
8310
8368
|
// OR if any descendant parameters indicate explicit navigation
|
|
8311
8369
|
for (const connection of connections) {
|
|
8312
|
-
const { paramName } = connection;
|
|
8370
|
+
const { paramName, options } = connection;
|
|
8313
8371
|
const defaultValue = parameterDefaults.get(paramName);
|
|
8314
8372
|
const resolvedValue = resolvedParams[paramName];
|
|
8315
8373
|
const userProvidedParam = paramName in params;
|
|
@@ -8318,9 +8376,10 @@ const createRoutePattern = (pattern) => {
|
|
|
8318
8376
|
// This literal corresponds to a parameter in the parent
|
|
8319
8377
|
if (
|
|
8320
8378
|
userProvidedParam ||
|
|
8321
|
-
(resolvedValue !== undefined &&
|
|
8379
|
+
(resolvedValue !== undefined &&
|
|
8380
|
+
options.isCustomValue?.(resolvedValue))
|
|
8322
8381
|
) {
|
|
8323
|
-
// Parameter was explicitly provided or has
|
|
8382
|
+
// Parameter was explicitly provided or has custom value - child is needed
|
|
8324
8383
|
childSpecificParamsAreDefaults = false;
|
|
8325
8384
|
break;
|
|
8326
8385
|
}
|
|
@@ -8421,7 +8480,7 @@ const createRoutePattern = (pattern) => {
|
|
|
8421
8480
|
// If explicitly undefined, don't include it (which means don't use child route)
|
|
8422
8481
|
} else if (
|
|
8423
8482
|
signal?.value !== undefined &&
|
|
8424
|
-
signal.value
|
|
8483
|
+
options.isCustomValue?.(signal.value)
|
|
8425
8484
|
) {
|
|
8426
8485
|
// No explicit override - use signal value if non-default
|
|
8427
8486
|
baseParams[paramName] = signal.value;
|
|
@@ -8439,7 +8498,6 @@ const createRoutePattern = (pattern) => {
|
|
|
8439
8498
|
// Add parent's signal parameters
|
|
8440
8499
|
for (const connection of parentPatternObj.connections) {
|
|
8441
8500
|
const { paramName, signal, options } = connection;
|
|
8442
|
-
const defaultValue = options.defaultValue;
|
|
8443
8501
|
|
|
8444
8502
|
// Skip if child route already handles this parameter
|
|
8445
8503
|
const childConnection = childPatternObj.connections.find(
|
|
@@ -8454,8 +8512,11 @@ const createRoutePattern = (pattern) => {
|
|
|
8454
8512
|
continue; // Already have this parameter
|
|
8455
8513
|
}
|
|
8456
8514
|
|
|
8457
|
-
// Only include
|
|
8458
|
-
if (
|
|
8515
|
+
// Only include custom signal values (not using defaults)
|
|
8516
|
+
if (
|
|
8517
|
+
signal?.value !== undefined &&
|
|
8518
|
+
options.isCustomValue?.(signal.value)
|
|
8519
|
+
) {
|
|
8459
8520
|
// Skip if parameter is consumed by child's literal path segments
|
|
8460
8521
|
const isConsumedByChildPath = childPatternObj.pattern.segments.some(
|
|
8461
8522
|
(segment) =>
|
|
@@ -8521,10 +8582,9 @@ const createRoutePattern = (pattern) => {
|
|
|
8521
8582
|
|
|
8522
8583
|
if (childConnection) {
|
|
8523
8584
|
const { options } = childConnection;
|
|
8524
|
-
const defaultValue = options.defaultValue;
|
|
8525
8585
|
|
|
8526
|
-
// Only include if it's
|
|
8527
|
-
if (userValue
|
|
8586
|
+
// Only include if it's a custom value (not default)
|
|
8587
|
+
if (options.isCustomValue?.(userValue)) {
|
|
8528
8588
|
baseParams[paramName] = userValue;
|
|
8529
8589
|
} else {
|
|
8530
8590
|
// User provided the default value - complete omission
|
|
@@ -8584,7 +8644,7 @@ const createRoutePattern = (pattern) => {
|
|
|
8584
8644
|
const hasNonDefaultChildParams = (childPatternObj.connections || []).some(
|
|
8585
8645
|
(childConnection) => {
|
|
8586
8646
|
const { signal, options } = childConnection;
|
|
8587
|
-
return signal?.value
|
|
8647
|
+
return options.isCustomValue?.(signal?.value);
|
|
8588
8648
|
},
|
|
8589
8649
|
);
|
|
8590
8650
|
|
|
@@ -8794,13 +8854,15 @@ const createRoutePattern = (pattern) => {
|
|
|
8794
8854
|
// 2. The source route has only query parameters that are non-default
|
|
8795
8855
|
const hasNonDefaultPathParams = connections.some((connection) => {
|
|
8796
8856
|
const resolvedValue = resolvedParams[connection.paramName];
|
|
8797
|
-
|
|
8857
|
+
|
|
8798
8858
|
// Check if this is a query parameter (not in the pattern path)
|
|
8799
8859
|
const isQueryParam = parsedPattern.queryParams.some(
|
|
8800
8860
|
(qp) => qp.name === connection.paramName,
|
|
8801
8861
|
);
|
|
8802
8862
|
// Allow non-default query parameters, but not path parameters
|
|
8803
|
-
return
|
|
8863
|
+
return (
|
|
8864
|
+
!isQueryParam && connection.options.isCustomValue?.(resolvedValue)
|
|
8865
|
+
);
|
|
8804
8866
|
});
|
|
8805
8867
|
|
|
8806
8868
|
if (hasNonDefaultPathParams) {
|
|
@@ -8830,8 +8892,7 @@ const createRoutePattern = (pattern) => {
|
|
|
8830
8892
|
// For non-immediate parents, only allow optimization if all resolved parameters have default values
|
|
8831
8893
|
const hasNonDefaultParameters = connections.some((connection) => {
|
|
8832
8894
|
const resolvedValue = resolvedParams[connection.paramName];
|
|
8833
|
-
|
|
8834
|
-
return resolvedValue !== defaultValue;
|
|
8895
|
+
return connection.options.isCustomValue?.(resolvedValue);
|
|
8835
8896
|
});
|
|
8836
8897
|
|
|
8837
8898
|
if (hasNonDefaultParameters) {
|
|
@@ -9100,13 +9161,12 @@ const createRoutePattern = (pattern) => {
|
|
|
9100
9161
|
// Also check target ancestor's own signal values for parameters not in resolvedParams
|
|
9101
9162
|
for (const connection of targetAncestor.connections) {
|
|
9102
9163
|
const { paramName, signal, options } = connection;
|
|
9103
|
-
const defaultValue = options.defaultValue;
|
|
9104
9164
|
|
|
9105
|
-
// Only include if not already processed and has
|
|
9165
|
+
// Only include if not already processed and has custom value (not default)
|
|
9106
9166
|
if (
|
|
9107
9167
|
!(paramName in ancestorParams) &&
|
|
9108
9168
|
signal?.value !== undefined &&
|
|
9109
|
-
signal.value
|
|
9169
|
+
options.isCustomValue?.(signal.value)
|
|
9110
9170
|
) {
|
|
9111
9171
|
// Don't include path parameters that correspond to literal segments we're optimizing away
|
|
9112
9172
|
const targetParam = targetParams.find((p) => p.name === paramName);
|
|
@@ -9136,13 +9196,12 @@ const createRoutePattern = (pattern) => {
|
|
|
9136
9196
|
while (currentParent) {
|
|
9137
9197
|
for (const connection of currentParent.connections) {
|
|
9138
9198
|
const { paramName, signal, options } = connection;
|
|
9139
|
-
const defaultValue = options.defaultValue;
|
|
9140
9199
|
|
|
9141
|
-
// Only inherit
|
|
9200
|
+
// Only inherit custom values (not defaults) that we don't already have
|
|
9142
9201
|
if (
|
|
9143
9202
|
!(paramName in ancestorParams) &&
|
|
9144
9203
|
signal?.value !== undefined &&
|
|
9145
|
-
signal.value
|
|
9204
|
+
options.isCustomValue?.(signal.value)
|
|
9146
9205
|
) {
|
|
9147
9206
|
// Check if this parameter would be redundant with target ancestor's literal segments
|
|
9148
9207
|
const isRedundant = isParameterRedundantWithLiteralSegments(
|
|
@@ -9217,13 +9276,12 @@ const createRoutePattern = (pattern) => {
|
|
|
9217
9276
|
// Check parent's signal connections for non-default values to inherit
|
|
9218
9277
|
for (const parentConnection of currentParent.connections) {
|
|
9219
9278
|
const { paramName, signal, options } = parentConnection;
|
|
9220
|
-
const defaultValue = options.defaultValue;
|
|
9221
9279
|
|
|
9222
|
-
// Only inherit if we don't have this param and parent has
|
|
9280
|
+
// Only inherit if we don't have this param and parent has custom value (not default)
|
|
9223
9281
|
if (
|
|
9224
9282
|
!(paramName in finalParams) &&
|
|
9225
9283
|
signal?.value !== undefined &&
|
|
9226
|
-
signal.value
|
|
9284
|
+
options.isCustomValue?.(signal.value)
|
|
9227
9285
|
) {
|
|
9228
9286
|
// Don't inherit if parameter corresponds to a literal in our path
|
|
9229
9287
|
const shouldInherit = !isParameterRedundantWithLiteralSegments(
|
|
@@ -9707,6 +9765,21 @@ const extractSearchParams = (urlObj, connections = []) => {
|
|
|
9707
9765
|
/**
|
|
9708
9766
|
* Build query parameters respecting hierarchical order from ancestor patterns
|
|
9709
9767
|
*/
|
|
9768
|
+
/**
|
|
9769
|
+
* Build hierarchical query parameters from pattern hierarchy
|
|
9770
|
+
*
|
|
9771
|
+
* IMPORTANT: This function implements parameter inheritance - child routes inherit
|
|
9772
|
+
* query parameters from their ancestor routes. This is intentional behavior that
|
|
9773
|
+
* allows child routes to preserve context from parent routes.
|
|
9774
|
+
*
|
|
9775
|
+
* For example:
|
|
9776
|
+
* - Parent route: /map/?lon=123
|
|
9777
|
+
* - Child route: /map/isochrone?iso_lon=456
|
|
9778
|
+
* - Final URL: /map/isochrone?lon=123&iso_lon=456
|
|
9779
|
+
*
|
|
9780
|
+
* The child route inherits 'lon' from its parent, maintaining navigation context.
|
|
9781
|
+
* Only parameters that match their defaults (static or dynamic) are omitted.
|
|
9782
|
+
*/
|
|
9710
9783
|
const buildHierarchicalQueryParams = (
|
|
9711
9784
|
parsedPattern,
|
|
9712
9785
|
params,
|