diff --git a/ccm-bundle-devel-wildfly/pom.xml b/ccm-bundle-devel-wildfly/pom.xml index 5bc921f3a..cf9cc9725 100644 --- a/ccm-bundle-devel-wildfly/pom.xml +++ b/ccm-bundle-devel-wildfly/pom.xml @@ -248,6 +248,14 @@ assets/ + + org.librecms + ccm-cms + jar + + icons/ + + org.librecms ccm-cms diff --git a/ccm-cms/package-lock.json b/ccm-cms/package-lock.json index d6175d880..04ba368ae 100644 --- a/ccm-cms/package-lock.json +++ b/ccm-cms/package-lock.json @@ -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", diff --git a/ccm-cms/package.json b/ccm-cms/package.json index 24f32b5c6..42d7b843f 100644 --- a/ccm-cms/package.json +++ b/ccm-cms/package.json @@ -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" diff --git a/ccm-cms/src/main/resources/META-INF/resources/components/librecms/cmsEditor-tiptap.xhtml b/ccm-cms/src/main/resources/META-INF/resources/components/librecms/cmsEditor-tiptap.xhtml deleted file mode 100644 index eeb024410..000000000 --- a/ccm-cms/src/main/resources/META-INF/resources/components/librecms/cmsEditor-tiptap.xhtml +++ /dev/null @@ -1,659 +0,0 @@ -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - -

#{cc.attrs.title}

-
- -

#{cc.attrs.title}

-
- -

#{cc.attrs.title}

-
- -

#{cc.attrs.title}

-
- -
#{cc.attrs.title}
-
- -
#{cc.attrs.title}
-
- -
#{cc.attrs.title}
-
-
-
- - - - -
- - -

- #{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale} -

-
- -

- #{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale} -

-
- -

- #{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale} -

-
- -
- #{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale} -
-
- -
- #{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale} -
-
- -
- #{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale} -
-
-
-
-
-
- - - - - - -
-
- - - -
-
- - - -
-
- - -
-
- - - - - - - - - - - - - -
-
- -
-
-
-
-
- - #{cc.attrs.editDialogCancelLabel} - - -
-
-
-
- diff --git a/ccm-cms/src/main/resources/META-INF/resources/components/librecms/cmsEditor.xhtml b/ccm-cms/src/main/resources/META-INF/resources/components/librecms/cmsEditor.xhtml index bb47d66a5..7c799ffe8 100644 --- a/ccm-cms/src/main/resources/META-INF/resources/components/librecms/cmsEditor.xhtml +++ b/ccm-cms/src/main/resources/META-INF/resources/components/librecms/cmsEditor.xhtml @@ -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}" - > -
-
- + + +

#{cc.attrs.title}

+
+ +

#{cc.attrs.title}

+
+ +

#{cc.attrs.title}

+
+ +

#{cc.attrs.title}

+
+ +
#{cc.attrs.title}
+
+ +
#{cc.attrs.title}
+
+ +
#{cc.attrs.title}
+
+
+
+ + + + +
+ + +

+ #{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale} +

+
+ +

+ #{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale} +

+
+ +

+ #{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale} +

+
+ +
+ #{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale} +
+
+ +
+ #{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale} +
+
+ +
+ #{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale} +
+
+
+
- #{cc.attrs.editDialogCancelLabel} - - +
+
+ + + + + + +
+
+ + + +
+
+ + + +
+
+ + +
+
+ + + + + + + + + + + + + +
+
+
+
+ + #{cc.attrs.editDialogCancelLabel} + + +
+
diff --git a/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/article/article-text/edit.xhtml b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/article/article-text/edit.xhtml index 2c05f63ba..f4e695674 100644 --- a/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/article/article-text/edit.xhtml +++ b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contenttypes/article/article-text/edit.xhtml @@ -30,37 +30,11 @@ /> - - - - - - diff --git a/ccm-cms/src/main/typescript/content-sections/article-text-step.js b/ccm-cms/src/main/typescript/content-sections/article-text-step.js deleted file mode 100644 index 1deaaa2d4..000000000 --- a/ccm-cms/src/main/typescript/content-sections/article-text-step.js +++ /dev/null @@ -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); - }); - } - }); - } -}); diff --git a/ccm-cms/src/main/typescript/content-sections/article-text-step.ts b/ccm-cms/src/main/typescript/content-sections/article-text-step.ts new file mode 100644 index 000000000..566325bfd --- /dev/null +++ b/ccm-cms/src/main/typescript/content-sections/article-text-step.ts @@ -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(); + } +}); diff --git a/ccm-cms/src/main/typescript/content-sections/article-text-step.ts.off b/ccm-cms/src/main/typescript/content-sections/article-text-step.ts.off deleted file mode 100644 index 57260930a..000000000 --- a/ccm-cms/src/main/typescript/content-sections/article-text-step.ts.off +++ /dev/null @@ -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(); - // } -}); diff --git a/ccm-cms/src/main/typescript/content-sections/ccm-cms-editorjs-image/ccm-cms-editorjs-image.ts b/ccm-cms/src/main/typescript/content-sections/ccm-cms-editorjs-image/ccm-cms-editorjs-image.ts deleted file mode 100644 index fb5d6c2bd..000000000 --- a/ccm-cms/src/main/typescript/content-sections/ccm-cms-editorjs-image/ccm-cms-editorjs-image.ts +++ /dev/null @@ -1,131 +0,0 @@ -import * as jquery from "jquery"; - -export class CmsImage { - static get toolbox() { - return { - title: "Image", - icon: "" - } - } - - public render() { - const figureElem = document.createElement("figure"); - - const imgElem = document.createElement("img"); - imgElem.src = "data:image/svg+xml;utf8,"; - imgElem.setAttribute("style", "width: 100%"); - figureElem.appendChild(imgElem); - - const captionElem = document.createElement("figcaption"); - captionElem.contentEditable = "true"; - captionElem.innerHTML = "Caption placeholder" - figureElem.appendChild(captionElem); - - return figureElem; - } - - public renderSettings() { - const updateImageButton = document.createElement("button"); - updateImageButton.title = "Update image"; - updateImageButton.classList.add("cdx-settings-button"); - updateImageButton.innerHTML = ""; - 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 = ""; - modalHeaderElem.appendChild(modalHeaderCloseButton); - - const modalBodyElem = document.createElement("div"); - modalBodyElem.classList.add("modal-body"); - modalBodyElem.innerHTML = "Update image placeholder"; - 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"); - } -} \ No newline at end of file diff --git a/ccm-cms/src/main/typescript/content-sections/cms-editor-tiptap.ts b/ccm-cms/src/main/typescript/content-sections/cms-editor-tiptap.ts deleted file mode 100644 index 3906a9790..000000000 --- a/ccm-cms/src/main/typescript/content-sections/cms-editor-tiptap.ts +++ /dev/null @@ -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 { - 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 { - 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 }; diff --git a/ccm-cms/src/main/typescript/content-sections/cms-editor.ts b/ccm-cms/src/main/typescript/content-sections/cms-editor.ts index 57ea77c70..f7d725a09 100644 --- a/ccm-cms/src/main/typescript/content-sections/cms-editor.ts +++ b/ccm-cms/src/main/typescript/content-sections/cms-editor.ts @@ -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 { + 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 { + 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 }; diff --git a/ccm-cms/src/main/typescript/content-sections/cms-editor/image/image.ts b/ccm-cms/src/main/typescript/content-sections/cms-editor/image/image.ts deleted file mode 100644 index e3b0deb3c..000000000 --- a/ccm-cms/src/main/typescript/content-sections/cms-editor/image/image.ts +++ /dev/null @@ -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; -} - -declare module "@tiptap/core" { - interface Commands { - ccmImage: { - addCcmImage: () => ReturnType; - }; - } -} - -export const CcmImage = Node.create({ - 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 - } - } - } -}); - diff --git a/ccm-cms/src/main/typescript/content-sections/cms-editor/image/index.ts b/ccm-cms/src/main/typescript/content-sections/cms-editor/image/index.ts deleted file mode 100644 index 2401e2032..000000000 --- a/ccm-cms/src/main/typescript/content-sections/cms-editor/image/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { CcmImage } from "./image"; - -export * from "./image"; - -export default CcmImage; \ No newline at end of file diff --git a/ccm-cms/webpack.config.js b/ccm-cms/webpack.config.js index 294cdf130..c19f3a6c8 100644 --- a/ccm-cms/webpack.config.js +++ b/ccm-cms/webpack.config.js @@ -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",