1
1
import { JSONSchema } from "@exabyte-io/esse.js/schema" ;
2
2
import forEach from "lodash/forEach" ;
3
- import getValue from "lodash/get" ;
4
3
import hasProperty from "lodash/has" ;
5
4
import isEmpty from "lodash/isEmpty" ;
6
5
@@ -10,19 +9,26 @@ export * from "@exabyte-io/esse.js/lib/js/esse/schemaUtils";
10
9
11
10
export const schemas : { [ key : string ] : string } = { } ;
12
11
12
+ interface SubstitutionMap {
13
+ [ key : string ] : string ;
14
+ }
15
+
16
+ interface Parameter {
17
+ key : string ;
18
+ values : string [ ] | boolean [ ] ;
19
+ namesMap ?: SubstitutionMap ;
20
+ }
21
+
13
22
interface Node {
14
- dataSelector : {
23
+ data : {
15
24
key : string ;
16
25
value : string ;
17
26
name : string ;
18
27
} ;
28
+ staticOptions : Parameter [ ] ;
19
29
children ?: Node [ ] ;
20
30
}
21
31
22
- function isNodeWithChildren ( node : Node ) : node is Required < Node > {
23
- return Boolean ( node . children ?. length ) ;
24
- }
25
-
26
32
/**
27
33
* Returns previously registered schema for InMemoryEntity
28
34
* @returns
@@ -52,53 +58,67 @@ export function typeofSchema(schema: JSONSchema) {
52
58
}
53
59
}
54
60
55
- function getEnumValues ( nodes : Node [ ] ) {
56
- if ( ! nodes . length ) return { } ;
61
+ function extractEnumOptions ( nodes ? : Node [ ] ) {
62
+ if ( ! nodes || ! nodes . length ) return { } ;
57
63
return {
58
- enum : nodes . map ( ( node ) => getValue ( node , node . dataSelector . value ) ) ,
64
+ enum : nodes . map ( ( node ) => node . data . value ) ,
65
+ enumNames : nodes . map ( ( node ) => node . data . name ) ,
59
66
} ;
60
67
}
61
68
62
- function getEnumNames ( nodes : Node [ ] ) {
63
- if ( ! nodes . length ) return { } ;
64
- return {
65
- enumNames : nodes . map ( ( node ) => getValue ( node , node . dataSelector . name ) ) ,
66
- } ;
69
+ function substituteName ( value : unknown , mapping ?: SubstitutionMap ) {
70
+ if ( typeof value !== "string" ) {
71
+ return JSON . stringify ( value ) ;
72
+ }
73
+ return mapping ? mapping [ value ] : value ;
74
+ }
75
+
76
+ function createStaticFields ( node : Node ) {
77
+ if ( ! node . staticOptions ) return { } ;
78
+ const fields : { [ key : string ] : { enum : string [ ] | boolean [ ] ; enumNames : string [ ] } } = { } ;
79
+ node . staticOptions
80
+ . filter ( ( o ) => o . key && o . values )
81
+ . forEach ( ( o ) => {
82
+ fields [ o . key ] = {
83
+ enum : o . values ,
84
+ enumNames : o . values . map ( ( v ) => substituteName ( v , o . namesMap ) ) ,
85
+ } ;
86
+ } ) ;
87
+ return fields ;
67
88
}
68
89
69
90
/**
70
91
* @summary Recursively generate `dependencies` for RJSF schema based on tree.
71
92
* @param {Object[] } nodes - Array of nodes (e.g. `[tree]` or `node.children`)
72
93
* @returns {{}|{dependencies: {}} }
73
94
*/
74
- export function buildDependencies ( nodes : Node [ ] ) : JSONSchema {
75
- if ( nodes . length === 0 || nodes . every ( ( n ) => ! n . children ?. length ) ) return { } ;
76
-
77
- const nodesWithChildren = nodes . filter ( isNodeWithChildren ) ;
78
- const parentKey = nodesWithChildren [ 0 ] . dataSelector . key ;
79
- const childKey = nodesWithChildren [ 0 ] . children [ 0 ] . dataSelector . key ;
80
-
81
- return {
82
- dependencies : {
83
- [ parentKey ] : {
84
- oneOf : nodesWithChildren . map ( ( node ) => {
85
- return {
86
- properties : {
87
- [ parentKey ] : {
88
- ...getEnumValues ( [ node ] ) ,
89
- ...getEnumNames ( [ node ] ) ,
90
- } ,
91
- [ childKey ] : {
92
- ...getEnumValues ( node . children ) ,
93
- ...getEnumNames ( node . children ) ,
94
- } ,
95
- } ,
96
- ...buildDependencies ( node . children ) ,
97
- } ;
98
- } ) ,
95
+ export function buildDependencies ( nodes ?: Node [ ] ) : JSONSchema {
96
+ const isEveryTerminal = nodes && nodes . every ( ( node ) => ! node . children ?. length ) ;
97
+ const isWithStaticOptions = nodes && nodes . some ( ( node ) => node ?. staticOptions ) ;
98
+ if ( ! nodes || ! nodes . length || ! nodes [ 0 ] . data ) return { } ;
99
+ const parentKey = nodes [ 0 ] . data . key ;
100
+
101
+ const cases = nodes . map ( ( node ) => {
102
+ const childKey = node . children ?. length && node . children [ 0 ] . data . key ;
103
+ return {
104
+ properties : {
105
+ [ parentKey ] : extractEnumOptions ( [ node ] ) ,
106
+ ...( childKey ? { [ childKey ] : extractEnumOptions ( node . children ) } : { } ) ,
107
+ ...createStaticFields ( node ) ,
99
108
} ,
100
- } ,
101
- } ;
109
+ ...buildDependencies ( node . children ) ,
110
+ } ;
111
+ } ) ;
112
+
113
+ return cases . length && ( ! isEveryTerminal || isWithStaticOptions )
114
+ ? {
115
+ dependencies : {
116
+ [ parentKey ] : {
117
+ oneOf : cases ,
118
+ } ,
119
+ } ,
120
+ }
121
+ : { } ;
102
122
}
103
123
104
124
interface Props {
@@ -131,9 +151,8 @@ export function getSchemaWithDependencies({
131
151
// RJSF does not automatically render dropdown widget if `enum` is not present
132
152
if ( modifyProperties && nodes . length ) {
133
153
const mod = {
134
- [ nodes [ 0 ] . dataSelector . key ] : {
135
- ...getEnumNames ( nodes ) ,
136
- ...getEnumValues ( nodes ) ,
154
+ [ nodes [ 0 ] . data . key ] : {
155
+ ...extractEnumOptions ( nodes ) ,
137
156
} ,
138
157
} ;
139
158
forEach ( mod , ( extraFields , key ) => {
0 commit comments