sygnal 2.7.0 → 2.8.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/dist/index.cjs.js CHANGED
@@ -3186,6 +3186,9 @@ function component (opts) {
3186
3186
  returnFunction = currySources ? (sources) => (new Component({ ...opts, sources })).sinks : (new Component(opts)).sinks;
3187
3187
  }
3188
3188
 
3189
+ returnFunction.componentName = name;
3190
+ returnFunction.isSygnalComponent = true;
3191
+
3189
3192
  return returnFunction
3190
3193
  }
3191
3194
 
@@ -3200,6 +3203,7 @@ class Component {
3200
3203
  // intent
3201
3204
  // request
3202
3205
  // model
3206
+ // context
3203
3207
  // response
3204
3208
  // view
3205
3209
  // children
@@ -3217,14 +3221,17 @@ class Component {
3217
3221
  // intent$
3218
3222
  // action$
3219
3223
  // model$
3224
+ // context$
3220
3225
  // response$
3221
3226
  // sendResponse$
3222
3227
  // children$
3223
3228
  // vdom$
3229
+ // currentState
3230
+ // currentContext
3224
3231
  // subComponentSink$
3225
3232
  // unmountRequest$
3226
3233
  // unmount()
3227
- // stateCache
3234
+ // _debug
3228
3235
 
3229
3236
  // [ INSTANTIATED STREAM OPERATOR ]
3230
3237
  // log
@@ -3232,7 +3239,7 @@ class Component {
3232
3239
  // [ OUTPUT ]
3233
3240
  // sinks
3234
3241
 
3235
- constructor({ name='NO NAME', sources, intent, request, model, response, view, children={}, components={}, initialState, calculated, storeCalculatedInState=true, DOMSourceName='DOM', stateSourceName='STATE', requestSourceName='HTTP', debug=false }) {
3242
+ constructor({ name='NO NAME', sources, intent, request, model, context, response, view, children={}, components={}, initialState, calculated, storeCalculatedInState=true, DOMSourceName='DOM', stateSourceName='STATE', requestSourceName='HTTP', debug=false }) {
3236
3243
  if (!sources || typeof sources != 'object') throw new Error('Missing or invalid sources')
3237
3244
 
3238
3245
  this.name = name;
@@ -3240,6 +3247,7 @@ class Component {
3240
3247
  this.intent = intent;
3241
3248
  this.request = request;
3242
3249
  this.model = model;
3250
+ this.context = context;
3243
3251
  this.response = response;
3244
3252
  this.view = view;
3245
3253
  this.children = children;
@@ -3259,7 +3267,7 @@ class Component {
3259
3267
 
3260
3268
  if (state$) {
3261
3269
  this.currentState = initialState || {};
3262
- this.sources[this.stateSourceName] = new state.StateSource(state$.map(val => {
3270
+ this.sources[stateSourceName] = new state.StateSource(state$.map(val => {
3263
3271
  this.currentState = val;
3264
3272
  return val
3265
3273
  }));
@@ -3282,6 +3290,7 @@ class Component {
3282
3290
  this.initAction$();
3283
3291
  this.initResponse$();
3284
3292
  this.initState();
3293
+ this.initContext();
3285
3294
  this.initModel$();
3286
3295
  this.initSendResponse$();
3287
3296
  this.initChildren$();
@@ -3455,6 +3464,61 @@ class Component {
3455
3464
  }
3456
3465
  }
3457
3466
 
3467
+ initContext() {
3468
+ if (!this.context && !this.sources.__parentContext$) {
3469
+ this.context$ = xs$1.of({});
3470
+ return
3471
+ }
3472
+ const repeatChecker = (a, b) => {
3473
+ if (a === b) return true
3474
+ if (typeof a !== 'object' || typeof b !== 'object') {
3475
+ return a === b
3476
+ }
3477
+ const entriesA = Object.entries(a);
3478
+ const entriesB = Object.entries(b);
3479
+ if (entriesA.length === 0 && entriesB.length === 0) return true
3480
+ if (entriesA.length !== entriesB.length) return false
3481
+ return entriesA.every(([name, value]) => {
3482
+ return b[name] === value
3483
+ })
3484
+ };
3485
+
3486
+ const state$ = this.sources[this.stateSourceName]?.stream.startWith({}).compose(_default$5(repeatChecker)) || xs$1.never();
3487
+ const parentContext$ = this.sources.__parentContext$.startWith({}).compose(_default$5(repeatChecker)) || xs$1.of({});
3488
+ if (this.context && typeof this.context !== 'object') {
3489
+ console.error(`[${this.name}] Context must be an object mapping names to values of functions: ignoring provided ${ typeof this.context }`);
3490
+ }
3491
+ this.context$ = xs$1.combine(state$, parentContext$)
3492
+ .map(([_, parent]) => {
3493
+ const _parent = typeof parent === 'object' ? parent : {};
3494
+ const context = typeof this.context === 'object' ? this.context : {};
3495
+ const state = this.currentState;
3496
+ const values = Object.entries(context).reduce((acc, current) => {
3497
+ const [name, value] = current;
3498
+ let _value;
3499
+ const valueType = typeof value;
3500
+ if (valueType === 'string') {
3501
+ _value = state[value];
3502
+ } else if (valueType === 'boolean') {
3503
+ _value = state[name];
3504
+ } else if (valueType === 'function') {
3505
+ _value = value(state);
3506
+ } else {
3507
+ console.error(`[${ this.name }] Invalid context entry '${ name }': must be the name of a state property or a function returning a value to use`);
3508
+ return acc
3509
+ }
3510
+ acc[name] = _value;
3511
+ return acc
3512
+ }, {});
3513
+ const newContext = { ..._parent, ...values };
3514
+ this.currentContext = newContext;
3515
+ return newContext
3516
+ })
3517
+ .compose(_default$5(repeatChecker))
3518
+ .startWith({});
3519
+ this.context$.subscribe({ next: _ => _ });
3520
+ }
3521
+
3458
3522
  initModel$() {
3459
3523
  if (typeof this.model == 'undefined') {
3460
3524
  this.model$ = this.sourceNames.reduce((a,s) => {
@@ -3504,7 +3568,7 @@ class Component {
3504
3568
  return `State reducer added: <${ action }>`
3505
3569
  } else {
3506
3570
  const extra = data && (data.type || data.command || data.name || data.key || (Array.isArray(data) && 'Array') || data);
3507
- return `Data sent to [${ sink }]: <${ action }> ${ extra }`
3571
+ return `Data sent to [${ sink }]: <${ action }> ${ JSON.stringify(extra) }`
3508
3572
  }
3509
3573
  }));
3510
3574
 
@@ -3730,7 +3794,7 @@ class Component {
3730
3794
  return acc
3731
3795
  }, {});
3732
3796
 
3733
- const newState = { ...state, ...calculated };
3797
+ const newState = { ...state, ...calculated, __context: this.currentContext };
3734
3798
 
3735
3799
  lastState = state;
3736
3800
  lastResult = newState;
@@ -3742,7 +3806,7 @@ class Component {
3742
3806
  cleanupCalculated(incomingState) {
3743
3807
  if (!incomingState || typeof incomingState !== 'object' || incomingState instanceof Array) return incomingState
3744
3808
  const state = this.storeCalculatedInState ? this.addCalculated(incomingState) : incomingState;
3745
- const { __props, __children, ...sanitized } = state;
3809
+ const { __props, __children, __context, ...sanitized } = state;
3746
3810
  const copy = { ...sanitized };
3747
3811
  if (!this.calculated) return copy
3748
3812
  const keys = Object.keys(this.calculated);
@@ -3764,10 +3828,10 @@ class Component {
3764
3828
  const stateStream = (enhancedState && enhancedState.stream) || xs$1.never();
3765
3829
 
3766
3830
  const objRepeatChecker = (a, b) => {
3767
- const { state, sygnalFactory, __props, __children, ...sanitized } = a;
3831
+ const { state, sygnalFactory, __props, __children, __context, ...sanitized } = a;
3768
3832
  const keys = Object.keys(sanitized);
3769
3833
  if (keys.length === 0) {
3770
- const { state, sygnalFactory, __props, __children, ...sanitizedB } = b;
3834
+ const { state, sygnalFactory, __props, __children, __context, ...sanitizedB } = b;
3771
3835
  return Object.keys(sanitizedB).length === 0
3772
3836
  }
3773
3837
  return keys.every(key => a[key] === b[key])
@@ -3792,6 +3856,10 @@ class Component {
3792
3856
  renderParams.__children = this.sources.children$.compose(_default$5(arrRepeatChecker));
3793
3857
  }
3794
3858
 
3859
+ if (this.context$) {
3860
+ renderParams.__context = this.context$.compose(_default$5(objRepeatChecker));
3861
+ }
3862
+
3795
3863
  const names = [];
3796
3864
  const streams = [];
3797
3865
 
@@ -3803,18 +3871,18 @@ class Component {
3803
3871
  const combined = xs$1.combine(...streams)
3804
3872
  // map the streams from an array back to an object with the render parameter names as the keys
3805
3873
  .map(arr => {
3806
- return names.reduce((acc, name, index) => {
3874
+ const params = names.reduce((acc, name, index) => {
3807
3875
  acc[name] = arr[index];
3808
3876
  if (name === 'state') acc[this.stateSourceName] = arr[index];
3809
3877
  return acc
3810
- }, {})
3878
+ }, {});
3879
+ return params
3811
3880
  });
3812
3881
 
3813
3882
  return combined
3814
3883
  }
3815
3884
 
3816
3885
  instantiateSubComponents(vDom$) {
3817
- let count = 0;
3818
3886
  return vDom$.fold((previousComponents, vDom) => {
3819
3887
  const componentNames = Object.keys(this.components);
3820
3888
  const foundComponents = getComponents(vDom, componentNames);
@@ -3845,7 +3913,6 @@ class Component {
3845
3913
 
3846
3914
 
3847
3915
  if (previousComponents[id]) {
3848
- if (this.debug) console.log(this.name, 'sameful', count++);
3849
3916
  const entry = previousComponents[id];
3850
3917
  acc[id] = entry;
3851
3918
  entry.props$.shamefullySendNext(props);
@@ -3854,8 +3921,6 @@ class Component {
3854
3921
  return acc
3855
3922
  }
3856
3923
 
3857
- if (this.debug) console.log(this.name, 'non-shameful', count++);
3858
-
3859
3924
  const props$ = xs$1.create().startWith(props);
3860
3925
  const children$ = xs$1.create().startWith(children);
3861
3926
 
@@ -3914,19 +3979,18 @@ class Component {
3914
3979
 
3915
3980
  const combined$ = xs$1.combine(this.sources[this.stateSourceName].stream.startWith(this.currentState), props$, children$)
3916
3981
  .map(([state, __props, __children]) => {
3917
- return typeof state === 'object' ? { ...this.addCalculated(state), __props, __children } : { value: state, __props, __children }
3982
+ return typeof state === 'object' ? { ...this.addCalculated(state), __props, __children, __context: this.currentContext } : { value: state, __props, __children, __context: this.currentContext }
3918
3983
  });
3919
3984
 
3920
3985
  const stateSource = new state.StateSource(combined$);
3921
3986
  const stateField = props.from;
3922
- props.filter;
3923
3987
  let lense;
3924
3988
 
3925
3989
  const factory = typeof props.of === 'function' ? props.of : this.components[props.of];
3926
3990
 
3927
3991
  const sanitizeItems = item => {
3928
3992
  if (typeof item === 'object') {
3929
- const { __props, __children, ...sanitized } = item;
3993
+ const { __props, __children, __context, ...sanitized } = item;
3930
3994
  return sanitized
3931
3995
  } else {
3932
3996
  return item
@@ -3938,7 +4002,7 @@ class Component {
3938
4002
  const { __props, __children } = state;
3939
4003
  if (!Array.isArray(state[stateField])) return []
3940
4004
  return state[stateField].map(item => {
3941
- return typeof item === 'object' ? { ...item, __props, __children } : { value: item, __props, __children }
4005
+ return typeof item === 'object' ? { ...item, __props, __children, __context: this.currentContext } : { value: item, __props, __children, __context: this.currentContext }
3942
4006
  })
3943
4007
  },
3944
4008
  set: (oldState, newState) => {
@@ -3962,7 +4026,7 @@ class Component {
3962
4026
  };
3963
4027
  } else if (typeof stateField === 'string') {
3964
4028
  if (typeof this.currentState === 'object') {
3965
- if(!(stateField in this.currentState) && !(stateField in this.calculated)) {
4029
+ if(!(this.currentState && stateField in this.currentState) && !(this.calculated && stateField in this.calculated)) {
3966
4030
  console.error(`Collection component in ${ this.name } is attempting to use non-existent state property '${ stateField }': To fix this error, specify a valid array property on the state. Attempting to use parent component state.`);
3967
4031
  lense = undefined;
3968
4032
  } else if (!Array.isArray(this.currentState[stateField])) {
@@ -4001,7 +4065,7 @@ class Component {
4001
4065
  lense = undefined;
4002
4066
  }
4003
4067
 
4004
- const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$ };
4068
+ const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$, __parentContext$: this.context$ };
4005
4069
  const sink$ = collection(factory, lense, { container: null })(sources);
4006
4070
  if (typeof sink$ !== 'object') {
4007
4071
  throw new Error('Invalid sinks returned from component factory of collection element')
@@ -4016,7 +4080,7 @@ class Component {
4016
4080
 
4017
4081
  const combined$ = xs$1.combine(this.sources[this.stateSourceName].stream.startWith(this.currentState), props$, children$)
4018
4082
  .map(([state, __props, __children]) => {
4019
- return typeof state === 'object' ? { ...this.addCalculated(state), __props, __children } : { value: state, __props, __children }
4083
+ return typeof state === 'object' ? { ...this.addCalculated(state), __props, __children, __context: this.currentContext } : { value: state, __props, __children, __context: this.currentContext }
4020
4084
  });
4021
4085
 
4022
4086
  const stateSource = new state.StateSource(combined$);
@@ -4026,7 +4090,7 @@ class Component {
4026
4090
  const fieldLense = {
4027
4091
  get: state => {
4028
4092
  const { __props, __children } = state;
4029
- return (typeof state[stateField] === 'object' && !(state[stateField] instanceof Array)) ? { ...state[stateField], __props, __children } : { value: state[stateField], __props, __children }
4093
+ return (typeof state[stateField] === 'object' && !(state[stateField] instanceof Array)) ? { ...state[stateField], __props, __children, __context: this.currentContext } : { value: state[stateField], __props, __children, __context: this.currentContext }
4030
4094
  },
4031
4095
  set: (oldState, newState) => {
4032
4096
  if (this.calculated && stateField in this.calculated) {
@@ -4034,7 +4098,7 @@ class Component {
4034
4098
  return oldState
4035
4099
  }
4036
4100
  if (typeof newState !== 'object' || newState instanceof Array) return { ...oldState, [stateField]: newState }
4037
- const { __props, __children, ...sanitized } = newState;
4101
+ const { __props, __children, __context, ...sanitized } = newState;
4038
4102
  return { ...oldState, [stateField]: sanitized }
4039
4103
  }
4040
4104
  };
@@ -4043,7 +4107,7 @@ class Component {
4043
4107
  get: state => state,
4044
4108
  set: (oldState, newState) => {
4045
4109
  if (typeof newState !== 'object' || newState instanceof Array) return newState
4046
- const { __props, __children, ...sanitized } = newState;
4110
+ const { __props, __children, __context, ...sanitized } = newState;
4047
4111
  return sanitized
4048
4112
  }
4049
4113
  };
@@ -4065,7 +4129,7 @@ class Component {
4065
4129
  }
4066
4130
 
4067
4131
  const switchableComponents = props.of;
4068
- const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$ };
4132
+ const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$, __parentContext$: this.context$ };
4069
4133
 
4070
4134
  const sink$ = isolate(switchable(switchableComponents, props$.map(props => props.current)), { [this.stateSourceName]: lense })(sources);
4071
4135
 
@@ -4084,7 +4148,7 @@ class Component {
4084
4148
 
4085
4149
  const combined$ = xs$1.combine(this.sources[this.stateSourceName].stream.startWith(this.currentState), props$, children$)
4086
4150
  .map(([state, __props, __children]) => {
4087
- return typeof state === 'object' ? { ...this.addCalculated(state), __props, __children } : { value: state, __props, __children }
4151
+ return typeof state === 'object' ? { ...this.addCalculated(state), __props, __children, __context: this.currentContext } : { value: state, __props, __children, __context: this.currentContext }
4088
4152
  });
4089
4153
 
4090
4154
  const stateSource = new state.StateSource(combined$);
@@ -4101,7 +4165,7 @@ class Component {
4101
4165
  const fieldLense = {
4102
4166
  get: state => {
4103
4167
  const { __props, __children } = state;
4104
- return typeof state[stateField] === 'object' ? { ...state[stateField], __props, __children } : { value: state[stateField], __props, __children }
4168
+ return typeof state[stateField] === 'object' ? { ...state[stateField], __props, __children, __context: this.currentContext } : { value: state[stateField], __props, __children, __context: this.currentContext }
4105
4169
  },
4106
4170
  set: (oldState, newState) => {
4107
4171
  if (this.calculated && stateField in this.calculated) {
@@ -4116,7 +4180,7 @@ class Component {
4116
4180
  get: state => state,
4117
4181
  set: (oldState, newState) => {
4118
4182
  if (typeof newState !== 'object' || newState instanceof Array) return newState
4119
- const { __props, __children, ...sanitized } = newState;
4183
+ const { __props, __children, __context, ...sanitized } = newState;
4120
4184
  return sanitized
4121
4185
  }
4122
4186
  };
@@ -4137,7 +4201,7 @@ class Component {
4137
4201
  lense = baseLense;
4138
4202
  }
4139
4203
 
4140
- const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$ };
4204
+ const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$, __parentContext$: this.context$ };
4141
4205
  const sink$ = isolate(factory, { [this.stateSourceName]: lense })(sources);
4142
4206
 
4143
4207
  if (typeof sink$ !== 'object') {
@@ -4232,7 +4296,7 @@ function getComponents(currentElement, componentNames, depth=0, index=0) {
4232
4296
  const sel = currentElement.sel;
4233
4297
  const isCollection = sel && sel.toLowerCase() === 'collection';
4234
4298
  const isSwitchable = sel && sel.toLowerCase() === 'switchable';
4235
- const isComponent = sel && (['collection', 'switchable', 'sygnal-factory', ...componentNames].includes(currentElement.sel)) || typeof currentElement.data?.props?.sygnalFactory === 'function';
4299
+ const isComponent = sel && (['collection', 'switchable', 'sygnal-factory', ...componentNames].includes(sel)) || typeof currentElement.data?.props?.sygnalFactory === 'function';
4236
4300
  const props = (currentElement.data && currentElement.data.props) || {};
4237
4301
  (currentElement.data && currentElement.data.attrs) || {};
4238
4302
  const children = currentElement.children || [];
@@ -4316,9 +4380,9 @@ function getComponentIdFromElement(el, depth, index) {
4316
4380
  base = `${date}-${rand}`;
4317
4381
  selMap.set(sel, base);
4318
4382
  }
4319
- const uid = `${base}-${depth}-${index}`;
4320
- const props = (el.data && el.data.props) || {};
4321
- const id = (props.id && JSON.stringify(props.id)) || uid;
4383
+ const uid = `${base}-${depth}-${index}`;
4384
+ const props = (el.data && el.data.props) || {};
4385
+ const id = (props.id && JSON.stringify(props.id)) || uid;
4322
4386
  const fullId = `${ name }::${ id }`;
4323
4387
  return fullId
4324
4388
  }