@jsenv/navi 0.29.389 → 0.29.391
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 +376 -135
- package/dist/dev/jsenv_navi.js.map +8 -8
- package/dist/jsenv_navi.js +376 -135
- package/dist/jsenv_navi.js.map +8 -8
- package/docs/drag_interactions.md +13 -5
- package/docs/pan_zoom.md +11 -4
- package/docs/resource.md +45 -1
- package/package.json +2 -2
package/dist/jsenv_navi.js
CHANGED
|
@@ -4794,14 +4794,11 @@ const CONTROL_TYPE_BY_SIGNAL_TYPE = {
|
|
|
4794
4794
|
// become something plainer — it is what says the value is a number (see
|
|
4795
4795
|
// isNumberInput), and what lets constraint messages use domain-specific
|
|
4796
4796
|
// wording instead of the generic "Ce nombre doit être...".
|
|
4797
|
+
// A type a picker preset draws (navi_time, navi_minute: see picker_preset.jsx)
|
|
4798
|
+
// has no row: resolveInputProps runs before the preset resolver, and a row
|
|
4799
|
+
// would turn the type into its host before the preset resolver reads it. The
|
|
4800
|
+
// preset hands the host type on itself.
|
|
4797
4801
|
const NAVI_TYPE_DEFAULTS = {
|
|
4798
|
-
navi_time: {
|
|
4799
|
-
"type": "time",
|
|
4800
|
-
"navi-input-type": "time",
|
|
4801
|
-
"min": 0,
|
|
4802
|
-
"max": 24 * 3600 - 1,
|
|
4803
|
-
"step": 1,
|
|
4804
|
-
},
|
|
4805
4802
|
navi_percentage: {
|
|
4806
4803
|
"type": "navi_number",
|
|
4807
4804
|
"navi-input-type": "percentage",
|
|
@@ -4863,7 +4860,6 @@ const isNumberInput = (type, naviInputType) => {
|
|
|
4863
4860
|
* Supported navi types and their targets:
|
|
4864
4861
|
* - `navi_percentage` → `navi_number` (0–100, step 1)
|
|
4865
4862
|
* - `navi_number` → `text` (inputMode="numeric", no spin buttons implied)
|
|
4866
|
-
* - `navi_time` → `time` (step in seconds)
|
|
4867
4863
|
*
|
|
4868
4864
|
* Standard HTML input types with formatters:
|
|
4869
4865
|
* - `date`, `month`, `week`, `time`, `datetime-local`, `datetime`:
|
|
@@ -16014,6 +16010,15 @@ const createResource = (
|
|
|
16014
16010
|
paramScope,
|
|
16015
16011
|
rerunOn,
|
|
16016
16012
|
dependencies,
|
|
16013
|
+
// Set on a scoped child: the resource owning each of its items, which
|
|
16014
|
+
// live in one store per owner (`store` is the owner's).
|
|
16015
|
+
ownerName,
|
|
16016
|
+
// Set on a scopedMany child: its per-owner store looked up by owner key,
|
|
16017
|
+
// and the owner whose GET embeds the collection. Options rather than
|
|
16018
|
+
// properties added afterwards, so every facade of the relation — the one
|
|
16019
|
+
// scopedMany returns and each withParams() of it — carries both.
|
|
16020
|
+
getChildStore,
|
|
16021
|
+
ownerDependency,
|
|
16017
16022
|
},
|
|
16018
16023
|
) => {
|
|
16019
16024
|
const params = paramScope.params;
|
|
@@ -16039,6 +16044,8 @@ const createResource = (
|
|
|
16039
16044
|
// private but exposed for convenience
|
|
16040
16045
|
store,
|
|
16041
16046
|
addItemSetup,
|
|
16047
|
+
ownerName,
|
|
16048
|
+
getChildStore,
|
|
16042
16049
|
};
|
|
16043
16050
|
|
|
16044
16051
|
resourceLifecycleManager.registerResource(stateFacade, {
|
|
@@ -16047,6 +16054,17 @@ const createResource = (
|
|
|
16047
16054
|
dependencies,
|
|
16048
16055
|
uniqueKeys,
|
|
16049
16056
|
});
|
|
16057
|
+
if (ownerDependency) {
|
|
16058
|
+
// A child collection mutated (POST etc.) makes the owner GET re-fetch:
|
|
16059
|
+
// the owner embeds the child array and only the backend knows the new
|
|
16060
|
+
// ordering. Registered per facade, since the rerun is looked up by the
|
|
16061
|
+
// facade whose action completed.
|
|
16062
|
+
resourceLifecycleManager.addDependency(
|
|
16063
|
+
stateFacade,
|
|
16064
|
+
ownerDependency.owner,
|
|
16065
|
+
ownerDependency.propertyName,
|
|
16066
|
+
);
|
|
16067
|
+
}
|
|
16050
16068
|
const onActionComplete = (actionCompleted) => {
|
|
16051
16069
|
resourceLifecycleManager.onActionComplete(actionCompleted, {
|
|
16052
16070
|
resourceScope: stateFacade,
|
|
@@ -16059,6 +16077,10 @@ const createResource = (
|
|
|
16059
16077
|
* Actions from parameterized resources only trigger rerun/reset for other actions with
|
|
16060
16078
|
* identical parameters, preventing cross-contamination between different parameter sets.
|
|
16061
16079
|
*
|
|
16080
|
+
* The actions are the resource's own kind: on a relation they keep its callback return
|
|
16081
|
+
* contract and write where it writes (`TABLE_COLUMNS.withParams(…).GET_MANY` returns
|
|
16082
|
+
* `[ownerId, items]` and replaces that owner's collection).
|
|
16083
|
+
*
|
|
16062
16084
|
* @param {Object} params - Parameters to bind to all actions of this resource (required)
|
|
16063
16085
|
* @param {{ rerunOn?: Object, dependencies?: Object[] }} [options] - reruns of that scope; a `rerunOn` key left out keeps the resource's value, and `dependencies` left out are the resource's
|
|
16064
16086
|
* @returns {Object} A new resource instance with parameter-bound actions and isolated lifecycle
|
|
@@ -16084,7 +16106,6 @@ const createResource = (
|
|
|
16084
16106
|
paramsToInject,
|
|
16085
16107
|
{ dependencies: withParamsDeps, rerunOn: withParamsRerunOn } = {},
|
|
16086
16108
|
) => {
|
|
16087
|
-
const declarationSite = getDeclarationSite();
|
|
16088
16109
|
if (!paramsToInject || Object.keys(paramsToInject).length === 0) {
|
|
16089
16110
|
throw new Error(`resource(${name}).withParams() requires parameters`);
|
|
16090
16111
|
}
|
|
@@ -16092,11 +16113,6 @@ const createResource = (
|
|
|
16092
16113
|
? { ...params, ...paramsToInject }
|
|
16093
16114
|
: paramsToInject;
|
|
16094
16115
|
const resolvedParamScope = getParamScope(resolvedParams);
|
|
16095
|
-
const createRestActionWithParams = createRestActionFactoryForRoot(name, {
|
|
16096
|
-
idKey,
|
|
16097
|
-
store,
|
|
16098
|
-
declarationSite,
|
|
16099
|
-
});
|
|
16100
16116
|
return createResource(name, {
|
|
16101
16117
|
idKey,
|
|
16102
16118
|
uniqueKeys,
|
|
@@ -16104,10 +16120,16 @@ const createResource = (
|
|
|
16104
16120
|
restCallbacks,
|
|
16105
16121
|
store,
|
|
16106
16122
|
addItemSetup,
|
|
16107
|
-
|
|
16123
|
+
// The actions of this resource's kind: a relation applies its results to
|
|
16124
|
+
// its relation (a scoped child to the per-owner stores), never as root
|
|
16125
|
+
// items of `store`.
|
|
16126
|
+
createRestAction,
|
|
16108
16127
|
paramScope: resolvedParamScope,
|
|
16109
16128
|
rerunOn: resolveRerunOn(withParamsRerunOn, rerunOn),
|
|
16110
16129
|
dependencies: withParamsDeps ?? dependencies,
|
|
16130
|
+
ownerName,
|
|
16131
|
+
getChildStore,
|
|
16132
|
+
ownerDependency,
|
|
16111
16133
|
});
|
|
16112
16134
|
};
|
|
16113
16135
|
stateFacade.withParams = withParams;
|
|
@@ -16152,6 +16174,13 @@ const createResource = (
|
|
|
16152
16174
|
} = {},
|
|
16153
16175
|
) => {
|
|
16154
16176
|
const declarationSite = getDeclarationSite();
|
|
16177
|
+
if (ownerName) {
|
|
16178
|
+
refuseCallbacksNamingScopedItem(
|
|
16179
|
+
`${name}.one("${propertyName}")`,
|
|
16180
|
+
{ GET, PUT, DELETE },
|
|
16181
|
+
{ name, ownerName, propertyName, declarationSite },
|
|
16182
|
+
);
|
|
16183
|
+
}
|
|
16155
16184
|
const childName = `${name}.${propertyName}`;
|
|
16156
16185
|
const childIdKey = childResource.idKey;
|
|
16157
16186
|
const childStore = childResource.store;
|
|
@@ -16212,7 +16241,11 @@ const createResource = (
|
|
|
16212
16241
|
);
|
|
16213
16242
|
});
|
|
16214
16243
|
|
|
16215
|
-
const createRestActionForOne = (
|
|
16244
|
+
const createRestActionForOne = (
|
|
16245
|
+
verb,
|
|
16246
|
+
callback,
|
|
16247
|
+
{ onActionComplete, paramScope },
|
|
16248
|
+
) => {
|
|
16216
16249
|
const applyResultToValue =
|
|
16217
16250
|
verb === "DELETE"
|
|
16218
16251
|
? (itemIdOrItemProps) => {
|
|
@@ -16327,6 +16360,13 @@ const createResource = (
|
|
|
16327
16360
|
} = {},
|
|
16328
16361
|
) => {
|
|
16329
16362
|
const declarationSite = getDeclarationSite();
|
|
16363
|
+
if (ownerName) {
|
|
16364
|
+
refuseCallbacksNamingScopedItem(
|
|
16365
|
+
`${name}.many("${propertyName}")`,
|
|
16366
|
+
{ GET_MANY, DELETE, DELETE_MANY },
|
|
16367
|
+
{ name, ownerName, propertyName, declarationSite },
|
|
16368
|
+
);
|
|
16369
|
+
}
|
|
16330
16370
|
const childStore = childResource.store;
|
|
16331
16371
|
const childIdKey = childResource.idKey;
|
|
16332
16372
|
const childName = `${name}.${propertyName}`;
|
|
@@ -16358,21 +16398,23 @@ const createResource = (
|
|
|
16358
16398
|
const createRestActionForMany = (
|
|
16359
16399
|
verb,
|
|
16360
16400
|
callback,
|
|
16361
|
-
{ isMany, onActionComplete },
|
|
16401
|
+
{ isMany, onActionComplete, paramScope },
|
|
16362
16402
|
) => {
|
|
16363
16403
|
if (!isMany) {
|
|
16364
16404
|
return createRestActionAffectingOneItem(verb, callback, {
|
|
16365
16405
|
onActionComplete,
|
|
16406
|
+
paramScope,
|
|
16366
16407
|
});
|
|
16367
16408
|
}
|
|
16368
16409
|
return createRestActionAffectingManyItems(verb, callback, {
|
|
16369
16410
|
onActionComplete,
|
|
16411
|
+
paramScope,
|
|
16370
16412
|
});
|
|
16371
16413
|
};
|
|
16372
16414
|
const createRestActionAffectingOneItem = (
|
|
16373
16415
|
verb,
|
|
16374
16416
|
callback,
|
|
16375
|
-
{ onActionComplete },
|
|
16417
|
+
{ onActionComplete, paramScope },
|
|
16376
16418
|
) => {
|
|
16377
16419
|
const applyResultToValue =
|
|
16378
16420
|
verb === "DELETE"
|
|
@@ -16435,7 +16477,7 @@ const createResource = (
|
|
|
16435
16477
|
const createRestActionAffectingManyItems = (
|
|
16436
16478
|
verb,
|
|
16437
16479
|
callback,
|
|
16438
|
-
{ onActionComplete },
|
|
16480
|
+
{ onActionComplete, paramScope },
|
|
16439
16481
|
) => {
|
|
16440
16482
|
const applyResultToValue =
|
|
16441
16483
|
verb === "GET"
|
|
@@ -16563,7 +16605,12 @@ const createResource = (
|
|
|
16563
16605
|
* Mutations apply directly to the owner's signal, so the parent GET is never rerun.
|
|
16564
16606
|
*
|
|
16565
16607
|
* Returns the child relationship resource, itself chainable:
|
|
16566
|
-
* `USER_PROFILE.one("theme", THEME)` adds a reactive `.theme` property on each profile
|
|
16608
|
+
* `USER_PROFILE.one("theme", THEME)` adds a reactive `.theme` property on each profile,
|
|
16609
|
+
* fed by what this resource's callbacks embed (`[id, { bio, theme: { id: 3 } }]`). A
|
|
16610
|
+
* profile exists only inside its user, so the chained relation declares none of the
|
|
16611
|
+
* callbacks whose result names a profile (`.one()` GET/PUT/DELETE, `.many()`
|
|
16612
|
+
* GET_MANY/DELETE/DELETE_MANY, any `.scopedOne()`/`.scopedMany()` callback): such a
|
|
16613
|
+
* result cannot say which user holds it, and the declaration throws.
|
|
16567
16614
|
*
|
|
16568
16615
|
* @param {string} propertyName - property holding the sub-object on each owner item
|
|
16569
16616
|
* @param {Object} [restCallbacks] - `{ idKey, rerunOn, dependencies, GET, POST, PUT, PATCH, DELETE }`
|
|
@@ -16583,6 +16630,14 @@ const createResource = (
|
|
|
16583
16630
|
DELETE,
|
|
16584
16631
|
} = {},
|
|
16585
16632
|
) => {
|
|
16633
|
+
const declarationSite = getDeclarationSite();
|
|
16634
|
+
if (ownerName) {
|
|
16635
|
+
refuseCallbacksNamingScopedItem(
|
|
16636
|
+
`${name}.scopedOne("${propertyName}")`,
|
|
16637
|
+
{ GET, POST, PUT, PATCH, DELETE },
|
|
16638
|
+
{ name, ownerName, propertyName, declarationSite },
|
|
16639
|
+
);
|
|
16640
|
+
}
|
|
16586
16641
|
const childName = `${name}.${propertyName}`;
|
|
16587
16642
|
|
|
16588
16643
|
// Callbacks added by chained .one()/.many() on the child resource,
|
|
@@ -16612,7 +16667,12 @@ const createResource = (
|
|
|
16612
16667
|
childSignal.value = childItem; // first activation: null → childItem
|
|
16613
16668
|
}
|
|
16614
16669
|
};
|
|
16615
|
-
|
|
16670
|
+
if (!ownerName) {
|
|
16671
|
+
// Declared on a scoped child, an owner id is only unique inside its
|
|
16672
|
+
// own owner and no callback addresses it (see
|
|
16673
|
+
// refuseCallbacksNamingScopedItem): nothing is looked up by it.
|
|
16674
|
+
applyPropsMap.set(ownerId, applyProps);
|
|
16675
|
+
}
|
|
16616
16676
|
|
|
16617
16677
|
applyProps(ownerItem[propertyName]);
|
|
16618
16678
|
|
|
@@ -16624,7 +16684,7 @@ const createResource = (
|
|
|
16624
16684
|
const createRestActionForScopedOne = (
|
|
16625
16685
|
verb,
|
|
16626
16686
|
callback,
|
|
16627
|
-
{ onActionComplete },
|
|
16687
|
+
{ onActionComplete, paramScope },
|
|
16628
16688
|
) => {
|
|
16629
16689
|
const childActionName = `${childName}.${verb}`;
|
|
16630
16690
|
return createAction(callback, {
|
|
@@ -16672,6 +16732,7 @@ const createResource = (
|
|
|
16672
16732
|
paramScope,
|
|
16673
16733
|
rerunOn: resolveRerunOn(scopedOneRerunOn, rerunOn),
|
|
16674
16734
|
dependencies: scopedOneDependencies ?? dependencies,
|
|
16735
|
+
ownerName: name,
|
|
16675
16736
|
});
|
|
16676
16737
|
};
|
|
16677
16738
|
|
|
@@ -16699,7 +16760,14 @@ const createResource = (
|
|
|
16699
16760
|
* `propertyName`), and the child's own GET_MANY reruns per its `rerunOn`.
|
|
16700
16761
|
*
|
|
16701
16762
|
* Returns the child relationship resource, itself chainable:
|
|
16702
|
-
* `TABLE_COLUMNS.one("dataType", DATA_TYPE)` adds a reactive `.dataType` property on each column
|
|
16763
|
+
* `TABLE_COLUMNS.one("dataType", DATA_TYPE)` adds a reactive `.dataType` property on each column,
|
|
16764
|
+
* fed by what this resource's callbacks embed (`[id, [{ name, dataType: { id: 2 } }]]`). A
|
|
16765
|
+
* column exists only inside its table — two tables can each have an `email` — so the chained
|
|
16766
|
+
* relation declares none of the callbacks whose result names a column (`.one()`
|
|
16767
|
+
* GET/PUT/DELETE, `.many()` GET_MANY/DELETE/DELETE_MANY, any `.scopedOne()`/`.scopedMany()`
|
|
16768
|
+
* callback): such a result cannot say which table holds it, and the declaration throws.
|
|
16769
|
+
* A `.scopedMany()` chained on it keeps one collection per column: `users.email` and
|
|
16770
|
+
* `admins.email` never share one.
|
|
16703
16771
|
*
|
|
16704
16772
|
* @param {string} propertyName - property holding the collection on each owner item
|
|
16705
16773
|
* @param {Object} [restCallbacks] - `{ idKey, rerunOn, dependencies, GET, GET_MANY, POST, POST_MANY, PUT, PUT_MANY, PATCH, PATCH_MANY, DELETE, DELETE_MANY }`
|
|
@@ -16724,6 +16792,25 @@ const createResource = (
|
|
|
16724
16792
|
DELETE_MANY,
|
|
16725
16793
|
} = {},
|
|
16726
16794
|
) => {
|
|
16795
|
+
const declarationSite = getDeclarationSite();
|
|
16796
|
+
if (ownerName) {
|
|
16797
|
+
refuseCallbacksNamingScopedItem(
|
|
16798
|
+
`${name}.scopedMany("${propertyName}")`,
|
|
16799
|
+
{
|
|
16800
|
+
GET,
|
|
16801
|
+
GET_MANY,
|
|
16802
|
+
POST,
|
|
16803
|
+
POST_MANY,
|
|
16804
|
+
PUT,
|
|
16805
|
+
PUT_MANY,
|
|
16806
|
+
PATCH,
|
|
16807
|
+
PATCH_MANY,
|
|
16808
|
+
DELETE,
|
|
16809
|
+
DELETE_MANY,
|
|
16810
|
+
},
|
|
16811
|
+
{ name, ownerName, propertyName, declarationSite },
|
|
16812
|
+
);
|
|
16813
|
+
}
|
|
16727
16814
|
const childName = `${name}.${propertyName}`;
|
|
16728
16815
|
|
|
16729
16816
|
// Callbacks added by chained .one()/.many() on the child resource,
|
|
@@ -16745,37 +16832,42 @@ const createResource = (
|
|
|
16745
16832
|
return childItem;
|
|
16746
16833
|
},
|
|
16747
16834
|
});
|
|
16748
|
-
|
|
16749
|
-
scopeMap.set(ownerKey, scope);
|
|
16750
|
-
return scope;
|
|
16835
|
+
return { childStore, idArraySignal: signal([]) };
|
|
16751
16836
|
};
|
|
16752
16837
|
addItemSetup((item) => {
|
|
16753
16838
|
const ownerId = item[idKey];
|
|
16754
|
-
|
|
16755
|
-
|
|
16756
|
-
|
|
16757
|
-
|
|
16758
|
-
|
|
16839
|
+
let scope;
|
|
16840
|
+
if (ownerName) {
|
|
16841
|
+
// The owner is itself a scoped child: its id is only unique inside its
|
|
16842
|
+
// own owner, so the scope is the item's alone, never looked up by id
|
|
16843
|
+
// (no callback addresses it, see refuseCallbacksNamingScopedItem).
|
|
16844
|
+
scope = createScope(ownerId);
|
|
16845
|
+
} else {
|
|
16846
|
+
// Reuse an existing scope if one was already created under a uniqueKey
|
|
16847
|
+
// value (e.g. rows were fetched by tablename before the full table was loaded).
|
|
16848
|
+
scope = scopeMap.get(ownerId);
|
|
16849
|
+
if (!scope) {
|
|
16850
|
+
for (const uniqueKey of uniqueKeys) {
|
|
16851
|
+
const uniqueKeyValue = item[uniqueKey];
|
|
16852
|
+
if (uniqueKeyValue !== undefined && scopeMap.has(uniqueKeyValue)) {
|
|
16853
|
+
scope = scopeMap.get(uniqueKeyValue);
|
|
16854
|
+
break;
|
|
16855
|
+
}
|
|
16856
|
+
}
|
|
16857
|
+
}
|
|
16858
|
+
if (!scope) {
|
|
16859
|
+
scope = createScope(ownerId);
|
|
16860
|
+
}
|
|
16861
|
+
// Register the scope under the id and every uniqueKey value so that
|
|
16862
|
+
// resolveOwnerId can address it whichever key a callback returns.
|
|
16863
|
+
scopeMap.set(ownerId, scope);
|
|
16759
16864
|
for (const uniqueKey of uniqueKeys) {
|
|
16760
16865
|
const uniqueKeyValue = item[uniqueKey];
|
|
16761
|
-
if (uniqueKeyValue !== undefined
|
|
16762
|
-
|
|
16763
|
-
break;
|
|
16866
|
+
if (uniqueKeyValue !== undefined) {
|
|
16867
|
+
scopeMap.set(uniqueKeyValue, scope);
|
|
16764
16868
|
}
|
|
16765
16869
|
}
|
|
16766
16870
|
}
|
|
16767
|
-
if (!scope) {
|
|
16768
|
-
scope = createScope(ownerId);
|
|
16769
|
-
}
|
|
16770
|
-
// Register the scope under the id and every uniqueKey value so that
|
|
16771
|
-
// resolveOwnerId can address it whichever key a callback returns.
|
|
16772
|
-
scopeMap.set(ownerId, scope);
|
|
16773
|
-
for (const uniqueKey of uniqueKeys) {
|
|
16774
|
-
const uniqueKeyValue = item[uniqueKey];
|
|
16775
|
-
if (uniqueKeyValue !== undefined) {
|
|
16776
|
-
scopeMap.set(uniqueKeyValue, scope);
|
|
16777
|
-
}
|
|
16778
|
-
}
|
|
16779
16871
|
|
|
16780
16872
|
const { childStore, idArraySignal } = scope;
|
|
16781
16873
|
const updateChildItemIdArray = createChildIdArrayUpdater(
|
|
@@ -16813,7 +16905,7 @@ const createResource = (
|
|
|
16813
16905
|
const createRestActionForScopedMany = (
|
|
16814
16906
|
verb,
|
|
16815
16907
|
callback,
|
|
16816
|
-
{ isMany, onActionComplete },
|
|
16908
|
+
{ isMany, onActionComplete, paramScope },
|
|
16817
16909
|
) => {
|
|
16818
16910
|
const childActionName = `${childName}.${verb}`;
|
|
16819
16911
|
return createAction(callback, {
|
|
@@ -16835,7 +16927,11 @@ const createResource = (
|
|
|
16835
16927
|
);
|
|
16836
16928
|
// Owner not in store yet: create the scope so actions can run before
|
|
16837
16929
|
// the parent item has been loaded (e.g. rows fetched before their table).
|
|
16838
|
-
|
|
16930
|
+
let scope = scopeMap.get(ownerId);
|
|
16931
|
+
if (!scope) {
|
|
16932
|
+
scope = createScope(ownerId);
|
|
16933
|
+
scopeMap.set(ownerId, scope);
|
|
16934
|
+
}
|
|
16839
16935
|
const { childStore, idArraySignal } = scope;
|
|
16840
16936
|
|
|
16841
16937
|
if (verb === "DELETE") {
|
|
@@ -16907,18 +17003,17 @@ const createResource = (
|
|
|
16907
17003
|
paramScope,
|
|
16908
17004
|
rerunOn: resolveRerunOn(scopedManyRerunOn, rerunOn),
|
|
16909
17005
|
dependencies: scopedManyDependencies ?? dependencies,
|
|
17006
|
+
ownerName: name,
|
|
17007
|
+
// Declared on a scoped child, the scopes are kept per item and never
|
|
17008
|
+
// registered by id (see addItemSetup above): there is no owner key to
|
|
17009
|
+
// find one by.
|
|
17010
|
+
getChildStore: ownerName
|
|
17011
|
+
? undefined
|
|
17012
|
+
: (ownerKey) => scopeMap.get(ownerKey)?.childStore,
|
|
17013
|
+
// scopedOne needs no such dependency: its mutation result carries the
|
|
17014
|
+
// updated object directly.
|
|
17015
|
+
ownerDependency: { owner: stateFacade, propertyName },
|
|
16910
17016
|
});
|
|
16911
|
-
// When a scoped child collection is mutated (POST etc.), the parent GET must
|
|
16912
|
-
// re-fetch: the parent embeds the child array and only the backend knows the
|
|
16913
|
-
// new ordering. (scopedOne does not need this: the mutation result contains
|
|
16914
|
-
// the updated object directly.)
|
|
16915
|
-
resourceLifecycleManager.addDependency(
|
|
16916
|
-
childResource,
|
|
16917
|
-
stateFacade,
|
|
16918
|
-
propertyName,
|
|
16919
|
-
);
|
|
16920
|
-
childResource.getChildStore = (ownerKey) =>
|
|
16921
|
-
scopeMap.get(ownerKey)?.childStore;
|
|
16922
17017
|
return childResource;
|
|
16923
17018
|
};
|
|
16924
17019
|
|
|
@@ -17139,7 +17234,7 @@ const getLastGetItemId = (action) => {
|
|
|
17139
17234
|
};
|
|
17140
17235
|
|
|
17141
17236
|
// Captures the "file:line:column" of the user code that invoked the public
|
|
17142
|
-
// function (resource(), .one(), .many(),
|
|
17237
|
+
// function (resource(), .one(), .many(), .scopedOne(), …), so invalid-result
|
|
17143
17238
|
// errors can point at where the callbacks were declared, not at this file.
|
|
17144
17239
|
// Must be called directly from the public function: the stack offset accounts
|
|
17145
17240
|
// for exactly two frames (getCallerInfo → getDeclarationSite → public fn → user code).
|
|
@@ -17160,6 +17255,36 @@ ${originalActionName} source location: ${declarationSite}`,
|
|
|
17160
17255
|
};
|
|
17161
17256
|
};
|
|
17162
17257
|
|
|
17258
|
+
// A scoped child's item exists only inside its owner: two owners can each hold
|
|
17259
|
+
// one with the same id. The callbacks of a relation chained on it whose result
|
|
17260
|
+
// names that item do so by its id alone — `.one()`/`.many()` as the parent
|
|
17261
|
+
// object or id, `.scopedOne()`/`.scopedMany()` as the `ownerId` of
|
|
17262
|
+
// `[ownerId, …]` — so applied they would write the owner's store (`store` of a
|
|
17263
|
+
// scoped child) or the scope of another owner's item. They are refused rather
|
|
17264
|
+
// than routed: routing needs the owner in the result, a contract none of them
|
|
17265
|
+
// has.
|
|
17266
|
+
const refuseCallbacksNamingScopedItem = (
|
|
17267
|
+
relationLabel,
|
|
17268
|
+
callbacks,
|
|
17269
|
+
{ name, ownerName, propertyName, declarationSite },
|
|
17270
|
+
) => {
|
|
17271
|
+
const declaredKeys = [];
|
|
17272
|
+
for (const key of Object.keys(callbacks)) {
|
|
17273
|
+
if (callbacks[key]) {
|
|
17274
|
+
declaredKeys.push(key);
|
|
17275
|
+
}
|
|
17276
|
+
}
|
|
17277
|
+
if (declaredKeys.length === 0) {
|
|
17278
|
+
return;
|
|
17279
|
+
}
|
|
17280
|
+
const declared = declaredKeys.join(", ");
|
|
17281
|
+
throw new Error(
|
|
17282
|
+
`${relationLabel} cannot declare ${declared}: a "${name}" item exists only inside its "${ownerName}", and the result of ${declared} names it without saying which "${ownerName}" holds it.
|
|
17283
|
+
Return "${propertyName}" embedded in what the "${name}" callbacks return ([ownerId, { …, ${propertyName} }]): those say which "${ownerName}".
|
|
17284
|
+
${relationLabel} source location: ${declarationSite}`,
|
|
17285
|
+
);
|
|
17286
|
+
};
|
|
17287
|
+
|
|
17163
17288
|
// Shared by .many() and .scopedMany(): converts a raw relationship value (an
|
|
17164
17289
|
// array of child props/ids, or anything else meaning "empty") into an array of
|
|
17165
17290
|
// child ids, upserting each entry into the child store. The id array signal is
|
|
@@ -17301,22 +17426,28 @@ Received an object with keys: ${keys.join(", ")}.`,
|
|
|
17301
17426
|
* // which in turn triggers the route Signal->URL sync and updates the browser URL.
|
|
17302
17427
|
*/
|
|
17303
17428
|
const syncResourceToSignals = (resource, propertyToSignalMap) => {
|
|
17304
|
-
if (resource.
|
|
17429
|
+
if (resource.ownerName) {
|
|
17430
|
+
// A scoped child's `store` is its owner's: watching it would sync the
|
|
17431
|
+
// owner items' properties, not the child's.
|
|
17305
17432
|
throw new Error(
|
|
17306
|
-
`syncResourceToSignals: "${resource.name}"
|
|
17433
|
+
`syncResourceToSignals: "${resource.name}" lives inside each "${resource.ownerName}", so it has no store of its own to watch.${resource.getChildStore ? " Use syncOwnedResourceToSignals instead." : ""}`,
|
|
17307
17434
|
);
|
|
17308
17435
|
}
|
|
17309
17436
|
syncStoreToSignals(resource.store, propertyToSignalMap);
|
|
17310
17437
|
};
|
|
17311
17438
|
|
|
17312
17439
|
/**
|
|
17313
|
-
* The same, for a `
|
|
17314
|
-
* per owner, so the store to watch is the one of the owner
|
|
17315
|
-
* (by id or by any unique key), and it is switched when the
|
|
17316
|
-
* Nothing is synced while `ownerSignal` holds `null`/`undefined`
|
|
17317
|
-
* owner nothing has been loaded for yet.
|
|
17440
|
+
* The same, for a `scopedMany` resource (or a `withParams()` of one): its items
|
|
17441
|
+
* live in one store per owner, so the store to watch is the one of the owner
|
|
17442
|
+
* `ownerSignal` names (by id or by any unique key), and it is switched when the
|
|
17443
|
+
* signal changes. Nothing is synced while `ownerSignal` holds `null`/`undefined`
|
|
17444
|
+
* or names an owner nothing has been loaded for yet.
|
|
17318
17445
|
*
|
|
17319
|
-
*
|
|
17446
|
+
* A `scopedOne` child has no store (each owner holds its one item), and a
|
|
17447
|
+
* `scopedMany` declared on a scoped child is kept per item with no owner key to
|
|
17448
|
+
* find it by: both are refused.
|
|
17449
|
+
*
|
|
17450
|
+
* @param {Object} resource - a resource made by `.scopedMany()`
|
|
17320
17451
|
* @param {import("@preact/signals").Signal} ownerSignal - the owner whose children are watched
|
|
17321
17452
|
* @param {Object} propertyToSignalMap - `{ [propertyName]: signal }`, as for `syncResourceToSignals`
|
|
17322
17453
|
*/
|
|
@@ -17326,8 +17457,13 @@ const syncOwnedResourceToSignals = (
|
|
|
17326
17457
|
propertyToSignalMap,
|
|
17327
17458
|
) => {
|
|
17328
17459
|
if (!resource.getChildStore) {
|
|
17460
|
+
if (!resource.ownerName) {
|
|
17461
|
+
throw new Error(
|
|
17462
|
+
`syncOwnedResourceToSignals: "${resource.name}" is not a scoped resource. Use syncResourceToSignals instead.`,
|
|
17463
|
+
);
|
|
17464
|
+
}
|
|
17329
17465
|
throw new Error(
|
|
17330
|
-
`syncOwnedResourceToSignals: "${resource.name}" is
|
|
17466
|
+
`syncOwnedResourceToSignals: "${resource.name}" has no store per "${resource.ownerName}" to watch: a scopedOne child is held by each owner, and a scopedMany declared on a scoped child has no owner key to find its store by.`,
|
|
17331
17467
|
);
|
|
17332
17468
|
}
|
|
17333
17469
|
effect(() => {
|
|
@@ -29313,7 +29449,7 @@ const TRANSITION_FURNITURE_CSS = /* css */ `.navi_fixed_bar[data-navi-transition
|
|
|
29313
29449
|
clip-path: var(--wall-holes-arriving, var(--wall-holes));
|
|
29314
29450
|
}
|
|
29315
29451
|
|
|
29316
|
-
:root[data-navi-route-transition] {
|
|
29452
|
+
:root[data-navi-route-transition-target="area"] {
|
|
29317
29453
|
& .navi_dialog::backdrop, & .navi_popover_backdrop {
|
|
29318
29454
|
backdrop-filter: none;
|
|
29319
29455
|
background: none;
|
|
@@ -29458,10 +29594,7 @@ const nameFurnitureAround = (areaElement) => {
|
|
|
29458
29594
|
if (!name) {
|
|
29459
29595
|
// A name the application wrote itself answers for that element, and it
|
|
29460
29596
|
// is saying something navi is not: it wants that bar moved on the pages'
|
|
29461
|
-
// clock, by its own CSS.
|
|
29462
|
-
// bar wearing one of ITS names is one this movement inherited from the
|
|
29463
|
-
// one it interrupted, and taking it for the application's would leave it
|
|
29464
|
-
// named for the rest of the document's life.
|
|
29597
|
+
// clock, by its own CSS.
|
|
29465
29598
|
if (getComputedStyle(element).viewTransitionName !== "none") {
|
|
29466
29599
|
continue;
|
|
29467
29600
|
}
|
|
@@ -31283,8 +31416,17 @@ const beginTransition = ({
|
|
|
31283
31416
|
url: absoluteUrl(url),
|
|
31284
31417
|
decision: navigationDecision,
|
|
31285
31418
|
walkHome: null,
|
|
31286
|
-
releaseReverting: null
|
|
31419
|
+
releaseReverting: null,
|
|
31420
|
+
restoreDuration: null
|
|
31287
31421
|
};
|
|
31422
|
+
// The document wears one transition at a time, and the one this interrupts
|
|
31423
|
+
// ends only after this one has begun — too late to take off what it wears
|
|
31424
|
+
// (see end). Taking over therefore starts by taking it off, so that what is
|
|
31425
|
+
// put on below describes this transition alone, including what it leaves
|
|
31426
|
+
// unset: a type, an area, a duration.
|
|
31427
|
+
if (currentTransition) {
|
|
31428
|
+
releaseTransitionRoot(currentTransition);
|
|
31429
|
+
}
|
|
31288
31430
|
currentTransition = transition;
|
|
31289
31431
|
// Said before the picture is taken: whoever names something for a movement
|
|
31290
31432
|
// between two pages decides on it now (see transition_destination.js).
|
|
@@ -31328,11 +31470,10 @@ const beginTransition = ({
|
|
|
31328
31470
|
// A duration of this relation's own, worn for the length of the transition —
|
|
31329
31471
|
// and whatever the application had written inline put back afterwards, not
|
|
31330
31472
|
// erased.
|
|
31331
|
-
let restoreDuration = null;
|
|
31332
31473
|
if (duration !== undefined) {
|
|
31333
31474
|
const durationBefore = documentElement.style.getPropertyValue(TRANSITION_DURATION_PROPERTY);
|
|
31334
31475
|
documentElement.style.setProperty(TRANSITION_DURATION_PROPERTY, typeof duration === "number" ? `${duration}ms` : duration);
|
|
31335
|
-
restoreDuration = () => {
|
|
31476
|
+
transition.restoreDuration = () => {
|
|
31336
31477
|
if (durationBefore) {
|
|
31337
31478
|
documentElement.style.setProperty(TRANSITION_DURATION_PROPERTY, durationBefore);
|
|
31338
31479
|
} else {
|
|
@@ -31393,8 +31534,11 @@ const beginTransition = ({
|
|
|
31393
31534
|
renderWait.stop();
|
|
31394
31535
|
}
|
|
31395
31536
|
// The page arriving is in the DOM and the transition has not started
|
|
31396
|
-
// playing: the one moment both states of the area can be known.
|
|
31397
|
-
|
|
31537
|
+
// playing: the one moment both states of the area can be known. The
|
|
31538
|
+
// browser runs this callback even for a transition skipped before its
|
|
31539
|
+
// first picture — the next one starting skips it — and a transition
|
|
31540
|
+
// replaced holds nothing: what it wore was taken off at the takeover.
|
|
31541
|
+
if (areaElement && currentTransition === transition) {
|
|
31398
31542
|
holdTransitionFurniture(transition, areaElement);
|
|
31399
31543
|
holdTransitionWindow(transition, areaElement, areaStateBefore);
|
|
31400
31544
|
}
|
|
@@ -31403,8 +31547,9 @@ const beginTransition = ({
|
|
|
31403
31547
|
// Whatever ends it — played out, skipped by another transition starting,
|
|
31404
31548
|
// failed before its callback ever ran — the hold is given back and the
|
|
31405
31549
|
// document is handed back to the application. Both are idempotent, and
|
|
31406
|
-
// the
|
|
31407
|
-
// ending late
|
|
31550
|
+
// the document is handed back only by the LAST transition begun: an
|
|
31551
|
+
// earlier one ending late was taken off by the one that replaced it, and
|
|
31552
|
+
// must not strip what that one is wearing.
|
|
31408
31553
|
renderWait.stop();
|
|
31409
31554
|
releaseRendering();
|
|
31410
31555
|
// The hold a way back took, when it is still standing: the pictures were
|
|
@@ -31416,16 +31561,7 @@ const beginTransition = ({
|
|
|
31416
31561
|
}
|
|
31417
31562
|
if (currentTransition === transition) {
|
|
31418
31563
|
currentTransition = null;
|
|
31419
|
-
|
|
31420
|
-
documentElement.removeAttribute(TRANSITION_TYPE_ATTRIBUTE);
|
|
31421
|
-
documentElement.removeAttribute(TRANSITION_TARGET_ATTRIBUTE);
|
|
31422
|
-
releaseTransitionWindow(transition);
|
|
31423
|
-
releaseTransitionDestination(transition);
|
|
31424
|
-
releaseTransitionFurniture(transition);
|
|
31425
|
-
releaseTransitionPress(transition);
|
|
31426
|
-
if (restoreDuration) {
|
|
31427
|
-
restoreDuration();
|
|
31428
|
-
}
|
|
31564
|
+
releaseTransitionRoot(transition);
|
|
31429
31565
|
}
|
|
31430
31566
|
};
|
|
31431
31567
|
transition.viewTransition = viewTransition;
|
|
@@ -31433,6 +31569,22 @@ const beginTransition = ({
|
|
|
31433
31569
|
viewTransition.finished.then(end, end);
|
|
31434
31570
|
};
|
|
31435
31571
|
|
|
31572
|
+
// Everything a transition wears on the document for its length, taken off by
|
|
31573
|
+
// its own end or by the transition taking over from it.
|
|
31574
|
+
const releaseTransitionRoot = transition => {
|
|
31575
|
+
const documentElement = document.documentElement;
|
|
31576
|
+
documentElement.removeAttribute(TRANSITION_ATTRIBUTE);
|
|
31577
|
+
documentElement.removeAttribute(TRANSITION_TYPE_ATTRIBUTE);
|
|
31578
|
+
documentElement.removeAttribute(TRANSITION_TARGET_ATTRIBUTE);
|
|
31579
|
+
releaseTransitionWindow(transition);
|
|
31580
|
+
releaseTransitionDestination(transition);
|
|
31581
|
+
releaseTransitionFurniture(transition);
|
|
31582
|
+
releaseTransitionPress(transition);
|
|
31583
|
+
if (transition.restoreDuration) {
|
|
31584
|
+
transition.restoreDuration();
|
|
31585
|
+
}
|
|
31586
|
+
};
|
|
31587
|
+
|
|
31436
31588
|
/**
|
|
31437
31589
|
* The movement playing, turned round rather than replaced: this navigation is
|
|
31438
31590
|
* exactly the way back of the one on screen — the door pressed again while the
|
|
@@ -49056,10 +49208,10 @@ const css$R = /* css */`.navi_expandable {
|
|
|
49056
49208
|
}
|
|
49057
49209
|
|
|
49058
49210
|
& > .navi_expandable_content_container {
|
|
49059
|
-
clip-path: inset(-9999px -9999px 0);
|
|
49060
49211
|
grid-template-rows: 0fr;
|
|
49061
49212
|
display: grid;
|
|
49062
49213
|
position: relative;
|
|
49214
|
+
overflow-y: clip;
|
|
49063
49215
|
|
|
49064
49216
|
& > .navi_expandable_content_sizer {
|
|
49065
49217
|
min-height: 0;
|
|
@@ -49072,7 +49224,6 @@ const css$R = /* css */`.navi_expandable {
|
|
|
49072
49224
|
}
|
|
49073
49225
|
|
|
49074
49226
|
&[data-open-direction="up"] > .navi_expandable_content_container {
|
|
49075
|
-
clip-path: inset(0 -9999px -9999px);
|
|
49076
49227
|
align-content: end;
|
|
49077
49228
|
|
|
49078
49229
|
& > .navi_expandable_content_sizer {
|
|
@@ -49094,9 +49245,9 @@ const css$R = /* css */`.navi_expandable {
|
|
|
49094
49245
|
}
|
|
49095
49246
|
|
|
49096
49247
|
& > .navi_expandable_content_container {
|
|
49097
|
-
clip-path: inset(-9999px 0 0 -9999px);
|
|
49098
49248
|
grid-template-rows: 0fr;
|
|
49099
49249
|
grid-template-columns: 0fr;
|
|
49250
|
+
overflow: clip;
|
|
49100
49251
|
|
|
49101
49252
|
& > .navi_expandable_content_sizer {
|
|
49102
49253
|
min-width: 0;
|
|
@@ -49114,7 +49265,6 @@ const css$R = /* css */`.navi_expandable {
|
|
|
49114
49265
|
}
|
|
49115
49266
|
|
|
49116
49267
|
&[data-open-direction="left"] > .navi_expandable_content_container {
|
|
49117
|
-
clip-path: inset(-9999px -9999px 0 0);
|
|
49118
49268
|
justify-content: end;
|
|
49119
49269
|
|
|
49120
49270
|
& > .navi_expandable_content_sizer {
|
|
@@ -49136,11 +49286,10 @@ const css$R = /* css */`.navi_expandable {
|
|
|
49136
49286
|
}
|
|
49137
49287
|
|
|
49138
49288
|
&[aria-expanded="true"][data-settled] > .navi_expandable_content_container {
|
|
49139
|
-
|
|
49289
|
+
overflow: visible;
|
|
49140
49290
|
}
|
|
49141
49291
|
|
|
49142
49292
|
&:not([aria-expanded="true"])[data-settled] > .navi_expandable_content_container {
|
|
49143
|
-
clip-path: none;
|
|
49144
49293
|
overflow: clip;
|
|
49145
49294
|
}
|
|
49146
49295
|
|
|
@@ -49237,11 +49386,12 @@ const useExpandableContext = partName => {
|
|
|
49237
49386
|
* that can scroll so the content revealed past its edge is on screen: an
|
|
49238
49387
|
* expandable opening at the bottom of a list otherwise grows below the
|
|
49239
49388
|
* fold, and the user sees the row grow but not what it grew with. Measured
|
|
49240
|
-
* on the open layout
|
|
49241
|
-
*
|
|
49242
|
-
*
|
|
49243
|
-
*
|
|
49244
|
-
*
|
|
49389
|
+
* on the open layout, once per opening; when `animation` plays the
|
|
49390
|
+
* scroller rides along with the reveal, at the track's own progress, and
|
|
49391
|
+
* otherwise it goes there at once. A `scroll-margin` on the expandable is
|
|
49392
|
+
* honored. An expandable taller than the scroller keeps its UI part on
|
|
49393
|
+
* screen and the content follows as far as it fits. Only the nearest
|
|
49394
|
+
* scroller moves — a popup or a page around it stays where it is.
|
|
49245
49395
|
* @param maxContentHeight - Caps the content height; taller content scrolls
|
|
49246
49396
|
* inside the expandable instead of growing it.
|
|
49247
49397
|
* @param mount - When the content is built and thrown away, a popup's own
|
|
@@ -49364,15 +49514,27 @@ const Expandable = props => {
|
|
|
49364
49514
|
cancelSettleWatchRef.current?.();
|
|
49365
49515
|
cancelSettleWatchRef.current = null;
|
|
49366
49516
|
};
|
|
49367
|
-
|
|
49517
|
+
// `stopRide`, when the scroller rides along with the reveal (keepInView):
|
|
49518
|
+
// the ride is part of the movement, so it ends with it — settled or
|
|
49519
|
+
// cancelled — and never on its own clock.
|
|
49520
|
+
const watchSettle = stopRide => {
|
|
49368
49521
|
const contentContainer = contentContainerRef.current;
|
|
49369
49522
|
const contentElement = contentContainer.firstElementChild.firstElementChild;
|
|
49370
|
-
|
|
49523
|
+
const cancelWatch = whenTransitionSettles(contentContainer, () => {
|
|
49371
49524
|
cancelSettleWatchRef.current = null;
|
|
49525
|
+
if (stopRide) {
|
|
49526
|
+
stopRide();
|
|
49527
|
+
}
|
|
49372
49528
|
contentElement.style.width = "";
|
|
49373
49529
|
contentElement.style.height = "";
|
|
49374
49530
|
setSettled(true);
|
|
49375
49531
|
});
|
|
49532
|
+
cancelSettleWatchRef.current = () => {
|
|
49533
|
+
if (stopRide) {
|
|
49534
|
+
stopRide();
|
|
49535
|
+
}
|
|
49536
|
+
cancelWatch();
|
|
49537
|
+
};
|
|
49376
49538
|
};
|
|
49377
49539
|
|
|
49378
49540
|
// The reveal needs the content at its final size before the track starts
|
|
@@ -49391,17 +49553,22 @@ const Expandable = props => {
|
|
|
49391
49553
|
} else {
|
|
49392
49554
|
contentElement.style.height = `${finalRect.height}px`;
|
|
49393
49555
|
}
|
|
49556
|
+
let stopRide = null;
|
|
49394
49557
|
if (keepInView) {
|
|
49395
|
-
//
|
|
49396
|
-
// this point, so where the content ends is known before the
|
|
49397
|
-
// starts
|
|
49398
|
-
//
|
|
49399
|
-
|
|
49400
|
-
|
|
49401
|
-
|
|
49402
|
-
|
|
49403
|
-
|
|
49404
|
-
|
|
49558
|
+
// Measured here and not once the reveal settles: the layout is the open
|
|
49559
|
+
// one at this point, so where the content ends is known before the
|
|
49560
|
+
// movement starts. The scroll then rides the movement rather than
|
|
49561
|
+
// running ahead of it (see rideScrollerAlongReveal).
|
|
49562
|
+
const revealScroll = measureRevealScroll(rootRef.current, revealDirection);
|
|
49563
|
+
if (revealScroll) {
|
|
49564
|
+
const finalContainerRect = contentContainer.getBoundingClientRect();
|
|
49565
|
+
stopRide = rideScrollerAlongReveal(revealScroll, {
|
|
49566
|
+
contentContainer,
|
|
49567
|
+
axis: isColumn ? "x" : "y",
|
|
49568
|
+
startSize: isColumn ? startRect.width : startRect.height,
|
|
49569
|
+
finalSize: isColumn ? finalContainerRect.width : finalContainerRect.height
|
|
49570
|
+
});
|
|
49571
|
+
}
|
|
49405
49572
|
}
|
|
49406
49573
|
// Put the tracks back where the last paint left them and let the
|
|
49407
49574
|
// transition play from there. In fr — px does not interpolate with fr.
|
|
@@ -49424,6 +49591,7 @@ const Expandable = props => {
|
|
|
49424
49591
|
contentContainer.style.transitionProperty = "";
|
|
49425
49592
|
contentContainer.style.gridTemplateColumns = "";
|
|
49426
49593
|
contentContainer.style.gridTemplateRows = "";
|
|
49594
|
+
return stopRide;
|
|
49427
49595
|
};
|
|
49428
49596
|
|
|
49429
49597
|
// What opening LOOKS like here, and how to undo it — the one thing an
|
|
@@ -49455,12 +49623,17 @@ const Expandable = props => {
|
|
|
49455
49623
|
// transfer there is nothing to restore on close either.
|
|
49456
49624
|
const restoreFocus = autoFocus && !silent && contentContainer ? openController.transferFocusOnOpen(contentContainer) : null;
|
|
49457
49625
|
if (revealing) {
|
|
49458
|
-
armReveal(startRect);
|
|
49459
|
-
watchSettle();
|
|
49626
|
+
const stopRide = armReveal(startRect);
|
|
49627
|
+
watchSettle(stopRide);
|
|
49460
49628
|
} else if (keepInView && !silent && contentContainer) {
|
|
49461
|
-
|
|
49462
|
-
|
|
49463
|
-
|
|
49629
|
+
const revealScroll = measureRevealScroll(rootRef.current, revealDirection);
|
|
49630
|
+
if (revealScroll) {
|
|
49631
|
+
// No behavior: the scroller's own scroll-behavior decides.
|
|
49632
|
+
revealScroll.scrollerEl.scrollTo({
|
|
49633
|
+
left: revealScroll.left,
|
|
49634
|
+
top: revealScroll.top
|
|
49635
|
+
});
|
|
49636
|
+
}
|
|
49464
49637
|
}
|
|
49465
49638
|
if (!silent) {
|
|
49466
49639
|
rootRef.current.dispatchEvent(createToggleEvent(true));
|
|
@@ -49502,7 +49675,7 @@ const Expandable = props => {
|
|
|
49502
49675
|
}
|
|
49503
49676
|
}
|
|
49504
49677
|
if (collapsing) {
|
|
49505
|
-
watchSettle();
|
|
49678
|
+
watchSettle(null);
|
|
49506
49679
|
}
|
|
49507
49680
|
rootRef.current.dispatchEvent(createToggleEvent(false));
|
|
49508
49681
|
};
|
|
@@ -49842,16 +50015,14 @@ Expandable.Content = ExpandableContent;
|
|
|
49842
50015
|
// the one exception, excluded at the call site.
|
|
49843
50016
|
const UI_INTERACTIVE_SELECTOR = ["a[href]", "button", "input", "select", "textarea", "label", "[role='button']", "[contenteditable='']", "[contenteditable='true']", "audio[controls]", "video[controls]"].join(", ");
|
|
49844
50017
|
|
|
49845
|
-
//
|
|
49846
|
-
// part and revealed content —
|
|
49847
|
-
// so the caller
|
|
49848
|
-
// openEffect). A root taller than the view is aligned on
|
|
49849
|
-
// what the user pressed stays on screen, and the content
|
|
49850
|
-
// fits. One scroller only — a popup or a page around it
|
|
49851
|
-
|
|
49852
|
-
|
|
49853
|
-
behavior
|
|
49854
|
-
}) => {
|
|
50018
|
+
// Where the nearest scroller that can scroll would have to be for the whole
|
|
50019
|
+
// expandable — UI part and revealed content — to be in view. Measured on the
|
|
50020
|
+
// layout as it stands, so the caller measures while the open layout is the
|
|
50021
|
+
// one laid out (see openEffect). A root taller than the view is aligned on
|
|
50022
|
+
// its UI side instead: what the user pressed stays on screen, and the content
|
|
50023
|
+
// follows as far as it fits. One scroller only — a popup or a page around it
|
|
50024
|
+
// is left where it is. Null when there is no scroller, or nothing to scroll.
|
|
50025
|
+
const measureRevealScroll = (rootEl, revealDirection) => {
|
|
49855
50026
|
let scrollerEl = null;
|
|
49856
50027
|
for (const scrollContainer of getScrollContainerSet(rootEl)) {
|
|
49857
50028
|
if (isScrollable(scrollContainer)) {
|
|
@@ -49860,7 +50031,7 @@ const scrollExpandableIntoView = (rootEl, {
|
|
|
49860
50031
|
}
|
|
49861
50032
|
}
|
|
49862
50033
|
if (!scrollerEl) {
|
|
49863
|
-
return;
|
|
50034
|
+
return null;
|
|
49864
50035
|
}
|
|
49865
50036
|
const rootRect = rootEl.getBoundingClientRect();
|
|
49866
50037
|
let block = "nearest";
|
|
@@ -49872,12 +50043,74 @@ const scrollExpandableIntoView = (rootEl, {
|
|
|
49872
50043
|
} else if (rootRect.width > scrollerEl.clientWidth) {
|
|
49873
50044
|
inline = revealDirection === "right" ? "start" : "end";
|
|
49874
50045
|
}
|
|
49875
|
-
|
|
50046
|
+
const {
|
|
50047
|
+
left,
|
|
50048
|
+
top
|
|
50049
|
+
} = getScrollIntoViewScopedOffsets(rootEl, {
|
|
49876
50050
|
container: scrollerEl,
|
|
49877
50051
|
block,
|
|
49878
|
-
inline
|
|
49879
|
-
behavior
|
|
50052
|
+
inline
|
|
49880
50053
|
});
|
|
50054
|
+
if (left === scrollerEl.scrollLeft && top === scrollerEl.scrollTop) {
|
|
50055
|
+
return null;
|
|
50056
|
+
}
|
|
50057
|
+
return {
|
|
50058
|
+
scrollerEl,
|
|
50059
|
+
left,
|
|
50060
|
+
top
|
|
50061
|
+
};
|
|
50062
|
+
};
|
|
50063
|
+
|
|
50064
|
+
// Moves the scroller toward where the reveal ends at the pace of the reveal
|
|
50065
|
+
// itself: every frame the track's progress is read from the container's size
|
|
50066
|
+
// and the scroll stands the same fraction of its own way. The scroll cannot
|
|
50067
|
+
// run ahead of the track: only the box's own growth extends the scroller's
|
|
50068
|
+
// range, and what the target adds around the box (a parent's padding, a
|
|
50069
|
+
// scroll-margin) becomes reachable as the track grows — a scroll started at
|
|
50070
|
+
// once would be clamped short of it. One clock, the transition's, so the
|
|
50071
|
+
// content never waits below the fold either. Returns what stops the ride.
|
|
50072
|
+
const rideScrollerAlongReveal = ({
|
|
50073
|
+
scrollerEl,
|
|
50074
|
+
left,
|
|
50075
|
+
top
|
|
50076
|
+
}, {
|
|
50077
|
+
contentContainer,
|
|
50078
|
+
axis,
|
|
50079
|
+
startSize,
|
|
50080
|
+
finalSize
|
|
50081
|
+
}) => {
|
|
50082
|
+
const startLeft = scrollerEl.scrollLeft;
|
|
50083
|
+
const startTop = scrollerEl.scrollTop;
|
|
50084
|
+
const leftDelta = left - startLeft;
|
|
50085
|
+
const topDelta = top - startTop;
|
|
50086
|
+
let frameId;
|
|
50087
|
+
const tick = () => {
|
|
50088
|
+
const rect = contentContainer.getBoundingClientRect();
|
|
50089
|
+
const size = axis === "x" ? rect.width : rect.height;
|
|
50090
|
+
let progress = 1;
|
|
50091
|
+
if (finalSize !== startSize) {
|
|
50092
|
+
progress = (size - startSize) / (finalSize - startSize);
|
|
50093
|
+
if (progress < 0) {
|
|
50094
|
+
progress = 0;
|
|
50095
|
+
} else if (progress > 1) {
|
|
50096
|
+
progress = 1;
|
|
50097
|
+
}
|
|
50098
|
+
}
|
|
50099
|
+
scrollerEl.scrollTo({
|
|
50100
|
+
left: startLeft + leftDelta * progress,
|
|
50101
|
+
top: startTop + topDelta * progress,
|
|
50102
|
+
// Driven frame by frame: a smooth scroll-behavior on the scroller is
|
|
50103
|
+
// for a scroll that lands, not for one that is held.
|
|
50104
|
+
behavior: "instant"
|
|
50105
|
+
});
|
|
50106
|
+
if (progress < 1) {
|
|
50107
|
+
frameId = requestAnimationFrame(tick);
|
|
50108
|
+
}
|
|
50109
|
+
};
|
|
50110
|
+
frameId = requestAnimationFrame(tick);
|
|
50111
|
+
return () => {
|
|
50112
|
+
cancelAnimationFrame(frameId);
|
|
50113
|
+
};
|
|
49881
50114
|
};
|
|
49882
50115
|
const createToggleEvent = open => {
|
|
49883
50116
|
const newState = open ? "open" : "closed";
|
|
@@ -72675,11 +72908,15 @@ const PickerNaviTime = props => {
|
|
|
72675
72908
|
max = "23:30",
|
|
72676
72909
|
step
|
|
72677
72910
|
} = props;
|
|
72678
|
-
|
|
72911
|
+
// resolveInputProps has already run (PickerFirstResolver) and left navi_time
|
|
72912
|
+
// as written: what is handed on is a time input, whose step is in seconds.
|
|
72913
|
+
const hostStep = timeStringToSeconds(step);
|
|
72914
|
+
const stepSeconds = hostStep ?? 1800;
|
|
72679
72915
|
const slots = useMemo(() => generateTimeSlots(min, max, stepSeconds), [min, max, stepSeconds]);
|
|
72680
72916
|
return jsx(Next, {
|
|
72681
72917
|
...props,
|
|
72682
72918
|
type: "time",
|
|
72919
|
+
step: hostStep,
|
|
72683
72920
|
children: jsx(List, {
|
|
72684
72921
|
selectable: true,
|
|
72685
72922
|
command: "--navi-send",
|
|
@@ -75091,6 +75328,10 @@ const PickerFirstResolver = props => {
|
|
|
75091
75328
|
...props
|
|
75092
75329
|
});
|
|
75093
75330
|
};
|
|
75331
|
+
|
|
75332
|
+
// PickerFirstResolver comes before the presets so a preset reads the
|
|
75333
|
+
// min/max/step a bound signal fills in (resolveInputProps); a preset type
|
|
75334
|
+
// therefore has no row in NAVI_TYPE_DEFAULTS (resolve_input_props.js).
|
|
75094
75335
|
const PickerResolved = /*#__PURE__*/createComponentResolver([PickerFirstResolver, PickerPresetResolver, PickerConfirmResolver, PickerCustomResolver, PickerTypeResolver, PickerButton]);
|
|
75095
75336
|
const PickerUI = /*#__PURE__*/Object.assign(PickerDefaultUI, {
|
|
75096
75337
|
Date: PickerDateUI,
|