Reverted to Tiptap
parent
50600de813
commit
a28673c8e2
|
|
@ -248,6 +248,14 @@
|
||||||
<include>assets/</include>
|
<include>assets/</include>
|
||||||
</includes>
|
</includes>
|
||||||
</overlay>
|
</overlay>
|
||||||
|
<overlay>
|
||||||
|
<groupId>org.librecms</groupId>
|
||||||
|
<artifactId>ccm-cms</artifactId>
|
||||||
|
<type>jar</type>
|
||||||
|
<includes>
|
||||||
|
<include>icons/</include>
|
||||||
|
</includes>
|
||||||
|
</overlay>
|
||||||
<overlay>
|
<overlay>
|
||||||
<groupId>org.librecms</groupId>
|
<groupId>org.librecms</groupId>
|
||||||
<artifactId>ccm-cms</artifactId>
|
<artifactId>ccm-cms</artifactId>
|
||||||
|
|
|
||||||
|
|
@ -10,36 +10,6 @@
|
||||||
"integrity": "sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==",
|
"integrity": "sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@editorjs/editorjs": {
|
|
||||||
"version": "2.22.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@editorjs/editorjs/-/editorjs-2.22.2.tgz",
|
|
||||||
"integrity": "sha512-rPCv7Z5LZebreQaaL4DZuWzoVGEqwB+P7BF1dsefGQNBmLyeLF412topeW2b6e+g4l1oQ7t75kCOACNTEyYYIA==",
|
|
||||||
"requires": {
|
|
||||||
"codex-notifier": "^1.1.2",
|
|
||||||
"codex-tooltip": "^1.0.2",
|
|
||||||
"nanoid": "^3.1.22"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@editorjs/header": {
|
|
||||||
"version": "2.6.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@editorjs/header/-/header-2.6.1.tgz",
|
|
||||||
"integrity": "sha512-EsnyVFv5uThpU9tbQ/dUPFCQoa/sBFy2n+9tN3wOXJGx7sjea4fdcacJ2UYhO+7pCgZ+aSgmMOyGLYHUFbchvA=="
|
|
||||||
},
|
|
||||||
"@editorjs/nested-list": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@editorjs/nested-list/-/nested-list-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-NumQfEivI29lOAuDMyVhn+VXUDGvWUPJMkjgKlUYRbnwgnPL4tK007+UzoVPLxv/f6lPOqeKcApvCj/MfskPNw=="
|
|
||||||
},
|
|
||||||
"@editorjs/quote": {
|
|
||||||
"version": "2.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@editorjs/quote/-/quote-2.4.0.tgz",
|
|
||||||
"integrity": "sha512-IWOBWjL2ngPP63GcIAltyD9kc7OVZFma4kS+T5JRHvKKDspYsnmrxsbRmCPc+coZQzqPxXHkiOZuNMdmGX/Y3w=="
|
|
||||||
},
|
|
||||||
"@editorjs/table": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@editorjs/table/-/table-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-PB8VM+GPRwGy7IlF+WrEQw2A2c36xEXBnYIvf2VGNJo8A7PjYHtuWrlyHHCnGpY4lHXYnavZ/U8pKAfXv86XjA=="
|
|
||||||
},
|
|
||||||
"@tiptap/core": {
|
"@tiptap/core": {
|
||||||
"version": "2.0.0-beta.104",
|
"version": "2.0.0-beta.104",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.0.0-beta.104.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.0.0-beta.104.tgz",
|
||||||
|
|
@ -243,11 +213,6 @@
|
||||||
"@tiptap/extension-text": "^2.0.0-beta.13"
|
"@tiptap/extension-text": "^2.0.0-beta.13"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/editorjs__header": {
|
|
||||||
"version": "2.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/editorjs__header/-/editorjs__header-2.6.0.tgz",
|
|
||||||
"integrity": "sha512-J9TyO/BjNVddi+syyXpvMRMtVz5Z62pwmFKynWsgP+wnJYdWF8ABqgomokIsAvuEwH5NHa/YxsTltYcPPGCRRQ=="
|
|
||||||
},
|
|
||||||
"@types/eslint": {
|
"@types/eslint": {
|
||||||
"version": "7.28.0",
|
"version": "7.28.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.0.tgz",
|
||||||
|
|
@ -761,16 +726,6 @@
|
||||||
"shallow-clone": "^3.0.0"
|
"shallow-clone": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"codex-notifier": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/codex-notifier/-/codex-notifier-1.1.2.tgz",
|
|
||||||
"integrity": "sha512-DCp6xe/LGueJ1N5sXEwcBc3r3PyVkEEDNWCVigfvywAkeXcZMk9K41a31tkEFBW0Ptlwji6/JlAb49E3Yrxbtg=="
|
|
||||||
},
|
|
||||||
"codex-tooltip": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/codex-tooltip/-/codex-tooltip-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-oC+Bu5X/zyhbPydgMSLWKoM/+vkJMqaLWu3Dt/jZgXS3MWK23INwC5DMBrVXZSufAFk0i0SUni38k9rLMyZn/w=="
|
|
||||||
},
|
|
||||||
"color-convert": {
|
"color-convert": {
|
||||||
"version": "1.9.3",
|
"version": "1.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||||
|
|
@ -826,11 +781,6 @@
|
||||||
"object-keys": "^1.0.12"
|
"object-keys": "^1.0.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"editorjs-latex": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/editorjs-latex/-/editorjs-latex-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-HIfVZFy4CJpLQk6xgNUmftxzTo5p4POFFEnW3cPUAKoqcL9FhLPr3vm8Yll+qirHla1b8okNmyw8lPIfUtXLaw=="
|
|
||||||
},
|
|
||||||
"electron-to-chromium": {
|
"electron-to-chromium": {
|
||||||
"version": "1.3.830",
|
"version": "1.3.830",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.830.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.830.tgz",
|
||||||
|
|
@ -1466,11 +1416,6 @@
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nanoid": {
|
|
||||||
"version": "3.1.25",
|
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz",
|
|
||||||
"integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q=="
|
|
||||||
},
|
|
||||||
"neo-async": {
|
"neo-async": {
|
||||||
"version": "2.6.2",
|
"version": "2.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
|
||||||
|
|
|
||||||
|
|
@ -22,11 +22,6 @@
|
||||||
"webpack-cli": "^4.8.0"
|
"webpack-cli": "^4.8.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@editorjs/editorjs": "^2.22.2",
|
|
||||||
"@editorjs/header": "^2.6.1",
|
|
||||||
"@editorjs/nested-list": "^1.0.2",
|
|
||||||
"@editorjs/quote": "^2.4.0",
|
|
||||||
"@editorjs/table": "^2.0.1",
|
|
||||||
"@tiptap/core": "^2.0.0-beta.103",
|
"@tiptap/core": "^2.0.0-beta.103",
|
||||||
"@tiptap/extension-subscript": "^2.0.0-beta.4",
|
"@tiptap/extension-subscript": "^2.0.0-beta.4",
|
||||||
"@tiptap/extension-superscript": "^2.0.0-beta.4",
|
"@tiptap/extension-superscript": "^2.0.0-beta.4",
|
||||||
|
|
@ -35,11 +30,9 @@
|
||||||
"@tiptap/extension-table-header": "^2.0.0-beta.16",
|
"@tiptap/extension-table-header": "^2.0.0-beta.16",
|
||||||
"@tiptap/extension-table-row": "^2.0.0-beta.14",
|
"@tiptap/extension-table-row": "^2.0.0-beta.14",
|
||||||
"@tiptap/starter-kit": "^2.0.0-beta.102",
|
"@tiptap/starter-kit": "^2.0.0-beta.102",
|
||||||
"@types/editorjs__header": "^2.6.0",
|
|
||||||
"acorn": "^8.4.1",
|
"acorn": "^8.4.1",
|
||||||
"bootstrap": "^4.6.0",
|
"bootstrap": "^4.6.0",
|
||||||
"bootstrap-icons": "^1.5.0",
|
"bootstrap-icons": "^1.5.0",
|
||||||
"editorjs-latex": "^1.0.0",
|
|
||||||
"jquery": "^3.6.0",
|
"jquery": "^3.6.0",
|
||||||
"popper.js": "^1.16.1",
|
"popper.js": "^1.16.1",
|
||||||
"sortablejs": "^1.14.0"
|
"sortablejs": "^1.14.0"
|
||||||
|
|
|
||||||
|
|
@ -1,659 +0,0 @@
|
||||||
<!DOCTYPE html [<!ENTITY times '×'>]>
|
|
||||||
<html
|
|
||||||
xmlns="http://www.w3.org/1999/xhtml"
|
|
||||||
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
|
|
||||||
xmlns:cc="http://xmlns.jcp.org/jsf/composite"
|
|
||||||
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
|
|
||||||
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
|
|
||||||
>
|
|
||||||
<cc:interface
|
|
||||||
shortDescription="A editor component for HTML texts using the TipTap editor. To use this component you have also to include the cms-editor.js file into the page."
|
|
||||||
>
|
|
||||||
<!-- <cc:attribute
|
|
||||||
name="addMethod"
|
|
||||||
required="true"
|
|
||||||
shortDescription="URL of teh endpoint for adding localized values."
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="addButtonLabel"
|
|
||||||
default="Add"
|
|
||||||
required="false"
|
|
||||||
shortDescription="Label for the add button"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="addDialogCancelLabel"
|
|
||||||
default="#{CmsAdminMessages['cancel_button.label']}"
|
|
||||||
required="false"
|
|
||||||
shortDescription="Label for the cancel and close buttons."
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="addDialogLocaleSelectHelp"
|
|
||||||
default="The locale of the value of add"
|
|
||||||
required="false"
|
|
||||||
shortDescription="Help text for the locale select field"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="addDialogLocaleSelectLabel"
|
|
||||||
default="#{CmsAdminMessages['locale_select.label']}"
|
|
||||||
required="false"
|
|
||||||
shortDescription="Label for the locale select field"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="addDialogSubmitLabel"
|
|
||||||
default="#{CmsAdminMessages['locale_add.label']}"
|
|
||||||
required="false"
|
|
||||||
shortDescription="Label for the submit button"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="addDialogTitle"
|
|
||||||
default="#{CmsAdminMessages['locale_add.dialog.title']}"
|
|
||||||
required="false"
|
|
||||||
shortDescription="Title for the dialog."
|
|
||||||
type="String"
|
|
||||||
/>-->
|
|
||||||
<cc:attribute
|
|
||||||
name="backUrl"
|
|
||||||
required="true"
|
|
||||||
shortDescription="The back URL."
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="canEdit"
|
|
||||||
default="true"
|
|
||||||
required="false"
|
|
||||||
shortDescription="Can the current user edit the text?"
|
|
||||||
type="boolean"
|
|
||||||
/>
|
|
||||||
<!-- <cc:attribute
|
|
||||||
name="editButtonLabel"
|
|
||||||
default="#{CmsAdminMessages['edit_button.label']}"
|
|
||||||
required="false"
|
|
||||||
shortDescription="Label for the edit button"
|
|
||||||
type="String"
|
|
||||||
/>-->
|
|
||||||
<cc:attribute
|
|
||||||
name="editDialogCancelLabel"
|
|
||||||
default="#{CmsAdminMessages['cancel_button.label']}"
|
|
||||||
required="false"
|
|
||||||
shortDescription="Label for the cancel and close button of the edit dialog"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="editDialogSubmitLabel"
|
|
||||||
default="#{CmsAdminMessages['save_button.label']}"
|
|
||||||
required="false"
|
|
||||||
shortDescription="Label for the submit button of the edit dialog"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="editDialogValueHelp"
|
|
||||||
default="Value to update"
|
|
||||||
required="false"
|
|
||||||
shortDescription="Help text for the value field"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="editDialogValueLabel"
|
|
||||||
default="Value"
|
|
||||||
required="false"
|
|
||||||
shortDescription="Label for the value field"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="editDialogTitle"
|
|
||||||
default="#{CmsAdminMessages['text.edit.dialog.title']}"
|
|
||||||
required="false"
|
|
||||||
shortDescription="Title for the edit dialog"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="editMethod"
|
|
||||||
required="true"
|
|
||||||
shortDescription="URL of the endpoint for editing/updating a value. The value of the locale to update is added after the provided URL, eg. /de for updating the german value."
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<!-- <cc:attribute
|
|
||||||
name="hasUnusedLocales"
|
|
||||||
required="true"
|
|
||||||
shortDescription="Are there unused locales? This will usually be an expression pointing to some method. The result must resolve to boolean."
|
|
||||||
type="boolean"
|
|
||||||
/>-->
|
|
||||||
<cc:attribute
|
|
||||||
name="headingLevel"
|
|
||||||
default="2"
|
|
||||||
required="false"
|
|
||||||
shortDescription="Level of the heading used for the component. Also determines the heading levels used for other parts of the component."
|
|
||||||
type="int"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="editorId"
|
|
||||||
required="true"
|
|
||||||
shortDescription="ID for the editor. Also used as prefix to generate IDs for some subcomponents"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<!-- <cc:attribute
|
|
||||||
name="emptyText"
|
|
||||||
default="#{text.editor.no_localized_values}"
|
|
||||||
required="false"
|
|
||||||
shortDescription="Text shown if the localized has no values yet."
|
|
||||||
type="String"
|
|
||||||
/>-->
|
|
||||||
<cc:attribute
|
|
||||||
name="messageSaveFailed"
|
|
||||||
default="Failed to save."
|
|
||||||
required="false"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="messageSaveSuccessful"
|
|
||||||
default="Saved sucessfully."
|
|
||||||
required="false"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="messageVariantLoadFailed"
|
|
||||||
default="Failed to load variant."
|
|
||||||
required="false"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="mode"
|
|
||||||
default="full"
|
|
||||||
shortDescription="The editor mode. Use 'full' to enable all options including adding images etc. Use 'textonly' for a minimal version that only supports text editing, but does not allow insertation of images etc."
|
|
||||||
required="false"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="objectIdentifier"
|
|
||||||
required="true"
|
|
||||||
shortDescription="Identifier of the object to which the localized string belongs"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="selectedLocale"
|
|
||||||
required="true"
|
|
||||||
shortDescription="The selected locale."
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<!-- <cc:attribute
|
|
||||||
name="removeButtonLabel"
|
|
||||||
default="Remove"
|
|
||||||
required="false"
|
|
||||||
shortDescription="Label for the remove button"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="removeDialogCancelLabel"
|
|
||||||
default="Cancel"
|
|
||||||
required="false"
|
|
||||||
shortDescription="Label for the cancel and close buttons of the remove dialog"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="removeDialogSubmitLabel"
|
|
||||||
default="Remove localized value"
|
|
||||||
required="false"
|
|
||||||
shortDescription="Label for the submit button of the remove dialog (removes the localized value)"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="removeDialogText"
|
|
||||||
default="Are you sure to remove the following localized value?"
|
|
||||||
required="false"
|
|
||||||
shortDescription="Text for the remove dialog"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="removeDialogTitle"
|
|
||||||
default="Remove localized value"
|
|
||||||
required="false"
|
|
||||||
shortDescription="The title of the remove dialog"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="removeMethod"
|
|
||||||
required="true"
|
|
||||||
shortDescription="URL of the endpoint for remvoving a value. The locale of the value to remove is added to the end of the URL. Eg. /de for removing the value for the locale de."
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="tableActionsHeading"
|
|
||||||
default="Actions"
|
|
||||||
required="false"
|
|
||||||
shortDescription="Heading for the action columns"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="tableLocaleHeading"
|
|
||||||
default="Locale"
|
|
||||||
required="false"
|
|
||||||
shortDescription="Heading for the locale column"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="tableWordCountHeading"
|
|
||||||
default="Words"
|
|
||||||
required="false"
|
|
||||||
shortDescription="Heading for the word count column"
|
|
||||||
type="String"
|
|
||||||
/>-->
|
|
||||||
<cc:attribute
|
|
||||||
name="title"
|
|
||||||
required="true"
|
|
||||||
shortDescription="Title/Heading of the editor widget"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<!-- <cc:attribute
|
|
||||||
name="unusedLocales"
|
|
||||||
required="true"
|
|
||||||
shortDescription="A collection of the unused locales of the edited localized string"
|
|
||||||
type="java.util.Collection"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="variants"
|
|
||||||
required="true"
|
|
||||||
shortDescription="Info about the available variants. Must be a List of CmsEditorLocaleVariantRow objects."
|
|
||||||
type="java.util.List"
|
|
||||||
/>-->
|
|
||||||
<cc:attribute
|
|
||||||
name="variantUrl"
|
|
||||||
required="true"
|
|
||||||
shortDescription="URL of the endpoint for retrieving a variant. The locale of the variant to retrieve is appended as last token."
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<!-- <cc:attribute name="viewButtonLabel" default="View" type="String" />
|
|
||||||
<cc:attribute name="viewDialogTitle" default="View" type="String" />
|
|
||||||
<cc:attribute
|
|
||||||
name="viewDialogEditButtonLabel"
|
|
||||||
default="Edit"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="viewDialogCloseButtonLabel"
|
|
||||||
default="Close"
|
|
||||||
type="String"
|
|
||||||
/>
|
|
||||||
<cc:attribute
|
|
||||||
name="wordCountUrl"
|
|
||||||
required="true"
|
|
||||||
shortDescription="URL of the endpoint for retrieving the wordcount of a variant. The locale of the variant to retrieve is appended as last token."
|
|
||||||
type="String"
|
|
||||||
/>-->
|
|
||||||
</cc:interface>
|
|
||||||
<cc:implementation>
|
|
||||||
<div
|
|
||||||
class="cms-editor"
|
|
||||||
data-locale="#{cc.attrs.selectedLocale}"
|
|
||||||
data-save-url="#{cc.attrs.editMethod}/#{variant.locale}"
|
|
||||||
data-variant-url="#{cc.attrs.variantUrl}/#{cc.attrs.selectedLocale}"
|
|
||||||
id="#{cc.attrs.editorId}"
|
|
||||||
>
|
|
||||||
<c:choose>
|
|
||||||
<c:when test="#{cc.attrs.headingLevel == 1}">
|
|
||||||
<h1>#{cc.attrs.title}</h1>
|
|
||||||
</c:when>
|
|
||||||
<c:when test="#{cc.attrs.headingLevel == 2}">
|
|
||||||
<h2>#{cc.attrs.title}</h2>
|
|
||||||
</c:when>
|
|
||||||
<c:when test="#{cc.attrs.headingLevel == 3}">
|
|
||||||
<h3>#{cc.attrs.title}</h3>
|
|
||||||
</c:when>
|
|
||||||
<c:when test="#{cc.attrs.headingLevel == 4}">
|
|
||||||
<h4>#{cc.attrs.title}</h4>
|
|
||||||
</c:when>
|
|
||||||
<c:when test="#{cc.attrs.headingLevel == 5}">
|
|
||||||
<h5>#{cc.attrs.title}</h5>
|
|
||||||
</c:when>
|
|
||||||
<c:when test="#{cc.attrs.headingLevel == 6}">
|
|
||||||
<h6>#{cc.attrs.title}</h6>
|
|
||||||
</c:when>
|
|
||||||
<c:otherwise>
|
|
||||||
<div>#{cc.attrs.title}</div>
|
|
||||||
</c:otherwise>
|
|
||||||
</c:choose>
|
|
||||||
<div class="cms-editor-messages">
|
|
||||||
<template id="cms-editor-msg-canvas-element-not-found">
|
|
||||||
<div class="alert alert alert-danger" role="alert">
|
|
||||||
#{CmsAdminMessages['cms_editor.internal_error']}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template id="cms-editor-msg-save-failed">
|
|
||||||
<div class="alert alert alert-danger" role="alert">
|
|
||||||
#{cc.attrs.messageSaveFailed}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template id="cms-editor-msg-save-successful">
|
|
||||||
<div class="alert alert-success" role="alert">
|
|
||||||
#{cc.attrs.messageSaveSuccessful}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template id="cms-editor-msg-variant-load-failed">
|
|
||||||
<div class="alert alert-warning" role="alert">
|
|
||||||
#{cc.attrs.messageVariantLoadFailed}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<c:choose>
|
|
||||||
<c:when test="#{cc.attrs.headingLevel == 1}">
|
|
||||||
<h2 id="#{cc.attrs.editorId}-edit-dialog-title">
|
|
||||||
#{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale}
|
|
||||||
</h2>
|
|
||||||
</c:when>
|
|
||||||
<c:when test="#{cc.attrs.headingLevel == 2}">
|
|
||||||
<h3 id="#{cc.attrs.editorId}-edit-dialog-title">
|
|
||||||
#{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale}
|
|
||||||
</h3>
|
|
||||||
</c:when>
|
|
||||||
<c:when test="#{cc.attrs.headingLevel == 3}">
|
|
||||||
<h4 id="#{cc.attrs.editorId}-edit-dialog-title">
|
|
||||||
#{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale}
|
|
||||||
</h4>
|
|
||||||
</c:when>
|
|
||||||
<c:when test="#{cc.attrs.headingLevel == 4}">
|
|
||||||
<h5 id="#{cc.attrs.editorId}-edit-dialog-title">
|
|
||||||
#{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale}
|
|
||||||
</h5>
|
|
||||||
</c:when>
|
|
||||||
<c:when test="#{cc.attrs.headingLevel == 5}">
|
|
||||||
<h6 id="#{cc.attrs.editorId}-edit-dialog-title">
|
|
||||||
#{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale}
|
|
||||||
</h6>
|
|
||||||
</c:when>
|
|
||||||
<c:otherwise>
|
|
||||||
<div id="#{cc.attrs.editorId}-edit-dialog-title">
|
|
||||||
#{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale}
|
|
||||||
</div>
|
|
||||||
</c:otherwise>
|
|
||||||
</c:choose>
|
|
||||||
<div
|
|
||||||
class="cms-tiptap-editor"
|
|
||||||
data-locale="#{cc.attrs.selectedLocale}"
|
|
||||||
data-variant-url="#{cc.attrs.variantUrl}/#{cc.attrs.selectedLocale}"
|
|
||||||
>
|
|
||||||
<div class="cms-tiptap-editor-buttons mb-1">
|
|
||||||
<div class="px-2 cms-tiptap-editor-button-row cms-tiptap-editor-textformatting">
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-emph"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.emph']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<bootstrap:svgIcon icon="type-italic" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-strong-emph"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.strong_emph']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<bootstrap:svgIcon icon="type-bold" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-code"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.code']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<bootstrap:svgIcon icon="code" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-strikethrough"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.strikethrough']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<bootstrap:svgIcon icon="type-strikethrough" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-subscript"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.subscript']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<span aria-hidden="true">x<sub>n</sub></span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-superscript"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.superscript']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<span aria-hidden="true">x<sup>n</sup></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="px-2 cms-tiptap-editor-button-row cms-tiptap-editor-headings">
|
|
||||||
<ui:repeat begin="1" end="6" var="level">
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-h#{level}"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.h'.concat(level)]}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<span aria-hidden="true">H#{level}</span>
|
|
||||||
</button>
|
|
||||||
</ui:repeat>
|
|
||||||
</div>
|
|
||||||
<div class="px-2 cms-tiptap-editor-button-row cms-tiptap-editor-paragraphs">
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-paragraph"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.paragraph']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<bootstrap:svgIcon icon="type" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-blockquote"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.blockquote']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<bootstrap:svgIcon icon="blockquote-left" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-codeblock"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.codeblock']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<bootstrap:svgIcon icon="code-square" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="px-2 cms-tiptap-editor-button-row cms-tiptap-editor-lists">
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-ul"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.ul']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<bootstrap:svgIcon icon="list-ul" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-ol"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.ol']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<bootstrap:svgIcon icon="list-ol" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="px-2 cms-tiptap-editor-button-row cms-tiptap-editor-table">
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-insert-table"
|
|
||||||
data-target="#insert-table-dialog"
|
|
||||||
data-toggle="modal"
|
|
||||||
title="#{CmsAdminMessage['cms_editor.buttons.insert_table']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<bootstrap:svgIcon icon="table" />
|
|
||||||
</button>
|
|
||||||
<div
|
|
||||||
aria-hidden="true"
|
|
||||||
aria-labelledby="insert-table-dialog-title"
|
|
||||||
class="modal fade cms-editor-insert-table-dialog"
|
|
||||||
id="insert-table-dialog"
|
|
||||||
tabindex="-1"
|
|
||||||
>
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<form class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h4
|
|
||||||
class="modal-title"
|
|
||||||
id="insert-table-dialog-title"
|
|
||||||
>
|
|
||||||
#{CmsAdminMessages['cms_editor.dialogs.insert_table.title']}
|
|
||||||
</h4>
|
|
||||||
<button
|
|
||||||
aria-label="#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.close']}"
|
|
||||||
class="close"
|
|
||||||
data-dismiss="modal"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<bootstrap:svgIcon icon="x" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<bootstrap:formGroupNumber
|
|
||||||
help="#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.rows.help']}"
|
|
||||||
inputId="rows"
|
|
||||||
label="#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.rows.label']}"
|
|
||||||
name="rows"
|
|
||||||
/>
|
|
||||||
<bootstrap:formGroupNumber
|
|
||||||
help="#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.cols.help']}"
|
|
||||||
inputId="cols"
|
|
||||||
label="#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.cols.label']}"
|
|
||||||
name="cols"
|
|
||||||
/>
|
|
||||||
<bootstrap:formCheck
|
|
||||||
checked="true"
|
|
||||||
inputId="headerRow"
|
|
||||||
label="#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.header_row.label']}"
|
|
||||||
name="headerRow"
|
|
||||||
value="true"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button
|
|
||||||
class="
|
|
||||||
btn btn-warning
|
|
||||||
cms-editor-cancel-button
|
|
||||||
"
|
|
||||||
data-dismiss="modal"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.close']}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-success"
|
|
||||||
data-dismiss="modal"
|
|
||||||
data-backdrop="false"
|
|
||||||
type="submit"
|
|
||||||
>
|
|
||||||
#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.submit']}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-remove-table"
|
|
||||||
title="#{CmsAdminMessage['cms_editor.buttons.remove_table']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<img src="#{request.contextPath}/icons/cms-editor/table-remove.svg" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-insert-table-row-before"
|
|
||||||
title="#{CmsAdminMessage['cms_editor.buttons.insert_table_row_before']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<img src="#{request.contextPath}/icons/cms-editor/table-add-row-before.svg" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-insert-table-row-after"
|
|
||||||
title="#{CmsAdminMessage['cms_editor.buttons.insert_table_row_after']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<img src="#{request.contextPath}/icons/cms-editor/table-add-row-after.svg" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-table-remove-row"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.remove_table_row']}"
|
|
||||||
type="button">
|
|
||||||
<img src="#{request.contextPath}/icons/cms-editor/table-remove-row.svg" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-insert-table-column-before"
|
|
||||||
title="#{CmsAdminMessage['cms_editor.buttons.insert_table_column_before']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<img src="#{request.contextPath}/icons/cms-editor/table-add-column-before.svg" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-insert-table-column-after"
|
|
||||||
title="#{CmsAdminMessage['cms_editor.buttons.insert_table_column_after']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<img src="#{request.contextPath}/icons/cms-editor/table-add-column-after.svg" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-remove-table-column"
|
|
||||||
title="#{CmsAdminMessage['cms_editor.buttons.remove_table_column']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<img src="#{request.contextPath}/icons/cms-editor/table-remove-column.svg" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-toggle-table-header-row"
|
|
||||||
title="#{CmsAdminMessage['cms_editor.buttons.toggle_header_row']}"
|
|
||||||
type="button">
|
|
||||||
<img src="#{request.contextPath}/icons/cms-editor/table-toggle-header-row.svg" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-toggle-table-header-column"
|
|
||||||
title="#{CmsAdminMessage['cms_editor.buttons.toggle_header_column']}"
|
|
||||||
type="button">
|
|
||||||
<img src="#{request.contextPath}/icons/cms-editor/table-toggle-header-column.svg" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-merge-table-cells"
|
|
||||||
title="#{CmsAdminMessage['cms_editor.buttons.merge-table-cells']}"
|
|
||||||
type="button">
|
|
||||||
<img src="#{request.contextPath}/icons/cms-editor/table-merge-cells.svg" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-split-table-cell"
|
|
||||||
title="#{CmsAdminMessage['cms_editor.buttons.split-table-cell']}"
|
|
||||||
type="button">
|
|
||||||
<img src="#{request.contextPath}/icons/cms-editor/table-split-cells.svg" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="px-2 cms-tiptap-editor-button-row cms-tiptap-editor-media">
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-insert-image"
|
|
||||||
title="#{CmsAdminMessage['cms_editor.buttons.insert_image']}"
|
|
||||||
type="button">
|
|
||||||
<bootstrap:svgIcon icon="image" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cms-tiptap-editor-canvas border"
|
|
||||||
data-locale="#{cc.attrs.selectedLocale}"
|
|
||||||
data-variant-url="#{cc.attrs.variantUrl}"
|
|
||||||
></div>
|
|
||||||
<div id="html-preview"></div>
|
|
||||||
<div class="mt-3">
|
|
||||||
<a
|
|
||||||
class="btn btn-warning cms-editor-cancel-button"
|
|
||||||
href="#{backUrl}"
|
|
||||||
>
|
|
||||||
#{cc.attrs.editDialogCancelLabel}
|
|
||||||
</a>
|
|
||||||
<button
|
|
||||||
class="btn btn-success cms-editor-save-button"
|
|
||||||
disabled="#{cc.attrs.canEdit ? '' : 'disabled'}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
#{cc.attrs.editDialogSubmitLabel}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</cc:implementation>
|
|
||||||
</html>
|
|
||||||
|
|
@ -293,22 +293,358 @@
|
||||||
data-save-url="#{cc.attrs.editMethod}/#{variant.locale}"
|
data-save-url="#{cc.attrs.editMethod}/#{variant.locale}"
|
||||||
data-variant-url="#{cc.attrs.variantUrl}/#{cc.attrs.selectedLocale}"
|
data-variant-url="#{cc.attrs.variantUrl}/#{cc.attrs.selectedLocale}"
|
||||||
id="#{cc.attrs.editorId}"
|
id="#{cc.attrs.editorId}"
|
||||||
></div>
|
>
|
||||||
<div id="editor-preview"></div>
|
<c:choose>
|
||||||
<div class="mt-3">
|
<c:when test="#{cc.attrs.headingLevel == 1}">
|
||||||
<a
|
<h1>#{cc.attrs.title}</h1>
|
||||||
class="btn btn-warning cms-editor-cancel-button"
|
</c:when>
|
||||||
href="#{backUrl}"
|
<c:when test="#{cc.attrs.headingLevel == 2}">
|
||||||
|
<h2>#{cc.attrs.title}</h2>
|
||||||
|
</c:when>
|
||||||
|
<c:when test="#{cc.attrs.headingLevel == 3}">
|
||||||
|
<h3>#{cc.attrs.title}</h3>
|
||||||
|
</c:when>
|
||||||
|
<c:when test="#{cc.attrs.headingLevel == 4}">
|
||||||
|
<h4>#{cc.attrs.title}</h4>
|
||||||
|
</c:when>
|
||||||
|
<c:when test="#{cc.attrs.headingLevel == 5}">
|
||||||
|
<h5>#{cc.attrs.title}</h5>
|
||||||
|
</c:when>
|
||||||
|
<c:when test="#{cc.attrs.headingLevel == 6}">
|
||||||
|
<h6>#{cc.attrs.title}</h6>
|
||||||
|
</c:when>
|
||||||
|
<c:otherwise>
|
||||||
|
<div>#{cc.attrs.title}</div>
|
||||||
|
</c:otherwise>
|
||||||
|
</c:choose>
|
||||||
|
<div class="cms-editor-messages">
|
||||||
|
<template id="cms-editor-msg-canvas-element-not-found">
|
||||||
|
<div class="alert alert alert-danger" role="alert">
|
||||||
|
#{CmsAdminMessages['cms_editor.internal_error']}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template id="cms-editor-msg-save-failed">
|
||||||
|
<div class="alert alert alert-danger" role="alert">
|
||||||
|
#{cc.attrs.messageSaveFailed}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template id="cms-editor-msg-save-successful">
|
||||||
|
<div class="alert alert-success" role="alert">
|
||||||
|
#{cc.attrs.messageSaveSuccessful}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template id="cms-editor-msg-variant-load-failed">
|
||||||
|
<div class="alert alert-warning" role="alert">
|
||||||
|
#{cc.attrs.messageVariantLoadFailed}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<c:choose>
|
||||||
|
<c:when test="#{cc.attrs.headingLevel == 1}">
|
||||||
|
<h2 id="#{cc.attrs.editorId}-edit-dialog-title">
|
||||||
|
#{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale}
|
||||||
|
</h2>
|
||||||
|
</c:when>
|
||||||
|
<c:when test="#{cc.attrs.headingLevel == 2}">
|
||||||
|
<h3 id="#{cc.attrs.editorId}-edit-dialog-title">
|
||||||
|
#{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale}
|
||||||
|
</h3>
|
||||||
|
</c:when>
|
||||||
|
<c:when test="#{cc.attrs.headingLevel == 3}">
|
||||||
|
<h4 id="#{cc.attrs.editorId}-edit-dialog-title">
|
||||||
|
#{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale}
|
||||||
|
</h4>
|
||||||
|
</c:when>
|
||||||
|
<c:when test="#{cc.attrs.headingLevel == 4}">
|
||||||
|
<h5 id="#{cc.attrs.editorId}-edit-dialog-title">
|
||||||
|
#{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale}
|
||||||
|
</h5>
|
||||||
|
</c:when>
|
||||||
|
<c:when test="#{cc.attrs.headingLevel == 5}">
|
||||||
|
<h6 id="#{cc.attrs.editorId}-edit-dialog-title">
|
||||||
|
#{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale}
|
||||||
|
</h6>
|
||||||
|
</c:when>
|
||||||
|
<c:otherwise>
|
||||||
|
<div id="#{cc.attrs.editorId}-edit-dialog-title">
|
||||||
|
#{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale}
|
||||||
|
</div>
|
||||||
|
</c:otherwise>
|
||||||
|
</c:choose>
|
||||||
|
<div
|
||||||
|
class="cms-tiptap-editor"
|
||||||
|
data-locale="#{cc.attrs.selectedLocale}"
|
||||||
|
data-variant-url="#{cc.attrs.variantUrl}/#{cc.attrs.selectedLocale}"
|
||||||
>
|
>
|
||||||
#{cc.attrs.editDialogCancelLabel}
|
<div class="cms-tiptap-editor-buttons mb-1">
|
||||||
</a>
|
<div class="px-2 cms-tiptap-editor-button-row cms-tiptap-editor-textformatting">
|
||||||
<button
|
<button
|
||||||
class="btn btn-success cms-editor-save-button"
|
class="btn btn-outline-dark tiptap-emph"
|
||||||
disabled="#{cc.attrs.canEdit ? '' : 'disabled'}"
|
title="#{CmsAdminMessages['cms_editor.buttons.emph']}"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
#{cc.attrs.editDialogSubmitLabel}
|
<bootstrap:svgIcon icon="type-italic" />
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-strong-emph"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.strong_emph']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<bootstrap:svgIcon icon="type-bold" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-code"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.code']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<bootstrap:svgIcon icon="code" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-strikethrough"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.strikethrough']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<bootstrap:svgIcon icon="type-strikethrough" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-subscript"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.subscript']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<span aria-hidden="true">x<sub>n</sub></span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-superscript"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.superscript']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<span aria-hidden="true">x<sup>n</sup></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="px-2 cms-tiptap-editor-button-row cms-tiptap-editor-headings">
|
||||||
|
<ui:repeat begin="1" end="6" var="level">
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-h#{level}"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.h'.concat(level)]}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<span aria-hidden="true">H#{level}</span>
|
||||||
|
</button>
|
||||||
|
</ui:repeat>
|
||||||
|
</div>
|
||||||
|
<div class="px-2 cms-tiptap-editor-button-row cms-tiptap-editor-paragraphs">
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-paragraph"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.paragraph']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<bootstrap:svgIcon icon="type" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-blockquote"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.blockquote']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<bootstrap:svgIcon icon="blockquote-left" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-codeblock"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.codeblock']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<bootstrap:svgIcon icon="code-square" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="px-2 cms-tiptap-editor-button-row cms-tiptap-editor-lists">
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-ul"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.ul']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<bootstrap:svgIcon icon="list-ul" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-ol"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.ol']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<bootstrap:svgIcon icon="list-ol" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="px-2 cms-tiptap-editor-button-row cms-tiptap-editor-table">
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-insert-table"
|
||||||
|
data-target="#insert-table-dialog"
|
||||||
|
data-toggle="modal"
|
||||||
|
title="#{CmsAdminMessage['cms_editor.buttons.insert_table']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<bootstrap:svgIcon icon="table" />
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
aria-hidden="true"
|
||||||
|
aria-labelledby="insert-table-dialog-title"
|
||||||
|
class="modal fade cms-editor-insert-table-dialog"
|
||||||
|
id="insert-table-dialog"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<form class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4
|
||||||
|
class="modal-title"
|
||||||
|
id="insert-table-dialog-title"
|
||||||
|
>
|
||||||
|
#{CmsAdminMessages['cms_editor.dialogs.insert_table.title']}
|
||||||
|
</h4>
|
||||||
|
<button
|
||||||
|
aria-label="#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.close']}"
|
||||||
|
class="close"
|
||||||
|
data-dismiss="modal"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<bootstrap:svgIcon icon="x" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<bootstrap:formGroupNumber
|
||||||
|
help="#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.rows.help']}"
|
||||||
|
inputId="rows"
|
||||||
|
label="#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.rows.label']}"
|
||||||
|
name="rows"
|
||||||
|
/>
|
||||||
|
<bootstrap:formGroupNumber
|
||||||
|
help="#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.cols.help']}"
|
||||||
|
inputId="cols"
|
||||||
|
label="#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.cols.label']}"
|
||||||
|
name="cols"
|
||||||
|
/>
|
||||||
|
<bootstrap:formCheck
|
||||||
|
checked="true"
|
||||||
|
inputId="headerRow"
|
||||||
|
label="#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.header_row.label']}"
|
||||||
|
name="headerRow"
|
||||||
|
value="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button
|
||||||
|
class="
|
||||||
|
btn btn-warning
|
||||||
|
cms-editor-cancel-button
|
||||||
|
"
|
||||||
|
data-dismiss="modal"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.close']}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-success"
|
||||||
|
data-dismiss="modal"
|
||||||
|
data-backdrop="false"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.submit']}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-remove-table"
|
||||||
|
title="#{CmsAdminMessage['cms_editor.buttons.remove_table']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<img src="#{request.contextPath}/icons/cms-editor/table-remove.svg" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-insert-table-row-before"
|
||||||
|
title="#{CmsAdminMessage['cms_editor.buttons.insert_table_row_before']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<img src="#{request.contextPath}/icons/cms-editor/table-add-row-before.svg" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-insert-table-row-after"
|
||||||
|
title="#{CmsAdminMessage['cms_editor.buttons.insert_table_row_after']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<img src="#{request.contextPath}/icons/cms-editor/table-add-row-after.svg" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-table-remove-row"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.remove_table_row']}"
|
||||||
|
type="button">
|
||||||
|
<img src="#{request.contextPath}/icons/cms-editor/table-remove-row.svg" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-insert-table-column-before"
|
||||||
|
title="#{CmsAdminMessage['cms_editor.buttons.insert_table_column_before']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<img src="#{request.contextPath}/icons/cms-editor/table-add-column-before.svg" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-insert-table-column-after"
|
||||||
|
title="#{CmsAdminMessage['cms_editor.buttons.insert_table_column_after']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<img src="#{request.contextPath}/icons/cms-editor/table-add-column-after.svg" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-remove-table-column"
|
||||||
|
title="#{CmsAdminMessage['cms_editor.buttons.remove_table_column']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<img src="#{request.contextPath}/icons/cms-editor/table-remove-column.svg" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-toggle-table-header-row"
|
||||||
|
title="#{CmsAdminMessage['cms_editor.buttons.toggle_header_row']}"
|
||||||
|
type="button">
|
||||||
|
<img src="#{request.contextPath}/icons/cms-editor/table-toggle-header-row.svg" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-toggle-table-header-column"
|
||||||
|
title="#{CmsAdminMessage['cms_editor.buttons.toggle_header_column']}"
|
||||||
|
type="button">
|
||||||
|
<img src="#{request.contextPath}/icons/cms-editor/table-toggle-header-column.svg" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-merge-table-cells"
|
||||||
|
title="#{CmsAdminMessage['cms_editor.buttons.merge-table-cells']}"
|
||||||
|
type="button">
|
||||||
|
<img src="#{request.contextPath}/icons/cms-editor/table-merge-cells.svg" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-split-table-cell"
|
||||||
|
title="#{CmsAdminMessage['cms_editor.buttons.split-table-cell']}"
|
||||||
|
type="button">
|
||||||
|
<img src="#{request.contextPath}/icons/cms-editor/table-split-cells.svg" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="cms-tiptap-editor-canvas border"
|
||||||
|
data-locale="#{cc.attrs.selectedLocale}"
|
||||||
|
data-variant-url="#{cc.attrs.variantUrl}"
|
||||||
|
></div>
|
||||||
|
<div class="mt-3">
|
||||||
|
<a
|
||||||
|
class="btn btn-warning cms-editor-cancel-button"
|
||||||
|
href="#{backUrl}"
|
||||||
|
>
|
||||||
|
#{cc.attrs.editDialogCancelLabel}
|
||||||
|
</a>
|
||||||
|
<button
|
||||||
|
class="btn btn-success cms-editor-save-button"
|
||||||
|
disabled="#{cc.attrs.canEdit ? '' : 'disabled'}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
#{cc.attrs.editDialogSubmitLabel}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</cc:implementation>
|
</cc:implementation>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -30,37 +30,11 @@
|
||||||
/>
|
/>
|
||||||
</c:if>
|
</c:if>
|
||||||
|
|
||||||
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">
|
|
||||||
Launch demo modal
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Modal -->
|
|
||||||
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
|
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
...
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
|
||||||
<button type="button" class="btn btn-primary">Save changes</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</ui:define>
|
</ui:define>
|
||||||
|
|
||||||
<ui:define name="scripts">
|
<ui:define name="scripts">
|
||||||
<script src="#{request.contextPath}/assets/@content-sections/article-text-step.js"></script>
|
<script src="#{request.contextPath}/assets/@content-sections/article-text-step.js"></script>
|
||||||
</ui:define>
|
</ui:define>
|
||||||
|
|
||||||
</ui:composition>
|
</ui:composition>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
import EditorJS from "@editorjs/editorjs";
|
|
||||||
import Header from "@editorjs/header";
|
|
||||||
import NestedList from "@editorjs/nested-list";
|
|
||||||
import Quote from "@editorjs/quote";
|
|
||||||
import Table from "@editorjs/table";
|
|
||||||
import { CmsImage } from "./ccm-cms-editorjs-image/ccm-cms-editorjs-image";
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", event => {
|
|
||||||
const editor = new EditorJS({
|
|
||||||
data: {
|
|
||||||
blocks: [
|
|
||||||
{
|
|
||||||
type: "header",
|
|
||||||
data: {
|
|
||||||
text: "Editor.js test",
|
|
||||||
level: 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "paragraph",
|
|
||||||
data: {
|
|
||||||
text: "Lorem ipsum"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
holder: "cms-article-text-editor",
|
|
||||||
tools: {
|
|
||||||
image: {
|
|
||||||
class: CmsImage,
|
|
||||||
inlineToolbar: true
|
|
||||||
},
|
|
||||||
header: Header,
|
|
||||||
list: {
|
|
||||||
class: NestedList,
|
|
||||||
inlineToolbar: true
|
|
||||||
},
|
|
||||||
quote: Quote,
|
|
||||||
table: Table
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const saveButton = document.querySelector(".cms-editor-save-button");
|
|
||||||
if (saveButton) {
|
|
||||||
saveButton.addEventListener("click", event => {
|
|
||||||
event.preventDefault();
|
|
||||||
const preview = document.querySelector("#editor-preview");
|
|
||||||
if (preview) {
|
|
||||||
editor.save().then(outputData => {
|
|
||||||
preview.innerHTML = JSON.stringify(outputData);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { CmsEditorBuilder, CmsEditor } from "./cms-editor";
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", event => {
|
||||||
|
const editorElem = document.querySelector("#cms-article-text-editor");
|
||||||
|
|
||||||
|
if (editorElem) {
|
||||||
|
const saveUrl = editorElem.getAttribute("data-save-url");
|
||||||
|
const variantUrl = editorElem.getAttribute("data-variant-url");
|
||||||
|
|
||||||
|
if (!saveUrl) {
|
||||||
|
console.error("saveUrl is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!variantUrl) {
|
||||||
|
console.error("variantUrl is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const builder = new CmsEditorBuilder(
|
||||||
|
editorElem as HTMLElement,
|
||||||
|
saveUrl,
|
||||||
|
variantUrl
|
||||||
|
);
|
||||||
|
|
||||||
|
builder.buildEditor();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
// import { CmsEditorBuilder, CmsEditor } from "./cms-editor";
|
|
||||||
|
|
||||||
// const editorElem = document.querySelector("#cms-article-text-editor");
|
|
||||||
|
|
||||||
// if (editorElem) {
|
|
||||||
// const saveUrl = editorElem.getAttribute("data-save-url");
|
|
||||||
// const variantUrl = editorElem.getAttribute("data-variant-url");
|
|
||||||
|
|
||||||
// if (!saveUrl) {
|
|
||||||
// console.error("saveUrl is null");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (!variantUrl) {
|
|
||||||
// console.error("variantUrl is null");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const builder = new CmsEditorBuilder(
|
|
||||||
// editorElem as HTMLElement,
|
|
||||||
// saveUrl,
|
|
||||||
// variantUrl
|
|
||||||
// );
|
|
||||||
|
|
||||||
// builder.buildEditor();
|
|
||||||
// }
|
|
||||||
});
|
|
||||||
|
|
@ -1,131 +0,0 @@
|
||||||
import * as jquery from "jquery";
|
|
||||||
|
|
||||||
export class CmsImage {
|
|
||||||
static get toolbox() {
|
|
||||||
return {
|
|
||||||
title: "Image",
|
|
||||||
icon: "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-image\" viewBox=\"0 0 16 16\"><path d=\"M6.002 5.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z\"/><path d=\"M2.002 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2h-12zm12 1a1 1 0 0 1 1 1v6.5l-3.777-1.947a.5.5 0 0 0-.577.093l-3.71 3.71-2.66-1.772a.5.5 0 0 0-.63.062L1.002 12V3a1 1 0 0 1 1-1h12z\"/></svg>"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public render() {
|
|
||||||
const figureElem = document.createElement("figure");
|
|
||||||
|
|
||||||
const imgElem = document.createElement("img");
|
|
||||||
imgElem.src = "data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-image\" viewBox=\"0 0 16 16\"><path d=\"M6.002 5.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z\"/><path d=\"M2.002 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2h-12zm12 1a1 1 0 0 1 1 1v6.5l-3.777-1.947a.5.5 0 0 0-.577.093l-3.71 3.71-2.66-1.772a.5.5 0 0 0-.63.062L1.002 12V3a1 1 0 0 1 1-1h12z\"/></svg>";
|
|
||||||
imgElem.setAttribute("style", "width: 100%");
|
|
||||||
figureElem.appendChild(imgElem);
|
|
||||||
|
|
||||||
const captionElem = document.createElement("figcaption");
|
|
||||||
captionElem.contentEditable = "true";
|
|
||||||
captionElem.innerHTML = "<i>Caption placeholder</i>"
|
|
||||||
figureElem.appendChild(captionElem);
|
|
||||||
|
|
||||||
return figureElem;
|
|
||||||
}
|
|
||||||
|
|
||||||
public renderSettings() {
|
|
||||||
const updateImageButton = document.createElement("button");
|
|
||||||
updateImageButton.title = "Update image";
|
|
||||||
updateImageButton.classList.add("cdx-settings-button");
|
|
||||||
updateImageButton.innerHTML = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-image\" viewBox=\"0 0 16 16\"><path d=\"M6.002 5.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z\"/><path d=\"M2.002 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2h-12zm12 1a1 1 0 0 1 1 1v6.5l-3.777-1.947a.5.5 0 0 0-.577.093l-3.71 3.71-2.66-1.772a.5.5 0 0 0-.63.062L1.002 12V3a1 1 0 0 1 1-1h12z\"/></svg>";
|
|
||||||
updateImageButton.addEventListener("click", (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
this.updateImage();
|
|
||||||
});
|
|
||||||
|
|
||||||
const settings = document.createElement("div");
|
|
||||||
settings.appendChild(updateImageButton);
|
|
||||||
|
|
||||||
return settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
public save(blockContent: any) {
|
|
||||||
return {
|
|
||||||
url: blockContent.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateImage() {
|
|
||||||
(jquery("#exampleModal") as any).modal("toggle");
|
|
||||||
|
|
||||||
const modalElem = document.createElement("div");
|
|
||||||
modalElem.classList.add("modal");
|
|
||||||
modalElem.classList.add("fade");
|
|
||||||
modalElem.id = "cms-editor-image-dialog";
|
|
||||||
modalElem.setAttribute("aria-labelledby", "cms-editor-image-dialog-title");
|
|
||||||
// modalElem.setAttribute("data-show", "true");
|
|
||||||
|
|
||||||
const modalDialogElem = document.createElement("div");
|
|
||||||
modalDialogElem.classList.add("modal-dialog");
|
|
||||||
modalElem.appendChild(modalDialogElem);
|
|
||||||
|
|
||||||
const modalContentElem = document.createElement("div");
|
|
||||||
modalContentElem.classList.add("modal-content");
|
|
||||||
modalDialogElem.appendChild(modalContentElem);
|
|
||||||
|
|
||||||
const modalHeaderElem = document.createElement("div");
|
|
||||||
modalHeaderElem.classList.add("modal-header");
|
|
||||||
modalContentElem.appendChild(modalHeaderElem);
|
|
||||||
|
|
||||||
const modalTitleElem = document.createElement("h3");
|
|
||||||
modalTitleElem.classList.add("modal-title");
|
|
||||||
modalTitleElem.id = "cms-editor-image-dialog-title";
|
|
||||||
modalTitleElem.textContent = "Update image";
|
|
||||||
modalHeaderElem.appendChild(modalTitleElem);
|
|
||||||
|
|
||||||
const modalHeaderCloseButton = document.createElement("button");
|
|
||||||
modalHeaderCloseButton.classList.add("close");
|
|
||||||
modalHeaderCloseButton.setAttribute("aria-label", "Cancel");
|
|
||||||
modalHeaderCloseButton.type = "button";
|
|
||||||
modalHeaderCloseButton.innerHTML = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-x-circle\" viewBox=\"0 0 16 16\"><path d=\"M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z\"/><path d=\"M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z\"/></svg>";
|
|
||||||
modalHeaderElem.appendChild(modalHeaderCloseButton);
|
|
||||||
|
|
||||||
const modalBodyElem = document.createElement("div");
|
|
||||||
modalBodyElem.classList.add("modal-body");
|
|
||||||
modalBodyElem.innerHTML = "<i>Update image placeholder</i>";
|
|
||||||
modalContentElem.appendChild(modalBodyElem);
|
|
||||||
|
|
||||||
const modalFooterElem = document.createElement("div");
|
|
||||||
const cancelButtonElem = document.createElement("button");
|
|
||||||
cancelButtonElem.classList.add("btn");
|
|
||||||
cancelButtonElem.classList.add("btn-warning");
|
|
||||||
cancelButtonElem.type = "button";
|
|
||||||
const saveButtonElem = document.createElement("button");
|
|
||||||
saveButtonElem.classList.add("btn");
|
|
||||||
saveButtonElem.classList.add("btn-success");
|
|
||||||
modalFooterElem.appendChild(cancelButtonElem);
|
|
||||||
modalFooterElem.appendChild(saveButtonElem);
|
|
||||||
modalContentElem.appendChild(modalFooterElem);
|
|
||||||
|
|
||||||
modalHeaderCloseButton.addEventListener("click", event => {
|
|
||||||
event.preventDefault();
|
|
||||||
const bodyElem = document.querySelector("body");
|
|
||||||
bodyElem?.removeChild(modalElem);
|
|
||||||
});
|
|
||||||
|
|
||||||
cancelButtonElem.addEventListener("click", event => {
|
|
||||||
event.preventDefault();
|
|
||||||
const bodyElem = document.querySelector("body");
|
|
||||||
bodyElem?.removeChild(modalElem);
|
|
||||||
});
|
|
||||||
|
|
||||||
saveButtonElem.addEventListener("click", event => {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
console.log("updating image...");
|
|
||||||
|
|
||||||
const bodyElem = document.querySelector("body");
|
|
||||||
bodyElem?.removeChild(modalElem);
|
|
||||||
});
|
|
||||||
|
|
||||||
const bodyElem = document.querySelector("body");
|
|
||||||
bodyElem?.appendChild(modalElem);
|
|
||||||
|
|
||||||
|
|
||||||
console.dir(jquery);
|
|
||||||
|
|
||||||
(jquery("#cms-editor-image-dialog") as any).modal();
|
|
||||||
(jquery("#cms-editor-image-dialog") as any).modal("toggle");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,808 +0,0 @@
|
||||||
import "bootstrap";
|
|
||||||
import * as $ from "jquery";
|
|
||||||
import { ChainedCommands, Editor, Node, mergeAttributes } from "@tiptap/core";
|
|
||||||
import Gapcursor from "@tiptap/extension-gapcursor";
|
|
||||||
import StarterKit from "@tiptap/starter-kit";
|
|
||||||
import Subscript from "@tiptap/extension-subscript";
|
|
||||||
import Superscript from "@tiptap/extension-superscript";
|
|
||||||
import Table from "@tiptap/extension-table";
|
|
||||||
import TableRow from "@tiptap/extension-table-row";
|
|
||||||
import TableCell from "@tiptap/extension-table-cell";
|
|
||||||
import TableHeader from "@tiptap/extension-table-header";
|
|
||||||
|
|
||||||
import CcmImage from "./cms-editor/image";
|
|
||||||
|
|
||||||
const BUTTONS: CmsEditorButton[] = [
|
|
||||||
{
|
|
||||||
selector: ".tiptap-emph",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().toggleItalic().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleItalic()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-strong-emph",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().toggleBold().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleBold()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-code",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().toggleCode().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleCode()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-strikethrough",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().toggleStrike().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleStrike()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-subscript",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleSubscript()
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleSubscript()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-superscript",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleSuperscript()
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleSuperscript()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-h1",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeading({ level: 1 })
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeading({ level: 1 })
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-h2",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeading({ level: 2 })
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeading({ level: 2 })
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-h3",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeading({ level: 3 })
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeading({ level: 3 })
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-h5",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeading({ level: 5 })
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeading({ level: 5 })
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-h6",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeading({ level: 6 })
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeading({ level: 6 })
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-paragraph",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().clearNodes().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.clearNodes()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-blockquote",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleBlockquote()
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleBlockquote()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-codeblock",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleCodeBlock()
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleCodeBlock()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-ul",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleBulletList()
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleBulletList()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-ol",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleOrderedList()
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleOrderedList()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".cms-editor-insert-table-dialog",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.insertTable()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".cms-editor-insert-table-dialog .btn-success",
|
|
||||||
command: cmsEditor => {
|
|
||||||
const dialog = cmsEditor
|
|
||||||
.getEditorElem()
|
|
||||||
.querySelector(".cms-editor-insert-table-dialog");
|
|
||||||
if (!dialog) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const rowsInput = dialog.querySelector(
|
|
||||||
"input#rows"
|
|
||||||
) as HTMLInputElement;
|
|
||||||
const colsInput = dialog.querySelector(
|
|
||||||
"input#cols"
|
|
||||||
) as HTMLInputElement;
|
|
||||||
const headerRowInput = dialog.querySelector(
|
|
||||||
"input#headerRow"
|
|
||||||
) as HTMLInputElement;
|
|
||||||
console.log(`rowsInput = ${rowsInput}`);
|
|
||||||
console.log(`colsInput = ${colsInput}`);
|
|
||||||
console.log(`headerRowInput = ${headerRowInput}`);
|
|
||||||
const rows = parseInt(rowsInput.value, 10);
|
|
||||||
const cols = parseInt(colsInput.value, 10);
|
|
||||||
const headerRow = JSON.parse(headerRowInput.value) as Boolean;
|
|
||||||
const insertTableDialog = $("#insert-table-dialog") as any;
|
|
||||||
insertTableDialog.modal("hide");
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.insertTable({
|
|
||||||
// allowTableNodeSelection: true,
|
|
||||||
// cellMinWidth: 150,
|
|
||||||
cols: cols,
|
|
||||||
// headerRow: headerRow,
|
|
||||||
// resizable: true,
|
|
||||||
rows: rows
|
|
||||||
})
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-insert-table-row-before",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().addRowBefore().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.addRowBefore()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-insert-table-row-after",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().addRowAfter().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.addRowAfter()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-insert-table-column-before",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.addColumnBefore()
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.addColumnBefore()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-insert-table-column-after",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().addColumnAfter().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.addColumnAfter()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-remove-table-row",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().deleteRow().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.deleteRow()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-remove-table-column",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().deleteColumn().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.deleteColumn()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-remove-table",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().deleteTable().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.deleteTable()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-toggle-table-header-row",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeaderRow()
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeaderRow()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-toggle-table-header-column",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeaderColumn()
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeaderColumn()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-merge-table-cells",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().mergeCells().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.mergeCells()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-split-table-cell",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().splitCell().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.splitCell()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-insert-image",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().addCcmImage().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().can().chain().focus().addCcmImage().run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// {
|
|
||||||
// selector: "",
|
|
||||||
// command: cmsEditor => {},
|
|
||||||
// can: cmsEditor => {}
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// selector: "",
|
|
||||||
// command: cmsEditor => {},
|
|
||||||
// can: cmsEditor => {}
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// selector: "",
|
|
||||||
// command: cmsEditor => {},
|
|
||||||
// can: cmsEditor => {}
|
|
||||||
// }
|
|
||||||
];
|
|
||||||
|
|
||||||
class CmsEditor {
|
|
||||||
private editor: Editor;
|
|
||||||
private editorElem: HTMLElement;
|
|
||||||
private saveUrl: string;
|
|
||||||
|
|
||||||
public constructor(
|
|
||||||
editor: Editor,
|
|
||||||
editorElem: HTMLElement,
|
|
||||||
saveUrl: string
|
|
||||||
) {
|
|
||||||
this.editor = editor;
|
|
||||||
this.editorElem = editorElem;
|
|
||||||
this.saveUrl = saveUrl;
|
|
||||||
|
|
||||||
console.log("initializing editor buttons");
|
|
||||||
const buttonsElem = editorElem.querySelector(
|
|
||||||
".cms-tiptap-editor-buttons"
|
|
||||||
);
|
|
||||||
if (buttonsElem) {
|
|
||||||
for (const button of BUTTONS) {
|
|
||||||
const buttonElem = buttonsElem.querySelector(button.selector);
|
|
||||||
if (buttonElem) {
|
|
||||||
buttonElem.addEventListener("click", event => {
|
|
||||||
event.preventDefault();
|
|
||||||
button.command(this);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.error("editorButtonsElem not found.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.on("selectionUpdate", ({ editor }: { editor: Editor }) => {
|
|
||||||
console.log(`checkButton - this.editorElem = ${this.editorElem}`);
|
|
||||||
const buttonsElem = editorElem.querySelector(
|
|
||||||
".cms-tiptap-editor-buttons"
|
|
||||||
);
|
|
||||||
if (!buttonsElem) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (const button of BUTTONS) {
|
|
||||||
const elem = buttonsElem.querySelector(button.selector);
|
|
||||||
if (elem) {
|
|
||||||
const buttonElem = elem as HTMLButtonElement;
|
|
||||||
if (button.can(this)) {
|
|
||||||
buttonElem.removeAttribute("disabled");
|
|
||||||
} else {
|
|
||||||
buttonElem.setAttribute("disabled", "disabled");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(`editorElem = ${editorElem}`);
|
|
||||||
|
|
||||||
const saveButton = editorElem.querySelector(".cms-editor-save-button");
|
|
||||||
saveButton?.addEventListener("click", event => this.save(event));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async save(event: Event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
const params = new URLSearchParams();
|
|
||||||
params.append("value", this.editor.getHTML());
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(this.saveUrl, {
|
|
||||||
method: "POST",
|
|
||||||
credentials: "include",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/x-www-form-urlencoded"
|
|
||||||
},
|
|
||||||
body: params
|
|
||||||
});
|
|
||||||
if (response.ok) {
|
|
||||||
} else {
|
|
||||||
this.showSaveFailedMessage(
|
|
||||||
response.status,
|
|
||||||
response.statusText
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.showSaveFailedErrMessage(error as string);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected showSaveFailedMessage(status: number, statusText: string) {
|
|
||||||
this.showMessage("#cms-editor-msg-save-failed");
|
|
||||||
console.error(
|
|
||||||
`Failed to save text. Status: ${statusText}. Status Text: ${statusText}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected showSaveFailedErrMessage(error: string) {
|
|
||||||
this.showMessage("#cms-editor-msg-save-failed");
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected showMessage(messageId: string) {
|
|
||||||
const template = this.editorElem.querySelector(
|
|
||||||
messageId
|
|
||||||
) as HTMLTemplateElement;
|
|
||||||
const message = template.content.cloneNode(true);
|
|
||||||
this.editorElem.querySelector(".cms-editor-messages")?.append(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getEditor(): Editor {
|
|
||||||
return this.editor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getEditorElem(): HTMLElement {
|
|
||||||
return this.editorElem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CmsEditorBuilder {
|
|
||||||
private editorElem: HTMLElement;
|
|
||||||
private saveUrl: string;
|
|
||||||
private variantUrl: string;
|
|
||||||
|
|
||||||
constructor(editorElem: HTMLElement, saveUrl: string, variantUrl: string) {
|
|
||||||
this.editorElem = editorElem;
|
|
||||||
this.saveUrl = saveUrl;
|
|
||||||
this.variantUrl = variantUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async buildEditor(): Promise<CmsEditor> {
|
|
||||||
console.log("Build CMS Editor.");
|
|
||||||
const canvasElement = this.editorElem.querySelector(
|
|
||||||
".cms-tiptap-editor-canvas"
|
|
||||||
);
|
|
||||||
if (!canvasElement) {
|
|
||||||
this.showMessage("#cms-editor-msg-canvas-element-not-found");
|
|
||||||
console.error("canvasElem not found.");
|
|
||||||
throw "canvasElem not found.";
|
|
||||||
}
|
|
||||||
|
|
||||||
const variant = await this.fetchVariant(this.variantUrl);
|
|
||||||
|
|
||||||
const editor: Editor = new Editor({
|
|
||||||
element: canvasElement,
|
|
||||||
extensions: [
|
|
||||||
Gapcursor,
|
|
||||||
CcmImage,
|
|
||||||
StarterKit,
|
|
||||||
Subscript,
|
|
||||||
Superscript,
|
|
||||||
Table.configure({
|
|
||||||
allowTableNodeSelection: true,
|
|
||||||
cellMinWidth: 100,
|
|
||||||
handleWidth: 25,
|
|
||||||
resizable: true
|
|
||||||
}),
|
|
||||||
TableRow,
|
|
||||||
TableHeader,
|
|
||||||
TableCell
|
|
||||||
],
|
|
||||||
content: variant,
|
|
||||||
onUpdate({editor}) {
|
|
||||||
const preview = document.querySelector("#html-preview");
|
|
||||||
if (preview) {
|
|
||||||
preview.innerHTML = editor.getHTML();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return new CmsEditor(editor, this.editorElem, this.saveUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async fetchVariant(variantUrl: string): Promise<string> {
|
|
||||||
try {
|
|
||||||
const response = await fetch(variantUrl, {
|
|
||||||
method: "GET",
|
|
||||||
credentials: "include"
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
return await response.text();
|
|
||||||
} else {
|
|
||||||
this.showLoadVariantFailedMessage(
|
|
||||||
response.status,
|
|
||||||
response.statusText
|
|
||||||
);
|
|
||||||
throw `Failed to load variant. Status: ${response.status}, Status Text: ${response.statusText}`;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.showLoadVariantFailedErrorMessage(error as string);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected showLoadVariantFailedMessage(status: number, statusText: string) {
|
|
||||||
this.showMessage("#cms-editor-msg-variant-load-failed");
|
|
||||||
console.error(
|
|
||||||
`Failed to load variant: HTTP Status: ${status}, statusText: ${statusText}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected showLoadVariantFailedErrorMessage(error: string) {
|
|
||||||
this.showMessage("#cms-editor-msg-variant-load-failed");
|
|
||||||
console.error(`Failed to load variant: ${error}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected showMessage(messageId: string) {
|
|
||||||
const template = this.editorElem.querySelector(
|
|
||||||
messageId
|
|
||||||
) as HTMLTemplateElement;
|
|
||||||
const message = template.content.cloneNode(true);
|
|
||||||
this.editorElem.querySelector(".cms-editor-messages")?.append(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CmsEditorParameters {
|
|
||||||
editorElem: HTMLElement;
|
|
||||||
variantUrl: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CmsEditorButton {
|
|
||||||
selector: string;
|
|
||||||
command: (cmsEditor: CmsEditor) => boolean;
|
|
||||||
can: (cmsEditor: CmsEditor) => boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface EditorParam {
|
|
||||||
editor: Editor;
|
|
||||||
}
|
|
||||||
|
|
||||||
export { CmsEditor, CmsEditorBuilder, CmsEditorParameters };
|
|
||||||
|
|
@ -1,2 +1,795 @@
|
||||||
//import EditorJS from '@editorjs/editorjs';
|
import "bootstrap";
|
||||||
|
import * as $ from "jquery";
|
||||||
|
import { ChainedCommands, Editor } from "@tiptap/core";
|
||||||
|
import Gapcursor from "@tiptap/extension-gapcursor";
|
||||||
|
import StarterKit from "@tiptap/starter-kit";
|
||||||
|
import Subscript from "@tiptap/extension-subscript";
|
||||||
|
import Superscript from "@tiptap/extension-superscript";
|
||||||
|
import Table from "@tiptap/extension-table";
|
||||||
|
import TableRow from "@tiptap/extension-table-row";
|
||||||
|
import TableCell from "@tiptap/extension-table-cell";
|
||||||
|
import TableHeader from "@tiptap/extension-table-header";
|
||||||
|
|
||||||
|
const BUTTONS: CmsEditorButton[] = [
|
||||||
|
{
|
||||||
|
selector: ".tiptap-emph",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().toggleItalic().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleItalic()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-strong-emph",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().toggleBold().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleBold()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-code",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().toggleCode().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleCode()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-strikethrough",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().toggleStrike().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleStrike()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-subscript",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleSubscript()
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleSubscript()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-superscript",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleSuperscript()
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleSuperscript()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-h1",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeading({ level: 1 })
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeading({ level: 1 })
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-h2",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeading({ level: 2 })
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeading({ level: 2 })
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-h3",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeading({ level: 3 })
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeading({ level: 3 })
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-h5",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeading({ level: 5 })
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeading({ level: 5 })
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-h6",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeading({ level: 6 })
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeading({ level: 6 })
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-paragraph",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().clearNodes().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.clearNodes()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-blockquote",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleBlockquote()
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleBlockquote()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-codeblock",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleCodeBlock()
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleCodeBlock()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-ul",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleBulletList()
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleBulletList()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-ol",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleOrderedList()
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleOrderedList()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".cms-editor-insert-table-dialog",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.insertTable()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".cms-editor-insert-table-dialog .btn-success",
|
||||||
|
command: cmsEditor => {
|
||||||
|
const dialog = cmsEditor
|
||||||
|
.getEditorElem()
|
||||||
|
.querySelector(".cms-editor-insert-table-dialog");
|
||||||
|
if (!dialog) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const rowsInput = dialog.querySelector(
|
||||||
|
"input#rows"
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const colsInput = dialog.querySelector(
|
||||||
|
"input#cols"
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const headerRowInput = dialog.querySelector(
|
||||||
|
"input#headerRow"
|
||||||
|
) as HTMLInputElement;
|
||||||
|
console.log(`rowsInput = ${rowsInput}`);
|
||||||
|
console.log(`colsInput = ${colsInput}`);
|
||||||
|
console.log(`headerRowInput = ${headerRowInput}`);
|
||||||
|
const rows = parseInt(rowsInput.value, 10);
|
||||||
|
const cols = parseInt(colsInput.value, 10);
|
||||||
|
const headerRow = JSON.parse(headerRowInput.value) as Boolean;
|
||||||
|
const insertTableDialog = $("#insert-table-dialog") as any;
|
||||||
|
insertTableDialog.modal("hide");
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.insertTable({
|
||||||
|
// allowTableNodeSelection: true,
|
||||||
|
// cellMinWidth: 150,
|
||||||
|
cols: cols,
|
||||||
|
// headerRow: headerRow,
|
||||||
|
// resizable: true,
|
||||||
|
rows: rows
|
||||||
|
})
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-insert-table-row-before",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().addRowBefore().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.addRowBefore()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-insert-table-row-after",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().addRowAfter().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.addRowAfter()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-insert-table-column-before",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.addColumnBefore()
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.addColumnBefore()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-insert-table-column-after",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().addColumnAfter().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.addColumnAfter()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-remove-table-row",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().deleteRow().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.deleteRow()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-remove-table-column",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().deleteColumn().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.deleteColumn()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-remove-table",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().deleteTable().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.deleteTable()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-toggle-table-header-row",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeaderRow()
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeaderRow()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-toggle-table-header-column",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeaderColumn()
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeaderColumn()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-merge-table-cells",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().mergeCells().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.mergeCells()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-split-table-cell",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().splitCell().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.splitCell()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// {
|
||||||
|
// selector: "",
|
||||||
|
// command: cmsEditor => {},
|
||||||
|
// can: cmsEditor => {}
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// selector: "",
|
||||||
|
// command: cmsEditor => {},
|
||||||
|
// can: cmsEditor => {}
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// selector: "",
|
||||||
|
// command: cmsEditor => {},
|
||||||
|
// can: cmsEditor => {}
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// selector: "",
|
||||||
|
// command: cmsEditor => {},
|
||||||
|
// can: cmsEditor => {}
|
||||||
|
// }
|
||||||
|
];
|
||||||
|
|
||||||
|
class CmsEditor {
|
||||||
|
private editor: Editor;
|
||||||
|
private editorElem: HTMLElement;
|
||||||
|
private saveUrl: string;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
editor: Editor,
|
||||||
|
editorElem: HTMLElement,
|
||||||
|
saveUrl: string
|
||||||
|
) {
|
||||||
|
this.editor = editor;
|
||||||
|
this.editorElem = editorElem;
|
||||||
|
this.saveUrl = saveUrl;
|
||||||
|
|
||||||
|
console.log("initializing editor buttons");
|
||||||
|
const buttonsElem = editorElem.querySelector(
|
||||||
|
".cms-tiptap-editor-buttons"
|
||||||
|
);
|
||||||
|
if (buttonsElem) {
|
||||||
|
for (const button of BUTTONS) {
|
||||||
|
const buttonElem = buttonsElem.querySelector(button.selector);
|
||||||
|
if (buttonElem) {
|
||||||
|
buttonElem.addEventListener("click", event => {
|
||||||
|
event.preventDefault();
|
||||||
|
button.command(this);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error("editorButtonsElem not found.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.on("selectionUpdate", ({ editor }: { editor: Editor }) => {
|
||||||
|
console.log(`checkButton - this.editorElem = ${this.editorElem}`);
|
||||||
|
const buttonsElem = editorElem.querySelector(
|
||||||
|
".cms-tiptap-editor-buttons"
|
||||||
|
);
|
||||||
|
if (!buttonsElem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const button of BUTTONS) {
|
||||||
|
const elem = buttonsElem.querySelector(button.selector);
|
||||||
|
if (elem) {
|
||||||
|
const buttonElem = elem as HTMLButtonElement;
|
||||||
|
if (button.can(this)) {
|
||||||
|
buttonElem.removeAttribute("disabled");
|
||||||
|
} else {
|
||||||
|
buttonElem.setAttribute("disabled", "disabled");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`editorElem = ${editorElem}`);
|
||||||
|
|
||||||
|
const saveButton = editorElem.querySelector(".cms-editor-save-button");
|
||||||
|
saveButton?.addEventListener("click", event => this.save(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async save(event: Event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.append("value", this.editor.getHTML());
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(this.saveUrl, {
|
||||||
|
method: "POST",
|
||||||
|
credentials: "include",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded"
|
||||||
|
},
|
||||||
|
body: params
|
||||||
|
});
|
||||||
|
if (response.ok) {
|
||||||
|
} else {
|
||||||
|
this.showSaveFailedMessage(
|
||||||
|
response.status,
|
||||||
|
response.statusText
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.showSaveFailedErrMessage(error as string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected showSaveFailedMessage(status: number, statusText: string) {
|
||||||
|
this.showMessage("#cms-editor-msg-save-failed");
|
||||||
|
console.error(
|
||||||
|
`Failed to save text. Status: ${statusText}. Status Text: ${statusText}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected showSaveFailedErrMessage(error: string) {
|
||||||
|
this.showMessage("#cms-editor-msg-save-failed");
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected showMessage(messageId: string) {
|
||||||
|
const template = this.editorElem.querySelector(
|
||||||
|
messageId
|
||||||
|
) as HTMLTemplateElement;
|
||||||
|
const message = template.content.cloneNode(true);
|
||||||
|
this.editorElem.querySelector(".cms-editor-messages")?.append(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getEditor(): Editor {
|
||||||
|
return this.editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getEditorElem(): HTMLElement {
|
||||||
|
return this.editorElem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CmsEditorBuilder {
|
||||||
|
private editorElem: HTMLElement;
|
||||||
|
private saveUrl: string;
|
||||||
|
private variantUrl: string;
|
||||||
|
|
||||||
|
constructor(editorElem: HTMLElement, saveUrl: string, variantUrl: string) {
|
||||||
|
this.editorElem = editorElem;
|
||||||
|
this.saveUrl = saveUrl;
|
||||||
|
this.variantUrl = variantUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async buildEditor(): Promise<CmsEditor> {
|
||||||
|
console.log("Build CMS Editor.");
|
||||||
|
const canvasElement = this.editorElem.querySelector(
|
||||||
|
".cms-tiptap-editor-canvas"
|
||||||
|
);
|
||||||
|
if (!canvasElement) {
|
||||||
|
this.showMessage("#cms-editor-msg-canvas-element-not-found");
|
||||||
|
console.error("canvasElem not found.");
|
||||||
|
throw "canvasElem not found.";
|
||||||
|
}
|
||||||
|
|
||||||
|
const variant = await this.fetchVariant(this.variantUrl);
|
||||||
|
|
||||||
|
const editor: Editor = new Editor({
|
||||||
|
element: canvasElement,
|
||||||
|
extensions: [
|
||||||
|
Gapcursor,
|
||||||
|
StarterKit,
|
||||||
|
Subscript,
|
||||||
|
Superscript,
|
||||||
|
Table.configure({
|
||||||
|
allowTableNodeSelection: true,
|
||||||
|
cellMinWidth: 100,
|
||||||
|
handleWidth: 25,
|
||||||
|
resizable: true
|
||||||
|
}),
|
||||||
|
TableRow,
|
||||||
|
TableHeader,
|
||||||
|
TableCell
|
||||||
|
],
|
||||||
|
content: variant
|
||||||
|
});
|
||||||
|
|
||||||
|
return new CmsEditor(editor, this.editorElem, this.saveUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async fetchVariant(variantUrl: string): Promise<string> {
|
||||||
|
try {
|
||||||
|
const response = await fetch(variantUrl, {
|
||||||
|
method: "GET",
|
||||||
|
credentials: "include"
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
return await response.text();
|
||||||
|
} else {
|
||||||
|
this.showLoadVariantFailedMessage(
|
||||||
|
response.status,
|
||||||
|
response.statusText
|
||||||
|
);
|
||||||
|
throw `Failed to load variant. Status: ${response.status}, Status Text: ${response.statusText}`;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.showLoadVariantFailedErrorMessage(error as string);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected showLoadVariantFailedMessage(status: number, statusText: string) {
|
||||||
|
this.showMessage("#cms-editor-msg-variant-load-failed");
|
||||||
|
console.error(
|
||||||
|
`Failed to load variant: HTTP Status: ${status}, statusText: ${statusText}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected showLoadVariantFailedErrorMessage(error: string) {
|
||||||
|
this.showMessage("#cms-editor-msg-variant-load-failed");
|
||||||
|
console.error(`Failed to load variant: ${error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected showMessage(messageId: string) {
|
||||||
|
const template = this.editorElem.querySelector(
|
||||||
|
messageId
|
||||||
|
) as HTMLTemplateElement;
|
||||||
|
const message = template.content.cloneNode(true);
|
||||||
|
this.editorElem.querySelector(".cms-editor-messages")?.append(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CmsEditorParameters {
|
||||||
|
editorElem: HTMLElement;
|
||||||
|
variantUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CmsEditorButton {
|
||||||
|
selector: string;
|
||||||
|
command: (cmsEditor: CmsEditor) => boolean;
|
||||||
|
can: (cmsEditor: CmsEditor) => boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EditorParam {
|
||||||
|
editor: Editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { CmsEditor, CmsEditorBuilder, CmsEditorParameters };
|
||||||
|
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
import { Node, mergeAttributes } from "@tiptap/core";
|
|
||||||
import { insertContent } from "@tiptap/core/dist/packages/core/src/extensions/commands";
|
|
||||||
|
|
||||||
export interface CcmImageOptions {
|
|
||||||
HTMLAttributes: Record<string, any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module "@tiptap/core" {
|
|
||||||
interface Commands<ReturnType> {
|
|
||||||
ccmImage: {
|
|
||||||
addCcmImage: () => ReturnType;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const CcmImage = Node.create<CcmImageOptions>({
|
|
||||||
name: "ccmImage",
|
|
||||||
|
|
||||||
// priority: 1000,
|
|
||||||
|
|
||||||
defaultOptions: {
|
|
||||||
HTMLAttributes: {},
|
|
||||||
},
|
|
||||||
|
|
||||||
group: "block",
|
|
||||||
|
|
||||||
content: "inline*",
|
|
||||||
|
|
||||||
parseHTML() {
|
|
||||||
return [
|
|
||||||
{ tag: "figure"}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
renderHTML({ HTMLAttributes }) {
|
|
||||||
return ["node-view", mergeAttributes(HTMLAttributes)];
|
|
||||||
// return [
|
|
||||||
// "figure",
|
|
||||||
// mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
|
|
||||||
// ["figcaption", HTMLAttributes, 0],
|
|
||||||
// // ["img", HTMLAttributes, 0]
|
|
||||||
// ]
|
|
||||||
},
|
|
||||||
|
|
||||||
addCommands() {
|
|
||||||
return {
|
|
||||||
addCcmImage: () => ({ commands }) => {
|
|
||||||
return commands.setNode("ccmImage");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// addKeyboardShortcut() {
|
|
||||||
// return {
|
|
||||||
// "Mod-Alt-i": () => this.editor.commands.addCcmImage()
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
|
|
||||||
addNodeView() {
|
|
||||||
return () => {
|
|
||||||
const dom = document.createElement("figure");
|
|
||||||
dom.classList.add("ccm-image");
|
|
||||||
const caption = document.createElement("figcaption") as HTMLElement;
|
|
||||||
caption.classList.add("label");
|
|
||||||
caption.innerHTML = "Node View";
|
|
||||||
caption.contentEditable = "true";
|
|
||||||
|
|
||||||
const content = document.createElement("div");
|
|
||||||
content.classList.add("content");
|
|
||||||
|
|
||||||
dom.append(caption, content);
|
|
||||||
|
|
||||||
return {
|
|
||||||
dom,
|
|
||||||
contentDom: content
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
import { CcmImage } from "./image";
|
|
||||||
|
|
||||||
export * from "./image";
|
|
||||||
|
|
||||||
export default CcmImage;
|
|
||||||
|
|
@ -3,7 +3,7 @@ module.exports = {
|
||||||
devtool: "source-map",
|
devtool: "source-map",
|
||||||
entry: {
|
entry: {
|
||||||
"cms-admin": "./src/main/typescript/content-sections/cms-admin.ts",
|
"cms-admin": "./src/main/typescript/content-sections/cms-admin.ts",
|
||||||
"article-text-step": "./src/main/typescript/content-sections/article-text-step.js"
|
"article-text-step": "./src/main/typescript/content-sections/article-text-step.ts"
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
filename: "[name].js",
|
filename: "[name].js",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue