@jsenv/navi 0.29.458 → 0.29.460
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/dev/jsenv_navi.js +343 -99
- package/dist/dev/jsenv_navi.js.map +19 -20
- package/dist/jsenv_navi.js +343 -99
- package/dist/jsenv_navi.js.map +19 -20
- package/docs/actions.md +15 -0
- package/docs/mobile_touch.md +56 -0
- package/docs/navigation.md +4 -2
- package/docs/popup_open.md +17 -0
- package/docs/resource.md +21 -0
- package/package.json +1 -1
package/dist/dev/jsenv_navi.js
CHANGED
|
@@ -1927,6 +1927,27 @@ const markErrorAsTakenByRender = (error) => {
|
|
|
1927
1927
|
}
|
|
1928
1928
|
};
|
|
1929
1929
|
|
|
1930
|
+
/**
|
|
1931
|
+
* A prerun's failure is owed to no screen: nothing on screen asked for that
|
|
1932
|
+
* run, so the component that owns the read asks again rather than show a
|
|
1933
|
+
* failure from before it existed (see `run` in use_async_data.jsx). Taken
|
|
1934
|
+
* once, so the run asking again fails as its own even when it fails with the
|
|
1935
|
+
* same error object. Held by the error, it lasts as long as the failure stays
|
|
1936
|
+
* on the action: a later run replaces it, whether or not an owner ever came.
|
|
1937
|
+
*/
|
|
1938
|
+
const prerunFailureSet = new WeakSet();
|
|
1939
|
+
const markErrorAsPrerunFailure = (error) => {
|
|
1940
|
+
if (error && typeof error === "object") {
|
|
1941
|
+
prerunFailureSet.add(error);
|
|
1942
|
+
}
|
|
1943
|
+
};
|
|
1944
|
+
const takePrerunFailure = (error) => {
|
|
1945
|
+
if (!error || typeof error !== "object") {
|
|
1946
|
+
return false;
|
|
1947
|
+
}
|
|
1948
|
+
return prerunFailureSet.delete(error);
|
|
1949
|
+
};
|
|
1950
|
+
|
|
1930
1951
|
/**
|
|
1931
1952
|
* When the answer "nobody took it" is final.
|
|
1932
1953
|
*
|
|
@@ -4352,6 +4373,9 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
4352
4373
|
console.log(`"${action}": failed (error: ${error})`);
|
|
4353
4374
|
}
|
|
4354
4375
|
error.action = action;
|
|
4376
|
+
if (isPrerun) {
|
|
4377
|
+
markErrorAsPrerunFailure(error);
|
|
4378
|
+
}
|
|
4355
4379
|
batch(() => {
|
|
4356
4380
|
errorSignal.value = error;
|
|
4357
4381
|
runningStateSignal.value = FAILED;
|
|
@@ -4928,6 +4952,25 @@ const runUnwatched = (startRun) => {
|
|
|
4928
4952
|
return result;
|
|
4929
4953
|
};
|
|
4930
4954
|
|
|
4955
|
+
/**
|
|
4956
|
+
* Starts a prerun navi has nobody to await. Its failure is not reported:
|
|
4957
|
+
* nothing on screen asked for it. It stays on the action, marked as a
|
|
4958
|
+
* prerun's (see markErrorAsPrerunFailure), where whatever owns the read asks
|
|
4959
|
+
* again.
|
|
4960
|
+
*/
|
|
4961
|
+
const prerunUnwatched = (startPrerun) => {
|
|
4962
|
+
let result;
|
|
4963
|
+
try {
|
|
4964
|
+
result = startPrerun();
|
|
4965
|
+
} catch {
|
|
4966
|
+
return undefined;
|
|
4967
|
+
}
|
|
4968
|
+
if (result && typeof result.catch === "function") {
|
|
4969
|
+
result.catch(() => {});
|
|
4970
|
+
}
|
|
4971
|
+
return result;
|
|
4972
|
+
};
|
|
4973
|
+
|
|
4931
4974
|
/**
|
|
4932
4975
|
* Reactively runs an action whenever the params derived from signals change.
|
|
4933
4976
|
*
|
|
@@ -11310,27 +11353,29 @@ const updateDocumentState = (value) => {
|
|
|
11310
11353
|
* `state`:
|
|
11311
11354
|
* - `undefined` — a neutral navigation: a link click, a `replaceUrl` writing a
|
|
11312
11355
|
* search param. A **replace** stays on the entry the document is already on,
|
|
11313
|
-
* so what that entry holds stays with it. A **push** opens a NEW entry
|
|
11314
|
-
*
|
|
11315
|
-
*
|
|
11316
|
-
*
|
|
11317
|
-
*
|
|
11356
|
+
* so what that entry holds stays with it. A **push** opens a NEW entry. One
|
|
11357
|
+
* arriving at another page (`isArrival`, see scroll_restoration.js) starts
|
|
11358
|
+
* empty: what was written for the entry being left — an open dialog, an
|
|
11359
|
+
* expanded picker (see `useNavState`) — describes that entry alone, and
|
|
11360
|
+
* carried forward it would reopen on the next screen, and on the one after
|
|
11361
|
+
* that, until something mounting the same id opens out of nowhere. A push
|
|
11362
|
+
* that keeps the page (a layer bound to a search param, opened over it)
|
|
11363
|
+
* carries the state: whatever holds it is still mounted under the new
|
|
11364
|
+
* address, and an empty entry would close it there — an expanded row
|
|
11365
|
+
* collapsing under the layer, expanding again when the back takes it away.
|
|
11318
11366
|
* - `null` — an explicit reset.
|
|
11319
11367
|
* - an object — built by the caller (`enter()`/`leave()` copy the current state
|
|
11320
11368
|
* themselves), taken as given.
|
|
11321
11369
|
*
|
|
11322
|
-
* `
|
|
11323
|
-
*
|
|
11324
|
-
* The push/replace split is the Navigation API's own rule for a `navigate()`
|
|
11325
|
-
* carrying no state — which is why via_navigation.js gets it from the browser
|
|
11326
|
-
* and via_history.js has to spell it out to say the same thing.
|
|
11370
|
+
* `currentState` is what the entry being left holds. `sharedState` always
|
|
11371
|
+
* wins: it describes the document, not the entry.
|
|
11327
11372
|
*/
|
|
11328
11373
|
const resolveEffectiveDocumentState = (
|
|
11329
11374
|
state,
|
|
11330
|
-
{ navigationType, currentState, sharedState },
|
|
11375
|
+
{ navigationType, isArrival, currentState, sharedState },
|
|
11331
11376
|
) => {
|
|
11332
11377
|
if (state === undefined) {
|
|
11333
|
-
if (navigationType === "push") {
|
|
11378
|
+
if (navigationType === "push" && isArrival) {
|
|
11334
11379
|
return sharedState;
|
|
11335
11380
|
}
|
|
11336
11381
|
return { ...(currentState || {}), ...sharedState };
|
|
@@ -11874,6 +11919,8 @@ const startAtTop = (url, { from } = {}) => {
|
|
|
11874
11919
|
positionByUrl.set(href, { x: 0, y: 0 });
|
|
11875
11920
|
window.scrollTo({ top: 0, left: 0, behavior: "instant" });
|
|
11876
11921
|
};
|
|
11922
|
+
// Also what decides whether a push carries the state of the page it keeps
|
|
11923
|
+
// (see resolveEffectiveDocumentState).
|
|
11877
11924
|
const isArrival = (url, { from }) => {
|
|
11878
11925
|
if (
|
|
11879
11926
|
from !== undefined &&
|
|
@@ -12309,7 +12356,8 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
12309
12356
|
|
|
12310
12357
|
const applyRoutingTask = (url, options) => {
|
|
12311
12358
|
// Read before the history is written: the url the reader is being taken
|
|
12312
|
-
// away from, which is what decides where a push lands (see startAtTop)
|
|
12359
|
+
// away from, which is what decides where a push lands (see startAtTop) and
|
|
12360
|
+
// whether it carries the state (see resolveEffectiveDocumentState).
|
|
12313
12361
|
const urlLeft = readAddress();
|
|
12314
12362
|
const {
|
|
12315
12363
|
reason,
|
|
@@ -12329,6 +12377,7 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
12329
12377
|
markUrlAsVisited(url);
|
|
12330
12378
|
const effectiveState = resolveEffectiveDocumentState(state, {
|
|
12331
12379
|
navigationType,
|
|
12380
|
+
isArrival: isArrival(url, { from: urlLeft }),
|
|
12332
12381
|
currentState: getDocumentState(),
|
|
12333
12382
|
sharedState: {
|
|
12334
12383
|
jsenv_visited_urls: Array.from(visitedUrlSet),
|
|
@@ -26464,10 +26513,24 @@ const resolveRerunOn = (rerunOn, inheritedRerunOn = defaultRerunOn) => {
|
|
|
26464
26513
|
};
|
|
26465
26514
|
};
|
|
26466
26515
|
|
|
26516
|
+
// What a `dependencies` entry names are callbacks, not verbs (`rerunOn` names
|
|
26517
|
+
// verbs, where `PATCH` covers `PATCH_MANY`): one callback of a resource can be
|
|
26518
|
+
// the write that creates (a move into a new item) while its sibling only edits.
|
|
26519
|
+
const WRITE_CALLBACK_NAMES = [
|
|
26520
|
+
"POST",
|
|
26521
|
+
"POST_MANY",
|
|
26522
|
+
"PUT",
|
|
26523
|
+
"PUT_MANY",
|
|
26524
|
+
"PATCH",
|
|
26525
|
+
"PATCH_MANY",
|
|
26526
|
+
"DELETE",
|
|
26527
|
+
"DELETE_MANY",
|
|
26528
|
+
];
|
|
26529
|
+
|
|
26467
26530
|
// This handles ALL resource lifecycle logic (rerun/reset) across all resources
|
|
26468
26531
|
const createResourceLifecycleManager = () => {
|
|
26469
26532
|
const registeredResources = new Map(); // Map<resourceInstance, lifecycleConfig>
|
|
26470
|
-
const resourceDependencies = new Map(); // Map<resourceInstance, Set<
|
|
26533
|
+
const resourceDependencies = new Map(); // Map<resourceInstance, Map<dependentResource, Set<writeCallbackName>>> — user-configured
|
|
26471
26534
|
const scopedManyParents = new Map(); // Map<childResource, Set<{resource, propertyName}>> — auto from scopedMany
|
|
26472
26535
|
|
|
26473
26536
|
const registerResource = (resourceScope, config) => {
|
|
@@ -26486,16 +26549,38 @@ const createResourceLifecycleManager = () => {
|
|
|
26486
26549
|
rangeReaderSet: new Set(),
|
|
26487
26550
|
});
|
|
26488
26551
|
|
|
26489
|
-
|
|
26490
|
-
|
|
26491
|
-
|
|
26492
|
-
|
|
26493
|
-
|
|
26494
|
-
|
|
26495
|
-
|
|
26552
|
+
for (const dependency of dependencies) {
|
|
26553
|
+
const [dependencyScope, writeNames] = resolveDependency(
|
|
26554
|
+
dependency,
|
|
26555
|
+
resourceScope,
|
|
26556
|
+
);
|
|
26557
|
+
let dependentMap = resourceDependencies.get(dependencyScope);
|
|
26558
|
+
if (!dependentMap) {
|
|
26559
|
+
dependentMap = new Map();
|
|
26560
|
+
resourceDependencies.set(dependencyScope, dependentMap);
|
|
26561
|
+
}
|
|
26562
|
+
let writeNameSet = dependentMap.get(resourceScope);
|
|
26563
|
+
if (!writeNameSet) {
|
|
26564
|
+
writeNameSet = new Set();
|
|
26565
|
+
dependentMap.set(resourceScope, writeNameSet);
|
|
26566
|
+
}
|
|
26567
|
+
for (const writeName of writeNames) {
|
|
26568
|
+
writeNameSet.add(writeName);
|
|
26496
26569
|
}
|
|
26497
26570
|
}
|
|
26498
26571
|
};
|
|
26572
|
+
// Whether `resourceScope` listed the facade that wrote, with this write
|
|
26573
|
+
// among the ones that count.
|
|
26574
|
+
const isWriteHeardBy = (resourceScope, triggeringAction, triggerScope) => {
|
|
26575
|
+
const writeNameSet = resourceDependencies
|
|
26576
|
+
.get(triggerScope)
|
|
26577
|
+
?.get(resourceScope);
|
|
26578
|
+
if (!writeNameSet) {
|
|
26579
|
+
return false;
|
|
26580
|
+
}
|
|
26581
|
+
const { verb, isMany } = triggeringAction.meta;
|
|
26582
|
+
return writeNameSet.has(isMany ? `${verb}_MANY` : verb);
|
|
26583
|
+
};
|
|
26499
26584
|
// Only the reader the resource exposes is registered; the ones `bindParams`
|
|
26500
26585
|
// makes share its signal (see resource_range_reader.js).
|
|
26501
26586
|
const registerRangeReader = (resourceScope, rangeReader) => {
|
|
@@ -26536,17 +26621,18 @@ const createResourceLifecycleManager = () => {
|
|
|
26536
26621
|
triggerVerb === "PATCH") &&
|
|
26537
26622
|
config.uniqueKeys.length > 0;
|
|
26538
26623
|
|
|
26539
|
-
const
|
|
26540
|
-
|
|
26541
|
-
|
|
26542
|
-
|
|
26624
|
+
const isHeardDependency = isWriteHeardBy(
|
|
26625
|
+
resourceScope,
|
|
26626
|
+
triggeringAction,
|
|
26627
|
+
triggerResourceScope,
|
|
26628
|
+
);
|
|
26543
26629
|
|
|
26544
26630
|
if (
|
|
26545
26631
|
!shouldRerunGetMany &&
|
|
26546
26632
|
!shouldRerunGet &&
|
|
26547
26633
|
triggerVerb !== "DELETE" &&
|
|
26548
26634
|
!hasUniqueKeyAutorerun &&
|
|
26549
|
-
!
|
|
26635
|
+
!isHeardDependency
|
|
26550
26636
|
) {
|
|
26551
26637
|
continue;
|
|
26552
26638
|
}
|
|
@@ -26667,21 +26753,10 @@ const createResourceLifecycleManager = () => {
|
|
|
26667
26753
|
}
|
|
26668
26754
|
|
|
26669
26755
|
// Cross-resource dependency effects: rerun dependent GET / GET_MANY
|
|
26670
|
-
//
|
|
26671
|
-
//
|
|
26672
|
-
// (POST, PUT, PATCH, DELETE) on the dependency should trigger a rerun.
|
|
26756
|
+
// after a write the dependency counts — every write for a resource
|
|
26757
|
+
// listed bare, the callbacks named in `on` otherwise.
|
|
26673
26758
|
{
|
|
26674
|
-
if (
|
|
26675
|
-
triggerResourceScope &&
|
|
26676
|
-
resourceDependencies
|
|
26677
|
-
.get(triggerResourceScope)
|
|
26678
|
-
?.has(resourceScope) &&
|
|
26679
|
-
(triggerVerb === "POST" ||
|
|
26680
|
-
triggerVerb === "PUT" ||
|
|
26681
|
-
triggerVerb === "PATCH" ||
|
|
26682
|
-
triggerVerb === "DELETE") &&
|
|
26683
|
-
candidateVerb === "GET"
|
|
26684
|
-
) {
|
|
26759
|
+
if (isHeardDependency && candidateVerb === "GET") {
|
|
26685
26760
|
actionsToRerun.add(actionCandidate);
|
|
26686
26761
|
reasonSet.add("dependency autorerun");
|
|
26687
26762
|
continue;
|
|
@@ -26758,9 +26833,10 @@ const createResourceLifecycleManager = () => {
|
|
|
26758
26833
|
continue;
|
|
26759
26834
|
}
|
|
26760
26835
|
const isSameResource = resourceScope === triggerResourceScope;
|
|
26761
|
-
const isDependent =
|
|
26762
|
-
|
|
26763
|
-
|
|
26836
|
+
const isDependent = isWriteHeardBy(
|
|
26837
|
+
resourceScope,
|
|
26838
|
+
triggeringAction,
|
|
26839
|
+
triggerResourceScope,
|
|
26764
26840
|
);
|
|
26765
26841
|
if (!isSameResource && !isDependent) {
|
|
26766
26842
|
continue;
|
|
@@ -26825,6 +26901,35 @@ const shouldRerunAfter = (rerunConfig, verb) => {
|
|
|
26825
26901
|
}
|
|
26826
26902
|
return false;
|
|
26827
26903
|
};
|
|
26904
|
+
// A `dependencies` entry: a resource (every write counts), or
|
|
26905
|
+
// `{ resource, on }` naming the write callbacks that do. A name that cannot
|
|
26906
|
+
// fire throws here: a dependency that never fires leaves a read on its old
|
|
26907
|
+
// value with nothing to say so.
|
|
26908
|
+
const resolveDependency = (dependency, dependentScope) => {
|
|
26909
|
+
if (!Object.hasOwn(dependency, "resource")) {
|
|
26910
|
+
return [dependency, WRITE_CALLBACK_NAMES];
|
|
26911
|
+
}
|
|
26912
|
+
const { resource: dependencyScope, on } = dependency;
|
|
26913
|
+
const where = `resource(${dependentScope.name}) dependency on ${dependencyScope.name}`;
|
|
26914
|
+
if (!Array.isArray(on) || on.length === 0) {
|
|
26915
|
+
throw new Error(
|
|
26916
|
+
`${where}: "on" lists the writes that count, e.g. on: ["POST", "DELETE"] — list the resource itself for every write`,
|
|
26917
|
+
);
|
|
26918
|
+
}
|
|
26919
|
+
for (const writeName of on) {
|
|
26920
|
+
if (!WRITE_CALLBACK_NAMES.includes(writeName)) {
|
|
26921
|
+
throw new Error(
|
|
26922
|
+
`${where}: "${writeName}" is not a write, "on" takes ${WRITE_CALLBACK_NAMES.join(", ")}`,
|
|
26923
|
+
);
|
|
26924
|
+
}
|
|
26925
|
+
if (!dependencyScope[writeName]) {
|
|
26926
|
+
throw new Error(
|
|
26927
|
+
`${where}: ${dependencyScope.name} has no ${writeName}, so it would never rerun ${dependentScope.name}`,
|
|
26928
|
+
);
|
|
26929
|
+
}
|
|
26930
|
+
}
|
|
26931
|
+
return [dependencyScope, on];
|
|
26932
|
+
};
|
|
26828
26933
|
const isParamSubset = (parentParams, childParams) => {
|
|
26829
26934
|
if (!parentParams || !childParams) {
|
|
26830
26935
|
return false;
|
|
@@ -27573,6 +27678,10 @@ const debug$2 = (...args) => {
|
|
|
27573
27678
|
* @param {Object} restCallbacks - `{ idKey, uniqueKeys, rerunOn, dependencies, GET, GET_MANY, GET_RANGE, POST, POST_MANY, PUT, PUT_MANY, PATCH, PATCH_MANY, DELETE, DELETE_MANY }`
|
|
27574
27679
|
* @param {string} [restCallbacks.idKey] - primary key property, defaults to `"id"`
|
|
27575
27680
|
* @param {string[]} [restCallbacks.uniqueKeys] - alternate keys the store can find an item by (e.g. `"username"`)
|
|
27681
|
+
* @param {Array<Object | { resource: Object, on: string[] }>} [restCallbacks.dependencies] -
|
|
27682
|
+
* resources whose writes rerun this one's reads: one listed bare counts every
|
|
27683
|
+
* write, `{ resource, on: ["PATCH_MANY", "DELETE"] }` only the callbacks it
|
|
27684
|
+
* names (`PATCH` is not `PATCH_MANY`)
|
|
27576
27685
|
* @param {{ signal: import("@preact/signals").Signal, when?: () => boolean }} [restCallbacks.persist] -
|
|
27577
27686
|
* mirrors the rows `GET` lands with into `signal` — a `stateSignal` with
|
|
27578
27687
|
* `persists: true` and `type: "object"` keeps them across a reload, whose
|
|
@@ -29663,6 +29772,7 @@ const useAsyncData = (promiseOrAction, {
|
|
|
29663
29772
|
loading = "delegate",
|
|
29664
29773
|
error = "delegate",
|
|
29665
29774
|
run,
|
|
29775
|
+
prerun,
|
|
29666
29776
|
onLoad
|
|
29667
29777
|
} = {}) => {
|
|
29668
29778
|
// The request this component owns, as a function: one action per instance,
|
|
@@ -29688,6 +29798,7 @@ const useAsyncData = (promiseOrAction, {
|
|
|
29688
29798
|
loadingEffect: loading,
|
|
29689
29799
|
errorEffect: error,
|
|
29690
29800
|
run,
|
|
29801
|
+
prerun,
|
|
29691
29802
|
onLoad
|
|
29692
29803
|
});
|
|
29693
29804
|
}
|
|
@@ -29719,8 +29830,14 @@ const useActionAsyncData = (action, {
|
|
|
29719
29830
|
loadingEffect,
|
|
29720
29831
|
errorEffect,
|
|
29721
29832
|
run,
|
|
29833
|
+
prerun,
|
|
29722
29834
|
onLoad
|
|
29723
29835
|
}) => {
|
|
29836
|
+
if (prerun) {
|
|
29837
|
+
// A head start draws none of what it starts, so it has no wait to
|
|
29838
|
+
// delegate.
|
|
29839
|
+
loadingEffect = "use";
|
|
29840
|
+
}
|
|
29724
29841
|
const loadingRef = useContext(LoadingContext);
|
|
29725
29842
|
// The boundary is where a suspension lands, so only the delegating path needs
|
|
29726
29843
|
// one: `loading: true` draws the wait itself and never suspends.
|
|
@@ -29736,13 +29853,18 @@ const useActionAsyncData = (action, {
|
|
|
29736
29853
|
// no-op — which is what makes starting it while rendering sound. From there
|
|
29737
29854
|
// the wait is the ordinary one: suspended into <Loading>, or drawn by the
|
|
29738
29855
|
// component under `loading: true`, exactly as for data someone else ran.
|
|
29739
|
-
if (run && action.
|
|
29856
|
+
if (run && action.paramsSignal.peek() !== undefined && runIsOwed(action)) {
|
|
29740
29857
|
// Nothing waits on this run: the failure is held by the action, and this
|
|
29741
29858
|
// hook is what reads it back.
|
|
29742
29859
|
runUnwatched(() => action.run({
|
|
29743
29860
|
reason: "useAsyncData({ run: true })"
|
|
29744
29861
|
}));
|
|
29745
29862
|
}
|
|
29863
|
+
if (prerun && action.runningStateSignal.peek() === IDLE && action.paramsSignal.peek() !== undefined) {
|
|
29864
|
+
prerunUnwatched(() => action.prerun({
|
|
29865
|
+
reason: "useAsyncData({ prerun: true })"
|
|
29866
|
+
}));
|
|
29867
|
+
}
|
|
29746
29868
|
|
|
29747
29869
|
// Use peek() instead of .value to avoid subscribing this component to the signal.
|
|
29748
29870
|
// Reading .value would make Preact re-render the component reactively when the state
|
|
@@ -29851,6 +29973,11 @@ const useActionAsyncData = (action, {
|
|
|
29851
29973
|
return [action.dataSignal.peek(), false, undefined];
|
|
29852
29974
|
}
|
|
29853
29975
|
if (runningState === FAILED) {
|
|
29976
|
+
if (prerun) {
|
|
29977
|
+
// Neither shown nor reported from a head start: the failure is the
|
|
29978
|
+
// owner's to ask again (see runIsOwed).
|
|
29979
|
+
return [action.dataSignal.peek(), false, undefined];
|
|
29980
|
+
}
|
|
29854
29981
|
if (dismissedActionWeakSet.has(action)) {
|
|
29855
29982
|
// Dismissing removes the error and nothing else: what is handed back is
|
|
29856
29983
|
// the action's state without it — the data it holds, and no run in
|
|
@@ -29956,6 +30083,21 @@ const useActionAsyncData = (action, {
|
|
|
29956
30083
|
throw pendingPromise;
|
|
29957
30084
|
};
|
|
29958
30085
|
|
|
30086
|
+
// What `run: true` starts from: an action nobody ran, or one a prerun failed.
|
|
30087
|
+
// Any other failure stays — a render retrying it would loop while the network
|
|
30088
|
+
// is down — but nothing on screen asked for a prerun, so its failure is asked
|
|
30089
|
+
// again, once: the mark is taken here, and the run started fails as its own.
|
|
30090
|
+
const runIsOwed = action => {
|
|
30091
|
+
const runningState = action.runningStateSignal.peek();
|
|
30092
|
+
if (runningState === IDLE) {
|
|
30093
|
+
return true;
|
|
30094
|
+
}
|
|
30095
|
+
if (runningState === FAILED) {
|
|
30096
|
+
return takePrerunFailure(action.errorSignal.peek());
|
|
30097
|
+
}
|
|
30098
|
+
return false;
|
|
30099
|
+
};
|
|
30100
|
+
|
|
29959
30101
|
// What a screen does with the data once, when it becomes known (see onLoad in
|
|
29960
30102
|
// the JSDoc above). Kept apart because the two questions it answers are not the
|
|
29961
30103
|
// ones the hook around it answers: WHEN — a layout effect, so a form taking its
|
|
@@ -39591,15 +39733,7 @@ const prefetchAsIf = (action, readParams, values) => {
|
|
|
39591
39733
|
// the arrival focuses the link, or is the one that asked), and a reset
|
|
39592
39734
|
// landing after that would pull the answer from under the page reading it.
|
|
39593
39735
|
const prerunAhead = (instance) => {
|
|
39594
|
-
|
|
39595
|
-
try {
|
|
39596
|
-
result = instance.prerun({ reason: "intent" });
|
|
39597
|
-
} catch {
|
|
39598
|
-
return;
|
|
39599
|
-
}
|
|
39600
|
-
if (result && typeof result.catch === "function") {
|
|
39601
|
-
result.catch(() => {});
|
|
39602
|
-
}
|
|
39736
|
+
prerunUnwatched(() => instance.prerun({ reason: "intent" }));
|
|
39603
39737
|
};
|
|
39604
39738
|
|
|
39605
39739
|
// I delibrately prefer the term "any" and avoid "some" so dev are not tempted to think
|
|
@@ -41687,7 +41821,7 @@ registerNaviCommand("--navi-open", (source, event, { anchor, value } = {}) => {
|
|
|
41687
41821
|
* The target is found the way --navi-open finds it, but silently: nothing
|
|
41688
41822
|
* runs here, and a source with no target says so at the release.
|
|
41689
41823
|
*/
|
|
41690
|
-
const announceOpeningPress = (source, command) => {
|
|
41824
|
+
const announceOpeningPress = (source, command, pointerdownEvent) => {
|
|
41691
41825
|
if (command !== "--navi-open" && command !== "--navi-toggle") {
|
|
41692
41826
|
return;
|
|
41693
41827
|
}
|
|
@@ -41698,7 +41832,7 @@ const announceOpeningPress = (source, command) => {
|
|
|
41698
41832
|
if (!target) {
|
|
41699
41833
|
return;
|
|
41700
41834
|
}
|
|
41701
|
-
dispatchCustomEvent(target, "navi_open_press");
|
|
41835
|
+
dispatchCustomEvent(target, "navi_open_press", { event: pointerdownEvent });
|
|
41702
41836
|
};
|
|
41703
41837
|
// "--navi-close:all" closes every expandable above the source, nearest first —
|
|
41704
41838
|
// a link leaving from a badge shown over a sheet leaves both. A surface that
|
|
@@ -41971,11 +42105,18 @@ registerNaviCommand("--navi-unselect", (source, event) => {
|
|
|
41971
42105
|
* where a delay is felt hardest — while the ~100-300ms between hovering a
|
|
41972
42106
|
* trigger and pressing it are free. The warming render starts at the frame
|
|
41973
42107
|
* after the one that follows the intent: nothing here needs the content in the
|
|
41974
|
-
* DOM before the click, only before the open that follows it, and
|
|
41975
|
-
*
|
|
41976
|
-
*
|
|
41977
|
-
*
|
|
41978
|
-
*
|
|
42108
|
+
* DOM before the click, only before the open that follows it, and a build
|
|
42109
|
+
* started at once would hold back the frame that shows the hover or the focus.
|
|
42110
|
+
*
|
|
42111
|
+
* Under a finger, `pointerover` is not an intent yet: it arrives with the
|
|
42112
|
+
* `pointerdown` of every touch, the ones starting a scroll included, and at
|
|
42113
|
+
* reading speed the browser decides a touch is a scroll about 35ms in — a
|
|
42114
|
+
* build started then holds that scroll start back. So a touch warms once it has been held
|
|
42115
|
+
* TOUCH_WARM_HOLD_MS without the browser taking it (`pointercancel`) or the
|
|
42116
|
+
* finger lifting; a quicker tap builds at the open, like a click with no hover
|
|
42117
|
+
* before it. Counted in animation frames, never a timeout: browsers hold timers
|
|
42118
|
+
* back while a finger is down, and a timeout started by the press fires at its
|
|
42119
|
+
* release, on top of the click.
|
|
41979
42120
|
*
|
|
41980
42121
|
* The anchor only says where the popup is placed, so intent inside it is read
|
|
41981
42122
|
* off the control it is aimed at, the nearest one around the target. A control
|
|
@@ -41999,8 +42140,11 @@ registerNaviCommand("--navi-unselect", (source, event) => {
|
|
|
41999
42140
|
* anchor: it says where the popup is placed, not what opens it — a card
|
|
42000
42141
|
* opened by a hold is pressed all day by taps meant for what it holds.
|
|
42001
42142
|
*
|
|
42002
|
-
* The press builds the content,
|
|
42003
|
-
*
|
|
42143
|
+
* The press builds the content when it starts, a touch once it has been held
|
|
42144
|
+
* as above: the announcement carries the pointerdown that made it. A press
|
|
42145
|
+
* released before its build leaves the building to the open its click asks
|
|
42146
|
+
* for. The press ending without an open throws the content away — its click
|
|
42147
|
+
* reaching the document with the popup still closed, no
|
|
42004
42148
|
* click coming after the release, the browser taking the gesture
|
|
42005
42149
|
* (`pointercancel`), or the next press starting, before any of its own
|
|
42006
42150
|
* handlers run: one press at a time holds a content. A keyboard opening builds
|
|
@@ -42044,6 +42188,12 @@ const cancelIdle = (id) =>
|
|
|
42044
42188
|
? cancelIdleCallback(id)
|
|
42045
42189
|
: clearTimeout(id);
|
|
42046
42190
|
|
|
42191
|
+
// How long a finger rests on an anchor before it warms it: past the ~35ms at
|
|
42192
|
+
// which the browser takes a touch moving at reading speed for a scroll, short
|
|
42193
|
+
// of the ~100ms a tap is held, so a deliberate tap is warmed before its
|
|
42194
|
+
// release.
|
|
42195
|
+
const TOUCH_WARM_HOLD_MS = 80;
|
|
42196
|
+
|
|
42047
42197
|
const usePopupContentMount = (
|
|
42048
42198
|
openController,
|
|
42049
42199
|
ref,
|
|
@@ -42134,14 +42284,35 @@ const usePopupContentMount = (
|
|
|
42134
42284
|
return undefined;
|
|
42135
42285
|
}
|
|
42136
42286
|
let cancelWarm = null;
|
|
42287
|
+
let warmIsTouchHold = false;
|
|
42137
42288
|
const warm = () => {
|
|
42138
42289
|
if (cancelWarm) {
|
|
42139
42290
|
return;
|
|
42140
42291
|
}
|
|
42292
|
+
warmIsTouchHold = false;
|
|
42141
42293
|
cancelWarm = requestFrameAfterNext(() => {
|
|
42142
42294
|
setContentMounted(true);
|
|
42143
42295
|
});
|
|
42144
42296
|
};
|
|
42297
|
+
const warmAfterTouchHold = () => {
|
|
42298
|
+
if (cancelWarm) {
|
|
42299
|
+
return;
|
|
42300
|
+
}
|
|
42301
|
+
warmIsTouchHold = true;
|
|
42302
|
+
cancelWarm = requestFrameAfterDuration(TOUCH_WARM_HOLD_MS, () => {
|
|
42303
|
+
setContentMounted(true);
|
|
42304
|
+
});
|
|
42305
|
+
};
|
|
42306
|
+
// A touch leaves the anchor when the finger lifts or the browser takes the
|
|
42307
|
+
// touch: either way, before the hold said anything.
|
|
42308
|
+
const onPointerLeave = () => {
|
|
42309
|
+
if (!warmIsTouchHold) {
|
|
42310
|
+
return;
|
|
42311
|
+
}
|
|
42312
|
+
cancelWarm();
|
|
42313
|
+
cancelWarm = null;
|
|
42314
|
+
warmIsTouchHold = false;
|
|
42315
|
+
};
|
|
42145
42316
|
// The anchor itself when it is a control (a Popover anchored on the button
|
|
42146
42317
|
// opening it), or the one the popup is written in (a picker's trigger).
|
|
42147
42318
|
const isOwnControl = (control) =>
|
|
@@ -42161,6 +42332,10 @@ const usePopupContentMount = (
|
|
|
42161
42332
|
return;
|
|
42162
42333
|
}
|
|
42163
42334
|
}
|
|
42335
|
+
if (pointeroverEvent.pointerType === "touch") {
|
|
42336
|
+
warmAfterTouchHold();
|
|
42337
|
+
return;
|
|
42338
|
+
}
|
|
42164
42339
|
warm();
|
|
42165
42340
|
};
|
|
42166
42341
|
const onFocusIn = (focusinEvent) => {
|
|
@@ -42171,10 +42346,12 @@ const usePopupContentMount = (
|
|
|
42171
42346
|
warm();
|
|
42172
42347
|
};
|
|
42173
42348
|
anchorElement.addEventListener("pointerover", onPointerOver);
|
|
42349
|
+
anchorElement.addEventListener("pointerleave", onPointerLeave);
|
|
42174
42350
|
anchorElement.addEventListener("focusin", onFocusIn);
|
|
42175
42351
|
return () => {
|
|
42176
42352
|
cancelWarm?.();
|
|
42177
42353
|
anchorElement.removeEventListener("pointerover", onPointerOver);
|
|
42354
|
+
anchorElement.removeEventListener("pointerleave", onPointerLeave);
|
|
42178
42355
|
anchorElement.removeEventListener("focusin", onFocusIn);
|
|
42179
42356
|
};
|
|
42180
42357
|
}, [contentMounted, anchor, mount]);
|
|
@@ -42184,12 +42361,14 @@ const usePopupContentMount = (
|
|
|
42184
42361
|
const stopPressWarmRef = useRef(null);
|
|
42185
42362
|
openController.onOpeningPress =
|
|
42186
42363
|
mount === "while-opened"
|
|
42187
|
-
? () => {
|
|
42364
|
+
? (openPressEvent) => {
|
|
42188
42365
|
if (contentMountedRef.current || openController.onOpen) {
|
|
42189
42366
|
return;
|
|
42190
42367
|
}
|
|
42191
|
-
const cancelBuild =
|
|
42192
|
-
|
|
42368
|
+
const cancelBuild = requestBuildOnPress(openPressEvent, {
|
|
42369
|
+
onBuild: () => {
|
|
42370
|
+
setContentMounted(true);
|
|
42371
|
+
},
|
|
42193
42372
|
});
|
|
42194
42373
|
const stopWatching = watchPressEnd(() => {
|
|
42195
42374
|
stopPressWarmRef.current = null;
|
|
@@ -42251,6 +42430,64 @@ const requestFrameAfterNext = (callback) => {
|
|
|
42251
42430
|
};
|
|
42252
42431
|
};
|
|
42253
42432
|
|
|
42433
|
+
// What an opening press builds, it builds at the frame after the next one, so
|
|
42434
|
+
// the pressed trigger is painted first — under a finger, once the touch has
|
|
42435
|
+
// been held TOUCH_WARM_HOLD_MS (see the top comment). Released or taken by the
|
|
42436
|
+
// browser before then, the press builds nothing: the open its click asks for
|
|
42437
|
+
// builds, when one comes. Called from the press's own pointerdown, through its
|
|
42438
|
+
// navi_open_press: the capture listeners below only hear what follows.
|
|
42439
|
+
const requestBuildOnPress = (openPressEvent, { onBuild, onGiveUp }) => {
|
|
42440
|
+
const pointerdownEvent = openPressEvent.detail.event;
|
|
42441
|
+
const cancelFrame =
|
|
42442
|
+
pointerdownEvent.pointerType === "touch"
|
|
42443
|
+
? requestFrameAfterDuration(TOUCH_WARM_HOLD_MS, () => {
|
|
42444
|
+
stopListening();
|
|
42445
|
+
onBuild();
|
|
42446
|
+
})
|
|
42447
|
+
: requestFrameAfterNext(() => {
|
|
42448
|
+
stopListening();
|
|
42449
|
+
onBuild();
|
|
42450
|
+
});
|
|
42451
|
+
const onPressEnd = (pointerEvent) => {
|
|
42452
|
+
if (pointerEvent.pointerId !== pointerdownEvent.pointerId) {
|
|
42453
|
+
return;
|
|
42454
|
+
}
|
|
42455
|
+
cancel();
|
|
42456
|
+
onGiveUp?.();
|
|
42457
|
+
};
|
|
42458
|
+
const stopListening = () => {
|
|
42459
|
+
document.removeEventListener("pointerup", onPressEnd, { capture: true });
|
|
42460
|
+
document.removeEventListener("pointercancel", onPressEnd, {
|
|
42461
|
+
capture: true,
|
|
42462
|
+
});
|
|
42463
|
+
};
|
|
42464
|
+
const cancel = () => {
|
|
42465
|
+
cancelFrame();
|
|
42466
|
+
stopListening();
|
|
42467
|
+
};
|
|
42468
|
+
document.addEventListener("pointerup", onPressEnd, { capture: true });
|
|
42469
|
+
document.addEventListener("pointercancel", onPressEnd, { capture: true });
|
|
42470
|
+
return cancel;
|
|
42471
|
+
};
|
|
42472
|
+
|
|
42473
|
+
// The first frame at least `ms` after the call, read off the frames' own
|
|
42474
|
+
// timestamps: a 120Hz screen waits as long as a 60Hz one.
|
|
42475
|
+
const requestFrameAfterDuration = (ms, callback) => {
|
|
42476
|
+
const start = performance.now();
|
|
42477
|
+
let frame;
|
|
42478
|
+
const onFrame = (timestamp) => {
|
|
42479
|
+
if (timestamp - start >= ms) {
|
|
42480
|
+
callback();
|
|
42481
|
+
return;
|
|
42482
|
+
}
|
|
42483
|
+
frame = requestAnimationFrame(onFrame);
|
|
42484
|
+
};
|
|
42485
|
+
frame = requestAnimationFrame(onFrame);
|
|
42486
|
+
return () => {
|
|
42487
|
+
cancelAnimationFrame(frame);
|
|
42488
|
+
};
|
|
42489
|
+
};
|
|
42490
|
+
|
|
42254
42491
|
// How long the click of a released press may take to arrive. Nothing tells a
|
|
42255
42492
|
// page that a release brings no click — a press that became a drag has its
|
|
42256
42493
|
// click swallowed (suppressClickAfterGesture in @jsenv/dom) — so past this the
|
|
@@ -51435,7 +51672,7 @@ const usePreloadOpeningOnPress = (ref, command, prefetch = true) => {
|
|
|
51435
51672
|
if (pointerdownEvent.button !== 0) {
|
|
51436
51673
|
return;
|
|
51437
51674
|
}
|
|
51438
|
-
announceOpeningPress(element, command);
|
|
51675
|
+
announceOpeningPress(element, command, pointerdownEvent);
|
|
51439
51676
|
};
|
|
51440
51677
|
element.addEventListener("pointerdown", onPointerDown);
|
|
51441
51678
|
return () => {
|
|
@@ -55624,7 +55861,9 @@ const Expandable = props => {
|
|
|
55624
55861
|
// its own (see announceOpeningPress in commands.js) — unless aimed at a
|
|
55625
55862
|
// control inside the UI part, whose click does not toggle (see onUIClick).
|
|
55626
55863
|
if (pointerdownEvent.button === 0 && !openController.opened && !isAimedAtControlInsideUI(pointerdownEvent.target)) {
|
|
55627
|
-
dispatchCustomEvent(rootRef.current, "navi_open_press"
|
|
55864
|
+
dispatchCustomEvent(rootRef.current, "navi_open_press", {
|
|
55865
|
+
event: pointerdownEvent
|
|
55866
|
+
});
|
|
55628
55867
|
}
|
|
55629
55868
|
};
|
|
55630
55869
|
|
|
@@ -56045,7 +56284,7 @@ const Expandable = props => {
|
|
|
56045
56284
|
,
|
|
56046
56285
|
onnavi_open_press: e => {
|
|
56047
56286
|
rest.onnavi_open_press?.(e);
|
|
56048
|
-
openController.onOpeningPress?.();
|
|
56287
|
+
openController.onOpeningPress?.(e);
|
|
56049
56288
|
},
|
|
56050
56289
|
onnavi_request_close: e => {
|
|
56051
56290
|
rest.onnavi_request_close?.(e);
|
|
@@ -56125,8 +56364,8 @@ const ExpandableUI = ({
|
|
|
56125
56364
|
onnavi_request_open: e => {
|
|
56126
56365
|
openController.open(e);
|
|
56127
56366
|
},
|
|
56128
|
-
onnavi_open_press:
|
|
56129
|
-
openController.onOpeningPress?.();
|
|
56367
|
+
onnavi_open_press: e => {
|
|
56368
|
+
openController.onOpeningPress?.(e);
|
|
56130
56369
|
},
|
|
56131
56370
|
onnavi_request_close: e => {
|
|
56132
56371
|
openController.requestClose(e, {
|
|
@@ -68273,10 +68512,10 @@ const DialogStandIn = props => {
|
|
|
68273
68512
|
}
|
|
68274
68513
|
// eslint-disable-next-line react/no-unknown-property
|
|
68275
68514
|
,
|
|
68276
|
-
onnavi_open_press:
|
|
68515
|
+
onnavi_open_press: e => {
|
|
68277
68516
|
// What the opening will read is asked for at once, as the dialog
|
|
68278
|
-
// proper does (see open_controller.js); the dialog itself
|
|
68279
|
-
//
|
|
68517
|
+
// proper does (see open_controller.js); the dialog itself once the
|
|
68518
|
+
// press is taken for one (see requestBuildOnPress) — the click that
|
|
68280
68519
|
// follows opens it built, or builds it itself when it comes sooner.
|
|
68281
68520
|
if (signal) {
|
|
68282
68521
|
preloadState(signal, readOpenValue(value));
|
|
@@ -68284,9 +68523,14 @@ const DialogStandIn = props => {
|
|
|
68284
68523
|
if (warmRef.current) {
|
|
68285
68524
|
return;
|
|
68286
68525
|
}
|
|
68287
|
-
warmRef.current =
|
|
68288
|
-
|
|
68289
|
-
|
|
68526
|
+
warmRef.current = requestBuildOnPress(e, {
|
|
68527
|
+
onBuild: () => {
|
|
68528
|
+
warmRef.current = null;
|
|
68529
|
+
build("press");
|
|
68530
|
+
},
|
|
68531
|
+
onGiveUp: () => {
|
|
68532
|
+
warmRef.current = null;
|
|
68533
|
+
}
|
|
68290
68534
|
});
|
|
68291
68535
|
}
|
|
68292
68536
|
});
|
|
@@ -68367,8 +68611,8 @@ const UncontrolledDialog = props => {
|
|
|
68367
68611
|
// The press announcing that request (see announceOpeningPress in
|
|
68368
68612
|
// commands.js), heard where the request is.
|
|
68369
68613
|
,
|
|
68370
|
-
onnavi_open_press:
|
|
68371
|
-
openController.onOpeningPress?.();
|
|
68614
|
+
onnavi_open_press: e => {
|
|
68615
|
+
openController.onOpeningPress?.(e);
|
|
68372
68616
|
},
|
|
68373
68617
|
onnavi_request_close: e => {
|
|
68374
68618
|
const closing = openController.requestClose(e, {
|
|
@@ -70255,8 +70499,8 @@ const UncontrolledPopover = props => {
|
|
|
70255
70499
|
// The press announcing that request (see announceOpeningPress in
|
|
70256
70500
|
// commands.js), heard where the request is.
|
|
70257
70501
|
,
|
|
70258
|
-
onnavi_open_press:
|
|
70259
|
-
openController.onOpeningPress?.();
|
|
70502
|
+
onnavi_open_press: e => {
|
|
70503
|
+
openController.onOpeningPress?.(e);
|
|
70260
70504
|
},
|
|
70261
70505
|
onnavi_request_close: e => {
|
|
70262
70506
|
const closing = openController.requestClose(e, {
|
|
@@ -71668,15 +71912,15 @@ const css$B = /* css */`.navi_picker_callout_dock > [data-picker-content] {
|
|
|
71668
71912
|
display: flex;
|
|
71669
71913
|
}
|
|
71670
71914
|
|
|
71915
|
+
&:has( > .navi_list_container:not([data-scroller], [data-overflow-visible])) {
|
|
71916
|
+
overflow: visible;
|
|
71917
|
+
}
|
|
71918
|
+
|
|
71671
71919
|
& .navi_list_container {
|
|
71672
71920
|
--list-border-radius: max(0px,
|
|
71673
71921
|
var(--popover-border-radius) - var(--popover-border-width));
|
|
71674
71922
|
overscroll-behavior: none;
|
|
71675
71923
|
width: 100%;
|
|
71676
|
-
|
|
71677
|
-
&:not([data-overflow-visible]) {
|
|
71678
|
-
overflow: auto;
|
|
71679
|
-
}
|
|
71680
71924
|
}
|
|
71681
71925
|
}
|
|
71682
71926
|
|
|
@@ -71695,15 +71939,15 @@ const css$B = /* css */`.navi_picker_callout_dock > [data-picker-content] {
|
|
|
71695
71939
|
flex-direction: column;
|
|
71696
71940
|
}
|
|
71697
71941
|
|
|
71942
|
+
&:has( > .navi_list_container:not([data-scroller], [data-overflow-visible])) {
|
|
71943
|
+
overflow: visible;
|
|
71944
|
+
}
|
|
71945
|
+
|
|
71698
71946
|
& .navi_list_container {
|
|
71699
71947
|
--list-border-radius: max(0px,
|
|
71700
71948
|
var(--dialog-border-radius) - var(--dialog-border-width));
|
|
71701
71949
|
overscroll-behavior: none;
|
|
71702
71950
|
width: 100%;
|
|
71703
|
-
|
|
71704
|
-
&:not([data-overflow-visible]) {
|
|
71705
|
-
overflow: auto;
|
|
71706
|
-
}
|
|
71707
71951
|
}
|
|
71708
71952
|
}
|
|
71709
71953
|
`;
|
|
@@ -72162,8 +72406,8 @@ const PickerCustom = props => {
|
|
|
72162
72406
|
},
|
|
72163
72407
|
// And the press announcing that request (see announceOpeningPress in
|
|
72164
72408
|
// commands.js), for the popup's content to be built on it.
|
|
72165
|
-
"onnavi_open_press":
|
|
72166
|
-
dispatchCustomEvent(popupRef.current, "navi_open_press");
|
|
72409
|
+
"onnavi_open_press": e => {
|
|
72410
|
+
dispatchCustomEvent(popupRef.current, "navi_open_press", e.detail);
|
|
72167
72411
|
},
|
|
72168
72412
|
"onnavi_request_close": e => {
|
|
72169
72413
|
const closing = dispatchCustomEvent(popupRef.current, "navi_request_close", e.detail);
|
|
@@ -72226,8 +72470,8 @@ const PickerCustom = props => {
|
|
|
72226
72470
|
// The press announcing that request (see announceOpeningPress in
|
|
72227
72471
|
// commands.js), heard where the request is — here too for the same
|
|
72228
72472
|
// reason. Nothing to gate: the announcer asked before saying it.
|
|
72229
|
-
onnavi_open_press:
|
|
72230
|
-
openController.onOpeningPress?.();
|
|
72473
|
+
onnavi_open_press: e => {
|
|
72474
|
+
openController.onOpeningPress?.(e);
|
|
72231
72475
|
},
|
|
72232
72476
|
onnavi_request_close: e => {
|
|
72233
72477
|
requestInteraction({
|
|
@@ -72380,7 +72624,9 @@ const PickerCustom = props => {
|
|
|
72380
72624
|
})) {
|
|
72381
72625
|
return;
|
|
72382
72626
|
}
|
|
72383
|
-
dispatchCustomEvent(pickerEl, "navi_open_press"
|
|
72627
|
+
dispatchCustomEvent(pickerEl, "navi_open_press", {
|
|
72628
|
+
event: pointerdownEvent
|
|
72629
|
+
});
|
|
72384
72630
|
};
|
|
72385
72631
|
}
|
|
72386
72632
|
let interactions = props.interactions;
|
|
@@ -75206,15 +75452,13 @@ const css$x = /* css */`@property --list-group-label-height {
|
|
|
75206
75452
|
flex-direction: column;
|
|
75207
75453
|
transition: opacity .2s;
|
|
75208
75454
|
display: flex;
|
|
75209
|
-
overflow: hidden;
|
|
75210
75455
|
|
|
75211
|
-
|
|
75212
|
-
|
|
75213
|
-
overflow: visible;
|
|
75456
|
+
&:not([data-borderless], [data-overflow-visible], [data-scroller]) {
|
|
75457
|
+
min-height: 0;
|
|
75214
75458
|
}
|
|
75215
75459
|
|
|
75216
|
-
&[data-
|
|
75217
|
-
|
|
75460
|
+
&[data-borderless] {
|
|
75461
|
+
--x-list-border-radius: 0px;
|
|
75218
75462
|
}
|
|
75219
75463
|
|
|
75220
75464
|
&[data-overflow-visible="x"] {
|