apostrophe 4.4.3 → 4.5.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 (82) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/index.js +2 -3
  3. package/lib/glob.js +12 -0
  4. package/lib/moog-require.js +3 -3
  5. package/modules/@apostrophecms/admin-bar/index.js +9 -2
  6. package/modules/@apostrophecms/admin-bar/ui/apos/apps/AposAdminBar.js +1 -1
  7. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +2 -2
  8. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextTitle.vue +0 -1
  9. package/modules/@apostrophecms/area/ui/apos/components/AposAreaContextualMenu.vue +2 -2
  10. package/modules/@apostrophecms/area/ui/apos/components/AposAreaEditor.vue +6 -6
  11. package/modules/@apostrophecms/area/ui/apos/components/AposAreaExpandedMenu.vue +7 -1
  12. package/modules/@apostrophecms/asset/index.js +5 -6
  13. package/modules/@apostrophecms/command-menu/index.js +7 -2
  14. package/modules/@apostrophecms/doc/index.js +2 -2
  15. package/modules/@apostrophecms/doc-type/index.js +1 -1
  16. package/modules/@apostrophecms/i18n/i18n/en.json +37 -1
  17. package/modules/@apostrophecms/i18n/index.js +2 -2
  18. package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalize.vue +5 -5
  19. package/modules/@apostrophecms/image/index.js +1 -1
  20. package/modules/@apostrophecms/image/ui/apos/components/AposImageRelationshipEditor.vue +4 -3
  21. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +221 -76
  22. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerDisplay.vue +91 -24
  23. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerEditor.vue +48 -51
  24. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerSelections.vue +1 -0
  25. package/modules/@apostrophecms/login/index.js +3 -3
  26. package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +3 -11
  27. package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +4 -12
  28. package/modules/@apostrophecms/modal/ui/apos/components/AposModalBody.vue +20 -6
  29. package/modules/@apostrophecms/modal/ui/apos/components/AposModalBreadcrumbs.vue +1 -1
  30. package/modules/@apostrophecms/modal/ui/apos/components/AposModalShareDraft.vue +1 -1
  31. package/modules/@apostrophecms/modal/ui/apos/components/AposModalTabs.vue +3 -5
  32. package/modules/@apostrophecms/modal/ui/apos/components/AposWidgetModalTabs.vue +3 -5
  33. package/modules/@apostrophecms/modal/ui/apos/mixins/AposDocErrorsMixin.js +2 -2
  34. package/modules/@apostrophecms/modal/ui/apos/mixins/AposDocsManagerMixin.js +44 -39
  35. package/modules/@apostrophecms/modal/ui/apos/mixins/AposModalTabsMixin.js +2 -2
  36. package/modules/@apostrophecms/oembed/index.js +2 -2
  37. package/modules/@apostrophecms/oembed-field/ui/apos/components/AposInputOembed.vue +2 -2
  38. package/modules/@apostrophecms/page/ui/apos/components/AposPagesManager.vue +1 -2
  39. package/modules/@apostrophecms/page/ui/apos/logic/AposPagesManager.js +14 -7
  40. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +23 -11
  41. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerDisplay.vue +4 -1
  42. package/modules/@apostrophecms/rich-text-widget/index.js +31 -3
  43. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapColor.vue +285 -0
  44. package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Color.js +5 -0
  45. package/modules/@apostrophecms/schema/lib/addFieldTypes.js +26 -13
  46. package/modules/@apostrophecms/schema/ui/apos/components/AposInputBoolean.vue +2 -2
  47. package/modules/@apostrophecms/schema/ui/apos/components/AposInputColor.vue +0 -1
  48. package/modules/@apostrophecms/schema/ui/apos/components/AposInputObject.vue +1 -0
  49. package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +13 -2
  50. package/modules/@apostrophecms/schema/ui/apos/logic/AposArrayEditor.js +11 -9
  51. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArea.js +4 -4
  52. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArray.js +7 -7
  53. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputObject.js +11 -0
  54. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputSlug.js +45 -51
  55. package/modules/@apostrophecms/schema/ui/apos/logic/AposSchema.js +3 -1
  56. package/modules/@apostrophecms/schema/ui/apos/mixins/AposInputMixin.js +1 -1
  57. package/modules/@apostrophecms/submitted-draft/index.js +2 -2
  58. package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +7 -3
  59. package/modules/@apostrophecms/ui/ui/apos/components/AposButtonSplit.vue +0 -1
  60. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenu.vue +12 -4
  61. package/modules/@apostrophecms/ui/ui/apos/components/AposFile.vue +4 -4
  62. package/modules/@apostrophecms/ui/ui/apos/components/AposFilterMenu.vue +2 -1
  63. package/modules/@apostrophecms/ui/ui/apos/components/AposLoading.vue +1 -1
  64. package/modules/@apostrophecms/ui/ui/apos/components/AposLoadingBlock.vue +50 -0
  65. package/modules/@apostrophecms/ui/ui/apos/components/AposSlat.vue +0 -1
  66. package/modules/@apostrophecms/ui/ui/apos/components/AposSlatList.vue +6 -4
  67. package/modules/@apostrophecms/ui/ui/apos/components/AposTagApply.vue +17 -11
  68. package/modules/@apostrophecms/ui/ui/apos/components/AposTree.vue +2 -2
  69. package/modules/@apostrophecms/ui/ui/apos/components/AposTreeRows.vue +2 -2
  70. package/modules/@apostrophecms/ui/ui/apos/lib/tooltip.js +2 -2
  71. package/modules/@apostrophecms/ui/ui/apos/stores/modal.js +3 -3
  72. package/modules/@apostrophecms/user/index.js +1 -0
  73. package/modules/@apostrophecms/util/index.js +12 -4
  74. package/modules/@apostrophecms/widget-type/ui/apos/components/AposWidgetEditor.vue +2 -2
  75. package/package.json +8 -7
  76. package/scripts/lint-i18n.js +8 -3
  77. package/test/command-menu.js +76 -12
  78. package/test/pages-rest.js +2 -2
  79. package/test/pieces.js +11 -11
  80. package/test/recursionGuard.js +5 -5
  81. package/test/utils/commands.js +26 -0
  82. package/test-lib/util.js +2 -2
@@ -0,0 +1,285 @@
1
+ <template>
2
+ <div class="apos-color-control">
3
+ <AposButton
4
+ type="rich-text"
5
+ class="apos-rich-text-editor__control"
6
+ :class="['apos-color-button', { 'apos-is-active': active }]"
7
+ :icon-only="false"
8
+ icon="circle-icon"
9
+ :icon-fill="indicatorColor"
10
+ :label="'apostrophe:richTextColor'"
11
+ :modifiers="['no-border', 'no-motion']"
12
+ :tooltip="{
13
+ content: 'apostrophe:richTextColor',
14
+ placement: 'top',
15
+ delay: 650
16
+ }"
17
+ @click="click"
18
+ >
19
+ <template #label>
20
+ <AposIndicator icon="chevron-down-icon" />
21
+ </template>
22
+ </AposButton>
23
+ <div
24
+ v-if="active"
25
+ v-click-outside-element="close"
26
+ class="apos-popover apos-color-control__dialog"
27
+ x-placement="bottom"
28
+ :class="{
29
+ 'apos-is-triggered': active,
30
+ 'apos-has-selection': hasSelection
31
+ }"
32
+ >
33
+ <AposContextMenuDialog menu-placement="bottom-center">
34
+ <div
35
+ v-if="editor"
36
+ class="text-color-component"
37
+ @mousedown="handleMouseDown"
38
+ >
39
+ <Picker
40
+ v-bind="pickerOptions"
41
+ :model-value="pickerValue"
42
+ @update:model-value="update"
43
+ @focus="focus"
44
+ />
45
+ </div>
46
+ <footer class="apos-color-control__footer">
47
+ <AposButton
48
+ type="primary"
49
+ label="apostrophe:close"
50
+ :modifiers="['small', 'margin-micro']"
51
+ @click="close"
52
+ />
53
+ </footer>
54
+ </AposContextMenuDialog>
55
+ </div>
56
+ </div>
57
+ </template>
58
+
59
+ <script>
60
+ import {
61
+ ref, watch, computed, defineComponent
62
+ } from 'vue';
63
+ import { Sketch as Picker } from '@ckpack/vue-color';
64
+ import tinycolor from 'tinycolor2';
65
+
66
+ export default defineComponent({
67
+ name: 'AposTiptapColor',
68
+ components: {
69
+ Picker
70
+ },
71
+ props: {
72
+ name: {
73
+ type: String,
74
+ required: true
75
+ },
76
+ options: {
77
+ type: Object,
78
+ required: true
79
+ },
80
+ tool: {
81
+ type: Object,
82
+ required: true
83
+ },
84
+ modelValue: {
85
+ type: Object,
86
+ default() {
87
+ return {};
88
+ }
89
+ },
90
+ editor: {
91
+ type: Object,
92
+ required: true
93
+ }
94
+ },
95
+ setup(props) {
96
+ const active = ref(false);
97
+ const next = ref('');
98
+ const tinyColorObj = ref(null);
99
+ const startsNull = ref(false);
100
+ const indicatorColor = ref('#000000');
101
+
102
+ const defaultOptions = {
103
+ presetColors: [
104
+ '#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321',
105
+ '#417505', '#BD10E0', '#9013FE', '#4A90E2', '#50E3C2',
106
+ '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF'
107
+ ],
108
+ disableAlpha: false,
109
+ disableFields: false,
110
+ format: 'hex8'
111
+ };
112
+
113
+ const projectOptions = computed(() => {
114
+ return window.apos.modules['@apostrophecms/rich-text-widget'];
115
+ });
116
+
117
+ const areaOptions = props.options || {};
118
+
119
+ const userOptions = computed(() => {
120
+ return {
121
+ ...projectOptions.value,
122
+ ...areaOptions
123
+ };
124
+ });
125
+
126
+ const mergedOptions = computed(() => {
127
+ return {
128
+ ...defaultOptions,
129
+ ...userOptions.value.color
130
+ };
131
+ });
132
+
133
+ const pickerOptions = computed(() => {
134
+ const {
135
+ presetColors, disableAlpha, disableFields
136
+ } = mergedOptions.value;
137
+
138
+ return {
139
+ presetColors,
140
+ disableAlpha,
141
+ disableFields
142
+ };
143
+ });
144
+
145
+ const format = computed(() => {
146
+ return mergedOptions.value.format;
147
+ });
148
+
149
+ const pickerValue = ref(next.value || '');
150
+
151
+ const hasSelection = computed(() => !props.editor.state.selection.empty);
152
+
153
+ watch(
154
+ () => props.editor.state.selection,
155
+ () => {
156
+ if (hasSelection.value) {
157
+ const newColor = props.editor.getAttributes('textStyle').color;
158
+ next.value = newColor || '#000000';
159
+ indicatorColor.value = next.value;
160
+ } else {
161
+ next.value = '#000000';
162
+ indicatorColor.value = next.value;
163
+ }
164
+ },
165
+ {
166
+ deep: true,
167
+ immediate: true
168
+ }
169
+ );
170
+
171
+ watch(pickerValue, (newColor) => {
172
+ indicatorColor.value = newColor;
173
+ });
174
+
175
+ const open = () => {
176
+ active.value = true;
177
+ };
178
+
179
+ const close = () => {
180
+ active.value = false;
181
+ };
182
+
183
+ const update = (value) => {
184
+ tinyColorObj.value = tinycolor(value.hsl);
185
+ next.value = tinyColorObj.value.toString(format.value);
186
+ props.editor.chain().focus().setColor(next.value).run();
187
+ indicatorColor.value = next.value;
188
+ };
189
+
190
+ const click = () => {
191
+ active.value = !active.value;
192
+ };
193
+
194
+ const focus = () => {
195
+ // Keep focus on the toolbar to prevent it from closing
196
+ props.editor.view.dom.focus();
197
+ };
198
+
199
+ const handleMouseDown = (event) => {
200
+ const target = event.target;
201
+ if (target.closest('.vc-sketch-saturation-wrap') || target.closest('.vc-sketch-presets')) {
202
+ event.preventDefault();
203
+ } else {
204
+ props.editor.view.dom.focus();
205
+ }
206
+ };
207
+
208
+ return {
209
+ active,
210
+ indicatorColor,
211
+ pickerOptions,
212
+ pickerValue,
213
+ hasSelection,
214
+ startsNull,
215
+ open,
216
+ close,
217
+ update,
218
+ click,
219
+ focus,
220
+ handleMouseDown
221
+ };
222
+ }
223
+ });
224
+ </script>
225
+
226
+ <style lang="scss" scoped>
227
+ .apos-color-control {
228
+ position: relative;
229
+ display: inline-block;
230
+ }
231
+
232
+ .apos-color-button {
233
+ display: flex;
234
+ align-items: center;
235
+ border: none;
236
+ background-color: transparent;
237
+ cursor: pointer;
238
+ }
239
+
240
+ .color-indicator {
241
+ flex-shrink: 0;
242
+ width: 12px;
243
+ height: 12px;
244
+ margin-right: 5px;
245
+ border-radius: 50%;
246
+ }
247
+
248
+ .button-label {
249
+ margin-right: 5px;
250
+ }
251
+
252
+ .chevron-down {
253
+ display: inline-block;
254
+ padding: 3px;
255
+ border: solid var(--a-base-8);
256
+ border-width: 0 2px 2px 0;
257
+ transform: rotate(45deg);
258
+ }
259
+
260
+ .apos-color-control__dialog {
261
+ z-index: $z-index-modal;
262
+ position: absolute;
263
+ top: calc(100% + 15px);
264
+ left: 5px;
265
+ opacity: 0;
266
+ pointer-events: none;
267
+ width: auto;
268
+ max-width: 90%;
269
+
270
+ &.apos-is-triggered.apos-has-selection {
271
+ opacity: 1;
272
+ pointer-events: auto;
273
+ }
274
+ }
275
+
276
+ .apos-is-active {
277
+ background-color: var(--a-base-7);
278
+ }
279
+
280
+ .apos-color-control__footer {
281
+ display: flex;
282
+ justify-content: flex-end;
283
+ margin-top: 10px;
284
+ }
285
+ </style>
@@ -0,0 +1,5 @@
1
+ // imports the tiptap extension from node_modules
2
+ import Color from '@tiptap/extension-color';
3
+ export default (options) => {
4
+ return Color.extend({});
5
+ };
@@ -722,7 +722,8 @@ module.exports = (self) => {
722
722
 
723
723
  destination[field.name] = checkStringLength(destination[field.name], field.min, field.max);
724
724
  }
