@jsenv/navi 0.16.23 → 0.16.25
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 +121 -49
- package/dist/jsenv_navi.js.map +4 -4
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -7723,7 +7723,11 @@ const createRoutePattern = (pattern) => {
|
|
|
7723
7723
|
}
|
|
7724
7724
|
}
|
|
7725
7725
|
|
|
7726
|
-
const parsedPattern = parsePattern(
|
|
7726
|
+
const parsedPattern = parsePattern(
|
|
7727
|
+
cleanPattern,
|
|
7728
|
+
parameterDefaults,
|
|
7729
|
+
connections,
|
|
7730
|
+
);
|
|
7727
7731
|
|
|
7728
7732
|
// Create signalSet to track all signals this pattern depends on
|
|
7729
7733
|
const signalSet = new Set();
|
|
@@ -9334,7 +9338,11 @@ const canParameterReachChildRoute = (
|
|
|
9334
9338
|
/**
|
|
9335
9339
|
* Parse a route pattern string into structured segments
|
|
9336
9340
|
*/
|
|
9337
|
-
const parsePattern = (
|
|
9341
|
+
const parsePattern = (
|
|
9342
|
+
pattern,
|
|
9343
|
+
parameterDefaults = new Map(),
|
|
9344
|
+
connections = [],
|
|
9345
|
+
) => {
|
|
9338
9346
|
// Handle root route
|
|
9339
9347
|
if (pattern === "/") {
|
|
9340
9348
|
return {
|
|
@@ -9403,7 +9411,27 @@ const parsePattern = (pattern, parameterDefaults = new Map()) => {
|
|
|
9403
9411
|
if (seg.startsWith(":")) {
|
|
9404
9412
|
// Parameter segment
|
|
9405
9413
|
const paramName = seg.slice(1).replace("?", ""); // Remove : and optional ?
|
|
9406
|
-
|
|
9414
|
+
|
|
9415
|
+
// Check if parameter should be optional:
|
|
9416
|
+
// 1. Explicitly marked with ?
|
|
9417
|
+
// 2. Has a default value
|
|
9418
|
+
// 3. Connected signal has undefined value and no explicit default (allows /map to match /map/:panel)
|
|
9419
|
+
let isOptional = seg.endsWith("?") || parameterDefaults.has(paramName);
|
|
9420
|
+
|
|
9421
|
+
if (!isOptional) {
|
|
9422
|
+
// Check if connected signal has undefined value (making parameter optional for index routes)
|
|
9423
|
+
const connection = connections.find(
|
|
9424
|
+
(conn) => conn.paramName === paramName,
|
|
9425
|
+
);
|
|
9426
|
+
if (
|
|
9427
|
+
connection &&
|
|
9428
|
+
connection.signal &&
|
|
9429
|
+
connection.signal.value === undefined &&
|
|
9430
|
+
!parameterDefaults.has(paramName)
|
|
9431
|
+
) {
|
|
9432
|
+
isOptional = true;
|
|
9433
|
+
}
|
|
9434
|
+
}
|
|
9407
9435
|
|
|
9408
9436
|
return {
|
|
9409
9437
|
type: "param",
|
|
@@ -9597,14 +9625,18 @@ const matchUrl = (
|
|
|
9597
9625
|
// Check for remaining URL segments
|
|
9598
9626
|
// Patterns with trailing slashes can match additional URL segments (like wildcards)
|
|
9599
9627
|
// Patterns without trailing slashes should match exactly (unless they're wildcards)
|
|
9628
|
+
// BUT: if pattern has children, it can also match additional segments (hierarchical matching)
|
|
9629
|
+
const hasChildren =
|
|
9630
|
+
patternObj && patternObj.children && patternObj.children.length > 0;
|
|
9600
9631
|
if (
|
|
9601
9632
|
!parsedPattern.wildcard &&
|
|
9602
9633
|
!parsedPattern.trailingSlash &&
|
|
9634
|
+
!hasChildren &&
|
|
9603
9635
|
urlSegmentIndex < urlSegments.length
|
|
9604
9636
|
) {
|
|
9605
|
-
return null; // Pattern without trailing slash should not match extra segments
|
|
9637
|
+
return null; // Pattern without trailing slash/wildcard/children should not match extra segments
|
|
9606
9638
|
}
|
|
9607
|
-
// If pattern has trailing slash or
|
|
9639
|
+
// If pattern has trailing slash, wildcard, or children, allow extra segments
|
|
9608
9640
|
|
|
9609
9641
|
// Add search parameters
|
|
9610
9642
|
const searchParams = extractSearchParams(urlObj, connections);
|
|
@@ -10262,77 +10294,107 @@ const updateRoutes = (
|
|
|
10262
10294
|
}
|
|
10263
10295
|
stateSignal.value = urlParamValue;
|
|
10264
10296
|
} else {
|
|
10265
|
-
//
|
|
10266
|
-
let
|
|
10297
|
+
// Route doesn't match - check if any matching route extracts this parameter
|
|
10298
|
+
let parameterExtractedByMatchingRoute = false;
|
|
10299
|
+
let matchingRouteInSameFamily = false;
|
|
10300
|
+
|
|
10267
10301
|
for (const otherRoute of routeSet) {
|
|
10268
10302
|
if (otherRoute === route || !otherRoute.matching) {
|
|
10269
10303
|
continue;
|
|
10270
10304
|
}
|
|
10271
10305
|
const otherRouteProperties = getRoutePrivateProperties(otherRoute);
|
|
10306
|
+
const otherParams = otherRouteProperties.rawParamsSignal.value;
|
|
10307
|
+
|
|
10308
|
+
// Check if this matching route extracts the parameter
|
|
10309
|
+
if (paramName in otherParams) {
|
|
10310
|
+
parameterExtractedByMatchingRoute = true;
|
|
10311
|
+
}
|
|
10312
|
+
|
|
10313
|
+
// Check if this matching route is in the same family using parent-child relationships
|
|
10314
|
+
const thisPatternObj = routePattern;
|
|
10272
10315
|
const otherPatternObj = otherRouteProperties.routePattern;
|
|
10273
10316
|
|
|
10274
|
-
//
|
|
10275
|
-
//
|
|
10276
|
-
|
|
10277
|
-
let
|
|
10317
|
+
// Routes are in same family if they share a hierarchical relationship:
|
|
10318
|
+
// 1. One is parent/ancestor of the other
|
|
10319
|
+
// 2. They share a common parent/ancestor
|
|
10320
|
+
let inSameFamily = false;
|
|
10321
|
+
|
|
10322
|
+
// Check if other route is ancestor of this route
|
|
10323
|
+
let currentParent = thisPatternObj.parent;
|
|
10278
10324
|
while (currentParent) {
|
|
10279
10325
|
if (currentParent === otherPatternObj) {
|
|
10280
|
-
|
|
10326
|
+
inSameFamily = true;
|
|
10281
10327
|
break;
|
|
10282
10328
|
}
|
|
10283
10329
|
currentParent = currentParent.parent;
|
|
10284
10330
|
}
|
|
10285
10331
|
|
|
10286
|
-
if
|
|
10287
|
-
|
|
10332
|
+
// Check if this route is ancestor of other route
|
|
10333
|
+
if (!inSameFamily) {
|
|
10334
|
+
currentParent = otherPatternObj.parent;
|
|
10335
|
+
while (currentParent) {
|
|
10336
|
+
if (currentParent === thisPatternObj) {
|
|
10337
|
+
inSameFamily = true;
|
|
10338
|
+
break;
|
|
10339
|
+
}
|
|
10340
|
+
currentParent = currentParent.parent;
|
|
10341
|
+
}
|
|
10288
10342
|
}
|
|
10289
10343
|
|
|
10290
|
-
//
|
|
10291
|
-
|
|
10292
|
-
|
|
10293
|
-
|
|
10294
|
-
|
|
10295
|
-
|
|
10296
|
-
|
|
10297
|
-
!siblingCandidateRoute.matching
|
|
10298
|
-
) {
|
|
10299
|
-
continue;
|
|
10344
|
+
// Check if they share a common parent (siblings or cousins)
|
|
10345
|
+
if (!inSameFamily) {
|
|
10346
|
+
const thisAncestors = new Set();
|
|
10347
|
+
currentParent = thisPatternObj.parent;
|
|
10348
|
+
while (currentParent) {
|
|
10349
|
+
thisAncestors.add(currentParent);
|
|
10350
|
+
currentParent = currentParent.parent;
|
|
10300
10351
|
}
|
|
10301
10352
|
|
|
10302
|
-
|
|
10303
|
-
|
|
10304
|
-
|
|
10305
|
-
|
|
10306
|
-
|
|
10307
|
-
|
|
10308
|
-
|
|
10309
|
-
hasMatchingSibling = true;
|
|
10310
|
-
break;
|
|
10353
|
+
currentParent = otherPatternObj.parent;
|
|
10354
|
+
while (currentParent) {
|
|
10355
|
+
if (thisAncestors.has(currentParent)) {
|
|
10356
|
+
inSameFamily = true;
|
|
10357
|
+
break;
|
|
10358
|
+
}
|
|
10359
|
+
currentParent = currentParent.parent;
|
|
10311
10360
|
}
|
|
10312
10361
|
}
|
|
10313
10362
|
|
|
10314
|
-
|
|
10315
|
-
|
|
10316
|
-
parentRouteMatching = true;
|
|
10317
|
-
break; // Found the parent route, no need to check other routes
|
|
10363
|
+
if (inSameFamily) {
|
|
10364
|
+
matchingRouteInSameFamily = true;
|
|
10318
10365
|
}
|
|
10319
10366
|
}
|
|
10320
10367
|
|
|
10321
|
-
|
|
10322
|
-
|
|
10368
|
+
// Only reset signal if:
|
|
10369
|
+
// 1. We're navigating within the same route family (not to completely unrelated routes)
|
|
10370
|
+
// 2. AND no matching route extracts this parameter from URL
|
|
10371
|
+
// 3. AND parameter has no default value (making it truly optional)
|
|
10372
|
+
if (matchingRouteInSameFamily && !parameterExtractedByMatchingRoute) {
|
|
10323
10373
|
const defaultValue = routePattern.parameterDefaults?.get(paramName);
|
|
10324
|
-
if (
|
|
10374
|
+
if (defaultValue === undefined) {
|
|
10375
|
+
// Parameter is not extracted within same family and has no default - reset it
|
|
10376
|
+
if (debug) {
|
|
10377
|
+
console.debug(
|
|
10378
|
+
`[route] Same family navigation, ${paramName} not extracted and has no default: resetting signal`,
|
|
10379
|
+
);
|
|
10380
|
+
}
|
|
10381
|
+
stateSignal.value = undefined;
|
|
10382
|
+
} else if (debug) {
|
|
10383
|
+
// Parameter has a default value - preserve current signal value
|
|
10325
10384
|
console.debug(
|
|
10326
|
-
`[route]
|
|
10385
|
+
`[route] Parameter ${paramName} has default value ${defaultValue}: preserving signal value: ${stateSignal.value}`,
|
|
10327
10386
|
);
|
|
10328
10387
|
}
|
|
10329
|
-
stateSignal.value = defaultValue;
|
|
10330
10388
|
} else if (debug) {
|
|
10331
|
-
|
|
10332
|
-
|
|
10333
|
-
|
|
10334
|
-
|
|
10335
|
-
|
|
10389
|
+
if (!matchingRouteInSameFamily) {
|
|
10390
|
+
console.debug(
|
|
10391
|
+
`[route] Different route family: preserving ${paramName} signal value: ${stateSignal.value}`,
|
|
10392
|
+
);
|
|
10393
|
+
} else {
|
|
10394
|
+
console.debug(
|
|
10395
|
+
`[route] Parameter ${paramName} extracted by matching route: preserving signal value: ${stateSignal.value}`,
|
|
10396
|
+
);
|
|
10397
|
+
}
|
|
10336
10398
|
}
|
|
10337
10399
|
}
|
|
10338
10400
|
}
|
|
@@ -10514,10 +10576,20 @@ const registerRoute = (routePattern) => {
|
|
|
10514
10576
|
const rawParamsSignal = signal(ROUTE_NOT_MATCHING_PARAMS);
|
|
10515
10577
|
const paramsSignal = computed(() => {
|
|
10516
10578
|
const rawParams = rawParamsSignal.value;
|
|
10517
|
-
|
|
10518
|
-
return
|
|
10579
|
+
const resolvedParams = routePattern.resolveParams(rawParams);
|
|
10580
|
+
return resolvedParams;
|
|
10519
10581
|
});
|
|
10520
10582
|
const visitedSignal = signal(false);
|
|
10583
|
+
|
|
10584
|
+
// Keep route.params synchronized with computed paramsSignal
|
|
10585
|
+
// This ensures route.params includes parameters from child routes
|
|
10586
|
+
effect(() => {
|
|
10587
|
+
const computedParams = paramsSignal.value;
|
|
10588
|
+
if (route.params !== computedParams) {
|
|
10589
|
+
route.params = computedParams;
|
|
10590
|
+
}
|
|
10591
|
+
});
|
|
10592
|
+
|
|
10521
10593
|
for (const { signal: stateSignal, paramName, options = {} } of connections) {
|
|
10522
10594
|
const { debug } = options;
|
|
10523
10595
|
|