mirror of
https://github.com/LibreSign/libresign.git
synced 2025-12-17 21:12:16 +01:00
feat: implement ckeditor
Signed-off-by: Vitor Mattos <vitor@php.rio>
This commit is contained in:
parent
6dd6a862cc
commit
fae5dbfa96
6 changed files with 3528 additions and 201 deletions
3350
package-lock.json
generated
3350
package-lock.json
generated
File diff suppressed because it is too large
Load diff
14
package.json
14
package.json
|
|
@ -20,6 +20,16 @@
|
||||||
"test:coverage": "jest --coverage"
|
"test:coverage": "jest --coverage"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@ckeditor/ckeditor5-build-decoupled-document": "^44.3.0",
|
||||||
|
"@ckeditor/ckeditor5-dev-webpack-plugin": "^31.1.13",
|
||||||
|
"@ckeditor/ckeditor5-editor-decoupled": "^44.3.0",
|
||||||
|
"@ckeditor/ckeditor5-essentials": "^44.3.0",
|
||||||
|
"@ckeditor/ckeditor5-image": "^44.3.0",
|
||||||
|
"@ckeditor/ckeditor5-mention": "^44.3.0",
|
||||||
|
"@ckeditor/ckeditor5-paragraph": "^44.3.0",
|
||||||
|
"@ckeditor/ckeditor5-theme-lark": "^44.3.0",
|
||||||
|
"@ckeditor/ckeditor5-ui": "^44.3.0",
|
||||||
|
"@ckeditor/ckeditor5-vue2": "^3.0.1",
|
||||||
"@fontsource/dancing-script": "^5.2.5",
|
"@fontsource/dancing-script": "^5.2.5",
|
||||||
"@libresign/vue-pdf-editor": "^1.4.5",
|
"@libresign/vue-pdf-editor": "^1.4.5",
|
||||||
"@marionebl/option": "^1.0.8",
|
"@marionebl/option": "^1.0.8",
|
||||||
|
|
@ -46,6 +56,9 @@
|
||||||
"debounce": "^2.2.0",
|
"debounce": "^2.2.0",
|
||||||
"js-confetti": "^0.12.0",
|
"js-confetti": "^0.12.0",
|
||||||
"pinia": "^2.3.1",
|
"pinia": "^2.3.1",
|
||||||
|
"postcss": "^8.5.3",
|
||||||
|
"postcss-loader": "^8.1.1",
|
||||||
|
"raw-loader": "^4.0.2",
|
||||||
"v-perfect-signature": "^1.4.0",
|
"v-perfect-signature": "^1.4.0",
|
||||||
"vue": "^2.7.16",
|
"vue": "^2.7.16",
|
||||||
"vue-advanced-cropper": "^1.11.7",
|
"vue-advanced-cropper": "^1.11.7",
|
||||||
|
|
@ -72,6 +85,7 @@
|
||||||
"babel-loader-exclude-node-modules-except": "^1.2.1",
|
"babel-loader-exclude-node-modules-except": "^1.2.1",
|
||||||
"esbuild-loader": "^4.3.0",
|
"esbuild-loader": "^4.3.0",
|
||||||
"openapi-typescript": "^7.6.1",
|
"openapi-typescript": "^7.6.1",
|
||||||
|
"svg-inline-loader": "^0.8.2",
|
||||||
"vue-template-compiler": "^2.7.16"
|
"vue-template-compiler": "^2.7.16"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
262
src/Components/TextEditor/TextEditor.vue
Normal file
262
src/Components/TextEditor/TextEditor.vue
Normal file
|
|
@ -0,0 +1,262 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div ref="containerToolbar" class="toolbar" />
|
||||||
|
<ckeditor v-if="ready"
|
||||||
|
:value="value"
|
||||||
|
:config="config"
|
||||||
|
:editor="editor"
|
||||||
|
class="editor"
|
||||||
|
@input="onEditorInput"
|
||||||
|
@ready="onEditorReady" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { loadState } from '@nextcloud/initial-state'
|
||||||
|
import CKEditor from '@ckeditor/ckeditor5-vue2'
|
||||||
|
import Editor from '@ckeditor/ckeditor5-editor-decoupled/src/decouplededitor.js'
|
||||||
|
import EssentialsPlugin from '@ckeditor/ckeditor5-essentials/src/essentials.js'
|
||||||
|
import ParagraphPlugin from '@ckeditor/ckeditor5-paragraph/src/paragraph.js'
|
||||||
|
import InsertVariablePlugin from '../../ckeditor/InsertVariablePlugin'
|
||||||
|
import { DropdownView } from '@ckeditor/ckeditor5-ui'
|
||||||
|
import { getLanguage } from '@nextcloud/l10n'
|
||||||
|
import logger from '../../logger.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TextEditor',
|
||||||
|
components: {
|
||||||
|
ckeditor: CKEditor.component,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
ready: false,
|
||||||
|
editor: Editor,
|
||||||
|
config: {
|
||||||
|
licenseKey: 'GPL',
|
||||||
|
autoParagraph: false,
|
||||||
|
plugins: [
|
||||||
|
EssentialsPlugin,
|
||||||
|
ParagraphPlugin,
|
||||||
|
InsertVariablePlugin,
|
||||||
|
],
|
||||||
|
toolbar: {
|
||||||
|
items: ['undo', 'redo', 'insertVariable'],
|
||||||
|
},
|
||||||
|
variaveis: loadState('libresign', 'signature_available_variables'),
|
||||||
|
language: 'en',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
value(val) {
|
||||||
|
console.log('Value mudou:', val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeMount() {
|
||||||
|
this.loadEditorTranslations(getLanguage())
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
console.log('Editor carregado:', this.editor);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
overrideDropdownPositionsToNorth(editor, toolbarView) {
|
||||||
|
const {
|
||||||
|
south, north, southEast, southWest, northEast, northWest,
|
||||||
|
southMiddleEast, southMiddleWest, northMiddleEast, northMiddleWest,
|
||||||
|
} = DropdownView.defaultPanelPositions
|
||||||
|
|
||||||
|
let panelPositions
|
||||||
|
|
||||||
|
if (editor.locale.uiLanguageDirection !== 'rtl') {
|
||||||
|
panelPositions = [
|
||||||
|
northEast, northWest, northMiddleEast, northMiddleWest, north,
|
||||||
|
southEast, southWest, southMiddleEast, southMiddleWest, south,
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
panelPositions = [
|
||||||
|
northWest, northEast, northMiddleWest, northMiddleEast, north,
|
||||||
|
southWest, southEast, southMiddleWest, southMiddleEast, south,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const item of toolbarView.items) {
|
||||||
|
if (!(item instanceof DropdownView)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
item.on('change:isOpen', () => {
|
||||||
|
if (!item.isOpen) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
item.panelView.position = DropdownView._getOptimalPosition({
|
||||||
|
element: item.panelView.element,
|
||||||
|
target: item.buttonView.element,
|
||||||
|
fitInViewport: true,
|
||||||
|
positions: panelPositions,
|
||||||
|
}).name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
overrideTooltipPositions(toolbarView) {
|
||||||
|
for (const item of toolbarView.items) {
|
||||||
|
if (item.buttonView) {
|
||||||
|
item.buttonView.tooltipPosition = 'n'
|
||||||
|
} else if (item.tooltipPosition) {
|
||||||
|
item.tooltipPosition = 'n'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async loadEditorTranslations(language) {
|
||||||
|
if (language === 'en') {
|
||||||
|
// The default, nothing to fetch
|
||||||
|
return this.showEditor('en')
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
logger.debug(`loading ${language} translations for CKEditor`)
|
||||||
|
await import(
|
||||||
|
/* webpackMode: "lazy-once" */
|
||||||
|
/* webpackPrefetch: true */
|
||||||
|
/* webpackPreload: true */
|
||||||
|
`@ckeditor/ckeditor5-build-decoupled-document/build/translations/${language}`
|
||||||
|
)
|
||||||
|
this.showEditor(language)
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`could not find CKEditor translations for "${language}"`, { error })
|
||||||
|
this.showEditor('en')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showEditor(language) {
|
||||||
|
logger.debug(`using "${language}" as CKEditor language`)
|
||||||
|
this.config.language = language
|
||||||
|
|
||||||
|
this.ready = true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {module:core/editor/editor~Editor} editor editor the editor instance
|
||||||
|
*/
|
||||||
|
onEditorReady(editor) {
|
||||||
|
logger.debug('TextEditor is ready', { editor })
|
||||||
|
|
||||||
|
// https://ckeditor.com/docs/ckeditor5/latest/examples/builds-custom/bottom-toolbar-editor.html
|
||||||
|
if (editor.ui) {
|
||||||
|
this.$refs.containerToolbar.appendChild(editor.ui.view.toolbar.element)
|
||||||
|
this.overrideDropdownPositionsToNorth(editor, editor.ui.view.toolbar)
|
||||||
|
this.overrideTooltipPositions(editor.ui.view.toolbar)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.editorInstance = editor
|
||||||
|
editor.setData(this.value.replace(/\n/g, '<br />'))
|
||||||
|
|
||||||
|
this.$emit('ready', editor)
|
||||||
|
},
|
||||||
|
onEditorInput(text) {
|
||||||
|
if (text !== this.value) {
|
||||||
|
logger.debug(`TextEditor input changed to <${text}>`)
|
||||||
|
this.$emit('input', text)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.editor {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 150px;
|
||||||
|
height: calc(100% - 75px);
|
||||||
|
overflow: scroll;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
&.ck {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(a) {
|
||||||
|
color: #07d;
|
||||||
|
}
|
||||||
|
:deep(p) {
|
||||||
|
cursor: text;
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/*
|
||||||
|
Overwrite the default z-index for CKEditor
|
||||||
|
https://github.com/ckeditor/ckeditor5/issues/1142
|
||||||
|
*/
|
||||||
|
.ck .ck-reset {
|
||||||
|
background: var(--color-main-background) !important;
|
||||||
|
}
|
||||||
|
/* Default ckeditor value of padding-inline-start, to overwrite the global styling from server */
|
||||||
|
.ck-content ul, .ck-content ol {
|
||||||
|
padding-inline-start: 40px;
|
||||||
|
}
|
||||||
|
.ck-list__item {
|
||||||
|
.ck-off {
|
||||||
|
background:var(--color-main-background) !important;
|
||||||
|
}
|
||||||
|
.ck-on {
|
||||||
|
background:var(--color-primary-element-light) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.custom-item-username {
|
||||||
|
color: var(--color-main-text) !important;
|
||||||
|
}
|
||||||
|
.link-title{
|
||||||
|
color: var(--color-main-text) !important;
|
||||||
|
margin-left: var(--default-grid-baseline) !important;
|
||||||
|
}
|
||||||
|
.link-icon {
|
||||||
|
width: 16px !important;
|
||||||
|
}
|
||||||
|
.custom-item {
|
||||||
|
width : 100% !important;
|
||||||
|
border-radius : 8px !important;
|
||||||
|
padding : 4px 8px !important;
|
||||||
|
display :block;
|
||||||
|
background:var(--color-main-background)!important;
|
||||||
|
}
|
||||||
|
.custom-item:hover {
|
||||||
|
background:var(--color-primary-element-light)!important;
|
||||||
|
}
|
||||||
|
.link-container{
|
||||||
|
border-radius :8px !important;
|
||||||
|
padding :4px 8px !important;
|
||||||
|
display : block;
|
||||||
|
width : 100% !important;
|
||||||
|
background:var(--color-main-background)!important;
|
||||||
|
}
|
||||||
|
.link-container:hover {
|
||||||
|
background:var(--color-primary-element-light)!important;
|
||||||
|
}
|
||||||
|
:root {
|
||||||
|
--ck-z-default: 10000;
|
||||||
|
--ck-balloon-border-width: 0;
|
||||||
|
}
|
||||||
|
.ck.ck-toolbar.ck-rounded-corners {
|
||||||
|
border-radius: var(--border-radius-large) !important;
|
||||||
|
}
|
||||||
|
.ck-rounded-corners .ck.ck-dropdown__panel, .ck.ck-dropdown__panel.ck-rounded-corners {
|
||||||
|
border-radius: var(--border-radius-large) !important;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck.ck-button {
|
||||||
|
border-radius: var(--border-radius-element) !important;
|
||||||
|
}
|
||||||
|
.ck-powered-by-balloon {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
42
src/ckeditor/InsertVariablePlugin.js
Normal file
42
src/ckeditor/InsertVariablePlugin.js
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import Plugin from '@ckeditor/ckeditor5-core/src/plugin'
|
||||||
|
import { addListToDropdown, createDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils';
|
||||||
|
import Collection from '@ckeditor/ckeditor5-utils/src/collection';
|
||||||
|
import Model from '@ckeditor/ckeditor5-ui/src/model'
|
||||||
|
|
||||||
|
export default class InsertVariablePlugin extends Plugin {
|
||||||
|
init() {
|
||||||
|
const editor = this.editor;
|
||||||
|
|
||||||
|
editor.ui.componentFactory.add('insertVariable', locale => {
|
||||||
|
const dropdownView = createDropdown(locale);
|
||||||
|
|
||||||
|
dropdownView.buttonView.set({
|
||||||
|
label: 'Insert Variable',
|
||||||
|
tooltip: true,
|
||||||
|
withText: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const items = new Collection();
|
||||||
|
const buttonModel = new Model({
|
||||||
|
withText: true,
|
||||||
|
label: 'Foo',
|
||||||
|
});
|
||||||
|
|
||||||
|
buttonModel.on('execute', () => {
|
||||||
|
console.log('Bar executed')
|
||||||
|
editor.model.change(writer => {
|
||||||
|
const position = editor.model.document.selection.getFirstPosition();
|
||||||
|
writer.insertText('{{bar}}', position);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
items.add({
|
||||||
|
type: 'button',
|
||||||
|
model: buttonModel,
|
||||||
|
});
|
||||||
|
addListToDropdown(dropdownView, items);
|
||||||
|
|
||||||
|
return dropdownView;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,7 +15,9 @@
|
||||||
</ul>
|
</ul>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="content__row">
|
<div class="content__row">
|
||||||
<NcTextArea ref="textareaEditor"
|
<TextEditor id="template"
|
||||||
|
v-model="inputValue" />
|
||||||
|
<!-- <NcTextArea ref="textareaEditor"
|
||||||
:value.sync="inputValue"
|
:value.sync="inputValue"
|
||||||
:label="t('libresign', 'Signature text template')"
|
:label="t('libresign', 'Signature text template')"
|
||||||
:placeholder="t('libresign', 'Signature text template')"
|
:placeholder="t('libresign', 'Signature text template')"
|
||||||
|
|
@ -25,7 +27,7 @@
|
||||||
@keydown.enter="save"
|
@keydown.enter="save"
|
||||||
@blur="save"
|
@blur="save"
|
||||||
@mousemove="resizeHeight"
|
@mousemove="resizeHeight"
|
||||||
@keypress="resizeHeight" />
|
@keypress="resizeHeight" /> -->
|
||||||
<NcButton v-if="showResetTemplate"
|
<NcButton v-if="showResetTemplate"
|
||||||
type="tertiary"
|
type="tertiary"
|
||||||
:aria-label="t('libresign', 'Reset to default')"
|
:aria-label="t('libresign', 'Reset to default')"
|
||||||
|
|
@ -68,6 +70,7 @@
|
||||||
</NcSettingsSection>
|
</NcSettingsSection>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
import TextEditor from '../../Components/TextEditor/TextEditor.vue'
|
||||||
import debounce from 'debounce'
|
import debounce from 'debounce'
|
||||||
|
|
||||||
import Undo from 'vue-material-design-icons/UndoVariant.vue'
|
import Undo from 'vue-material-design-icons/UndoVariant.vue'
|
||||||
|
|
@ -86,6 +89,7 @@ import NcTextField from '@nextcloud/vue/components/NcTextField'
|
||||||
export default {
|
export default {
|
||||||
name: 'SignatureTextTemplate',
|
name: 'SignatureTextTemplate',
|
||||||
components: {
|
components: {
|
||||||
|
TextEditor,
|
||||||
NcButton,
|
NcButton,
|
||||||
NcNoteCard,
|
NcNoteCard,
|
||||||
NcSettingsSection,
|
NcSettingsSection,
|
||||||
|
|
@ -97,7 +101,7 @@ export default {
|
||||||
return {
|
return {
|
||||||
name: t('libresign', 'Signature text template'),
|
name: t('libresign', 'Signature text template'),
|
||||||
description: t('libresign', 'This template will be mixed to signature.'),
|
description: t('libresign', 'This template will be mixed to signature.'),
|
||||||
defaultSignatureTextTemplate: loadState('libresign', 'default_signature_text_template'),
|
defaultSignatureTextTemplate: '<p>' + loadState('libresign', 'default_signature_text_template').replace(/\n/g, '<br>') + '</p>',
|
||||||
defaultSignatureFontSize: loadState('libresign', 'default_signature_font_size'),
|
defaultSignatureFontSize: loadState('libresign', 'default_signature_font_size'),
|
||||||
signatureTextTemplate: loadState('libresign', 'signature_text_template'),
|
signatureTextTemplate: loadState('libresign', 'signature_text_template'),
|
||||||
fontSize: loadState('libresign', 'signature_font_size'),
|
fontSize: loadState('libresign', 'signature_font_size'),
|
||||||
|
|
@ -182,6 +186,18 @@ export default {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0 4px;
|
gap: 0 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#template {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100px;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
|
||||||
|
&:active,
|
||||||
|
&:focus,
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--color-primary-element) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.text-pre-line {
|
.text-pre-line {
|
||||||
white-space: pre-line;
|
white-space: pre-line;
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,20 @@
|
||||||
*/
|
*/
|
||||||
const { merge } = require('webpack-merge')
|
const { merge } = require('webpack-merge')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
const CKEditorWebpackPlugin = require('@ckeditor/ckeditor5-dev-webpack-plugin')
|
||||||
|
const { styles } = require('@ckeditor/ckeditor5-dev-utils')
|
||||||
const BabelLoaderExcludeNodeModulesExcept = require('babel-loader-exclude-node-modules-except')
|
const BabelLoaderExcludeNodeModulesExcept = require('babel-loader-exclude-node-modules-except')
|
||||||
const { EsbuildPlugin } = require('esbuild-loader')
|
const { EsbuildPlugin } = require('esbuild-loader')
|
||||||
const nextcloudWebpackConfig = require('@nextcloud/webpack-vue-config')
|
const nextcloudWebpackConfig = require('@nextcloud/webpack-vue-config')
|
||||||
const CopyPlugin = require('copy-webpack-plugin');
|
const CopyPlugin = require('copy-webpack-plugin');
|
||||||
|
|
||||||
|
function getPostCssConfig(ckEditorOpts) {
|
||||||
|
// CKEditor is not compatbile with postcss@8 and postcss-loader@4 despite stating so.
|
||||||
|
// Adapted from https://github.com/ckeditor/ckeditor5/issues/8112#issuecomment-960579351
|
||||||
|
const { plugins, ...rest } = styles.getPostCssConfig(ckEditorOpts);
|
||||||
|
return { postcssOptions: { plugins }, ...rest };
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = merge(nextcloudWebpackConfig, {
|
module.exports = merge(nextcloudWebpackConfig, {
|
||||||
entry: {
|
entry: {
|
||||||
init: path.resolve(path.join('src', 'init.js')),
|
init: path.resolve(path.join('src', 'init.js')),
|
||||||
|
|
@ -49,12 +58,14 @@ module.exports = merge(nextcloudWebpackConfig, {
|
||||||
target: 'es2020',
|
target: 'es2020',
|
||||||
},
|
},
|
||||||
exclude: BabelLoaderExcludeNodeModulesExcept([
|
exclude: BabelLoaderExcludeNodeModulesExcept([
|
||||||
|
'@ckeditor',
|
||||||
'@nextcloud/event-bus',
|
'@nextcloud/event-bus',
|
||||||
]),
|
]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(ttf|otf|eot|woff|woff2)$/,
|
test: /\.(ttf|otf|eot|woff|woff2)$/,
|
||||||
type: 'asset/inline',
|
type: 'asset/inline',
|
||||||
|
exclude: /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/,
|
||||||
},
|
},
|
||||||
// Load raw SVGs to be able to inject them via v-html
|
// Load raw SVGs to be able to inject them via v-html
|
||||||
{
|
{
|
||||||
|
|
@ -69,10 +80,38 @@ module.exports = merge(nextcloudWebpackConfig, {
|
||||||
test: /pdf\.worker(\.min)?\.mjs$/,
|
test: /pdf\.worker(\.min)?\.mjs$/,
|
||||||
type: 'asset/resource'
|
type: 'asset/resource'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
test: /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/,
|
||||||
|
type: 'asset/source'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(svg)$/i,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: 'svg-inline-loader',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
exclude: path.join(__dirname, 'node_modules', '@ckeditor'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /ckeditor5-[^/\\]+[/\\].+\.css$/,
|
||||||
|
loader: 'postcss-loader',
|
||||||
|
options: getPostCssConfig({
|
||||||
|
themeImporter: {
|
||||||
|
themePath: require.resolve('@ckeditor/ckeditor5-theme-lark'),
|
||||||
|
},
|
||||||
|
minify: true,
|
||||||
|
}),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
cache: true,
|
cache: true,
|
||||||
plugins: [
|
plugins: [
|
||||||
|
// CKEditor needs its own plugin to be built using webpack.
|
||||||
|
new CKEditorWebpackPlugin({
|
||||||
|
// See https://ckeditor.com/docs/ckeditor5/latest/features/ui-language.html
|
||||||
|
language: 'en',
|
||||||
|
}),
|
||||||
new CopyPlugin({
|
new CopyPlugin({
|
||||||
patterns: [
|
patterns: [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue