@jsenv/navi 0.27.18 → 0.27.19
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 +86 -50
- package/dist/jsenv_navi.js.map +6 -6
- package/package.json +3 -3
package/dist/jsenv_navi.js
CHANGED
|
@@ -427,6 +427,21 @@ const compareTwoJsValues = (
|
|
|
427
427
|
) {
|
|
428
428
|
return true;
|
|
429
429
|
}
|
|
430
|
+
// Date objects must be compared by time value, not by enumerable keys (which are empty)
|
|
431
|
+
{
|
|
432
|
+
const aIsDate = a instanceof Date;
|
|
433
|
+
const bIsDate = b instanceof Date;
|
|
434
|
+
if (aIsDate !== bIsDate) {
|
|
435
|
+
return false;
|
|
436
|
+
}
|
|
437
|
+
if (aIsDate && bIsDate) {
|
|
438
|
+
const aTime = a.getTime();
|
|
439
|
+
const bTime = b.getTime();
|
|
440
|
+
if (aTime !== bTime) {
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
430
445
|
const aKeys = Object.keys(a);
|
|
431
446
|
const bKeys = Object.keys(b);
|
|
432
447
|
if (aKeys.length !== bKeys.length) {
|
|
@@ -5263,17 +5278,19 @@ const generateSignalId = () => {
|
|
|
5263
5278
|
const stateSignal = (defaultValue, options = {}) => {
|
|
5264
5279
|
const {
|
|
5265
5280
|
id,
|
|
5281
|
+
// NOTE: when adding support for a new type here, also update route_pattern.js
|
|
5282
|
+
// (buildQueryString for encoding, extractSearchParams for decoding)
|
|
5266
5283
|
type,
|
|
5267
5284
|
min,
|
|
5268
5285
|
max,
|
|
5269
5286
|
step,
|
|
5270
5287
|
oneOf,
|
|
5271
5288
|
localStorageRepresentation,
|
|
5272
|
-
urlRepresentation,
|
|
5273
5289
|
persists = false,
|
|
5274
5290
|
debug,
|
|
5275
5291
|
default: staticFallback,
|
|
5276
5292
|
ignoreArrayOrder,
|
|
5293
|
+
autoFix,
|
|
5277
5294
|
} = options;
|
|
5278
5295
|
|
|
5279
5296
|
// Check if defaultValue is a signal (dynamic default) or static value
|
|
@@ -5303,7 +5320,7 @@ const stateSignal = (defaultValue, options = {}) => {
|
|
|
5303
5320
|
step,
|
|
5304
5321
|
oneOf,
|
|
5305
5322
|
localStorageRepresentation,
|
|
5306
|
-
|
|
5323
|
+
autoFix,
|
|
5307
5324
|
});
|
|
5308
5325
|
const readFromLocalStorage = persists
|
|
5309
5326
|
? () => {
|
|
@@ -5428,7 +5445,12 @@ const stateSignal = (defaultValue, options = {}) => {
|
|
|
5428
5445
|
return valueDescriptor.get.call(preactSignal);
|
|
5429
5446
|
},
|
|
5430
5447
|
set(newValue) {
|
|
5431
|
-
|
|
5448
|
+
const processedValue = processValue(newValue);
|
|
5449
|
+
// const currentValue = valueDescriptor.get.call(preactSignal);
|
|
5450
|
+
// if (compareTwoJsValues(processedValue, currentValue)) {
|
|
5451
|
+
// return;
|
|
5452
|
+
// }
|
|
5453
|
+
valueDescriptor.set.call(preactSignal, processedValue);
|
|
5432
5454
|
},
|
|
5433
5455
|
enumerable: true,
|
|
5434
5456
|
configurable: true,
|
|
@@ -5436,6 +5458,12 @@ const stateSignal = (defaultValue, options = {}) => {
|
|
|
5436
5458
|
|
|
5437
5459
|
const facadeSignal = preactSignal;
|
|
5438
5460
|
facadeSignal.validity = validity;
|
|
5461
|
+
facadeSignal.validSignal = computed(() => {
|
|
5462
|
+
// Reading facadeSignal.value establishes the reactive dependency.
|
|
5463
|
+
// eslint-disable-next-line no-unused-expressions
|
|
5464
|
+
facadeSignal.value;
|
|
5465
|
+
return validity.representations.valid?.value;
|
|
5466
|
+
});
|
|
5439
5467
|
facadeSignal.__signalId = signalIdString;
|
|
5440
5468
|
facadeSignal.toString = () => `{navi_state_signal:${signalIdString}}`;
|
|
5441
5469
|
// 1. when signal value changes to undefined, it needs to fallback to default value
|
|
@@ -5552,14 +5580,6 @@ const stateSignal = (defaultValue, options = {}) => {
|
|
|
5552
5580
|
}
|
|
5553
5581
|
});
|
|
5554
5582
|
}
|
|
5555
|
-
// update validity object according to the signal value
|
|
5556
|
-
{
|
|
5557
|
-
effect(() => {
|
|
5558
|
-
const value = preactSignal.value;
|
|
5559
|
-
facadeSignal.value = processValue(value);
|
|
5560
|
-
});
|
|
5561
|
-
}
|
|
5562
|
-
|
|
5563
5583
|
// Create isDefaultValue function for this signal
|
|
5564
5584
|
const isDefaultValue = (value) => {
|
|
5565
5585
|
const currentDefault = getDefaultValue(false);
|
|
@@ -12340,38 +12360,6 @@ const encodeParamValue = (value, isWildcard = false) => {
|
|
|
12340
12360
|
return encodeURIComponent(value);
|
|
12341
12361
|
};
|
|
12342
12362
|
|
|
12343
|
-
/**
|
|
12344
|
-
* Build query string from parameters, respecting rawUrlPart values
|
|
12345
|
-
*/
|
|
12346
|
-
const buildQueryString = (params) => {
|
|
12347
|
-
const searchParamPairs = [];
|
|
12348
|
-
|
|
12349
|
-
for (const [key, value] of Object.entries(params)) {
|
|
12350
|
-
if (value !== undefined && value !== null) {
|
|
12351
|
-
const encodedKey = encodeURIComponent(key);
|
|
12352
|
-
|
|
12353
|
-
// Handle array values - join with commas
|
|
12354
|
-
if (Array.isArray(value)) {
|
|
12355
|
-
if (value.length === 0) ; else {
|
|
12356
|
-
const encodedValue = value
|
|
12357
|
-
.map((item) => encodeURIComponent(String(item)))
|
|
12358
|
-
.join(",");
|
|
12359
|
-
searchParamPairs.push(`${encodedKey}=${encodedValue}`);
|
|
12360
|
-
}
|
|
12361
|
-
}
|
|
12362
|
-
// Handle boolean values - if true, just add the key without value
|
|
12363
|
-
else if (value === true || value === "") {
|
|
12364
|
-
searchParamPairs.push(encodedKey);
|
|
12365
|
-
} else {
|
|
12366
|
-
const encodedValue = encodeParamValue(value, false); // Search params encode slashes
|
|
12367
|
-
searchParamPairs.push(`${encodedKey}=${encodedValue}`);
|
|
12368
|
-
}
|
|
12369
|
-
}
|
|
12370
|
-
}
|
|
12371
|
-
|
|
12372
|
-
return searchParamPairs.join("&");
|
|
12373
|
-
};
|
|
12374
|
-
|
|
12375
12363
|
// Function to detect signals in route patterns and connect them
|
|
12376
12364
|
const detectSignals = (routePattern) => {
|
|
12377
12365
|
const signalConnections = [];
|
|
@@ -12838,6 +12826,45 @@ const matchUrl = (
|
|
|
12838
12826
|
return params;
|
|
12839
12827
|
};
|
|
12840
12828
|
|
|
12829
|
+
/**
|
|
12830
|
+
* Build query string from parameters, respecting rawUrlPart values
|
|
12831
|
+
*/
|
|
12832
|
+
const buildQueryString = (params) => {
|
|
12833
|
+
const searchParamPairs = [];
|
|
12834
|
+
|
|
12835
|
+
for (const [key, value] of Object.entries(params)) {
|
|
12836
|
+
if (value !== undefined && value !== null) {
|
|
12837
|
+
const encodedKey = encodeURIComponent(key);
|
|
12838
|
+
|
|
12839
|
+
// Handle array values - join with commas
|
|
12840
|
+
if (Array.isArray(value)) {
|
|
12841
|
+
if (value.length === 0) ; else {
|
|
12842
|
+
const encodedValue = value
|
|
12843
|
+
.map((item) => encodeURIComponent(String(item)))
|
|
12844
|
+
.join(",");
|
|
12845
|
+
searchParamPairs.push(`${encodedKey}=${encodedValue}`);
|
|
12846
|
+
}
|
|
12847
|
+
}
|
|
12848
|
+
// Handle boolean values - if true, just add the key without value
|
|
12849
|
+
else if (value === true || value === "") {
|
|
12850
|
+
searchParamPairs.push(encodedKey);
|
|
12851
|
+
}
|
|
12852
|
+
// Handle Date objects - format as YYYY-MM-DD using UTC to match new Date('YYYY-MM-DD') semantics
|
|
12853
|
+
else if (value instanceof Date) {
|
|
12854
|
+
const yyyy = value.getUTCFullYear();
|
|
12855
|
+
const mm = String(value.getUTCMonth() + 1).padStart(2, "0");
|
|
12856
|
+
const dd = String(value.getUTCDate()).padStart(2, "0");
|
|
12857
|
+
searchParamPairs.push(`${encodedKey}=${yyyy}-${mm}-${dd}`);
|
|
12858
|
+
} else {
|
|
12859
|
+
const encodedValue = encodeParamValue(value, false); // Search params encode slashes
|
|
12860
|
+
searchParamPairs.push(`${encodedKey}=${encodedValue}`);
|
|
12861
|
+
}
|
|
12862
|
+
}
|
|
12863
|
+
}
|
|
12864
|
+
|
|
12865
|
+
return searchParamPairs.join("&");
|
|
12866
|
+
};
|
|
12867
|
+
|
|
12841
12868
|
/**
|
|
12842
12869
|
* Extract search parameters from URL
|
|
12843
12870
|
*/
|
|
@@ -12905,6 +12932,17 @@ const extractSearchParams = (urlObj, queryConnectionMap) => {
|
|
|
12905
12932
|
// ?walk=0 → false
|
|
12906
12933
|
params[key] =
|
|
12907
12934
|
decodedValue === "true" || decodedValue === "1" || decodedValue === "";
|
|
12935
|
+
} else if (signalType === "date") {
|
|
12936
|
+
const decodedValue = decodeURIComponent(rawValue);
|
|
12937
|
+
// Accept both "YYYY-MM-DD" and full ISO string, always parse as UTC date
|
|
12938
|
+
const datePart = decodedValue.slice(0, 10);
|
|
12939
|
+
const [year, month, day] = datePart.split("-").map(Number);
|
|
12940
|
+
const d = new Date(Date.UTC(year, month - 1, day));
|
|
12941
|
+
params[key] = isNaN(d.getTime()) ? decodedValue : d;
|
|
12942
|
+
} else if (signalType === "datetime") {
|
|
12943
|
+
const decodedValue = decodeURIComponent(rawValue);
|
|
12944
|
+
const d = new Date(decodedValue);
|
|
12945
|
+
params[key] = isNaN(d.getTime()) ? decodedValue : d;
|
|
12908
12946
|
} else {
|
|
12909
12947
|
params[key] = decodeURIComponent(rawValue);
|
|
12910
12948
|
}
|
|
@@ -13756,8 +13794,6 @@ const route = (pattern, { searchParams } = {}) => {
|
|
|
13756
13794
|
// eslint-disable-next-line no-loop-func
|
|
13757
13795
|
const cleanupSignalUrlEffect = effect(() => {
|
|
13758
13796
|
const value = paramSignal.value;
|
|
13759
|
-
const urlValue =
|
|
13760
|
-
paramSignal.validity?.representations.url.value ?? value;
|
|
13761
13797
|
// Use peek() to avoid subscribing to URL-derived signals.
|
|
13762
13798
|
// This effect should only re-run when the param signal changes,
|
|
13763
13799
|
// not when the URL changes (which would create a cycle: signal→URL→signal).
|
|
@@ -13783,11 +13819,11 @@ const route = (pattern, { searchParams } = {}) => {
|
|
|
13783
13819
|
}
|
|
13784
13820
|
if (debug) {
|
|
13785
13821
|
console.debug(
|
|
13786
|
-
`[route] Signal->URL: ${paramName} adding custom value ${
|
|
13822
|
+
`[route] Signal->URL: ${paramName} adding custom value ${value} to URL (default: ${connection.getDefaultValue()})`,
|
|
13787
13823
|
);
|
|
13788
13824
|
}
|
|
13789
13825
|
route.replaceParams(
|
|
13790
|
-
{ [paramName]:
|
|
13826
|
+
{ [paramName]: value },
|
|
13791
13827
|
{
|
|
13792
13828
|
callReason: `${paramName} signal change on ${route}`,
|
|
13793
13829
|
isSignalChange: true,
|
|
@@ -13813,17 +13849,17 @@ const route = (pattern, { searchParams } = {}) => {
|
|
|
13813
13849
|
return;
|
|
13814
13850
|
}
|
|
13815
13851
|
|
|
13816
|
-
if (
|
|
13852
|
+
if (compareTwoJsValues(value, urlParamValue)) {
|
|
13817
13853
|
// Values already match, no sync needed
|
|
13818
13854
|
return;
|
|
13819
13855
|
}
|
|
13820
13856
|
if (debug) {
|
|
13821
13857
|
console.debug(
|
|
13822
|
-
`[route] Signal->URL: ${paramName} updating URL ${urlParamValue} -> ${
|
|
13858
|
+
`[route] Signal->URL: ${paramName} updating URL ${urlParamValue} -> ${value}`,
|
|
13823
13859
|
);
|
|
13824
13860
|
}
|
|
13825
13861
|
route.replaceParams(
|
|
13826
|
-
{ [paramName]:
|
|
13862
|
+
{ [paramName]: value },
|
|
13827
13863
|
{
|
|
13828
13864
|
callReason: `${paramName} signal change on ${route}`,
|
|
13829
13865
|
isSignalChange: true,
|