@ng-formworks/core 17.2.7 → 18.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.
Files changed (177) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +834 -0
  3. package/esm2022/lib/framework-library/framework-library.service.mjs +175 -0
  4. package/esm2022/lib/framework-library/framework.mjs +15 -0
  5. package/esm2022/lib/framework-library/no-framework.component.mjs +18 -0
  6. package/esm2022/lib/framework-library/no-framework.module.mjs +27 -0
  7. package/esm2022/lib/framework-library/no.framework.mjs +19 -0
  8. package/esm2022/lib/json-schema-form.component.mjs +765 -0
  9. package/esm2022/lib/json-schema-form.module.mjs +26 -0
  10. package/esm2022/lib/json-schema-form.service.mjs +676 -0
  11. package/esm2022/lib/locale/de-validation-messages.mjs +60 -0
  12. package/esm2022/lib/locale/en-validation-messages.mjs +60 -0
  13. package/esm2022/lib/locale/es-validation-messages.mjs +57 -0
  14. package/esm2022/lib/locale/fr-validation-messages.mjs +60 -0
  15. package/esm2022/lib/locale/index.mjs +8 -0
  16. package/esm2022/lib/locale/it-validation-messages.mjs +60 -0
  17. package/esm2022/lib/locale/pt-validation-messages.mjs +60 -0
  18. package/esm2022/lib/locale/zh-validation-messages.mjs +60 -0
  19. package/esm2022/lib/shared/convert-schema-to-draft6.function.mjs +300 -0
  20. package/esm2022/lib/shared/form-group.functions.mjs +442 -0
  21. package/esm2022/lib/shared/format-regex.constants.mjs +54 -0
  22. package/esm2022/lib/shared/index.mjs +12 -0
  23. package/esm2022/lib/shared/json-schema.functions.mjs +784 -0
  24. package/esm2022/lib/shared/json.validators.mjs +884 -0
  25. package/esm2022/lib/shared/jsonpointer.functions.mjs +1026 -0
  26. package/esm2022/lib/shared/layout.functions.mjs +1158 -0
  27. package/esm2022/lib/shared/merge-schemas.function.mjs +345 -0
  28. package/esm2022/lib/shared/utility.functions.mjs +380 -0
  29. package/esm2022/lib/shared/validator.functions.mjs +584 -0
  30. package/esm2022/lib/widget-library/add-reference.component.mjs +61 -0
  31. package/esm2022/lib/widget-library/button.component.mjs +72 -0
  32. package/esm2022/lib/widget-library/checkbox.component.mjs +105 -0
  33. package/esm2022/lib/widget-library/checkboxes.component.mjs +147 -0
  34. package/esm2022/lib/widget-library/file.component.mjs +35 -0
  35. package/esm2022/lib/widget-library/hidden.component.mjs +54 -0
  36. package/esm2022/lib/widget-library/index.mjs +55 -0
  37. package/esm2022/lib/widget-library/input.component.mjs +119 -0
  38. package/esm2022/lib/widget-library/message.component.mjs +38 -0
  39. package/esm2022/lib/widget-library/none.component.mjs +21 -0
  40. package/esm2022/lib/widget-library/number.component.mjs +123 -0
  41. package/esm2022/lib/widget-library/one-of.component.mjs +35 -0
  42. package/esm2022/lib/widget-library/orderable.directive.mjs +123 -0
  43. package/esm2022/lib/widget-library/radios.component.mjs +153 -0
  44. package/esm2022/lib/widget-library/root.component.mjs +79 -0
  45. package/esm2022/lib/widget-library/section.component.mjs +199 -0
  46. package/esm2022/lib/widget-library/select-framework.component.mjs +51 -0
  47. package/esm2022/lib/widget-library/select-widget.component.mjs +46 -0
  48. package/esm2022/lib/widget-library/select.component.mjs +150 -0
  49. package/esm2022/lib/widget-library/submit.component.mjs +82 -0
  50. package/esm2022/lib/widget-library/tab.component.mjs +41 -0
  51. package/esm2022/lib/widget-library/tabs.component.mjs +108 -0
  52. package/esm2022/lib/widget-library/template.component.mjs +46 -0
  53. package/esm2022/lib/widget-library/textarea.component.mjs +104 -0
  54. package/esm2022/lib/widget-library/widget-library.module.mjs +42 -0
  55. package/esm2022/lib/widget-library/widget-library.service.mjs +226 -0
  56. package/esm2022/ng-formworks-core.mjs +5 -0
  57. package/esm2022/public_api.mjs +13 -0
  58. package/fesm2022/ng-formworks-core.mjs +10151 -0
  59. package/fesm2022/ng-formworks-core.mjs.map +1 -0
  60. package/index.d.ts +5 -0
  61. package/lib/framework-library/framework-library.service.d.ts +55 -0
  62. package/lib/framework-library/framework.d.ts +13 -0
  63. package/lib/framework-library/no-framework.component.d.ts +8 -0
  64. package/lib/framework-library/no-framework.module.d.ts +9 -0
  65. package/lib/framework-library/no.framework.d.ts +10 -0
  66. package/lib/json-schema-form.component.d.ts +218 -0
  67. package/lib/json-schema-form.module.d.ts +11 -0
  68. package/lib/json-schema-form.service.d.ts +115 -0
  69. package/lib/locale/de-validation-messages.d.ts +1 -0
  70. package/lib/locale/en-validation-messages.d.ts +1 -0
  71. package/lib/locale/es-validation-messages.d.ts +1 -0
  72. package/lib/locale/fr-validation-messages.d.ts +1 -0
  73. package/{src/lib/locale/index.ts → lib/locale/index.d.ts} +7 -7
  74. package/lib/locale/it-validation-messages.d.ts +1 -0
  75. package/lib/locale/pt-validation-messages.d.ts +1 -0
  76. package/lib/locale/zh-validation-messages.d.ts +1 -0
  77. package/lib/shared/convert-schema-to-draft6.function.d.ts +21 -0
  78. package/lib/shared/form-group.functions.d.ts +100 -0
  79. package/lib/shared/format-regex.constants.d.ts +19 -0
  80. package/lib/shared/index.d.ts +9 -0
  81. package/lib/shared/json-schema.functions.d.ts +193 -0
  82. package/lib/shared/json.validators.d.ts +441 -0
  83. package/lib/shared/jsonpointer.functions.d.ts +416 -0
  84. package/lib/shared/layout.functions.d.ts +83 -0
  85. package/lib/shared/merge-schemas.function.d.ts +19 -0
  86. package/lib/shared/utility.functions.d.ts +165 -0
  87. package/{src/lib/shared/validator.functions.ts → lib/shared/validator.functions.d.ts} +364 -601
  88. package/lib/widget-library/add-reference.component.d.ts +20 -0
  89. package/lib/widget-library/button.component.d.ts +21 -0
  90. package/lib/widget-library/checkbox.component.d.ts +24 -0
  91. package/lib/widget-library/checkboxes.component.d.ts +24 -0
  92. package/lib/widget-library/file.component.d.ts +21 -0
  93. package/lib/widget-library/hidden.component.d.ts +19 -0
  94. package/{src/lib/widget-library/index.ts → lib/widget-library/index.d.ts} +47 -56
  95. package/lib/widget-library/input.component.d.ts +22 -0
  96. package/lib/widget-library/message.component.d.ts +15 -0
  97. package/lib/widget-library/none.component.d.ts +8 -0
  98. package/lib/widget-library/number.component.d.ts +25 -0
  99. package/lib/widget-library/one-of.component.d.ts +21 -0
  100. package/lib/widget-library/orderable.directive.d.ts +41 -0
  101. package/lib/widget-library/radios.component.d.ts +23 -0
  102. package/lib/widget-library/root.component.d.ts +17 -0
  103. package/lib/widget-library/section.component.d.ts +19 -0
  104. package/lib/widget-library/select-framework.component.d.ts +18 -0
  105. package/lib/widget-library/select-widget.component.d.ts +18 -0
  106. package/lib/widget-library/select.component.d.ts +24 -0
  107. package/lib/widget-library/submit.component.d.ts +24 -0
  108. package/lib/widget-library/tab.component.d.ts +14 -0
  109. package/lib/widget-library/tabs.component.d.ts +20 -0
  110. package/lib/widget-library/template.component.d.ts +18 -0
  111. package/lib/widget-library/textarea.component.d.ts +21 -0
  112. package/lib/widget-library/widget-library.module.d.ts +31 -0
  113. package/lib/widget-library/widget-library.service.d.ts +22 -0
  114. package/package.json +66 -53
  115. package/{src/public_api.ts → public_api.d.ts} +9 -21
  116. package/karma.conf.js +0 -46
  117. package/ng-package.json +0 -11
  118. package/src/lib/framework-library/framework-library.service.ts +0 -195
  119. package/src/lib/framework-library/framework.ts +0 -11
  120. package/src/lib/framework-library/no-framework.component.html +0 -2
  121. package/src/lib/framework-library/no-framework.component.ts +0 -11
  122. package/src/lib/framework-library/no-framework.module.ts +0 -18
  123. package/src/lib/framework-library/no.framework.ts +0 -11
  124. package/src/lib/json-schema-form.component.html +0 -7
  125. package/src/lib/json-schema-form.component.ts +0 -809
  126. package/src/lib/json-schema-form.module.ts +0 -17
  127. package/src/lib/json-schema-form.service.ts +0 -907
  128. package/src/lib/locale/de-validation-messages.ts +0 -58
  129. package/src/lib/locale/en-validation-messages.ts +0 -58
  130. package/src/lib/locale/es-validation-messages.ts +0 -55
  131. package/src/lib/locale/fr-validation-messages.ts +0 -58
  132. package/src/lib/locale/it-validation-messages.ts +0 -58
  133. package/src/lib/locale/pt-validation-messages.ts +0 -58
  134. package/src/lib/locale/zh-validation-messages.ts +0 -58
  135. package/src/lib/locale-dates/en-US.ts +0 -5
  136. package/src/lib/shared/convert-schema-to-draft6.function.ts +0 -321
  137. package/src/lib/shared/form-group.functions.ts +0 -522
  138. package/src/lib/shared/format-regex.constants.ts +0 -73
  139. package/src/lib/shared/index.ts +0 -40
  140. package/src/lib/shared/json-schema.functions.ts +0 -788
  141. package/src/lib/shared/json.validators.ts +0 -878
  142. package/src/lib/shared/jsonpointer.functions.ts +0 -1012
  143. package/src/lib/shared/jspointer.functions.json.spec.ts +0 -103
  144. package/src/lib/shared/layout.functions.ts +0 -1233
  145. package/src/lib/shared/merge-schemas.function.ts +0 -329
  146. package/src/lib/shared/utility.functions.ts +0 -373
  147. package/src/lib/shared/validator.functions.spec.ts +0 -55
  148. package/src/lib/widget-library/add-reference.component.ts +0 -59
  149. package/src/lib/widget-library/button.component.ts +0 -54
  150. package/src/lib/widget-library/checkbox.component.ts +0 -74
  151. package/src/lib/widget-library/checkboxes.component.ts +0 -104
  152. package/src/lib/widget-library/file.component.ts +0 -36
  153. package/src/lib/widget-library/hidden.component.ts +0 -39
  154. package/src/lib/widget-library/input.component.ts +0 -76
  155. package/src/lib/widget-library/message.component.ts +0 -29
  156. package/src/lib/widget-library/none.component.ts +0 -12
  157. package/src/lib/widget-library/number.component.ts +0 -79
  158. package/src/lib/widget-library/one-of.component.ts +0 -36
  159. package/src/lib/widget-library/orderable.directive.ts +0 -130
  160. package/src/lib/widget-library/radios.component.ts +0 -101
  161. package/src/lib/widget-library/root.component.ts +0 -78
  162. package/src/lib/widget-library/section.component.ts +0 -133
  163. package/src/lib/widget-library/select-framework.component.ts +0 -50
  164. package/src/lib/widget-library/select-widget.component.ts +0 -46
  165. package/src/lib/widget-library/select.component.ts +0 -96
  166. package/src/lib/widget-library/submit.component.ts +0 -68
  167. package/src/lib/widget-library/tab.component.ts +0 -29
  168. package/src/lib/widget-library/tabs.component.ts +0 -83
  169. package/src/lib/widget-library/template.component.ts +0 -52
  170. package/src/lib/widget-library/textarea.component.ts +0 -68
  171. package/src/lib/widget-library/widget-library.module.ts +0 -13
  172. package/src/lib/widget-library/widget-library.service.ts +0 -234
  173. package/src/test.ts +0 -18
  174. package/tsconfig.lib.json +0 -25
  175. package/tsconfig.lib.prod.json +0 -9
  176. package/tsconfig.spec.json +0 -17
  177. package/tslint.json +0 -11
