@jsenv/navi 0.30.24 → 0.30.26
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 +194 -24
- package/dist/dev/jsenv_navi.js.map +5 -5
- package/dist/jsenv_navi.js +194 -24
- package/dist/jsenv_navi.js.map +5 -5
- package/docs/AI_INSTRUCTIONS.md +5 -1
- package/docs/actions.md +4 -0
- package/docs/dynamic_import.md +18 -0
- package/docs/resource.md +119 -10
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -3535,6 +3535,7 @@ const updateActions = ({
|
|
|
3535
3535
|
|
|
3536
3536
|
const actionToRunPrivateProperties =
|
|
3537
3537
|
getActionPrivateProperties(actionToPrerunOrRun);
|
|
3538
|
+
actionToRunPrivateProperties.answeredAsync = false;
|
|
3538
3539
|
let performRunResult;
|
|
3539
3540
|
try {
|
|
3540
3541
|
performRunResult = actionToRunPrivateProperties.performRun({
|
|
@@ -3559,6 +3560,7 @@ const updateActions = ({
|
|
|
3559
3560
|
activationWeakSet.add(actionToPrerunOrRun);
|
|
3560
3561
|
|
|
3561
3562
|
if (performRunResult && typeof performRunResult.then === "function") {
|
|
3563
|
+
actionToRunPrivateProperties.answeredAsync = true;
|
|
3562
3564
|
actionPromiseMap.set(actionToPrerunOrRun, performRunResult);
|
|
3563
3565
|
allThenableArray.push(performRunResult);
|
|
3564
3566
|
hasAsync = true;
|
|
@@ -3588,7 +3590,9 @@ const updateActions = ({
|
|
|
3588
3590
|
|
|
3589
3591
|
// Execute promotions (prerun -> run requested)
|
|
3590
3592
|
for (const actionToPromote of willPromoteSet) {
|
|
3591
|
-
|
|
3593
|
+
writeActionState(() => {
|
|
3594
|
+
actionToPromote.isPrerunSignal.value = false;
|
|
3595
|
+
});
|
|
3592
3596
|
}
|
|
3593
3597
|
}
|
|
3594
3598
|
|
|
@@ -3597,13 +3601,21 @@ const updateActions = ({
|
|
|
3597
3601
|
// that IS carrying it. `await action.run()` then means the same thing to
|
|
3598
3602
|
// every caller: the second one waits for the first one's request and reads
|
|
3599
3603
|
// the same data, instead of being handed `undefined` before the network has
|
|
3600
|
-
// said anything.
|
|
3604
|
+
// said anything. It answers in the tense of that run: the answer of a run
|
|
3605
|
+
// that answered with a promise comes back as one, so `run().catch()` holds
|
|
3606
|
+
// whatever state the instance is in. See the run() JSDoc.
|
|
3601
3607
|
{
|
|
3602
3608
|
for (const actionToJoin of willJoinSet) {
|
|
3603
3609
|
const runningPromise = actionPromiseMap.get(actionToJoin);
|
|
3604
3610
|
if (runningPromise) {
|
|
3605
3611
|
hasAsync = true;
|
|
3606
3612
|
resultArray.push({ type: "async", promise: runningPromise });
|
|
3613
|
+
} else if (getActionPrivateProperties(actionToJoin).answeredAsync) {
|
|
3614
|
+
hasAsync = true;
|
|
3615
|
+
resultArray.push({
|
|
3616
|
+
type: "async",
|
|
3617
|
+
promise: Promise.resolve(actionToJoin.dataSignal.peek()),
|
|
3618
|
+
});
|
|
3607
3619
|
} else {
|
|
3608
3620
|
resultArray.push({
|
|
3609
3621
|
type: "sync",
|
|
@@ -3809,6 +3821,10 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
3809
3821
|
* the one that asked first or the one that arrived while the request was
|
|
3810
3822
|
* out. Use `rerun()` to force a fresh run ("refresh", "check now", any
|
|
3811
3823
|
* explicit user intent to go back to the network).
|
|
3824
|
+
*
|
|
3825
|
+
* An asynchronous callback answers with a promise, joined or not:
|
|
3826
|
+
* `ACTION.run().catch(…)` holds in every state. A synchronous one answers
|
|
3827
|
+
* synchronously, and throws its failure.
|
|
3812
3828
|
*/
|
|
3813
3829
|
const run = (options) => {
|
|
3814
3830
|
action.debug(`${action}.run(${stringifyForDisplay(options)})`);
|
|
@@ -4267,7 +4283,9 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
4267
4283
|
// another run's, or idle.
|
|
4268
4284
|
return;
|
|
4269
4285
|
}
|
|
4270
|
-
|
|
4286
|
+
writeActionState(() => {
|
|
4287
|
+
runningStateSignal.value = ABORTED;
|
|
4288
|
+
});
|
|
4271
4289
|
internalAbortController.abort(abortReason);
|
|
4272
4290
|
actionAbortMap.delete(action);
|
|
4273
4291
|
if (isPrerun && (globalAbortSignal.aborted || abortSignal.aborted)) {
|
|
@@ -4291,7 +4309,7 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
4291
4309
|
|
|
4292
4310
|
actionAbortMap.set(action, abort);
|
|
4293
4311
|
|
|
4294
|
-
|
|
4312
|
+
writeActionState(() => {
|
|
4295
4313
|
if (valueSignal.peek() === undefined) {
|
|
4296
4314
|
// An action saying nothing of its own may still have a value the
|
|
4297
4315
|
// page kept from the previous document (see kept_values.js).
|
|
@@ -4370,7 +4388,7 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
4370
4388
|
* before the UI attempts to render the now-missing resource.
|
|
4371
4389
|
*/
|
|
4372
4390
|
|
|
4373
|
-
|
|
4391
|
+
writeActionState(() => {
|
|
4374
4392
|
const value = resultToValue
|
|
4375
4393
|
? resultToValue(runResult, action)
|
|
4376
4394
|
: runResult;
|
|
@@ -4399,7 +4417,9 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
4399
4417
|
error === internalAbortSignal.reason) ||
|
|
4400
4418
|
error.name === "AbortError";
|
|
4401
4419
|
if (isAbort) {
|
|
4402
|
-
|
|
4420
|
+
writeActionState(() => {
|
|
4421
|
+
runningStateSignal.value = ABORTED;
|
|
4422
|
+
});
|
|
4403
4423
|
if (isPrerun && abortSignal.aborted) {
|
|
4404
4424
|
prerunProtectionRegistry.unprotect(action);
|
|
4405
4425
|
}
|
|
@@ -4410,7 +4430,7 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
4410
4430
|
if (isPrerun) {
|
|
4411
4431
|
markErrorAsPrerunFailure(error);
|
|
4412
4432
|
}
|
|
4413
|
-
|
|
4433
|
+
writeActionState(() => {
|
|
4414
4434
|
errorSignal.value = error;
|
|
4415
4435
|
runningStateSignal.value = FAILED;
|
|
4416
4436
|
onError?.(error, { event, action, args });
|
|
@@ -4502,7 +4522,7 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
4502
4522
|
}
|
|
4503
4523
|
|
|
4504
4524
|
actionPromiseMap.delete(action);
|
|
4505
|
-
|
|
4525
|
+
writeActionState(() => {
|
|
4506
4526
|
if (!willRunOrPrerun) {
|
|
4507
4527
|
errorSignal.value = undefined;
|
|
4508
4528
|
valueSignal.value = valueInitial;
|
|
@@ -4520,6 +4540,9 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
4520
4540
|
provisionalValue,
|
|
4521
4541
|
inheritData,
|
|
4522
4542
|
keepsAnswers: keptAnswerRegistry !== null,
|
|
4543
|
+
// Whether the last run answered with a promise, written by
|
|
4544
|
+
// updateActions: the tense a run joining this instance answers in.
|
|
4545
|
+
answeredAsync: false,
|
|
4523
4546
|
|
|
4524
4547
|
performRun,
|
|
4525
4548
|
performReset,
|
|
@@ -5003,6 +5026,35 @@ const isPlainObject$2 = (obj) => {
|
|
|
5003
5026
|
);
|
|
5004
5027
|
};
|
|
5005
5028
|
|
|
5029
|
+
// Every state write of a run (start, end, failure, abort, reset), batched. At
|
|
5030
|
+
// the end of the batch preact runs the effects those writes woke up, anywhere
|
|
5031
|
+
// in the app, and rethrows the first one that threw: from there it would fail
|
|
5032
|
+
// this run, or leave it RUNNING, or stop the update half way. That throw is
|
|
5033
|
+
// the effect's, so it is reported on its own, as action_error_report.js
|
|
5034
|
+
// reports an error (thrown from a timer), and the action carries on. What
|
|
5035
|
+
// `write` throws (resultToValue, onComplete…) is the run's, and goes on.
|
|
5036
|
+
const writeActionState = (write) => {
|
|
5037
|
+
let writeThrew = false;
|
|
5038
|
+
let writeError;
|
|
5039
|
+
try {
|
|
5040
|
+
batch(() => {
|
|
5041
|
+
try {
|
|
5042
|
+
write();
|
|
5043
|
+
} catch (e) {
|
|
5044
|
+
writeThrew = true;
|
|
5045
|
+
writeError = e;
|
|
5046
|
+
}
|
|
5047
|
+
});
|
|
5048
|
+
} catch (effectError) {
|
|
5049
|
+
setTimeout(() => {
|
|
5050
|
+
throw effectError;
|
|
5051
|
+
});
|
|
5052
|
+
}
|
|
5053
|
+
if (writeThrew) {
|
|
5054
|
+
throw writeError;
|
|
5055
|
+
}
|
|
5056
|
+
};
|
|
5057
|
+
|
|
5006
5058
|
/**
|
|
5007
5059
|
* Reactively runs an action whenever the params derived from signals change.
|
|
5008
5060
|
*
|
|
@@ -27535,7 +27587,11 @@ ${[idKey, ...uniqueKeys].join(", ")}`,
|
|
|
27535
27587
|
* (docs/resource.md).
|
|
27536
27588
|
*/
|
|
27537
27589
|
const upsertAnswer = (...args) => upsertWith(args, true);
|
|
27538
|
-
|
|
27590
|
+
// Batched like upsertWith: a removal observer may drop rows of another
|
|
27591
|
+
// store (a `.one()` declared with `onDelete: "cascade"`), and an effect
|
|
27592
|
+
// reading both runs once, never between the child leaving and its parents.
|
|
27593
|
+
const drop = (...args) => batch(() => dropNow(...args));
|
|
27594
|
+
const dropNow = (...args) => {
|
|
27539
27595
|
const removedItemArray = [];
|
|
27540
27596
|
const triggerRemovedMutations = () => {
|
|
27541
27597
|
if (removedItemArray.length === 0) {
|
|
@@ -27570,6 +27626,7 @@ ${[idKey, ...uniqueKeys].join(", ")}`,
|
|
|
27570
27626
|
hasFound = true;
|
|
27571
27627
|
idToRemoveSet.delete(existingItemId);
|
|
27572
27628
|
idRemovedArray.push(existingItemId);
|
|
27629
|
+
removedItemArray.push(existingItem);
|
|
27573
27630
|
} else {
|
|
27574
27631
|
arrayWithoutDroppedItems.push(existingItem);
|
|
27575
27632
|
}
|
|
@@ -29629,6 +29686,15 @@ const createResource = (
|
|
|
29629
29686
|
* The property is reactive: updating the child item anywhere propagates immediately.
|
|
29630
29687
|
* The child resource exists independently — it is not owned by, nor deleted with, the parent.
|
|
29631
29688
|
*
|
|
29689
|
+
* The other way round is declared with `onDelete`. Left out, a parent outlives its
|
|
29690
|
+
* child: once the child leaves its store, the property reads an empty object (truthy,
|
|
29691
|
+
* `valueOf()` is `null`, no fields) until the parent is read again — a game whose
|
|
29692
|
+
* organizer is deleted is still a game. `onDelete: "cascade"` is for a parent that is
|
|
29693
|
+
* nothing without its child (an address-book row, the person in it): when the child
|
|
29694
|
+
* leaves its store, the parents pointing at it leave theirs in the same write, and
|
|
29695
|
+
* with them every list holding them. Which one a relation needs is decided in
|
|
29696
|
+
* docs/resource.md, "When the child is deleted".
|
|
29697
|
+
*
|
|
29632
29698
|
* Use it when the child is a first-class entity with its own store, shared across
|
|
29633
29699
|
* parents (a user referenced by many games). When the child only exists inside its
|
|
29634
29700
|
* owner, use `.scopedOne()` instead.
|
|
@@ -29648,7 +29714,12 @@ const createResource = (
|
|
|
29648
29714
|
*
|
|
29649
29715
|
* @param {string} propertyName - property holding the child on each parent item
|
|
29650
29716
|
* @param {Object} childResource - the independent resource created by `resource()`
|
|
29651
|
-
* @param {Object} [restCallbacks] - `{ rerunOn, dependencies, GET, PUT, DELETE }`
|
|
29717
|
+
* @param {Object} [restCallbacks] - `{ rerunOn, dependencies, onDelete, GET, PUT, DELETE }`
|
|
29718
|
+
* @param {"cascade"} [restCallbacks.onDelete] - `"cascade"`: a parent leaves its store
|
|
29719
|
+
* when its child leaves the child's (the child's `DELETE`, `DELETE_MANY`, a write
|
|
29720
|
+
* named with verb `DELETE`, a cascade of its own). Not the relation's own `DELETE`,
|
|
29721
|
+
* which unlinks: the property becomes `null` and the parent stays. Throws on a
|
|
29722
|
+
* relation declared on a scoped child.
|
|
29652
29723
|
* @see docs/resource.md
|
|
29653
29724
|
*/
|
|
29654
29725
|
stateFacade.one = (
|
|
@@ -29657,6 +29728,7 @@ const createResource = (
|
|
|
29657
29728
|
{
|
|
29658
29729
|
rerunOn: oneRerunOn,
|
|
29659
29730
|
dependencies: oneDependencies,
|
|
29731
|
+
onDelete,
|
|
29660
29732
|
|
|
29661
29733
|
GET,
|
|
29662
29734
|
PUT,
|
|
@@ -29664,16 +29736,54 @@ const createResource = (
|
|
|
29664
29736
|
} = {},
|
|
29665
29737
|
) => {
|
|
29666
29738
|
const declarationSite = getDeclarationSite();
|
|
29739
|
+
const relationLabel = `${name}.one("${propertyName}")`;
|
|
29667
29740
|
if (ownerName) {
|
|
29668
29741
|
refuseCallbacksNamingScopedItem(
|
|
29669
|
-
|
|
29742
|
+
relationLabel,
|
|
29670
29743
|
{ GET, PUT, DELETE },
|
|
29671
29744
|
{ name, ownerName, propertyName, declarationSite },
|
|
29672
29745
|
);
|
|
29673
29746
|
}
|
|
29747
|
+
if (onDelete !== undefined && onDelete !== "cascade") {
|
|
29748
|
+
throw new TypeError(
|
|
29749
|
+
`${relationLabel}: onDelete must be "cascade" or left out, got ${onDelete}.
|
|
29750
|
+
${relationLabel} source location: ${declarationSite}`,
|
|
29751
|
+
);
|
|
29752
|
+
}
|
|
29753
|
+
const cascade = onDelete === "cascade";
|
|
29754
|
+
if (cascade && ownerName) {
|
|
29755
|
+
// A scoped item lives in its owner's collection, not in a store this
|
|
29756
|
+
// relation could drop it from.
|
|
29757
|
+
throw new Error(
|
|
29758
|
+
`${relationLabel} cannot declare onDelete: "cascade": a "${name}" item exists only inside its "${ownerName}", and leaves through the "${ownerName}" callbacks.
|
|
29759
|
+
Read the "${ownerName}" again after the "${childResource.name}" is deleted (dependencies).
|
|
29760
|
+
${relationLabel} source location: ${declarationSite}`,
|
|
29761
|
+
);
|
|
29762
|
+
}
|
|
29674
29763
|
const childName = `${name}.${propertyName}`;
|
|
29675
29764
|
const childIdKey = childResource.idKey;
|
|
29676
29765
|
const childStore = childResource.store;
|
|
29766
|
+
// The id each parent points at, carried by the parent row (copies of a row
|
|
29767
|
+
// keep it, a spread does not): a removal is matched against the parents
|
|
29768
|
+
// the store holds at that moment, under the ids they have then.
|
|
29769
|
+
const childIdSymbol = cascade ? Symbol(`${childName} id`) : undefined;
|
|
29770
|
+
if (cascade) {
|
|
29771
|
+
childStore.observeRemovals((removedChildArray) => {
|
|
29772
|
+
const removedChildIdSet = new Set();
|
|
29773
|
+
for (const removedChild of removedChildArray) {
|
|
29774
|
+
removedChildIdSet.add(removedChild[childIdKey]);
|
|
29775
|
+
}
|
|
29776
|
+
const orphanIdArray = [];
|
|
29777
|
+
for (const parentItem of store.arraySignal.peek()) {
|
|
29778
|
+
if (removedChildIdSet.has(parentItem[childIdSymbol])) {
|
|
29779
|
+
orphanIdArray.push(parentItem[idKey]);
|
|
29780
|
+
}
|
|
29781
|
+
}
|
|
29782
|
+
if (orphanIdArray.length > 0) {
|
|
29783
|
+
store.drop(orphanIdArray);
|
|
29784
|
+
}
|
|
29785
|
+
});
|
|
29786
|
+
}
|
|
29677
29787
|
addItemSetup((item, answer) => {
|
|
29678
29788
|
const childItemIdSignal = signal();
|
|
29679
29789
|
const updateChildItemId = (value, answer) => {
|
|
@@ -29728,6 +29838,11 @@ const createResource = (
|
|
|
29728
29838
|
get: () => childItemFacadeSignal.value,
|
|
29729
29839
|
set: updateChildItemId,
|
|
29730
29840
|
});
|
|
29841
|
+
if (cascade) {
|
|
29842
|
+
Object.defineProperty(item, childIdSymbol, {
|
|
29843
|
+
get: () => childItemIdSignal.peek(),
|
|
29844
|
+
});
|
|
29845
|
+
}
|
|
29731
29846
|
debug$2(
|
|
29732
29847
|
`setup ${item}.${propertyName} is one "${childResource.name}" (current value: ${childItemSignal.peek()})`,
|
|
29733
29848
|
);
|
|
@@ -38153,10 +38268,12 @@ const barsOutside = (areaElement) => {
|
|
|
38153
38268
|
};
|
|
38154
38269
|
|
|
38155
38270
|
// Room around what a bar's picture holds for what paints past every box — a
|
|
38156
|
-
// shadow, the
|
|
38271
|
+
// shadow, the bar's own filter — which no API measures: a filter cuts whatever
|
|
38157
38272
|
// falls outside its region.
|
|
38158
38273
|
const WALL_FILTER_INK_ROOM = 100;
|
|
38159
38274
|
const SVG_NS = "http://www.w3.org/2000/svg";
|
|
38275
|
+
// A blur() in a computed filter, its radius in px.
|
|
38276
|
+
const BLUR_FUNCTION_REGEXP = /blur\(([^)]*)\)/g;
|
|
38160
38277
|
|
|
38161
38278
|
// The bar's picture dimmed, not a rectangle over it: each wall's colour is
|
|
38162
38279
|
// flooded atop the bar's own pixels (Porter-Duff source-atop, which no blend
|
|
@@ -38184,11 +38301,17 @@ const dimBarPicture = (bar, sources) => {
|
|
|
38184
38301
|
const ownFilter = getComputedStyle(bar).filter;
|
|
38185
38302
|
const chain = ownFilter === "none" ? [] : [ownFilter];
|
|
38186
38303
|
for (const source of sources) {
|
|
38187
|
-
const backdropFilter =
|
|
38188
|
-
|
|
38189
|
-
|
|
38190
|
-
|
|
38191
|
-
chain.push(
|
|
38304
|
+
const backdropFilter = readBackdropFilter(source);
|
|
38305
|
+
if (backdropFilter !== "none") {
|
|
38306
|
+
// Blurred alone, the bar would fade to transparent over its edges and
|
|
38307
|
+
// show the live document through them: its blur stays inside its shape.
|
|
38308
|
+
chain.push(
|
|
38309
|
+
backdropFilter.replace(
|
|
38310
|
+
BLUR_FUNCTION_REGEXP,
|
|
38311
|
+
(_, radius) =>
|
|
38312
|
+
`url(#${createShapeBlurFilter(parseFloat(radius), region).id})`,
|
|
38313
|
+
),
|
|
38314
|
+
);
|
|
38192
38315
|
}
|
|
38193
38316
|
chain.push(`url(#${createWallFilter(source, region).id})`);
|
|
38194
38317
|
}
|
|
@@ -38197,7 +38320,59 @@ const dimBarPicture = (bar, sources) => {
|
|
|
38197
38320
|
dimmedBars.push(bar);
|
|
38198
38321
|
};
|
|
38199
38322
|
|
|
38323
|
+
// The wall's backdrop-filter as computed, so a blur radius written in em or
|
|
38324
|
+
// rem reads in px, as the wall resolves it where it paints.
|
|
38325
|
+
const readBackdropFilter = (source) => {
|
|
38326
|
+
const sourceStyle = getComputedStyle(source);
|
|
38327
|
+
const backdropFilter = sourceStyle
|
|
38328
|
+
.getPropertyValue("--backdrop-filter")
|
|
38329
|
+
.trim();
|
|
38330
|
+
if (!backdropFilter || backdropFilter === "none") {
|
|
38331
|
+
return "none";
|
|
38332
|
+
}
|
|
38333
|
+
const probe = document.createElement("div");
|
|
38334
|
+
probe.style.fontSize = sourceStyle.fontSize;
|
|
38335
|
+
probe.style.filter = backdropFilter;
|
|
38336
|
+
document.body.append(probe);
|
|
38337
|
+
const filter = getComputedStyle(probe).filter;
|
|
38338
|
+
probe.remove();
|
|
38339
|
+
return filter;
|
|
38340
|
+
};
|
|
38341
|
+
|
|
38200
38342
|
const createWallFilter = (source, region) => {
|
|
38343
|
+
const filter = createFilter(region);
|
|
38344
|
+
const flood = document.createElementNS(SVG_NS, "feFlood");
|
|
38345
|
+
flood.setAttribute("flood-color", readBackdropColor(source));
|
|
38346
|
+
const composite = document.createElementNS(SVG_NS, "feComposite");
|
|
38347
|
+
composite.setAttribute("in2", "SourceGraphic");
|
|
38348
|
+
composite.setAttribute("operator", "atop");
|
|
38349
|
+
filter.append(flood, composite);
|
|
38350
|
+
return filter;
|
|
38351
|
+
};
|
|
38352
|
+
|
|
38353
|
+
// Colour kept, alpha set to 1.
|
|
38354
|
+
const OPAQUE_MATRIX = "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 1";
|
|
38355
|
+
|
|
38356
|
+
// The wall's blur kept inside the bar. At rest the window is blurred as one
|
|
38357
|
+
// image; a CSS blur() on the bar sees only the bar, so its edges go half
|
|
38358
|
+
// transparent and its colour spreads past its box. Made opaque, each pixel is
|
|
38359
|
+
// the mean of the bar's own pixels around it (feColorMatrix works on
|
|
38360
|
+
// unpremultiplied colours), then cut back to the bar's own alpha.
|
|
38361
|
+
const createShapeBlurFilter = (stdDeviation, region) => {
|
|
38362
|
+
const filter = createFilter(region);
|
|
38363
|
+
const blur = document.createElementNS(SVG_NS, "feGaussianBlur");
|
|
38364
|
+
blur.setAttribute("stdDeviation", stdDeviation);
|
|
38365
|
+
const opaque = document.createElementNS(SVG_NS, "feColorMatrix");
|
|
38366
|
+
opaque.setAttribute("type", "matrix");
|
|
38367
|
+
opaque.setAttribute("values", OPAQUE_MATRIX);
|
|
38368
|
+
const clip = document.createElementNS(SVG_NS, "feComposite");
|
|
38369
|
+
clip.setAttribute("in2", "SourceAlpha");
|
|
38370
|
+
clip.setAttribute("operator", "in");
|
|
38371
|
+
filter.append(blur, opaque, clip);
|
|
38372
|
+
return filter;
|
|
38373
|
+
};
|
|
38374
|
+
|
|
38375
|
+
const createFilter = (region) => {
|
|
38201
38376
|
if (!wallFilterSvg) {
|
|
38202
38377
|
wallFilterSvg = document.createElementNS(SVG_NS, "svg");
|
|
38203
38378
|
wallFilterSvg.setAttribute(WALL_FILTERS_ATTRIBUTE, "");
|
|
@@ -38207,18 +38382,13 @@ const createWallFilter = (source, region) => {
|
|
|
38207
38382
|
wallFilterCount++;
|
|
38208
38383
|
const filter = document.createElementNS(SVG_NS, "filter");
|
|
38209
38384
|
filter.id = `navi-transition-wall-${wallFilterCount}`;
|
|
38210
|
-
// linearRGB, the default, floods another colour than the wall's
|
|
38385
|
+
// linearRGB, the default, floods another colour than the wall's and blurs
|
|
38386
|
+
// in another space than its backdrop-filter.
|
|
38211
38387
|
filter.setAttribute("color-interpolation-filters", "sRGB");
|
|
38212
38388
|
filter.setAttribute("x", region.x);
|
|
38213
38389
|
filter.setAttribute("y", region.y);
|
|
38214
38390
|
filter.setAttribute("width", region.width);
|
|
38215
38391
|
filter.setAttribute("height", region.height);
|
|
38216
|
-
const flood = document.createElementNS(SVG_NS, "feFlood");
|
|
38217
|
-
flood.setAttribute("flood-color", readBackdropColor(source));
|
|
38218
|
-
const composite = document.createElementNS(SVG_NS, "feComposite");
|
|
38219
|
-
composite.setAttribute("in2", "SourceGraphic");
|
|
38220
|
-
composite.setAttribute("operator", "atop");
|
|
38221
|
-
filter.append(flood, composite);
|
|
38222
38392
|
wallFilterSvg.appendChild(filter);
|
|
38223
38393
|
return filter;
|
|
38224
38394
|
};
|