Reverted to Tiptap
parent
50600de813
commit
a28673c8e2
|
|
@ -248,6 +248,14 @@
|
|||
<include>assets/</include>
|
||||
</includes>
|
||||
</overlay>
|
||||
<overlay>
|
||||
<groupId>org.librecms</groupId>
|
||||
<artifactId>ccm-cms</artifactId>
|
||||
<type>jar</type>
|
||||
<includes>
|
||||
<include>icons/</include>
|
||||
</includes>
|
||||
</overlay>
|
||||
<overlay>
|
||||
<groupId>org.librecms</groupId>
|
||||
<artifactId>ccm-cms</artifactId>
|
||||
|
|
|
|||
|
|
@ -10,36 +10,6 @@
|
|||
"integrity": "sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==",
|
||||
"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": {
|
||||
"version": "2.0.0-beta.104",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"@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": {
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.0.tgz",
|
||||
|
|
@ -761,16 +726,6 @@
|
|||
"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": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
|
|
@ -826,11 +781,6 @@
|
|||
"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": {
|
||||
"version": "1.3.830",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.830.tgz",
|
||||
|
|
@ -1466,11 +1416,6 @@
|
|||
"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": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
|
||||
|
|
|
|||
|
|
@ -22,11 +22,6 @@
|
|||
"webpack-cli": "^4.8.0"
|
||||
},
|
||||
"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/extension-subscript": "^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-row": "^2.0.0-beta.14",
|
||||
"@tiptap/starter-kit": "^2.0.0-beta.102",
|
||||
"@types/editorjs__header": "^2.6.0",
|
||||
"acorn": "^8.4.1",
|
||||
"bootstrap": "^4.6.0",
|
||||
"bootstrap-icons": "^1.5.0",
|
||||
"editorjs-latex": "^1.0.0",
|
||||
"jquery": "^3.6.0",
|
||||
"popper.js": "^1.16.1",
|
||||
"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-variant-url="#{cc.attrs.variantUrl}/#{cc.attrs.selectedLocale}"
|
||||
id="#{cc.attrs.editorId}"
|
||||
></div>
|
||||
<div id="editor-preview"></div>
|
||||
<div class="mt-3">
|
||||
<a
|
||||
class="btn btn-warning cms-editor-cancel-button"
|
||||
href="#{backUrl}"
|
||||
>
|
||||
<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}"
|
||||
>
|
||||
#{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 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>
|
||||
<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>
|
||||
</cc:implementation>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -30,37 +30,11 @@
|
|||
/>
|
||||
</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 name="scripts">
|
||||
<script src="#{request.contextPath}/assets/@content-sections/article-text-step.js"></script>
|
||||
</ui:define>
|
||||
|
||||
</ui:composition>
|
||||
</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",
|
||||
entry: {
|
||||
"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: {
|
||||
filename: "[name].js",
|
||||
|
|
|
|||
Loading…
Reference in New Issue