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
@@ -0,0 +1,170 @@
1
+ import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin';
2
+
3
+ export default {
4
+ name: 'AposInputString',
5
+ mixins: [ AposInputMixin ],
6
+ emits: [ 'return' ],
7
+ data () {
8
+ return {
9
+ step: undefined,
10
+ wasPopulated: false
11
+ };
12
+ },
13
+ computed: {
14
+ tabindex () {
15
+ return this.field.disableFocus ? '-1' : '0';
16
+ },
17
+ type () {
18
+ if (this.field.type) {
19
+ if (this.field.type === 'float' || this.field.type === 'integer') {
20
+ return 'number';
21
+ }
22
+ if (this.field.type === 'string' || this.field.type === 'slug') {
23
+ return 'text';
24
+ }
25
+ return this.field.type;
26
+ } else {
27
+ return 'text';
28
+ }
29
+ },
30
+ classes () {
31
+ return [ 'apos-input', `apos-input--${this.type}`, this.value.duplicate && 'apos-input--error' ];
32
+ },
33
+ icon () {
34
+ if (this.error) {
35
+ return 'circle-medium-icon';
36
+ } else if (this.field.icon) {
37
+ return this.field.icon;
38
+ } else {
39
+ return null;
40
+ }
41
+ }
42
+ },
43
+ watch: {
44
+ followingValues: {
45
+ // We may be following multiple fields, like firstName and lastName,
46
+ // or none at all, depending
47
+ deep: true,
48
+ handler(newValue, oldValue) {
49
+ // Follow the value of the other field(s), but only if our
50
+ // previous value matched the previous value of the other field(s)
51
+ oldValue = Object.values(oldValue).join(' ').trim();
52
+ newValue = Object.values(newValue).join(' ').trim();
53
+ if ((!this.wasPopulated && ((this.next == null) || (!this.next.length))) || (this.next === oldValue)) {
54
+ this.next = newValue;
55
+ }
56
+ }
57
+ },
58
+ next() {
59
+ if (this.next && this.next.length) {
60
+ this.wasPopulated = true;
61
+ }
62
+ }
63
+ },
64
+ mounted() {
65
+ this.defineStep();
66
+ this.wasPopulated = this.next && this.next.length;
67
+ },
68
+ methods: {
69
+ enterEmit() {
70
+ if (this.field.enterSubmittable) {
71
+ // Include the validated results in cases where an Enter keydown should
72
+ // act as submitting a form.
73
+ this.$emit('return', {
74
+ data: this.next,
75
+ error: this.validate(this.next)
76
+ });
77
+ } else {
78
+ this.$emit('return');
79
+ }
80
+ },
81
+ validate(value) {
82
+ if (value == null) {
83
+ value = '';
84
+ }
85
+ if (typeof value === 'string' && !value.length) {
86
+ // Also correct for float and integer because Vue coerces
87
+ // number fields to either a number or the empty string
88
+ return this.field.required ? 'required' : false;
89
+ }
90
+
91
+ const minMaxFields = [
92
+ 'integer',
93
+ 'float',
94
+ 'string',
95
+ 'date',
96
+ 'password'
97
+ ];
98
+
99
+ if (typeof value === 'string' && this.field.pattern) {
100
+ const regex = new RegExp(this.field.pattern);
101
+ if (!regex.test(value)) {
102
+ return 'invalid';
103
+ }
104
+ }
105
+
106
+ if (this.field.min && minMaxFields.includes(this.field.type)) {
107
+ if ((value != null) && value.length && (this.minMaxComparable(value) < this.field.min)) {
108
+ return 'min';
109
+ }
110
+ }
111
+ if (this.field.max && minMaxFields.includes(this.field.type)) {
112
+ if ((value != null) && value.length && (this.minMaxComparable(value) > this.field.max)) {
113
+ return 'max';
114
+ }
115
+ }
116
+ if (this.field.type === 'email' && value) {
117
+ // regex source: https://emailregex.com/
118
+ 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,}))$/);
119
+ if (!matches) {
120
+ return 'invalid';
121
+ }
122
+ }
123
+ return false;
124
+ },
125
+ defineStep() {
126
+ if (this.type === 'number') {
127
+ this.step = this.field.type === 'float' ? 'any' : 1;
128
+ }
129
+ },
130
+ convert(s) {
131
+ if (this.field.type === 'integer') {
132
+ if ((s == null) || (s === '')) {
133
+ return s;
134
+ } else {
135
+ return parseInt(s);
136
+ }
137
+ } else if (this.field.type === 'float') {
138
+ if ((s == null) || (s === '')) {
139
+ return s;
140
+ } else {
141
+ // The native parse float converts 3.0 to 3 and makes
142
+ // next to become integer. In theory we don't need parseFloat
143
+ // as the value is natively guarded by the browser 'number' type.
144
+ // However we need a float value sent to the backend
145
+ // and we force that when focus is lost.
146
+ if (this.focus && `${s}`.match(/\.[0]*$/)) {
147
+ return s;
148
+ }
149
+ return parseFloat(s);
150
+ }
151
+ } else {
152
+ if (s == null) {
153
+ return '';
154
+ } else {
155
+ return s.toString();
156
+ }
157
+ }
158
+ },
159
+ minMaxComparable(s) {
160
+ const converted = this.convert(s);
161
+ if ([ 'integer', 'float', 'date', 'range', 'time' ].includes(this.field.type)) {
162
+ // Compare the actual values for these types
163
+ return converted;
164
+ } else {
165
+ // Compare the length for other types, like string or password or url
166
+ return converted.length;
167
+ }
168
+ }
169
+ }
170
+ };
@@ -0,0 +1,118 @@
1
+ // A component designed to be used as a scaffold for AposInputString and
2
+ // friends, which override the `body` slot
3
+ export default {
4
+ name: 'AposInputWrapper',
5
+ inject: {
6
+ originalDoc: {
7
+ default: () => ({
8
+ ref: null
9
+ })
10
+ }
11
+ },
12
+ props: {
13
+ field: {
14
+ type: Object,
15
+ required: true
16
+ },
17
+ error: {
18
+ type: [ String, Boolean, Object ],
19
+ default: null
20
+ },
21
+ uid: {
22
+ type: Number,
23
+ required: true
24
+ },
25
+ modifiers: {
26
+ type: Array,
27
+ default() {
28
+ return [];
29
+ }
30
+ },
31
+ items: {
32
+ type: Array,
33
+ default() {
34
+ return [];
35
+ }
36
+ },
37
+ displayOptions: {
38
+ type: Object,
39
+ default() {
40
+ return {};
41
+ }
42
+ }
43
+ },
44
+ data () {
45
+ return {
46
+ wrapEl: 'div',
47
+ labelEl: 'label'
48
+ };
49
+ },
50
+ computed: {
51
+ label () {
52
+ const { label, publishedLabel } = this.field;
53
+
54
+ if (
55
+ this.originalDoc.ref &&
56
+ this.originalDoc.ref.lastPublishedAt &&
57
+ publishedLabel
58
+ ) {
59
+ return publishedLabel;
60
+ }
61
+
62
+ return label;
63
+ },
64
+ classList: function () {
65
+ const classes = [
66
+ 'apos-field',
67
+ `apos-field--${this.field.type}`,
68
+ `apos-field--${this.field.name}`
69
+ ];
70
+ if (this.field.classes) {
71
+ classes.push(this.field.classes);
72
+ }
73
+ if (this.errorClasses) {
74
+ classes.push(this.errorClasses);
75
+ }
76
+ if (this.modifiers) {
77
+ this.modifiers.forEach((m) => {
78
+ classes.push(`apos-field--${m}`);
79
+ });
80
+ }
81
+ return classes;
82
+ },
83
+ errorClasses: function () {
84
+ if (!this.error) {
85
+ return null;
86
+ }
87
+
88
+ let error = 'unknown';
89
+
90
+ if (typeof this.error === 'string') {
91
+ error = this.error;
92
+ } else if (this.error.name) {
93
+ error = this.error.name;
94
+ }
95
+
96
+ return `apos-field--error apos-field--error-${error}`;
97
+ },
98
+ errorMessage () {
99
+ if (this.error) {
100
+ if (typeof this.error === 'string') {
101
+ return this.error;
102
+ } else if (this.error.message) {
103
+ return this.error.message;
104
+ } else {
105
+ return 'Error';
106
+ }
107
+ } else {
108
+ return false;
109
+ }
110
+ }
111
+ },
112
+ mounted: function () {
113
+ if (this.field.type === 'radio' || this.field.type === 'checkbox') {
114
+ this.wrapEl = 'fieldset';
115
+ this.labelEl = 'legend';
116
+ }
117
+ }
118
+ };
@@ -0,0 +1,281 @@
1
+ import { detectFieldChange } from 'Modules/@apostrophecms/schema/lib/detectChange';
2
+
3
+ export default {
4
+ name: 'AposSchema',
5
+ props: {
6
+ value: {
7
+ type: Object,
8
+ required: true
9
+ },
10
+ generation: {
11
+ type: Number,
12
+ required: false,
13
+ default() {
14
+ return null;
15
+ }
16
+ },
17
+ schema: {
18
+ type: Array,
19
+ required: true
20
+ },
21
+ fieldStyle: {
22
+ type: String,
23
+ required: false,
24
+ default: ''
25
+ },
26
+ currentFields: {
27
+ type: Array,
28
+ default() {
29
+ return null;
30
+ }
31
+ },
32
+ followingValues: {
33
+ type: Object,
34
+ default() {
35
+ return {};
36
+ }
37
+ },
38
+ conditionalFields: {
39
+ type: Object,
40
+ default() {
41
+ return {};
42
+ }
43
+ },
44
+ modifiers: {
45
+ type: Array,
46
+ default() {
47
+ return [];
48
+ }
49
+ },
50
+ triggerValidation: Boolean,
51
+ utilityRail: {
52
+ type: Boolean,
53
+ default() {
54
+ return false;
55
+ }
56
+ },
57
+ docId: {
58
+ type: String,
59
+ default() {
60
+ return null;
61
+ }
62
+ },
63
+ serverErrors: {
64
+ type: Object,
65
+ default() {
66
+ return null;
67
+ }
68
+ },
69
+ displayOptions: {
70
+ type: Object,
71
+ default() {
72
+ return {};
73
+ }
74
+ },
75
+ changed: {
76
+ type: Array,
77
+ default() {
78
+ return [];
79
+ }
80
+ }
81
+ },
82
+ emits: [
83
+ 'input',
84
+ 'reset',
85
+ 'validate',
86
+ 'update-doc-data'
87
+ ],
88
+ data() {
89
+ return {
90
+ schemaReady: false,
91
+ next: {
92
+ hasErrors: false,
93
+ data: {},
94
+ fieldErrors: {}
95
+ },
96
+ fieldState: {},
97
+ fieldComponentMap: window.apos.schema.components.fields || {}
98
+ };
99
+ },
100
+ computed: {
101
+ fields() {
102
+ const fields = {};
103
+ this.schema.forEach(item => {
104
+ fields[item.name] = {};
105
+ fields[item.name].field = item;
106
+ fields[item.name].value = {
107
+ data: this.value[item.name]
108
+ };
109
+ fields[item.name].serverError = this.serverErrors && this.serverErrors[item.name];
110
+ fields[item.name].modifiers = [
111
+ ...(this.modifiers || []),
112
+ ...(item.modifiers || [])
113
+ ];
114
+ });
115
+ return fields;
116
+ }
117
+ },
118
+ watch: {
119
+ fieldState: {
120
+ deep: true,
121
+ handler() {
122
+ this.updateNextAndEmit();
123
+ }
124
+ },
125
+ schema() {
126
+ this.populateDocData();
127
+ },
128
+ 'value.data._id'(_id) {
129
+ // The doc might be swapped out completely in cases such as the media
130
+ // library editor. Repopulate the fields if that happens.
131
+ if (
132
+ // If the fieldState had been cleared and there's new populated data
133
+ (!this.fieldState._id && _id) ||
134
+ // or if there *is* active fieldState, but the new data is a new doc
135
+ (this.fieldState._id && _id !== this.fieldState._id.data)
136
+ ) {
137
+ // repopulate the schema.
138
+ this.populateDocData();
139
+ }
140
+ },
141
+ generation() {
142
+ // repopulate the schema.
143
+ this.populateDocData();
144
+ },
145
+ conditionalFields(newVal, oldVal) {
146
+ for (const field in oldVal) {
147
+ if (!this.fieldState[field] || (newVal[field] === oldVal[field]) || !this.fieldState[field].ranValidation) {
148
+ continue;
149
+ }
150
+
151
+ if (
152
+ (newVal[field] === false) ||
153
+ (newVal[field] && this.fieldState[field].ranValidation)
154
+ ) {
155
+ this.$emit('validate');
156
+ }
157
+ }
158
+ }
159
+ },
160
+ created() {
161
+ this.populateDocData();
162
+ },
163
+ methods: {
164
+ getDisplayOptions(fieldName) {
165
+ let options = {};
166
+ if (this.displayOptions) {
167
+ options = { ...this.displayOptions };
168
+ }
169
+ if (this.changed && this.changed.includes(fieldName)) {
170
+ options.changed = true;
171
+ }
172
+ return options;
173
+ },
174
+ populateDocData() {
175
+ this.schemaReady = false;
176
+ const next = {
177
+ hasErrors: false,
178
+ data: {}
179
+ };
180
+
181
+ const fieldState = {};
182
+
183
+ // Though not in the schema, keep track of the _id field.
184
+ if (this.value.data._id) {
185
+ next.data._id = this.value.data._id;
186
+ fieldState._id = { data: this.value.data._id };
187
+ }
188
+ // Though not *always* in the schema, keep track of the archived status.
189
+ if (this.value.data.archived !== undefined) {
190
+ next.data.archived = this.value.data.archived;
191
+ fieldState.archived = { data: this.value.data.archived };
192
+ }
193
+
194
+ this.schema.forEach(field => {
195
+ const value = this.value.data[field.name];
196
+ fieldState[field.name] = {
197
+ error: false,
198
+ data: (value === undefined) ? field.def : value
199
+ };
200
+ next.data[field.name] = fieldState[field.name].data;
201
+ });
202
+ this.next = next;
203
+ this.fieldState = fieldState;
204
+
205
+ // Wait until the next tick so the parent editor component is done
206
+ // updating. This is only really a concern in editors that can swap
207
+ // the active doc/object without unmounting AposSchema.
208
+ this.$nextTick(() => {
209
+ this.schemaReady = true;
210
+ // Signal that the schema data is ready to be tracked.
211
+ this.$emit('reset');
212
+ });
213
+ },
214
+ updateNextAndEmit() {
215
+ if (!this.schemaReady) {
216
+ return;
217
+ }
218
+ const oldHasErrors = this.next.hasErrors;
219
+ // destructure these for non-linked comparison
220
+ const oldFieldState = { ...this.next.fieldState };
221
+ const newFieldState = { ...this.fieldState };
222
+
223
+ let changeFound = false;
224
+
225
+ this.next.hasErrors = false;
226
+ this.next.fieldState = { ...this.fieldState };
227
+
228
+ this.schema.filter(field => this.displayComponent(field)).forEach(field => {
229
+ if (this.fieldState[field.name].error) {
230
+ this.next.hasErrors = true;
231
+ }
232
+ if (
233
+ this.fieldState[field.name].data !== undefined &&
234
+ detectFieldChange(field, this.next.data[field.name], this.fieldState[field.name].data)
235
+ ) {
236
+ changeFound = true;
237
+ this.next.data[field.name] = this.fieldState[field.name].data;
238
+ } else {
239
+ this.next.data[field.name] = this.value.data[field.name];
240
+ }
241
+ });
242
+ if (
243
+ oldHasErrors !== this.next.hasErrors ||
244
+ oldFieldState !== newFieldState
245
+ ) {
246
+ // Otherwise the save button may never unlock
247
+ changeFound = true;
248
+ }
249
+
250
+ if (changeFound) {
251
+ // ... removes need for deep watch at parent level
252
+ this.$emit('input', { ...this.next });
253
+ }
254
+ },
255
+ displayComponent({ name, hidden = false }) {
256
+ if (hidden === true) {
257
+ return false;
258
+ }
259
+
260
+ if (this.currentFields && !this.currentFields.includes(name)) {
261
+ return false;
262
+ }
263
+
264
+ // Might not be a conditional field at all, so test explicitly for false
265
+ if (this.conditionalFields[name] === false) {
266
+ return false;
267
+ }
268
+
269
+ return true;
270
+ },
271
+ scrollFieldIntoView(fieldName) {
272
+ // The refs for a name are an array if that ref was assigned
273
+ // in a v-for. We know there is only one in this case
274
+ // https://forum.vuejs.org/t/this-refs-theid-returns-an-array/31995/9
275
+ this.$refs[fieldName][0].$el.scrollIntoView();
276
+ },
277
+ onUpdateDocData(data) {
278
+ this.$emit('update-doc-data', data);
279
+ }
280
+ }
281
+ };
@@ -0,0 +1,85 @@
1
+ export default {
2
+ name: 'AposSearchList',
3
+ props: {
4
+ list: {
5
+ type: Array,
6
+ default() {
7
+ return [];
8
+ }
9
+ },
10
+ customFields: {
11
+ type: Array,
12
+ default() {
13
+ return [];
14
+ }
15
+ },
16
+ selectedItems: {
17
+ type: Array,
18
+ default() {
19
+ return [];
20
+ }
21
+ },
22
+ disabledTooltip: {
23
+ type: String,
24
+ default: null
25
+ },
26
+ label: {
27
+ type: String,
28
+ default: ''
29
+ },
30
+ help: {
31
+ type: [ String, Object ],
32
+ default: ''
33
+ },
34
+ icon: {
35
+ type: String,
36
+ default: 'text-box-icon'
37
+ },
38
+ iconSize: {
39
+ type: Number,
40
+ default: 20
41
+ },
42
+ fields: {
43
+ type: Array,
44
+ default() {
45
+ return [ 'slug' ];
46
+ }
47
+ }
48
+ },
49
+ emits: [ 'select' ],
50
+ methods: {
51
+ select(item, $event) {
52
+ if (item.disabled) {
53
+ $event.stopPropagation();
54
+ return;
55
+ }
56
+ const selectedItems = this.selectedItems;
57
+ if (!selectedItems.some(selectedItem => selectedItem._id === item._id)) {
58
+ // Never modify a prop
59
+ this.$emit('select', [ ...selectedItems, item ]);
60
+ }
61
+ },
62
+ getClasses(item) {
63
+ const classes = {
64
+ 'apos-search__item': true
65
+ };
66
+ if (item.disabled) {
67
+ classes['apos-search__item--disabled'] = true;
68
+ }
69
+ item.classes && Array.isArray(item.classes) && item.classes.forEach(suffix => {
70
+ classes[`apos-search__item--${suffix}`] = true;
71
+ });
72
+
73
+ return classes;
74
+ },
75
+ getTooltip(item) {
76
+ return item.disabled && item.tooltip !== false ? this.disabledTooltip : null;
77
+ },
78
+ getIcon(item) {
79
+ return {
80
+ icon: item.icon ?? this.icon,
81
+ iconSize: item.iconSize || this.iconSize
82
+ };
83
+ }
84
+ }
85
+ };
@@ -698,7 +698,7 @@ module.exports = {
698
698
  // Make the query available to templates for easy access to
699
699
  // filter settings etc.
700
700
  query: req.query,
701
- url: unrefreshed(req.url)
701
+ url: unrefreshed(req.prefix + req.url)
702
702
  };
703
703
 
704
704
  _.extend(args, data);
@@ -96,8 +96,8 @@ export default {
96
96
  }
97
97
 
98
98
  // Set the column width to the spacer width plus 15 for extra wiggle
99
- // room.
100
- colWidths[col.property] = ref.clientWidth + 15;
99
+ // room. Add additional width if specified.
100
+ colWidths[col.property] = ref.clientWidth + 15 + (col.extraWidth ?? 0);
101
101
  });
102
102
  this.$emit('calculated', colWidths);
103
103
  },