@jsenv/navi 0.18.19 → 0.18.21
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/jsenv_navi.js +111 -103
- package/dist/jsenv_navi.js.map +5 -5
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -630,109 +630,6 @@ const stringifyForDisplay = (
|
|
|
630
630
|
return String(value);
|
|
631
631
|
};
|
|
632
632
|
|
|
633
|
-
/**
|
|
634
|
-
* Reactively runs an action whenever the params derived from signals change.
|
|
635
|
-
*
|
|
636
|
-
* @param {object} action - The action to run.
|
|
637
|
-
* @param {Function} deriveActionParamsFromSignals - A function that reads signals and returns
|
|
638
|
-
* the params to pass to the action. It is re-evaluated automatically whenever a signal it
|
|
639
|
-
* read changes. Return `false`/`null`/`undefined` to skip running the action.
|
|
640
|
-
* @param {object} [options]
|
|
641
|
-
* @param {number} [options.debounce] - When set, the action is only run once the derived params
|
|
642
|
-
* have been stable for this many milliseconds. Useful to avoid firing a backend call on every
|
|
643
|
-
* keystroke: set `debounce: 500` and the request is sent only after the user stops interacting
|
|
644
|
-
* with the filters for 500 ms.
|
|
645
|
-
*
|
|
646
|
-
* Example — auto-refresh a result list while the user tweaks filters:
|
|
647
|
-
* ```js
|
|
648
|
-
* actionRunEffect(searchAction, () => ({
|
|
649
|
-
* query: querySignal.value,
|
|
650
|
-
* page: pageSignal.value,
|
|
651
|
-
* }), { debounce: 500 });
|
|
652
|
-
* ```
|
|
653
|
-
* The action will not fire while the user is actively changing filters; it fires once
|
|
654
|
-
* they pause for half a second.
|
|
655
|
-
*/
|
|
656
|
-
const actionRunEffect = (
|
|
657
|
-
action,
|
|
658
|
-
deriveActionParamsFromSignals,
|
|
659
|
-
{ debounce, meta } = {},
|
|
660
|
-
) => {
|
|
661
|
-
let lastTruthyParams;
|
|
662
|
-
let actionParamsSignal = computed(() => {
|
|
663
|
-
const params = deriveActionParamsFromSignals();
|
|
664
|
-
action.debug(
|
|
665
|
-
`Derived params for action "${action}": ${stringifyForDisplay(params)}`,
|
|
666
|
-
);
|
|
667
|
-
if (!params) {
|
|
668
|
-
// normalize falsy values to undefined so that any falsy value ends up in the same state of "don't run the action"
|
|
669
|
-
return undefined;
|
|
670
|
-
}
|
|
671
|
-
if (params && typeof params.then === "function") {
|
|
672
|
-
{
|
|
673
|
-
console.warn(
|
|
674
|
-
`actionRunEffect second arg is returning a promise. This is not supported, the function should be sync and return params to give to the action`,
|
|
675
|
-
);
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
if (lastTruthyParams === undefined) {
|
|
679
|
-
lastTruthyParams = params;
|
|
680
|
-
}
|
|
681
|
-
return params;
|
|
682
|
-
});
|
|
683
|
-
if (debounce) {
|
|
684
|
-
actionParamsSignal = debounceSignal(actionParamsSignal, {
|
|
685
|
-
delay: debounce,
|
|
686
|
-
});
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
const actionRunnedByThisEffect = action.bindParams(actionParamsSignal, {
|
|
690
|
-
syncParams: debounce ? actionParamsSignal.flush : undefined,
|
|
691
|
-
onChange: (actionTarget, actionTargetPrevious, { explicitRunIntent }) => {
|
|
692
|
-
if (explicitRunIntent) {
|
|
693
|
-
// The caller already issued an explicit run/rerun/prerun/reset/abort —
|
|
694
|
-
// don't attempt to also auto-run from the params change to avoid double-runs.
|
|
695
|
-
action.debug(
|
|
696
|
-
`"${actionTarget}": explicit run intent detected -> skipping auto-run from params change`,
|
|
697
|
-
);
|
|
698
|
-
return;
|
|
699
|
-
}
|
|
700
|
-
if (!actionTargetPrevious && actionTarget) {
|
|
701
|
-
// first run
|
|
702
|
-
if (!actionTarget.params) {
|
|
703
|
-
// falsy params, don't run
|
|
704
|
-
return;
|
|
705
|
-
}
|
|
706
|
-
actionTarget.run({ reason: "truthy params first run" });
|
|
707
|
-
return;
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
if (
|
|
711
|
-
actionTargetPrevious &&
|
|
712
|
-
!actionTargetPrevious.isPrerun &&
|
|
713
|
-
actionTarget
|
|
714
|
-
) {
|
|
715
|
-
// params changed
|
|
716
|
-
if (!actionTarget.params) {
|
|
717
|
-
// falsy params, don't run
|
|
718
|
-
actionTargetPrevious.abort("abortOnFalsyParams");
|
|
719
|
-
return;
|
|
720
|
-
}
|
|
721
|
-
if (compareTwoJsValues(lastTruthyParams, actionTarget.params)) {
|
|
722
|
-
actionTarget.run({ reason: "params restored to last truthy value" });
|
|
723
|
-
} else {
|
|
724
|
-
actionTarget.rerun({ reason: "params modified" });
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
},
|
|
728
|
-
meta,
|
|
729
|
-
});
|
|
730
|
-
if (actionParamsSignal.peek()) {
|
|
731
|
-
actionRunnedByThisEffect.run({ reason: "initial truthy params" });
|
|
732
|
-
}
|
|
733
|
-
return actionRunnedByThisEffect;
|
|
734
|
-
};
|
|
735
|
-
|
|
736
633
|
/**
|
|
737
634
|
* jsenv/navi - createJsValueWeakMap
|
|
738
635
|
*
|
|
@@ -1478,6 +1375,7 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
1478
1375
|
keepOldData = false,
|
|
1479
1376
|
meta = {},
|
|
1480
1377
|
|
|
1378
|
+
outputSignal,
|
|
1481
1379
|
completeSideEffect,
|
|
1482
1380
|
} = options;
|
|
1483
1381
|
if (!Object.hasOwn(options, "params")) {
|
|
@@ -1896,6 +1794,9 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
1896
1794
|
valueSignal.value = value;
|
|
1897
1795
|
runningStateSignal.value = COMPLETED;
|
|
1898
1796
|
const data = dataSignal.value;
|
|
1797
|
+
if (outputSignal) {
|
|
1798
|
+
outputSignal.value = data;
|
|
1799
|
+
}
|
|
1899
1800
|
onComplete?.(data, action);
|
|
1900
1801
|
completeSideEffect?.(action);
|
|
1901
1802
|
});
|
|
@@ -2012,6 +1913,9 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
2012
1913
|
if (!keepOldData) {
|
|
2013
1914
|
valueSignal.value = valueInitial;
|
|
2014
1915
|
}
|
|
1916
|
+
if (outputSignal) {
|
|
1917
|
+
outputSignal.value = null;
|
|
1918
|
+
}
|
|
2015
1919
|
isPrerunSignal.value = true;
|
|
2016
1920
|
runningStateSignal.value = IDLE;
|
|
2017
1921
|
});
|
|
@@ -2386,6 +2290,110 @@ const COMPLETED_ACTION = createAction(() => undefined, {
|
|
|
2386
2290
|
});
|
|
2387
2291
|
getActionPrivateProperties(COMPLETED_ACTION).performRun({});
|
|
2388
2292
|
|
|
2293
|
+
/**
|
|
2294
|
+
* Reactively runs an action whenever the params derived from signals change.
|
|
2295
|
+
*
|
|
2296
|
+
* @param {object} action - The action to run.
|
|
2297
|
+
* @param {Function} deriveActionParamsFromSignals - A function that reads signals and returns
|
|
2298
|
+
* the params to pass to the action. It is re-evaluated automatically whenever a signal it
|
|
2299
|
+
* read changes. Return `false`/`null`/`undefined` to skip running the action.
|
|
2300
|
+
* @param {object} [options]
|
|
2301
|
+
* @param {number} [options.debounce] - When set, the action is only run once the derived params
|
|
2302
|
+
* have been stable for this many milliseconds. Useful to avoid firing a backend call on every
|
|
2303
|
+
* keystroke: set `debounce: 500` and the request is sent only after the user stops interacting
|
|
2304
|
+
* with the filters for 500 ms.
|
|
2305
|
+
*
|
|
2306
|
+
* Example — auto-refresh a result list while the user tweaks filters:
|
|
2307
|
+
* ```js
|
|
2308
|
+
* actionRunEffect(searchAction, () => ({
|
|
2309
|
+
* query: querySignal.value,
|
|
2310
|
+
* page: pageSignal.value,
|
|
2311
|
+
* }), { debounce: 500 });
|
|
2312
|
+
* ```
|
|
2313
|
+
* The action will not fire while the user is actively changing filters; it fires once
|
|
2314
|
+
* they pause for half a second.
|
|
2315
|
+
*/
|
|
2316
|
+
const actionRunEffect = (
|
|
2317
|
+
action,
|
|
2318
|
+
deriveActionParamsFromSignals,
|
|
2319
|
+
{ debounce, meta } = {},
|
|
2320
|
+
) => {
|
|
2321
|
+
if (typeof action === "function") action = createAction(action);
|
|
2322
|
+
let lastTruthyParams;
|
|
2323
|
+
let actionParamsSignal = computed(() => {
|
|
2324
|
+
const params = deriveActionParamsFromSignals();
|
|
2325
|
+
action.debug(
|
|
2326
|
+
`Derived params for action "${action}": ${stringifyForDisplay(params)}`,
|
|
2327
|
+
);
|
|
2328
|
+
if (!params) {
|
|
2329
|
+
// normalize falsy values to undefined so that any falsy value ends up in the same state of "don't run the action"
|
|
2330
|
+
return undefined;
|
|
2331
|
+
}
|
|
2332
|
+
if (params && typeof params.then === "function") {
|
|
2333
|
+
{
|
|
2334
|
+
console.warn(
|
|
2335
|
+
`actionRunEffect second arg is returning a promise. This is not supported, the function should be sync and return params to give to the action`,
|
|
2336
|
+
);
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
if (lastTruthyParams === undefined) {
|
|
2340
|
+
lastTruthyParams = params;
|
|
2341
|
+
}
|
|
2342
|
+
return params;
|
|
2343
|
+
});
|
|
2344
|
+
if (debounce) {
|
|
2345
|
+
actionParamsSignal = debounceSignal(actionParamsSignal, {
|
|
2346
|
+
delay: debounce,
|
|
2347
|
+
});
|
|
2348
|
+
}
|
|
2349
|
+
|
|
2350
|
+
const actionRunnedByThisEffect = action.bindParams(actionParamsSignal, {
|
|
2351
|
+
syncParams: debounce ? actionParamsSignal.flush : undefined,
|
|
2352
|
+
onChange: (actionTarget, actionTargetPrevious, { explicitRunIntent }) => {
|
|
2353
|
+
if (explicitRunIntent) {
|
|
2354
|
+
// The caller already issued an explicit run/rerun/prerun/reset/abort —
|
|
2355
|
+
// don't attempt to also auto-run from the params change to avoid double-runs.
|
|
2356
|
+
action.debug(
|
|
2357
|
+
`"${actionTarget}": explicit run intent detected -> skipping auto-run from params change`,
|
|
2358
|
+
);
|
|
2359
|
+
return;
|
|
2360
|
+
}
|
|
2361
|
+
if (!actionTargetPrevious && actionTarget) {
|
|
2362
|
+
// first run
|
|
2363
|
+
if (!actionTarget.params) {
|
|
2364
|
+
// falsy params, don't run
|
|
2365
|
+
return;
|
|
2366
|
+
}
|
|
2367
|
+
actionTarget.run({ reason: "truthy params first run" });
|
|
2368
|
+
return;
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
if (
|
|
2372
|
+
actionTargetPrevious &&
|
|
2373
|
+
!actionTargetPrevious.isPrerun &&
|
|
2374
|
+
actionTarget
|
|
2375
|
+
) {
|
|
2376
|
+
// params changed
|
|
2377
|
+
if (!actionTarget.params) {
|
|
2378
|
+
// falsy params, don't run
|
|
2379
|
+
actionTargetPrevious.abort("abortOnFalsyParams");
|
|
2380
|
+
return;
|
|
2381
|
+
}
|
|
2382
|
+
if (compareTwoJsValues(lastTruthyParams, actionTarget.params)) {
|
|
2383
|
+
actionTarget.run({ reason: "params restored to last truthy value" });
|
|
2384
|
+
} else {
|
|
2385
|
+
actionTarget.rerun({ reason: "params modified" });
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
},
|
|
2389
|
+
meta,
|
|
2390
|
+
});
|
|
2391
|
+
if (actionParamsSignal.peek()) {
|
|
2392
|
+
actionRunnedByThisEffect.run({ reason: "initial truthy params" });
|
|
2393
|
+
}
|
|
2394
|
+
return actionRunnedByThisEffect;
|
|
2395
|
+
};
|
|
2396
|
+
|
|
2389
2397
|
const useActionData = (action) => {
|
|
2390
2398
|
if (!action) {
|
|
2391
2399
|
return undefined;
|