apostrophe 3.4.0 → 3.7.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 +85 -0
- package/README.md +1 -1
- package/deploy-test-count +1 -1
- package/index.js +125 -5
- package/lib/moog-require.js +41 -3
- package/lib/moog.js +20 -8
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarLocale.vue +42 -23
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +25 -13
- package/modules/@apostrophecms/area/index.js +9 -0
- package/modules/@apostrophecms/area/lib/custom-tags/area.js +1 -1
- package/modules/@apostrophecms/area/lib/custom-tags/widget.js +1 -1
- package/modules/@apostrophecms/area/ui/apos/apps/AposAreas.js +3 -0
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +6 -6
- package/modules/@apostrophecms/asset/index.js +8 -8
- package/modules/@apostrophecms/asset/lib/globalIcons.js +2 -0
- package/modules/@apostrophecms/asset/lib/webpack/src/webpack.scss.js +5 -2
- package/modules/@apostrophecms/doc/index.js +13 -3
- package/modules/@apostrophecms/doc-type/index.js +1 -1
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +3 -0
- package/modules/@apostrophecms/i18n/i18n/en.json +11 -2
- package/modules/@apostrophecms/i18n/i18n/es.json +383 -0
- package/modules/@apostrophecms/i18n/i18n/pt-BR.json +380 -0
- package/modules/@apostrophecms/i18n/i18n/sk.json +381 -0
- package/modules/@apostrophecms/i18n/index.js +10 -1
- package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalize.vue +153 -121
- package/modules/@apostrophecms/image/index.js +2 -1
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerEditor.vue +24 -13
- package/modules/@apostrophecms/login/index.js +36 -17
- package/modules/@apostrophecms/login/ui/apos/components/TheAposLogin.vue +8 -0
- package/modules/@apostrophecms/migration/index.js +1 -1
- package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +6 -2
- package/modules/@apostrophecms/modal/ui/apos/components/AposModalConfirm.vue +1 -1
- package/modules/@apostrophecms/modal/ui/apos/mixins/AposEditorMixin.js +6 -0
- package/modules/@apostrophecms/module/index.js +1 -1
- package/modules/@apostrophecms/permission/index.js +1 -1
- package/modules/@apostrophecms/permission/ui/apos/components/AposInputRole.vue +4 -2
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +4 -1
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerDisplay.vue +1 -1
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +42 -10
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapStyles.vue +3 -0
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Classes.js +6 -10
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Default.js +64 -0
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Document.js +15 -0
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Heading.js +23 -0
- package/modules/@apostrophecms/schema/index.js +97 -20
- package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +1 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputArray.vue +4 -1
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputRadio.vue +8 -5
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputRelationship.vue +24 -2
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputSelect.vue +24 -6
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +0 -4
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +0 -7
- package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +25 -3
- package/modules/@apostrophecms/schema/ui/apos/mixins/AposInputMixin.js +10 -2
- package/modules/@apostrophecms/template/index.js +61 -36
- package/modules/@apostrophecms/template/lib/custom-tags/component.js +1 -1
- package/modules/@apostrophecms/template/lib/custom-tags/render.js +6 -2
- package/modules/@apostrophecms/ui/index.js +6 -2
- package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +16 -3
- package/modules/@apostrophecms/ui/ui/apos/components/AposCellContextMenu.vue +1 -1
- package/modules/@apostrophecms/ui/ui/apos/components/AposIndicator.vue +5 -0
- package/modules/@apostrophecms/ui/ui/apos/lib/i18next.js +16 -2
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_tables.scss +4 -3
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_theme.scss +3 -0
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_widgets.scss +3 -0
- package/modules/@apostrophecms/ui/ui/apos/scss/global/import-all.scss +2 -1
- package/modules/@apostrophecms/user/index.js +21 -0
- package/modules/@apostrophecms/util/index.js +2 -2
- package/modules/@apostrophecms/util/ui/src/http.js +12 -8
- package/modules/@apostrophecms/widget-type/index.js +1 -1
- package/modules/@apostrophecms/widget-type/ui/apos/components/AposWidgetEditor.vue +1 -0
- package/package.json +3 -3
- package/test/extra_node_modules/improve-global/index.js +7 -0
- package/test/extra_node_modules/improve-piece-type/index.js +7 -0
- package/test/improve-overrides.js +30 -0
- package/test/login.js +183 -0
- package/test/modules/@apostrophecms/global/index.js +8 -0
- package/test/modules/fragment-all/views/aux-test.html +7 -0
- package/test/modules/fragment-all/views/fragment.html +5 -0
- package/test/moog.js +47 -0
- package/test/package.json +5 -4
- package/test/reverse-relationship.js +170 -0
- package/test/subdir-project/app.js +3 -0
- package/test/subdir-project.js +26 -0
- package/test/templates.js +7 -1
- package/test-lib/test.js +23 -12
- package/test-lib/util.js +1 -0
|
@@ -266,6 +266,9 @@ module.exports = {
|
|
|
266
266
|
enableDeserializeUsers() {
|
|
267
267
|
self.passport.deserializeUser(function (id, cb) {
|
|
268
268
|
self.deserializeUser(id).then(function (user) {
|
|
269
|
+
if (user) {
|
|
270
|
+
user._viaSession = true;
|
|
271
|
+
}
|
|
269
272
|
return cb(null, user);
|
|
270
273
|
}).catch(cb);
|
|
271
274
|
});
|
|
@@ -283,7 +286,12 @@ module.exports = {
|
|
|
283
286
|
|
|
284
287
|
async deserializeUser(id) {
|
|
285
288
|
const req = self.apos.task.getReq();
|
|
286
|
-
const user = await self.apos.user.find(req, {
|
|
289
|
+
const user = await self.apos.user.find(req, {
|
|
290
|
+
_id: id,
|
|
291
|
+
disabled: {
|
|
292
|
+
$ne: true
|
|
293
|
+
}
|
|
294
|
+
}).toObject();
|
|
287
295
|
if (!user) {
|
|
288
296
|
return null;
|
|
289
297
|
}
|
|
@@ -357,21 +365,6 @@ module.exports = {
|
|
|
357
365
|
return 1000 * 60 * 60 * (self.options.passwordResetHours || 48);
|
|
358
366
|
},
|
|
359
367
|
|
|
360
|
-
// Invoked by passport after an authentication strategy succeeds
|
|
361
|
-
// and the user has been logged in. Invokes `loginAfterLogin` on
|
|
362
|
-
// any modules that have one and redirects to `req.redirect` or,
|
|
363
|
-
// if it is not set, to `/`.
|
|
364
|
-
|
|
365
|
-
async afterLogin(req, res) {
|
|
366
|
-
try {
|
|
367
|
-
await self.emit('after', req);
|
|
368
|
-
} catch (e) {
|
|
369
|
-
self.apos.util.error(e);
|
|
370
|
-
return res.redirect('/');
|
|
371
|
-
}
|
|
372
|
-
return res.redirect(req.redirect || '/');
|
|
373
|
-
},
|
|
374
|
-
|
|
375
368
|
getBrowserData(req) {
|
|
376
369
|
return {
|
|
377
370
|
action: self.action,
|
|
@@ -390,7 +383,7 @@ module.exports = {
|
|
|
390
383
|
const adminReq = self.apos.task.getReq();
|
|
391
384
|
const user = await self.apos.user.find(adminReq, {}).relationships(false).limit(1).toObject();
|
|
392
385
|
|
|
393
|
-
if (!user) {
|
|
386
|
+
if (!user && !self.apos.options.test) {
|
|
394
387
|
self.apos.util.warnDev('There are no users created for this installation of ApostropheCMS yet.');
|
|
395
388
|
}
|
|
396
389
|
},
|
|
@@ -408,10 +401,36 @@ module.exports = {
|
|
|
408
401
|
before: '@apostrophecms/i18n',
|
|
409
402
|
middleware: self.passport.initialize()
|
|
410
403
|
},
|
|
404
|
+
passportExtendLogin: {
|
|
405
|
+
before: '@apostrophecms/i18n',
|
|
406
|
+
middleware(req, res, next) {
|
|
407
|
+
const superLogin = req.login.bind(req);
|
|
408
|
+
req.login = (user, callback) => {
|
|
409
|
+
return superLogin(user, (err) => {
|
|
410
|
+
if (err) {
|
|
411
|
+
return callback(err);
|
|
412
|
+
}
|
|
413
|
+
req.session.loginAt = Date.now();
|
|
414
|
+
return callback(null);
|
|
415
|
+
});
|
|
416
|
+
};
|
|
417
|
+
return next();
|
|
418
|
+
}
|
|
419
|
+
},
|
|
411
420
|
passportSession: {
|
|
412
421
|
before: '@apostrophecms/i18n',
|
|
413
422
|
middleware: self.passport.session()
|
|
414
423
|
},
|
|
424
|
+
honorLoginInvalidBefore: {
|
|
425
|
+
before: '@apostrophecms/i18n',
|
|
426
|
+
middleware(req, res, next) {
|
|
427
|
+
if (req.user && req.user._viaSession && req.user.loginInvalidBefore && ((!req.session.loginAt) || (req.session.loginAt < req.user.loginInvalidBefore))) {
|
|
428
|
+
req.session.destroy();
|
|
429
|
+
delete req.user;
|
|
430
|
+
}
|
|
431
|
+
return next();
|
|
432
|
+
}
|
|
433
|
+
},
|
|
415
434
|
addUserToData: {
|
|
416
435
|
before: '@apostrophecms/i18n',
|
|
417
436
|
middleware(req, res, next) {
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
type="primary"
|
|
38
38
|
label="apostrophe:login"
|
|
39
39
|
button-type="submit"
|
|
40
|
+
class="apos-login__submit"
|
|
40
41
|
:modifiers="['gradient-on-hover', 'block']"
|
|
41
42
|
@click="submit"
|
|
42
43
|
/>
|
|
@@ -121,6 +122,9 @@ export default {
|
|
|
121
122
|
},
|
|
122
123
|
methods: {
|
|
123
124
|
async submit() {
|
|
125
|
+
if (this.busy) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
124
128
|
this.busy = true;
|
|
125
129
|
this.error = '';
|
|
126
130
|
try {
|
|
@@ -298,4 +302,8 @@ export default {
|
|
|
298
302
|
margin-left: auto;
|
|
299
303
|
}
|
|
300
304
|
}
|
|
305
|
+
|
|
306
|
+
.apos-login__submit ::v-deep .apos-button {
|
|
307
|
+
height: 47px;
|
|
308
|
+
}
|
|
301
309
|
</style>
|
|
@@ -260,7 +260,7 @@ module.exports = {
|
|
|
260
260
|
// Intentionally emitted regardless of whether the site is new or not.
|
|
261
261
|
//
|
|
262
262
|
// This is the right time to park pages, for instance, because the
|
|
263
|
-
// database is guaranteed to be in a
|
|
263
|
+
// database is guaranteed to be in a stable state, whether because the
|
|
264
264
|
// site is new or because migrations ran successfully.
|
|
265
265
|
await self.emit('after');
|
|
266
266
|
} finally {
|
|
@@ -43,7 +43,11 @@
|
|
|
43
43
|
</h2>
|
|
44
44
|
<div class="apos-modal__controls--header" v-if="hasBeenLocalized || hasPrimaryControls">
|
|
45
45
|
<div class="apos-modal__locale" v-if="hasBeenLocalized">
|
|
46
|
-
<span class="apos-modal__locale-label">
|
|
46
|
+
<span class="apos-modal__locale-label">
|
|
47
|
+
{{ $t('apostrophe:locale') }}:
|
|
48
|
+
</span> <span class="apos-modal__locale-name">
|
|
49
|
+
{{ currentLocale }}
|
|
50
|
+
</span>
|
|
47
51
|
</div>
|
|
48
52
|
<div class="apos-modal__controls--primary" v-if="hasPrimaryControls">
|
|
49
53
|
<slot name="primaryControls" />
|
|
@@ -447,9 +451,9 @@ export default {
|
|
|
447
451
|
$height: 190px;
|
|
448
452
|
top: 50%;
|
|
449
453
|
bottom: -50%;
|
|
454
|
+
display: flex;
|
|
450
455
|
height: $height;
|
|
451
456
|
transform: translateY(math.div($height, 2) * -1);
|
|
452
|
-
display: flex;
|
|
453
457
|
justify-content: center;
|
|
454
458
|
align-items: center;
|
|
455
459
|
text-align: center;
|
|
@@ -446,7 +446,7 @@ module.exports = {
|
|
|
446
446
|
// By default browser data is pushed only for the `apos` scene, so public
|
|
447
447
|
// site pages will not be cluttered with it, except on the /login page and
|
|
448
448
|
// other pages that opt into the `apos` scene. If `scene` is set to `public`
|
|
449
|
-
// then the data is available
|
|
449
|
+
// then the data is available all the time.
|
|
450
450
|
//
|
|
451
451
|
// Be sure to use `extendMethods` when implementing `getBrowserData`
|
|
452
452
|
// as your base class may also implement `getBrowserData`.
|
|
@@ -234,7 +234,7 @@ module.exports = {
|
|
|
234
234
|
}
|
|
235
235
|
permissions.push({
|
|
236
236
|
name: 'edit',
|
|
237
|
-
label: module.options.singleton ? '
|
|
237
|
+
label: module.options.singleton ? 'apostrophe:modify' : 'apostrophe:modifyOrDelete',
|
|
238
238
|
value: self.can(req, 'edit', module.name)
|
|
239
239
|
});
|
|
240
240
|
permissions.push({
|
|
@@ -126,7 +126,6 @@ export default {
|
|
|
126
126
|
const list = document.createElement('ul');
|
|
127
127
|
const intro = document.createElement('p');
|
|
128
128
|
const followUp = document.createElement('p');
|
|
129
|
-
const link = document.createElement('a');
|
|
130
129
|
intro.appendChild(document.createTextNode(this.$t('apostrophe:piecePermissionsIntro')));
|
|
131
130
|
followUp.appendChild(document.createTextNode(this.$t('apostrophe:piecePermissionsPieceTypeList')));
|
|
132
131
|
html.appendChild(intro);
|
|
@@ -137,7 +136,10 @@ export default {
|
|
|
137
136
|
list.appendChild(li);
|
|
138
137
|
});
|
|
139
138
|
html.appendChild(list);
|
|
140
|
-
return {
|
|
139
|
+
return {
|
|
140
|
+
content: html,
|
|
141
|
+
localize: false
|
|
142
|
+
};
|
|
141
143
|
},
|
|
142
144
|
validate(value) {
|
|
143
145
|
if (this.field.required && !value.length) {
|
|
@@ -211,7 +211,10 @@ export default {
|
|
|
211
211
|
// Add computed singular label to context menu
|
|
212
212
|
this.moreMenu.menu.unshift({
|
|
213
213
|
action: 'new',
|
|
214
|
-
label:
|
|
214
|
+
label: {
|
|
215
|
+
key: 'apostrophe:newDocType',
|
|
216
|
+
type: this.$t(this.moduleLabels.singular)
|
|
217
|
+
}
|
|
215
218
|
});
|
|
216
219
|
}
|
|
217
220
|
apos.bus.$on('content-changed', this.getPieces);
|
package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue
CHANGED
|
@@ -26,9 +26,8 @@
|
|
|
26
26
|
</AposContextMenuDialog>
|
|
27
27
|
</bubble-menu>
|
|
28
28
|
<div class="apos-rich-text-editor__editor" :class="editorModifiers">
|
|
29
|
-
<editor-content :editor="editor" :class="
|
|
29
|
+
<editor-content :editor="editor" :class="editorOptions.className" />
|
|
30
30
|
</div>
|
|
31
|
-
<!-- Using actual DOM element rather than :after to ease localization -->
|
|
32
31
|
<div class="apos-rich-text-editor__editor_after" :class="editorModifiers">
|
|
33
32
|
{{ $t('apostrophe:emptyRichTextWidget') }}
|
|
34
33
|
</div>
|
|
@@ -103,11 +102,27 @@ export default {
|
|
|
103
102
|
|
|
104
103
|
activeOptions.styles = this.enhanceStyles(activeOptions.styles || this.defaultOptions.styles);
|
|
105
104
|
|
|
105
|
+
activeOptions.className = (activeOptions.className !== undefined)
|
|
106
|
+
? activeOptions.className : this.moduleOptions.className;
|
|
107
|
+
|
|
106
108
|
return activeOptions;
|
|
107
109
|
},
|
|
108
|
-
|
|
110
|
+
autofocus() {
|
|
111
|
+
// Only true for a new rich text widget
|
|
112
|
+
return !this.stripPlaceholderBrs(this.value.content).length;
|
|
113
|
+
},
|
|
109
114
|
initialContent() {
|
|
110
|
-
|
|
115
|
+
const content = this.stripPlaceholderBrs(this.value.content);
|
|
116
|
+
if (!content.length) {
|
|
117
|
+
// If we don't supply a valid instance of the first style, then
|
|
118
|
+
// the text align control will not work until the user manually
|
|
119
|
+
// applies a style or refreshes the page
|
|
120
|
+
const defaultStyle = this.editorOptions.styles.find(style => style.def);
|
|
121
|
+
const _class = defaultStyle.class ? ` class="${defaultStyle.class}"` : '';
|
|
122
|
+
return `<${defaultStyle.tag}${_class}></${defaultStyle.tag}>`;
|
|
123
|
+
} else {
|
|
124
|
+
return content;
|
|
125
|
+
}
|
|
111
126
|
},
|
|
112
127
|
toolbar() {
|
|
113
128
|
return this.editorOptions.toolbar;
|
|
@@ -136,7 +151,7 @@ export default {
|
|
|
136
151
|
aposTiptapExtensions() {
|
|
137
152
|
return (apos.tiptapExtensions || [])
|
|
138
153
|
.map(extension => extension({
|
|
139
|
-
styles: this.editorOptions.styles,
|
|
154
|
+
styles: this.editorOptions.styles.map(this.localizeStyle),
|
|
140
155
|
types: this.tiptapTypes
|
|
141
156
|
}));
|
|
142
157
|
}
|
|
@@ -153,7 +168,7 @@ export default {
|
|
|
153
168
|
mounted() {
|
|
154
169
|
this.editor = new Editor({
|
|
155
170
|
content: this.initialContent,
|
|
156
|
-
autofocus:
|
|
171
|
+
autofocus: this.autofocus,
|
|
157
172
|
onUpdate: this.editorUpdate,
|
|
158
173
|
extensions: [
|
|
159
174
|
StarterKit,
|
|
@@ -217,7 +232,6 @@ export default {
|
|
|
217
232
|
// commands and parameters used internally.
|
|
218
233
|
enhanceStyles(styles) {
|
|
219
234
|
const self = this;
|
|
220
|
-
const enhanced = [];
|
|
221
235
|
(styles || []).forEach(style => {
|
|
222
236
|
style.options = {};
|
|
223
237
|
for (const key in self.tiptapTextCommands) {
|
|
@@ -242,9 +256,7 @@ export default {
|
|
|
242
256
|
style.options.class = style.class;
|
|
243
257
|
}
|
|
244
258
|
|
|
245
|
-
if (style.type) {
|
|
246
|
-
enhanced.push(style);
|
|
247
|
-
} else {
|
|
259
|
+
if (!style.type) {
|
|
248
260
|
apos.notify('apostrophe:richTextStyleConfigWarning', {
|
|
249
261
|
type: 'warning',
|
|
250
262
|
dismiss: true,
|
|
@@ -256,7 +268,27 @@ export default {
|
|
|
256
268
|
});
|
|
257
269
|
}
|
|
258
270
|
});
|
|
271
|
+
|
|
272
|
+
// ensure a default so we can rely on it throughout
|
|
273
|
+
const hasDefault = !!styles.find(style => style.def);
|
|
274
|
+
if (!hasDefault && styles.length) {
|
|
275
|
+
// If no dev set default, use the first paragraph we can find
|
|
276
|
+
if (styles.filter(style => style.type === 'paragraph').length) {
|
|
277
|
+
styles.filter(style => style.type === 'paragraph')[0].def = true;
|
|
278
|
+
} else {
|
|
279
|
+
// Otherwise, set the first style
|
|
280
|
+
styles[0].def = true;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
259
283
|
return styles;
|
|
284
|
+
},
|
|
285
|
+
localizeStyle(style) {
|
|
286
|
+
style.label = this.$t(style.label);
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
...style,
|
|
290
|
+
label: this.$t(style.label)
|
|
291
|
+
};
|
|
260
292
|
}
|
|
261
293
|
}
|
|
262
294
|
};
|
|
@@ -52,6 +52,9 @@ export default {
|
|
|
52
52
|
const style = styles[i];
|
|
53
53
|
if (this.editor.isActive(style.type, (style.options || {}))) {
|
|
54
54
|
return i;
|
|
55
|
+
} else if (this.editor.state.selection.$head.parent.type.name === 'defaultNode' && style.def) {
|
|
56
|
+
// Look deeper to see if custom defaultNode is active
|
|
57
|
+
return i;
|
|
55
58
|
}
|
|
56
59
|
}
|
|
57
60
|
return 0;
|
|
@@ -25,9 +25,7 @@ export default (options) => {
|
|
|
25
25
|
const tag = element.tagName.toLowerCase();
|
|
26
26
|
// This tag is not configured
|
|
27
27
|
if (!allow[tag]) {
|
|
28
|
-
return
|
|
29
|
-
class: null
|
|
30
|
-
};
|
|
28
|
+
return null;
|
|
31
29
|
}
|
|
32
30
|
const classes = (element.getAttribute('class') || '')
|
|
33
31
|
.split(' ')
|
|
@@ -36,13 +34,11 @@ export default (options) => {
|
|
|
36
34
|
// If no valid classes for this parse, default to the
|
|
37
35
|
// the first setting for this tag (including null for tags defined without classes).
|
|
38
36
|
// else, remove classes.
|
|
39
|
-
return
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
:
|
|
43
|
-
|
|
44
|
-
)
|
|
45
|
-
};
|
|
37
|
+
return classes.length
|
|
38
|
+
? classes.join(' ')
|
|
39
|
+
: (
|
|
40
|
+
allow[tag].length ? allow[tag][0] : null
|
|
41
|
+
);
|
|
46
42
|
}
|
|
47
43
|
}
|
|
48
44
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// If a style has been marked `def`, we need to make sure it
|
|
2
|
+
// and it's attributes are prioritized in our editor.
|
|
3
|
+
// It's easier to create a new Node with our specifications and put it
|
|
4
|
+
// at the front of the line than try to infer between editor instantiation
|
|
5
|
+
// and the editor-user setting styles via the toolbar to know when its needed.
|
|
6
|
+
|
|
7
|
+
import { Node } from '@tiptap/core';
|
|
8
|
+
import Heading from '@tiptap/extension-heading';
|
|
9
|
+
import Paragraph from '@tiptap/extension-paragraph';
|
|
10
|
+
|
|
11
|
+
const nodeMap = {
|
|
12
|
+
heading: Heading,
|
|
13
|
+
paragraph: Paragraph
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default (options) => {
|
|
17
|
+
const def = options.styles.filter(style => style.def)[0];
|
|
18
|
+
|
|
19
|
+
// Configuration has a default style
|
|
20
|
+
if (def) {
|
|
21
|
+
const nodeName = 'defaultNode';
|
|
22
|
+
const attrs = {
|
|
23
|
+
class: def.options.class || null
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
if (def.type === 'heading' || def.type === 'paragraph') {
|
|
27
|
+
return nodeMap[def.type].extend({
|
|
28
|
+
name: nodeName,
|
|
29
|
+
defaultOptions: {
|
|
30
|
+
HTMLAttributes: attrs,
|
|
31
|
+
levels: def.options.level ? [ def.options.level ] : null
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (def.type === 'textStyle') {
|
|
37
|
+
return Node.create({
|
|
38
|
+
group: 'block',
|
|
39
|
+
content: 'text*',
|
|
40
|
+
name: nodeName,
|
|
41
|
+
defaultOptions: {
|
|
42
|
+
HTMLAttributes: attrs
|
|
43
|
+
},
|
|
44
|
+
renderHTML: () => {
|
|
45
|
+
return [ 'span', attrs, 0 ];
|
|
46
|
+
},
|
|
47
|
+
parseHTML() {
|
|
48
|
+
return [
|
|
49
|
+
{
|
|
50
|
+
tag: 'span',
|
|
51
|
+
getAttrs: element => {
|
|
52
|
+
const hasStyles = element.hasAttribute('style');
|
|
53
|
+
if (!hasStyles) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
return {};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
];
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Acts as a custom Document extension
|
|
2
|
+
import { Node } from '@tiptap/core';
|
|
3
|
+
export default (options) => {
|
|
4
|
+
const def = options.styles.filter(style => style.def)[0];
|
|
5
|
+
let content = 'block+'; // one or more block nodes (default Document setting)
|
|
6
|
+
if (def) {
|
|
7
|
+
// one/more defaultNodes (created in ./Default) or one/more other block nodes
|
|
8
|
+
content = '(defaultNode|block)+';
|
|
9
|
+
}
|
|
10
|
+
return Node.create({
|
|
11
|
+
name: 'doc',
|
|
12
|
+
topNode: true,
|
|
13
|
+
content
|
|
14
|
+
});
|
|
15
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Lock Heading levels down to just those provided via configuration
|
|
2
|
+
import Heading from '@tiptap/extension-heading';
|
|
3
|
+
|
|
4
|
+
export default (options) => {
|
|
5
|
+
const headings = options.styles.filter(style => style.type === 'heading');
|
|
6
|
+
const levels = headings.map(heading => heading.options.level);
|
|
7
|
+
const defaultLevel = headings.filter(heading => heading.def).length
|
|
8
|
+
? headings.filter(heading => heading.def)[0].options.level
|
|
9
|
+
: levels[0];
|
|
10
|
+
return Heading.extend({
|
|
11
|
+
defaultOptions: {
|
|
12
|
+
levels
|
|
13
|
+
},
|
|
14
|
+
addAttributes() {
|
|
15
|
+
return {
|
|
16
|
+
level: {
|
|
17
|
+
default: defaultLevel,
|
|
18
|
+
rendered: false
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
};
|