apostrophe 3.58.0 → 3.59.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 (41) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/DEVELOPMENT.md +0 -12
  3. package/modules/@apostrophecms/area/index.js +7 -0
  4. package/modules/@apostrophecms/asset/index.js +0 -7
  5. package/modules/@apostrophecms/asset/lib/webpack/apos/webpack.config.js +4 -8
  6. package/modules/@apostrophecms/asset/lib/webpack/apos/webpack.vue.js +1 -1
  7. package/modules/@apostrophecms/asset/lib/webpack/src/webpack.config.js +1 -7
  8. package/modules/@apostrophecms/doc-type/index.js +3 -1
  9. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +11 -4
  10. package/modules/@apostrophecms/express/index.js +28 -0
  11. package/modules/@apostrophecms/modal/ui/apos/mixins/AposDocErrorsMixin.js +5 -4
  12. package/modules/@apostrophecms/modal/ui/apos/mixins/AposEditorMixin.js +11 -8
  13. package/modules/@apostrophecms/modal/ui/apos/mixins/AposModalTabsMixin.js +4 -3
  14. package/modules/@apostrophecms/module/index.js +6 -0
  15. package/modules/@apostrophecms/piece-page-type/index.js +9 -3
  16. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposImageControlDialog.vue +10 -3
  17. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapAnchor.vue +6 -1
  18. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapLink.vue +12 -3
  19. package/modules/@apostrophecms/schema/index.js +105 -51
  20. package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +3 -3
  21. package/modules/@apostrophecms/schema/ui/apos/components/AposInputArray.vue +10 -6
  22. package/modules/@apostrophecms/schema/ui/apos/components/AposInputObject.vue +5 -3
  23. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRadio.vue +16 -11
  24. package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +2 -1
  25. package/modules/@apostrophecms/schema/ui/apos/components/AposSubform.vue +3 -3
  26. package/modules/@apostrophecms/schema/ui/apos/lib/conditionalFields.js +76 -71
  27. package/modules/@apostrophecms/schema/ui/apos/logic/AposArrayEditor.js +9 -1
  28. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArray.js +48 -20
  29. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputDateAndTime.js +10 -1
  30. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputObject.js +8 -3
  31. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRadio.js +3 -2
  32. package/modules/@apostrophecms/schema/ui/apos/logic/AposSchema.js +45 -25
  33. package/modules/@apostrophecms/schema/ui/apos/logic/AposSubform.js +4 -1
  34. package/modules/@apostrophecms/schema/ui/apos/mixins/AposInputConditionalFieldsMixin.js +11 -4
  35. package/modules/@apostrophecms/schema/ui/apos/mixins/AposInputFollowingMixin.js +1 -1
  36. package/modules/@apostrophecms/schema/ui/apos/mixins/AposInputMixin.js +4 -6
  37. package/modules/@apostrophecms/template/index.js +99 -31
  38. package/modules/@apostrophecms/widget-type/ui/apos/components/AposWidgetEditor.vue +5 -2
  39. package/package.json +3 -4
  40. package/test/pieces.js +15 -0
  41. package/test/schemas.js +1700 -0
@@ -374,6 +374,25 @@ module.exports = {
374
374
  // All fields should have an initial value in the database
375
375
  instance[field.name] = null;
376
376
  }
377
+ // A workaround specifically for areas. They must have a
378
+ // unique `_id` which makes `klona` a poor way to establish
379
+ // a default, and we don't pass functions in schema
380
+ // definitions, but top-level areas should always exist
381
+ // for reasonable results if the output of `newInstance`
382
+ // is saved without further editing on the front end
383
+ if ((field.type === 'area') && (!instance[field.name])) {
384
+ instance[field.name] = {
385
+ metaType: 'area',
386
+ items: [],
387
+ _id: self.apos.util.generateId()
388
+ };
389
+ }
390
+ // A workaround specifically for objects. These too need
391
+ // to have reasonable values in parked pages and any other
392
+ // situation where the data never passes through the UI
393
+ if ((field.type === 'object') && ((!instance[field.name]) || _.isEmpty(instance[field.name]))) {
394
+ instance[field.name] = self.newInstance(field.schema);
395
+ }
377
396
  }
378
397
  return instance;
379
398
  },
@@ -465,6 +484,78 @@ module.exports = {
465
484
  });
466
485
  },
467
486
 
