apostrophe 3.53.0 → 3.55.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 (77) hide show
  1. package/CHANGELOG.md +58 -1
  2. package/defaults.js +1 -0
  3. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextModeAndSettings.vue +5 -2
  4. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextTitle.vue +28 -19
  5. package/modules/@apostrophecms/any-doc-type/index.js +2 -2
  6. package/modules/@apostrophecms/any-page-type/index.js +2 -2
  7. package/modules/@apostrophecms/doc/index.js +55 -29
  8. package/modules/@apostrophecms/doc-type/index.js +11 -6
  9. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocContextMenu.vue +4 -440
  10. package/modules/@apostrophecms/doc-type/ui/apos/logic/AposDocContextMenu.js +445 -0
  11. package/modules/@apostrophecms/i18n/i18n/de.json +113 -105
  12. package/modules/@apostrophecms/i18n/i18n/es.json +10 -0
  13. package/modules/@apostrophecms/i18n/i18n/fr.json +8 -0
  14. package/modules/@apostrophecms/i18n/i18n/pt-BR.json +10 -0
  15. package/modules/@apostrophecms/i18n/i18n/sk.json +8 -0
  16. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +1 -0
  17. package/modules/@apostrophecms/log/index.js +429 -0
  18. package/modules/@apostrophecms/login/index.js +47 -4
  19. package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +14 -1
  20. package/modules/@apostrophecms/modal/ui/apos/mixins/AposEditorMixin.js +1 -1
  21. package/modules/@apostrophecms/module/index.js +32 -6
  22. package/modules/@apostrophecms/module/lib/log.js +68 -0
  23. package/modules/@apostrophecms/page/index.js +71 -19
  24. package/modules/@apostrophecms/page/lib/legacy-migrations.js +0 -57
  25. package/modules/@apostrophecms/page/ui/apos/components/AposPagesManager.vue +8 -285
  26. package/modules/@apostrophecms/page/ui/apos/logic/AposPagesManager.js +291 -0
  27. package/modules/@apostrophecms/page-type/index.js +39 -26
  28. package/modules/@apostrophecms/piece-type/index.js +19 -11
  29. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +1 -0
  30. package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +2 -357
  31. package/modules/@apostrophecms/schema/ui/apos/components/AposInputArea.vue +2 -86
  32. package/modules/@apostrophecms/schema/ui/apos/components/AposInputArray.vue +2 -254
  33. package/modules/@apostrophecms/schema/ui/apos/components/AposInputAttachment.vue +2 -77
  34. package/modules/@apostrophecms/schema/ui/apos/components/AposInputBoolean.vue +2 -44
  35. package/modules/@apostrophecms/schema/ui/apos/components/AposInputCheckboxes.vue +2 -64
  36. package/modules/@apostrophecms/schema/ui/apos/components/AposInputColor.vue +2 -94
  37. package/modules/@apostrophecms/schema/ui/apos/components/AposInputDateAndTime.vue +3 -47
  38. package/modules/@apostrophecms/schema/ui/apos/components/AposInputObject.vue +2 -82
  39. package/modules/@apostrophecms/schema/ui/apos/components/AposInputPassword.vue +2 -37
  40. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRadio.vue +2 -26
  41. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRange.vue +2 -57
  42. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRelationship.vue +2 -259
  43. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSelect.vue +2 -38
  44. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +2 -275
  45. package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +2 -167
  46. package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +2 -115
  47. package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +3 -279
  48. package/modules/@apostrophecms/schema/ui/apos/components/AposSearchList.vue +2 -83
  49. package/modules/@apostrophecms/schema/ui/apos/lib/detectChange.js +10 -1
  50. package/modules/@apostrophecms/schema/ui/apos/logic/AposArrayEditor.js +361 -0
  51. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArea.js +89 -0
  52. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArray.js +257 -0
  53. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputAttachment.js +81 -0
  54. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputBoolean.js +48 -0
  55. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputCheckboxes.js +68 -0
  56. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputColor.js +98 -0
  57. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputDateAndTime.js +49 -0
  58. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputObject.js +86 -0
  59. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputPassword.js +41 -0
  60. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRadio.js +29 -0
  61. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRange.js +60 -0
  62. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRelationship.js +262 -0
  63. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputSelect.js +41 -0
  64. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputSlug.js +278 -0
  65. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputString.js +170 -0
  66. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputWrapper.js +118 -0
  67. package/modules/@apostrophecms/schema/ui/apos/logic/AposSchema.js +281 -0
  68. package/modules/@apostrophecms/schema/ui/apos/logic/AposSearchList.js +85 -0
  69. package/modules/@apostrophecms/template/index.js +1 -1
  70. package/modules/@apostrophecms/ui/ui/apos/components/AposTreeHeader.vue +2 -2
  71. package/modules/@apostrophecms/util/index.js +83 -13
  72. package/modules/@apostrophecms/util/lib/logger.js +19 -17
  73. package/package.json +1 -1
  74. package/test/docs.js +35 -2
  75. package/test/log.js +1765 -0
  76. package/test/pages.js +57 -0
  77. package/test-lib/util.js +1 -1
