sygnal 5.1.4 → 5.2.0
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/README.md +8 -7
- package/dist/astro/client.cjs.js +25 -10
- package/dist/astro/client.mjs +25 -10
- package/dist/index.cjs.js +32 -10
- package/dist/index.d.ts +2 -1
- package/dist/index.esm.js +32 -10
- package/dist/sygnal.min.js +1 -1
- package/package.json +1 -1
- package/src/component.ts +18 -11
- package/src/extra/run.ts +4 -0
- package/src/extra/testing.ts +4 -0
- package/src/index.d.ts +2 -1
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ cd my-app
|
|
|
26
26
|
npm run dev
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
Choose from Vite (SPA), Vike (SSR), or Astro templates
|
|
29
|
+
Choose from Vite (SPA), Vike (SSR), or Astro templates in JavaScript or TypeScript.
|
|
30
30
|
|
|
31
31
|
**Or add to an existing project:**
|
|
32
32
|
|
|
@@ -313,18 +313,18 @@ App.model = {
|
|
|
313
313
|
|
|
314
314
|
### Disposal Hooks
|
|
315
315
|
|
|
316
|
-
Cleanup on unmount:
|
|
316
|
+
Cleanup on unmount with the built-in `DISPOSE` action:
|
|
317
317
|
|
|
318
318
|
```jsx
|
|
319
|
-
MyComponent.intent = ({ dispose$ }) => ({
|
|
320
|
-
CLEANUP: dispose$,
|
|
321
|
-
})
|
|
322
|
-
|
|
323
319
|
MyComponent.model = {
|
|
324
|
-
|
|
320
|
+
DISPOSE: {
|
|
321
|
+
EFFECT: (state) => clearInterval(state.timerId),
|
|
322
|
+
},
|
|
325
323
|
}
|
|
326
324
|
```
|
|
327
325
|
|
|
326
|
+
For advanced cases needing stream composition, the `dispose$` source is also available in intent.
|
|
327
|
+
|
|
328
328
|
### Testing
|
|
329
329
|
|
|
330
330
|
Test components in isolation with `renderComponent`:
|
|
@@ -469,6 +469,7 @@ h('div', [h('h1', 'Hello'), h('button.btn', 'Click')])
|
|
|
469
469
|
| [AI Discussion Panel](./examples/ai-panel-spa) | Complex SPA with custom drivers |
|
|
470
470
|
| [Sygnal ToDoMVC](https://github.com/tpresley/sygnal-todomvc) | [Live Demo](https://tpresley.github.io/sygnal-todomvc/) |
|
|
471
471
|
| [Sygnal 2048](https://github.com/tpresley/sygnal-2048) | [Live Demo](https://tpresley.github.io/sygnal-2048/) |
|
|
472
|
+
| [Sygnal Mahjong](https://github.com/tpresley/mahjong-trainer) | [Live Demo](https://tpresley.github.io/mahjong-trainer/) |
|
|
472
473
|
| [Sygnal Calculator](https://github.com/tpresley/sygnal-calculator) | [Live Demo](https://tpresley.github.io/sygnal-calculator/) |
|
|
473
474
|
|
|
474
475
|
## Acknowledgments
|
package/dist/astro/client.cjs.js
CHANGED
|
@@ -5447,6 +5447,7 @@ const ENVIRONMENT = (typeof window != 'undefined' && window) || (typeof process
|
|
|
5447
5447
|
const BOOTSTRAP_ACTION = 'BOOTSTRAP';
|
|
5448
5448
|
const INITIALIZE_ACTION = 'INITIALIZE';
|
|
5449
5449
|
const HYDRATE_ACTION = 'HYDRATE';
|
|
5450
|
+
const DISPOSE_ACTION = 'DISPOSE';
|
|
5450
5451
|
const PARENT_SINK_NAME = 'PARENT';
|
|
5451
5452
|
const CHILD_SOURCE_NAME = 'CHILD';
|
|
5452
5453
|
const READY_SINK_NAME = 'READY';
|
|
@@ -5752,8 +5753,15 @@ class Component {
|
|
|
5752
5753
|
}
|
|
5753
5754
|
}
|
|
5754
5755
|
dispose() {
|
|
5755
|
-
//
|
|
5756
|
-
|
|
5756
|
+
// Fire the DISPOSE built-in action so model handlers can run cleanup logic
|
|
5757
|
+
const hasDispose = this.model && (this.model[DISPOSE_ACTION] || Object.keys(this.model).some(k => k.includes('|') && k.split('|')[0].trim() === DISPOSE_ACTION));
|
|
5758
|
+
if (hasDispose && this.action$ && typeof this.action$.shamefullySendNext === 'function') {
|
|
5759
|
+
try {
|
|
5760
|
+
this.action$.shamefullySendNext({ type: DISPOSE_ACTION });
|
|
5761
|
+
}
|
|
5762
|
+
catch (_) { }
|
|
5763
|
+
}
|
|
5764
|
+
// Signal disposal to the component via dispose$ stream (for advanced use cases)
|
|
5757
5765
|
if (this._disposeListener) {
|
|
5758
5766
|
try {
|
|
5759
5767
|
this._disposeListener.next(true);
|
|
@@ -5762,7 +5770,7 @@ class Component {
|
|
|
5762
5770
|
catch (_) { }
|
|
5763
5771
|
this._disposeListener = null;
|
|
5764
5772
|
}
|
|
5765
|
-
// Tear down streams on next microtask to allow
|
|
5773
|
+
// Tear down streams on next microtask to allow DISPOSE/cleanup actions to process
|
|
5766
5774
|
setTimeout(() => {
|
|
5767
5775
|
// Complete the action$ stream to stop the entire component cycle
|
|
5768
5776
|
if (this.action$ && typeof this.action$.shamefullySendComplete === 'function') {
|
|
@@ -7336,11 +7344,11 @@ function __sortFunctionFromObj(item) {
|
|
|
7336
7344
|
}
|
|
7337
7345
|
let ascending = true;
|
|
7338
7346
|
if (typeof directionRaw === 'string') {
|
|
7339
|
-
if (!['asc', '
|
|
7340
|
-
console.error('Sort object string values must be asc or
|
|
7347
|
+
if (!['asc', 'desc'].includes(directionRaw.toLowerCase())) {
|
|
7348
|
+
console.error('Sort object string values must be asc or desc:', item);
|
|
7341
7349
|
return undefined;
|
|
7342
7350
|
}
|
|
7343
|
-
ascending = directionRaw.toLowerCase()
|
|
7351
|
+
ascending = directionRaw.toLowerCase() !== 'desc';
|
|
7344
7352
|
}
|
|
7345
7353
|
if (typeof directionRaw === 'number') {
|
|
7346
7354
|
if (directionRaw !== 1 && directionRaw !== -1) {
|
|
@@ -7359,9 +7367,9 @@ function sortFunctionFromProp(sortProp) {
|
|
|
7359
7367
|
if (propType === 'function')
|
|
7360
7368
|
return sortProp;
|
|
7361
7369
|
if (propType === 'string') {
|
|
7362
|
-
// if passed either 'asc' or '
|
|
7363
|
-
if (sortProp.toLowerCase() === 'asc' || sortProp.toLowerCase() === '
|
|
7364
|
-
const ascending = sortProp.toLowerCase()
|
|
7370
|
+
// if passed either 'asc' or 'desc' sort on the entire item
|
|
7371
|
+
if (sortProp.toLowerCase() === 'asc' || sortProp.toLowerCase() === 'desc') {
|
|
7372
|
+
const ascending = sortProp.toLowerCase() !== 'desc';
|
|
7365
7373
|
return (a, b) => __baseSort(a, b, ascending);
|
|
7366
7374
|
}
|
|
7367
7375
|
// assume it's a field/property name, and sort it ascending
|
|
@@ -7372,7 +7380,7 @@ function sortFunctionFromProp(sortProp) {
|
|
|
7372
7380
|
const sorters = sortProp.map(item => {
|
|
7373
7381
|
if (typeof item === 'function')
|
|
7374
7382
|
return item;
|
|
7375
|
-
if (typeof item === 'string' && !['asc', '
|
|
7383
|
+
if (typeof item === 'string' && !['asc', 'desc'].includes(item.toLowerCase()))
|
|
7376
7384
|
return (a, b) => __baseSort(a[item], b[item], true);
|
|
7377
7385
|
if (isObj(item)) {
|
|
7378
7386
|
return __sortFunctionFromObj(item);
|
|
@@ -7706,6 +7714,13 @@ function run(app, drivers = {}, options = {}) {
|
|
|
7706
7714
|
sources.STATE.stream.removeListener(persistListener);
|
|
7707
7715
|
persistListener = null;
|
|
7708
7716
|
}
|
|
7717
|
+
// Trigger the component's dispose() which fires the DISPOSE action and dispose$ stream
|
|
7718
|
+
if (typeof sinks.__dispose === 'function') {
|
|
7719
|
+
try {
|
|
7720
|
+
sinks.__dispose();
|
|
7721
|
+
}
|
|
7722
|
+
catch (_) { }
|
|
7723
|
+
}
|
|
7709
7724
|
rawDispose();
|
|
7710
7725
|
};
|
|
7711
7726
|
const exposed = { sources, sinks, dispose };
|
package/dist/astro/client.mjs
CHANGED
|
@@ -5445,6 +5445,7 @@ const ENVIRONMENT = (typeof window != 'undefined' && window) || (typeof process
|
|
|
5445
5445
|
const BOOTSTRAP_ACTION = 'BOOTSTRAP';
|
|
5446
5446
|
const INITIALIZE_ACTION = 'INITIALIZE';
|
|
5447
5447
|
const HYDRATE_ACTION = 'HYDRATE';
|
|
5448
|
+
const DISPOSE_ACTION = 'DISPOSE';
|
|
5448
5449
|
const PARENT_SINK_NAME = 'PARENT';
|
|
5449
5450
|
const CHILD_SOURCE_NAME = 'CHILD';
|
|
5450
5451
|
const READY_SINK_NAME = 'READY';
|
|
@@ -5750,8 +5751,15 @@ class Component {
|
|
|
5750
5751
|
}
|
|
5751
5752
|
}
|
|
5752
5753
|
dispose() {
|
|
5753
|
-
//
|
|
5754
|
-
|
|
5754
|
+
// Fire the DISPOSE built-in action so model handlers can run cleanup logic
|
|
5755
|
+
const hasDispose = this.model && (this.model[DISPOSE_ACTION] || Object.keys(this.model).some(k => k.includes('|') && k.split('|')[0].trim() === DISPOSE_ACTION));
|
|
5756
|
+
if (hasDispose && this.action$ && typeof this.action$.shamefullySendNext === 'function') {
|
|
5757
|
+
try {
|
|
5758
|
+
this.action$.shamefullySendNext({ type: DISPOSE_ACTION });
|
|
5759
|
+
}
|
|
5760
|
+
catch (_) { }
|
|
5761
|
+
}
|
|
5762
|
+
// Signal disposal to the component via dispose$ stream (for advanced use cases)
|
|
5755
5763
|
if (this._disposeListener) {
|
|
5756
5764
|
try {
|
|
5757
5765
|
this._disposeListener.next(true);
|
|
@@ -5760,7 +5768,7 @@ class Component {
|
|
|
5760
5768
|
catch (_) { }
|
|
5761
5769
|
this._disposeListener = null;
|
|
5762
5770
|
}
|
|
5763
|
-
// Tear down streams on next microtask to allow
|
|
5771
|
+
// Tear down streams on next microtask to allow DISPOSE/cleanup actions to process
|
|
5764
5772
|
setTimeout(() => {
|
|
5765
5773
|
// Complete the action$ stream to stop the entire component cycle
|
|
5766
5774
|
if (this.action$ && typeof this.action$.shamefullySendComplete === 'function') {
|
|
@@ -7334,11 +7342,11 @@ function __sortFunctionFromObj(item) {
|
|
|
7334
7342
|
}
|
|
7335
7343
|
let ascending = true;
|
|
7336
7344
|
if (typeof directionRaw === 'string') {
|
|
7337
|
-
if (!['asc', '
|
|
7338
|
-
console.error('Sort object string values must be asc or
|
|
7345
|
+
if (!['asc', 'desc'].includes(directionRaw.toLowerCase())) {
|
|
7346
|
+
console.error('Sort object string values must be asc or desc:', item);
|
|
7339
7347
|
return undefined;
|
|
7340
7348
|
}
|
|
7341
|
-
ascending = directionRaw.toLowerCase()
|
|
7349
|
+
ascending = directionRaw.toLowerCase() !== 'desc';
|
|
7342
7350
|
}
|
|
7343
7351
|
if (typeof directionRaw === 'number') {
|
|
7344
7352
|
if (directionRaw !== 1 && directionRaw !== -1) {
|
|
@@ -7357,9 +7365,9 @@ function sortFunctionFromProp(sortProp) {
|
|
|
7357
7365
|
if (propType === 'function')
|
|
7358
7366
|
return sortProp;
|
|
7359
7367
|
if (propType === 'string') {
|
|
7360
|
-
// if passed either 'asc' or '
|
|
7361
|
-
if (sortProp.toLowerCase() === 'asc' || sortProp.toLowerCase() === '
|
|
7362
|
-
const ascending = sortProp.toLowerCase()
|
|
7368
|
+
// if passed either 'asc' or 'desc' sort on the entire item
|
|
7369
|
+
if (sortProp.toLowerCase() === 'asc' || sortProp.toLowerCase() === 'desc') {
|
|
7370
|
+
const ascending = sortProp.toLowerCase() !== 'desc';
|
|
7363
7371
|
return (a, b) => __baseSort(a, b, ascending);
|
|
7364
7372
|
}
|
|
7365
7373
|
// assume it's a field/property name, and sort it ascending
|
|
@@ -7370,7 +7378,7 @@ function sortFunctionFromProp(sortProp) {
|
|
|
7370
7378
|
const sorters = sortProp.map(item => {
|
|
7371
7379
|
if (typeof item === 'function')
|
|
7372
7380
|
return item;
|
|
7373
|
-
if (typeof item === 'string' && !['asc', '
|
|
7381
|
+
if (typeof item === 'string' && !['asc', 'desc'].includes(item.toLowerCase()))
|
|
7374
7382
|
return (a, b) => __baseSort(a[item], b[item], true);
|
|
7375
7383
|
if (isObj(item)) {
|
|
7376
7384
|
return __sortFunctionFromObj(item);
|
|
@@ -7704,6 +7712,13 @@ function run(app, drivers = {}, options = {}) {
|
|
|
7704
7712
|
sources.STATE.stream.removeListener(persistListener);
|
|
7705
7713
|
persistListener = null;
|
|
7706
7714
|
}
|
|
7715
|
+
// Trigger the component's dispose() which fires the DISPOSE action and dispose$ stream
|
|
7716
|
+
if (typeof sinks.__dispose === 'function') {
|
|
7717
|
+
try {
|
|
7718
|
+
sinks.__dispose();
|
|
7719
|
+
}
|
|
7720
|
+
catch (_) { }
|
|
7721
|
+
}
|
|
7707
7722
|
rawDispose();
|
|
7708
7723
|
};
|
|
7709
7724
|
const exposed = { sources, sinks, dispose };
|
package/dist/index.cjs.js
CHANGED
|
@@ -2185,6 +2185,7 @@ const ENVIRONMENT = (typeof window != 'undefined' && window) || (typeof process
|
|
|
2185
2185
|
const BOOTSTRAP_ACTION = 'BOOTSTRAP';
|
|
2186
2186
|
const INITIALIZE_ACTION = 'INITIALIZE';
|
|
2187
2187
|
const HYDRATE_ACTION = 'HYDRATE';
|
|
2188
|
+
const DISPOSE_ACTION = 'DISPOSE';
|
|
2188
2189
|
const PARENT_SINK_NAME = 'PARENT';
|
|
2189
2190
|
const CHILD_SOURCE_NAME = 'CHILD';
|
|
2190
2191
|
const READY_SINK_NAME = 'READY';
|
|
@@ -2490,8 +2491,15 @@ class Component {
|
|
|
2490
2491
|
}
|
|
2491
2492
|
}
|
|
2492
2493
|
dispose() {
|
|
2493
|
-
//
|
|
2494
|
-
|
|
2494
|
+
// Fire the DISPOSE built-in action so model handlers can run cleanup logic
|
|
2495
|
+
const hasDispose = this.model && (this.model[DISPOSE_ACTION] || Object.keys(this.model).some(k => k.includes('|') && k.split('|')[0].trim() === DISPOSE_ACTION));
|
|
2496
|
+
if (hasDispose && this.action$ && typeof this.action$.shamefullySendNext === 'function') {
|
|
2497
|
+
try {
|
|
2498
|
+
this.action$.shamefullySendNext({ type: DISPOSE_ACTION });
|
|
2499
|
+
}
|
|
2500
|
+
catch (_) { }
|
|
2501
|
+
}
|
|
2502
|
+
// Signal disposal to the component via dispose$ stream (for advanced use cases)
|
|
2495
2503
|
if (this._disposeListener) {
|
|
2496
2504
|
try {
|
|
2497
2505
|
this._disposeListener.next(true);
|
|
@@ -2500,7 +2508,7 @@ class Component {
|
|
|
2500
2508
|
catch (_) { }
|
|
2501
2509
|
this._disposeListener = null;
|
|
2502
2510
|
}
|
|
2503
|
-
// Tear down streams on next microtask to allow
|
|
2511
|
+
// Tear down streams on next microtask to allow DISPOSE/cleanup actions to process
|
|
2504
2512
|
setTimeout(() => {
|
|
2505
2513
|
// Complete the action$ stream to stop the entire component cycle
|
|
2506
2514
|
if (this.action$ && typeof this.action$.shamefullySendComplete === 'function') {
|
|
@@ -4074,11 +4082,11 @@ function __sortFunctionFromObj(item) {
|
|
|
4074
4082
|
}
|
|
4075
4083
|
let ascending = true;
|
|
4076
4084
|
if (typeof directionRaw === 'string') {
|
|
4077
|
-
if (!['asc', '
|
|
4078
|
-
console.error('Sort object string values must be asc or
|
|
4085
|
+
if (!['asc', 'desc'].includes(directionRaw.toLowerCase())) {
|
|
4086
|
+
console.error('Sort object string values must be asc or desc:', item);
|
|
4079
4087
|
return undefined;
|
|
4080
4088
|
}
|
|
4081
|
-
ascending = directionRaw.toLowerCase()
|
|
4089
|
+
ascending = directionRaw.toLowerCase() !== 'desc';
|
|
4082
4090
|
}
|
|
4083
4091
|
if (typeof directionRaw === 'number') {
|
|
4084
4092
|
if (directionRaw !== 1 && directionRaw !== -1) {
|
|
@@ -4097,9 +4105,9 @@ function sortFunctionFromProp(sortProp) {
|
|
|
4097
4105
|
if (propType === 'function')
|
|
4098
4106
|
return sortProp;
|
|
4099
4107
|
if (propType === 'string') {
|
|
4100
|
-
// if passed either 'asc' or '
|
|
4101
|
-
if (sortProp.toLowerCase() === 'asc' || sortProp.toLowerCase() === '
|
|
4102
|
-
const ascending = sortProp.toLowerCase()
|
|
4108
|
+
// if passed either 'asc' or 'desc' sort on the entire item
|
|
4109
|
+
if (sortProp.toLowerCase() === 'asc' || sortProp.toLowerCase() === 'desc') {
|
|
4110
|
+
const ascending = sortProp.toLowerCase() !== 'desc';
|
|
4103
4111
|
return (a, b) => __baseSort(a, b, ascending);
|
|
4104
4112
|
}
|
|
4105
4113
|
// assume it's a field/property name, and sort it ascending
|
|
@@ -4110,7 +4118,7 @@ function sortFunctionFromProp(sortProp) {
|
|
|
4110
4118
|
const sorters = sortProp.map(item => {
|
|
4111
4119
|
if (typeof item === 'function')
|
|
4112
4120
|
return item;
|
|
4113
|
-
if (typeof item === 'string' && !['asc', '
|
|
4121
|
+
if (typeof item === 'string' && !['asc', 'desc'].includes(item.toLowerCase()))
|
|
4114
4122
|
return (a, b) => __baseSort(a[item], b[item], true);
|
|
4115
4123
|
if (isObj(item)) {
|
|
4116
4124
|
return __sortFunctionFromObj(item);
|
|
@@ -5009,6 +5017,13 @@ function run(app, drivers = {}, options = {}) {
|
|
|
5009
5017
|
sources.STATE.stream.removeListener(persistListener);
|
|
5010
5018
|
persistListener = null;
|
|
5011
5019
|
}
|
|
5020
|
+
// Trigger the component's dispose() which fires the DISPOSE action and dispose$ stream
|
|
5021
|
+
if (typeof sinks.__dispose === 'function') {
|
|
5022
|
+
try {
|
|
5023
|
+
sinks.__dispose();
|
|
5024
|
+
}
|
|
5025
|
+
catch (_) { }
|
|
5026
|
+
}
|
|
5012
5027
|
rawDispose();
|
|
5013
5028
|
};
|
|
5014
5029
|
const exposed = { sources, sinks, dispose };
|
|
@@ -5805,6 +5820,13 @@ function renderComponent(componentDef, options = {}) {
|
|
|
5805
5820
|
catch (_) { }
|
|
5806
5821
|
stateListener = null;
|
|
5807
5822
|
}
|
|
5823
|
+
// Trigger the component's dispose() which fires the DISPOSE action and dispose$ stream
|
|
5824
|
+
if (typeof sinks.__dispose === 'function') {
|
|
5825
|
+
try {
|
|
5826
|
+
sinks.__dispose();
|
|
5827
|
+
}
|
|
5828
|
+
catch (_) { }
|
|
5829
|
+
}
|
|
5808
5830
|
rawDispose();
|
|
5809
5831
|
};
|
|
5810
5832
|
return {
|
package/dist/index.d.ts
CHANGED
|
@@ -117,6 +117,7 @@ type WithDefaultActions<STATE, ACTIONS> = ACTIONS & {
|
|
|
117
117
|
BOOTSTRAP?: never;
|
|
118
118
|
INITIALIZE?: STATE;
|
|
119
119
|
HYDRATE?: any;
|
|
120
|
+
DISPOSE?: never;
|
|
120
121
|
}
|
|
121
122
|
|
|
122
123
|
type ComponentModel<STATE, PROPS, DRIVERS, ACTIONS, CALCULATED, SINK_RETURNS extends NonStateSinkReturns = {}> = keyof ACTIONS extends never
|
|
@@ -239,7 +240,7 @@ export type Filter<ITEM = any> = (item: ITEM) => boolean
|
|
|
239
240
|
export type SortFunction<ITEM = any> = (a: ITEM, b: ITEM) => number
|
|
240
241
|
|
|
241
242
|
export type SortObject<ITEM = any> = {
|
|
242
|
-
[field: string]: 'asc' | '
|
|
243
|
+
[field: string]: 'asc' | 'desc' | SortFunction<ITEM>
|
|
243
244
|
}
|
|
244
245
|
|
|
245
246
|
/**
|
package/dist/index.esm.js
CHANGED
|
@@ -2168,6 +2168,7 @@ const ENVIRONMENT = (typeof window != 'undefined' && window) || (typeof process
|
|
|
2168
2168
|
const BOOTSTRAP_ACTION = 'BOOTSTRAP';
|
|
2169
2169
|
const INITIALIZE_ACTION = 'INITIALIZE';
|
|
2170
2170
|
const HYDRATE_ACTION = 'HYDRATE';
|
|
2171
|
+
const DISPOSE_ACTION = 'DISPOSE';
|
|
2171
2172
|
const PARENT_SINK_NAME = 'PARENT';
|
|
2172
2173
|
const CHILD_SOURCE_NAME = 'CHILD';
|
|
2173
2174
|
const READY_SINK_NAME = 'READY';
|
|
@@ -2473,8 +2474,15 @@ class Component {
|
|
|
2473
2474
|
}
|
|
2474
2475
|
}
|
|
2475
2476
|
dispose() {
|
|
2476
|
-
//
|
|
2477
|
-
|
|
2477
|
+
// Fire the DISPOSE built-in action so model handlers can run cleanup logic
|
|
2478
|
+
const hasDispose = this.model && (this.model[DISPOSE_ACTION] || Object.keys(this.model).some(k => k.includes('|') && k.split('|')[0].trim() === DISPOSE_ACTION));
|
|
2479
|
+
if (hasDispose && this.action$ && typeof this.action$.shamefullySendNext === 'function') {
|
|
2480
|
+
try {
|
|
2481
|
+
this.action$.shamefullySendNext({ type: DISPOSE_ACTION });
|
|
2482
|
+
}
|
|
2483
|
+
catch (_) { }
|
|
2484
|
+
}
|
|
2485
|
+
// Signal disposal to the component via dispose$ stream (for advanced use cases)
|
|
2478
2486
|
if (this._disposeListener) {
|
|
2479
2487
|
try {
|
|
2480
2488
|
this._disposeListener.next(true);
|
|
@@ -2483,7 +2491,7 @@ class Component {
|
|
|
2483
2491
|
catch (_) { }
|
|
2484
2492
|
this._disposeListener = null;
|
|
2485
2493
|
}
|
|
2486
|
-
// Tear down streams on next microtask to allow
|
|
2494
|
+
// Tear down streams on next microtask to allow DISPOSE/cleanup actions to process
|
|
2487
2495
|
setTimeout(() => {
|
|
2488
2496
|
// Complete the action$ stream to stop the entire component cycle
|
|
2489
2497
|
if (this.action$ && typeof this.action$.shamefullySendComplete === 'function') {
|
|
@@ -4057,11 +4065,11 @@ function __sortFunctionFromObj(item) {
|
|
|
4057
4065
|
}
|
|
4058
4066
|
let ascending = true;
|
|
4059
4067
|
if (typeof directionRaw === 'string') {
|
|
4060
|
-
if (!['asc', '
|
|
4061
|
-
console.error('Sort object string values must be asc or
|
|
4068
|
+
if (!['asc', 'desc'].includes(directionRaw.toLowerCase())) {
|
|
4069
|
+
console.error('Sort object string values must be asc or desc:', item);
|
|
4062
4070
|
return undefined;
|
|
4063
4071
|
}
|
|
4064
|
-
ascending = directionRaw.toLowerCase()
|
|
4072
|
+
ascending = directionRaw.toLowerCase() !== 'desc';
|
|
4065
4073
|
}
|
|
4066
4074
|
if (typeof directionRaw === 'number') {
|
|
4067
4075
|
if (directionRaw !== 1 && directionRaw !== -1) {
|
|
@@ -4080,9 +4088,9 @@ function sortFunctionFromProp(sortProp) {
|
|
|
4080
4088
|
if (propType === 'function')
|
|
4081
4089
|
return sortProp;
|
|
4082
4090
|
if (propType === 'string') {
|
|
4083
|
-
// if passed either 'asc' or '
|
|
4084
|
-
if (sortProp.toLowerCase() === 'asc' || sortProp.toLowerCase() === '
|
|
4085
|
-
const ascending = sortProp.toLowerCase()
|
|
4091
|
+
// if passed either 'asc' or 'desc' sort on the entire item
|
|
4092
|
+
if (sortProp.toLowerCase() === 'asc' || sortProp.toLowerCase() === 'desc') {
|
|
4093
|
+
const ascending = sortProp.toLowerCase() !== 'desc';
|
|
4086
4094
|
return (a, b) => __baseSort(a, b, ascending);
|
|
4087
4095
|
}
|
|
4088
4096
|
// assume it's a field/property name, and sort it ascending
|
|
@@ -4093,7 +4101,7 @@ function sortFunctionFromProp(sortProp) {
|
|
|
4093
4101
|
const sorters = sortProp.map(item => {
|
|
4094
4102
|
if (typeof item === 'function')
|
|
4095
4103
|
return item;
|
|
4096
|
-
if (typeof item === 'string' && !['asc', '
|
|
4104
|
+
if (typeof item === 'string' && !['asc', 'desc'].includes(item.toLowerCase()))
|
|
4097
4105
|
return (a, b) => __baseSort(a[item], b[item], true);
|
|
4098
4106
|
if (isObj(item)) {
|
|
4099
4107
|
return __sortFunctionFromObj(item);
|
|
@@ -4992,6 +5000,13 @@ function run(app, drivers = {}, options = {}) {
|
|
|
4992
5000
|
sources.STATE.stream.removeListener(persistListener);
|
|
4993
5001
|
persistListener = null;
|
|
4994
5002
|
}
|
|
5003
|
+
// Trigger the component's dispose() which fires the DISPOSE action and dispose$ stream
|
|
5004
|
+
if (typeof sinks.__dispose === 'function') {
|
|
5005
|
+
try {
|
|
5006
|
+
sinks.__dispose();
|
|
5007
|
+
}
|
|
5008
|
+
catch (_) { }
|
|
5009
|
+
}
|
|
4995
5010
|
rawDispose();
|
|
4996
5011
|
};
|
|
4997
5012
|
const exposed = { sources, sinks, dispose };
|
|
@@ -5788,6 +5803,13 @@ function renderComponent(componentDef, options = {}) {
|
|
|
5788
5803
|
catch (_) { }
|
|
5789
5804
|
stateListener = null;
|
|
5790
5805
|
}
|
|
5806
|
+
// Trigger the component's dispose() which fires the DISPOSE action and dispose$ stream
|
|
5807
|
+
if (typeof sinks.__dispose === 'function') {
|
|
5808
|
+
try {
|
|
5809
|
+
sinks.__dispose();
|
|
5810
|
+
}
|
|
5811
|
+
catch (_) { }
|
|
5812
|
+
}
|
|
5791
5813
|
rawDispose();
|
|
5792
5814
|
};
|
|
5793
5815
|
return {
|