487
+ async evaluateCondition(req, field, clause, destination, conditionalFields) {
488
+ for (const [ key, val ] of Object.entries(clause)) {
489
+ const destinationKey = _.get(destination, key);
490
+
491
+ if (key === '$or') {
492
+ const results = await Promise.all(val.map(clause => self.evaluateCondition(req, field, clause, destination, conditionalFields)));
493
+ const testResults = _.isPlainObject(results?.[0])
494
+ ? results.some(({ value }) => value)
495
+ : results.some((value) => value);
496
+ if (!testResults) {
497
+ return false;
498
+ }
499
+ continue;
500
+ } else if (val.$ne) {
501
+ // eslint-disable-next-line eqeqeq
502
+ if (val.$ne == destinationKey) {
503
+ return false;
504
+ }
505
+ }
506
+
507
+ // Handle external conditions:
508
+ // - `if: { 'methodName()': true }`
509
+ // - `if: { 'moduleName:methodName()': 'expected value' }`
510
+ // Checking if key ends with a closing parenthesis here to throw later if any argument is passed.
511
+ if (key.endsWith(')')) {
512
+ let externalConditionResult;
513
+
514
+ try {
515
+ externalConditionResult = await self.evaluateMethod(req, key, field.name, field.moduleName, destination._id);
516
+ } catch (error) {
517
+ throw self.apos.error('invalid', error.message);
518
+ }
519
+
520
+ if (externalConditionResult !== val) {
521
+ return false;
522
+ };
523
+
524
+ // Stop there, this is an external condition thus
525
+ // does not need to be checked against doc fields.
526
+ continue;
527
+ }
528
+
529
+ if (val.min && destinationKey < val.min) {
530
+ return false;
531
+ }
532
+ if (val.max && destinationKey > val.max) {
533
+ return false;
534
+ }
535
+
536
+ if (conditionalFields?.[key] === false) {
537
+ return false;
538
+ }
539
+
540
+ if (typeof val === 'boolean' && !destinationKey) {
541
+ return false;
542
+ }
543
+
544
+ // eslint-disable-next-line eqeqeq
545
+ if ((typeof val === 'string' || typeof val === 'number') && destinationKey != val) {
546
+ return false;
547
+ }
548
+ }
549
+
550
+ return true;
551
+ },
552
+
553
+ async isFieldRequired(req, field, destination) {
554
+ return field.requiredIf
555
+ ? await self.evaluateCondition(req, field, field.requiredIf, destination)
556
+ : field.required;
557
+ },
558
+
468
559
  // Convert submitted `data` object according to `schema`, sanitizing it
469
560
  // and populating the appropriate properties of `destination` with it.
470
561
  //