@@ -35,283 +35,10 @@
35
35
  </template>
36
36
 
37
37
  <script>
38
- // NOTE: This is a temporary component, copying AposInputString. Base modules
39
- // already have `type: 'slug'` fields, so this is needed to avoid distracting
40
- // errors.
41
- import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin';
42
- import sluggo from 'sluggo';
43
- import debounce from 'debounce-async';
44
- import { klona } from 'klona';
45
-
38
+ import AposInputSlugLogic from '../logic/AposInputSlug';
46
39
  export default {
47
40
  name: 'AposInputSlug',
48
- mixins: [ AposInputMixin ],
49
- emits: [ 'return' ],
50
- data() {
51
- return {
52
- conflict: false,
53
- isArchived: null,
54
- originalSlugPartsLength: null
55
- };
56
- },
57
- computed: {
58
- tabindex () {
59
- return this.field.disableFocus ? '-1' : '0';
60
- },
61
- type () {
62
- if (this.field.type) {
63
- return this.field.type;
64
- } else {
65
- return 'text';
66
- }
67
- },
68
- classes () {
69
- return [ 'apos-input', 'apos-input--text', 'apos-input--slug' ];
70
- },
71
- wrapperClasses () {
72
- return [ 'apos-input-wrapper' ].concat(this.localePrefix ? [ 'apos-input-wrapper--with-prefix' ] : []);
73
- },
74
- icon () {
75
- if (this.error) {
76
- return 'circle-medium-icon';
77
- } else if (this.field.icon) {
78
- return this.field.icon;
79
- } else {
80
- return null;
81
- }
82
- },
83
- prefix () {
84
- return this.field.prefix || '';
85
- },
86
- localePrefix() {
87
- return this.field.page && apos.i18n.locales[apos.i18n.locale].prefix;
88
- }
89
- },
90
- watch: {
91
- followingValues: {
92
- // We are usually interested in followingValue.title, but a
93
- // secondary slug field could be configured to watch
94
- // one or more other fields
95
- deep: true,
96
- handler(newValue, oldValue) {
97
- const newClone = klona(newValue);
98
- const oldClone = klona(oldValue);
99
-
100
- // Track whether the slug is archived for prefixing.
101
- this.isArchived = newValue.archived;
102
- // We only want the string properties to build the slug itself.
103
- delete newClone.archived;
104
- delete oldClone.archived;
105
-
106
- oldValue = Object.values(oldClone).join(' ');
107
- newValue = Object.values(newClone).join(' ');
108
-
109
- if (this.compatible(oldValue, this.next) && !newValue.archived) {
110
- // If this is a page slug, we only replace the last section of the slug.
111
- if (this.field.page) {
112
- let parts = this.next.split('/');
113
- parts = parts.filter(part => part.length > 0);
114
- if ((!this.originalSlugPartsLength && parts.length) || (this.originalSlugPartsLength && parts.length === (this.originalSlugPartsLength - 1))) {
115
- // Remove last path component so we can replace it
116
- parts.pop();
117
- }
118
- parts.push(this.slugify(newValue, { componentOnly: true }));
119
- if (parts[0].length) {
120
- // TODO: handle page archives.
121
- this.next = `/${parts.join('/')}`;
122
- }
123
- } else {
124
- this.next = this.slugify(newValue);
125
- }
126
- }
127
- }
128
- }
129
- },
130
- async mounted() {
131
- this.debouncedCheckConflict = debounce(() => this.checkConflict(), 250);
132
- if (this.next.length) {
133
- await this.debouncedCheckConflict();
134
- }
135
- this.originalSlugPartsLength = this.next.split('/').length;
136
- },
137
- methods: {
138
- async watchNext() {
139
- this.next = this.slugify(this.next);
140
- this.validateAndEmit();
141
- try {
142
- await this.debouncedCheckConflict();
143
- } catch (e) {
144
- if (e === 'canceled') {
145
- // That's fine
146
- } else {
147
- throw e;
148
- }
149
- }
150
- },
151
- validate(value) {
152
- if (this.conflict) {
153
- return {
154
- name: 'conflict',
155
- message: 'apostrophe:slugInUse'
156
- };
157
- }
158
- if (this.field.required) {
159
- if (!value.length) {
160
- return 'required';
161
- }
162
- }
163
- if (this.field.min) {
164
- if (value.length && (value.length < this.field.min)) {
165
- return 'min';
166
- }
167
- }
168
- if (this.field.max) {
169
- if (value.length && (value.length > this.field.max)) {
170
- return 'max';
171
- }
172
- }
173
- return false;
174
- },
175
- compatible(title, slug) {
176
- if ((typeof title) !== 'string') {
177
- title = '';
178
- }
179
- if (this.field.page) {
180
- const matches = slug.match(/[^/]+$/);
181
- slug = (matches && matches[0]) || '';
182
- }
183
- return ((title === '') && (slug === `${this.prefix}`)) ||
184
- this.slugify(title) === this.slugify(slug);
185
- },
186
- // if componentOnly is true, we are slugifying just one component of
187
- // a slug as part of following the title field, and so we do *not*
188
- // want to allow slashes (when editing a page) or set a prefix.
189
- slugify(s, { componentOnly = false } = {}) {
190
- const options = {
191
- def: ''
192
- };
193
-
194
- if (this.field.page && !componentOnly) {
195
- options.allow = '/';
196
- }
197
-
198
- let preserveDash = false;
199
- // When you are typing a slug it feels wrong for hyphens you typed
200
- // to disappear as you go, so if the last character is not valid in a slug,
201
- // restore it after we call sluggo for the full string
202
- if (this.focus && s.length && (sluggo(s.charAt(s.length - 1), options) === '')) {
203
- preserveDash = true;
204
- }
205
-
206
- s = sluggo(s, options);
207
- if (preserveDash) {
208
- s += '-';
209
- }
210
-
211
- if (this.field.page && !componentOnly) {
212
- if (!this.followingValues?.title) {
213
- const nextParts = this.next.split('/');
214
- if (s === nextParts[nextParts.length - 1]) {
215
- s = '';
216
- if (this.originalSlugPartsLength === nextParts.length) {
217
- nextParts.pop();
218
- }
219
- this.next = nextParts.join('/');
220
- }
221
- }
222
- if (!s.charAt(0) !== '/') {
223
- s = `/${s}`;
224
- }
225
- s = s.replace(/\/+/g, '/');
226
- if (s !== '/') {
227
- s = s.replace(/\/$/, '');
228
- }
229
- if (!this.followingValues?.title && s.length) {
230
- s += '/';
231
- }
232
- }
233
-
234
- if (!componentOnly) {
235
- s = this.setPrefix(s);
236
- }
237
-
238
- return s;
239
- },
240
- setPrefix (slug) {
241
- // Get a fresh clone of the slug.
242
- let updated = slug;
243
- const archivedRegexp = new RegExp(`^deduplicate-[a-z0-9]+-${this.prefix}`);
244
-
245
- // Prefix if the slug doesn't start with the prefix OR if its archived
246
- // and it doesn't start with the dedupe+prefix pattern.
247
- if (
248
- !updated.startsWith(this.prefix) ||
249
- (this.isArchived && !updated.match(archivedRegexp))
250
- ) {
251
- let archivePrefix = '';
252
- // If archived, remove the dedupe pattern to add again later.
253
- if (this.isArchived) {
254
- archivePrefix = updated.match(/^deduplicate-[a-z0-9]+-/);
255
- updated = updated.replace(archivePrefix, '');
256
- }
257
-
258
- if (this.prefix.startsWith(updated)) {
259
- // If they delete the `-`, and the prefix is `recipe-`,
260
- // we want to restore `recipe-`, not set it to `recipe-recipe`
261
- updated = this.prefix;
262
- } else {
263
- // Make sure we're not double prefixing archived slugs.
264
- updated = updated.startsWith(this.prefix) ? updated : this.prefix + updated;
265
- }
266
- // Reapply the dedupe pattern if archived. If being restored from the
267
- // doc editor modal it will momentarily be tracked as archived but
268
- // without not have the archive prefix, so check that too.
269
- updated = this.isArchived && archivePrefix ? `${archivePrefix}${updated}` : updated;
270
- }
271
-
272
- return updated;
273
- },
274
- async checkConflict() {
275
- let slug;
276
- try {
277
- slug = this.next;
278
- if (slug.length) {
279
- await apos.http.post(`${apos.doc.action}/slug-taken`, {
280
- body: {
281
- slug,
282
- _id: this.docId
283
- },
284
- draft: true
285
- });
286
- // Still relevant?
287
- if (slug === this.next) {
288
- this.conflict = false;
289
- this.validateAndEmit();
290
- } else {
291
- // Can ignore it, another request
292
- // probably already in-flight
293
- }
294
- }
295
- } catch (e) {
296
- // 409: Conflict (slug in use)
297
- if (e.status === 409) {
298
- // Still relevant?
299
- if (slug === this.next) {
300
- this.conflict = true;
301
- this.validateAndEmit();
302
- } else {
303
- // Can ignore it, another request
304
- // probably already in-flight
305
- }
306
- } else {
307
- throw e;
308
- }
309
- }
310
- },
311
- passFocus() {
312
- this.$refs.input.focus();
313
- }
314
- }
41
+ mixins: [ AposInputSlugLogic ]
315
42
  };
