sygnal 3.0.1 → 4.0.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 +5 -4
- package/dist/index.cjs.js +104 -29
- package/dist/index.d.ts +222 -0
- package/dist/index.esm.js +104 -31
- package/dist/jsx-dev-runtime.js +224 -0
- package/dist/jsx-runtime.js +224 -0
- package/dist/jsx.cjs.js +14 -2
- package/dist/jsx.esm.js +14 -2
- package/dist/sygnal.min.js +1 -1
- package/package.json +15 -3
- package/src/collection.js +76 -0
- package/src/component.js +1372 -0
- package/src/extra/classes.js +82 -0
- package/src/extra/driverFactories.js +124 -0
- package/src/extra/eventDriver.js +34 -0
- package/src/extra/logDriver.js +9 -0
- package/src/extra/processForm.js +30 -0
- package/src/extra/run.js +62 -0
- package/src/index.d.ts +221 -0
- package/src/index.js +21 -0
- package/src/jsx.js +2 -0
- package/src/pragma/fn.js +54 -0
- package/src/pragma/index.js +133 -0
- package/src/pragma/is.js +23 -0
- package/src/switchable.js +103 -0
- package/src/sygnal.d.ts +47 -0
package/README.md
CHANGED
|
@@ -623,12 +623,13 @@ RootComponent.model = {
|
|
|
623
623
|
|
|
624
624
|
Another very common task in web pages and browser applications is to work with form inputs. Unfortunately, the logic and stream plumbing required to do this routine task can be challenging to developers new to observables (and is frustrating even for most veterans). Sygnal's processForm() helper function takes any HTML form element, and automatically extracts the values from all input fields contained within it. By default processForm() listens to both 'input' and 'submit' events, but can be configured to listen to any combination of standard or custom events on the form itself or its inputs.
|
|
625
625
|
|
|
626
|
-
The Observable from `processForm` always returns objects with the current value of every field in the form, so the following will print something like:
|
|
626
|
+
The Observable from `processForm` always returns objects with the current value of every field in the form along with the name of the JS event that initiated it, so the following will print something like:
|
|
627
627
|
|
|
628
628
|
```javascript
|
|
629
629
|
{
|
|
630
|
-
'first-name': '
|
|
631
|
-
'last-name': '
|
|
630
|
+
'first-name': 'Bob',
|
|
631
|
+
'last-name': 'Smith',
|
|
632
|
+
eventType: 'submit'
|
|
632
633
|
}
|
|
633
634
|
```
|
|
634
635
|
|
|
@@ -683,7 +684,7 @@ npm run build
|
|
|
683
684
|
The results will be in the 'dist' folder, and you can serve it locally by running:
|
|
684
685
|
|
|
685
686
|
```bash
|
|
686
|
-
npm preview
|
|
687
|
+
npm run preview
|
|
687
688
|
```
|
|
688
689
|
|
|
689
690
|
Alternatively, you can use any other bundler of your choice (Webpack, Babel, Rollup, etc.). To use JSX in your components while using alternative bundlers, you will need to configure your bundler to use Sygnal's JSX pragma. This is slightly different for each bundler, but looks generally like:
|
package/dist/index.cjs.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
var isolate = require('@cycle/isolate');
|
|
4
4
|
var state = require('@cycle/state');
|
|
5
|
+
var dom = require('@cycle/dom');
|
|
5
6
|
var xs$1 = require('xstream');
|
|
6
7
|
var run$1 = require('@cycle/run');
|
|
7
|
-
var dom = require('@cycle/dom');
|
|
8
8
|
|
|
9
9
|
function collection(component, stateLense, opts={}) {
|
|
10
10
|
const {
|
|
@@ -66,6 +66,13 @@ function collection(component, stateLense, opts={}) {
|
|
|
66
66
|
return isolate(state.makeCollection(collectionOpts), isolateOpts)(sources)
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
const Collection = (props) => {
|
|
70
|
+
const { children, ...sanitizedProps } = props;
|
|
71
|
+
return dom.h('collection', { props: sanitizedProps }, children)
|
|
72
|
+
};
|
|
73
|
+
Collection.label = 'collection';
|
|
74
|
+
Collection.preventInstantiation = true;
|
|
75
|
+
|
|
69
76
|
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
|
|
70
77
|
|
|
71
78
|
var dropRepeats$1 = {};
|
|
@@ -2845,6 +2852,13 @@ function _switchable (factories, sources, name$, switched=['DOM'], stateSourceNa
|
|
|
2845
2852
|
return switchedSinks
|
|
2846
2853
|
}
|
|
2847
2854
|
|
|
2855
|
+
const Switchable = (props) => {
|
|
2856
|
+
const { children, ...sanitizedProps } = props;
|
|
2857
|
+
return dom.h('switchable', { props: sanitizedProps }, children)
|
|
2858
|
+
};
|
|
2859
|
+
Switchable.label = 'switchable';
|
|
2860
|
+
Switchable.preventInstantiation = true;
|
|
2861
|
+
|
|
2848
2862
|
var delay$1 = {};
|
|
2849
2863
|
|
|
2850
2864
|
Object.defineProperty(delay$1, "__esModule", { value: true });
|
|
@@ -3154,7 +3168,7 @@ const CHILD_SOURCE_NAME = 'CHILD';
|
|
|
3154
3168
|
let COMPONENT_COUNT = 0;
|
|
3155
3169
|
|
|
3156
3170
|
|
|
3157
|
-
const ABORT = '~#~#~ABORT~#~#~';
|
|
3171
|
+
const ABORT$1 = '~#~#~ABORT~#~#~';
|
|
3158
3172
|
|
|
3159
3173
|
function component (opts) {
|
|
3160
3174
|
const { name, sources, isolateOpts, stateSourceName='STATE' } = opts;
|
|
@@ -3239,13 +3253,14 @@ class Component {
|
|
|
3239
3253
|
// [ OUTPUT ]
|
|
3240
3254
|
// sinks
|
|
3241
3255
|
|
|
3242
|
-
constructor({ name='NO NAME', sources, intent, model, context, response, view, peers={}, components={}, initialState, calculated, storeCalculatedInState=true, DOMSourceName='DOM', stateSourceName='STATE', requestSourceName='HTTP', debug=false }) {
|
|
3256
|
+
constructor({ name='NO NAME', sources, intent, model, hmrActions, context, response, view, peers={}, components={}, initialState, calculated, storeCalculatedInState=true, DOMSourceName='DOM', stateSourceName='STATE', requestSourceName='HTTP', debug=false }) {
|
|
3243
3257
|
if (!sources || !isObj(sources)) throw new Error('Missing or invalid sources')
|
|
3244
3258
|
|
|
3245
3259
|
this.name = name;
|
|
3246
3260
|
this.sources = sources;
|
|
3247
3261
|
this.intent = intent;
|
|
3248
3262
|
this.model = model;
|
|
3263
|
+
this.hmrActions = hmrActions;
|
|
3249
3264
|
this.context = context;
|
|
3250
3265
|
this.response = response;
|
|
3251
3266
|
this.view = view;
|
|
@@ -3275,7 +3290,8 @@ class Component {
|
|
|
3275
3290
|
const props$ = sources.props$;
|
|
3276
3291
|
if (props$) {
|
|
3277
3292
|
this.sources.props$ = props$.map(val => {
|
|
3278
|
-
|
|
3293
|
+
const { sygnalFactory, sygnalOptions, ...sanitizedProps } = val;
|
|
3294
|
+
this.currentProps = sanitizedProps;
|
|
3279
3295
|
return val
|
|
3280
3296
|
});
|
|
3281
3297
|
}
|
|
@@ -3306,6 +3322,7 @@ class Component {
|
|
|
3306
3322
|
|
|
3307
3323
|
this.initChildSources$();
|
|
3308
3324
|
this.initIntent$();
|
|
3325
|
+
this.initHmrActions();
|
|
3309
3326
|
this.initAction$();
|
|
3310
3327
|
this.initState();
|
|
3311
3328
|
this.initContext();
|
|
@@ -3340,6 +3357,23 @@ class Component {
|
|
|
3340
3357
|
}
|
|
3341
3358
|
}
|
|
3342
3359
|
|
|
3360
|
+
initHmrActions() {
|
|
3361
|
+
if (typeof this.hmrActions === 'undefined') {
|
|
3362
|
+
this.hmrAction$ = xs$1.of().filter(_ => false);
|
|
3363
|
+
return
|
|
3364
|
+
}
|
|
3365
|
+
if (typeof this.hmrActions === 'string') {
|
|
3366
|
+
this.hmrActions = [this.hmrActions];
|
|
3367
|
+
}
|
|
3368
|
+
if (!Array.isArray(this.hmrActions)) {
|
|
3369
|
+
throw new Error(`[${ this.name }] hmrActions must be the name of an action or an array of names of actions to run when a component is hot-reloaded`)
|
|
3370
|
+
}
|
|
3371
|
+
if (this.hmrActions.some(action => typeof action !== 'string')) {
|
|
3372
|
+
throw new Error(`[${ this.name }] hmrActions must be the name of an action or an array of names of actions to run when a component is hot-reloaded`)
|
|
3373
|
+
}
|
|
3374
|
+
this.hmrAction$ = xs$1.fromArray(this.hmrActions.map(action => ({ type: action })));
|
|
3375
|
+
}
|
|
3376
|
+
|
|
3343
3377
|
initAction$() {
|
|
3344
3378
|
const requestSource = (this.sources && this.sources[this.requestSourceName]) || null;
|
|
3345
3379
|
|
|
@@ -3359,11 +3393,12 @@ class Component {
|
|
|
3359
3393
|
|
|
3360
3394
|
const action$ = ((runner instanceof xs$1.Stream) ? runner : (runner.apply && runner(this.sources) || xs$1.never()));
|
|
3361
3395
|
const bootstrap$ = xs$1.of({ type: BOOTSTRAP_ACTION }).compose(_default$4(10));
|
|
3362
|
-
const
|
|
3396
|
+
const hmrAction$ = window?.__SYGNAL_HMR_UPDATING === true ? this.hmrAction$ : xs$1.of().filter(_ => false);
|
|
3397
|
+
const wrapped$ = (this.model[BOOTSTRAP_ACTION] && window?.__SYGNAL_HMR_UPDATING !== true) ? _default$3(bootstrap$, action$) : _default$3(xs$1.of().compose(_default$4(1)).filter(_ => false), hmrAction$, action$);
|
|
3363
3398
|
|
|
3364
3399
|
|
|
3365
3400
|
let initialApiData;
|
|
3366
|
-
if (requestSource && typeof requestSource.select == 'function') {
|
|
3401
|
+
if (window?.__SYGNAL_HMR_UPDATING !== true && requestSource && typeof requestSource.select == 'function') {
|
|
3367
3402
|
initialApiData = requestSource.select('initial')
|
|
3368
3403
|
.flatten();
|
|
3369
3404
|
} else {
|
|
@@ -3377,12 +3412,12 @@ class Component {
|
|
|
3377
3412
|
}
|
|
3378
3413
|
|
|
3379
3414
|
initState() {
|
|
3380
|
-
if (this.model
|
|
3415
|
+
if (this.model !== undefined) {
|
|
3381
3416
|
if (this.model[INITIALIZE_ACTION] === undefined) {
|
|
3382
3417
|
this.model[INITIALIZE_ACTION] = {
|
|
3383
3418
|
[this.stateSourceName]: (_, data) => ({ ...this.addCalculated(data) })
|
|
3384
3419
|
};
|
|
3385
|
-
} else {
|
|
3420
|
+
} else if (isObj(this.model[INITIALIZE_ACTION])) {
|
|
3386
3421
|
Object.keys(this.model[INITIALIZE_ACTION]).forEach(name => {
|
|
3387
3422
|
if (name !== this.stateSourceName) {
|
|
3388
3423
|
console.warn(`${ INITIALIZE_ACTION } can only be used with the ${ this.stateSourceName } source... disregarding ${ name }`);
|
|
@@ -3400,7 +3435,7 @@ class Component {
|
|
|
3400
3435
|
}
|
|
3401
3436
|
|
|
3402
3437
|
const state$ = this.sources[this.stateSourceName]?.stream.startWith({}).compose(_default$5(objIsEqual)) || xs$1.never();
|
|
3403
|
-
const parentContext$ = this.sources.__parentContext
|
|
3438
|
+
const parentContext$ = this.sources.__parentContext$?.startWith({}).compose(_default$5(objIsEqual)) || xs$1.of({});
|
|
3404
3439
|
if (this.context && !isObj(this.context)) {
|
|
3405
3440
|
console.error(`[${this.name}] Context must be an object mapping names to values of functions: ignoring provided ${ typeof this.context }`);
|
|
3406
3441
|
}
|
|
@@ -3448,7 +3483,7 @@ class Component {
|
|
|
3448
3483
|
if (this.isSubComponent && this.initialState) {
|
|
3449
3484
|
console.warn(`[${ this.name }] Initial state provided to sub-component. This will overwrite any state provided by the parent component.`);
|
|
3450
3485
|
}
|
|
3451
|
-
const shimmed$ = this.initialState ? _default$3(xs$1.of(initial), this.action$).compose(_default$4(0)) : this.action$;
|
|
3486
|
+
const shimmed$ = (this.initialState && window?.__SYGNAL_HMR_UPDATING !== true) ? _default$3(xs$1.of(initial), this.action$).compose(_default$4(0)) : this.action$;
|
|
3452
3487
|
const onState = () => this.makeOnAction(shimmed$, true, this.action$);
|
|
3453
3488
|
const onNormal = () => this.makeOnAction(this.action$, false, this.action$);
|
|
3454
3489
|
|
|
@@ -3590,7 +3625,11 @@ class Component {
|
|
|
3590
3625
|
const renderParameters$ = this.collectRenderParameters();
|
|
3591
3626
|
|
|
3592
3627
|
this.vdom$ = renderParameters$
|
|
3593
|
-
.map(
|
|
3628
|
+
.map(params => {
|
|
3629
|
+
const { props, state, children, context, ...peers } = params;
|
|
3630
|
+
const { sygnalFactory, sygnalOptions, ...sanitizedProps} = props || {};
|
|
3631
|
+
return this.view({ ...sanitizedProps, state, children, context, peers }, state, context, peers)
|
|
3632
|
+
})
|
|
3594
3633
|
.compose(this.log('View rendered'))
|
|
3595
3634
|
.map(vDom => vDom || { sel: 'div', data: {}, children: [] })
|
|
3596
3635
|
.compose(this.instantiateSubComponents.bind(this))
|
|
@@ -3633,20 +3672,22 @@ class Component {
|
|
|
3633
3672
|
this.log(`<${ name }> Triggered a next() action: <${ type }> ${ delay }ms delay`, true);
|
|
3634
3673
|
};
|
|
3635
3674
|
|
|
3636
|
-
const
|
|
3675
|
+
const props = { ...this.currentProps, children: this.currentChildren, context: this.currentContext };
|
|
3637
3676
|
|
|
3638
3677
|
let data = action.data;
|
|
3639
3678
|
if (isStateSink) {
|
|
3640
3679
|
return (state) => {
|
|
3641
3680
|
const _state = this.isSubComponent ? this.currentState : state;
|
|
3642
3681
|
const enhancedState = this.addCalculated(_state);
|
|
3643
|
-
|
|
3644
|
-
|
|
3682
|
+
props.state = enhancedState;
|
|
3683
|
+
const newState = reducer(enhancedState, data, next, props);
|
|
3684
|
+
if (newState == ABORT$1) return _state
|
|
3645
3685
|
return this.cleanupCalculated(newState)
|
|
3646
3686
|
}
|
|
3647
3687
|
} else {
|
|
3648
3688
|
const enhancedState = this.addCalculated(this.currentState);
|
|
3649
|
-
|
|
3689
|
+
props.state = enhancedState;
|
|
3690
|
+
const reduced = reducer(enhancedState, data, next, props);
|
|
3650
3691
|
const type = typeof reduced;
|
|
3651
3692
|
if (isObj(reduced) || ['string', 'number', 'boolean', 'function'].includes(type)) return reduced
|
|
3652
3693
|
if (type == 'undefined') {
|
|
@@ -3655,7 +3696,7 @@ class Component {
|
|
|
3655
3696
|
}
|
|
3656
3697
|
throw new Error(`Invalid reducer type for ${ name } ${ type }`)
|
|
3657
3698
|
}
|
|
3658
|
-
}).filter(result => result != ABORT);
|
|
3699
|
+
}).filter(result => result != ABORT$1);
|
|
3659
3700
|
} else if (reducer === undefined || reducer === true) {
|
|
3660
3701
|
returnStream$ = filtered$.map(({data}) => data);
|
|
3661
3702
|
} else {
|
|
@@ -3914,8 +3955,8 @@ class Component {
|
|
|
3914
3955
|
} else {
|
|
3915
3956
|
const name = collectionOf.componentName || collectionOf.label || collectionOf.name || 'FUNCTION_COMPONENT';
|
|
3916
3957
|
const view = collectionOf;
|
|
3917
|
-
const { model, intent, context, peers, components, initialState, calculated, storeCalculatedInState, DOMSourceName, stateSourceName, debug } = collectionOf;
|
|
3918
|
-
const options = { name, view, model, intent, context, peers, components, initialState, calculated, storeCalculatedInState, DOMSourceName, stateSourceName, debug };
|
|
3958
|
+
const { model, intent, hmrActions, context, peers, components, initialState, calculated, storeCalculatedInState, DOMSourceName, stateSourceName, debug } = collectionOf;
|
|
3959
|
+
const options = { name, view, model, intent, hmrActions, context, peers, components, initialState, calculated, storeCalculatedInState, DOMSourceName, stateSourceName, debug };
|
|
3919
3960
|
factory = component(options);
|
|
3920
3961
|
}
|
|
3921
3962
|
} else if (this.components[collectionOf]) {
|
|
@@ -4066,8 +4107,8 @@ class Component {
|
|
|
4066
4107
|
if (!current.isSygnalComponent) {
|
|
4067
4108
|
const name = current.componentName || current.label || current.name || 'FUNCTION_COMPONENT';
|
|
4068
4109
|
const view = current;
|
|
4069
|
-
const { model, intent, context, peers, components, initialState, calculated, storeCalculatedInState, DOMSourceName, stateSourceName, debug } = current;
|
|
4070
|
-
const options = { name, view, model, intent, context, peers, components, initialState, calculated, storeCalculatedInState, DOMSourceName, stateSourceName, debug };
|
|
4110
|
+
const { model, intent, hmrActions, context, peers, components, initialState, calculated, storeCalculatedInState, DOMSourceName, stateSourceName, debug } = current;
|
|
4111
|
+
const options = { name, view, model, intent, hmrActions, context, peers, components, initialState, calculated, storeCalculatedInState, DOMSourceName, stateSourceName, debug };
|
|
4071
4112
|
switchableComponents[key] = component(options);
|
|
4072
4113
|
}
|
|
4073
4114
|
});
|
|
@@ -4682,34 +4723,66 @@ function logDriver(out$) {
|
|
|
4682
4723
|
}
|
|
4683
4724
|
|
|
4684
4725
|
function run(app, drivers={}, options={}) {
|
|
4685
|
-
const { mountPoint='#root', fragments=true } = options;
|
|
4726
|
+
const { mountPoint='#root', fragments=true, useDefaultDrivers=true } = options;
|
|
4686
4727
|
if (!app.isSygnalComponent) {
|
|
4687
4728
|
const name = app.name || app.componentName || app.label || "FUNCTIONAL_COMPONENT";
|
|
4688
4729
|
const view = app;
|
|
4689
|
-
const { model, intent, context, peers, components, initialState, calculated, storeCalculatedInState, DOMSourceName, stateSourceName, debug } = app;
|
|
4690
|
-
const options = { name, view, model, intent, context, peers, components, initialState, calculated, storeCalculatedInState, DOMSourceName, stateSourceName, debug };
|
|
4730
|
+
const { model, intent, hmrActions, context, peers, components, initialState, calculated, storeCalculatedInState, DOMSourceName, stateSourceName, debug } = app;
|
|
4731
|
+
const options = { name, view, model, intent, hmrActions, context, peers, components, initialState, calculated, storeCalculatedInState, DOMSourceName, stateSourceName, debug };
|
|
4691
4732
|
|
|
4692
4733
|
app = component(options);
|
|
4693
4734
|
}
|
|
4694
4735
|
|
|
4695
4736
|
const wrapped = state.withState(app, 'STATE');
|
|
4696
4737
|
|
|
4697
|
-
const baseDrivers = {
|
|
4738
|
+
const baseDrivers = useDefaultDrivers ? {
|
|
4698
4739
|
EVENTS: eventBusDriver,
|
|
4699
4740
|
DOM: dom.makeDOMDriver(mountPoint, { snabbdomOptions: { experimental: { fragments } } }),
|
|
4700
4741
|
LOG: logDriver
|
|
4701
|
-
};
|
|
4742
|
+
} : {};
|
|
4702
4743
|
|
|
4703
4744
|
const combinedDrivers = { ...baseDrivers, ...drivers };
|
|
4704
4745
|
|
|
4705
|
-
|
|
4746
|
+
const { sources, sinks, run: _run } = run$1.setup(wrapped, combinedDrivers);
|
|
4747
|
+
const dispose = _run();
|
|
4748
|
+
|
|
4749
|
+
const exposed = { sources, sinks, dispose };
|
|
4750
|
+
|
|
4751
|
+
const hmr = (newComponent) => {
|
|
4752
|
+
exposed.sinks.STATE.shamefullySendNext((state) => {
|
|
4753
|
+
window.__SYGNAL_HMR_UPDATING = true;
|
|
4754
|
+
exposed.dispose();
|
|
4755
|
+
const App = newComponent.default;
|
|
4756
|
+
App.initialState = state;
|
|
4757
|
+
const updated = run(App, drivers);
|
|
4758
|
+
exposed.sources = updated.sources;
|
|
4759
|
+
exposed.sinks = updated.sinks;
|
|
4760
|
+
exposed.dispose = updated.dispose;
|
|
4761
|
+
updated.sinks.STATE.shamefullySendNext(() => {
|
|
4762
|
+
return { ...state }
|
|
4763
|
+
});
|
|
4764
|
+
updated.sources.STATE.stream.setDebugListener({
|
|
4765
|
+
next: () => {
|
|
4766
|
+
exposed.sources.STATE.stream.setDebugListener(null);
|
|
4767
|
+
setTimeout(() => {
|
|
4768
|
+
window.__SYGNAL_HMR_UPDATING = false;
|
|
4769
|
+
}, 100);
|
|
4770
|
+
}
|
|
4771
|
+
});
|
|
4772
|
+
return ABORT
|
|
4773
|
+
});
|
|
4774
|
+
};
|
|
4775
|
+
|
|
4776
|
+
exposed.hmr = hmr;
|
|
4777
|
+
|
|
4778
|
+
return exposed
|
|
4706
4779
|
}
|
|
4707
4780
|
|
|
4708
4781
|
/**
|
|
4709
4782
|
* return a validated and properly separated string of CSS class names from any number of strings, arrays, and objects
|
|
4710
4783
|
*
|
|
4711
|
-
* @param {...String|Array|Object} args any number of strings or arrays with valid CSS class names, or objects where the keys are valid class names and the values evaluate to true or false
|
|
4712
|
-
* @return {String} list of `active` classes separated by spaces
|
|
4784
|
+
* @param { ...String | Array | Object } args any number of strings or arrays with valid CSS class names, or objects where the keys are valid class names and the values evaluate to true or false
|
|
4785
|
+
* @return { String } list of `active` classes separated by spaces
|
|
4713
4786
|
*
|
|
4714
4787
|
* any `string` or `array` arguments are simply validated and appended to the result
|
|
4715
4788
|
* `objects` will evaluate the values (which can be booleans or functions), and the keys with `thruthy` values will be validated and appended to the result
|
|
@@ -5071,7 +5144,9 @@ sampleCombine = function sampleCombine() {
|
|
|
5071
5144
|
var _default = sampleCombine$1.default = sampleCombine;
|
|
5072
5145
|
|
|
5073
5146
|
exports.xs = xs$1;
|
|
5074
|
-
exports.ABORT = ABORT;
|
|
5147
|
+
exports.ABORT = ABORT$1;
|
|
5148
|
+
exports.Collection = Collection;
|
|
5149
|
+
exports.Switchable = Switchable;
|
|
5075
5150
|
exports.classes = classes;
|
|
5076
5151
|
exports.collection = collection;
|
|
5077
5152
|
exports.component = component;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/// <reference path="../src/sygnal.d.ts" />
|
|
2
|
+
import { Stream } from 'xstream';
|
|
3
|
+
export { MemoryStream, Stream } from 'xstream';
|
|
4
|
+
import { MainDOMSource } from '@cycle/dom';
|
|
5
|
+
import { StateSource } from '@cycle/state';
|
|
6
|
+
|
|
7
|
+
type ABORT = '~#~#~ABORT~#~#~'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A function that takes component properties and returns a JSX element.
|
|
11
|
+
*
|
|
12
|
+
* @template STATE - State type
|
|
13
|
+
* @template PROPS - Props type
|
|
14
|
+
* @template CONTEXT - Context type
|
|
15
|
+
*
|
|
16
|
+
* @param { PROPS & { state?: STATE, children?: JSX.Element | JSX.Element[], context?: CONTEXT } } props - Component props augmented with the current state, children, context, and any peers
|
|
17
|
+
* @returns { JSX.Element } The JSX element rendered by the component.
|
|
18
|
+
*/
|
|
19
|
+
type ComponentProps<STATE, PROPS, CONTEXT> = (
|
|
20
|
+
props: PROPS & { state?: STATE, children?: JSX.Element | JSX.Element[], context?: CONTEXT },
|
|
21
|
+
state: STATE,
|
|
22
|
+
context: CONTEXT,
|
|
23
|
+
peers: { [peer: string]: JSX.Element | JSX.Element[] }
|
|
24
|
+
) => JSX.Element;
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
type NextFunction<ACTIONS=any> = ACTIONS extends object
|
|
28
|
+
? <ACTION_KEY extends keyof ACTIONS>(
|
|
29
|
+
action: ACTION_KEY,
|
|
30
|
+
data?: ACTIONS[ACTION_KEY],
|
|
31
|
+
delay?: number
|
|
32
|
+
) => void
|
|
33
|
+
: (action: string, data?: any, delay?: number) => void;
|
|
34
|
+
|
|
35
|
+
type Reducer<STATE, PROPS, ACTIONS = any, DATA = any, RETURN = any> = (
|
|
36
|
+
state: STATE,
|
|
37
|
+
args: DATA,
|
|
38
|
+
next: NextFunction<ACTIONS>,
|
|
39
|
+
props: PROPS
|
|
40
|
+
) => RETURN | ABORT | undefined;
|
|
41
|
+
|
|
42
|
+
type StateOnlyReducer<STATE, RETURN = any> = (
|
|
43
|
+
state: STATE
|
|
44
|
+
) => RETURN | ABORT;
|
|
45
|
+
|
|
46
|
+
type Event<DATA=any> = { type: string, data: DATA }
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Valid values for a sink
|
|
50
|
+
*
|
|
51
|
+
* - true: Whatever value is received from the intent for this action is passed on as-is.
|
|
52
|
+
* - Function: A reducer
|
|
53
|
+
*/
|
|
54
|
+
type SinkValue<STATE, PROPS, ACTIONS, DATA, RETURN> = true | Reducer<STATE, PROPS, ACTIONS, DATA, RETURN>
|
|
55
|
+
|
|
56
|
+
type DefaultSinks<STATE, PROPS, ACTIONS, DATA> = {
|
|
57
|
+
STATE?: SinkValue<STATE, PROPS, ACTIONS, DATA, STATE>;
|
|
58
|
+
EVENTS?: SinkValue<STATE, PROPS, ACTIONS, DATA, Event>;
|
|
59
|
+
LOG?: SinkValue<STATE, PROPS, ACTIONS, DATA, any>;
|
|
60
|
+
PARENT?: SinkValue<STATE, PROPS, ACTIONS, DATA, any>;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
type CustomDriverSinks<STATE, PROPS, DRIVERS, ACTIONS, ACTION_ENTRY> = keyof DRIVERS extends never ? {
|
|
64
|
+
[driver: string]: SinkValue<STATE, PROPS, ACTIONS, any, any>;
|
|
65
|
+
} : {
|
|
66
|
+
[DRIVER_KEY in keyof DRIVERS]: SinkValue<STATE, PROPS, ACTIONS, ACTION_ENTRY, DRIVERS[DRIVER_KEY]>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
type ModelEntry<STATE, PROPS, DRIVERS, ACTIONS, ACTION_ENTRY> = SinkValue<STATE, PROPS, ACTIONS, ACTION_ENTRY, STATE> | Partial<DefaultSinks<STATE, PROPS, ACTIONS, ACTION_ENTRY> & CustomDriverSinks<STATE, PROPS, DRIVERS, ACTIONS, ACTION_ENTRY>>;
|
|
70
|
+
|
|
71
|
+
type WithDefaultActions<STATE, ACTIONS> = ACTIONS & {
|
|
72
|
+
BOOTSTRAP?: never;
|
|
73
|
+
INITIALIZE?: STATE;
|
|
74
|
+
HYDRATE?: any;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
type ComponentModel<STATE, PROPS, DRIVERS, ACTIONS> = keyof ACTIONS extends never ? {
|
|
78
|
+
[action: string]: ModelEntry<STATE, PROPS, DRIVERS, WithDefaultActions<STATE, ACTIONS>, any>;
|
|
79
|
+
} : {
|
|
80
|
+
[ACTION_KEY in keyof WithDefaultActions<STATE, ACTIONS>]?: ModelEntry<STATE, PROPS, DRIVERS, WithDefaultActions<STATE, ACTIONS>, WithDefaultActions<STATE, ACTIONS>[ACTION_KEY]>;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
type ChildSource = {
|
|
84
|
+
select: (type: string) => Stream<any>
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
type DefaultDrivers<STATE, EVENTS=any> = {
|
|
88
|
+
STATE: {
|
|
89
|
+
source: StateSource<STATE>;
|
|
90
|
+
sink: STATE;
|
|
91
|
+
};
|
|
92
|
+
DOM: {
|
|
93
|
+
source: MainDOMSource;
|
|
94
|
+
sink: never;
|
|
95
|
+
};
|
|
96
|
+
EVENTS: {
|
|
97
|
+
source: Stream<Event<EVENTS>>;
|
|
98
|
+
sink: EVENTS;
|
|
99
|
+
};
|
|
100
|
+
LOG: {
|
|
101
|
+
source: never;
|
|
102
|
+
sink: any;
|
|
103
|
+
}
|
|
104
|
+
CHILD: {
|
|
105
|
+
source: ChildSource;
|
|
106
|
+
sink: never;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
type Sources<DRIVERS> = {
|
|
111
|
+
[DRIVER_KEY in keyof DRIVERS]: DRIVERS[DRIVER_KEY] extends { source: infer SOURCE } ? SOURCE : never;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
type Actions<ACTIONS> = keyof ACTIONS extends never ? {
|
|
115
|
+
[action: string]: Stream<any>;
|
|
116
|
+
} : {
|
|
117
|
+
[ACTION_KEY in keyof ACTIONS]: Stream<ACTIONS[ACTION_KEY]>;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
type CombinedSources<STATE, DRIVERS> = Sources<DefaultDrivers<STATE> & DRIVERS>;
|
|
121
|
+
|
|
122
|
+
interface ComponentIntent<STATE, DRIVERS, ACTIONS> {
|
|
123
|
+
(args: CombinedSources<STATE, DRIVERS>): Partial<Actions<ACTIONS>>;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
type Calculated<STATE, CALCULATED> = keyof CALCULATED extends never ? {
|
|
127
|
+
[field: string]: boolean | StateOnlyReducer<STATE, any>;
|
|
128
|
+
} : {
|
|
129
|
+
[CALCULATED_KEY in keyof CALCULATED]: boolean | StateOnlyReducer<STATE, CALCULATED[CALCULATED_KEY]>;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
type Context<STATE, CONTEXT> = keyof CONTEXT extends never ? {
|
|
133
|
+
[field: string]: boolean | StateOnlyReducer<STATE, any>;
|
|
134
|
+
} : {
|
|
135
|
+
[CONTEXT_KEY in keyof CONTEXT]: boolean | StateOnlyReducer<STATE, CONTEXT[CONTEXT_KEY]>;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
type Lense<PARENT_STATE=any, CHILD_STATE=any> = {
|
|
139
|
+
get: (state: PARENT_STATE) => CHILD_STATE;
|
|
140
|
+
set: (state: PARENT_STATE, childState: CHILD_STATE) => PARENT_STATE;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
type Filter<ARRAY=any[]> = (array: ARRAY) => ARRAY
|
|
144
|
+
|
|
145
|
+
type SortFunction<ITEM=any> = (a: ITEM, b: ITEM) => number
|
|
146
|
+
type SortObject<ITEM=any> = {
|
|
147
|
+
[field: string]: 'asc' | 'dec' | SortFunction<ITEM>;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
type FixDrivers<DRIVERS> =
|
|
151
|
+
0 extends (1 & DRIVERS)
|
|
152
|
+
? {}
|
|
153
|
+
: DRIVERS extends object
|
|
154
|
+
? DRIVERS
|
|
155
|
+
: {};
|
|
156
|
+
/**
|
|
157
|
+
* Sygnal Component
|
|
158
|
+
* @template STATE - State
|
|
159
|
+
* @template PROPS - Props (from JSX element)
|
|
160
|
+
* @template DRIVERS - Custom Drivers (default drivers are automatically applied)
|
|
161
|
+
* @template ACTIONS - Actions (key = action name; value = type expected for Observable values for that action)
|
|
162
|
+
* @template CALCULATED - Calculated state values (key = calculated variable name; value = type of the calculated variable)
|
|
163
|
+
* @template CONTEXT - Context (key = context variable name; value = type of the context variable)
|
|
164
|
+
*/
|
|
165
|
+
type Component<STATE=any, PROPS={[prop: string]: any}, DRIVERS={}, ACTIONS={}, CALCULATED={}, CONTEXT={}> = ComponentProps<STATE, PROPS, CONTEXT> & {
|
|
166
|
+
label?: string;
|
|
167
|
+
DOMSourceName?: string;
|
|
168
|
+
stateSourceName?: string;
|
|
169
|
+
requestSourceName?: string;
|
|
170
|
+
model?: ComponentModel<STATE, PROPS, FixDrivers<DRIVERS>, ACTIONS>;
|
|
171
|
+
intent?: ComponentIntent<STATE, FixDrivers<DRIVERS>, ACTIONS>;
|
|
172
|
+
initialState?: STATE;
|
|
173
|
+
calculated?: Calculated<STATE, CALCULATED>;
|
|
174
|
+
storeCalculatedInState?: boolean;
|
|
175
|
+
context?: Context<STATE, CONTEXT>;
|
|
176
|
+
peers?: { [name: string]: Component };
|
|
177
|
+
components?: { [name: string]: Component };
|
|
178
|
+
debug?: boolean;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Sygnal Root Component
|
|
183
|
+
* @template STATE - State
|
|
184
|
+
* @template PROPS - Props (from JSX element)
|
|
185
|
+
* @template DRIVERS - Custom Drivers (default drivers are automatically applied)
|
|
186
|
+
* @template ACTIONS - Actions (key = action name; value = type expected for Observable values for that action)
|
|
187
|
+
* @template CALCULATED - Calculated state values (key = calculated variable name; value = type of the calculated variable)
|
|
188
|
+
* @template CONTEXT - Context (key = context variable name; value = type of the context variable)
|
|
189
|
+
*/
|
|
190
|
+
type RootComponent<STATE=any, DRIVERS={}, ACTIONS=any, CALCULATED=any, CONTEXT=any> = Component<STATE, any, DRIVERS, ACTIONS, CALCULATED, CONTEXT>
|
|
191
|
+
|
|
192
|
+
type CollectionProps<PROPS=any> = {
|
|
193
|
+
of: any;
|
|
194
|
+
from: string | Lense;
|
|
195
|
+
filter?: Filter;
|
|
196
|
+
sort?: string | SortFunction | SortObject;
|
|
197
|
+
} & Omit<PROPS, 'of' | 'from' | 'filter' | 'sort'>;
|
|
198
|
+
|
|
199
|
+
type SwitchableProps<PROPS=any> = {
|
|
200
|
+
of: any;
|
|
201
|
+
current: string;
|
|
202
|
+
state?: string | Lense;
|
|
203
|
+
} & Omit<PROPS, 'of' | 'state' | 'current'>;
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* JSX Types
|
|
207
|
+
*/
|
|
208
|
+
declare global {
|
|
209
|
+
namespace JSX {
|
|
210
|
+
interface IntrinsicElements {
|
|
211
|
+
[elemName: string]: any;
|
|
212
|
+
collection: CollectionProps<any>;
|
|
213
|
+
switchable: SwitchableProps<any>;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
interface Element {
|
|
217
|
+
children?: JSX.Element;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export type { CollectionProps, Component, Event, Filter, Lense, RootComponent, SortFunction, SortObject, SwitchableProps };
|