sygnal 2.7.1 → 2.8.1

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
@@ -3203,6 +3203,7 @@ class Component {
3203
3203
  // intent
3204
3204
  // request
3205
3205
  // model
3206
+ // context
3206
3207
  // response
3207
3208
  // view
3208
3209
  // children
@@ -3220,10 +3221,13 @@ class Component {
3220
3221
  // intent$
3221
3222
  // action$
3222
3223
  // model$
3224
+ // context$
3223
3225
  // response$
3224
3226
  // sendResponse$
3225
3227
  // children$
3226
3228
  // vdom$
3229
+ // currentState
3230
+ // currentContext
3227
3231
  // subComponentSink$
3228
3232
  // unmountRequest$
3229
3233
  // unmount()
@@ -3235,7 +3239,7 @@ class Component {
3235
3239
  // [ OUTPUT ]
3236
3240
  // sinks
3237
3241
 
3238
- 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 }) {
3239
3243
  if (!sources || typeof sources != 'object') throw new Error('Missing or invalid sources')
3240
3244
 
3241
3245
  this.name = name;
@@ -3243,6 +3247,7 @@ class Component {
3243
3247
  this.intent = intent;
3244
3248
  this.request = request;
3245
3249
  this.model = model;
3250
+ this.context = context;
3246
3251
  this.response = response;
3247
3252
  this.view = view;
3248
3253
  this.children = children;
@@ -3262,7 +3267,7 @@ class Component {
3262
3267
 
3263
3268
  if (state$) {
3264
3269
  this.currentState = initialState || {};
3265
- this.sources[this.stateSourceName] = new state.StateSource(state$.map(val => {
3270
+ this.sources[stateSourceName] = new state.StateSource(state$.map(val => {
3266
3271
  this.currentState = val;
3267
3272
  return val
3268
3273
  }));
@@ -3285,6 +3290,7 @@ class Component {
3285
3290
  this.initAction$();
3286
3291
  this.initResponse$();
3287
3292
  this.initState();
3293
+ this.initContext();
3288
3294
  this.initModel$();
3289
3295
  this.initSendResponse$();
3290
3296
  this.initChildren$();
@@ -3458,6 +3464,61 @@ class Component {
3458
3464
  }
3459
3465
  }
3460
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
+
3461
3522
  initModel$() {
3462
3523
  if (typeof this.model == 'undefined') {
3463
3524
  this.model$ = this.sourceNames.reduce((a,s) => {
@@ -3507,7 +3568,7 @@ class Component {
3507
3568
  return `State reducer added: <${ action }>`
3508
3569
  } else {
3509
3570
  const extra = data && (data.type || data.command || data.name || data.key || (Array.isArray(data) && 'Array') || data);
3510
- return `Data sent to [${ sink }]: <${ action }> ${ extra }`
3571
+ return `Data sent to [${ sink }]: <${ action }> ${ JSON.stringify(extra) }`
3511
3572
  }
3512
3573
  }));
3513
3574
 
@@ -3733,7 +3794,7 @@ class Component {
3733
3794
  return acc
3734
3795
  }, {});
3735
3796
 
3736
- const newState = { ...state, ...calculated };
3797
+ const newState = { ...state, ...calculated, __context: this.currentContext };
3737
3798
 
3738
3799
  lastState = state;
3739
3800
  lastResult = newState;
@@ -3745,7 +3806,7 @@ class Component {
3745
3806
  cleanupCalculated(incomingState) {
3746
3807
  if (!incomingState || typeof incomingState !== 'object' || incomingState instanceof Array) return incomingState
3747
3808
  const state = this.storeCalculatedInState ? this.addCalculated(incomingState) : incomingState;
3748
- const { __props, __children, ...sanitized } = state;
3809
+ const { __props, __children, __context, ...sanitized } = state;
3749
3810
  const copy = { ...sanitized };
3750
3811
  if (!this.calculated) return copy
3751
3812
  const keys = Object.keys(this.calculated);
@@ -3767,10 +3828,10 @@ class Component {
3767
3828
  const stateStream = (enhancedState && enhancedState.stream) || xs$1.never();
3768
3829
 
3769
3830
  const objRepeatChecker = (a, b) => {
3770
- const { state, sygnalFactory, __props, __children, ...sanitized } = a;
3831
+ const { state, sygnalFactory, __props, __children, __context, ...sanitized } = a;
3771
3832
  const keys = Object.keys(sanitized);
3772
3833
  if (keys.length === 0) {
3773
- const { state, sygnalFactory, __props, __children, ...sanitizedB } = b;
3834
+ const { state, sygnalFactory, __props, __children, __context, ...sanitizedB } = b;
3774
3835
  return Object.keys(sanitizedB).length === 0
3775
3836
  }
3776
3837
  return keys.every(key => a[key] === b[key])
@@ -3795,6 +3856,10 @@ class Component {
3795
3856
  renderParams.__children = this.sources.children$.compose(_default$5(arrRepeatChecker));
3796
3857
  }
3797
3858
 
3859
+ if (this.context$) {
3860
+ renderParams.__context = this.context$.compose(_default$5(objRepeatChecker));
3861
+ }
3862
+
3798
3863
  const names = [];
3799
3864
  const streams = [];
3800
3865
 
@@ -3806,11 +3871,12 @@ class Component {
3806
3871
  const combined = xs$1.combine(...streams)
3807
3872
  // map the streams from an array back to an object with the render parameter names as the keys
3808
3873
  .map(arr => {
3809
- return names.reduce((acc, name, index) => {
3874
+ const params = names.reduce((acc, name, index) => {
3810
3875
  acc[name] = arr[index];
3811
3876
  if (name === 'state') acc[this.stateSourceName] = arr[index];
3812
3877
  return acc
3813
- }, {})
3878
+ }, {});
3879
+ return params
3814
3880
  });
3815
3881
 
3816
3882
  return combined
@@ -3913,7 +3979,7 @@ class Component {
3913
3979
 
3914
3980
  const combined$ = xs$1.combine(this.sources[this.stateSourceName].stream.startWith(this.currentState), props$, children$)
3915
3981
  .map(([state, __props, __children]) => {
3916
- 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 }
3917
3983
  });
3918
3984
 
3919
3985
  const stateSource = new state.StateSource(combined$);
@@ -3924,7 +3990,7 @@ class Component {
3924
3990
 
3925
3991
  const sanitizeItems = item => {
3926
3992
  if (typeof item === 'object') {
3927
- const { __props, __children, ...sanitized } = item;
3993
+ const { __props, __children, __context, ...sanitized } = item;
3928
3994
  return sanitized
3929
3995
  } else {
3930
3996
  return item
@@ -3936,7 +4002,7 @@ class Component {
3936
4002
  const { __props, __children } = state;
3937
4003
  if (!Array.isArray(state[stateField])) return []
3938
4004
  return state[stateField].map(item => {
3939
- 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 }
3940
4006
  })
3941
4007
  },
3942
4008
  set: (oldState, newState) => {
@@ -3960,7 +4026,7 @@ class Component {
3960
4026
  };
3961
4027
  } else if (typeof stateField === 'string') {
3962
4028
  if (typeof this.currentState === 'object') {
3963
- if(!(stateField in this.currentState) && !(stateField in this.calculated)) {
4029
+ if(!(this.currentState && stateField in this.currentState) && !(this.calculated && stateField in this.calculated)) {
3964
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.`);
3965
4031
  lense = undefined;
3966
4032
  } else if (!Array.isArray(this.currentState[stateField])) {
@@ -3999,7 +4065,7 @@ class Component {
3999
4065
  lense = undefined;
4000
4066
  }
4001
4067
 
4002
- const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$ };
4068
+ const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$, __parentContext$: this.context$ };
4003
4069
  const sink$ = collection(factory, lense, { container: null })(sources);
4004
4070
  if (typeof sink$ !== 'object') {
4005
4071
  throw new Error('Invalid sinks returned from component factory of collection element')
@@ -4014,7 +4080,7 @@ class Component {
4014
4080
 
4015
4081
  const combined$ = xs$1.combine(this.sources[this.stateSourceName].stream.startWith(this.currentState), props$, children$)
4016
4082
  .map(([state, __props, __children]) => {
4017
- 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 }
4018
4084
  });
4019
4085
 
4020
4086
  const stateSource = new state.StateSource(combined$);
@@ -4024,7 +4090,7 @@ class Component {
4024
4090
  const fieldLense = {
4025
4091
  get: state => {
4026
4092
  const { __props, __children } = state;
4027
- 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 }
4028
4094
  },
4029
4095
  set: (oldState, newState) => {
4030
4096
  if (this.calculated && stateField in this.calculated) {
@@ -4032,7 +4098,7 @@ class Component {
4032
4098
  return oldState
4033
4099
  }
4034
4100
  if (typeof newState !== 'object' || newState instanceof Array) return { ...oldState, [stateField]: newState }
4035
- const { __props, __children, ...sanitized } = newState;
4101
+ const { __props, __children, __context, ...sanitized } = newState;
4036
4102
  return { ...oldState, [stateField]: sanitized }
4037
4103
  }
4038
4104
  };
@@ -4041,7 +4107,7 @@ class Component {
4041
4107
  get: state => state,
4042
4108
  set: (oldState, newState) => {
4043
4109
  if (typeof newState !== 'object' || newState instanceof Array) return newState
4044
- const { __props, __children, ...sanitized } = newState;
4110
+ const { __props, __children, __context, ...sanitized } = newState;
4045
4111
  return sanitized
4046
4112
  }
4047
4113
  };
@@ -4063,7 +4129,7 @@ class Component {
4063
4129
  }
4064
4130
 
4065
4131
  const switchableComponents = props.of;
4066
- const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$ };
4132
+ const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$, __parentContext$: this.context$ };
4067
4133
 
4068
4134
  const sink$ = isolate(switchable(switchableComponents, props$.map(props => props.current)), { [this.stateSourceName]: lense })(sources);
4069
4135
 
@@ -4082,7 +4148,7 @@ class Component {
4082
4148
 
4083
4149
  const combined$ = xs$1.combine(this.sources[this.stateSourceName].stream.startWith(this.currentState), props$, children$)
4084
4150
  .map(([state, __props, __children]) => {
4085
- 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 }
4086
4152
  });
4087
4153
 
4088
4154
  const stateSource = new state.StateSource(combined$);
@@ -4099,7 +4165,7 @@ class Component {
4099
4165
  const fieldLense = {
4100
4166
  get: state => {
4101
4167
  const { __props, __children } = state;
4102
- 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 }
4103
4169
  },
4104
4170
  set: (oldState, newState) => {
4105
4171
  if (this.calculated && stateField in this.calculated) {
@@ -4114,7 +4180,7 @@ class Component {
4114
4180
  get: state => state,
4115
4181
  set: (oldState, newState) => {
4116
4182
  if (typeof newState !== 'object' || newState instanceof Array) return newState
4117
- const { __props, __children, ...sanitized } = newState;
4183
+ const { __props, __children, __context, ...sanitized } = newState;
4118
4184
  return sanitized
4119
4185
  }
4120
4186
  };
@@ -4135,7 +4201,7 @@ class Component {
4135
4201
  lense = baseLense;
4136
4202
  }
4137
4203
 
4138
- const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$ };
4204
+ const sources = { ...this.sources, [this.stateSourceName]: stateSource, props$, children$, __parentContext$: this.context$ };
4139
4205
  const sink$ = isolate(factory, { [this.stateSourceName]: lense })(sources);
4140
4206
 
4141
4207
  if (typeof sink$ !== 'object') {
@@ -4221,7 +4287,7 @@ class Component {
4221
4287
 
4222
4288
 
4223
4289
 
4224
- function getComponents(currentElement, componentNames, depth=0, index=0) {
4290
+ function getComponents(currentElement, componentNames, depth=0, index=0, parentId) {
4225
4291
  if (!currentElement) return {}
4226
4292
 
4227
4293
  if (currentElement.data?.componentsProcessed) return {}
@@ -4236,9 +4302,10 @@ function getComponents(currentElement, componentNames, depth=0, index=0) {
4236
4302
  const children = currentElement.children || [];
4237
4303
 
4238
4304
  let found = {};
4239
-
4305
+
4306
+ let id = parentId;
4240
4307
  if (isComponent) {
4241
- const id = getComponentIdFromElement(currentElement, depth, index);
4308
+ id = getComponentIdFromElement(currentElement, depth, index, parentId);
4242
4309
  if (isCollection) {
4243
4310
  if (!props.of) throw new Error(`Collection element missing required 'component' property`)
4244
4311
  if (typeof props.of !== 'string' && typeof props.of !== 'function') throw new Error(`Invalid 'component' property of collection element: found ${ typeof props.of } requires string or component factory function`)
@@ -4261,7 +4328,7 @@ function getComponents(currentElement, componentNames, depth=0, index=0) {
4261
4328
  }
4262
4329
 
4263
4330
  if (children.length > 0) {
4264
- children.map((child, i) => getComponents(child, componentNames, depth + 1, index + i))
4331
+ children.map((child, i) => getComponents(child, componentNames, depth + 1, index + i, id))
4265
4332
  .forEach((child) => {
4266
4333
  Object.entries(child).forEach(([id, el]) => found[id] = el);
4267
4334
  });
@@ -4270,7 +4337,7 @@ function getComponents(currentElement, componentNames, depth=0, index=0) {
4270
4337
  return found
4271
4338
  }
4272
4339
 
4273
- function injectComponents(currentElement, components, componentNames, depth=0, index=0) {
4340
+ function injectComponents(currentElement, components, componentNames, depth=0, index=0, parentId) {
4274
4341
  if (!currentElement) return
4275
4342
  if (currentElement.data?.componentsInjected) return currentElement
4276
4343
  if (depth === 0 && currentElement.data) currentElement.data.componentsInjected = true;
@@ -4283,8 +4350,9 @@ function injectComponents(currentElement, components, componentNames, depth=0, i
4283
4350
  (currentElement.data && currentElement.data.props) || {};
4284
4351
  const children = currentElement.children || [];
4285
4352
 
4353
+ let id = parentId;
4286
4354
  if (isComponent) {
4287
- const id = getComponentIdFromElement(currentElement, depth, index);
4355
+ id = getComponentIdFromElement(currentElement, depth, index, parentId);
4288
4356
  const component = components[id];
4289
4357
  if (isCollection) {
4290
4358
  currentElement.sel = 'div';
@@ -4296,28 +4364,21 @@ function injectComponents(currentElement, components, componentNames, depth=0, i
4296
4364
  return component
4297
4365
  }
4298
4366
  } else if (children.length > 0) {
4299
- currentElement.children = children.map((child, i) => injectComponents(child, components, componentNames, depth + 1, index + i)).flat();
4367
+ currentElement.children = children.map((child, i) => injectComponents(child, components, componentNames, depth + 1, index + i, id)).flat();
4300
4368
  return currentElement
4301
4369
  } else {
4302
4370
  return currentElement
4303
4371
  }
4304
4372
  }
4305
4373
 
4306
- const selMap = new Map();
4307
- function getComponentIdFromElement(el, depth, index) {
4308
- const sel = el.sel;
4309
- const name = typeof sel === 'string' ? sel : 'functionComponent';
4310
- let base = selMap.get(sel);
4311
- if (!base) {
4312
- const date = Date.now();
4313
- const rand = Math.floor(Math.random() * 10000);
4314
- base = `${date}-${rand}`;
4315
- selMap.set(sel, base);
4316
- }
4317
- const uid = `${base}-${depth}-${index}`;
4318
- const props = (el.data && el.data.props) || {};
4319
- const id = (props.id && JSON.stringify(props.id)) || uid;
4320
- const fullId = `${ name }::${ id }`;
4374
+ function getComponentIdFromElement(el, depth, index, parentId) {
4375
+ const sel = el.sel;
4376
+ const name = typeof sel === 'string' ? sel : 'functionComponent';
4377
+ const uid = `${depth}:${index}`;
4378
+ const props = el.data?.props || {};
4379
+ const id = (props.id && JSON.stringify(props.id).replaceAll('"', '')) || uid;
4380
+ const parentString = parentId ? `${ parentId }|` : '';
4381
+ const fullId = `${ parentString }${ name }::${ id }`;
4321
4382
  return fullId
4322
4383
  }
4323
4384