apostrophe 4.17.1-alpha.3 → 4.18.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/.eslintignore +1 -0
- package/.stylelintignore +4 -0
- package/CHANGELOG.md +21 -0
- package/lib/universal/check-if-conditions.mjs +209 -0
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +5 -2
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBreakpointPreviewMode.vue +77 -30
- package/modules/@apostrophecms/area/ui/apos/components/AposWidgetControls.vue +1 -1
- package/modules/@apostrophecms/doc-type/ui/apos/logic/AposDocContextMenu.js +1 -1
- package/modules/@apostrophecms/express/index.js +6 -0
- package/modules/@apostrophecms/i18n/i18n/de.json +9 -1
- package/modules/@apostrophecms/i18n/i18n/en.json +8 -0
- package/modules/@apostrophecms/i18n/i18n/es.json +8 -0
- package/modules/@apostrophecms/i18n/i18n/fr.json +8 -0
- package/modules/@apostrophecms/i18n/i18n/it.json +8 -0
- package/modules/@apostrophecms/i18n/i18n/pt-BR.json +8 -0
- package/modules/@apostrophecms/i18n/i18n/sk.json +8 -0
- package/modules/@apostrophecms/image-widget/index.js +130 -4
- package/modules/@apostrophecms/image-widget/views/fragment.html +89 -0
- package/modules/@apostrophecms/image-widget/views/widget.html +7 -34
- package/modules/@apostrophecms/modal/ui/apos/mixins/AposEditorMixin.js +14 -4
- package/modules/@apostrophecms/module/index.js +5 -0
- package/modules/@apostrophecms/rich-text-widget/index.js +80 -7
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposImageControlDialog.vue +164 -12
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +8 -5
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapDivider.vue +3 -1
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapLink.vue +42 -3
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Image.js +220 -17
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Link.js +5 -0
- package/modules/@apostrophecms/schema/index.js +104 -76
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputRelationship.vue +1 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposSearchList.vue +43 -67
- package/modules/@apostrophecms/schema/ui/apos/lib/conditionalFields.js +47 -57
- package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArray.js +34 -7
- package/modules/@apostrophecms/schema/ui/apos/logic/AposInputObject.js +19 -1
- package/modules/@apostrophecms/schema/ui/apos/logic/AposInputSlug.js +17 -2
- package/modules/@apostrophecms/schema/ui/apos/logic/AposInputString.js +20 -2
- package/modules/@apostrophecms/schema/ui/apos/mixins/AposInputConditionalFieldsMixin.js +0 -1
- package/modules/@apostrophecms/schema/ui/apos/mixins/AposInputFollowingMixin.js +6 -1
- package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +5 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenu.vue +25 -9
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_breakpoint_preview.scss +9 -2
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_inputs.scss +75 -3
- package/package.json +3 -3
- package/test/express.js +136 -1
- package/test/schema-conditions.js +1107 -0
- package/lib/check-if-conditions.js +0 -62
package/.eslintignore
CHANGED
package/.stylelintignore
ADDED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.18.0 (2025-06-11)
|
|
4
|
+
|
|
5
|
+
### Adds
|
|
6
|
+
|
|
7
|
+
* Adds MongoDB-style support (comparison operators) for conditional fields and all systems that use conditions. Conditional fields now have access to the `following` values from the parent schema fields.
|
|
8
|
+
* Add `followingIgnore` option to the `string` field schema. A boolean `true` results in all `following` values being ignored (not attempted to be used as a value for the field). When array of strings, the UI will ignore every item that matches a `following` field name.
|
|
9
|
+
* Adds link configuration to the `@apostrophecms/image-widget` UI and a new option `linkWithType` to control what document types can be linked to. Opt-out of the widget inline styles (reset) by setting `inlineStyles: false` in the widget configuration or contextual options (area).
|
|
10
|
+
* Use the link configuration of the Rich Text widget for image links too. It respects the existing `linkWithType` Rich Text option and uses the same schema (`linkFields`) used for text links. The fields from that schema can opt-in for specific tiptap extension now via a field property `extensions` (array) with possible array values `Link` and/or `Image`. You still need to specify the `htmlAttribute` property (the name of the attribute to be added to the link tag) in the schema when adding more fields. If the `extensions` property is not set, the field will be applied for both tiptap extensions.
|
|
11
|
+
* Adds body style support for breakpoint preview mode. Created new `[data-apos-refreshable-body]` div inside the container during breapoint preview.
|
|
12
|
+
Switch body attributes to this new div to keep supporting body styles in breakpoint preview mode.
|
|
13
|
+
|
|
14
|
+
### Changes
|
|
15
|
+
|
|
16
|
+
* Set the `Cache-Control` header to `no-store` for error pages in order to prevent the risk of serving stale error pages to users.
|
|
17
|
+
* Updates rich-text default configuration.
|
|
18
|
+
|
|
19
|
+
### Fixes
|
|
20
|
+
|
|
21
|
+
* The Download links in the media library now immediately download the file as expected, rather than navigating to the image in the current tab. `AposButton` now supports the `:download="true"` prop as expected.
|
|
22
|
+
* Using an API key with the editor, contributor or guest role now have a `req` object with the corresponding rights. The old behavior gave non-admin API keys less access than expected.
|
|
23
|
+
|
|
3
24
|
## 4.17.1 (2025-05-16)
|
|
4
25
|
|
|
5
26
|
### Fixes
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
|
|
2
|
+
// NOTE: This is a universal library for evaluating MongoDB-style conditions
|
|
3
|
+
// against a document or sub-document. Do not use any browser or node specific
|
|
4
|
+
// APIs here.
|
|
5
|
+
//
|
|
6
|
+
//
|
|
7
|
+
// Evaluate a field conditions against current schema values (doc or sub-doc).
|
|
8
|
+
// Expects a `conditions` object containing MongoDB-style conditions.
|
|
9
|
+
// Operators `$or` and `$and` are supported as top-level keys to allow
|
|
10
|
+
// for complex logical conditions. All comparison MongoDB operators are supported
|
|
11
|
+
// as object values.
|
|
12
|
+
// The condition object properties are field paths (dot notation is supported
|
|
13
|
+
// for objects), and the values are the conditions to evaluate against the
|
|
14
|
+
// document values.
|
|
15
|
+
// The `voterFn` function can be provided for evaluating
|
|
16
|
+
// conditions - return `false` to stop further evaluation.
|
|
17
|
+
// The function is called with three arguments:
|
|
18
|
+
// `voterFn(propName, condition, docValue)` where `propName` is the condition field path,
|
|
19
|
+
// `condition` is the condition value, and `docValue` is the field value extracted
|
|
20
|
+
// from the document.
|
|
21
|
+
// Example usage:
|
|
22
|
+
// ```js
|
|
23
|
+
// doc = {
|
|
24
|
+
// assignee: 'john',
|
|
25
|
+
// status: 'active',
|
|
26
|
+
// stats: { count: 15 },
|
|
27
|
+
// category: 'news'
|
|
28
|
+
// };
|
|
29
|
+
// const conditions = {
|
|
30
|
+
// assignee: { '$exists': true },
|
|
31
|
+
// status: { '$in': ['active', 'pending'] },
|
|
32
|
+
// 'stats.count': { '$gte': 10 },
|
|
33
|
+
// $or: [
|
|
34
|
+
// { 'category': 'news' },
|
|
35
|
+
// { 'category': 'blog' }
|
|
36
|
+
// ]
|
|
37
|
+
// };
|
|
38
|
+
// function voterFn(propName, condition, docValue) {
|
|
39
|
+
// if (propName === 'status' && docValue === 'inactive') {
|
|
40
|
+
// return false; // Reject the condition for 'status' if it's 'inactive'
|
|
41
|
+
// }
|
|
42
|
+
// // Non-boolean return values are ignored
|
|
43
|
+
// }
|
|
44
|
+
// const result = checkIfConditions(doc, conditions, voterFn);
|
|
45
|
+
// ```
|
|
46
|
+
export default function checkIfConditions(doc, conditions, voterFn = () => null) {
|
|
47
|
+
return Object.entries(conditions).every(([ key, condition ]) => {
|
|
48
|
+
if (key === '$or') {
|
|
49
|
+
return checkOrConditions(doc, condition, voterFn);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (key === '$and') {
|
|
53
|
+
return checkAndConditions(doc, condition, voterFn);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const docValue = getNestedPropValue(doc, key);
|
|
57
|
+
|
|
58
|
+
// If the custom voter rejects the condition, we stop evaluating
|
|
59
|
+
// and return false immediately.
|
|
60
|
+
if (voterFn(key, condition, docValue) === false) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// External conditions should be handled outside of this function,
|
|
65
|
+
// so we skip them here. Use the `voterFn` to gather external condition
|
|
66
|
+
// entries and evaluate them separately.
|
|
67
|
+
if (isExternalCondition(key)) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Otherwise, we evaluate the condition against the document value.
|
|
72
|
+
return evaluate(docValue, condition);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function isExternalCondition(conditionKey) {
|
|
77
|
+
return conditionKey.endsWith(')');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function checkOrConditions(doc, conditions, voterFn) {
|
|
81
|
+
return conditions.some((condition) => {
|
|
82
|
+
return checkIfConditions(doc, condition, voterFn);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function checkAndConditions(doc, conditions, voterFn) {
|
|
87
|
+
return conditions.every((condition) => {
|
|
88
|
+
return checkIfConditions(doc, condition, voterFn);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function getNestedPropValue(doc, key) {
|
|
93
|
+
if (!key.includes('.')) {
|
|
94
|
+
return doc?.[key];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const keys = key.split('.');
|
|
98
|
+
let currentValue = doc;
|
|
99
|
+
while (keys.length > 0) {
|
|
100
|
+
if (
|
|
101
|
+
// The `==` comparison is intentionally used here to match
|
|
102
|
+
// both `null` and `undefined`
|
|
103
|
+
currentValue == null ||
|
|
104
|
+
// Support i.e. `stringField.length`
|
|
105
|
+
// eslint-disable-next-line valid-typeof
|
|
106
|
+
typeof currentValue?.[keys[0]] == null
|
|
107
|
+
) {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
if (Array.isArray(currentValue)) {
|
|
111
|
+
// Support i.e. `arrayField.length` or `arrayField.0`
|
|
112
|
+
if (!Object.hasOwn(currentValue, keys[0])) {
|
|
113
|
+
return currentValue.flatMap(item => {
|
|
114
|
+
return getNestedPropValue(item, keys.join('.'));
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const prop = keys.shift();
|
|
119
|
+
currentValue = currentValue[prop];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return currentValue;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Comparison operators registry for MongoDB-style conditions.
|
|
126
|
+
// https://www.mongodb.com/docs/manual/reference/operator/query-comparison/
|
|
127
|
+
const opRegistry = {};
|
|
128
|
+
opRegistry.$eq = (docValue, conditionValue) => {
|
|
129
|
+
if (Array.isArray(docValue)) {
|
|
130
|
+
if (Array.isArray(conditionValue)) {
|
|
131
|
+
// Unlike MongoDB, we don't match the index order of the arrays.
|
|
132
|
+
return docValue.length === conditionValue.length &&
|
|
133
|
+
conditionValue.every(value => docValue.includes(value));
|
|
134
|
+
}
|
|
135
|
+
return docValue.includes(conditionValue);
|
|
136
|
+
}
|
|
137
|
+
return docValue === conditionValue;
|
|
138
|
+
};
|
|
139
|
+
opRegistry.$ne = (docValue, conditionValue) => (
|
|
140
|
+
opRegistry.$eq(docValue, conditionValue) === false
|
|
141
|
+
);
|
|
142
|
+
opRegistry.$exists = (docValue, conditionValue) => {
|
|
143
|
+
// Per MongoDB documentation, $exists should treat null and undefined the same.
|
|
144
|
+
// == null and != null are documented to match or reject null, undefined
|
|
145
|
+
// and nothing else.
|
|
146
|
+
return (conditionValue ? docValue != null : docValue == null);
|
|
147
|
+
};
|
|
148
|
+
opRegistry.$in = (docValue, conditionValue) => {
|
|
149
|
+
if (!Array.isArray(conditionValue)) {
|
|
150
|
+
throw new Error('$in and $nin operators require an array as condition value');
|
|
151
|
+
}
|
|
152
|
+
if (Array.isArray(docValue)) {
|
|
153
|
+
return conditionValue.some(value => docValue.includes(value));
|
|
154
|
+
}
|
|
155
|
+
return conditionValue.includes(docValue);
|
|
156
|
+
};
|
|
157
|
+
opRegistry.$nin = (docValue, conditionValue) => (
|
|
158
|
+
opRegistry.$in(docValue, conditionValue) === false
|
|
159
|
+
);
|
|
160
|
+
opRegistry.$gt = (docValue, conditionValue) => (
|
|
161
|
+
docValue > conditionValue
|
|
162
|
+
);
|
|
163
|
+
opRegistry.$lt = (docValue, conditionValue) => (
|
|
164
|
+
docValue < conditionValue
|
|
165
|
+
);
|
|
166
|
+
opRegistry.$gte = (docValue, conditionValue) => (
|
|
167
|
+
docValue >= conditionValue
|
|
168
|
+
);
|
|
169
|
+
opRegistry.$lte = (docValue, conditionValue) => (
|
|
170
|
+
docValue <= conditionValue
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
export function evaluate(docValue, conditionValue) {
|
|
174
|
+
if (
|
|
175
|
+
typeof conditionValue === 'object' &&
|
|
176
|
+
conditionValue !== null
|
|
177
|
+
) {
|
|
178
|
+
// Empty objects are not valid conditions
|
|
179
|
+
if (Object.keys(conditionValue).length === 0) {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
// Evaluate every operator in the conditionValue object.
|
|
183
|
+
// A MongoDB-style condition object is expected.
|
|
184
|
+
// We check for the presence of known comparison operators and
|
|
185
|
+
// evaluate them accordingly.
|
|
186
|
+
return Object.entries(conditionValue).every(([ operator, operand ]) => {
|
|
187
|
+
switch (operator) {
|
|
188
|
+
// BC, support the min and max properties because they were already supported
|
|
189
|
+
// in a different routine.
|
|
190
|
+
case 'min':
|
|
191
|
+
return docValue >= operand;
|
|
192
|
+
case 'max':
|
|
193
|
+
return docValue <= operand;
|
|
194
|
+
default: {
|
|
195
|
+
if (opRegistry[operator]) {
|
|
196
|
+
return opRegistry[operator](docValue, operand);
|
|
197
|
+
}
|
|
198
|
+
throw new Error(`Unsupported operator: ${operator}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (Array.isArray(docValue)) {
|
|
205
|
+
return docValue.includes(conditionValue);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return conditionValue === docValue;
|
|
209
|
+
}
|
|
@@ -551,7 +551,9 @@ export default {
|
|
|
551
551
|
}
|
|
552
552
|
},
|
|
553
553
|
async refresh(options = {}) {
|
|
554
|
-
|
|
554
|
+
// In breakpoint preview mode, uses the fake body.
|
|
555
|
+
const refreshable = document.querySelector('[data-apos-refreshable-body]') ||
|
|
556
|
+
document.querySelector('[data-apos-refreshable]');
|
|
555
557
|
if (options.scrollcheck) {
|
|
556
558
|
window.apos.adminBar.scrollPosition = {
|
|
557
559
|
x: window.scrollX,
|
|
@@ -613,7 +615,8 @@ export default {
|
|
|
613
615
|
// "@ notation" PATCH feature. Sort the areas by DOM depth
|
|
614
616
|
// to ensure parents patch before children
|
|
615
617
|
this.original = {};
|
|
616
|
-
const els = Array.from(document.querySelectorAll('[data-apos-area-newly-editable]'))
|
|
618
|
+
const els = Array.from(document.querySelectorAll('[data-apos-area-newly-editable]'))
|
|
619
|
+
.filter(el => el.getAttribute('data-doc-id') === this.context._id);
|
|
617
620
|
els.sort((a, b) => {
|
|
618
621
|
const da = depth(a);
|
|
619
622
|
const db = depth(b);
|
package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBreakpointPreviewMode.vue
CHANGED
|
@@ -56,7 +56,6 @@
|
|
|
56
56
|
</div>
|
|
57
57
|
</template>
|
|
58
58
|
<script>
|
|
59
|
-
|
|
60
59
|
export default {
|
|
61
60
|
name: 'TheAposContextBreakpointPreviewMode',
|
|
62
61
|
props: {
|
|
@@ -88,7 +87,10 @@ export default {
|
|
|
88
87
|
originalBodyBackground: null,
|
|
89
88
|
shortcuts: this.getShortcuts(),
|
|
90
89
|
breakpoints: this.getBreakpointItems(),
|
|
91
|
-
showDropdown: false
|
|
90
|
+
showDropdown: false,
|
|
91
|
+
bodyEl: null,
|
|
92
|
+
refreshableBodyEl: null,
|
|
93
|
+
observer: new MutationObserver(this.observerCallback)
|
|
92
94
|
};
|
|
93
95
|
},
|
|
94
96
|
computed: {
|
|
@@ -130,6 +132,7 @@ export default {
|
|
|
130
132
|
}
|
|
131
133
|
},
|
|
132
134
|
mounted() {
|
|
135
|
+
this.bodyEl = document.querySelector('body');
|
|
133
136
|
this.setShowDropdown();
|
|
134
137
|
apos.bus.$on(
|
|
135
138
|
'command-menu-admin-bar-toggle-breakpoint-preview-mode',
|
|
@@ -151,18 +154,56 @@ export default {
|
|
|
151
154
|
);
|
|
152
155
|
},
|
|
153
156
|
methods: {
|
|
157
|
+
observerCallback(mutationList, observer) {
|
|
158
|
+
for (const mutation of mutationList) {
|
|
159
|
+
if (
|
|
160
|
+
mutation.type !== 'attributes' ||
|
|
161
|
+
mutation.attributeName.startsWith('data-apos') ||
|
|
162
|
+
mutation.attributeName === 'data-breakpoint-preview-mode'
|
|
163
|
+
) {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
const bodyAttribute = mutation.target
|
|
167
|
+
.getAttribute(mutation.attributeName);
|
|
168
|
+
this.refreshableBodyEl.setAttribute(mutation.attributeName, bodyAttribute);
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
createFakeBody(refreshableEl) {
|
|
173
|
+
this.refreshableBodyEl = document.createElement('div');
|
|
174
|
+
this.refreshableBodyEl.setAttribute('data-apos-refreshable-body', '');
|
|
175
|
+
Array.from(refreshableEl.childNodes).forEach(child => {
|
|
176
|
+
this.refreshableBodyEl.append(child);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
Array.from(this.bodyEl.attributes || {})
|
|
180
|
+
.filter(({ name }) => !name.startsWith('data-apos'))
|
|
181
|
+
.forEach(({ name, value }) => {
|
|
182
|
+
this.refreshableBodyEl.setAttribute(name, value);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
refreshableEl.append(this.refreshableBodyEl);
|
|
186
|
+
},
|
|
187
|
+
|
|
154
188
|
switchBreakpointPreviewMode({
|
|
155
189
|
mode,
|
|
156
190
|
label,
|
|
157
191
|
width,
|
|
158
192
|
height
|
|
159
193
|
}) {
|
|
160
|
-
document.querySelector('
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
194
|
+
const refreshableEl = document.querySelector('[data-apos-refreshable]');
|
|
195
|
+
|
|
196
|
+
// Only when switching to mobile preview from the normal state
|
|
197
|
+
if (!this.mode) {
|
|
198
|
+
this.createFakeBody(refreshableEl);
|
|
199
|
+
this.observer.observe(this.bodyEl, { attributes: true });
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
this.bodyEl.setAttribute('data-breakpoint-preview-mode', mode);
|
|
203
|
+
refreshableEl.setAttribute('data-resizable', this.resizable);
|
|
204
|
+
refreshableEl.setAttribute('data-label', this.$t(label));
|
|
205
|
+
refreshableEl.style.width = width;
|
|
206
|
+
refreshableEl.style.height = height;
|
|
166
207
|
|
|
167
208
|
this.mode = mode;
|
|
168
209
|
this.$emit('switch-breakpoint-preview-mode', {
|
|
@@ -178,33 +219,39 @@ export default {
|
|
|
178
219
|
height
|
|
179
220
|
});
|
|
180
221
|
},
|
|
181
|
-
toggleBreakpointPreviewMode({
|
|
182
|
-
mode
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
document.querySelector('[data-apos-refreshable]').style.removeProperty('width');
|
|
192
|
-
document.querySelector('[data-apos-refreshable]').style.removeProperty('height');
|
|
193
|
-
document.querySelector('[data-apos-refreshable]').style.removeProperty('background');
|
|
194
|
-
|
|
195
|
-
this.mode = null;
|
|
196
|
-
this.$emit('reset-breakpoint-preview-mode');
|
|
197
|
-
this.saveState({ mode: this.mode });
|
|
222
|
+
toggleBreakpointPreviewMode(state) {
|
|
223
|
+
if (this.mode === state.mode || state.mode === null) {
|
|
224
|
+
this.resetBreakpointPreview();
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
this.switchBreakpointPreviewMode(state);
|
|
229
|
+
},
|
|
230
|
+
resetBreakpointPreview() {
|
|
231
|
+
const refreshableEl = document.querySelector('[data-apos-refreshable]');
|
|
198
232
|
|
|
233
|
+
this.observer.disconnect();
|
|
234
|
+
if (!this.refreshableBodyEl) {
|
|
199
235
|
return;
|
|
200
236
|
}
|
|
201
237
|
|
|
202
|
-
this.
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
height
|
|
238
|
+
Array.from(this.refreshableBodyEl.childNodes).forEach(child => {
|
|
239
|
+
if (child.nodeType !== Node.TEXT_NODE || child.nodeValue.trim()) {
|
|
240
|
+
refreshableEl.append(child);
|
|
241
|
+
}
|
|
207
242
|
});
|
|
243
|
+
this.refreshableBodyEl.remove();
|
|
244
|
+
this.refreshableBodyEl = null;
|
|
245
|
+
|
|
246
|
+
this.bodyEl.removeAttribute('data-breakpoint-preview-mode');
|
|
247
|
+
refreshableEl.removeAttribute('data-resizable');
|
|
248
|
+
refreshableEl.removeAttribute('data-label');
|
|
249
|
+
refreshableEl.style.removeProperty('width');
|
|
250
|
+
refreshableEl.style.removeProperty('height');
|
|
251
|
+
|
|
252
|
+
this.mode = null;
|
|
253
|
+
this.$emit('reset-breakpoint-preview-mode');
|
|
254
|
+
this.saveState({ mode: this.mode });
|
|
208
255
|
},
|
|
209
256
|
loadState() {
|
|
210
257
|
return JSON.parse(sessionStorage.getItem('aposBreakpointPreviewMode') || '{}');
|
|
@@ -2,7 +2,7 @@ import { detectDocChange } from 'Modules/@apostrophecms/schema/lib/detectChange'
|
|
|
2
2
|
import AposPublishMixin from 'Modules/@apostrophecms/ui/mixins/AposPublishMixin';
|
|
3
3
|
import AposArchiveMixin from 'Modules/@apostrophecms/ui/mixins/AposArchiveMixin';
|
|
4
4
|
import AposModifiedMixin from 'Modules/@apostrophecms/ui/mixins/AposModifiedMixin';
|
|
5
|
-
import checkIfConditions from 'apostrophe/lib/check-if-conditions';
|
|
5
|
+
import checkIfConditions from 'apostrophe/lib/universal/check-if-conditions.mjs';
|
|
6
6
|
|
|
7
7
|
export default {
|
|
8
8
|
name: 'AposDocContextMenu',
|
|
@@ -315,6 +315,12 @@ module.exports = {
|
|
|
315
315
|
const info = self.options.apiKeys[key];
|
|
316
316
|
if (info.role === 'admin') {
|
|
317
317
|
taskReq = self.apos.task.getReq();
|
|
318
|
+
} else if (info.role === 'editor') {
|
|
319
|
+
taskReq = self.apos.task.getEditorReq();
|
|
320
|
+
} else if (info.role === 'contributor') {
|
|
321
|
+
taskReq = self.apos.task.getContributorReq();
|
|
322
|
+
} else if (info.role === 'guest') {
|
|
323
|
+
taskReq = self.apos.task.getGuestReq();
|
|
318
324
|
} else {
|
|
319
325
|
taskReq = self.apos.task.getAnonReq();
|
|
320
326
|
}
|
|
@@ -251,7 +251,11 @@
|
|
|
251
251
|
"lastUpdatedBy": "Zuletzt gespeichert um {{ updatedAt }} <br /> von {{ updatedBy }}",
|
|
252
252
|
"leavePageDescription": "Der Inhalt, den du bearbeiten möchtest, gehört zu einem anderen Dokument und muss darin geändert werden.\nÄnderungen an {{ oldTitle }} werden automatisch gespeichert.",
|
|
253
253
|
"leavePageHeading": "{{ oldTitle }} verlassen um {{ newTitle }} zu bearbeiten?",
|
|
254
|
+
"linkHrefHelp": "Geben Sie die vollständige URL ein, beginnend mit https:// oder http://",
|
|
254
255
|
"linkTarget": "Link-Ziel",
|
|
256
|
+
"linkTitle": "Linktitel",
|
|
257
|
+
"linkTitleRelHelp": "Wenn leer, wird der Titel des zugehörigen Dokuments verwendet.",
|
|
258
|
+
"linkTitleUrlHelp": "Wenn leer, wird die Bildunterschrift verwendet.",
|
|
255
259
|
"linkTo": "Verlinken zu...",
|
|
256
260
|
"listJoiner": ", ",
|
|
257
261
|
"live": "Live",
|
|
@@ -455,11 +459,15 @@
|
|
|
455
459
|
"richTextBulletedList": "Liste",
|
|
456
460
|
"richTextCodeBlock": "Code-Block",
|
|
457
461
|
"richTextColor": "Farbe",
|
|
462
|
+
"richTextH1": "Überschrift 1",
|
|
458
463
|
"richTextH2": "Überschrift 2",
|
|
459
464
|
"richTextH3": "Überschrift 3",
|
|
460
465
|
"richTextH4": "Überschrift 4",
|
|
466
|
+
"richTextH5": "Überschrift 5",
|
|
467
|
+
"richTextH6": "Überschrift 6",
|
|
461
468
|
"richTextHighlight": "Hervorheben",
|
|
462
469
|
"richTextHorizontalRule": "Horizontale Linie",
|
|
470
|
+
"richTextHorizontalRuleDescription": "Fügen Sie einen horizontalen Trenner hinzu",
|
|
463
471
|
"richTextInsertMenuHeading": "Element einfügen...",
|
|
464
472
|
"richTextItalic": "Kursiv",
|
|
465
473
|
"richTextLink": "Link",
|
|
@@ -469,7 +477,7 @@
|
|
|
469
477
|
"richTextNodeMultipleStyles": "Mehrere blocks...",
|
|
470
478
|
"richTextNodeStyles": "Block Stile",
|
|
471
479
|
"richTextOrderedList": "Nummerierte Liste",
|
|
472
|
-
"richTextParagraph": "
|
|
480
|
+
"richTextParagraph": "Absatz",
|
|
473
481
|
"richTextPlaceholder": "Beginne dein Text hier...",
|
|
474
482
|
"richTextPlaceholderWithInsertMenu": "Beginnen Sie zu tippen oder drücken Sie '/' für Befehle...",
|
|
475
483
|
"richTextRedo": "Wiederholen",
|
|
@@ -251,7 +251,11 @@
|
|
|
251
251
|
"lastUpdatedBy": "Last saved on {{ updatedAt }} <br /> by {{ updatedBy }}",
|
|
252
252
|
"leavePageDescription": "The content you're trying to edit belongs to another document and must be edited there.\nChanges made to {{ oldTitle }} are saved automatically.",
|
|
253
253
|
"leavePageHeading": "Leave {{ oldTitle }} to edit {{ newTitle }}?",
|
|
254
|
+
"linkHrefHelp": "Write the full URL, beginning with https:// or http://",
|
|
254
255
|
"linkTarget": "Link Target",
|
|
256
|
+
"linkTitle": "Link Title",
|
|
257
|
+
"linkTitleRelHelp": "If empty, the title of the related document will be used.",
|
|
258
|
+
"linkTitleUrlHelp": "If empty, the caption of the image will be used.",
|
|
255
259
|
"linkTo": "Link To...",
|
|
256
260
|
"listJoiner": ", ",
|
|
257
261
|
"live": "Live",
|
|
@@ -455,11 +459,15 @@
|
|
|
455
459
|
"richTextBulletedList": "Bulleted List",
|
|
456
460
|
"richTextCodeBlock": "Code Block",
|
|
457
461
|
"richTextColor": "Color",
|
|
462
|
+
"richTextH1": "Heading 1 (H1)",
|
|
458
463
|
"richTextH2": "Heading 2 (H2)",
|
|
459
464
|
"richTextH3": "Heading 3 (H3)",
|
|
460
465
|
"richTextH4": "Heading 4 (H4)",
|
|
466
|
+
"richTextH5": "Heading 5 (H5)",
|
|
467
|
+
"richTextH6": "Heading 6 (H6)",
|
|
461
468
|
"richTextHighlight": "Mark",
|
|
462
469
|
"richTextHorizontalRule": "Horizontal Rule",
|
|
470
|
+
"richTextHorizontalRuleDescription": "Add a horizontal separator",
|
|
463
471
|
"richTextInsertMenuHeading": "Insert element...",
|
|
464
472
|
"richTextItalic": "Italic",
|
|
465
473
|
"richTextLink": "Link",
|
|
@@ -251,7 +251,11 @@
|
|
|
251
251
|
"lastUpdatedBy": "Guardado por última vez el {{ updatedAt }} <br /> por {{ updatedBy }}",
|
|
252
252
|
"leavePageDescription": "El contenido que esta buscando editar pertenece a otro documento y debe ser editado ahí.\nCambios hechos a {{ oldTitle }} son guardados automaticamente.",
|
|
253
253
|
"leavePageHeading": "¿Salir de {{ oldTitle }} para editar {{ newTitle }}?",
|
|
254
|
+
"linkHrefHelp": "Escribe la URL completa, comenzando con https:// o http://",
|
|
254
255
|
"linkTarget": "Objetivo del enlace",
|
|
256
|
+
"linkTitle": "Título del enlace",
|
|
257
|
+
"linkTitleRelHelp": "Si está vacío, se utilizará el título del documento relacionado.",
|
|
258
|
+
"linkTitleUrlHelp": "Si está vacío, se utilizará el título de la imagen.",
|
|
255
259
|
"linkTo": "Enlace a...",
|
|
256
260
|
"listJoiner": ", ",
|
|
257
261
|
"live": "En Vivo",
|
|
@@ -455,11 +459,15 @@
|
|
|
455
459
|
"richTextBulletedList": "Lista con Puntos",
|
|
456
460
|
"richTextCodeBlock": "Bloque de Código",
|
|
457
461
|
"richTextColor": "Color",
|
|
462
|
+
"richTextH1": "Título 1 (H1)",
|
|
458
463
|
"richTextH2": "Título 2 (H2)",
|
|
459
464
|
"richTextH3": "Título 3 (H3)",
|
|
460
465
|
"richTextH4": "Título 4 (H4)",
|
|
466
|
+
"richTextH5": "Título 5 (H5)",
|
|
467
|
+
"richTextH6": "Título 6 (H6)",
|
|
461
468
|
"richTextHighlight": "Marcador",
|
|
462
469
|
"richTextHorizontalRule": "Regla Horizontal",
|
|
470
|
+
"richTextHorizontalRuleDescription": "Agregar un separador horizontal",
|
|
463
471
|
"richTextInsertMenuHeading": "Insertar elemento...",
|
|
464
472
|
"richTextItalic": "Cursiva",
|
|
465
473
|
"richTextLink": "Liga",
|
|
@@ -251,7 +251,11 @@
|
|
|
251
251
|
"lastUpdatedBy": "Enregistré dernièrement le {{ updatedAt }} <br /> par {{ updatedBy }}",
|
|
252
252
|
"leavePageDescription": "Le contenu que vous essayez d'éditer appartient à un autre document et doit être édité là-bas.\nLes modifications effectuées à {{ oldTitle }} sont enregistrées automatiquement.",
|
|
253
253
|
"leavePageHeading": "Quitter {{ oldTitle }} pour éditer {{ newTitle }} ?",
|
|
254
|
+
"linkHrefHelp": "Écrivez l'URL complète, commençant par https:// ou http://",
|
|
254
255
|
"linkTarget": "Cible du lien",
|
|
256
|
+
"linkTitle": "Titre du lien",
|
|
257
|
+
"linkTitleRelHelp": "Si vide, le titre du document associé sera utilisé.",
|
|
258
|
+
"linkTitleUrlHelp": "Si vide, la légende de l'image sera utilisée.",
|
|
255
259
|
"linkTo": "Lien vers...",
|
|
256
260
|
"listJoiner": ", ",
|
|
257
261
|
"live": "En Live",
|
|
@@ -455,11 +459,15 @@
|
|
|
455
459
|
"richTextBulletedList": "Liste à Puces",
|
|
456
460
|
"richTextCodeBlock": "Bloc de Code",
|
|
457
461
|
"richTextColor": "Couleur",
|
|
462
|
+
"richTextH1": "Titre de niveau 1 (H1)",
|
|
458
463
|
"richTextH2": "Titre de niveau 2 (H2)",
|
|
459
464
|
"richTextH3": "Titre de niveau 3 (H3)",
|
|
460
465
|
"richTextH4": "Titre de niveau 4 (H4)",
|
|
466
|
+
"richTextH5": "Titre de niveau 5 (H5)",
|
|
467
|
+
"richTextH6": "Titre de niveau 6 (H6)",
|
|
461
468
|
"richTextHighlight": "Surligner",
|
|
462
469
|
"richTextHorizontalRule": "Règle Horizontale",
|
|
470
|
+
"richTextHorizontalRuleDescription": "Ajouter un séparateur horizontal",
|
|
463
471
|
"richTextInsertMenuHeading": "Insérer un élément...",
|
|
464
472
|
"richTextItalic": "Italique",
|
|
465
473
|
"richTextLink": "Lien",
|
|
@@ -251,7 +251,11 @@
|
|
|
251
251
|
"lastUpdatedBy": "Ultimo salvataggio il {{ updatedAt }} <br /> da {{ updatedBy }}",
|
|
252
252
|
"leavePageDescription": "Il contenuto che stai cercando di modificare appartiene a un altro documento e deve essere modificato lì.\nLe modifiche apportate a {{ oldTitle }} sono salvate automaticamente.",
|
|
253
253
|
"leavePageHeading": "Lascia {{ oldTitle }} per modificare {{ newTitle }}?",
|
|
254
|
+
"linkHrefHelp": "Scrivi l'URL completo, iniziando con https:// o http://",
|
|
254
255
|
"linkTarget": "Destinazione del Link",
|
|
256
|
+
"linkTitle": "Titolo del collegamento",
|
|
257
|
+
"linkTitleRelHelp": "Se vuoto, verrà utilizzato il titolo del documento correlato.",
|
|
258
|
+
"linkTitleUrlHelp": "Se vuoto, verrà utilizzata la didascalia dell'immagine.",
|
|
255
259
|
"linkTo": "Link a...",
|
|
256
260
|
"listJoiner": ", ",
|
|
257
261
|
"live": "Live",
|
|
@@ -455,11 +459,15 @@
|
|
|
455
459
|
"richTextBulletedList": "Lista puntata",
|
|
456
460
|
"richTextCodeBlock": "Blocco del codice",
|
|
457
461
|
"richTextColor": "Colore",
|
|
462
|
+
"richTextH1": "Intestazione 1 (H1)",
|
|
458
463
|
"richTextH2": "Intestazione 2 (H2)",
|
|
459
464
|
"richTextH3": "Intestazione 3 (H3)",
|
|
460
465
|
"richTextH4": "Intestazione 4 (H4)",
|
|
466
|
+
"richTextH5": "Intestazione 5 (H5)",
|
|
467
|
+
"richTextH6": "Intestazione 6 (H6)",
|
|
461
468
|
"richTextHighlight": "Evidenzia",
|
|
462
469
|
"richTextHorizontalRule": "Regola orizzontale",
|
|
470
|
+
"richTextHorizontalRuleDescription": "Aggiungi un separatore orizzontale",
|
|
463
471
|
"richTextInsertMenuHeading": "Inserisci elemento...",
|
|
464
472
|
"richTextItalic": "Corsivo",
|
|
465
473
|
"richTextLink": "Link",
|
|
@@ -251,7 +251,11 @@
|
|
|
251
251
|
"lastUpdatedBy": "Salvo por último em {{ updatedAt }} <br /> por {{ updatedBy }}",
|
|
252
252
|
"leavePageDescription": "O conteúdo que você está tentando editar pertence a outro documento e deve ser editado lá.\nMudanças em {{ oldTitle }} são salvas automaticamente.",
|
|
253
253
|
"leavePageHeading": "Sair de {{ oldTitle }} para editar {{ newTitle }}?",
|
|
254
|
+
"linkHrefHelp": "Escreva a URL completa, começando com https:// ou http://",
|
|
254
255
|
"linkTarget": "Alvo do Link",
|
|
256
|
+
"linkTitle": "Título do link",
|
|
257
|
+
"linkTitleRelHelp": "Se estiver vazio, o título do documento relacionado será usado.",
|
|
258
|
+
"linkTitleUrlHelp": "Se estiver vazio, a legenda da imagem será usada.",
|
|
255
259
|
"linkTo": "Link Para...",
|
|
256
260
|
"listJoiner": ", ",
|
|
257
261
|
"live": "Ao vivo",
|
|
@@ -455,11 +459,15 @@
|
|
|
455
459
|
"richTextBulletedList": "Lista com marcadores",
|
|
456
460
|
"richTextCodeBlock": "Bloco de código",
|
|
457
461
|
"richTextColor": "Cor",
|
|
462
|
+
"richTextH1": "Título 1 (H1)",
|
|
458
463
|
"richTextH2": "Título 2 (H2)",
|
|
459
464
|
"richTextH3": "Título 3 (H3)",
|
|
460
465
|
"richTextH4": "Título 4 (H4)",
|
|
466
|
+
"richTextH5": "Título 5 (H5)",
|
|
467
|
+
"richTextH6": "Título 6 (H6)",
|
|
461
468
|
"richTextHighlight": "Marca",
|
|
462
469
|
"richTextHorizontalRule": "Regra Horizontal",
|
|
470
|
+
"richTextHorizontalRuleDescription": "Adicionar um separador horizontal",
|
|
463
471
|
"richTextInsertMenuHeading": "Inserir elemento...",
|
|
464
472
|
"richTextItalic": "Itálico",
|
|
465
473
|
"richTextLink": "Link",
|