@jsenv/navi 0.29.389 → 0.29.390
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 +252 -92
- package/dist/dev/jsenv_navi.js.map +7 -7
- package/dist/jsenv_navi.js +252 -92
- package/dist/jsenv_navi.js.map +7 -7
- 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
|
|
@@ -72675,11 +72827,15 @@ const PickerNaviTime = props => {
|
|
|
72675
72827
|
max = "23:30",
|
|
72676
72828
|
step
|
|
72677
72829
|
} = props;
|
|
72678
|
-
|
|
72830
|
+
// resolveInputProps has already run (PickerFirstResolver) and left navi_time
|
|
72831
|
+
// as written: what is handed on is a time input, whose step is in seconds.
|
|
72832
|
+
const hostStep = timeStringToSeconds(step);
|
|
72833
|
+
const stepSeconds = hostStep ?? 1800;
|
|
72679
72834
|
const slots = useMemo(() => generateTimeSlots(min, max, stepSeconds), [min, max, stepSeconds]);
|
|
72680
72835
|
return jsx(Next, {
|
|
72681
72836
|
...props,
|
|
72682
72837
|
type: "time",
|
|
72838
|
+
step: hostStep,
|
|
72683
72839
|
children: jsx(List, {
|
|
72684
72840
|
selectable: true,
|
|
72685
72841
|
command: "--navi-send",
|
|
@@ -75091,6 +75247,10 @@ const PickerFirstResolver = props => {
|
|
|
75091
75247
|
...props
|
|
75092
75248
|
});
|
|
75093
75249
|
};
|
|
75250
|
+
|
|
75251
|
+
// PickerFirstResolver comes before the presets so a preset reads the
|
|
75252
|
+
// min/max/step a bound signal fills in (resolveInputProps); a preset type
|
|
75253
|
+
// therefore has no row in NAVI_TYPE_DEFAULTS (resolve_input_props.js).
|
|
75094
75254
|
const PickerResolved = /*#__PURE__*/createComponentResolver([PickerFirstResolver, PickerPresetResolver, PickerConfirmResolver, PickerCustomResolver, PickerTypeResolver, PickerButton]);
|
|
75095
75255
|
const PickerUI = /*#__PURE__*/Object.assign(PickerDefaultUI, {
|
|
75096
75256
|
Date: PickerDateUI,
|