725
- }
725
+ },
726
+ def: ''
726
727
  });
727
728
 
728
729
  self.addFieldType({
@@ -889,11 +890,12 @@ module.exports = (self) => {
889
890
  try {
890
891
  await self.convert(req, schema, data, result);
891
892
  } catch (e) {
892
- for (const error of e) {
893
- errors.push({
894
- path: error.path,
895
- error: error.error
896
- });
893
+ if (Array.isArray(e)) {
894
+ for (const error of e) {
895
+ errors.push(error);
896
+ }
897
+ } else {
898
+ throw e;
897
899
  }
898
900
  }
899
901
  result.metaType = 'objectItem';
@@ -912,7 +914,7 @@ module.exports = (self) => {
912
914
  self.register(metaType, type, field.schema);
913
915
  },
914
916
  validate: function (field, options, warn, fail) {
915
- for (const subField of field.schema || field.fields.add) {
917
+ for (const subField of field.schema) {
916
918
  self.validateField(subField, options, field);
917
919
  }
918
920
  },
@@ -1139,12 +1141,7 @@ module.exports = (self) => {
1139
1141
  field.schema = self.fieldsToArray(`Relationship field ${field.name}`, field.fields);
1140
1142
  }
1141
1143
  }
1142
- if (field.schema && !field.fieldsStorage) {
1143
- field.fieldsStorage = field.name.replace(/^_/, '') + 'Fields';
1144
- }
1145
- if (field.schema && !Array.isArray(field.schema)) {
1146
- fail('schema property should be an array if present at this stage');
1147
- }
1144
+ validateSchema(field);
1148
1145
  if (field.filters) {
1149
1146
  fail('"filters" property should be changed to "builders" for 3.x');
1150
1147
  }
@@ -1157,6 +1154,22 @@ module.exports = (self) => {
1157
1154
  fail('withType property, ' + type + ', does not match the name of any piece or page type module.');
1158
1155
  }
1159
1156
  }
1157
+
1158
+ function validateSchema(_field) {
1159
+ if (!_field.schema) {
1160
+ return;
1161
+ }
1162
+ if (!Array.isArray(_field.schema)) {
1163
+ fail('schema property should be an array if present at this stage');
1164
+ }
1165
+ self.validate(_field.schema, {
1166
+ type: 'relationship',
1167
+ subtype: _field.withType
1168
+ });
1169
+ if (!_field.fieldsStorage) {
1170
+ _field.fieldsStorage = _field.name.replace(/^_/, '') + 'Fields';
1171
+ }
1172
+ }
1160
1173
  },
1161
1174
  isEqual(req, field, one, two) {
1162
1175
  const ids1 = one[field.idsStorage] || [];
@@ -25,7 +25,7 @@
25
25
  class="apos-boolean__icon"
26
26
  title=""
27
27
  />
28
- {{ trueLabel || 'Yes' }}
28
+ {{ trueLabel || $t('apostrophe:yes') }}
29
29
  </label>
30
30
  <input
31
31
  :id="`${uid}-false`"
@@ -44,7 +44,7 @@
44
44
  class="apos-boolean__icon"
45
45
  title=""
46
46
  />
47
- {{ falseLabel || 'No' }}
47
+ {{ falseLabel || $t('apostrophe:no') }}
48
48
  </label>
49
49
  </div>
50
50
  </template>
@@ -12,7 +12,6 @@
12
12
  <AposContextMenu
13
13
  :button="buttonOptions"
14
14
  menu-placement="bottom-start"
15
- menu-offset="5, 20"
16
15
  :disabled="field.readOnly"
17
16
  :tooltip="tooltip"
18
17
  @open="open"
@@ -21,6 +21,7 @@
21
21
  :doc-id="docId"
22
22
  :conditional-fields="conditionalFields"
23
23
  :following-values="followingValuesWithParent"
24
+ :server-errors="currentDocServerErrors"
24
25
  @update:model-value="evaluateConditions(values)"
25
26
  @validate="emitValidate()"
26
27
  />
@@ -91,7 +91,7 @@
91
91
  class="apos-field__error"
92
92
  data-apos-test="field-error"
93
93
  >
94
- {{ $t(errorMessage) }}
94
+ {{ getTranslatedErrorMessage(errorMessage) }}
95
95
  </div>
96
96
  </component>
97
97
  <!-- CSS Escape hatch for additional interfaces like relatipnship managers -->
@@ -103,7 +103,18 @@
103
103
  import AposInputWrapperLogic from '../logic/AposInputWrapper';
104
104
  export default {
105
105
  name: 'AposInputWrapper',
106
- mixins: [ AposInputWrapperLogic ]
106
+ mixins: [ AposInputWrapperLogic ],
107
+ methods: {
108
+ getTranslatedErrorMessage(message) {
109
+ if (message === 'required') {
110
+ return this.$t('apostrophe:required');
111
+ }
112
+ if (message === 'invalid') {
113
+ return this.$t('apostrophe:invalid');
114
+ }
115
+ return this.$t(message);
116
+ }
117
+ }
107
118
  };
108
119
  </script>
109
120
 
@@ -1,6 +1,6 @@
1
1
  import AposModifiedMixin from 'Modules/@apostrophecms/ui/mixins/AposModifiedMixin';
2
2
  import AposEditorMixin from 'Modules/@apostrophecms/modal/mixins/AposEditorMixin';
3
- import cuid from 'cuid';
3
+ import { createId } from '@paralleldrive/cuid2';
4
4
  import { klona } from 'klona';
5
5
  import { get } from 'lodash';
6
6
  import { detectDocChange } from 'Modules/@apostrophecms/schema/lib/detectChange';
@@ -42,7 +42,7 @@ export default {
42
42
  // Automatically add `_id` to default items
43
43
  const items = this.items.map(item => ({
44
44
  ...item,
45
- _id: item._id || cuid()
45
+ _id: item._id || createId()
46
46
  }));
47
47
 
48
48
  return {
@@ -144,13 +144,15 @@ export default {
144
144
  if (this.next.length) {
145
145
  this.setCurrentDoc(this.next.at(0)._id);
146
146
  }
147
- if (this.serverError && this.serverError.data && this.serverError.data.errors) {
147
+ if (this.serverError?.data?.errors?.length) {
148
148
  const first = this.serverError.data.errors[0];
149
- const [ _id, name ] = first.path.split('.');
150
- await this.select(_id);
151
- const aposSchema = this.$refs.schema;
152
- await this.$nextTick();
153
- aposSchema.scrollFieldIntoView(name);
149
+ const [ _id, name ] = first.path?.split('.') || [];
150
+ if (_id) {
151
+ await this.select(_id);
152
+ const aposSchema = this.$refs.schema;
153
+ await this.$nextTick();
154
+ name && aposSchema.scrollFieldIntoView(name);
155
+ }
154
156
  }
155
157
  this.titleFieldChoices = await this.getTitleFieldChoices();
156
158
  },
@@ -193,7 +195,7 @@ export default {
193
195
  async add() {
194
196
  if (await this.validate(true, false)) {
195
197
  const item = this.newInstance();
196
- item._id = cuid();
198
+ item._id = createId();
197
199
  this.next.push(item);
198
200
  await this.select(item._id);
199
201
  this.updateMinMax();
@@ -1,6 +1,6 @@
1
1
 
2
2
  import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin';
3
- import cuid from 'cuid';
3
+ import { createId } from '@paralleldrive/cuid2';
4
4
 
5
5
  export default {
6
6
  name: 'AposInputArea',
@@ -43,8 +43,8 @@ export default {
43
43
  for (const [ name, options ] of Object.entries(widgets)) {
44
44
  result.push({
45
45
  name,
46
- label: options.addLabel || apos.modules[`${name}-widget`].label,
47
- icon: apos.modules[`${name}-widget`].icon
46
+ label: options.addLabel || apos.modules[`${name}-widget`]?.label,
47
+ icon: apos.modules[`${name}-widget`]?.icon
48
48
  });
49
49
  }
50
50
  return result;
@@ -65,7 +65,7 @@ export default {
65
65
  getEmptyValue() {
66
66
  return {
67
67
  metaType: 'area',
68
- _id: cuid(),
68
+ _id: createId(),
69
69
  items: []
70
70
  };
71
71
  },
@@ -3,7 +3,7 @@ import AposInputFollowingMixin from 'Modules/@apostrophecms/schema/mixins/AposIn
3
3
  import AposInputConditionalFieldsMixin from 'Modules/@apostrophecms/schema/mixins/AposInputConditionalFieldsMixin';
4
4
  import { getConditionTypesObject } from 'Modules/@apostrophecms/schema/lib/conditionalFields';
5
5
 
6
- import cuid from 'cuid';
6
+ import { createId } from '@paralleldrive/cuid2';
7
7
  import { klona } from 'klona';
8
8
  import { get } from 'lodash';
9
9
  import { Sortable } from 'sortablejs-vue3';
@@ -45,7 +45,7 @@ export default {
45
45
  return alwaysExpand(this.field, this.schema);
46
46
  },
47
47
  listId() {
48
- return `sortableList-${cuid()}`;
48
+ return `sortableList-${createId()}`;
49
49
  },
50
50
  dragOptions() {
51
51
  return {
@@ -72,9 +72,9 @@ export default {
72
72
  const error = this.error || this.serverError;
73
73
  // Server-side errors behave differently
74
74
  const name = error?.name || error;
75
- if (name === 'invalid') {
76
- // Always due to a subproperty which will display its own error,
77
- // don't confuse the user
75
+ if (name === 'invalid' && !this.serverError) {
76
+ // Not always due to a subproperty which will display its own error,
77
+ // don't confuse the user if so
78
78
  return false;
79
79
  }
80
80
  return error;
@@ -222,7 +222,7 @@ export default {
222
222
  delete this.itemsConditionalFields[_id];
223
223
  },
224
224
  add() {
225
- const _id = cuid();
225
+ const _id = createId();
226
226
  this.items.push({
227
227
  _id,
228
228
  schemaInput: {
@@ -280,7 +280,7 @@ function modelItems(items, field, schema) {
280
280
  return items.map(item => {
281
281
  const open = alwaysExpand(field, schema);
282
282
  return {
283
- _id: item._id || cuid(),
283
+ _id: item._id || createId(),
284
284
  schemaInput: {
285
285
  data: item || {}
286
286
  },
@@ -67,6 +67,17 @@ export default {
67
67
  },
68
68
  currentDocMeta() {
69
69
  return this.objectMeta.aposMeta || {};
70
+ },
71
+ currentDocServerErrors() {
72
+ let serverErrors = null;
73
+ (this.serverError?.data?.errors || [])
74
+ .forEach(error => {
75
+ if (error.path) {
76
+ serverErrors = serverErrors || {};
77
+ serverErrors[error.path] = error;
78
+ }
79
+ });
80
+ return serverErrors;
70
81
  }
71
82
  },
72
83
  watch: {