@flowerforce/flower-core 3.0.1-beta.7 → 3.1.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/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ ## 3.1.0 (2024-06-06)
2
+
3
+
4
+ ### 🚀 Features
5
+
6
+ - add restart action in core ([800da33](https://github.com/flowerforce/flower/commit/800da33))
7
+
8
+ - add devtool to demo - remove duplicated export type ([88117d6](https://github.com/flowerforce/flower/commit/88117d6))
9
+
10
+ - allow custom functions when defining rules and navigation between nodes ([b35f5da](https://github.com/flowerforce/flower/commit/b35f5da))
11
+
12
+
13
+ ### 🩹 Fixes
14
+
15
+ - fix circular dependency on rules matchers ([0bcb02f](https://github.com/flowerforce/flower/commit/0bcb02f))
16
+
17
+ - align packages ([210a284](https://github.com/flowerforce/flower/commit/210a284))
18
+
1
19
  ## 3.0.0 (2024-05-20)
2
20
 
3
21
  This was a version bump only for flower-core to align it with other projects, there were no code changes.
package/dist/index.cjs.js CHANGED
@@ -18,6 +18,7 @@ var mapKeys = require('lodash/mapKeys');
18
18
  var mapValues = require('lodash/mapValues');
19
19
  var _trimStart = require('lodash/trimStart');
20
20
  var _intersection = require('lodash/intersection');
21
+ var flat = require('flat');
21
22
 
22
23
  const Emitter = new tinyEmitter.TinyEmitter();
23
24
 
@@ -190,6 +191,7 @@ const rulesMatcherUtils = {
190
191
  },
191
192
  getKeys: (rules, options) => {
192
193
  if (!rules) return null;
194
+ if (typeof rules == 'function') return [];
193
195
  if (!rulesMatcherUtils.forceArray(rules).every(r => rulesMatcherUtils.isObject(r))) return null;
194
196
  const keys = {};
195
197
  const conditions = Array.isArray(rules) ? {
@@ -219,6 +221,9 @@ const operators = {
219
221
 
220
222
  const rulesMatcher = (rules, formValue = {}, apply = true, options) => {
221
223
  if (!rules) return [apply];
224
+ if (typeof rules === 'function') {
225
+ return [rules(formValue) === apply];
226
+ }
222
227
  const conditions = Array.isArray(rules) ? {
223
228
  $and: rules
224
229
  } : rules;
@@ -347,6 +352,9 @@ const CoreUtils = {
347
352
  if (typeof rule.rules === 'string') {
348
353
  return false;
349
354
  }
355
+ if (typeof rule.rules === 'function') {
356
+ return rule.rules(value);
357
+ }
350
358
  if (rule.rules === null) {
351
359
  return true;
352
360
  }
@@ -750,164 +758,6 @@ const FlowerCoreReducers = {
750
758
  }
751
759
  };
752
760
 
753
- function isBuffer (obj) {
754
- return obj &&
755
- obj.constructor &&
756
- (typeof obj.constructor.isBuffer === 'function') &&
757
- obj.constructor.isBuffer(obj)
758
- }
759
-
760
- function keyIdentity (key) {
761
- return key
762
- }
763
-
764
- function flatten (target, opts) {
765
- opts = opts || {};
766
-
767
- const delimiter = opts.delimiter || '.';
768
- const maxDepth = opts.maxDepth;
769
- const transformKey = opts.transformKey || keyIdentity;
770
- const output = {};
771
-
772
- function step (object, prev, currentDepth) {
773
- currentDepth = currentDepth || 1;
774
- Object.keys(object).forEach(function (key) {
775
- const value = object[key];
776
- const isarray = opts.safe && Array.isArray(value);
777
- const type = Object.prototype.toString.call(value);
778
- const isbuffer = isBuffer(value);
779
- const isobject = (
780
- type === '[object Object]' ||
781
- type === '[object Array]'
782
- );
783
-
784
- const newKey = prev
785
- ? prev + delimiter + transformKey(key)
786
- : transformKey(key);
787
-
788
- if (!isarray && !isbuffer && isobject && Object.keys(value).length &&
789
- (!opts.maxDepth || currentDepth < maxDepth)) {
790
- return step(value, newKey, currentDepth + 1)
791
- }
792
-
793
- output[newKey] = value;
794
- });
795
- }
796
-
797
- step(target);
798
-
799
- return output
800
- }
801
-
802
- function unflatten (target, opts) {
803
- opts = opts || {};
804
-
805
- const delimiter = opts.delimiter || '.';
806
- const overwrite = opts.overwrite || false;
807
- const transformKey = opts.transformKey || keyIdentity;
808
- const result = {};
809
-
810
- const isbuffer = isBuffer(target);
811
- if (isbuffer || Object.prototype.toString.call(target) !== '[object Object]') {
812
- return target
813
- }
814
-
815
- // safely ensure that the key is
816
- // an integer.
817
- function getkey (key) {
818
- const parsedKey = Number(key);
819
-
820
- return (
821
- isNaN(parsedKey) ||
822
- key.indexOf('.') !== -1 ||
823
- opts.object
824
- )
825
- ? key
826
- : parsedKey
827
- }
828
-
829
- function addKeys (keyPrefix, recipient, target) {
830
- return Object.keys(target).reduce(function (result, key) {
831
- result[keyPrefix + delimiter + key] = target[key];
832
-
833
- return result
834
- }, recipient)
835
- }
836
-
837
- function isEmpty (val) {
838
- const type = Object.prototype.toString.call(val);
839
- const isArray = type === '[object Array]';
840
- const isObject = type === '[object Object]';
841
-
842
- if (!val) {
843
- return true
844
- } else if (isArray) {
845
- return !val.length
846
- } else if (isObject) {
847
- return !Object.keys(val).length
848
- }
849
- }
850
-
851
- target = Object.keys(target).reduce(function (result, key) {
852
- const type = Object.prototype.toString.call(target[key]);
853
- const isObject = (type === '[object Object]' || type === '[object Array]');
854
- if (!isObject || isEmpty(target[key])) {
855
- result[key] = target[key];
856
- return result
857
- } else {
858
- return addKeys(
859
- key,
860
- result,
861
- flatten(target[key], opts)
862
- )
863
- }
864
- }, {});
865
-
866
- Object.keys(target).forEach(function (key) {
867
- const split = key.split(delimiter).map(transformKey);
868
- let key1 = getkey(split.shift());
869
- let key2 = getkey(split[0]);
870
- let recipient = result;
871
-
872
- while (key2 !== undefined) {
873
- if (key1 === '__proto__') {
874
- return
875
- }
876
-
877
- const type = Object.prototype.toString.call(recipient[key1]);
878
- const isobject = (
879
- type === '[object Object]' ||
880
- type === '[object Array]'
881
- );
882
-
883
- // do not write over falsey, non-undefined values if overwrite is false
884
- if (!overwrite && !isobject && typeof recipient[key1] !== 'undefined') {
885
- return
886
- }
887
-
888
- if ((overwrite && !isobject) || (!overwrite && recipient[key1] == null)) {
889
- recipient[key1] = (
890
- typeof key2 === 'number' &&
891
- !opts.object
892
- ? []
893
- : {}
894
- );
895
- }
896
-
897
- recipient = recipient[key1];
898
- if (split.length > 0) {
899
- key1 = getkey(split.shift());
900
- key2 = getkey(split[0]);
901
- }
902
- }
903
-
904
- // unflatten again for 'messy objects'
905
- recipient[key1] = unflatten(target[key], opts);
906
- });
907
-
908
- return result
909
- }
910
-
911
761
  const FlowerCoreStateSelectors = {
912
762
  selectGlobal: state => state && state.flower,
913
763
  selectFlower: name => state => _get(state, [name]),
@@ -966,6 +816,9 @@ const FlowerCoreStateSelectors = {
966
816
  $self: _get(newState, [flowName, ...id.split('.')])
967
817
  } : {});
968
818
  if (!rules) return false;
819
+ if (typeof rules === 'function') {
820
+ return !rules(state);
821
+ }
969
822
  if (!keys) return false;
970
823
  const res = keys.reduce((acc, inc) => {
971
824
  const k = inc;
@@ -974,7 +827,7 @@ const FlowerCoreStateSelectors = {
974
827
  });
975
828
  }, {});
976
829
  const [disabled] = MatchRules.rulesMatcher(rules, {
977
- ...unflatten(res)
830
+ ...flat.unflatten(res)
978
831
  }, false, {
979
832
  prefix: flowName
980
833
  });
package/dist/index.esm.js CHANGED
@@ -16,6 +16,7 @@ import mapKeys from 'lodash/mapKeys';
16
16
  import mapValues from 'lodash/mapValues';
17
17
  import _trimStart from 'lodash/trimStart';
18
18
  import _intersection from 'lodash/intersection';
19
+ import { unflatten } from 'flat';
19
20
 
20
21
  const Emitter = new TinyEmitter();
21
22
 
@@ -188,6 +189,7 @@ const rulesMatcherUtils = {
188
189
  },
189
190
  getKeys: (rules, options) => {
190
191
  if (!rules) return null;
192
+ if (typeof rules == 'function') return [];
191
193
  if (!rulesMatcherUtils.forceArray(rules).every(r => rulesMatcherUtils.isObject(r))) return null;
192
194
  const keys = {};
193
195
  const conditions = Array.isArray(rules) ? {
@@ -217,6 +219,9 @@ const operators = {
217
219
 
218
220
  const rulesMatcher = (rules, formValue = {}, apply = true, options) => {
219
221
  if (!rules) return [apply];
222
+ if (typeof rules === 'function') {
223
+ return [rules(formValue) === apply];
224
+ }
220
225
  const conditions = Array.isArray(rules) ? {
221
226
  $and: rules
222
227
  } : rules;
@@ -345,6 +350,9 @@ const CoreUtils = {
345
350
  if (typeof rule.rules === 'string') {
346
351
  return false;
347
352
  }
353
+ if (typeof rule.rules === 'function') {
354
+ return rule.rules(value);
355
+ }
348
356
  if (rule.rules === null) {
349
357
  return true;
350
358
  }
@@ -748,164 +756,6 @@ const FlowerCoreReducers = {
748
756
  }
749
757
  };
750
758
 
751
- function isBuffer (obj) {
752
- return obj &&
753
- obj.constructor &&
754
- (typeof obj.constructor.isBuffer === 'function') &&
755
- obj.constructor.isBuffer(obj)
756
- }
757
-
758
- function keyIdentity (key) {
759
- return key
760
- }
761
-
762
- function flatten (target, opts) {
763
- opts = opts || {};
764
-
765
- const delimiter = opts.delimiter || '.';
766
- const maxDepth = opts.maxDepth;
767
- const transformKey = opts.transformKey || keyIdentity;
768
- const output = {};
769
-
770
- function step (object, prev, currentDepth) {
771
- currentDepth = currentDepth || 1;
772
- Object.keys(object).forEach(function (key) {
773
- const value = object[key];
774
- const isarray = opts.safe && Array.isArray(value);
775
- const type = Object.prototype.toString.call(value);
776
- const isbuffer = isBuffer(value);
777
- const isobject = (
778
- type === '[object Object]' ||
779
- type === '[object Array]'
780
- );
781
-
782
- const newKey = prev
783
- ? prev + delimiter + transformKey(key)
784
- : transformKey(key);
785
-
786
- if (!isarray && !isbuffer && isobject && Object.keys(value).length &&
787
- (!opts.maxDepth || currentDepth < maxDepth)) {
788
- return step(value, newKey, currentDepth + 1)
789
- }
790
-
791
- output[newKey] = value;
792
- });
793
- }
794
-
795
- step(target);
796
-
797
- return output
798
- }
799
-
800
- function unflatten (target, opts) {
801
- opts = opts || {};
802
-
803
- const delimiter = opts.delimiter || '.';
804
- const overwrite = opts.overwrite || false;
805
- const transformKey = opts.transformKey || keyIdentity;
806
- const result = {};
807
-
808
- const isbuffer = isBuffer(target);
809
- if (isbuffer || Object.prototype.toString.call(target) !== '[object Object]') {
810
- return target
811
- }
812
-
813
- // safely ensure that the key is
814
- // an integer.
815
- function getkey (key) {
816
- const parsedKey = Number(key);
817
-
818
- return (
819
- isNaN(parsedKey) ||
820
- key.indexOf('.') !== -1 ||
821
- opts.object
822
- )
823
- ? key
824
- : parsedKey
825
- }
826
-
827
- function addKeys (keyPrefix, recipient, target) {
828
- return Object.keys(target).reduce(function (result, key) {
829
- result[keyPrefix + delimiter + key] = target[key];
830
-
831
- return result
832
- }, recipient)
833
- }
834
-
835
- function isEmpty (val) {
836
- const type = Object.prototype.toString.call(val);
837
- const isArray = type === '[object Array]';
838
- const isObject = type === '[object Object]';
839
-
840
- if (!val) {
841
- return true
842
- } else if (isArray) {
843
- return !val.length
844
- } else if (isObject) {
845
- return !Object.keys(val).length
846
- }
847
- }
848
-
849
- target = Object.keys(target).reduce(function (result, key) {
850
- const type = Object.prototype.toString.call(target[key]);
851
- const isObject = (type === '[object Object]' || type === '[object Array]');
852
- if (!isObject || isEmpty(target[key])) {
853
- result[key] = target[key];
854
- return result
855
- } else {
856
- return addKeys(
857
- key,
858
- result,
859
- flatten(target[key], opts)
860
- )
861
- }
862
- }, {});
863
-
864
- Object.keys(target).forEach(function (key) {
865
- const split = key.split(delimiter).map(transformKey);
866
- let key1 = getkey(split.shift());
867
- let key2 = getkey(split[0]);
868
- let recipient = result;
869
-
870
- while (key2 !== undefined) {
871
- if (key1 === '__proto__') {
872
- return
873
- }
874
-
875
- const type = Object.prototype.toString.call(recipient[key1]);
876
- const isobject = (
877
- type === '[object Object]' ||
878
- type === '[object Array]'
879
- );
880
-
881
- // do not write over falsey, non-undefined values if overwrite is false
882
- if (!overwrite && !isobject && typeof recipient[key1] !== 'undefined') {
883
- return
884
- }
885
-
886
- if ((overwrite && !isobject) || (!overwrite && recipient[key1] == null)) {
887
- recipient[key1] = (
888
- typeof key2 === 'number' &&
889
- !opts.object
890
- ? []
891
- : {}
892
- );
893
- }
894
-
895
- recipient = recipient[key1];
896
- if (split.length > 0) {
897
- key1 = getkey(split.shift());
898
- key2 = getkey(split[0]);
899
- }
900
- }
901
-
902
- // unflatten again for 'messy objects'
903
- recipient[key1] = unflatten(target[key], opts);
904
- });
905
-
906
- return result
907
- }
908
-
909
759
  const FlowerCoreStateSelectors = {
910
760
  selectGlobal: state => state && state.flower,
911
761
  selectFlower: name => state => _get(state, [name]),
@@ -964,6 +814,9 @@ const FlowerCoreStateSelectors = {
964
814
  $self: _get(newState, [flowName, ...id.split('.')])
965
815
  } : {});
966
816
  if (!rules) return false;
817
+ if (typeof rules === 'function') {
818
+ return !rules(state);
819
+ }
967
820
  if (!keys) return false;
968
821
  const res = keys.reduce((acc, inc) => {
969
822
  const k = inc;
@@ -1,4 +1,5 @@
1
+ import { FunctionRule } from './interfaces';
1
2
  export declare const MatchRules: {
2
- rulesMatcher: (rules?: Record<string, any> | Record<string, any>[], formValue?: Record<string, any>, apply?: boolean, options?: Record<string, any>) => boolean[];
3
+ rulesMatcher: (rules?: Record<string, any> | Record<string, any>[] | FunctionRule, formValue?: Record<string, any>, apply?: boolean, options?: Record<string, any>) => boolean[];
3
4
  utils: import("./rules-matcher/interface").RulesMatcherUtils;
4
5
  };
@@ -67,6 +67,7 @@ type RulesWithName = {
67
67
  rules: RulesObject<any>;
68
68
  };
69
69
  };
70
+ export type FunctionRule = (data: Record<string, any>) => boolean;
70
71
  export type RulesObject<T> = RulesValuesType<T> | {
71
72
  [K in keyof typeof RulesModes]: Array<RulesOperatorsInArray<RulesValuesType<T>>> | Array<RulesObject<RulesValuesType<T>>>;
72
73
  } | Array<Exclude<RulesOperatorsInArray<RulesValuesType<T>>, undefined>>;
@@ -78,7 +79,7 @@ export type GetPath = (idValue?: string) => {
78
79
  export type AllEqual = (...args: Array<number | string | boolean>[]) => boolean;
79
80
  export type FindValidRule<T = Rules<RulesObject<any>>> = (nextRules: {
80
81
  rules: {
81
- rules: T;
82
+ rules: T | FunctionRule;
82
83
  };
83
84
  }, value: Record<string, any>, prefix?: {
84
85
  prefix: string;
@@ -1,5 +1,5 @@
1
1
  import { RulesObject } from './CoreInterface';
2
- import { Flower, Form, Node } from './Store';
2
+ import { Flower, Form, INode } from './Store';
3
3
  export interface ISelectors {
4
4
  selectGlobal<T extends Record<string, any>>(state: {
5
5
  flower: {
@@ -18,7 +18,7 @@ export interface ISelectors {
18
18
  makeSelectCurrentNodeId<T extends Record<string, any>>(flower: Flower<T>, startNodeId: Flower<T>['startId']): string;
19
19
  makeSelectPrevNodeRetain<T extends Record<string, any>>(nodes: Flower<T>['nodes'], history: Flower<T>['history'], current: Flower<T>['current']): boolean | string | undefined;
20
20
  makeSelectCurrentNodeDisabled<T extends Record<string, any>>(nodes: {
21
- [x: string]: Partial<Node>;
21
+ [x: string]: Partial<INode>;
22
22
  }, current: Flower<T>['current']): boolean;
23
23
  makeSelectNodeErrors<T extends Record<string, any>>(form: Form<T> | undefined): {
24
24
  touched: boolean;
@@ -27,7 +27,7 @@ export interface Flower<T extends Record<string, any>> {
27
27
  current: string;
28
28
  history: string[];
29
29
  nodes: {
30
- [x: string]: Node;
30
+ [x: string]: INode;
31
31
  };
32
32
  nextRules: {
33
33
  [x: string]: RulesByNodeId<T>[];
@@ -37,7 +37,7 @@ export interface Flower<T extends Record<string, any>> {
37
37
  [x: string]: Form<T>;
38
38
  };
39
39
  }
40
- export interface Node {
40
+ export interface INode {
41
41
  nodeId: string;
42
42
  nodeType: string;
43
43
  retain?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowerforce/flower-core",
3
- "version": "3.0.1-beta.7",
3
+ "version": "3.1.0",
4
4
  "description": "Core functions for flowerJS",
5
5
  "repository": {
6
6
  "type": "git",
@@ -25,12 +25,14 @@
25
25
  "lodash": ">=4"
26
26
  },
27
27
  "devDependencies": {
28
+ "@types/flat": "^5.0.5",
28
29
  "@types/lodash": "^4.17.1",
29
30
  "jest": "^29.7.0",
30
31
  "ts-jest": "^29.1.2",
31
32
  "typescript": "^5.4.5"
32
33
  },
33
34
  "dependencies": {
35
+ "flat": "5.0.2",
34
36
  "tiny-emitter": "^2.1.0"
35
37
  },
36
38
  "exports": {