@@ -504,7 +595,16 @@ module.exports = {
504
595
 
505
596
  if (convert) {
506
597
  try {
507
- await convert(req, field, data, destination);
598
+ const isRequired = await self.isFieldRequired(req, field, destination);
599
+ await convert(
600
+ req,
601
+ {
602
+ ...field,
603
+ required: isRequired
604
+ },
605
+ data,
606
+ destination
607
+ );
508
608
  } catch (error) {
509
609
  if (Array.isArray(error)) {
510
610
  const invalid = self.apos.error('invalid', {
@@ -572,7 +672,7 @@ module.exports = {
572
672
  for (const field of schema) {
573
673
  if (field.if) {
574
674
  try {
575
- const result = await evaluate(field.if, field.name, field.moduleName);
675
+ const result = await self.evaluateCondition(req, field, field.if, object, conditionalFields);
576
676
  const previous = conditionalFields[field.name];
577
677
  if (previous !== result) {
578
678
  change = true;
@@ -598,55 +698,6 @@ module.exports = {
598
698
  } else {
599
699
  return true;
600
700
  }
601
- async function evaluate(clause, fieldName, fieldModuleName) {
602
- let result = true;
603
- for (const [ key, val ] of Object.entries(clause)) {
604
- if (key === '$or') {
605
- const results = await Promise.all(val.map(clause => evaluate(clause, fieldName, fieldModuleName)));
606
-
607
- if (!results.some(({ value }) => value)) {
608
- result = false;
609
- break;
610
- }
611
-
612
- // No need to go further here, the key is an "$or" condition...
613
- continue;
614
- }
615
-
616
- // Handle external conditions:
617
- // - `if: { 'methodName()': true }`
618
- // - `if: { 'moduleName:methodName()': 'expected value' }`
619
- // Checking if key ends with a closing parenthesis here to throw later if any argument is passed.
620
- if (key.endsWith(')')) {
621
- let externalConditionResult;
622
-
623
- try {
624
- externalConditionResult = await self.evaluateMethod(req, key, fieldName, fieldModuleName, object._id);
625
- } catch (error) {
626
- throw self.apos.error('invalid', error.message);
627
- }
628
-
629
- if (externalConditionResult !== val) {
630
- result = false;
631
- break;
632
- };
633
-
634
- // Stop there, this is an external condition thus
635
- // does not need to be checked against doc fields.
636
- continue;
637
- }
638
-
639
- if (conditionalFields[key] === false) {
640
- result = false;
641
- break;
642
- }
643
- if (val !== object[key]) {
644
- result = false;
645
- break;
646
- }
647
- }
648
- return result;
649
- }
650
701
  },
651
702
 
652
703
  async evaluateMethod(req, methodKey, fieldName, fieldModuleName, docId = null, optionalParenthesis = false) {
@@ -1246,6 +1297,9 @@ module.exports = {
1246
1297
  if (field.if && field.if.$or && !Array.isArray(field.if.$or)) {
1247
1298
  fail(`$or conditional must be an array of conditions. Current $or configuration: ${JSON.stringify(field.if.$or)}`);
1248
1299
  }
1300
+ if (field.requiredIf && field.requiredIf.$or && !Array.isArray(field.requiredIf.$or)) {
1301
+ fail(`$or conditional must be an array of conditions. Current $or configuration: ${JSON.stringify(field.requiredIf.$or)}`);
1302
+ }
1249
1303
  if (!field.editPermission && field.permission) {
1250
1304
  field.editPermission = field.permission;
1251
1305
  }
@@ -65,13 +65,13 @@
65
65
  :schema="schema"
66
66
  :trigger-validation="triggerValidation"
67
67
  :following-values="followingValues()"
68
- :conditional-fields="conditionalFields()"
68
+ :conditional-fields="conditionalFields"
69
69
  :value="currentDoc"
70
- @input="currentDocUpdate"
71
- @validate="triggerValidate"
72
70
  :server-errors="currentDocServerErrors"
73
71
  ref="schema"
74
72
  :doc-id="docId"
73
+ @input="currentDocUpdate"
74
+ @validate="triggerValidate"
75
75
  />
76
76
  </div>
77
77
  </div>
@@ -1,7 +1,9 @@
1
1
  <template>
2
2
  <AposInputWrapper
3
- :field="field" :error="effectiveError"
4
- :uid="uid" :items="next"
3
+ :field="field"
4
+ :error="effectiveError"
5
+ :uid="uid"
6
+ :items="next"
5
7
  :display-options="displayOptions"
6
8
  >
7
9
  <template #additional>
@@ -54,19 +56,21 @@
54
56
  :id="listId"
55
57
  >
56
58
  <AposSchema
59
+ v-model="item.schemaInput"
57
60
  v-for="(item, index) in items"
58
- :key="item._id"
59
- :schema="schema"
60
61
  class="apos-input-array-inline-item"
61
62
  :class="item.open && !alwaysExpand ? 'apos-input-array-inline-item--active' : null"
62
- v-model="item.schemaInput"
63
+ :key="item._id"
64
+ :schema="schema"
63
65
  :trigger-validation="triggerValidation"
64
66
  :generation="generation"
65
67
  :modifiers="['small', 'inverted']"
66
68
  :doc-id="docId"
67
69
  :following-values="getFollowingValues(item)"
68
- :conditional-fields="conditionalFields(item.schemaInput?.data || {})"
70
+ :conditional-fields="itemsConditionalFields[item._id]"
69
71
  :field-style="field.style"
72
+ @input="setItemsConditionalFields(item._id)"
73
+ @validate="emitValidate()"
70
74
  >
71
75
  <template #before>
72
76
  <component
@@ -11,14 +11,16 @@
11
11
  <div class="apos-input-object">
12
12
  <div class="apos-input-wrapper">
13
13
  <AposSchema
14
+ v-model="schemaInput"
15
+ ref="schema"
14
16
  :schema="schema"
15
17
  :trigger-validation="triggerValidation"
16
18
  :generation="generation"
17
19
  :doc-id="docId"
18
- v-model="schemaInput"
19
- :conditional-fields="conditionalFields(values)"
20
+ :conditional-fields="conditionalFields"
20
21
  :following-values="followingValuesWithParent"
21
- ref="schema"
22
+ @input="evaluateConditions(values)"
23
+ @validate="emitValidate()"
22
24
  />
23
25
  </div>
24
26
  </div>
@@ -7,31 +7,36 @@
7
7
  >
8
8
  <template #body>
9
9
  <label
10
- class="apos-choice-label" :for="getChoiceId(uid, choice.value)"
11
- v-for="choice in choices" :key="choice.value"
10
+ class="apos-choice-label"
11
+ v-for="{label, value, tooltip} in choices"
12
+ :key="value"
13
+ :for="getChoiceId(uid, value)"
12
14
  :class="{'apos-choice-label--disabled': field.readOnly}"
13
15
  >
14
16
  <input
15
- type="radio" class="apos-sr-only apos-input--choice apos-input--radio"
16
- :value="JSON.stringify(choice.value)" :name="field.name"
17
- :id="getChoiceId(uid, choice.value)"
18
- :checked="next === choice.value"
17
+ type="radio"
18
+ class="apos-sr-only apos-input--choice apos-input--radio"
19
+ :id="getChoiceId(uid, value)"
20
+ :value="JSON.stringify(value)"
21
+ :name="field.name"
22
+ :checked="next === value"
19
23
  tabindex="1"
20
24
  :disabled="field.readOnly"
21
25
  @change="change($event.target.value)"
22
26
  >
23
27
  <span class="apos-input-indicator" aria-hidden="true">
24
28
  <component
25
- :is="`${next === choice.value ? 'check-bold-icon' : 'span'}`"
26
- :size="8" v-if="next === choice.value"
29
+ v-if="next === value"
30
+ :is="`${next === value ? 'check-bold-icon' : 'span'}`"
31
+ :size="8"
27
32
  />
28
33
  </span>
29
34
  <span class="apos-choice-label-text">
30
- {{ $t(choice.label) }}
35
+ {{ $t(label) }}
31
36
  <AposIndicator
32
- v-if="choice.tooltip"
37
+ v-if="tooltip"
33
38
  class="apos-choice-label-info"
34
- :tooltip="choice.tooltip"
39
+ :tooltip="tooltip"
35
40
  :icon-size="14"
36
41
  icon="information-icon"
37
42
  />
@@ -39,7 +39,7 @@
39
39
  v-model="fieldState[field.name]"
40
40
  :is="fieldComponentMap[field.type]"
41
41
  :following-values="followingValues[field.name]"
42
- :condition-met="conditionalFields[field.name]"
42
+ :condition-met="conditionalFields?.if[field.name]"
43
43
  :field="fields[field.name].field"
44
44
  :modifiers="fields[field.name].modifiers"
45
45
  :display-options="getDisplayOptions(field.name)"
@@ -49,6 +49,7 @@
49
49
  :ref="field.name"
50
50
  :generation="generation"
51
51
  @update-doc-data="onUpdateDocData"
52
+ @validate="emitValidate()"
52
53
  />
53
54
  </component>
54
55
  <slot name="after" />
@@ -7,15 +7,15 @@
7
7
  {{ $t(subform.label) }}
8
8
  </span>
9
9
  <AposSchema
10
- :class="{ 'apos-subform__disabled': busy }"
10
+ ref="schema"
11
11
  data-apos-test="subformSchema"
12
+ :class="{ 'apos-subform__disabled': busy }"
12
13
  :data-apos-test-name="subform.name"
13
- ref="schema"
14
14
  :trigger-validation="triggerValidation"
15
15
  :schema="schema"
16
16
  :value="docFields"
17
17
  :following-values="followingValues()"
18
- :conditional-fields="conditionalFields()"
18
+ :conditional-fields="conditionalFields"
19
19
  :server-errors="serverErrors"
20
20
  :modifiers="['small']"
21
21
  @input="updateDocFields"
@@ -1,3 +1,9 @@
1
+ // Supported field conditional types,
2
+ // you can add a condition type to this array to make it available to the frontend
3
+ const conditionTypes = [ 'if', 'requiredIf' ];
4
+ export const getConditionTypesObject = () => Object
5
+ .fromEntries(conditionTypes.map((key) => ([ key, {} ])));
6
+
1
7
  // Evaluate the external conditions found in each field
2
8
  // via API calls - made in parallel for performance-
3
9
  // and store their result for reusability.
@@ -5,50 +11,51 @@
5
11
  // `docId` - the current docId (from prop or context)
6
12
  // `$t` - the i18n function (usually `this.$t`)
7
13
  export async function evaluateExternalConditions(schema, docId, $t) {
8
- let externalConditionsResults = {};
14
+ const externalConditionsResults = getConditionTypesObject();
9
15
 
10
16
  for (const field of schema) {
11
- if (field.if) {
12
- const externalConditionKeys = Object
13
- .entries(field.if)
14
- .flatMap(getExternalConditionKeys)
15
- .filter(Boolean);
16
-
17
- const uniqExternalConditionKeys = [ ...new Set(externalConditionKeys) ];
18
-
19
- let results = [];
20
-
21
- try {
22
- const promises = uniqExternalConditionKeys
23
- .map(key => externalConditionsResults[key] !== undefined
24
- ? null
25
- : evaluateExternalCondition(key, field._id, docId)
26
- )
17
+ for (const conditionType of conditionTypes) {
18
+ if (field[conditionType]) {
19
+ const externalConditionKeys = Object
20
+ .entries(field[conditionType])
21
+ .flatMap((entry) => getExternalConditionKeys(entry, conditionType))
27
22
  .filter(Boolean);
28
23
 
29
- results = await Promise.all(promises);
30
-
31
- externalConditionsResults = {
32
- ...externalConditionsResults,
33
- ...Object.fromEntries(results)
34
- };
35
- } catch (error) {
36
- await apos.notify($t('apostrophe:errorEvaluatingExternalCondition', { name: field.name }), {
37
- type: 'danger',
38
- icon: 'alert-circle-icon',
39
- dismiss: true,
40
- localize: false
41
- });
24
+ const uniqExternalConditionKeys = [ ...new Set(externalConditionKeys) ];
25
+
26
+ try {
27
+ const promises = uniqExternalConditionKeys
28
+ .map(key => (externalConditionsResults[conditionType][key] !== undefined
29
+ ? null
30
+ : evaluateExternalCondition(key, field._id, docId))
31
+ )
32
+ .filter(Boolean);
33
+
34
+ const results = await Promise.all(promises);
35
+
36
+ externalConditionsResults[conditionType] = {
37
+ ...externalConditionsResults[conditionType],
38
+ ...Object.fromEntries(results)
39
+ };
40
+ } catch (error) {
41
+ await apos.notify($t('apostrophe:errorEvaluatingExternalCondition', { name: field.name }), {
42
+ type: 'danger',
43
+ icon: 'alert-circle-icon',
44
+ dismiss: true,
45
+ localize: false
46
+ });
47
+ }
42
48
  }
43
49
  }
44
50
  }
45
51
  return externalConditionsResults;
46
52
 
47
- function getExternalConditionKeys([ key, val ]) {
53
+ function getExternalConditionKeys([ key, val ], conditionType) {
48
54
  if (key === '$or') {
49
- return val.flatMap(nested => Object.entries(nested).map(getExternalConditionKeys));
55
+ return val.flatMap(nested => Object.entries(nested)
56
+ .map((entry) => getExternalConditionKeys(entry, conditionType)));
50
57
  }
51
- if (isExternalCondition(key)) {
58
+ if (isExternalCondition(key, conditionType)) {
52
59
  return key;
53
60
  }
54
61
  return null;
@@ -71,7 +78,7 @@ export async function evaluateExternalCondition(conditionKey, fieldId, docId) {
71
78
  return [ conditionKey, result ];
72
79
  }
73
80
  // Checking if key ends with a closing parenthesis here to throw later if any argument is passed.
74
- export function isExternalCondition(conditionKey) {
81
+ export function isExternalCondition(conditionKey, conditionType) {
75
82
  if (!conditionKey.endsWith(')')) {
76
83
  return false;
77
84
  }
@@ -79,7 +86,7 @@ export function isExternalCondition(conditionKey) {
79
86
  const [ methodDefinition ] = conditionKey.split('(');
80
87
 
81
88
  if (!conditionKey.endsWith('()')) {
82
- console.warn(`Warning in \`if\` definition: "${methodDefinition}()" should not be passed any argument.`);
89
+ console.warn(`Warning in \`${conditionType}\` definition: "${methodDefinition}()" should not be passed any argument.`);
83
90
  }
84
91
 
85
92
  return true;
@@ -103,56 +110,49 @@ export function isExternalCondition(conditionKey) {
103
110
  // `values` - the schema (all) values
104
111
  // `externalConditionsResults` - the results of the external conditions,
105
112
  // as returned by `evaluateExternalConditions`
106
- export function conditionalFields(
113
+ export function getConditionalFields(
107
114
  schema,
108
115
  fields,
109
116
  values,
110
117
  externalConditionsResults
111
118
  ) {
112
- const conditionalFields = {};
113
-
114
- while (true) {
115
- let change = false;
116
- for (const field of schema) {
117
- if (field.if) {
118
- const result = evaluate(field.if);
119
- const previous = conditionalFields[field.name];
120
- if (previous !== result) {
121
- change = true;
122
- }
123
- conditionalFields[field.name] = result;
119
+ const conditionalFields = getConditionTypesObject();
120
+
121
+ for (const field of schema) {
122
+ for (const conditionType of conditionTypes) {
123
+ if (field[conditionType]) {
124
+ const result = evaluate(field[conditionType], conditionType);
125
+ conditionalFields[conditionType][field.name] = result;
124
126
  }
125
127
  }
126
- if (!change) {
127
- break;
128
- }
129
128
  }
130
129
 
131
- const result = {};
130
+ const result = getConditionTypesObject();
131
+
132
132
  for (const field of fields) {
133
- if (field.if) {
134
- result[field.name] = conditionalFields[field.name];
133
+ for (const conditionType of conditionTypes) {
134
+ if (field[conditionType]) {
135
+ result[conditionType][field.name] = conditionalFields[conditionType][field.name];
136
+ }
135
137
  }
136
138
  }
139
+
137
140
  return result;
138
141
 
139
- function evaluate(clause) {
140
- let result = true;
142
+ function evaluate(clause, conditionType) {
141
143
  for (const [ key, val ] of Object.entries(clause)) {
142
144
  if (key === '$or') {
143
- if (!val.some(clause => evaluate(clause))) {
144
- result = false;
145
- break;
145
+ if (!val.some(clause => evaluate(clause, conditionType))) {
146
+ return false;
146
147
  }
147
148
 
148
149
  // No need to go further here, the key is an "$or" condition...
149
150
  continue;
150
151
  }
151
152
 
152
- if (isExternalCondition(key)) {
153
- if (externalConditionsResults[key] !== val) {
154
- result = false;
155
- break;
153
+ if (isExternalCondition(key, conditionType)) {
154
+ if (externalConditionsResults[conditionType][key] !== val) {
155
+ return false;
156
156
  }
157
157
 
158
158
  // Stop there, this is an external condition thus
@@ -160,22 +160,27 @@ export function conditionalFields(
160
160
  continue;
161
161
  }
162
162
 
163
- if (conditionalFields[key] === false) {
164
- result = false;
165
- break;
163
+ if (conditionalFields[conditionType][key] === false) {
164
+ return false;
166
165
  }
167
166
 
168
167
  const fieldValue = values[key];
169
168
 
170
169
  if (Array.isArray(fieldValue)) {
171
- result = fieldValue.includes(val);
172
- break;
170
+ return fieldValue.includes(val);
171
+ }
172
+
173
+ if (val.min && fieldValue < val.min) {
174
+ return false;
175
+ }
176
+ if (val.max && fieldValue > val.max) {
177
+ return false;
173
178
  }
179
+
174
180
  if (val !== fieldValue) {
175
- result = false;
176
- break;
181
+ return false;
177
182
  }
178
183
  }
179
- return result;
184
+ return true;
180
185
  }
181
186
  }
@@ -20,6 +20,10 @@ export default {
20
20
  required: true,
21
21
  type: Object
22
22
  },
23
+ inputSchema: {
24
+ type: Array,
25
+ required: true
26
+ },
23
27
  serverError: {
24
28
  type: Object,
25
29
  default: null
@@ -80,7 +84,8 @@ export default {
80
84
  },
81
85
  schema() {
82
86
  // For AposDocEditorMixin
83
- return (this.field.schema || []).filter(field => apos.schema.components.fields[field.type]);
87
+ return (this.inputSchema || this.field.schema || [])
88
+ .filter(field => apos.schema.components.fields[field.type]);
84
89
  },
85
90
  countLabel() {
86
91
  return this.$t('apostrophe:numberAdded', {
@@ -132,6 +137,8 @@ export default {
132
137
  },
133
138
  async mounted() {
134
139
  this.modal.active = true;
140
+ await this.evaluateExternalConditions();
141
+ this.evaluateConditions();
135
142
  if (this.next.length) {
136
143
  this.select(this.next[0]._id);
137
144
  }
@@ -179,6 +186,7 @@ export default {
179
186
  },
180
187
  currentDocUpdate(currentDoc) {
181
188
  this.currentDoc = currentDoc;
189
+ this.evaluateConditions();
182
190
  },
183
191
  async add() {
184
192
  if (await this.validate(true, false)) {