316
43
  </script>
317
44
 
@@ -37,175 +37,10 @@
37
37
  </template>
38
38
 
39
39
  <script>
40
- import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin';
41
-
40
+ import AposInputStringLogic from '../logic/AposInputString';
42
41
  export default {
43
42
  name: 'AposInputString',
44
- mixins: [ AposInputMixin ],
45
- emits: [ 'return' ],
46
- data () {
47
- return {
48
- step: undefined,
49
- wasPopulated: false
50
- };
51
- },
52
- computed: {
53
- tabindex () {
54
- return this.field.disableFocus ? '-1' : '0';
55
- },
56
- type () {
57
- if (this.field.type) {
58
- if (this.field.type === 'float' || this.field.type === 'integer') {
59
- return 'number';
60
- }
61
- if (this.field.type === 'string' || this.field.type === 'slug') {
62
- return 'text';
63
- }
64
- return this.field.type;
65
- } else {
66
- return 'text';
67
- }
68
- },
69
- classes () {
70
- return [ 'apos-input', `apos-input--${this.type}`, this.value.duplicate && 'apos-input--error' ];
71
- },
72
- icon () {
73
- if (this.error) {
74
- return 'circle-medium-icon';
75
- } else if (this.field.icon) {
76
- return this.field.icon;
77
- } else {
78
- return null;
79
- }
80
- }
81
- },
82
- watch: {
83
- followingValues: {
84
- // We may be following multiple fields, like firstName and lastName,
85
- // or none at all, depending
86
- deep: true,
87
- handler(newValue, oldValue) {
88
- // Follow the value of the other field(s), but only if our
89
- // previous value matched the previous value of the other field(s)
90
- oldValue = Object.values(oldValue).join(' ').trim();
91
- newValue = Object.values(newValue).join(' ').trim();
92
- if ((!this.wasPopulated && ((this.next == null) || (!this.next.length))) || (this.next === oldValue)) {
93
- this.next = newValue;
94
- }
95
- }
96
- },
97
- next() {
98
- if (this.next && this.next.length) {
99
- this.wasPopulated = true;
100
- }
101
- }
102
- },
103
- mounted() {
104
- this.defineStep();
105
- this.wasPopulated = this.next && this.next.length;
106
- },
107
- methods: {
108
- enterEmit() {
109
- if (this.field.enterSubmittable) {
110
- // Include the validated results in cases where an Enter keydown should
111
- // act as submitting a form.
112
- this.$emit('return', {
113
- data: this.next,
114
- error: this.validate(this.next)
115
- });
116
- } else {
117
- this.$emit('return');
118
- }
119
- },
120
- validate(value) {
121
- if (value == null) {
122
- value = '';
123
- }
124
- if (typeof value === 'string' && !value.length) {
125
- // Also correct for float and integer because Vue coerces
126
- // number fields to either a number or the empty string
127
- return this.field.required ? 'required' : false;
128
- }
129
-
130
- const minMaxFields = [
131
- 'integer',
132
- 'float',
133
- 'string',
134
- 'date',
135
- 'password'
136
- ];
137
-
138
- if (typeof value === 'string' && this.field.pattern) {
139
- const regex = new RegExp(this.field.pattern);
140
- if (!regex.test(value)) {
141
- return 'invalid';
142
- }
143
- }
144
-
145
- if (this.field.min && minMaxFields.includes(this.field.type)) {
146
- if ((value != null) && value.length && (this.minMaxComparable(value) < this.field.min)) {
147
- return 'min';
148
- }
149
- }
150
- if (this.field.max && minMaxFields.includes(this.field.type)) {
151
- if ((value != null) && value.length && (this.minMaxComparable(value) > this.field.max)) {
152
- return 'max';
153
- }
154
- }
155
- if (this.field.type === 'email' && value) {
156
- // regex source: https://emailregex.com/
157
- const matches = value.match(/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/);
158
- if (!matches) {
159
- return 'invalid';
160
- }
161
- }
162
- return false;
163
- },
164
- defineStep() {
165
- if (this.type === 'number') {
166
- this.step = this.field.type === 'float' ? 'any' : 1;
167
- }
168
- },
169
- convert(s) {
170
- if (this.field.type === 'integer') {
171
- if ((s == null) || (s === '')) {
172
- return s;
173
- } else {
174
- return parseInt(s);
175
- }
176
- } else if (this.field.type === 'float') {
177
- if ((s == null) || (s === '')) {
178
- return s;
179
- } else {
180
- // The native parse float converts 3.0 to 3 and makes
181
- // next to become integer. In theory we don't need parseFloat
182
- // as the value is natively guarded by the browser 'number' type.
183
- // However we need a float value sent to the backend
184
- // and we force that when focus is lost.
185
- if (this.focus && `${s}`.match(/\.[0]*$/)) {
186
- return s;
187
- }
188
- return parseFloat(s);
189
- }
190
- } else {
191
- if (s == null) {
192
- return '';
193
- } else {
194
- return s.toString();
195
- }
196
- }
197
- },
198
- minMaxComparable(s) {
199
- const converted = this.convert(s);
200
- if ([ 'integer', 'float', 'date', 'range', 'time' ].includes(this.field.type)) {
201
- // Compare the actual values for these types
202
- return converted;
203
- } else {
204
- // Compare the length for other types, like string or password or url
205
- return converted.length;
206
- }
207
- }
208
- }
43
+ mixins: [ AposInputStringLogic ]
209
44
  };
