@jsenv/navi 0.29.9 → 0.29.10
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 +152 -31
- package/dist/jsenv_navi.js.map +17 -11
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -1663,6 +1663,11 @@ const generateSignalId = () => {
|
|
|
1663
1663
|
* @param {"string" | "number" | "boolean" | "object"} [options.type="string"] - Type for localStorage serialization/deserialization
|
|
1664
1664
|
* @param {number} [options.step] - For number type: step size for precision. Values will be rounded to nearest multiple of step.
|
|
1665
1665
|
* @param {Array} [options.oneOf] - Array of valid values for validation. Signal will be marked invalid if value is not in this array
|
|
1666
|
+
* @param {boolean} [options.weak=false] - The param qualifies one visit, not the screen: it is written into a
|
|
1667
|
+
* url only when explicitly named (`routeParams={{ edit: id }}`), never inherited from the signal's current
|
|
1668
|
+
* value, and it goes back to the default value when the route stops matching. Use it for params like an
|
|
1669
|
+
* "edit this one" id, where every other link to the screen must lead to the plain screen.
|
|
1670
|
+
* Incompatible with `persists` (throws).
|
|
1666
1671
|
* @param {boolean} [options.debug=false] - Enable debug logging for this signal's operations
|
|
1667
1672
|
* @returns {import("@preact/signals").Signal} A signal that can be synchronized with a source signal and/or persisted in localStorage. The signal includes a `validity` property for validation state.
|
|
1668
1673
|
*
|
|
@@ -1735,8 +1740,15 @@ const stateSignal = (defaultValue, options = {}) => {
|
|
|
1735
1740
|
default: staticFallback,
|
|
1736
1741
|
ignoreArrayOrder,
|
|
1737
1742
|
autoFix,
|
|
1743
|
+
weak = false,
|
|
1738
1744
|
} = options;
|
|
1739
1745
|
|
|
1746
|
+
if (weak && persists) {
|
|
1747
|
+
throw new TypeError(
|
|
1748
|
+
`stateSignal "${id}": weak and persists are contradictory — a weak param qualifies one visit, it cannot be restored from a previous session.`,
|
|
1749
|
+
);
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1740
1752
|
// Check if defaultValue is a signal (dynamic default) or static value
|
|
1741
1753
|
const isDynamicDefault =
|
|
1742
1754
|
defaultValue &&
|
|
@@ -2099,6 +2111,23 @@ setBaseUrl(
|
|
|
2099
2111
|
: window.location.origin,
|
|
2100
2112
|
);
|
|
2101
2113
|
|
|
2114
|
+
/**
|
|
2115
|
+
* The single place where "this param was not provided, take the signal value"
|
|
2116
|
+
* happens while building a url. A weak param qualifies one visit, not the
|
|
2117
|
+
* screen: it enters a url only when the caller names it (or when it is already
|
|
2118
|
+
* in the url being preserved), so here it always reads as absent.
|
|
2119
|
+
* Not reading the signal also keeps the built url from depending on it.
|
|
2120
|
+
*/
|
|
2121
|
+
const readSignalForUrlBuild = (connection) => {
|
|
2122
|
+
if (!connection || !connection.signal) {
|
|
2123
|
+
return undefined;
|
|
2124
|
+
}
|
|
2125
|
+
if (connection.weak) {
|
|
2126
|
+
return undefined;
|
|
2127
|
+
}
|
|
2128
|
+
return connection.signal.value;
|
|
2129
|
+
};
|
|
2130
|
+
|
|
2102
2131
|
/**
|
|
2103
2132
|
* Creates a custom route pattern matcher
|
|
2104
2133
|
*/
|
|
@@ -2173,7 +2202,7 @@ const createRoutePattern = (pattern, { searchParams = {} } = {}) => {
|
|
|
2173
2202
|
// Parameter was explicitly provided - always respect explicit parameters
|
|
2174
2203
|
continue;
|
|
2175
2204
|
}
|
|
2176
|
-
const signalValue = connection
|
|
2205
|
+
const signalValue = readSignalForUrlBuild(connection);
|
|
2177
2206
|
if (signalValue !== undefined) {
|
|
2178
2207
|
// Parameter was not provided, check signal value
|
|
2179
2208
|
resolvedParams[paramName] = signalValue;
|
|
@@ -2186,7 +2215,7 @@ const createRoutePattern = (pattern, { searchParams = {} } = {}) => {
|
|
|
2186
2215
|
// Parameter was explicitly provided - always respect explicit parameters
|
|
2187
2216
|
continue;
|
|
2188
2217
|
}
|
|
2189
|
-
const signalValue = connection
|
|
2218
|
+
const signalValue = readSignalForUrlBuild(connection);
|
|
2190
2219
|
if (signalValue !== undefined) {
|
|
2191
2220
|
// Parameter was not provided, check signal value
|
|
2192
2221
|
resolvedParams[paramName] = signalValue;
|
|
@@ -2229,7 +2258,7 @@ const createRoutePattern = (pattern, { searchParams = {} } = {}) => {
|
|
|
2229
2258
|
continue;
|
|
2230
2259
|
}
|
|
2231
2260
|
|
|
2232
|
-
const ancestorSignalValue = ancestorConnection
|
|
2261
|
+
const ancestorSignalValue = readSignalForUrlBuild(ancestorConnection);
|
|
2233
2262
|
if (
|
|
2234
2263
|
ancestorSignalValue !== undefined &&
|
|
2235
2264
|
ancestorSignalValue !== ancestorConnection.getDefaultValue()
|
|
@@ -2307,7 +2336,7 @@ const createRoutePattern = (pattern, { searchParams = {} } = {}) => {
|
|
|
2307
2336
|
if (childParam in resolvedParams) {
|
|
2308
2337
|
continue;
|
|
2309
2338
|
}
|
|
2310
|
-
const childSignalValue = childConnection
|
|
2339
|
+
const childSignalValue = readSignalForUrlBuild(childConnection);
|
|
2311
2340
|
// Only include if not already resolved and is non-default
|
|
2312
2341
|
if (
|
|
2313
2342
|
childSignalValue !== undefined &&
|
|
@@ -2349,7 +2378,7 @@ const createRoutePattern = (pattern, { searchParams = {} } = {}) => {
|
|
|
2349
2378
|
}
|
|
2350
2379
|
} else {
|
|
2351
2380
|
// Parameter not provided but signal has a value
|
|
2352
|
-
const signalValue = connection
|
|
2381
|
+
const signalValue = readSignalForUrlBuild(connection);
|
|
2353
2382
|
if (connection.isCustomValue(signalValue)) {
|
|
2354
2383
|
// Only include custom values
|
|
2355
2384
|
filtered[paramName] = signalValue;
|
|
@@ -2368,7 +2397,7 @@ const createRoutePattern = (pattern, { searchParams = {} } = {}) => {
|
|
|
2368
2397
|
}
|
|
2369
2398
|
} else {
|
|
2370
2399
|
// Parameter not provided but signal has a value
|
|
2371
|
-
const signalValue = connection
|
|
2400
|
+
const signalValue = readSignalForUrlBuild(connection);
|
|
2372
2401
|
if (connection.isCustomValue(signalValue)) {
|
|
2373
2402
|
// Only include custom values
|
|
2374
2403
|
filtered[paramName] = signalValue;
|
|
@@ -2385,7 +2414,7 @@ const createRoutePattern = (pattern, { searchParams = {} } = {}) => {
|
|
|
2385
2414
|
const canReachLiteralValue = (literalValue, params, literalPosition) => {
|
|
2386
2415
|
// Check parent's own parameters (signals and user params)
|
|
2387
2416
|
const parentCanProvide = connections.some((conn) => {
|
|
2388
|
-
const signalValue = conn
|
|
2417
|
+
const signalValue = readSignalForUrlBuild(conn);
|
|
2389
2418
|
const userValue = params[conn.paramName];
|
|
2390
2419
|
const effectiveValue = userValue !== undefined ? userValue : signalValue;
|
|
2391
2420
|
return (
|
|
@@ -2414,7 +2443,7 @@ const createRoutePattern = (pattern, { searchParams = {} } = {}) => {
|
|
|
2414
2443
|
return false;
|
|
2415
2444
|
}
|
|
2416
2445
|
return connsAtPosition.some((conn) => {
|
|
2417
|
-
const signalValue = conn
|
|
2446
|
+
const signalValue = readSignalForUrlBuild(conn);
|
|
2418
2447
|
return signalValue === literalValue && conn.isCustomValue(signalValue);
|
|
2419
2448
|
});
|
|
2420
2449
|
};
|
|
@@ -2469,7 +2498,7 @@ const createRoutePattern = (pattern, { searchParams = {} } = {}) => {
|
|
|
2469
2498
|
pathConnectionMap.get(paramName) ||
|
|
2470
2499
|
queryConnectionMap.get(paramName);
|
|
2471
2500
|
if (parentConnection) {
|
|
2472
|
-
parentParamValue = parentConnection
|
|
2501
|
+
parentParamValue = readSignalForUrlBuild(parentConnection);
|
|
2473
2502
|
}
|
|
2474
2503
|
}
|
|
2475
2504
|
|
|
@@ -2554,7 +2583,7 @@ const createRoutePattern = (pattern, { searchParams = {} } = {}) => {
|
|
|
2554
2583
|
paramValue = item.userValue;
|
|
2555
2584
|
} else {
|
|
2556
2585
|
paramName = item.paramName;
|
|
2557
|
-
paramValue = item
|
|
2586
|
+
paramValue = readSignalForUrlBuild(item);
|
|
2558
2587
|
// Only include custom parent signal values (not using defaults)
|
|
2559
2588
|
if (paramValue === undefined || !item.isCustomValue(paramValue)) {
|
|
2560
2589
|
return { isCompatible: true, shouldInclude: false };
|
|
@@ -2661,7 +2690,7 @@ const createRoutePattern = (pattern, { searchParams = {} } = {}) => {
|
|
|
2661
2690
|
if (!siblingConnection) {
|
|
2662
2691
|
continue;
|
|
2663
2692
|
}
|
|
2664
|
-
const siblingSignalValue = siblingConnection
|
|
2693
|
+
const siblingSignalValue = readSignalForUrlBuild(siblingConnection);
|
|
2665
2694
|
if (siblingSignalValue === undefined) {
|
|
2666
2695
|
continue;
|
|
2667
2696
|
}
|
|
@@ -2705,7 +2734,7 @@ const createRoutePattern = (pattern, { searchParams = {} } = {}) => {
|
|
|
2705
2734
|
const explicitValue = params[paramName];
|
|
2706
2735
|
const connection =
|
|
2707
2736
|
pathConnectionMap.get(paramName) || queryConnectionMap.get(paramName);
|
|
2708
|
-
const signalValue = connection
|
|
2737
|
+
const signalValue = readSignalForUrlBuild(connection);
|
|
2709
2738
|
|
|
2710
2739
|
// Check if the parameter has the required value
|
|
2711
2740
|
if (
|
|
@@ -2759,7 +2788,7 @@ const createRoutePattern = (pattern, { searchParams = {} } = {}) => {
|
|
|
2759
2788
|
hasActiveParams = true;
|
|
2760
2789
|
}
|
|
2761
2790
|
} else {
|
|
2762
|
-
const signalValue = connection
|
|
2791
|
+
const signalValue = readSignalForUrlBuild(connection);
|
|
2763
2792
|
if (signalValue !== undefined) {
|
|
2764
2793
|
// No explicit override - use signal value
|
|
2765
2794
|
childParams[paramName] = signalValue;
|
|
@@ -2976,7 +3005,7 @@ const createRoutePattern = (pattern, { searchParams = {} } = {}) => {
|
|
|
2976
3005
|
}
|
|
2977
3006
|
// If explicitly undefined, don't include it (which means don't use child route)
|
|
2978
3007
|
} else {
|
|
2979
|
-
const signalValue = connection
|
|
3008
|
+
const signalValue = readSignalForUrlBuild(connection);
|
|
2980
3009
|
if (
|
|
2981
3010
|
signalValue !== undefined &&
|
|
2982
3011
|
connection.isCustomValue(signalValue)
|
|
@@ -3016,7 +3045,7 @@ const createRoutePattern = (pattern, { searchParams = {} } = {}) => {
|
|
|
3016
3045
|
continue; // Already have this parameter
|
|
3017
3046
|
}
|
|
3018
3047
|
|
|
3019
|
-
const signalValue = connection
|
|
3048
|
+
const signalValue = readSignalForUrlBuild(connection);
|
|
3020
3049
|
// Only include custom signal values (not using defaults)
|
|
3021
3050
|
if (
|
|
3022
3051
|
signalValue !== undefined &&
|
|
@@ -3198,7 +3227,7 @@ const createRoutePattern = (pattern, { searchParams = {} } = {}) => {
|
|
|
3198
3227
|
paramName,
|
|
3199
3228
|
connection,
|
|
3200
3229
|
] of childPatternObj.pathConnectionMap) {
|
|
3201
|
-
const signalValue = connection
|
|
3230
|
+
const signalValue = readSignalForUrlBuild(connection);
|
|
3202
3231
|
if (
|
|
3203
3232
|
signalValue !== undefined &&
|
|
3204
3233
|
connection.isCustomValue(signalValue)
|
|
@@ -3220,7 +3249,7 @@ const createRoutePattern = (pattern, { searchParams = {} } = {}) => {
|
|
|
3220
3249
|
paramName,
|
|
3221
3250
|
connection,
|
|
3222
3251
|
] of childPatternObj.queryConnectionMap) {
|
|
3223
|
-
const signalValue = connection
|
|
3252
|
+
const signalValue = readSignalForUrlBuild(connection);
|
|
3224
3253
|
if (
|
|
3225
3254
|
signalValue !== undefined &&
|
|
3226
3255
|
connection.isCustomValue(signalValue)
|
|
@@ -3840,7 +3869,7 @@ const createRoutePattern = (pattern, { searchParams = {} } = {}) => {
|
|
|
3840
3869
|
);
|
|
3841
3870
|
for (const conn of targetAncestor.connections) {
|
|
3842
3871
|
console.debug(
|
|
3843
|
-
`[${pattern}] tryDirectOptimization: Target connection ${conn.paramName}: value=${conn
|
|
3872
|
+
`[${pattern}] tryDirectOptimization: Target connection ${conn.paramName}: value=${readSignalForUrlBuild(conn)}, isCustom=${conn.isCustomValue(readSignalForUrlBuild(conn))}`,
|
|
3844
3873
|
);
|
|
3845
3874
|
}
|
|
3846
3875
|
}
|
|
@@ -3857,7 +3886,7 @@ const createRoutePattern = (pattern, { searchParams = {} } = {}) => {
|
|
|
3857
3886
|
}
|
|
3858
3887
|
|
|
3859
3888
|
// Only include if not already processed and has custom value (not default)
|
|
3860
|
-
const signalValue = connection
|
|
3889
|
+
const signalValue = readSignalForUrlBuild(connection);
|
|
3861
3890
|
if (signalValue !== undefined) {
|
|
3862
3891
|
// Don't include path parameters that correspond to literal segments we're optimizing away
|
|
3863
3892
|
const targetParam = targetParams.find((p) => p.name === paramName);
|
|
@@ -3903,7 +3932,7 @@ const createRoutePattern = (pattern, { searchParams = {} } = {}) => {
|
|
|
3903
3932
|
}
|
|
3904
3933
|
|
|
3905
3934
|
// Only inherit custom values (not defaults) that we don't already have
|
|
3906
|
-
const signalValue = connection
|
|
3935
|
+
const signalValue = readSignalForUrlBuild(connection);
|
|
3907
3936
|
if (
|
|
3908
3937
|
signalValue !== undefined &&
|
|
3909
3938
|
connection.isCustomValue(signalValue)
|
|
@@ -3989,7 +4018,7 @@ const createRoutePattern = (pattern, { searchParams = {} } = {}) => {
|
|
|
3989
4018
|
}
|
|
3990
4019
|
|
|
3991
4020
|
// Only inherit if we don't have this param and parent has custom value (not default)
|
|
3992
|
-
const parentSignalValue = parentConnection
|
|
4021
|
+
const parentSignalValue = readSignalForUrlBuild(parentConnection);
|
|
3993
4022
|
if (
|
|
3994
4023
|
parentSignalValue !== undefined &&
|
|
3995
4024
|
parentConnection.isCustomValue(parentSignalValue)
|
|
@@ -4075,7 +4104,36 @@ const createRoutePattern = (pattern, { searchParams = {} } = {}) => {
|
|
|
4075
4104
|
// search params are updated. When buildMostPreciseUrl performs an ancestor
|
|
4076
4105
|
// optimisation (e.g. "/map/isochrone/compare" → "/map/isochrone") it is trusted
|
|
4077
4106
|
// as-is because the built pathname will differ from the route's own base pathname.
|
|
4107
|
+
// Weak params are never inherited from their signal, but a url that already
|
|
4108
|
+
// carries one keeps it: staying on the same screen while another param
|
|
4109
|
+
// changes must not end the visit this param qualifies.
|
|
4110
|
+
const carryOverWeakParams = (currentUrl, params) => {
|
|
4111
|
+
let paramsWithWeak = params;
|
|
4112
|
+
for (const [paramName, connection] of [
|
|
4113
|
+
...pathConnectionMap,
|
|
4114
|
+
...queryConnectionMap,
|
|
4115
|
+
]) {
|
|
4116
|
+
if (!connection.weak || paramName in paramsWithWeak) {
|
|
4117
|
+
continue;
|
|
4118
|
+
}
|
|
4119
|
+
const currentValue = connection.signal.peek();
|
|
4120
|
+
if (currentValue === undefined) {
|
|
4121
|
+
continue;
|
|
4122
|
+
}
|
|
4123
|
+
const currentParams = applyOn(currentUrl);
|
|
4124
|
+
if (!currentParams || currentParams[paramName] === undefined) {
|
|
4125
|
+
continue;
|
|
4126
|
+
}
|
|
4127
|
+
if (paramsWithWeak === params) {
|
|
4128
|
+
paramsWithWeak = { ...params };
|
|
4129
|
+
}
|
|
4130
|
+
paramsWithWeak[paramName] = currentParams[paramName];
|
|
4131
|
+
}
|
|
4132
|
+
return paramsWithWeak;
|
|
4133
|
+
};
|
|
4134
|
+
|
|
4078
4135
|
const buildUrlPreservingPath = (currentUrl, params = {}) => {
|
|
4136
|
+
params = carryOverWeakParams(currentUrl, params);
|
|
4079
4137
|
const relativeBuiltUrl = buildMostPreciseUrl(params);
|
|
4080
4138
|
if (!currentUrl) {
|
|
4081
4139
|
return resolveRouteUrl(relativeBuiltUrl);
|
|
@@ -4332,7 +4390,7 @@ const parsePattern = (pattern, { pathConnectionMap, queryConnectionMap }) => {
|
|
|
4332
4390
|
if (
|
|
4333
4391
|
connection &&
|
|
4334
4392
|
connection.signal &&
|
|
4335
|
-
connection
|
|
4393
|
+
readSignalForUrlBuild(connection) === undefined &&
|
|
4336
4394
|
!hasDefault
|
|
4337
4395
|
) {
|
|
4338
4396
|
isOptional = true;
|
|
@@ -5516,7 +5574,10 @@ const route = (pattern, { searchParams } = {}) => {
|
|
|
5516
5574
|
});
|
|
5517
5575
|
};
|
|
5518
5576
|
route.matchesParams = (providedParams) => {
|
|
5519
|
-
|
|
5577
|
+
// paramsSignal (not route.params) so a component calling this during render
|
|
5578
|
+
// subscribes to param changes: a navigation from /games/me/a to /games/me/b
|
|
5579
|
+
// leaves matchingSignal untouched, only the params change.
|
|
5580
|
+
const currentParams = route.paramsSignal.value;
|
|
5520
5581
|
const resolvedParams = routePattern.resolveParams({
|
|
5521
5582
|
...currentParams,
|
|
5522
5583
|
...providedParams,
|
|
@@ -5883,6 +5944,22 @@ This prevents cross-test pollution and ensures clean state.`,
|
|
|
5883
5944
|
}
|
|
5884
5945
|
}
|
|
5885
5946
|
|
|
5947
|
+
// A weak param qualifies one visit: leaving the route ends it,
|
|
5948
|
+
// whatever the family and whatever the default. Coming back by a
|
|
5949
|
+
// url that does not carry the param comes back to a blank screen.
|
|
5950
|
+
if (connection.weak) {
|
|
5951
|
+
if (!parameterExtractedByMatchingRoute) {
|
|
5952
|
+
const defaultValue = connection.getDefaultValue();
|
|
5953
|
+
if (debug) {
|
|
5954
|
+
console.debug(
|
|
5955
|
+
`[route] weak param ${paramName}: route no longer matching, back to ${defaultValue}`,
|
|
5956
|
+
);
|
|
5957
|
+
}
|
|
5958
|
+
paramSignal.value = defaultValue;
|
|
5959
|
+
}
|
|
5960
|
+
continue;
|
|
5961
|
+
}
|
|
5962
|
+
|
|
5886
5963
|
// Only reset signal if:
|
|
5887
5964
|
// 1. We're navigating within the same route family (not to completely unrelated routes)
|
|
5888
5965
|
// 2. AND no matching route extracts this parameter from URL
|
|
@@ -31360,6 +31437,15 @@ const useActionAsyncData = (action, {
|
|
|
31360
31437
|
return [staleData, true, undefined];
|
|
31361
31438
|
}
|
|
31362
31439
|
|
|
31440
|
+
// An action without params has nothing to run and no one to start it: this is
|
|
31441
|
+
// where a route action lands when its params getter returns false. It is not a
|
|
31442
|
+
// load in progress, so there is nothing to wait for — suspending here would
|
|
31443
|
+
// throw a promise that never settles, and the whole <Loading> subtree would
|
|
31444
|
+
// stay hidden for good, silently.
|
|
31445
|
+
if (runningState !== RUNNING && action.paramsSignal.peek() === undefined) {
|
|
31446
|
+
return [action.dataSignal.peek(), false, undefined];
|
|
31447
|
+
}
|
|
31448
|
+
|
|
31363
31449
|
// IDLE or RUNNING with loadingEffect: "delegate" — suspend
|
|
31364
31450
|
const reason = runningState === RUNNING ? "loading" : "idle";
|
|
31365
31451
|
loadingRef.current = {
|
|
@@ -33534,11 +33620,12 @@ const collectBranches = children => {
|
|
|
33534
33620
|
type: "container",
|
|
33535
33621
|
node: child
|
|
33536
33622
|
};
|
|
33623
|
+
const guardMatching = route ? route.matchingSignal.value : false;
|
|
33537
33624
|
if (!matchingBranch) {
|
|
33538
33625
|
if (matchingChild) {
|
|
33539
33626
|
// Real leaf match inside — always select this container
|
|
33540
33627
|
matchingBranch = branch;
|
|
33541
|
-
} else if (
|
|
33628
|
+
} else if (guardMatching) {
|
|
33542
33629
|
// No leaf match but an explicit route guard matches — select this
|
|
33543
33630
|
// container so it can render its own fallback inside its layout
|
|
33544
33631
|
matchingBranch = branch;
|
|
@@ -33556,7 +33643,12 @@ const collectBranches = children => {
|
|
|
33556
33643
|
type: "leaf",
|
|
33557
33644
|
node: child
|
|
33558
33645
|
};
|
|
33559
|
-
|
|
33646
|
+
// every signal is read even once a match is found: reading is what
|
|
33647
|
+
// subscribes the container to it, and a branch that is skipped today is
|
|
33648
|
+
// the one that must wake the container up tomorrow
|
|
33649
|
+
const matching = route.matchingSignal.value;
|
|
33650
|
+
const paramsMatching = routeParams ? route.matchesParams(routeParams) : true;
|
|
33651
|
+
if (!matchingBranch && matching && paramsMatching) {
|
|
33560
33652
|
matchingBranch = branch;
|
|
33561
33653
|
}
|
|
33562
33654
|
}
|
|
@@ -52047,6 +52139,10 @@ const css$p = /* css */`
|
|
|
52047
52139
|
keyboard here (see navi-focus-delegate below). */
|
|
52048
52140
|
outline-color: var(--navi-focus-outline-color);
|
|
52049
52141
|
outline-offset: 0px;
|
|
52142
|
+
/* No grey flash under a finger: what a press does is said by the chevron's
|
|
52143
|
+
own background, and the browser's rectangle is drawn square over corners
|
|
52144
|
+
that are round. Inherited, so the three pieces inside get it too. */
|
|
52145
|
+
-webkit-tap-highlight-color: var(--navi-control-tap-highlight-color);
|
|
52050
52146
|
}
|
|
52051
52147
|
/* The middle holds the keyboard, and this box wears its ring: whatever is in
|
|
52052
52148
|
there fills it, so a ring of its own would be drawn a pixel inside this
|
|
@@ -52173,7 +52269,12 @@ const css$p = /* css */`
|
|
|
52173
52269
|
border-radius: 0;
|
|
52174
52270
|
cursor: pointer;
|
|
52175
52271
|
}
|
|
52176
|
-
|
|
52272
|
+
/* Said as data-hover rather than :hover: a touch browser synthesizes the
|
|
52273
|
+
enter and never the leave, so a CSS :hover would stay grey under the last
|
|
52274
|
+
chevron pressed until something else was touched. What tracks it (see
|
|
52275
|
+
pseudo_styles.js) knows there is no hover on such a device and simply does
|
|
52276
|
+
not set the attribute. */
|
|
52277
|
+
.navi_picker_spin > .navi_picker_spin_way_out[data-hover] {
|
|
52177
52278
|
background: color-mix(in srgb, currentColor 8%, transparent);
|
|
52178
52279
|
}
|
|
52179
52280
|
/* Nothing that way: still there, still pressable — pressing it is how one
|
|
@@ -52199,22 +52300,28 @@ const css$p = /* css */`
|
|
|
52199
52300
|
of a rounded spin is rounded there too, and nowhere else — the two corners
|
|
52200
52301
|
it does not own stay at the 0 above. Said with inherit rather than clipped
|
|
52201
52302
|
away with overflow, which would cut the focus ring of the very button it
|
|
52202
|
-
rounds.
|
|
52303
|
+
rounds.
|
|
52304
|
+
Which chevron is which is asked of the chevron itself (data-way-out) rather
|
|
52305
|
+
than of its place among its siblings: the loading outline is a <span> too
|
|
52306
|
+
and it is written first, so :first-of-type named IT and the chevron at the
|
|
52307
|
+
start went unrounded. */
|
|
52203
52308
|
.navi_picker_spin:not([data-vertical])
|
|
52204
|
-
> .navi_picker_spin_way_out
|
|
52309
|
+
> .navi_picker_spin_way_out[data-way-out="start"] {
|
|
52205
52310
|
border-start-start-radius: inherit;
|
|
52206
52311
|
border-end-start-radius: inherit;
|
|
52207
52312
|
}
|
|
52208
52313
|
.navi_picker_spin:not([data-vertical])
|
|
52209
|
-
> .navi_picker_spin_way_out
|
|
52314
|
+
> .navi_picker_spin_way_out[data-way-out="end"] {
|
|
52210
52315
|
border-start-end-radius: inherit;
|
|
52211
52316
|
border-end-end-radius: inherit;
|
|
52212
52317
|
}
|
|
52213
|
-
.navi_picker_spin[data-vertical]
|
|
52318
|
+
.navi_picker_spin[data-vertical]
|
|
52319
|
+
> .navi_picker_spin_way_out[data-way-out="start"] {
|
|
52214
52320
|
border-start-start-radius: inherit;
|
|
52215
52321
|
border-start-end-radius: inherit;
|
|
52216
52322
|
}
|
|
52217
|
-
.navi_picker_spin[data-vertical]
|
|
52323
|
+
.navi_picker_spin[data-vertical]
|
|
52324
|
+
> .navi_picker_spin_way_out[data-way-out="end"] {
|
|
52218
52325
|
border-end-end-radius: inherit;
|
|
52219
52326
|
border-end-start-radius: inherit;
|
|
52220
52327
|
}
|
|
@@ -52455,6 +52562,7 @@ const Spin = ({
|
|
|
52455
52562
|
const wayOut = atStart => {
|
|
52456
52563
|
const isNext = atStart ? startIsNext : !startIsNext;
|
|
52457
52564
|
return jsx(WayOut, {
|
|
52565
|
+
atStart: atStart,
|
|
52458
52566
|
unavailableMessage: wayOutMessage(atStart ? startAllowed : endAllowed, isNext ? "spin.nothing_after" : "spin.nothing_before"),
|
|
52459
52567
|
label: isNext ? nextLabel ?? naviI18n("spin.next") : previousLabel ?? naviI18n("spin.previous"),
|
|
52460
52568
|
onPress: e => {
|
|
@@ -52632,6 +52740,7 @@ const Spin = ({
|
|
|
52632
52740
|
// the container's own tabIndex, or the field's), where the arrows already mean
|
|
52633
52741
|
// this.
|
|
52634
52742
|
const WayOut = ({
|
|
52743
|
+
atStart,
|
|
52635
52744
|
commandFor,
|
|
52636
52745
|
unavailableMessage,
|
|
52637
52746
|
label,
|
|
@@ -52640,6 +52749,17 @@ const WayOut = ({
|
|
|
52640
52749
|
}) => jsx(Box, {
|
|
52641
52750
|
as: "span",
|
|
52642
52751
|
baseClassName: "navi_picker_spin_way_out"
|
|
52752
|
+
// Which end of the box it sits in, said by the chevron rather than read
|
|
52753
|
+
// from its place among its siblings: that is what the corners it is
|
|
52754
|
+
// rounded by are keyed on (see the CSS above).
|
|
52755
|
+
,
|
|
52756
|
+
|
|
52757
|
+
"data-way-out": atStart ? "start" : "end"
|
|
52758
|
+
// Tracked rather than left to CSS :hover, which stays on after a tap on a
|
|
52759
|
+
// touch device (see the CSS above).
|
|
52760
|
+
,
|
|
52761
|
+
|
|
52762
|
+
pseudoClasses: WAY_OUT_PSEUDO_CLASSES
|
|
52643
52763
|
// Announced as a button because that is what it is to whoever cannot see
|
|
52644
52764
|
// the chevron — and marked unavailable rather than removed when there is
|
|
52645
52765
|
// nothing that way, so it keeps its place.
|
|
@@ -52691,6 +52811,7 @@ const WayOut = ({
|
|
|
52691
52811
|
})
|
|
52692
52812
|
});
|
|
52693
52813
|
const PICKER_SPIN_PSEUDO_CLASSES = [":hover", ":focus-visible"];
|
|
52814
|
+
const WAY_OUT_PSEUDO_CLASSES = [":hover"];
|
|
52694
52815
|
|
|
52695
52816
|
// A padding written on this box would sit between its border and the chevrons,
|
|
52696
52817
|
// which are meant to reach the corners they are rounded by — so each padding
|