@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/dev/jsenv_navi.js
CHANGED
|
@@ -4955,14 +4955,11 @@ const CONTROL_TYPE_BY_SIGNAL_TYPE = {
|
|
|
4955
4955
|
// become something plainer — it is what says the value is a number (see
|
|
4956
4956
|
// isNumberInput), and what lets constraint messages use domain-specific
|
|
4957
4957
|
// wording instead of the generic "Ce nombre doit être...".
|
|
4958
|
+
// A type a picker preset draws (navi_time, navi_minute: see picker_preset.jsx)
|
|
4959
|
+
// has no row: resolveInputProps runs before the preset resolver, and a row
|
|
4960
|
+
// would turn the type into its host before the preset resolver reads it. The
|
|
4961
|
+
// preset hands the host type on itself.
|
|
4958
4962
|
const NAVI_TYPE_DEFAULTS = {
|
|
4959
|
-
navi_time: {
|
|
4960
|
-
"type": "time",
|
|
4961
|
-
"navi-input-type": "time",
|
|
4962
|
-
"min": 0,
|
|
4963
|
-
"max": 24 * 3600 - 1,
|
|
4964
|
-
"step": 1,
|
|
4965
|
-
},
|
|
4966
4963
|
navi_percentage: {
|
|
4967
4964
|
"type": "navi_number",
|
|
4968
4965
|
"navi-input-type": "percentage",
|
|
@@ -5024,7 +5021,6 @@ const isNumberInput = (type, naviInputType) => {
|
|
|
5024
5021
|
* Supported navi types and their targets:
|
|
5025
5022
|
* - `navi_percentage` → `navi_number` (0–100, step 1)
|
|
5026
5023
|
* - `navi_number` → `text` (inputMode="numeric", no spin buttons implied)
|
|
5027
|
-
* - `navi_time` → `time` (step in seconds)
|
|
5028
5024
|
*
|
|
5029
5025
|
* Standard HTML input types with formatters:
|
|
5030
5026
|
* - `date`, `month`, `week`, `time`, `datetime-local`, `datetime`:
|
|
@@ -16611,6 +16607,15 @@ const createResource = (
|
|
|
16611
16607
|
paramScope,
|
|
16612
16608
|
rerunOn,
|
|
16613
16609
|
dependencies,
|
|
16610
|
+
// Set on a scoped child: the resource owning each of its items, which
|
|
16611
|
+
// live in one store per owner (`store` is the owner's).
|
|
16612
|
+
ownerName,
|
|
16613
|
+
// Set on a scopedMany child: its per-owner store looked up by owner key,
|
|
16614
|
+
// and the owner whose GET embeds the collection. Options rather than
|
|
16615
|
+
// properties added afterwards, so every facade of the relation — the one
|
|
16616
|
+
// scopedMany returns and each withParams() of it — carries both.
|
|
16617
|
+
getChildStore,
|
|
16618
|
+
ownerDependency,
|
|
16614
16619
|
},
|
|
16615
16620
|
) => {
|
|
16616
16621
|
const params = paramScope.params;
|
|
@@ -16636,6 +16641,8 @@ const createResource = (
|
|
|
16636
16641
|
// private but exposed for convenience
|
|
16637
16642
|
store,
|
|
16638
16643
|
addItemSetup,
|
|
16644
|
+
ownerName,
|
|
16645
|
+
getChildStore,
|
|
16639
16646
|
};
|
|
16640
16647
|
|
|
16641
16648
|
resourceLifecycleManager.registerResource(stateFacade, {
|
|
@@ -16644,6 +16651,17 @@ const createResource = (
|
|
|
16644
16651
|
dependencies,
|
|
16645
16652
|
uniqueKeys,
|
|
16646
16653
|
});
|
|
16654
|
+
if (ownerDependency) {
|
|
16655
|
+
// A child collection mutated (POST etc.) makes the owner GET re-fetch:
|
|
16656
|
+
// the owner embeds the child array and only the backend knows the new
|
|
16657
|
+
// ordering. Registered per facade, since the rerun is looked up by the
|
|
16658
|
+
// facade whose action completed.
|
|
16659
|
+
resourceLifecycleManager.addDependency(
|
|
16660
|
+
stateFacade,
|
|
16661
|
+
ownerDependency.owner,
|
|
16662
|
+
ownerDependency.propertyName,
|
|
16663
|
+
);
|
|
16664
|
+
}
|
|
16647
16665
|
const onActionComplete = (actionCompleted) => {
|
|
16648
16666
|
resourceLifecycleManager.onActionComplete(actionCompleted, {
|
|
16649
16667
|
resourceScope: stateFacade,
|
|
@@ -16656,6 +16674,10 @@ const createResource = (
|
|
|
16656
16674
|
* Actions from parameterized resources only trigger rerun/reset for other actions with
|
|
16657
16675
|
* identical parameters, preventing cross-contamination between different parameter sets.
|
|
16658
16676
|
*
|
|
16677
|
+
* The actions are the resource's own kind: on a relation they keep its callback return
|
|
16678
|
+
* contract and write where it writes (`TABLE_COLUMNS.withParams(…).GET_MANY` returns
|
|
16679
|
+
* `[ownerId, items]` and replaces that owner's collection).
|
|
16680
|
+
*
|
|
16659
16681
|
* @param {Object} params - Parameters to bind to all actions of this resource (required)
|
|
16660
16682
|
* @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
|
|
16661
16683
|
* @returns {Object} A new resource instance with parameter-bound actions and isolated lifecycle
|
|
@@ -16681,7 +16703,6 @@ const createResource = (
|
|
|
16681
16703
|
paramsToInject,
|
|
16682
16704
|
{ dependencies: withParamsDeps, rerunOn: withParamsRerunOn } = {},
|
|
16683
16705
|
) => {
|
|
16684
|
-
const declarationSite = getDeclarationSite();
|
|
16685
16706
|
if (!paramsToInject || Object.keys(paramsToInject).length === 0) {
|
|
16686
16707
|
throw new Error(`resource(${name}).withParams() requires parameters`);
|
|
16687
16708
|
}
|
|
@@ -16689,11 +16710,6 @@ const createResource = (
|
|
|
16689
16710
|
? { ...params, ...paramsToInject }
|
|
16690
16711
|
: paramsToInject;
|
|
16691
16712
|
const resolvedParamScope = getParamScope(resolvedParams);
|
|
16692
|
-
const createRestActionWithParams = createRestActionFactoryForRoot(name, {
|
|
16693
|
-
idKey,
|
|
16694
|
-
store,
|
|
16695
|
-
declarationSite,
|
|
16696
|
-
});
|
|
16697
16713
|
return createResource(name, {
|
|
16698
16714
|
idKey,
|
|
16699
16715
|
uniqueKeys,
|
|
@@ -16701,10 +16717,16 @@ const createResource = (
|
|
|
16701
16717
|
restCallbacks,
|
|
16702
16718
|
store,
|
|
16703
16719
|
addItemSetup,
|
|
16704
|
-
|
|
16720
|
+
// The actions of this resource's kind: a relation applies its results to
|
|
16721
|
+
// its relation (a scoped child to the per-owner stores), never as root
|
|
16722
|
+
// items of `store`.
|
|
16723
|
+
createRestAction,
|
|
16705
16724
|
paramScope: resolvedParamScope,
|
|
16706
16725
|
rerunOn: resolveRerunOn(withParamsRerunOn, rerunOn),
|
|
16707
16726
|
dependencies: withParamsDeps ?? dependencies,
|
|
16727
|
+
ownerName,
|
|
16728
|
+
getChildStore,
|
|
16729
|
+
ownerDependency,
|
|
16708
16730
|
});
|
|
16709
16731
|
};
|
|
16710
16732
|
stateFacade.withParams = withParams;
|
|
@@ -16749,6 +16771,13 @@ const createResource = (
|
|
|
16749
16771
|
} = {},
|
|
16750
16772
|
) => {
|
|
16751
16773
|
const declarationSite = getDeclarationSite();
|
|
16774
|
+
if (ownerName) {
|
|
16775
|
+
refuseCallbacksNamingScopedItem(
|
|
16776
|
+
`${name}.one("${propertyName}")`,
|
|
16777
|
+
{ GET, PUT, DELETE },
|
|
16778
|
+
{ name, ownerName, propertyName, declarationSite },
|
|
16779
|
+
);
|
|
16780
|
+
}
|
|
16752
16781
|
const childName = `${name}.${propertyName}`;
|
|
16753
16782
|
const childIdKey = childResource.idKey;
|
|
16754
16783
|
const childStore = childResource.store;
|
|
@@ -16809,7 +16838,11 @@ const createResource = (
|
|
|
16809
16838
|
);
|
|
16810
16839
|
});
|
|
16811
16840
|
|
|
16812
|
-
const createRestActionForOne = (
|
|
16841
|
+
const createRestActionForOne = (
|
|
16842
|
+
verb,
|
|
16843
|
+
callback,
|
|
16844
|
+
{ onActionComplete, paramScope },
|
|
16845
|
+
) => {
|
|
16813
16846
|
const applyResultToValue =
|
|
16814
16847
|
verb === "DELETE"
|
|
16815
16848
|
? (itemIdOrItemProps) => {
|
|
@@ -16924,6 +16957,13 @@ const createResource = (
|
|
|
16924
16957
|
} = {},
|
|
16925
16958
|
) => {
|
|
16926
16959
|
const declarationSite = getDeclarationSite();
|
|
16960
|
+
if (ownerName) {
|
|
16961
|
+
refuseCallbacksNamingScopedItem(
|
|
16962
|
+
`${name}.many("${propertyName}")`,
|
|
16963
|
+
{ GET_MANY, DELETE, DELETE_MANY },
|
|
16964
|
+
{ name, ownerName, propertyName, declarationSite },
|
|
16965
|
+
);
|
|
16966
|
+
}
|
|
16927
16967
|
const childStore = childResource.store;
|
|
16928
16968
|
const childIdKey = childResource.idKey;
|
|
16929
16969
|
const childName = `${name}.${propertyName}`;
|
|
@@ -16955,21 +16995,23 @@ const createResource = (
|
|
|
16955
16995
|
const createRestActionForMany = (
|
|
16956
16996
|
verb,
|
|
16957
16997
|
callback,
|
|
16958
|
-
{ isMany, onActionComplete },
|
|
16998
|
+
{ isMany, onActionComplete, paramScope },
|
|
16959
16999
|
) => {
|
|
16960
17000
|
if (!isMany) {
|
|
16961
17001
|
return createRestActionAffectingOneItem(verb, callback, {
|
|
16962
17002
|
onActionComplete,
|
|
17003
|
+
paramScope,
|
|
16963
17004
|
});
|
|
16964
17005
|
}
|
|
16965
17006
|
return createRestActionAffectingManyItems(verb, callback, {
|
|
16966
17007
|
onActionComplete,
|
|
17008
|
+
paramScope,
|
|
16967
17009
|
});
|
|
16968
17010
|
};
|
|
16969
17011
|
const createRestActionAffectingOneItem = (
|
|
16970
17012
|
verb,
|
|
16971
17013
|
callback,
|
|
16972
|
-
{ onActionComplete },
|
|
17014
|
+
{ onActionComplete, paramScope },
|
|
16973
17015
|
) => {
|
|
16974
17016
|
const applyResultToValue =
|
|
16975
17017
|
verb === "DELETE"
|
|
@@ -17032,7 +17074,7 @@ const createResource = (
|
|
|
17032
17074
|
const createRestActionAffectingManyItems = (
|
|
17033
17075
|
verb,
|
|
17034
17076
|
callback,
|
|
17035
|
-
{ onActionComplete },
|
|
17077
|
+
{ onActionComplete, paramScope },
|
|
17036
17078
|
) => {
|
|
17037
17079
|
const applyResultToValue =
|
|
17038
17080
|
verb === "GET"
|
|
@@ -17160,7 +17202,12 @@ const createResource = (
|
|
|
17160
17202
|
* Mutations apply directly to the owner's signal, so the parent GET is never rerun.
|
|
17161
17203
|
*
|
|
17162
17204
|
* Returns the child relationship resource, itself chainable:
|
|
17163
|
-
* `USER_PROFILE.one("theme", THEME)` adds a reactive `.theme` property on each profile
|
|
17205
|
+
* `USER_PROFILE.one("theme", THEME)` adds a reactive `.theme` property on each profile,
|
|
17206
|
+
* fed by what this resource's callbacks embed (`[id, { bio, theme: { id: 3 } }]`). A
|
|
17207
|
+
* profile exists only inside its user, so the chained relation declares none of the
|
|
17208
|
+
* callbacks whose result names a profile (`.one()` GET/PUT/DELETE, `.many()`
|
|
17209
|
+
* GET_MANY/DELETE/DELETE_MANY, any `.scopedOne()`/`.scopedMany()` callback): such a
|
|
17210
|
+
* result cannot say which user holds it, and the declaration throws.
|
|
17164
17211
|
*
|
|
17165
17212
|
* @param {string} propertyName - property holding the sub-object on each owner item
|
|
17166
17213
|
* @param {Object} [restCallbacks] - `{ idKey, rerunOn, dependencies, GET, POST, PUT, PATCH, DELETE }`
|
|
@@ -17180,6 +17227,14 @@ const createResource = (
|
|
|
17180
17227
|
DELETE,
|
|
17181
17228
|
} = {},
|
|
17182
17229
|
) => {
|
|
17230
|
+
const declarationSite = getDeclarationSite();
|
|
17231
|
+
if (ownerName) {
|
|
17232
|
+
refuseCallbacksNamingScopedItem(
|
|
17233
|
+
`${name}.scopedOne("${propertyName}")`,
|
|
17234
|
+
{ GET, POST, PUT, PATCH, DELETE },
|
|
17235
|
+
{ name, ownerName, propertyName, declarationSite },
|
|
17236
|
+
);
|
|
17237
|
+
}
|
|
17183
17238
|
const childName = `${name}.${propertyName}`;
|
|
17184
17239
|
|
|
17185
17240
|
// Callbacks added by chained .one()/.many() on the child resource,
|
|
@@ -17209,7 +17264,12 @@ const createResource = (
|
|
|
17209
17264
|
childSignal.value = childItem; // first activation: null → childItem
|
|
17210
17265
|
}
|
|
17211
17266
|
};
|
|
17212
|
-
|
|
17267
|
+
if (!ownerName) {
|
|
17268
|
+
// Declared on a scoped child, an owner id is only unique inside its
|
|
17269
|
+
// own owner and no callback addresses it (see
|
|
17270
|
+
// refuseCallbacksNamingScopedItem): nothing is looked up by it.
|
|
17271
|
+
applyPropsMap.set(ownerId, applyProps);
|
|
17272
|
+
}
|
|
17213
17273
|
|
|
17214
17274
|
applyProps(ownerItem[propertyName]);
|
|
17215
17275
|
|
|
@@ -17221,7 +17281,7 @@ const createResource = (
|
|
|
17221
17281
|
const createRestActionForScopedOne = (
|
|
17222
17282
|
verb,
|
|
17223
17283
|
callback,
|
|
17224
|
-
{ onActionComplete },
|
|
17284
|
+
{ onActionComplete, paramScope },
|
|
17225
17285
|
) => {
|
|
17226
17286
|
const childActionName = `${childName}.${verb}`;
|
|
17227
17287
|
return createAction(callback, {
|
|
@@ -17269,6 +17329,7 @@ const createResource = (
|
|
|
17269
17329
|
paramScope,
|
|
17270
17330
|
rerunOn: resolveRerunOn(scopedOneRerunOn, rerunOn),
|
|
17271
17331
|
dependencies: scopedOneDependencies ?? dependencies,
|
|
17332
|
+
ownerName: name,
|
|
17272
17333
|
});
|
|
17273
17334
|
};
|
|
17274
17335
|
|
|
@@ -17296,7 +17357,14 @@ const createResource = (
|
|
|
17296
17357
|
* `propertyName`), and the child's own GET_MANY reruns per its `rerunOn`.
|
|
17297
17358
|
*
|
|
17298
17359
|
* Returns the child relationship resource, itself chainable:
|
|
17299
|
-
* `TABLE_COLUMNS.one("dataType", DATA_TYPE)` adds a reactive `.dataType` property on each column
|
|
17360
|
+
* `TABLE_COLUMNS.one("dataType", DATA_TYPE)` adds a reactive `.dataType` property on each column,
|
|
17361
|
+
* fed by what this resource's callbacks embed (`[id, [{ name, dataType: { id: 2 } }]]`). A
|
|
17362
|
+
* column exists only inside its table — two tables can each have an `email` — so the chained
|
|
17363
|
+
* relation declares none of the callbacks whose result names a column (`.one()`
|
|
17364
|
+
* GET/PUT/DELETE, `.many()` GET_MANY/DELETE/DELETE_MANY, any `.scopedOne()`/`.scopedMany()`
|
|
17365
|
+
* callback): such a result cannot say which table holds it, and the declaration throws.
|
|
17366
|
+
* A `.scopedMany()` chained on it keeps one collection per column: `users.email` and
|
|
17367
|
+
* `admins.email` never share one.
|
|
17300
17368
|
*
|
|
17301
17369
|
* @param {string} propertyName - property holding the collection on each owner item
|
|
17302
17370
|
* @param {Object} [restCallbacks] - `{ idKey, rerunOn, dependencies, GET, GET_MANY, POST, POST_MANY, PUT, PUT_MANY, PATCH, PATCH_MANY, DELETE, DELETE_MANY }`
|
|
@@ -17321,6 +17389,25 @@ const createResource = (
|
|
|
17321
17389
|
DELETE_MANY,
|
|
17322
17390
|
} = {},
|
|
17323
17391
|
) => {
|
|
17392
|
+
const declarationSite = getDeclarationSite();
|
|
17393
|
+
if (ownerName) {
|
|
17394
|
+
refuseCallbacksNamingScopedItem(
|
|
17395
|
+
`${name}.scopedMany("${propertyName}")`,
|
|
17396
|
+
{
|
|
17397
|
+
GET,
|
|
17398
|
+
GET_MANY,
|
|
17399
|
+
POST,
|
|
17400
|
+
POST_MANY,
|
|
17401
|
+
PUT,
|
|
17402
|
+
PUT_MANY,
|
|
17403
|
+
PATCH,
|
|
17404
|
+
PATCH_MANY,
|
|
17405
|
+
DELETE,
|
|
17406
|
+
DELETE_MANY,
|
|
17407
|
+
},
|
|
17408
|
+
{ name, ownerName, propertyName, declarationSite },
|
|
17409
|
+
);
|
|
17410
|
+
}
|
|
17324
17411
|
const childName = `${name}.${propertyName}`;
|
|
17325
17412
|
|
|
17326
17413
|
// Callbacks added by chained .one()/.many() on the child resource,
|
|
@@ -17342,37 +17429,42 @@ const createResource = (
|
|
|
17342
17429
|
return childItem;
|
|
17343
17430
|
},
|
|
17344
17431
|
});
|
|
17345
|
-
|
|
17346
|
-
scopeMap.set(ownerKey, scope);
|
|
17347
|
-
return scope;
|
|
17432
|
+
return { childStore, idArraySignal: signal([]) };
|
|
17348
17433
|
};
|
|
17349
17434
|
addItemSetup((item) => {
|
|
17350
17435
|
const ownerId = item[idKey];
|
|
17351
|
-
|
|
17352
|
-
|
|
17353
|
-
|
|
17354
|
-
|
|
17355
|
-
|
|
17436
|
+
let scope;
|
|
17437
|
+
if (ownerName) {
|
|
17438
|
+
// The owner is itself a scoped child: its id is only unique inside its
|
|
17439
|
+
// own owner, so the scope is the item's alone, never looked up by id
|
|
17440
|
+
// (no callback addresses it, see refuseCallbacksNamingScopedItem).
|
|
17441
|
+
scope = createScope(ownerId);
|
|
17442
|
+
} else {
|
|
17443
|
+
// Reuse an existing scope if one was already created under a uniqueKey
|
|
17444
|
+
// value (e.g. rows were fetched by tablename before the full table was loaded).
|
|
17445
|
+
scope = scopeMap.get(ownerId);
|
|
17446
|
+
if (!scope) {
|
|
17447
|
+
for (const uniqueKey of uniqueKeys) {
|
|
17448
|
+
const uniqueKeyValue = item[uniqueKey];
|
|
17449
|
+
if (uniqueKeyValue !== undefined && scopeMap.has(uniqueKeyValue)) {
|
|
17450
|
+
scope = scopeMap.get(uniqueKeyValue);
|
|
17451
|
+
break;
|
|
17452
|
+
}
|
|
17453
|
+
}
|
|
17454
|
+
}
|
|
17455
|
+
if (!scope) {
|
|
17456
|
+
scope = createScope(ownerId);
|
|
17457
|
+
}
|
|
17458
|
+
// Register the scope under the id and every uniqueKey value so that
|
|
17459
|
+
// resolveOwnerId can address it whichever key a callback returns.
|
|
17460
|
+
scopeMap.set(ownerId, scope);
|
|
17356
17461
|
for (const uniqueKey of uniqueKeys) {
|
|
17357
17462
|
const uniqueKeyValue = item[uniqueKey];
|
|
17358
|
-
if (uniqueKeyValue !== undefined
|
|
17359
|
-
|
|
17360
|
-
break;
|
|
17463
|
+
if (uniqueKeyValue !== undefined) {
|
|
17464
|
+
scopeMap.set(uniqueKeyValue, scope);
|
|
17361
17465
|
}
|
|
17362
17466
|
}
|
|
17363
17467
|
}
|
|
17364
|
-
if (!scope) {
|
|
17365
|
-
scope = createScope(ownerId);
|
|
17366
|
-
}
|
|
17367
|
-
// Register the scope under the id and every uniqueKey value so that
|
|
17368
|
-
// resolveOwnerId can address it whichever key a callback returns.
|
|
17369
|
-
scopeMap.set(ownerId, scope);
|
|
17370
|
-
for (const uniqueKey of uniqueKeys) {
|
|
17371
|
-
const uniqueKeyValue = item[uniqueKey];
|
|
17372
|
-
if (uniqueKeyValue !== undefined) {
|
|
17373
|
-
scopeMap.set(uniqueKeyValue, scope);
|
|
17374
|
-
}
|
|
17375
|
-
}
|
|
17376
17468
|
|
|
17377
17469
|
const { childStore, idArraySignal } = scope;
|
|
17378
17470
|
const updateChildItemIdArray = createChildIdArrayUpdater(
|
|
@@ -17410,7 +17502,7 @@ const createResource = (
|
|
|
17410
17502
|
const createRestActionForScopedMany = (
|
|
17411
17503
|
verb,
|
|
17412
17504
|
callback,
|
|
17413
|
-
{ isMany, onActionComplete },
|
|
17505
|
+
{ isMany, onActionComplete, paramScope },
|
|
17414
17506
|
) => {
|
|
17415
17507
|
const childActionName = `${childName}.${verb}`;
|
|
17416
17508
|
return createAction(callback, {
|
|
@@ -17432,7 +17524,11 @@ const createResource = (
|
|
|
17432
17524
|
);
|
|
17433
17525
|
// Owner not in store yet: create the scope so actions can run before
|
|
17434
17526
|
// the parent item has been loaded (e.g. rows fetched before their table).
|
|
17435
|
-
|
|
17527
|
+
let scope = scopeMap.get(ownerId);
|
|
17528
|
+
if (!scope) {
|
|
17529
|
+
scope = createScope(ownerId);
|
|
17530
|
+
scopeMap.set(ownerId, scope);
|
|
17531
|
+
}
|
|
17436
17532
|
const { childStore, idArraySignal } = scope;
|
|
17437
17533
|
|
|
17438
17534
|
if (verb === "DELETE") {
|
|
@@ -17504,18 +17600,17 @@ const createResource = (
|
|
|
17504
17600
|
paramScope,
|
|
17505
17601
|
rerunOn: resolveRerunOn(scopedManyRerunOn, rerunOn),
|
|
17506
17602
|
dependencies: scopedManyDependencies ?? dependencies,
|
|
17603
|
+
ownerName: name,
|
|
17604
|
+
// Declared on a scoped child, the scopes are kept per item and never
|
|
17605
|
+
// registered by id (see addItemSetup above): there is no owner key to
|
|
17606
|
+
// find one by.
|
|
17607
|
+
getChildStore: ownerName
|
|
17608
|
+
? undefined
|
|
17609
|
+
: (ownerKey) => scopeMap.get(ownerKey)?.childStore,
|
|
17610
|
+
// scopedOne needs no such dependency: its mutation result carries the
|
|
17611
|
+
// updated object directly.
|
|
17612
|
+
ownerDependency: { owner: stateFacade, propertyName },
|
|
17507
17613
|
});
|
|
17508
|
-
// When a scoped child collection is mutated (POST etc.), the parent GET must
|
|
17509
|
-
// re-fetch: the parent embeds the child array and only the backend knows the
|
|
17510
|
-
// new ordering. (scopedOne does not need this: the mutation result contains
|
|
17511
|
-
// the updated object directly.)
|
|
17512
|
-
resourceLifecycleManager.addDependency(
|
|
17513
|
-
childResource,
|
|
17514
|
-
stateFacade,
|
|
17515
|
-
propertyName,
|
|
17516
|
-
);
|
|
17517
|
-
childResource.getChildStore = (ownerKey) =>
|
|
17518
|
-
scopeMap.get(ownerKey)?.childStore;
|
|
17519
17614
|
return childResource;
|
|
17520
17615
|
};
|
|
17521
17616
|
|
|
@@ -17736,7 +17831,7 @@ const getLastGetItemId = (action) => {
|
|
|
17736
17831
|
};
|
|
17737
17832
|
|
|
17738
17833
|
// Captures the "file:line:column" of the user code that invoked the public
|
|
17739
|
-
// function (resource(), .one(), .many(),
|
|
17834
|
+
// function (resource(), .one(), .many(), .scopedOne(), …), so invalid-result
|
|
17740
17835
|
// errors can point at where the callbacks were declared, not at this file.
|
|
17741
17836
|
// Must be called directly from the public function: the stack offset accounts
|
|
17742
17837
|
// for exactly two frames (getCallerInfo → getDeclarationSite → public fn → user code).
|
|
@@ -17757,6 +17852,36 @@ ${originalActionName} source location: ${declarationSite}`,
|
|
|
17757
17852
|
};
|
|
17758
17853
|
};
|
|
17759
17854
|
|
|
17855
|
+
// A scoped child's item exists only inside its owner: two owners can each hold
|
|
17856
|
+
// one with the same id. The callbacks of a relation chained on it whose result
|
|
17857
|
+
// names that item do so by its id alone — `.one()`/`.many()` as the parent
|
|
17858
|
+
// object or id, `.scopedOne()`/`.scopedMany()` as the `ownerId` of
|
|
17859
|
+
// `[ownerId, …]` — so applied they would write the owner's store (`store` of a
|
|
17860
|
+
// scoped child) or the scope of another owner's item. They are refused rather
|
|
17861
|
+
// than routed: routing needs the owner in the result, a contract none of them
|
|
17862
|
+
// has.
|
|
17863
|
+
const refuseCallbacksNamingScopedItem = (
|
|
17864
|
+
relationLabel,
|
|
17865
|
+
callbacks,
|
|
17866
|
+
{ name, ownerName, propertyName, declarationSite },
|
|
17867
|
+
) => {
|
|
17868
|
+
const declaredKeys = [];
|
|
17869
|
+
for (const key of Object.keys(callbacks)) {
|
|
17870
|
+
if (callbacks[key]) {
|
|
17871
|
+
declaredKeys.push(key);
|
|
17872
|
+
}
|
|
17873
|
+
}
|
|
17874
|
+
if (declaredKeys.length === 0) {
|
|
17875
|
+
return;
|
|
17876
|
+
}
|
|
17877
|
+
const declared = declaredKeys.join(", ");
|
|
17878
|
+
throw new Error(
|
|
17879
|
+
`${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.
|
|
17880
|
+
Return "${propertyName}" embedded in what the "${name}" callbacks return ([ownerId, { …, ${propertyName} }]): those say which "${ownerName}".
|
|
17881
|
+
${relationLabel} source location: ${declarationSite}`,
|
|
17882
|
+
);
|
|
17883
|
+
};
|
|
17884
|
+
|
|
17760
17885
|
// Shared by .many() and .scopedMany(): converts a raw relationship value (an
|
|
17761
17886
|
// array of child props/ids, or anything else meaning "empty") into an array of
|
|
17762
17887
|
// child ids, upserting each entry into the child store. The id array signal is
|
|
@@ -17898,22 +18023,28 @@ Received an object with keys: ${keys.join(", ")}.`,
|
|
|
17898
18023
|
* // which in turn triggers the route Signal->URL sync and updates the browser URL.
|
|
17899
18024
|
*/
|
|
17900
18025
|
const syncResourceToSignals = (resource, propertyToSignalMap) => {
|
|
17901
|
-
if (resource.
|
|
18026
|
+
if (resource.ownerName) {
|
|
18027
|
+
// A scoped child's `store` is its owner's: watching it would sync the
|
|
18028
|
+
// owner items' properties, not the child's.
|
|
17902
18029
|
throw new Error(
|
|
17903
|
-
`syncResourceToSignals: "${resource.name}"
|
|
18030
|
+
`syncResourceToSignals: "${resource.name}" lives inside each "${resource.ownerName}", so it has no store of its own to watch.${resource.getChildStore ? " Use syncOwnedResourceToSignals instead." : ""}`,
|
|
17904
18031
|
);
|
|
17905
18032
|
}
|
|
17906
18033
|
syncStoreToSignals(resource.store, propertyToSignalMap);
|
|
17907
18034
|
};
|
|
17908
18035
|
|
|
17909
18036
|
/**
|
|
17910
|
-
* The same, for a `
|
|
17911
|
-
* per owner, so the store to watch is the one of the owner
|
|
17912
|
-
* (by id or by any unique key), and it is switched when the
|
|
17913
|
-
* Nothing is synced while `ownerSignal` holds `null`/`undefined`
|
|
17914
|
-
* owner nothing has been loaded for yet.
|
|
18037
|
+
* The same, for a `scopedMany` resource (or a `withParams()` of one): its items
|
|
18038
|
+
* live in one store per owner, so the store to watch is the one of the owner
|
|
18039
|
+
* `ownerSignal` names (by id or by any unique key), and it is switched when the
|
|
18040
|
+
* signal changes. Nothing is synced while `ownerSignal` holds `null`/`undefined`
|
|
18041
|
+
* or names an owner nothing has been loaded for yet.
|
|
17915
18042
|
*
|
|
17916
|
-
*
|
|
18043
|
+
* A `scopedOne` child has no store (each owner holds its one item), and a
|
|
18044
|
+
* `scopedMany` declared on a scoped child is kept per item with no owner key to
|
|
18045
|
+
* find it by: both are refused.
|
|
18046
|
+
*
|
|
18047
|
+
* @param {Object} resource - a resource made by `.scopedMany()`
|
|
17917
18048
|
* @param {import("@preact/signals").Signal} ownerSignal - the owner whose children are watched
|
|
17918
18049
|
* @param {Object} propertyToSignalMap - `{ [propertyName]: signal }`, as for `syncResourceToSignals`
|
|
17919
18050
|
*/
|
|
@@ -17923,8 +18054,13 @@ const syncOwnedResourceToSignals = (
|
|
|
17923
18054
|
propertyToSignalMap,
|
|
17924
18055
|
) => {
|
|
17925
18056
|
if (!resource.getChildStore) {
|
|
18057
|
+
if (!resource.ownerName) {
|
|
18058
|
+
throw new Error(
|
|
18059
|
+
`syncOwnedResourceToSignals: "${resource.name}" is not a scoped resource. Use syncResourceToSignals instead.`,
|
|
18060
|
+
);
|
|
18061
|
+
}
|
|
17926
18062
|
throw new Error(
|
|
17927
|
-
`syncOwnedResourceToSignals: "${resource.name}" is
|
|
18063
|
+
`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.`,
|
|
17928
18064
|
);
|
|
17929
18065
|
}
|
|
17930
18066
|
effect(() => {
|
|
@@ -30219,7 +30355,7 @@ const TRANSITION_FURNITURE_CSS = /* css */ `.navi_fixed_bar[data-navi-transition
|
|
|
30219
30355
|
clip-path: var(--wall-holes-arriving, var(--wall-holes));
|
|
30220
30356
|
}
|
|
30221
30357
|
|
|
30222
|
-
:root[data-navi-route-transition] {
|
|
30358
|
+
:root[data-navi-route-transition-target="area"] {
|
|
30223
30359
|
& .navi_dialog::backdrop, & .navi_popover_backdrop {
|
|
30224
30360
|
backdrop-filter: none;
|
|
30225
30361
|
background: none;
|
|
@@ -30364,10 +30500,7 @@ const nameFurnitureAround = (areaElement) => {
|
|
|
30364
30500
|
if (!name) {
|
|
30365
30501
|
// A name the application wrote itself answers for that element, and it
|
|
30366
30502
|
// is saying something navi is not: it wants that bar moved on the pages'
|
|
30367
|
-
// clock, by its own CSS.
|
|
30368
|
-
// bar wearing one of ITS names is one this movement inherited from the
|
|
30369
|
-
// one it interrupted, and taking it for the application's would leave it
|
|
30370
|
-
// named for the rest of the document's life.
|
|
30503
|
+
// clock, by its own CSS.
|
|
30371
30504
|
if (getComputedStyle(element).viewTransitionName !== "none") {
|
|
30372
30505
|
continue;
|
|
30373
30506
|
}
|
|
@@ -32189,8 +32322,17 @@ const beginTransition = ({
|
|
|
32189
32322
|
url: absoluteUrl(url),
|
|
32190
32323
|
decision: navigationDecision,
|
|
32191
32324
|
walkHome: null,
|
|
32192
|
-
releaseReverting: null
|
|
32325
|
+
releaseReverting: null,
|
|
32326
|
+
restoreDuration: null
|
|
32193
32327
|
};
|
|
32328
|
+
// The document wears one transition at a time, and the one this interrupts
|
|
32329
|
+
// ends only after this one has begun — too late to take off what it wears
|
|
32330
|
+
// (see end). Taking over therefore starts by taking it off, so that what is
|
|
32331
|
+
// put on below describes this transition alone, including what it leaves
|
|
32332
|
+
// unset: a type, an area, a duration.
|
|
32333
|
+
if (currentTransition) {
|
|
32334
|
+
releaseTransitionRoot(currentTransition);
|
|
32335
|
+
}
|
|
32194
32336
|
currentTransition = transition;
|
|
32195
32337
|
// Said before the picture is taken: whoever names something for a movement
|
|
32196
32338
|
// between two pages decides on it now (see transition_destination.js).
|
|
@@ -32234,11 +32376,10 @@ const beginTransition = ({
|
|
|
32234
32376
|
// A duration of this relation's own, worn for the length of the transition —
|
|
32235
32377
|
// and whatever the application had written inline put back afterwards, not
|
|
32236
32378
|
// erased.
|
|
32237
|
-
let restoreDuration = null;
|
|
32238
32379
|
if (duration !== undefined) {
|
|
32239
32380
|
const durationBefore = documentElement.style.getPropertyValue(TRANSITION_DURATION_PROPERTY);
|
|
32240
32381
|
documentElement.style.setProperty(TRANSITION_DURATION_PROPERTY, typeof duration === "number" ? `${duration}ms` : duration);
|
|
32241
|
-
restoreDuration = () => {
|
|
32382
|
+
transition.restoreDuration = () => {
|
|
32242
32383
|
if (durationBefore) {
|
|
32243
32384
|
documentElement.style.setProperty(TRANSITION_DURATION_PROPERTY, durationBefore);
|
|
32244
32385
|
} else {
|
|
@@ -32299,8 +32440,11 @@ const beginTransition = ({
|
|
|
32299
32440
|
renderWait.stop();
|
|
32300
32441
|
}
|
|
32301
32442
|
// The page arriving is in the DOM and the transition has not started
|
|
32302
|
-
// playing: the one moment both states of the area can be known.
|
|
32303
|
-
|
|
32443
|
+
// playing: the one moment both states of the area can be known. The
|
|
32444
|
+
// browser runs this callback even for a transition skipped before its
|
|
32445
|
+
// first picture — the next one starting skips it — and a transition
|
|
32446
|
+
// replaced holds nothing: what it wore was taken off at the takeover.
|
|
32447
|
+
if (areaElement && currentTransition === transition) {
|
|
32304
32448
|
holdTransitionFurniture(transition, areaElement);
|
|
32305
32449
|
holdTransitionWindow(transition, areaElement, areaStateBefore);
|
|
32306
32450
|
}
|
|
@@ -32309,8 +32453,9 @@ const beginTransition = ({
|
|
|
32309
32453
|
// Whatever ends it — played out, skipped by another transition starting,
|
|
32310
32454
|
// failed before its callback ever ran — the hold is given back and the
|
|
32311
32455
|
// document is handed back to the application. Both are idempotent, and
|
|
32312
|
-
// the
|
|
32313
|
-
// ending late
|
|
32456
|
+
// the document is handed back only by the LAST transition begun: an
|
|
32457
|
+
// earlier one ending late was taken off by the one that replaced it, and
|
|
32458
|
+
// must not strip what that one is wearing.
|
|
32314
32459
|
renderWait.stop();
|
|
32315
32460
|
releaseRendering();
|
|
32316
32461
|
// The hold a way back took, when it is still standing: the pictures were
|
|
@@ -32322,16 +32467,7 @@ const beginTransition = ({
|
|
|
32322
32467
|
}
|
|
32323
32468
|
if (currentTransition === transition) {
|
|
32324
32469
|
currentTransition = null;
|
|
32325
|
-
|
|
32326
|
-
documentElement.removeAttribute(TRANSITION_TYPE_ATTRIBUTE);
|
|
32327
|
-
documentElement.removeAttribute(TRANSITION_TARGET_ATTRIBUTE);
|
|
32328
|
-
releaseTransitionWindow(transition);
|
|
32329
|
-
releaseTransitionDestination(transition);
|
|
32330
|
-
releaseTransitionFurniture(transition);
|
|
32331
|
-
releaseTransitionPress(transition);
|
|
32332
|
-
if (restoreDuration) {
|
|
32333
|
-
restoreDuration();
|
|
32334
|
-
}
|
|
32470
|
+
releaseTransitionRoot(transition);
|
|
32335
32471
|
}
|
|
32336
32472
|
};
|
|
32337
32473
|
transition.viewTransition = viewTransition;
|
|
@@ -32339,6 +32475,22 @@ const beginTransition = ({
|
|
|
32339
32475
|
viewTransition.finished.then(end, end);
|
|
32340
32476
|
};
|
|
32341
32477
|
|
|
32478
|
+
// Everything a transition wears on the document for its length, taken off by
|
|
32479
|
+
// its own end or by the transition taking over from it.
|
|
32480
|
+
const releaseTransitionRoot = transition => {
|
|
32481
|
+
const documentElement = document.documentElement;
|
|
32482
|
+
documentElement.removeAttribute(TRANSITION_ATTRIBUTE);
|
|
32483
|
+
documentElement.removeAttribute(TRANSITION_TYPE_ATTRIBUTE);
|
|
32484
|
+
documentElement.removeAttribute(TRANSITION_TARGET_ATTRIBUTE);
|
|
32485
|
+
releaseTransitionWindow(transition);
|
|
32486
|
+
releaseTransitionDestination(transition);
|
|
32487
|
+
releaseTransitionFurniture(transition);
|
|
32488
|
+
releaseTransitionPress(transition);
|
|
32489
|
+
if (transition.restoreDuration) {
|
|
32490
|
+
transition.restoreDuration();
|
|
32491
|
+
}
|
|
32492
|
+
};
|
|
32493
|
+
|
|
32342
32494
|
/**
|
|
32343
32495
|
* The movement playing, turned round rather than replaced: this navigation is
|
|
32344
32496
|
* exactly the way back of the one on screen — the door pressed again while the
|
|
@@ -74139,11 +74291,15 @@ const PickerNaviTime = props => {
|
|
|
74139
74291
|
max = "23:30",
|
|
74140
74292
|
step
|
|
74141
74293
|
} = props;
|
|
74142
|
-
|
|
74294
|
+
// resolveInputProps has already run (PickerFirstResolver) and left navi_time
|
|
74295
|
+
// as written: what is handed on is a time input, whose step is in seconds.
|
|
74296
|
+
const hostStep = timeStringToSeconds(step);
|
|
74297
|
+
const stepSeconds = hostStep ?? 1800;
|
|
74143
74298
|
const slots = useMemo(() => generateTimeSlots(min, max, stepSeconds), [min, max, stepSeconds]);
|
|
74144
74299
|
return jsx(Next, {
|
|
74145
74300
|
...props,
|
|
74146
74301
|
type: "time",
|
|
74302
|
+
step: hostStep,
|
|
74147
74303
|
children: jsx(List, {
|
|
74148
74304
|
selectable: true,
|
|
74149
74305
|
command: "--navi-send",
|
|
@@ -77189,6 +77345,10 @@ const warnOnUnknownPickerType = props => {
|
|
|
77189
77345
|
pickerTypeWarnedSet.add(type);
|
|
77190
77346
|
console.warn(`[navi] <Picker type="${type}"> — "${type}" is not a picker type. ` + `The picker holds a single text value instead, so an object given to it is written as "[object Object]". ` + `Types are: date, month, week, time, datetime, duration, color, file, text, array, object, confirm — ` + `"object" being the one for a popup holding several named controls.`);
|
|
77191
77347
|
};
|
|
77348
|
+
|
|
77349
|
+
// PickerFirstResolver comes before the presets so a preset reads the
|
|
77350
|
+
// min/max/step a bound signal fills in (resolveInputProps); a preset type
|
|
77351
|
+
// therefore has no row in NAVI_TYPE_DEFAULTS (resolve_input_props.js).
|
|
77192
77352
|
const PickerResolved = /*#__PURE__*/createComponentResolver([PickerFirstResolver, PickerPresetResolver, PickerConfirmResolver, PickerCustomResolver, PickerTypeResolver, PickerButton]);
|
|
77193
77353
|
const PickerUI = /*#__PURE__*/Object.assign(PickerDefaultUI, {
|
|
77194
77354
|
Date: PickerDateUI,
|