apostrophe 3.60.0 → 3.61.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.
- package/CHANGELOG.md +30 -0
- package/index.js +3 -3
- package/modules/@apostrophecms/area/index.js +22 -0
- package/modules/@apostrophecms/cache/index.js +9 -0
- package/modules/@apostrophecms/doc-type/index.js +32 -6
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +1 -1
- package/modules/@apostrophecms/login/index.js +1 -4
- package/modules/@apostrophecms/modal/ui/apos/mixins/AposEditorMixin.js +1 -0
- package/modules/@apostrophecms/module/index.js +2 -2
- package/modules/@apostrophecms/notification/index.js +9 -9
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposImageControlDialog.vue +44 -17
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapAnchor.vue +2 -2
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapImage.vue +33 -6
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapLink.vue +15 -8
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Image.js +2 -1
- package/modules/@apostrophecms/schema/index.js +3 -2
- package/modules/@apostrophecms/schema/lib/addFieldTypes.js +26 -4
- package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +8 -4
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputArray.vue +2 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputPassword.vue +4 -2
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputSelect.vue +1 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +4 -2
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +2 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +10 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +1 -0
- package/modules/@apostrophecms/schema/ui/apos/logic/AposArrayEditor.js +3 -0
- package/modules/@apostrophecms/schema/ui/apos/logic/AposInputPassword.js +3 -0
- package/modules/@apostrophecms/schema/ui/apos/logic/AposInputSlug.js +3 -0
- package/modules/@apostrophecms/template/index.js +8 -4
- package/modules/@apostrophecms/ui/ui/apos/components/AposCheckbox.vue +14 -8
- package/modules/@apostrophecms/ui/ui/apos/components/AposLabel.vue +16 -1
- package/modules/@apostrophecms/ui/ui/apos/components/AposSelect.vue +5 -0
- package/modules/@apostrophecms/ui/ui/apos/mixins/AposAdvisoryLockMixin.js +1 -1
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_theme.scss +4 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.61.0 (2023-12-21)
|
|
4
|
+
|
|
5
|
+
### Adds
|
|
6
|
+
|
|
7
|
+
* Add a `validate` method to the `url` field type to allow the use of the `pattern` property.
|
|
8
|
+
* Add `autocomplete` attribute to schema fields that implement it (cf. [HTML attribute: autocomplete](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete)).
|
|
9
|
+
* Add the `delete` method to the `@apostrophecms/cache` module so we don't have to rely on direct MongoDB manipulation to remove a cache item.
|
|
10
|
+
* Adds tag property to fields in order to show a tag next to the field title (used in advanced permission for the admin field). Adds new sensitive label color.
|
|
11
|
+
* Pass on the module name and the full, namespaced template name to external front ends, e.g. Astro.
|
|
12
|
+
Also make this information available to other related methods for future and project-level use.
|
|
13
|
+
* Fixes the AposCheckbox component to be used more easily standalone, accepts a single model value instead of an array.
|
|
14
|
+
|
|
15
|
+
### Fixes
|
|
16
|
+
|
|
17
|
+
* Fix `date` schema field query builder to work with arrays.
|
|
18
|
+
* Fix `if` on pages. When you open the `AposDocEditor` modal on pages, you now see an up to date view of the visible fields.
|
|
19
|
+
* Pass on complete annotation information for nested areas when adding or editing a nested widget using an external front, like Astro.
|
|
20
|
+
* We can now close the image modal in rich-text widgets when we click outside of the modal.
|
|
21
|
+
The click on the cancel button now works too.
|
|
22
|
+
* Fixes the `clearLoginAttempts` method to work with the new `@apostrophecms/cache` module `delete` method.
|
|
23
|
+
|
|
24
|
+
## 3.60.1 (2023-12-06)
|
|
25
|
+
|
|
26
|
+
### Fixes
|
|
27
|
+
|
|
28
|
+
* corrected an issue where the use of the doc template library can result in errors at startup when
|
|
29
|
+
replicating certain content to new locales. This was not a bug in the doc template library.
|
|
30
|
+
Apostrophe was not invoking `findForEditing` where it should have.
|
|
31
|
+
|
|
3
32
|
## 3.60.0 (2023-11-29)
|
|
4
33
|
|
|
5
34
|
### Adds
|
|
@@ -510,6 +539,7 @@ those writing mocha tests of Apostrophe modules.
|
|
|
510
539
|
* Hide the suggestion help from the relationship input list when the user starts typing a search term.
|
|
511
540
|
* Hide the suggestion hint from the relationship input list when the user starts typing a search term except when there are no matches to display.
|
|
512
541
|
* Disable context menu for related items when their `relationship` field has no sub-[`fields`](https://v3.docs.apostrophecms.org/guide/relationships.html#providing-context-with-fields) configured.
|
|
542
|
+
* Logic for checking whether we are running a unit test of an external module under mocha now uses `includes` for a simpler, safer test that should be more cross-platform.
|
|
513
543
|
|
|
514
544
|
## 3.42.0 (2023-03-16)
|
|
515
545
|
|
package/index.js
CHANGED
|
@@ -525,12 +525,12 @@ async function apostrophe(options, telemetry, rootSpan) {
|
|
|
525
525
|
// and throws an exception if we don't
|
|
526
526
|
function findTestModule() {
|
|
527
527
|
let m = module;
|
|
528
|
-
const
|
|
529
|
-
if (!require.main.filename.
|
|
528
|
+
const testFor = `node_modules${path.sep}mocha`;
|
|
529
|
+
if (!require.main.filename.includes(testFor)) {
|
|
530
530
|
throw new Error('mocha does not seem to be running, is this really a test?');
|
|
531
531
|
}
|
|
532
532
|
while (m) {
|
|
533
|
-
if (m.parent && m.parent.filename.
|
|
533
|
+
if (m.parent && m.parent.filename.includes(testFor)) {
|
|
534
534
|
return m;
|
|
535
535
|
} else if (!m.parent) {
|
|
536
536
|
// Mocha v10 doesn't inject mocha paths inside `module`, therefore, we only detect the parent until the last parent. But we can get Mocha running using `require.main` - Amin
|
|
@@ -69,6 +69,28 @@ module.exports = {
|
|
|
69
69
|
}
|
|
70
70
|
async function render() {
|
|
71
71
|
if (req.aposExternalFront) {
|
|
72
|
+
// Simulate an area and annotate it so that the
|
|
73
|
+
// widget's sub-areas wind up with the right metadata
|
|
74
|
+
const area = {
|
|
75
|
+
metaType: 'area',
|
|
76
|
+
items: [ widget ],
|
|
77
|
+
_docId
|
|
78
|
+
};
|
|
79
|
+
self.apos.template.annotateAreaForExternalFront(field, area);
|
|
80
|
+
// Annotate sub-areas. It's like annotating a doc, but not quite,
|
|
81
|
+
// so this logic is reproduced partially
|
|
82
|
+
self.apos.doc.walk(area, (o, k, v) => {
|
|
83
|
+
if (v && v.metaType === 'area') {
|
|
84
|
+
const manager = self.apos.util.getManagerOf(o);
|
|
85
|
+
if (!manager) {
|
|
86
|
+
self.apos.util.warnDevOnce('noManagerForDocInExternalFront', `No manager for: ${o.metaType} ${o.type || ''}`);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const field = manager.schema.find(f => f.name === k);
|
|
90
|
+
v._docId = _docId;
|
|
91
|
+
self.apos.template.annotateAreaForExternalFront(field, v);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
72
94
|
const result = {
|
|
73
95
|
...req.data,
|
|
74
96
|
options,
|
|
@@ -79,6 +79,15 @@ module.exports = {
|
|
|
79
79
|
return self.cacheCollection.removeMany({ namespace });
|
|
80
80
|
},
|
|
81
81
|
|
|
82
|
+
// Delete a single key and value from the cache.
|
|
83
|
+
|
|
84
|
+
async delete(namespace, username) {
|
|
85
|
+
await self.cacheCollection.deleteOne({
|
|
86
|
+
namespace,
|
|
87
|
+
key: username
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
|
|
82
91
|
// Establish the collection that will store all of the caches and
|
|
83
92
|
// set up its indexes.
|
|
84
93
|
|
|
@@ -225,8 +225,8 @@ module.exports = {
|
|
|
225
225
|
handlers(self) {
|
|
226
226
|
return {
|
|
227
227
|
beforeSave: {
|
|
228
|
-
prepareForStorage(req, doc) {
|
|
229
|
-
self.apos.schema.prepareForStorage(req, doc);
|
|
228
|
+
prepareForStorage(req, doc, options) {
|
|
229
|
+
self.apos.schema.prepareForStorage(req, doc, options);
|
|
230
230
|
},
|
|
231
231
|
async updateCacheField(req, doc) {
|
|
232
232
|
await self.updateCacheField(req, doc);
|
|
@@ -1028,9 +1028,16 @@ module.exports = {
|
|
|
1028
1028
|
});
|
|
1029
1029
|
const toId = draft._id.replace(`:${draft.aposLocale}`, `:${toLocale}:draft`);
|
|
1030
1030
|
const actionModule = self.apos.page.isPage(draft) ? self.apos.page : self;
|
|
1031
|
+
// Use findForEditing so that we are successful even for edge cases
|
|
1032
|
+
// like doc templates that don't appear in public renderings, but
|
|
1033
|
+
// also use permission('view') so that we are not actually restricted
|
|
1034
|
+
// to what we can edit, avoiding any confusion about whether there
|
|
1035
|
+
// is really an existing localized doc or not and preventing the
|
|
1036
|
+
// possibility of inserting an unwanted duplicate. The update() call will
|
|
1037
|
+
// still stop us if edit permissions are an issue
|
|
1031
1038
|
const existing = await actionModule.findForEditing(toReq, {
|
|
1032
1039
|
_id: toId
|
|
1033
|
-
}).toObject();
|
|
1040
|
+
}).permission('view').toObject();
|
|
1034
1041
|
// We only want to copy schema properties, leave non-schema
|
|
1035
1042
|
// properties of the source document alone
|
|
1036
1043
|
const data = Object.fromEntries(Object.entries(draft).filter(([ key, value ]) => self.schema.find(field => field.name === key)));
|
|
@@ -1056,8 +1063,15 @@ module.exports = {
|
|
|
1056
1063
|
// A page that is not the home page, being replicated for the first time
|
|
1057
1064
|
let { lastTargetId, lastPosition } = await self.apos.page.inferLastTargetIdAndPosition(draft);
|
|
1058
1065
|
let localizedTargetId = lastTargetId.replace(`:${draft.aposLocale}`, `:${toLocale}:draft`);
|
|
1066
|
+
// When fetching the target (parent or peer), always use findForEditing
|
|
1067
|
+
// so we don't miss doc templates and other edge cases, but also use
|
|
1068
|
+
// .permission('view') because we are not actually editing the target
|
|
1069
|
+
// and should not be blocked over edit permissions. Later change this check
|
|
1070
|
+
// to 'create' ("can create a child of this doc"), but not until we're ready
|
|
1071
|
+
// to do it for all creation attempts
|
|
1059
1072
|
const localizedTarget = await actionModule
|
|
1060
|
-
.
|
|
1073
|
+
.findForEditing(toReq, self.apos.page.getIdCriteria(localizedTargetId))
|
|
1074
|
+
.permission('view')
|
|
1061
1075
|
.archived(null)
|
|
1062
1076
|
.areas(false)
|
|
1063
1077
|
.relationships(false)
|
|
@@ -1070,7 +1084,13 @@ module.exports = {
|
|
|
1070
1084
|
parentNotLocalized: true
|
|
1071
1085
|
});
|
|
1072
1086
|
} else {
|
|
1073
|
-
const originalTarget = await actionModule
|
|
1087
|
+
const originalTarget = await actionModule
|
|
1088
|
+
.findForEditing(req, self.apos.page.getIdCriteria(lastTargetId))
|
|
1089
|
+
.permission('view')
|
|
1090
|
+
.archived(null)
|
|
1091
|
+
.areas(false)
|
|
1092
|
+
.relationships(false)
|
|
1093
|
+
.toObject();
|
|
1074
1094
|
if (!originalTarget) {
|
|
1075
1095
|
// Almost impossible (race conditions like someone removing it while we're in the modal)
|
|
1076
1096
|
throw self.apos.error('notfound');
|
|
@@ -1078,7 +1098,13 @@ module.exports = {
|
|
|
1078
1098
|
const criteria = {
|
|
1079
1099
|
path: self.apos.page.getParentPath(originalTarget)
|
|
1080
1100
|
};
|
|
1081
|
-
const localizedTarget = await actionModule
|
|
1101
|
+
const localizedTarget = await actionModule
|
|
1102
|
+
.findForEditing(toReq, criteria)
|
|
1103
|
+
.permission('view')
|
|
1104
|
+
.archived(null)
|
|
1105
|
+
.areas(false)
|
|
1106
|
+
.relationships(false)
|
|
1107
|
+
.toObject();
|
|
1082
1108
|
if (!localizedTarget) {
|
|
1083
1109
|
throw self.apos.error('notfound', req.t('apostrophe:parentNotLocalized'), {
|
|
1084
1110
|
// Also provide as data for code that prefers to localize client side
|
|
@@ -350,8 +350,8 @@ export default {
|
|
|
350
350
|
type: this.$t(this.moduleOptions.label)
|
|
351
351
|
};
|
|
352
352
|
if (this.docId) {
|
|
353
|
-
this.evaluateConditions();
|
|
354
353
|
await this.loadDoc();
|
|
354
|
+
this.evaluateConditions();
|
|
355
355
|
try {
|
|
356
356
|
if (this.manuallyPublished) {
|
|
357
357
|
this.published = await apos.http.get(this.getOnePath, {
|
|
@@ -890,10 +890,7 @@ module.exports = {
|
|
|
890
890
|
},
|
|
891
891
|
|
|
892
892
|
async clearLoginAttempts (username, namespace = loginAttemptsNamespace) {
|
|
893
|
-
await self.apos.cache.
|
|
894
|
-
namespace,
|
|
895
|
-
key: username
|
|
896
|
-
});
|
|
893
|
+
await self.apos.cache.delete(namespace, username);
|
|
897
894
|
},
|
|
898
895
|
|
|
899
896
|
addToAdminBar() {
|
|
@@ -432,8 +432,8 @@ module.exports = {
|
|
|
432
432
|
await self.apos.area.loadDeferredWidgets(req);
|
|
433
433
|
if (req.aposExternalFront) {
|
|
434
434
|
data = self.apos.template.getRenderDataArgs(req, data, self);
|
|
435
|
-
await self.apos.template.annotateDataForExternalFront(req, template, data);
|
|
436
|
-
self.apos.template.pruneDataForExternalFront(req, template, data);
|
|
435
|
+
await self.apos.template.annotateDataForExternalFront(req, template, data, self.__meta.name);
|
|
436
|
+
self.apos.template.pruneDataForExternalFront(req, template, data, self.__meta.name);
|
|
437
437
|
// Reply with JSON
|
|
438
438
|
return data;
|
|
439
439
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// This module provides a framework for triggering notifications
|
|
2
2
|
// within the Apostrophe admin UI. Notifications may be triggered
|
|
3
|
-
// either on the browser or the server side, via `apos.
|
|
3
|
+
// either on the browser or the server side, via `apos.notify`.
|
|
4
4
|
//
|
|
5
5
|
// ## Options
|
|
6
6
|
//
|
|
@@ -209,17 +209,17 @@ module.exports = {
|
|
|
209
209
|
action: self.action
|
|
210
210
|
};
|
|
211
211
|
},
|
|
212
|
-
//
|
|
213
|
-
//
|
|
214
|
-
//
|
|
215
|
-
// If you do not have a `req` it is acceptable to pass a user `_id` string
|
|
212
|
+
// When used server-side, call with `req` as the first argument,
|
|
213
|
+
// or if you do not have a `req` it is acceptable to pass a user `_id` string
|
|
216
214
|
// in place of `req`. Someone must be the recipient.
|
|
217
215
|
//
|
|
218
|
-
//
|
|
219
|
-
//
|
|
216
|
+
// When called client-side, there is no req argument because the recipient is always the current user.
|
|
217
|
+
//
|
|
218
|
+
// The `message` argument should be a key that exists in a localization file.
|
|
219
|
+
// If it does not, it will be displayed directly as a fallback.
|
|
220
220
|
//
|
|
221
|
-
// `options.type` styles the notification and may be set to `error`,
|
|
222
|
-
// `warning` or `success`. If not set, a "plain" default style is used.
|
|
221
|
+
// The `options.type` styles the notification and may be set to `error`, `danger`,
|
|
222
|
+
// `warning`, `info` or `success`. If not set, a "plain" default style is used.
|
|
223
223
|
//
|
|
224
224
|
// If `options.dismiss` is set to `true`, the message will auto-dismiss after 5 seconds.
|
|
225
225
|
// If it is set to a number of seconds, it will dismiss after that number of seconds.
|
package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposImageControlDialog.vue
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
3
|
v-if="active"
|
|
4
|
-
v-click-outside-element="
|
|
4
|
+
v-click-outside-element="close"
|
|
5
5
|
class="apos-popover apos-image-control__dialog"
|
|
6
6
|
x-placement="bottom"
|
|
7
7
|
:class="{
|
|
8
8
|
'apos-is-triggered': active,
|
|
9
|
-
'apos-has-selection':
|
|
9
|
+
'apos-has-selection': hasSelection
|
|
10
10
|
}"
|
|
11
11
|
>
|
|
12
12
|
<AposContextMenuDialog
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
<footer class="apos-image-control__footer">
|
|
27
27
|
<AposButton
|
|
28
28
|
type="default" label="apostrophe:cancel"
|
|
29
|
-
@click="
|
|
29
|
+
@click="close"
|
|
30
30
|
:modifiers="formModifiers"
|
|
31
31
|
/>
|
|
32
32
|
<AposButton
|
|
@@ -55,9 +55,14 @@ export default {
|
|
|
55
55
|
active: {
|
|
56
56
|
type: Boolean,
|
|
57
57
|
required: true
|
|
58
|
+
},
|
|
59
|
+
hasSelection: {
|
|
60
|
+
type: Boolean,
|
|
61
|
+
required: true,
|
|
62
|
+
default: false
|
|
58
63
|
}
|
|
59
64
|
},
|
|
60
|
-
emits: [ 'before-commands', '
|
|
65
|
+
emits: [ 'before-commands', 'close' ],
|
|
61
66
|
data() {
|
|
62
67
|
return {
|
|
63
68
|
generation: 1,
|
|
@@ -92,27 +97,52 @@ export default {
|
|
|
92
97
|
{
|
|
93
98
|
name: 'caption',
|
|
94
99
|
label: this.$t('apostrophe:caption'),
|
|
95
|
-
type: 'string'
|
|
100
|
+
type: 'string',
|
|
101
|
+
def: ''
|
|
96
102
|
}
|
|
97
103
|
]
|
|
98
104
|
};
|
|
99
105
|
},
|
|
100
106
|
computed: {
|
|
107
|
+
attributes() {
|
|
108
|
+
return this.editor.getAttributes('image');
|
|
109
|
+
},
|
|
101
110
|
lastSelectionTime() {
|
|
102
|
-
return this.editor.view.lastSelectionTime;
|
|
111
|
+
return this.editor.view.input.lastSelectionTime;
|
|
103
112
|
},
|
|
104
113
|
schema() {
|
|
105
114
|
return this.originalSchema;
|
|
106
115
|
}
|
|
107
116
|
},
|
|
108
117
|
watch: {
|
|
109
|
-
|
|
118
|
+
'attributes.imageId': {
|
|
119
|
+
handler(newVal, oldVal) {
|
|
120
|
+
if (newVal === oldVal) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
this.close();
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
active(newVal, oldVal) {
|
|
110
128
|
if (newVal) {
|
|
111
129
|
window.addEventListener('keydown', this.keyboardHandler);
|
|
112
|
-
this.populateFields();
|
|
113
130
|
} else {
|
|
114
131
|
window.removeEventListener('keydown', this.keyboardHandler);
|
|
115
132
|
}
|
|
133
|
+
|
|
134
|
+
if (newVal !== oldVal && this.hasSelection) {
|
|
135
|
+
this.populateFields();
|
|
136
|
+
this.evaluateConditions();
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
lastSelectionTime(newVal, oldVal) {
|
|
140
|
+
if (newVal === oldVal) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
this.populateFields();
|
|
145
|
+
this.evaluateConditions();
|
|
116
146
|
}
|
|
117
147
|
},
|
|
118
148
|
async mounted() {
|
|
@@ -120,11 +150,8 @@ export default {
|
|
|
120
150
|
this.evaluateConditions();
|
|
121
151
|
},
|
|
122
152
|
methods: {
|
|
123
|
-
|
|
124
|
-
this.$emit('
|
|
125
|
-
},
|
|
126
|
-
done() {
|
|
127
|
-
this.$emit('done');
|
|
153
|
+
close() {
|
|
154
|
+
this.$emit('close');
|
|
128
155
|
},
|
|
129
156
|
save() {
|
|
130
157
|
this.triggerValidation = true;
|
|
@@ -142,24 +169,24 @@ export default {
|
|
|
142
169
|
style: this.docFields.data.style,
|
|
143
170
|
alt: this.docFields.data.alt
|
|
144
171
|
});
|
|
145
|
-
this.
|
|
172
|
+
this.close();
|
|
146
173
|
});
|
|
147
174
|
},
|
|
148
175
|
keyboardHandler(e) {
|
|
149
176
|
if (e.keyCode === 27) {
|
|
150
|
-
this.
|
|
177
|
+
this.close();
|
|
151
178
|
}
|
|
152
179
|
if (e.keyCode === 13) {
|
|
153
180
|
if (this.docFields.data.href || e.metaKey) {
|
|
154
181
|
this.save();
|
|
155
|
-
this.
|
|
182
|
+
this.close();
|
|
156
183
|
}
|
|
157
184
|
e.preventDefault();
|
|
158
185
|
}
|
|
159
186
|
},
|
|
160
187
|
async populateFields() {
|
|
161
188
|
try {
|
|
162
|
-
const attrs = this.
|
|
189
|
+
const attrs = this.attributes;
|
|
163
190
|
this.docFields.data = {};
|
|
164
191
|
this.schema.forEach((item) => {
|
|
165
192
|
this.docFields.data[item.name] = attrs[item.name] || '';
|
|
@@ -107,7 +107,7 @@ export default {
|
|
|
107
107
|
return this.editor.isActive('anchor') || this.active;
|
|
108
108
|
},
|
|
109
109
|
lastSelectionTime() {
|
|
110
|
-
return this.editor.view.lastSelectionTime;
|
|
110
|
+
return this.editor.view.input.lastSelectionTime;
|
|
111
111
|
},
|
|
112
112
|
hasSelection() {
|
|
113
113
|
const { state } = this.editor;
|
|
@@ -129,7 +129,7 @@ export default {
|
|
|
129
129
|
window.removeEventListener('keydown', this.keyboardHandler);
|
|
130
130
|
}
|
|
131
131
|
},
|
|
132
|
-
'editor.view.lastSelectionTime': {
|
|
132
|
+
'editor.view.input.lastSelectionTime': {
|
|
133
133
|
handler(newVal, oldVal) {
|
|
134
134
|
this.populateFields();
|
|
135
135
|
}
|
|
@@ -7,19 +7,19 @@
|
|
|
7
7
|
:label="tool.label"
|
|
8
8
|
:icon-only="!!tool.icon"
|
|
9
9
|
:icon="tool.icon || false"
|
|
10
|
+
:icon-size="tool.iconSize || 16"
|
|
10
11
|
:modifiers="['no-border', 'no-motion']"
|
|
11
12
|
:tooltip="{
|
|
12
13
|
content: tool.label,
|
|
13
14
|
placement: 'top',
|
|
14
15
|
delay: 650
|
|
15
16
|
}"
|
|
16
|
-
@close="close"
|
|
17
17
|
/>
|
|
18
18
|
<AposImageControlDialog
|
|
19
19
|
:active="active"
|
|
20
20
|
:editor="editor"
|
|
21
|
+
:has-selection="hasSelection"
|
|
21
22
|
@close="close"
|
|
22
|
-
@click.stop="$event => null"
|
|
23
23
|
/>
|
|
24
24
|
</div>
|
|
25
25
|
</template>
|
|
@@ -42,18 +42,45 @@ export default {
|
|
|
42
42
|
active: false
|
|
43
43
|
};
|
|
44
44
|
},
|
|
45
|
+
watch: {
|
|
46
|
+
hasSelection(newVal, oldVal) {
|
|
47
|
+
if (!newVal) {
|
|
48
|
+
this.close();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
45
52
|
computed: {
|
|
53
|
+
attributes() {
|
|
54
|
+
return this.editor.getAttributes('image');
|
|
55
|
+
},
|
|
46
56
|
buttonActive() {
|
|
47
|
-
return this.
|
|
57
|
+
return this.attributes.imageId || this.active;
|
|
58
|
+
},
|
|
59
|
+
hasSelection() {
|
|
60
|
+
const { state } = this.editor;
|
|
61
|
+
const { selection } = this.editor.state;
|
|
62
|
+
|
|
63
|
+
// Text is selected
|
|
64
|
+
const { from, to } = selection;
|
|
65
|
+
const text = state.doc.textBetween(from, to, '');
|
|
66
|
+
|
|
67
|
+
// Image node is selected
|
|
68
|
+
const { content = [] } = selection.content().content;
|
|
69
|
+
const [ { type } = {} ] = content;
|
|
70
|
+
|
|
71
|
+
return text !== '' || type?.name === 'image';
|
|
48
72
|
}
|
|
49
73
|
},
|
|
50
74
|
methods: {
|
|
51
75
|
click() {
|
|
52
|
-
this.
|
|
76
|
+
if (this.hasSelection) {
|
|
77
|
+
this.active = !this.active;
|
|
78
|
+
}
|
|
53
79
|
},
|
|
54
80
|
close() {
|
|
55
|
-
this.active
|
|
56
|
-
|
|
81
|
+
if (this.active) {
|
|
82
|
+
this.active = false;
|
|
83
|
+
}
|
|
57
84
|
}
|
|
58
85
|
}
|
|
59
86
|
};
|
|
@@ -168,11 +168,14 @@ export default {
|
|
|
168
168
|
};
|
|
169
169
|
},
|
|
170
170
|
computed: {
|
|
171
|
+
attributes() {
|
|
172
|
+
return this.editor.getAttributes('link');
|
|
173
|
+
},
|
|
171
174
|
buttonActive() {
|
|
172
|
-
return this.
|
|
175
|
+
return this.attributes.href || this.active;
|
|
173
176
|
},
|
|
174
177
|
lastSelectionTime() {
|
|
175
|
-
return this.editor.view.lastSelectionTime;
|
|
178
|
+
return this.editor.view.input.lastSelectionTime;
|
|
176
179
|
},
|
|
177
180
|
hasSelection() {
|
|
178
181
|
const { state } = this.editor;
|
|
@@ -186,6 +189,15 @@ export default {
|
|
|
186
189
|
}
|
|
187
190
|
},
|
|
188
191
|
watch: {
|
|
192
|
+
'attributes.href': {
|
|
193
|
+
handler(newVal, oldVal) {
|
|
194
|
+
if (newVal === oldVal) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
this.close();
|
|
199
|
+
}
|
|
200
|
+
},
|
|
189
201
|
active(newVal) {
|
|
190
202
|
if (newVal) {
|
|
191
203
|
this.hasLinkOnOpen = !!(this.docFields.data.href);
|
|
@@ -194,11 +206,6 @@ export default {
|
|
|
194
206
|
window.removeEventListener('keydown', this.keyboardHandler);
|
|
195
207
|
}
|
|
196
208
|
},
|
|
197
|
-
'editor.view.lastSelectionTime': {
|
|
198
|
-
handler(newVal, oldVal) {
|
|
199
|
-
this.populateFields();
|
|
200
|
-
}
|
|
201
|
-
},
|
|
202
209
|
hasSelection(newVal, oldVal) {
|
|
203
210
|
if (!newVal) {
|
|
204
211
|
this.close();
|
|
@@ -265,7 +272,7 @@ export default {
|
|
|
265
272
|
},
|
|
266
273
|
async populateFields() {
|
|
267
274
|
try {
|
|
268
|
-
const attrs = this.
|
|
275
|
+
const attrs = this.attributes;
|
|
269
276
|
if (attrs.target) {
|
|
270
277
|
// checkboxes field expects an array
|
|
271
278
|
attrs.target = [ attrs.target ];
|
|
@@ -24,7 +24,7 @@ export default options => {
|
|
|
24
24
|
|
|
25
25
|
draggable: true,
|
|
26
26
|
|
|
27
|
-
isolating:
|
|
27
|
+
isolating: true,
|
|
28
28
|
|
|
29
29
|
addAttributes() {
|
|
30
30
|
return {
|
|
@@ -109,6 +109,7 @@ export default options => {
|
|
|
109
109
|
return {
|
|
110
110
|
setImage: (attrs) => ({ chain }) => {
|
|
111
111
|
return chain()
|
|
112
|
+
.focus()
|
|
112
113
|
.insertContent({
|
|
113
114
|
type: this.name,
|
|
114
115
|
attrs,
|
|
@@ -999,9 +999,10 @@ module.exports = {
|
|
|
999
999
|
//
|
|
1000
1000
|
// Currently `req` does not impact this, but that may change.
|
|
1001
1001
|
|
|
1002
|
-
prepareForStorage(req, doc) {
|
|
1002
|
+
prepareForStorage(req, doc, options = {}) {
|
|
1003
1003
|
const can = (field) => {
|
|
1004
|
-
return
|
|
1004
|
+
return options.permissions === false ||
|
|
1005
|
+
(!field.withType && !field.editPermission && !field.viewPermission) ||
|
|
1005
1006
|
(field.withType && self.apos.permission.can(req, 'view', field.withType)) ||
|
|
1006
1007
|
(field.editPermission && self.apos.permission.can(req, field.editPermission.action, field.editPermission.type)) ||
|
|
1007
1008
|
(field.viewPermission && self.apos.permission.can(req, field.viewPermission.action, field.viewPermission.type)) ||
|
|
@@ -544,6 +544,18 @@ module.exports = (self) => {
|
|
|
544
544
|
vueComponent: 'AposInputString',
|
|
545
545
|
async convert(req, field, data, destination) {
|
|
546
546
|
destination[field.name] = self.apos.launder.url(data[field.name], field.def, true);
|
|
547
|
+
|
|
548
|
+
if (field.required && (data[field.name] == null || !data[field.name].toString().length)) {
|
|
549
|
+
throw self.apos.error('required');
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
if (field.pattern) {
|
|
553
|
+
const regex = new RegExp(field.pattern);
|
|
554
|
+
|
|
555
|
+
if (!regex.test(destination[field.name])) {
|
|
556
|
+
throw self.apos.error('invalid');
|
|
557
|
+
}
|
|
558
|
+
}
|
|
547
559
|
},
|
|
548
560
|
diffable: function (value) {
|
|
549
561
|
// URLs are fine to diff and display
|
|
@@ -553,6 +565,18 @@ module.exports = (self) => {
|
|
|
553
565
|
// always return a valid string
|
|
554
566
|
return '';
|
|
555
567
|
},
|
|
568
|
+
validate(field, options, warn, fail) {
|
|
569
|
+
if (!field.pattern) {
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const isRegexInstance = field.pattern instanceof RegExp;
|
|
574
|
+
if (!isRegexInstance && typeof field.pattern !== 'string') {
|
|
575
|
+
fail('The pattern property must be a RegExp or a String');
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
field.pattern = isRegexInstance ? field.pattern.source : field.pattern;
|
|
579
|
+
},
|
|
556
580
|
addQueryBuilder(field, query) {
|
|
557
581
|
query.addBuilder(field.name, {
|
|
558
582
|
finalize: function () {
|
|
@@ -613,18 +637,16 @@ module.exports = (self) => {
|
|
|
613
637
|
finalize: function () {
|
|
614
638
|
if (self.queryBuilderInterested(query, field.name)) {
|
|
615
639
|
const value = query.get(field.name);
|
|
616
|
-
|
|
640
|
+
const criteria = {};
|
|
617
641
|
if (Array.isArray(value)) {
|
|
618
|
-
criteria = {};
|
|
619
642
|
criteria[field.name] = {
|
|
620
643
|
$gte: value[0],
|
|
621
644
|
$lte: value[1]
|
|
622
645
|
};
|
|
623
646
|
} else {
|
|
624
|
-
criteria = {};
|
|
625
647
|
criteria[field.name] = self.apos.launder.date(value);
|
|
626
|
-
query.and(criteria);
|
|
627
648
|
}
|
|
649
|
+
query.and(criteria);
|
|
628
650
|
}
|
|
629
651
|
},
|
|
630
652
|
launder: function (value) {
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<AposModal
|
|
3
|
-
class="apos-array-editor"
|
|
3
|
+
class="apos-array-editor"
|
|
4
|
+
:modal="modal"
|
|
4
5
|
:modal-title="modalTitle"
|
|
5
|
-
@inactive="modal.active = false"
|
|
6
|
-
@
|
|
6
|
+
@inactive="modal.active = false"
|
|
7
|
+
@show-modal="modal.showModal = true"
|
|
8
|
+
@esc="confirmAndCancel"
|
|
9
|
+
@no-modal="emitSafeClose"
|
|
7
10
|
>
|
|
8
11
|
<template #secondaryControls>
|
|
9
12
|
<AposButton
|
|
@@ -88,7 +91,8 @@ import AposArrayEditorLogic from '../logic/AposArrayEditor';
|
|
|
88
91
|
|
|
89
92
|
export default {
|
|
90
93
|
name: 'AposArrayEditor',
|
|
91
|
-
mixins: [ AposArrayEditorLogic ]
|
|
94
|
+
mixins: [ AposArrayEditorLogic ],
|
|
95
|
+
emits: [ 'safe-close' ]
|
|
92
96
|
};
|
|
93
97
|
</script>
|
|
94
98
|
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
<th
|
|
43
43
|
v-for="subfield in visibleSchema()"
|
|
44
44
|
:key="subfield._id"
|
|
45
|
+
:style="subfield.columnStyle || {}"
|
|
45
46
|
>
|
|
46
47
|
{{ $t(subfield.label) }}
|
|
47
48
|
</th>
|
|
@@ -76,6 +77,7 @@
|
|
|
76
77
|
<component
|
|
77
78
|
:is="field.style === 'table' ? 'td' : 'div'"
|
|
78
79
|
class="apos-input-array-inline-item-controls"
|
|
80
|
+
:style="(field.style === 'table' && field.columnStyle) || {}"
|
|
79
81
|
>
|
|
80
82
|
<AposIndicator
|
|
81
83
|
v-if="field.draggable"
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
:required="field.required"
|
|
16
16
|
:id="uid"
|
|
17
17
|
:tabindex="tabindex"
|
|
18
|
-
|
|
18
|
+
:autocomplete="field.autocomplete"
|
|
19
|
+
@keydown.enter="emitReturn"
|
|
19
20
|
>
|
|
20
21
|
</div>
|
|
21
22
|
</template>
|
|
@@ -26,6 +27,7 @@
|
|
|
26
27
|
import AposInputPasswordLogic from '../logic/AposInputPassword';
|
|
27
28
|
export default {
|
|
28
29
|
name: 'AposInputPassword',
|
|
29
|
-
mixins: [ AposInputPasswordLogic ]
|
|
30
|
+
mixins: [ AposInputPasswordLogic ],
|
|
31
|
+
emits: [ 'return' ]
|
|
30
32
|
};
|
|
31
33
|
</script>
|
|
@@ -18,10 +18,11 @@
|
|
|
18
18
|
:class="classes"
|
|
19
19
|
v-model="next" :type="type"
|
|
20
20
|
:placeholder="$t(field.placeholder)"
|
|
21
|
-
@keydown.enter="
|
|
21
|
+
@keydown.enter="emitReturn"
|
|
22
22
|
:disabled="field.readOnly" :required="field.required"
|
|
23
23
|
:id="uid" :tabindex="tabindex"
|
|
24
24
|
ref="input"
|
|
25
|
+
:autocomplete="field.autocomplete"
|
|
25
26
|
>
|
|
26
27
|
<component
|
|
27
28
|
v-if="icon"
|
|
@@ -38,7 +39,8 @@
|
|
|
38
39
|
import AposInputSlugLogic from '../logic/AposInputSlug';
|
|
39
40
|
export default {
|
|
40
41
|
name: 'AposInputSlug',
|
|
41
|
-
mixins: [ AposInputSlugLogic ]
|
|
42
|
+
mixins: [ AposInputSlugLogic ],
|
|
43
|
+
emits: [ 'return' ]
|
|
42
44
|
};
|
|
43
45
|
</script>
|
|
44
46
|
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
:disabled="field.readOnly"
|
|
15
15
|
:required="field.required"
|
|
16
16
|
:id="uid" :tabindex="tabindex"
|
|
17
|
+
:autocomplete="field.autocomplete"
|
|
17
18
|
/>
|
|
18
19
|
<input
|
|
19
20
|
v-else :class="classes"
|
|
@@ -24,6 +25,7 @@
|
|
|
24
25
|
:required="field.required"
|
|
25
26
|
:id="uid" :tabindex="tabindex"
|
|
26
27
|
:step="step"
|
|
28
|
+
:autocomplete="field.autocomplete"
|
|
27
29
|
>
|
|
28
30
|
<component
|
|
29
31
|
v-if="icon"
|
|
@@ -17,6 +17,12 @@
|
|
|
17
17
|
<span v-if="field.required" class="apos-field__required">
|
|
18
18
|
*
|
|
19
19
|
</span>
|
|
20
|
+
<AposLabel
|
|
21
|
+
class="apos-field__tag"
|
|
22
|
+
v-if="field.tag"
|
|
23
|
+
:label="field.tag.value || field.tag"
|
|
24
|
+
:modifiers="[ `apos-is-${field.tag.type || 'success'}`, 'apos-is-filled' ]"
|
|
25
|
+
/>
|
|
20
26
|
<span
|
|
21
27
|
v-if="(field.help || field.htmlHelp) && displayOptions.helpTooltip"
|
|
22
28
|
class="apos-field__help-tooltip"
|
|
@@ -119,6 +125,10 @@ export default {
|
|
|
119
125
|
color: var(--a-danger);
|
|
120
126
|
}
|
|
121
127
|
|
|
128
|
+
.apos-field__tag {
|
|
129
|
+
margin-left: 5px;
|
|
130
|
+
}
|
|
131
|
+
|
|
122
132
|
.apos-field__help-tooltip {
|
|
123
133
|
position: relative;
|
|
124
134
|
top: 2px;
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
v-for="field in schema" :key="field.name.concat(field._id ?? '')"
|
|
33
33
|
:data-apos-field="field.name"
|
|
34
34
|
:is="fieldStyle === 'table' ? 'td' : 'div'"
|
|
35
|
+
:style="(fieldStyle === 'table' && field.columnStyle) || {}"
|
|
35
36
|
v-show="displayComponent(field)"
|
|
36
37
|
>
|
|
37
38
|
<component
|
|
@@ -890,20 +890,24 @@ module.exports = {
|
|
|
890
890
|
self.insertions[key].push(componentName);
|
|
891
891
|
},
|
|
892
892
|
|
|
893
|
-
async annotateDataForExternalFront(req, template, data) {
|
|
894
|
-
const docs = self.getDocsForExternalFront(req, template, data);
|
|
893
|
+
async annotateDataForExternalFront(req, template, data, moduleName) {
|
|
894
|
+
const docs = self.getDocsForExternalFront(req, template, data, moduleName);
|
|
895
895
|
for (const doc of docs) {
|
|
896
896
|
self.annotateDocForExternalFront(doc);
|
|
897
897
|
}
|
|
898
898
|
data.aposBodyData = await self.getBodyData(req);
|
|
899
|
+
// Already contains module name too
|
|
900
|
+
data.template = template;
|
|
901
|
+
// For simple cases (not piece pages and the like)
|
|
902
|
+
data.module = moduleName;
|
|
899
903
|
return data;
|
|
900
904
|
},
|
|
901
905
|
|
|
902
|
-
pruneDataForExternalFront(req, data,
|
|
906
|
+
pruneDataForExternalFront(req, template, data, moduleName) {
|
|
903
907
|
return data;
|
|
904
908
|
},
|
|
905
909
|
|
|
906
|
-
getDocsForExternalFront(req, template, data) {
|
|
910
|
+
getDocsForExternalFront(req, template, data, moduleName) {
|
|
907
911
|
return [ data.home, ...(data.page?._ancestors || []), ...(data.page?._children || []), data.page, data.piece, ...(data.pieces || []) ].filter(doc => !!doc);
|
|
908
912
|
},
|
|
909
913
|
|
|
@@ -5,19 +5,21 @@
|
|
|
5
5
|
:tabindex="{'-1' : field.hideLabel}"
|
|
6
6
|
>
|
|
7
7
|
<input
|
|
8
|
+
v-model="checkProxy"
|
|
8
9
|
type="checkbox" class="apos-sr-only apos-input--choice apos-input--checkbox"
|
|
9
|
-
:value="choice.value"
|
|
10
|
+
:value="choice.value"
|
|
11
|
+
:name="field.name"
|
|
10
12
|
:id="id" :aria-label="choice.label || field.label"
|
|
11
13
|
:tabindex="tabindex" :disabled="field.readOnly || choice.readOnly"
|
|
12
|
-
|
|
13
|
-
@change="updateThis"
|
|
14
|
+
@change="update"
|
|
14
15
|
>
|
|
15
16
|
<span class="apos-input-indicator" aria-hidden="true">
|
|
16
17
|
<component
|
|
18
|
+
v-if="isChecked(checked)"
|
|
17
19
|
:is="`${
|
|
18
20
|
choice.indeterminate ? 'minus-icon' : 'check-bold-icon'
|
|
19
21
|
}`"
|
|
20
|
-
:size="10"
|
|
22
|
+
:size="10"
|
|
21
23
|
/>
|
|
22
24
|
</span>
|
|
23
25
|
<span
|
|
@@ -39,7 +41,7 @@ export default {
|
|
|
39
41
|
},
|
|
40
42
|
props: {
|
|
41
43
|
checked: {
|
|
42
|
-
type: [ Array, Boolean
|
|
44
|
+
type: [ Array, Boolean ],
|
|
43
45
|
default: false
|
|
44
46
|
},
|
|
45
47
|
choice: {
|
|
@@ -74,8 +76,7 @@ export default {
|
|
|
74
76
|
return this.checked;
|
|
75
77
|
},
|
|
76
78
|
set(val) {
|
|
77
|
-
|
|
78
|
-
if (!this.choice.indeterminate) {
|
|
79
|
+
if (!this.choice.indeterminate || this.choice.triggerIndeterminateEvent) {
|
|
79
80
|
// Only update the model if the box was *not* indeterminate.
|
|
80
81
|
this.$emit('change', val);
|
|
81
82
|
}
|
|
@@ -83,10 +84,15 @@ export default {
|
|
|
83
84
|
}
|
|
84
85
|
},
|
|
85
86
|
methods: {
|
|
87
|
+
isChecked(checked) {
|
|
88
|
+
return Array.isArray(checked)
|
|
89
|
+
? checked.includes(this.choice.value)
|
|
90
|
+
: checked;
|
|
91
|
+
},
|
|
86
92
|
// This event is only necessary if the parent needs to do *more* than simply
|
|
87
93
|
// keep track of an array of checkbox values. For example, AposTagApply
|
|
88
94
|
// does extra work with indeterminate values.
|
|
89
|
-
|
|
95
|
+
update($event) {
|
|
90
96
|
this.$emit('updated', $event);
|
|
91
97
|
}
|
|
92
98
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<span
|
|
3
|
-
class="apos-label"
|
|
3
|
+
class="apos-label"
|
|
4
|
+
:class="modifiers"
|
|
4
5
|
v-apos-tooltip="tooltip"
|
|
5
6
|
>
|
|
6
7
|
{{ $t(label) }}
|
|
@@ -49,6 +50,11 @@ export default {
|
|
|
49
50
|
border-color: var(--a-danger);
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
.apos-is-sensitive {
|
|
54
|
+
border-color: var(--a-sensitive-medium);
|
|
55
|
+
color: var(--a-sensitive);
|
|
56
|
+
}
|
|
57
|
+
|
|
52
58
|
.apos-is-success {
|
|
53
59
|
border-color: var(--a-success);
|
|
54
60
|
}
|
|
@@ -58,6 +64,11 @@ export default {
|
|
|
58
64
|
color: var(--a-base-1);
|
|
59
65
|
}
|
|
60
66
|
|
|
67
|
+
.apos-is-sensitive {
|
|
68
|
+
border-color: var(--a-sensitive-medium);
|
|
69
|
+
color: var(--a-sensitive);
|
|
70
|
+
}
|
|
71
|
+
|
|
61
72
|
.apos-is-warning.apos-is-filled {
|
|
62
73
|
background-color: var(--a-warning-fade);
|
|
63
74
|
}
|
|
@@ -66,6 +77,10 @@ export default {
|
|
|
66
77
|
background-color: var(--a-danger-fade);
|
|
67
78
|
}
|
|
68
79
|
|
|
80
|
+
.apos-is-sensitive.apos-is-filled {
|
|
81
|
+
background-color: var(--a-sensitive-light);
|
|
82
|
+
}
|
|
83
|
+
|
|
69
84
|
.apos-is-success.apos-is-filled {
|
|
70
85
|
background-color: var(--a-success-fade);
|
|
71
86
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
:class="classes"
|
|
6
6
|
:uid="uid"
|
|
7
7
|
:disabled="disabled"
|
|
8
|
+
:autocomplete="autocomplete"
|
|
8
9
|
@change="change($event.target.value)"
|
|
9
10
|
>
|
|
10
11
|
<option
|
|
@@ -61,6 +62,10 @@ export default {
|
|
|
61
62
|
disabled: {
|
|
62
63
|
type: Boolean,
|
|
63
64
|
default: false
|
|
65
|
+
},
|
|
66
|
+
autocomplete: {
|
|
67
|
+
type: String,
|
|
68
|
+
default: null
|
|
64
69
|
}
|
|
65
70
|
},
|
|
66
71
|
emits: [ 'change' ],
|