210
45
  </script>
211
46
 
@@ -57,123 +57,10 @@
57
57
  </template>
58
58
 
59
59
  <script>
60
- // A component designed to be used as a scaffold for AposInputString and
61
- // friends, which override the `body` slot
60
+ import AposInputWrapperLogic from '../logic/AposInputWrapper';
62
61
  export default {
63
62
  name: 'AposInputWrapper',
64
- inject: {
65
- originalDoc: {
66
- default: () => ({
67
- ref: null
68
- })
69
- }
70
- },
71
- props: {
72
- field: {
73
- type: Object,
74
- required: true
75
- },
76
- error: {
77
- type: [ String, Boolean, Object ],
78
- default: null
79
- },
80
- uid: {
81
- type: Number,
82
- required: true
83
- },
84
- modifiers: {
85
- type: Array,
86
- default() {
87
- return [];
88
- }
89
- },
90
- items: {
91
- type: Array,
92
- default() {
93
- return [];
94
- }
95
- },
96
- displayOptions: {
97
- type: Object,
98
- default() {
99
- return {};
100
- }
101
- }
102
- },
103
- data () {
104
- return {
105
- wrapEl: 'div',
106
- labelEl: 'label'
107
- };
108
- },
109
- computed: {
110
- label () {
111
- const { label, publishedLabel } = this.field;
112
-
113
- if (
114
- this.originalDoc.ref &&
115
- this.originalDoc.ref.lastPublishedAt &&
116
- publishedLabel
117
- ) {
118
- return publishedLabel;
119
- }
120
-
121
- return label;
122
- },
123
- classList: function () {
124
- const classes = [
125
- 'apos-field',
126
- `apos-field--${this.field.type}`,
127
- `apos-field--${this.field.name}`
128
- ];
129
- if (this.field.classes) {
130
- classes.push(this.field.classes);
131
- }
132
- if (this.errorClasses) {
133
- classes.push(this.errorClasses);
134
- }
135
- if (this.modifiers) {
136
- this.modifiers.forEach((m) => {
137
- classes.push(`apos-field--${m}`);
138
- });
139
- }
140
- return classes;
141
- },
142
- errorClasses: function () {
143
- if (!this.error) {
144
- return null;
145
- }
146
-
147
- let error = 'unknown';
148
-
149
- if (typeof this.error === 'string') {
150
- error = this.error;
151
- } else if (this.error.name) {
152
- error = this.error.name;
153
- }
154
-
155
- return `apos-field--error apos-field--error-${error}`;
156
- },
157
- errorMessage () {
158
- if (this.error) {
159
- if (typeof this.error === 'string') {
160
- return this.error;
161
- } else if (this.error.message) {
162
- return this.error.message;
163
- } else {
164
- return 'Error';
165
- }
166
- } else {
167
- return false;
168
- }
169
- }
170
- },
171
- mounted: function () {
172
- if (this.field.type === 'radio' || this.field.type === 'checkbox') {
173
- this.wrapEl = 'fieldset';
174
- this.labelEl = 'legend';
175
- }
176
- }
63
+ mixins: [ AposInputWrapperLogic ]
177
64
  };
178
65
  </script>
179
66