@@ -1,1233 +0,0 @@
1
- import cloneDeep from 'lodash/cloneDeep';
2
- import _isArray from 'lodash/isArray';
3
- import _isPlainObject from 'lodash/isPlainObject';
4
- import uniqueId from 'lodash/uniqueId';
5
- import { TitleMapItem } from '../json-schema-form.service';
6
- import {
7
- checkInlineType,
8
- getFromSchema,
9
- getInputType,
10
- isInputRequired,
11
- removeRecursiveReferences,
12
- updateInputOptions
13
- } from './json-schema.functions';
14
- import { JsonPointer } from './jsonpointer.functions';
15
- import {
16
- copy,
17
- fixTitle,
18
- forEach,
19
- hasOwn
20
- } from './utility.functions';
21
- import {
22
- inArray,
23
- isArray,
24
- isDefined,
25
- isEmpty,
26
- isNumber,
27
- isObject,
28
- isString
29
- } from './validator.functions';
30
-
31
-
32
-
33
-
34
- /**
35
- * Layout function library:
36
- *
37
- * buildLayout: Builds a complete layout from an input layout and schema
38
- *
39
- * buildLayoutFromSchema: Builds a complete layout entirely from an input schema
40
- *
41
- * mapLayout:
42
- *
43
- * getLayoutNode:
44
- *
45
- * buildTitleMap:
46
- */
47
-
48
- /**
49
- * 'buildLayout' function
50
- *
51
- * // jsf
52
- * // widgetLibrary
53
- * //
54
- */
55
- export function buildLayout_original(jsf, widgetLibrary) {
56
- let hasSubmitButton = !JsonPointer.get(jsf, '/formOptions/addSubmit');
57
- const formLayout = mapLayout(jsf.layout, (layoutItem, index, layoutPointer) => {
58
- const newNode: any = {
59
- _id: uniqueId(),
60
- options: {},
61
- };
62
- if (isObject(layoutItem)) {
63
- Object.assign(newNode, layoutItem);
64
- Object.keys(newNode)
65
- .filter(option => !inArray(option, [
66
- '_id', '$ref', 'arrayItem', 'arrayItemType', 'dataPointer', 'dataType',
67
- 'items', 'key', 'name', 'options', 'recursiveReference', 'type', 'widget'
68
- ]))
69
- .forEach(option => {
70
- newNode.options[option] = newNode[option];
71
- delete newNode[option];
72
- });
73
- if (!hasOwn(newNode, 'type') && isString(newNode.widget)) {
74
- newNode.type = newNode.widget;
75
- delete newNode.widget;
76
- }
77
- if (!hasOwn(newNode.options, 'title')) {
78
- if (hasOwn(newNode.options, 'legend')) {
79
- newNode.options.title = newNode.options.legend;
80
- delete newNode.options.legend;
81
- }
82
- }
83
- if (!hasOwn(newNode.options, 'validationMessages')) {
84
- if (hasOwn(newNode.options, 'errorMessages')) {
85
- newNode.options.validationMessages = newNode.options.errorMessages;
86
- delete newNode.options.errorMessages;
87
-
88
- // Convert Angular Schema Form (AngularJS) 'validationMessage' to
89
- // Angular JSON Schema Form 'validationMessages'
90
- // TV4 codes from https://github.com/geraintluff/tv4/blob/master/source/api.js
91
- } else if (hasOwn(newNode.options, 'validationMessage')) {
92
- if (typeof newNode.options.validationMessage === 'string') {
93
- newNode.options.validationMessages = newNode.options.validationMessage;
94
- } else {
95
- newNode.options.validationMessages = {};
96
- Object.keys(newNode.options.validationMessage).forEach(key => {
97
- const code = key + '';
98
- const newKey =
99
- code === '0' ? 'type' :
100
- code === '1' ? 'enum' :
101
- code === '100' ? 'multipleOf' :
102
- code === '101' ? 'minimum' :
103
- code === '102' ? 'exclusiveMinimum' :
104
- code === '103' ? 'maximum' :
105
- code === '104' ? 'exclusiveMaximum' :
106
- code === '200' ? 'minLength' :
107
- code === '201' ? 'maxLength' :
108
- code === '202' ? 'pattern' :
109
- code === '300' ? 'minProperties' :
110
- code === '301' ? 'maxProperties' :
111
- code === '302' ? 'required' :
112
- code === '304' ? 'dependencies' :
113
- code === '400' ? 'minItems' :
114
- code === '401' ? 'maxItems' :
115
- code === '402' ? 'uniqueItems' :
116
- code === '500' ? 'format' : code + '';
117
- newNode.options.validationMessages[newKey] = newNode.options.validationMessage[key];
118
- });
119
- }
120
- delete newNode.options.validationMessage;
121
- }
122
- }
123
- } else if (JsonPointer.isJsonPointer(layoutItem)) {
124
- newNode.dataPointer = layoutItem;
125
- } else if (isString(layoutItem)) {
126
- newNode.key = layoutItem;
127
- } else {
128
- console.error('buildLayout error: Form layout element not recognized:');
129
- console.error(layoutItem);
130
- return null;
131
- }
132
- let nodeSchema: any = null;
133
-
134
- // If newNode does not have a dataPointer, try to find an equivalent
135
- if (!hasOwn(newNode, 'dataPointer')) {
136
-
137
- // If newNode has a key, change it to a dataPointer
138
- if (hasOwn(newNode, 'key')) {
139
- newNode.dataPointer = newNode.key === '*' ? newNode.key :
140
- JsonPointer.compile(JsonPointer.parseObjectPath(newNode.key), '-');
141
- delete newNode.key;
142
-
143
- // If newNode is an array, search for dataPointer in child nodes
144
- } else if (hasOwn(newNode, 'type') && newNode.type.slice(-5) === 'array') {
145
- const findDataPointer = (items) => {
146
- if (items === null || typeof items !== 'object') { return; }
147
- if (hasOwn(items, 'dataPointer')) { return items.dataPointer; }
148
- if (isArray(items.items)) {
149
- for (const item of items.items) {
150
- if (hasOwn(item, 'dataPointer') && item.dataPointer.indexOf('/-') !== -1) {
151
- return item.dataPointer;
152
- }
153
- if (hasOwn(item, 'items')) {
154
- const searchItem = findDataPointer(item);
155
- if (searchItem) { return searchItem; }
156
- }
157
- }
158
- }
159
- };
160
- const childDataPointer = findDataPointer(newNode);
161
- if (childDataPointer) {
162
- newNode.dataPointer =
163
- childDataPointer.slice(0, childDataPointer.lastIndexOf('/-'));
164
- }
165
- }
166
- }
167
-
168
- if (hasOwn(newNode, 'dataPointer')) {
169
- if (newNode.dataPointer === '*') {
170
- return buildLayoutFromSchema(jsf, widgetLibrary, jsf.formValues);
171
- }
172
- const nodeValue =
173
- JsonPointer.get(jsf.formValues, newNode.dataPointer.replace(/\/-/g, '/1'));
174
-
175
- // TODO: Create function getFormValues(jsf, dataPointer, forRefLibrary)
176
- // check formOptions.setSchemaDefaults and formOptions.setLayoutDefaults
177
- // then set apropriate values from initialVaues, schema, or layout
178
-
179
- newNode.dataPointer =
180
- JsonPointer.toGenericPointer(newNode.dataPointer, jsf.arrayMap);
181
- const LastKey = JsonPointer.toKey(newNode.dataPointer);
182
- if (!newNode.name && isString(LastKey) && LastKey !== '-') {
183
- newNode.name = LastKey;
184
- }
185
- const shortDataPointer = removeRecursiveReferences(
186
- newNode.dataPointer, jsf.dataRecursiveRefMap, jsf.arrayMap
187
- );
188
- const recursive = !shortDataPointer.length ||
189
- shortDataPointer !== newNode.dataPointer;
190
- let schemaPointer: string;
191
- if (!jsf.dataMap.has(shortDataPointer)) {
192
- jsf.dataMap.set(shortDataPointer, new Map());
193
- }
194
- const nodeDataMap = jsf.dataMap.get(shortDataPointer);
195
- if (nodeDataMap.has('schemaPointer')) {
196
- schemaPointer = nodeDataMap.get('schemaPointer');
197
- } else {
198
- schemaPointer = JsonPointer.toSchemaPointer(shortDataPointer, jsf.schema);
199
- nodeDataMap.set('schemaPointer', schemaPointer);
200
- }
201
- nodeDataMap.set('disabled', !!newNode.options.disabled);
202
- nodeSchema = JsonPointer.get(jsf.schema, schemaPointer);
203
- if (nodeSchema) {
204
- if (!hasOwn(newNode, 'type')) {
205
- newNode.type = getInputType(nodeSchema, newNode);
206
- } else if (!widgetLibrary.hasWidget(newNode.type)) {
207
- const oldWidgetType = newNode.type;
208
- newNode.type = getInputType(nodeSchema, newNode);
209
- console.error(`error: widget type "${oldWidgetType}" ` +
210
- `not found in library. Replacing with "${newNode.type}".`);
211
- } else {
212
- newNode.type = checkInlineType(newNode.type, nodeSchema, newNode);
213
- }
214
- if (nodeSchema.type === 'object' && isArray(nodeSchema.required)) {
215
- nodeDataMap.set('required', nodeSchema.required);
216
- }
217
- newNode.dataType =
218
- nodeSchema.type || (hasOwn(nodeSchema, '$ref') ? '$ref' : null);
219
- updateInputOptions(newNode, nodeSchema, jsf);
220
-
221
- // Present checkboxes as single control, rather than array
222
- if (newNode.type === 'checkboxes' && hasOwn(nodeSchema, 'items')) {
223
- updateInputOptions(newNode, nodeSchema.items, jsf);
224
- } else if (newNode.dataType === 'array') {
225
- newNode.options.maxItems = Math.min(
226
- nodeSchema.maxItems || 1000, newNode.options.maxItems || 1000
227
- );
228
- newNode.options.minItems = Math.max(
229
- nodeSchema.minItems || 0, newNode.options.minItems || 0
230
- );
231
- newNode.options.listItems = Math.max(
232
- newNode.options.listItems || 0, isArray(nodeValue) ? nodeValue.length : 0
233
- );
234
- newNode.options.tupleItems =
235
- isArray(nodeSchema.items) ? nodeSchema.items.length : 0;
236
- if (newNode.options.maxItems < newNode.options.tupleItems) {
237
- newNode.options.tupleItems = newNode.options.maxItems;
238
- newNode.options.listItems = 0;
239
- } else if (newNode.options.maxItems <
240
- newNode.options.tupleItems + newNode.options.listItems
241
- ) {
242
- newNode.options.listItems =
243
- newNode.options.maxItems - newNode.options.tupleItems;
244
- } else if (newNode.options.minItems >
245
- newNode.options.tupleItems + newNode.options.listItems
246
- ) {
247
- newNode.options.listItems =
248
- newNode.options.minItems - newNode.options.tupleItems;
249
- }
250
- if (!nodeDataMap.has('maxItems')) {
251
- nodeDataMap.set('maxItems', newNode.options.maxItems);
252
- nodeDataMap.set('minItems', newNode.options.minItems);
253
- nodeDataMap.set('tupleItems', newNode.options.tupleItems);
254
- nodeDataMap.set('listItems', newNode.options.listItems);
255
- }
256
- if (!jsf.arrayMap.has(shortDataPointer)) {
257
- jsf.arrayMap.set(shortDataPointer, newNode.options.tupleItems);
258
- }
259
- }
260
- if (isInputRequired(jsf.schema, schemaPointer)) {
261
- newNode.options.required = true;
262
- jsf.fieldsRequired = true;
263
- }
264
- } else {
265
- // TODO: create item in FormGroup model from layout key (?)
266
- updateInputOptions(newNode, {}, jsf);
267
- }
268
-
269
- if (!newNode.options.title && !/^\d+$/.test(newNode.name)) {
270
- newNode.options.title = fixTitle(newNode.name);
271
- }
272
-
273
- if (hasOwn(newNode.options, 'copyValueTo')) {
274
- if (typeof newNode.options.copyValueTo === 'string') {
275
- newNode.options.copyValueTo = [newNode.options.copyValueTo];
276
- }
277
- if (isArray(newNode.options.copyValueTo)) {
278
- newNode.options.copyValueTo = newNode.options.copyValueTo.map(item =>
279
- JsonPointer.compile(JsonPointer.parseObjectPath(item), '-')
280
- );
281
- }
282
- }
283
-
284
- newNode.widget = widgetLibrary.getWidget(newNode.type);
285
- nodeDataMap.set('inputType', newNode.type);
286
- nodeDataMap.set('widget', newNode.widget);
287
-
288
- if (newNode.dataType === 'array' &&
289
- (hasOwn(newNode, 'items') || hasOwn(newNode, 'additionalItems'))
290
- ) {
291
- const itemRefPointer = removeRecursiveReferences(
292
- newNode.dataPointer + '/-', jsf.dataRecursiveRefMap, jsf.arrayMap
293
- );
294
- if (!jsf.dataMap.has(itemRefPointer)) {
295
- jsf.dataMap.set(itemRefPointer, new Map());
296
- }
297
- jsf.dataMap.get(itemRefPointer).set('inputType', 'section');
298
-
299
- // Fix insufficiently nested array item groups
300
- if (newNode.items.length > 1) {
301
- const arrayItemGroup = [];
302
- for (let i = newNode.items.length - 1; i >= 0; i--) {
303
- const subItem = newNode.items[i];
304
- if (hasOwn(subItem, 'dataPointer') &&
305
- subItem.dataPointer.slice(0, itemRefPointer.length) === itemRefPointer
306
- ) {
307
- const arrayItem = newNode.items.splice(i, 1)[0];
308
- arrayItem.dataPointer = newNode.dataPointer + '/-' +
309
- arrayItem.dataPointer.slice(itemRefPointer.length);
310
- arrayItemGroup.unshift(arrayItem);
311
- } else {
312
- subItem.arrayItem = true;
313
- // TODO: Check schema to get arrayItemType and removable
314
- subItem.arrayItemType = 'list';
315
- subItem.removable = newNode.options.removable !== false;
316
- }
317
- }
318
- if (arrayItemGroup.length) {
319
- newNode.items.push({
320
- _id: uniqueId(),
321
- arrayItem: true,
322
- arrayItemType: newNode.options.tupleItems > newNode.items.length ?
323
- 'tuple' : 'list',
324
- items: arrayItemGroup,
325
- options: { removable: newNode.options.removable !== false, },
326
- dataPointer: newNode.dataPointer + '/-',
327
- type: 'section',
328
- widget: widgetLibrary.getWidget('section'),
329
- });
330
- }
331
- } else {
332
- // TODO: Fix to hndle multiple items
333
- newNode.items[0].arrayItem = true;
334
- if (!newNode.items[0].dataPointer) {
335
- newNode.items[0].dataPointer =
336
- JsonPointer.toGenericPointer(itemRefPointer, jsf.arrayMap);
337
- }
338
- if (!JsonPointer.has(newNode, '/items/0/options/removable')) {
339
- newNode.items[0].options.removable = true;
340
- }
341
- if (newNode.options.orderable === false) {
342
- newNode.items[0].options.orderable = false;
343
- }
344
- newNode.items[0].arrayItemType =
345
- newNode.options.tupleItems ? 'tuple' : 'list';
346
- }
347
-
348
- if (isArray(newNode.items)) {
349
- const arrayListItems =
350
- newNode.items.filter(item => item.type !== '$ref').length -
351
- newNode.options.tupleItems;
352
- if (arrayListItems > newNode.options.listItems) {
353
- newNode.options.listItems = arrayListItems;
354
- nodeDataMap.set('listItems', arrayListItems);
355
- }
356
- }
357
-
358
- if (!hasOwn(jsf.layoutRefLibrary, itemRefPointer)) {
359
- jsf.layoutRefLibrary[itemRefPointer] =
360
- cloneDeep(newNode.items[newNode.items.length - 1]);
361
- if (recursive) {
362
- jsf.layoutRefLibrary[itemRefPointer].recursiveReference = true;
363
- }
364
- forEach(jsf.layoutRefLibrary[itemRefPointer], (item, key) => {
365
- if (hasOwn(item, '_id')) { item._id = null; }
366
- if (recursive) {
367
- if (hasOwn(item, 'dataPointer')) {
368
- item.dataPointer = item.dataPointer.slice(itemRefPointer.length);
369
- }
370
- }
371
- }, 'top-down');
372
- }
373
-
374
- // Add any additional default items
375
- if (!newNode.recursiveReference || newNode.options.required) {
376
- const arrayLength = Math.min(Math.max(
377
- newNode.options.tupleItems + newNode.options.listItems,
378
- isArray(nodeValue) ? nodeValue.length : 0
379
- ), newNode.options.maxItems);
380
- for (let i = newNode.items.length; i < arrayLength; i++) {
381
- newNode.items.push(getLayoutNode({
382
- $ref: itemRefPointer,
383
- dataPointer: newNode.dataPointer,
384
- recursiveReference: newNode.recursiveReference,
385
- }, jsf, widgetLibrary));
386
- }
387
- }
388
-
389
- // If needed, add button to add items to array
390
- if (newNode.options.addable !== false &&
391
- newNode.options.minItems < newNode.options.maxItems &&
392
- (newNode.items[newNode.items.length - 1] || {}).type !== '$ref'
393
- ) {
394
- let buttonText = 'Add';
395
- if (newNode.options.title) {
396
- if (/^add\b/i.test(newNode.options.title)) {
397
- buttonText = newNode.options.title;
398
- } else {
399
- buttonText += ' ' + newNode.options.title;
400
- }
401
- } else if (newNode.name && !/^\d+$/.test(newNode.name)) {
402
- if (/^add\b/i.test(newNode.name)) {
403
- buttonText += ' ' + fixTitle(newNode.name);
404
- } else {
405
- buttonText = fixTitle(newNode.name);
406
- }
407
-
408
- // If newNode doesn't have a title, look for title of parent array item
409
- } else {
410
- const parentSchema =
411
- getFromSchema(jsf.schema, newNode.dataPointer, 'parentSchema');
412
- if (hasOwn(parentSchema, 'title')) {
413
- buttonText += ' to ' + parentSchema.title;
414
- } else {
415
- const pointerArray = JsonPointer.parse(newNode.dataPointer);
416
- buttonText += ' to ' + fixTitle(pointerArray[pointerArray.length - 2]);
417
- }
418
- }
419
- newNode.items.push({
420
- _id: uniqueId(),
421
- arrayItem: true,
422
- arrayItemType: 'list',
423
- dataPointer: newNode.dataPointer + '/-',
424
- options: {
425
- listItems: newNode.options.listItems,
426
- maxItems: newNode.options.maxItems,
427
- minItems: newNode.options.minItems,
428
- removable: false,
429
- title: buttonText,
430
- tupleItems: newNode.options.tupleItems,
431
- },
432
- recursiveReference: recursive,
433
- type: '$ref',
434
- widget: widgetLibrary.getWidget('$ref'),
435
- $ref: itemRefPointer,
436
- });
437
- if (isString(JsonPointer.get(newNode, '/style/add'))) {
438
- newNode.items[newNode.items.length - 1].options.fieldStyle =
439
- newNode.style.add;
440
- delete newNode.style.add;
441
- if (isEmpty(newNode.style)) { delete newNode.style; }
442
- }
443
- }
444
- } else {
445
- newNode.arrayItem = false;
446
- }
447
- } else if (hasOwn(newNode, 'type') || hasOwn(newNode, 'items')) {
448
- const parentType: string =
449
- JsonPointer.get(jsf.layout, layoutPointer, 0, -2).type;
450
- if (!hasOwn(newNode, 'type')) {
451
- newNode.type =
452
- inArray(parentType, ['tabs', 'tabarray']) ? 'tab' : 'array';
453
- }
454
- newNode.arrayItem = parentType === 'array';
455
- newNode.widget = widgetLibrary.getWidget(newNode.type);
456
- updateInputOptions(newNode, {}, jsf);
457
- }
458
- if (newNode.type === 'submit') { hasSubmitButton = true; }
459
- return newNode;
460
- });
461
- if (jsf.hasRootReference) {
462
- const fullLayout = cloneDeep(formLayout);
463
- if (fullLayout[fullLayout.length - 1].type === 'submit') { fullLayout.pop(); }
464
- jsf.layoutRefLibrary[''] = {
465
- _id: null,
466
- dataPointer: '',
467
- dataType: 'object',
468
- items: fullLayout,
469
- name: '',
470
- options: cloneDeep(jsf.formOptions.defaultWidgetOptions),
471
- recursiveReference: true,
472
- required: false,
473
- type: 'section',
474
- widget: widgetLibrary.getWidget('section'),
475
- };
476
- }
477
- if (!hasSubmitButton) {
478
- formLayout.push({
479
- _id: uniqueId(),
480
- options: { title: 'Submit' },
481
- type: 'submit',
482
- widget: widgetLibrary.getWidget('submit'),
483
- });
484
- }
485
- return formLayout;
486
- }
487
-
488
- //TODO-review:this implements a quick 'post' fix rather than an
489
- //integrared ideal fix
490
- export function buildLayout(jsf, widgetLibrary) {
491
- let layout=buildLayout_original(jsf, widgetLibrary);
492
- if (jsf.formValues) {
493
- let fixedLayout = fixNestedArrayLayout({
494
- builtLayout: layout,
495
- formData: jsf.formValues
496
- });
497
- }
498
- return layout;
499
- }
500
-
501
-
502
-
503
- function fixNestedArrayLayout(options: any) {
504
- let { builtLayout, formData } = options;
505
- let arrLengths = {};
506
- let traverseObj = function (obj, path, onValue?) {
507
- if (_isArray(obj)) {
508
- onValue && onValue(obj, path);
509
- obj.forEach((item, ind) => {
510
- onValue && onValue(item, path + "/" + ind);
511
- traverseObj(item, path + "/" + ind, onValue);
512
- });
513
- return;
514
- }
515
- if (_isPlainObject(obj)) {
516
- onValue && onValue(obj, path);
517
- Object.keys(obj).forEach(key => {
518
- onValue && onValue(obj[key], path + "/" + key);
519
- traverseObj(obj[key], path + "/" + key, onValue);
520
- });
521
- return
522
- }
523
- }
524
- traverseObj(formData, "", (value, path) => {
525
- if (_isArray(value)) {
526
- arrLengths[path] = arrLengths[path] || value.length;
527
- }
528
- });
529
-
530
- let getDataSize = (options: any) => {
531
- let { data, dataPointer, indexArray } = options;
532
- let dashCount = 0;
533
- let dpInstance = dataPointer.substring(1).split("/").map((part, pind) => {
534
- if (part == "-" && indexArray[dashCount] != undefined) {
535
- return indexArray[dashCount++];
536
- }
537
- return part;
538
- })
539
- .join("/");
540
- dpInstance = "/" + dpInstance;
541
- let arrSize = arrLengths[dpInstance];
542
- return arrSize;
543
- }
544
- //still too buggy
545
- let createNonRefItem = (nodeWithRef: any) => {
546
- let templateNode = {
547
- "type": "section", //check this could also be array?
548
- "recursiveReference": false,//check this
549
- "items": []
550
- }
551
- let clone = cloneDeep(nodeWithRef);
552
- //commented out for now so that it behaves as ususal
553
- //_.merge(clone,templateNode);
554
- return clone;
555
- }
556
-
557
- let rebuildLayout = (options: any) => {
558
- let { builtLayout, indices, parentDataPointer, indexPos } = options;
559
- indices = indices || [];
560
- indexPos = indexPos == undefined ? indexPos = -1 : indexPos;
561
- if (_isArray(builtLayout)) {
562
- builtLayout.forEach((item, index) => {
563
- rebuildLayout({
564
- builtLayout: item,
565
- indices: indices,
566
- indexPos: indexPos,
567
- parentDataPointer: builtLayout.dataPointer || parentDataPointer
568
- })
569
- })
570
- return;
571
- }
572
-
573
- let dataTypes=["array"];//check only array for now
574
- //for now added condition to ignore recursive references
575
- if (builtLayout.items && dataTypes.indexOf(builtLayout.dataType)>=0
576
- && builtLayout.dataPointer
577
- && !builtLayout.recursiveReference
578
- ) {
579
- let numDataItems: any = getDataSize({
580
- data: formData,
581
- dataPointer: builtLayout.dataPointer,
582
- indexArray: indices
583
- });
584
- let numActualItems = builtLayout.items.length;
585
- //check if there's ref items, if so ignore it and therefore
586
- //decrement the item count
587
- builtLayout.items.forEach(item => {
588
- if (item.type && item.type == "$ref") {
589
- numActualItems--;
590
- }
591
- });
592
- numActualItems = Math.max(numActualItems, 0);//avoid dealing with negatives
593
- if (numActualItems < numDataItems) {
594
-
595
- let numItemsNeeded = numDataItems - numActualItems;
596
- //added to ignore recursive references
597
- if (numActualItems == 0 && builtLayout.items[0].recursiveReference) {
598
- numItemsNeeded = 0
599
- }
600
- for (let i = 0; i < numItemsNeeded; i++) {
601
- //node must not be of type "type": "$ref"
602
- //if it is then manufacture our own
603
- let isRefNode = builtLayout.items[0].type && builtLayout.items[0].type == "$ref";
604
- let newItem = isRefNode
605
- ? createNonRefItem(builtLayout.items[0])
606
- : cloneDeep(builtLayout.items[0]);//copy first
607
- newItem._id = uniqueId("new_");
608
- builtLayout.items.unshift(newItem);
609
- }
610
- if (builtLayout.options.listItems) {
611
- builtLayout.options.listItems = numDataItems;
612
- }
613
- }
614
- indices[builtLayout.dataPointer] = indices[builtLayout.dataPointer] || -1;
615
- indexPos++;
616
- builtLayout.items.forEach((item, index) => {
617
- indices[indexPos] = index
618
- rebuildLayout({
619
- builtLayout: item,
620
- indices: indices,
621
- parentDataPointer: builtLayout.dataPointer,
622
- indexPos: indexPos
623
- })
624
- })
625
- indexPos--;
626
- } else {
627
- if (builtLayout.items) {
628
- builtLayout.items.forEach((item, index) => {
629
- rebuildLayout({
630
- builtLayout: item,
631
- indices: indices,
632
- parentDataPointer: parentDataPointer,
633
- indexPos: indexPos
634
- })
635
- })
636
-
637
- }
638
- }
639
-
640
-
641
- }
642
- rebuildLayout({
643
- builtLayout: builtLayout
644
- });
645
- //NB original is mutated
646
- let fixedLayout = builtLayout;
647
- return fixedLayout;
648
- }
649
-
650
- /**
651
- * 'buildLayoutFromSchema' function
652
- *
653
- * // jsf -
654
- * // widgetLibrary -
655
- * // nodeValue -
656
- * // { string = '' } schemaPointer -
657
- * // { string = '' } dataPointer -
658
- * // { boolean = false } arrayItem -
659
- * // { string = null } arrayItemType -
660
- * // { boolean = null } removable -
661
- * // { boolean = false } forRefLibrary -
662
- * // { string = '' } dataPointerPrefix -
663
- * //
664
- */
665
- export function buildLayoutFromSchema(
666
- jsf, widgetLibrary, nodeValue = null, schemaPointer = '',
667
- dataPointer = '', arrayItem = false, arrayItemType: string = null,
668
- removable: boolean = null, forRefLibrary = false, dataPointerPrefix = ''
669
- ) {
670
- const schema = JsonPointer.get(jsf.schema, schemaPointer);
671
- if (!hasOwn(schema, 'type') && !hasOwn(schema, '$ref') &&
672
- !hasOwn(schema, 'x-schema-form')
673
- ) { return null; }
674
- const newNodeType: string = getInputType(schema);
675
- if (!isDefined(nodeValue) && (
676
- jsf.formOptions.setSchemaDefaults === true ||
677
- (jsf.formOptions.setSchemaDefaults === 'auto' && isEmpty(jsf.formValues))
678
- )) {
679
- nodeValue = JsonPointer.get(jsf.schema, schemaPointer + '/default');
680
- }
681
- let newNode: any = {
682
- _id: forRefLibrary ? null : uniqueId(),
683
- arrayItem: arrayItem,
684
- dataPointer: JsonPointer.toGenericPointer(dataPointer, jsf.arrayMap),
685
- dataType: schema.type || (hasOwn(schema, '$ref') ? '$ref' : null),
686
- options: {},
687
- required: isInputRequired(jsf.schema, schemaPointer),
688
- type: newNodeType,
689
- widget: widgetLibrary.getWidget(newNodeType),
690
- };
691
- const lastDataKey = JsonPointer.toKey(newNode.dataPointer);
692
- if (lastDataKey !== '-') { newNode.name = lastDataKey; }
693
- if (newNode.arrayItem) {
694
- newNode.arrayItemType = arrayItemType;
695
- newNode.options.removable = removable !== false;
696
- }
697
- const shortDataPointer = removeRecursiveReferences(
698
- dataPointerPrefix + dataPointer, jsf.dataRecursiveRefMap, jsf.arrayMap
699
- );
700
- const recursive = !shortDataPointer.length ||
701
- shortDataPointer !== dataPointerPrefix + dataPointer;
702
- if (!jsf.dataMap.has(shortDataPointer)) {
703
- jsf.dataMap.set(shortDataPointer, new Map());
704
- }
705
- const nodeDataMap = jsf.dataMap.get(shortDataPointer);
706
- if (!nodeDataMap.has('inputType')) {
707
- nodeDataMap.set('schemaPointer', schemaPointer);
708
- nodeDataMap.set('inputType', newNode.type);
709
- nodeDataMap.set('widget', newNode.widget);
710
- nodeDataMap.set('disabled', !!newNode.options.disabled);
711
- }
712
- updateInputOptions(newNode, schema, jsf);
713
- if (!newNode.options.title && newNode.name && !/^\d+$/.test(newNode.name)) {
714
- newNode.options.title = fixTitle(newNode.name);
715
- }
716
-
717
- if (newNode.dataType === 'object') {
718
- if (isArray(schema.required) && !nodeDataMap.has('required')) {
719
- nodeDataMap.set('required', schema.required);
720
- }
721
- if (isObject(schema.properties)) {
722
- const newSection: any[] = [];
723
- const propertyKeys = schema['ui:order'] || Object.keys(schema.properties);
724
- if (propertyKeys.includes('*') && !hasOwn(schema.properties, '*')) {
725
- const unnamedKeys = Object.keys(schema.properties)
726
- .filter(key => !propertyKeys.includes(key));
727
- for (let i = propertyKeys.length - 1; i >= 0; i--) {
728
- if (propertyKeys[i] === '*') {
729
- propertyKeys.splice(i, 1, ...unnamedKeys);
730
- }
731
- }
732
- }
733
- propertyKeys
734
- .filter(key => hasOwn(schema.properties, key) ||
735
- hasOwn(schema, 'additionalProperties')
736
- )
737
- .forEach(key => {
738
- const keySchemaPointer = hasOwn(schema.properties, key) ?
739
- '/properties/' + key : '/additionalProperties';
740
- const innerItem = buildLayoutFromSchema(
741
- jsf, widgetLibrary, isObject(nodeValue) ? nodeValue[key] : null,
742
- schemaPointer + keySchemaPointer,
743
- dataPointer + '/' + key,
744
- false, null, null, forRefLibrary, dataPointerPrefix
745
- );
746
- if (innerItem) {
747
- if (isInputRequired(schema, '/' + key)) {
748
- innerItem.options.required = true;
749
- jsf.fieldsRequired = true;
750
- }
751
- newSection.push(innerItem);
752
- }
753
- });
754
- if (dataPointer === '' && !forRefLibrary) {
755
- newNode = newSection;
756
- } else {
757
- newNode.items = newSection;
758
- }
759
- }
760
- // TODO: Add patternProperties and additionalProperties inputs?
761
- // ... possibly provide a way to enter both key names and values?
762
- // if (isObject(schema.patternProperties)) { }
763
- // if (isObject(schema.additionalProperties)) { }
764
-
765
- } else if (newNode.dataType === 'array') {
766
- newNode.items = [];
767
- newNode.options.maxItems = Math.min(
768
- schema.maxItems || 1000, newNode.options.maxItems || 1000
769
- );
770
- newNode.options.minItems = Math.max(
771
- schema.minItems || 0, newNode.options.minItems || 0
772
- );
773
- if (!newNode.options.minItems && isInputRequired(jsf.schema, schemaPointer)) {
774
- newNode.options.minItems = 1;
775
- }
776
- if (!hasOwn(newNode.options, 'listItems')) { newNode.options.listItems = 1; }
777
- newNode.options.tupleItems = isArray(schema.items) ? schema.items.length : 0;
778
- if (newNode.options.maxItems <= newNode.options.tupleItems) {
779
- newNode.options.tupleItems = newNode.options.maxItems;
780
- newNode.options.listItems = 0;
781
- } else if (newNode.options.maxItems <
782
- newNode.options.tupleItems + newNode.options.listItems
783
- ) {
784
- newNode.options.listItems = newNode.options.maxItems - newNode.options.tupleItems;
785
- } else if (newNode.options.minItems >
786
- newNode.options.tupleItems + newNode.options.listItems
787
- ) {
788
- newNode.options.listItems = newNode.options.minItems - newNode.options.tupleItems;
789
- }
790
- if (!nodeDataMap.has('maxItems')) {
791
- nodeDataMap.set('maxItems', newNode.options.maxItems);
792
- nodeDataMap.set('minItems', newNode.options.minItems);
793
- nodeDataMap.set('tupleItems', newNode.options.tupleItems);
794
- nodeDataMap.set('listItems', newNode.options.listItems);
795
- }
796
- if (!jsf.arrayMap.has(shortDataPointer)) {
797
- jsf.arrayMap.set(shortDataPointer, newNode.options.tupleItems);
798
- }
799
- removable = newNode.options.removable !== false;
800
- let additionalItemsSchemaPointer: string = null;
801
-
802
- // If 'items' is an array = tuple items
803
- if (isArray(schema.items)) {
804
- newNode.items = [];
805
- for (let i = 0; i < newNode.options.tupleItems; i++) {
806
- let newItem: any;
807
- const itemRefPointer = removeRecursiveReferences(
808
- shortDataPointer + '/' + i, jsf.dataRecursiveRefMap, jsf.arrayMap
809
- );
810
- const itemRecursive = !itemRefPointer.length ||
811
- itemRefPointer !== shortDataPointer + '/' + i;
812
-
813
- // If removable, add tuple item layout to layoutRefLibrary
814
- if (removable && i >= newNode.options.minItems) {
815
- if (!hasOwn(jsf.layoutRefLibrary, itemRefPointer)) {
816
- // Set to null first to prevent recursive reference from causing endless loop
817
- jsf.layoutRefLibrary[itemRefPointer] = null;
818
- jsf.layoutRefLibrary[itemRefPointer] = buildLayoutFromSchema(
819
- jsf, widgetLibrary, isArray(nodeValue) ? nodeValue[i] : null,
820
- schemaPointer + '/items/' + i,
821
- itemRecursive ? '' : dataPointer + '/' + i,
822
- true, 'tuple', true, true, itemRecursive ? dataPointer + '/' + i : ''
823
- );
824
- if (itemRecursive) {
825
- jsf.layoutRefLibrary[itemRefPointer].recursiveReference = true;
826
- }
827
- }
828
- newItem = getLayoutNode({
829
- $ref: itemRefPointer,
830
- dataPointer: dataPointer + '/' + i,
831
- recursiveReference: itemRecursive,
832
- }, jsf, widgetLibrary, isArray(nodeValue) ? nodeValue[i] : null);
833
- } else {
834
- newItem = buildLayoutFromSchema(
835
- jsf, widgetLibrary, isArray(nodeValue) ? nodeValue[i] : null,
836
- schemaPointer + '/items/' + i,
837
- dataPointer + '/' + i,
838
- true, 'tuple', false, forRefLibrary, dataPointerPrefix
839
- );
840
- }
841
- if (newItem) { newNode.items.push(newItem); }
842
- }
843
-
844
- // If 'additionalItems' is an object = additional list items, after tuple items
845
- if (isObject(schema.additionalItems)) {
846
- additionalItemsSchemaPointer = schemaPointer + '/additionalItems';
847
- }
848
-
849
- // If 'items' is an object = list items only (no tuple items)
850
- } else if (isObject(schema.items)) {
851
- additionalItemsSchemaPointer = schemaPointer + '/items';
852
- }
853
-
854
- if (additionalItemsSchemaPointer) {
855
- const itemRefPointer = removeRecursiveReferences(
856
- shortDataPointer + '/-', jsf.dataRecursiveRefMap, jsf.arrayMap
857
- );
858
- const itemRecursive = !itemRefPointer.length ||
859
- itemRefPointer !== shortDataPointer + '/-';
860
- const itemSchemaPointer = removeRecursiveReferences(
861
- additionalItemsSchemaPointer, jsf.schemaRecursiveRefMap, jsf.arrayMap
862
- );
863
- // Add list item layout to layoutRefLibrary
864
- if (itemRefPointer.length && !hasOwn(jsf.layoutRefLibrary, itemRefPointer)) {
865
- // Set to null first to prevent recursive reference from causing endless loop
866
- jsf.layoutRefLibrary[itemRefPointer] = null;
867
- jsf.layoutRefLibrary[itemRefPointer] = buildLayoutFromSchema(
868
- jsf, widgetLibrary, null,
869
- itemSchemaPointer,
870
- itemRecursive ? '' : dataPointer + '/-',
871
- true, 'list', removable, true, itemRecursive ? dataPointer + '/-' : ''
872
- );
873
- if (itemRecursive) {
874
- jsf.layoutRefLibrary[itemRefPointer].recursiveReference = true;
875
- }
876
- }
877
-
878
- // Add any additional default items
879
- if (!itemRecursive || newNode.options.required) {
880
- const arrayLength = Math.min(Math.max(
881
- itemRecursive ? 0 :
882
- newNode.options.tupleItems + newNode.options.listItems,
883
- isArray(nodeValue) ? nodeValue.length : 0
884
- ), newNode.options.maxItems);
885
- if (newNode.items.length < arrayLength) {
886
- for (let i = newNode.items.length; i < arrayLength; i++) {
887
- newNode.items.push(getLayoutNode({
888
- $ref: itemRefPointer,
889
- dataPointer: dataPointer + '/-',
890
- recursiveReference: itemRecursive,
891
- }, jsf, widgetLibrary, isArray(nodeValue) ? nodeValue[i] : null));
892
- }
893
- }
894
- }
895
-
896
- // If needed, add button to add items to array
897
- if (newNode.options.addable !== false &&
898
- newNode.options.minItems < newNode.options.maxItems &&
899
- (newNode.items[newNode.items.length - 1] || {}).type !== '$ref'
900
- ) {
901
- let buttonText =
902
- ((jsf.layoutRefLibrary[itemRefPointer] || {}).options || {}).title;
903
- const prefix = buttonText ? 'Add ' : 'Add to ';
904
- if (!buttonText) {
905
- buttonText = schema.title || fixTitle(JsonPointer.toKey(dataPointer));
906
- }
907
- if (!/^add\b/i.test(buttonText)) { buttonText = prefix + buttonText; }
908
- newNode.items.push({
909
- _id: uniqueId(),
910
- arrayItem: true,
911
- arrayItemType: 'list',
912
- dataPointer: newNode.dataPointer + '/-',
913
- options: {
914
- listItems: newNode.options.listItems,
915
- maxItems: newNode.options.maxItems,
916
- minItems: newNode.options.minItems,
917
- removable: false,
918
- title: buttonText,
919
- tupleItems: newNode.options.tupleItems,
920
- },
921
- recursiveReference: itemRecursive,
922
- type: '$ref',
923
- widget: widgetLibrary.getWidget('$ref'),
924
- $ref: itemRefPointer,
925
- });
926
- }
927
- }
928
-
929
- } else if (newNode.dataType === '$ref') {
930
- const schemaRef = JsonPointer.compile(schema.$ref);
931
- const dataRef = JsonPointer.toDataPointer(schemaRef, jsf.schema);
932
- let buttonText = '';
933
-
934
- // Get newNode title
935
- if (newNode.options.add) {
936
- buttonText = newNode.options.add;
937
- } else if (newNode.name && !/^\d+$/.test(newNode.name)) {
938
- buttonText =
939
- (/^add\b/i.test(newNode.name) ? '' : 'Add ') + fixTitle(newNode.name);
940
-
941
- // If newNode doesn't have a title, look for title of parent array item
942
- } else {
943
- const parentSchema =
944
- JsonPointer.get(jsf.schema, schemaPointer, 0, -1);
945
- if (hasOwn(parentSchema, 'title')) {
946
- buttonText = 'Add to ' + parentSchema.title;
947
- } else {
948
- const pointerArray = JsonPointer.parse(newNode.dataPointer);
949
- buttonText = 'Add to ' + fixTitle(pointerArray[pointerArray.length - 2]);
950
- }
951
- }
952
- Object.assign(newNode, {
953
- recursiveReference: true,
954
- widget: widgetLibrary.getWidget('$ref'),
955
- $ref: dataRef,
956
- });
957
- Object.assign(newNode.options, {
958
- removable: false,
959
- title: buttonText,
960
- });
961
- if (isNumber(JsonPointer.get(jsf.schema, schemaPointer, 0, -1).maxItems)) {
962
- newNode.options.maxItems =
963
- JsonPointer.get(jsf.schema, schemaPointer, 0, -1).maxItems;
964
- }
965
-
966
- // Add layout template to layoutRefLibrary
967
- if (dataRef.length) {
968
- if (!hasOwn(jsf.layoutRefLibrary, dataRef)) {
969
- // Set to null first to prevent recursive reference from causing endless loop
970
- jsf.layoutRefLibrary[dataRef] = null;
971
- const newLayout = buildLayoutFromSchema(
972
- jsf, widgetLibrary, null, schemaRef, '',
973
- newNode.arrayItem, newNode.arrayItemType, true, true, dataPointer
974
- );
975
- if (newLayout) {
976
- newLayout.recursiveReference = true;
977
- jsf.layoutRefLibrary[dataRef] = newLayout;
978
- } else {
979
- delete jsf.layoutRefLibrary[dataRef];
980
- }
981
- } else if (!jsf.layoutRefLibrary[dataRef].recursiveReference) {
982
- jsf.layoutRefLibrary[dataRef].recursiveReference = true;
983
- }
984
- }
985
- }
986
- return newNode;
987
- }
988
-
989
- /**
990
- * 'mapLayout' function
991
- *
992
- * Creates a new layout by running each element in an existing layout through
993
- * an iteratee. Recursively maps within array elements 'items' and 'tabs'.
994
- * The iteratee is invoked with four arguments: (value, index, layout, path)
995
- *
996
- * The returned layout may be longer (or shorter) then the source layout.
997
- *
998
- * If an item from the source layout returns multiple items (as '*' usually will),
999
- * this function will keep all returned items in-line with the surrounding items.
1000
- *
1001
- * If an item from the source layout causes an error and returns null, it is
1002
- * skipped without error, and the function will still return all non-null items.
1003
- *
1004
- * // layout - the layout to map
1005
- * // { (v: any, i?: number, l?: any, p?: string) => any }
1006
- * function - the funciton to invoke on each element
1007
- * // { string|string[] = '' } layoutPointer - the layoutPointer to layout, inside rootLayout
1008
- * // { any[] = layout } rootLayout - the root layout, which conatins layout
1009
- * //
1010
- */
1011
- export function mapLayout(layout, fn, layoutPointer = '', rootLayout = layout) {
1012
- let indexPad = 0;
1013
- let newLayout: any[] = [];
1014
- forEach(layout, (item, index) => {
1015
- const realIndex = +index + indexPad;
1016
- const newLayoutPointer = layoutPointer + '/' + realIndex;
1017
- let newNode: any = copy(item);
1018
- let itemsArray: any[] = [];
1019
- if (isObject(item)) {
1020
- if (hasOwn(item, 'tabs')) {
1021
- item.items = item.tabs;
1022
- delete item.tabs;
1023
- }
1024
- if (hasOwn(item, 'items')) {
1025
- itemsArray = isArray(item.items) ? item.items : [item.items];
1026
- }
1027
- }
1028
- if (itemsArray.length) {
1029
- newNode.items = mapLayout(itemsArray, fn, newLayoutPointer + '/items', rootLayout);
1030
- }
1031
- newNode = fn(newNode, realIndex, newLayoutPointer, rootLayout);
1032
- if (!isDefined(newNode)) {
1033
- indexPad--;
1034
- } else {
1035
- if (isArray(newNode)) { indexPad += newNode.length - 1; }
1036
- newLayout = newLayout.concat(newNode);
1037
- }
1038
- });
1039
- return newLayout;
1040
- }
1041
-
1042
- /**
1043
- * 'getLayoutNode' function
1044
- * Copy a new layoutNode from layoutRefLibrary
1045
- *
1046
- * // refNode -
1047
- * // layoutRefLibrary -
1048
- * // { any = null } widgetLibrary -
1049
- * // { any = null } nodeValue -
1050
- * // copied layoutNode
1051
- */
1052
- export function getLayoutNode(
1053
- refNode, jsf, widgetLibrary: any = null, nodeValue: any = null
1054
- ) {
1055
-
1056
- // If recursive reference and building initial layout, return Add button
1057
- if (refNode.recursiveReference && widgetLibrary) {
1058
- const newLayoutNode = cloneDeep(refNode);
1059
- if (!newLayoutNode.options) { newLayoutNode.options = {}; }
1060
- Object.assign(newLayoutNode, {
1061
- recursiveReference: true,
1062
- widget: widgetLibrary.getWidget('$ref'),
1063
- });
1064
- Object.assign(newLayoutNode.options, {
1065
- removable: false,
1066
- title: 'Add ' + newLayoutNode.$ref,
1067
- });
1068
- return newLayoutNode;
1069
-
1070
- // Otherwise, return referenced layout
1071
- } else {
1072
- let newLayoutNode = jsf.layoutRefLibrary[refNode.$ref];
1073
- // If value defined, build new node from schema (to set array lengths)
1074
- if (isDefined(nodeValue)) {
1075
- newLayoutNode = buildLayoutFromSchema(
1076
- jsf, widgetLibrary, nodeValue,
1077
- JsonPointer.toSchemaPointer(refNode.$ref, jsf.schema),
1078
- refNode.$ref, newLayoutNode.arrayItem,
1079
- newLayoutNode.arrayItemType, newLayoutNode.options.removable, false
1080
- );
1081
- } else {
1082
- // If value not defined, copy node from layoutRefLibrary
1083
- newLayoutNode = cloneDeep(newLayoutNode);
1084
- JsonPointer.forEachDeep(newLayoutNode, (subNode, pointer) => {
1085
-
1086
- // Reset all _id's in newLayoutNode to unique values
1087
- if (hasOwn(subNode, '_id')) { subNode._id = uniqueId(); }
1088
-
1089
- // If adding a recursive item, prefix current dataPointer
1090
- // to all dataPointers in new layoutNode
1091
- if (refNode.recursiveReference && hasOwn(subNode, 'dataPointer')) {
1092
- subNode.dataPointer = refNode.dataPointer + subNode.dataPointer;
1093
- }
1094
- });
1095
- }
1096
- return newLayoutNode;
1097
- }
1098
- }
1099
-
1100
- /**
1101
- * 'buildTitleMap' function
1102
- *
1103
- * // titleMap -
1104
- * // enumList -
1105
- * // { boolean = true } fieldRequired -
1106
- * // { boolean = true } flatList -
1107
- * // { TitleMapItem[] }
1108
- */
1109
- export function buildTitleMap(
1110
- titleMap, enumList, fieldRequired = true, flatList = true
1111
- ) {
1112
- let newTitleMap: TitleMapItem[] = [];
1113
- let hasEmptyValue = false;
1114
- if (titleMap) {
1115
- if (isArray(titleMap)) {
1116
- if (enumList) {
1117
- for (const i of Object.keys(titleMap)) {
1118
- if (isObject(titleMap[i])) { // JSON Form style
1119
- const value = titleMap[i].value;
1120
- if (enumList.includes(value)) {
1121
- const name = titleMap[i].name;
1122
- newTitleMap.push({ name, value });
1123
- if (value === undefined || value === null) { hasEmptyValue = true; }
1124
- }
1125
- } else if (isString(titleMap[i])) { // React Jsonschema Form style
1126
- if (i < enumList.length) {
1127
- const name = titleMap[i];
1128
- const value = enumList[i];
1129
- newTitleMap.push({ name, value });
1130
- if (value === undefined || value === null) { hasEmptyValue = true; }
1131
- }
1132
- }
1133
- }
1134
- } else { // If array titleMap and no enum list, just return the titleMap - Angular Schema Form style
1135
- newTitleMap = titleMap;
1136
- if (!fieldRequired) {
1137
- hasEmptyValue = !!newTitleMap
1138
- .filter(i => i.value === undefined || i.value === null)
1139
- .length;
1140
- }
1141
- }
1142
- } else if (enumList) { // Alternate JSON Form style, with enum list
1143
- for (const i of Object.keys(enumList)) {
1144
- const value = enumList[i];
1145
- if (hasOwn(titleMap, value)) {
1146
- const name = titleMap[value];
1147
- newTitleMap.push({ name, value });
1148
- if (value === undefined || value === null) { hasEmptyValue = true; }
1149
- }
1150
- }
1151
- } else { // Alternate JSON Form style, without enum list
1152
- for (const value of Object.keys(titleMap)) {
1153
- const name = titleMap[value];
1154
- newTitleMap.push({ name, value });
1155
- if (value === undefined || value === null) { hasEmptyValue = true; }
1156
- }
1157
- }
1158
- } else if (enumList) { // Build map from enum list alone
1159
- for (const i of Object.keys(enumList)) {
1160
- const name = enumList[i];
1161
- const value = enumList[i];
1162
- newTitleMap.push({ name, value });
1163
- if (value === undefined || value === null) { hasEmptyValue = true; }
1164
- }
1165
- } else { // If no titleMap and no enum list, return default map of boolean values
1166
- newTitleMap = [{ name: 'True', value: true }, { name: 'False', value: false }];
1167
- }
1168
-
1169
- // Does titleMap have groups?
1170
- if (newTitleMap.some(title => hasOwn(title, 'group'))) {
1171
- hasEmptyValue = false;
1172
-
1173
- // If flatList = true, flatten items & update name to group: name
1174
- if (flatList) {
1175
- newTitleMap = newTitleMap.reduce((groupTitleMap, title) => {
1176
- if (hasOwn(title, 'group')) {
1177
- if (isArray(title.items)) {
1178
- groupTitleMap = [
1179
- ...groupTitleMap,
1180
- ...title.items.map(item =>
1181
- ({ ...item, ...{ name: `${title.group}: ${item.name}` } })
1182
- )
1183
- ];
1184
- if (title.items.some(item => item.value === undefined || item.value === null)) {
1185
- hasEmptyValue = true;
1186
- }
1187
- }
1188
- if (hasOwn(title, 'name') && hasOwn(title, 'value')) {
1189
- title.name = `${title.group}: ${title.name}`;
1190
- delete title.group;
1191
- groupTitleMap.push(title);
1192
- if (title.value === undefined || title.value === null) {
1193
- hasEmptyValue = true;
1194
- }
1195
- }
1196
- } else {
1197
- groupTitleMap.push(title);
1198
- if (title.value === undefined || title.value === null) {
1199
- hasEmptyValue = true;
1200
- }
1201
- }
1202
- return groupTitleMap;
1203
- }, []);
1204
-
1205
- // If flatList = false, combine items from matching groups
1206
- } else {
1207
- newTitleMap = newTitleMap.reduce((groupTitleMap, title) => {
1208
- if (hasOwn(title, 'group')) {
1209
- if (title.group !== (groupTitleMap[groupTitleMap.length - 1] || {}).group) {
1210
- groupTitleMap.push({ group: title.group, items: title.items || [] });
1211
- }
1212
- if (hasOwn(title, 'name') && hasOwn(title, 'value')) {
1213
- groupTitleMap[groupTitleMap.length - 1].items
1214
- .push({ name: title.name, value: title.value });
1215
- if (title.value === undefined || title.value === null) {
1216
- hasEmptyValue = true;
1217
- }
1218
- }
1219
- } else {
1220
- groupTitleMap.push(title);
1221
- if (title.value === undefined || title.value === null) {
1222
- hasEmptyValue = true;
1223
- }
1224
- }
1225
- return groupTitleMap;
1226
- }, []);
1227
- }
1228
- }
1229
- if (!fieldRequired && !hasEmptyValue) {
1230
- newTitleMap.unshift({ name: '<em>None</em>', value: null });
1231
- }
1232
- return newTitleMap;
1233
- }