diff --git a/package.json b/package.json index 3e15cd2..0ff6cb6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@esi_package/ckeditor5-import", - "version": "0.0.5", + "version": "0.0.6", "description": "A plugin for CKEditor 5.", "keywords": [ "ckeditor", diff --git a/sample/ckeditor.ts b/sample/ckeditor.ts index abd87d0..a8d75bf 100644 --- a/sample/ckeditor.ts +++ b/sample/ckeditor.ts @@ -84,8 +84,7 @@ ClassicEditor 'undo', 'redo', '|', - 'importDocx', - 'insertDocx', + 'uploadDocx', 'mathlive', '|', 'heading', diff --git a/src/import.ts b/src/import.ts index f7a1de8..c81a6bc 100644 --- a/src/import.ts +++ b/src/import.ts @@ -1,4 +1,4 @@ -import { Plugin, ButtonView } from 'ckeditor5'; +import { Plugin, ButtonView, DropdownView, DropdownButtonView, createDropdown, addToolbarToDropdown, addListToDropdown, Collection, ListDropdownItemDefinition, ListDropdownButtonDefinition } from 'ckeditor5'; import ckeditor5Icon from '../theme/icons/ckeditor.svg'; import ckeditor5Iconn from '../theme/icons/ckeditorr.svg'; @@ -19,126 +19,42 @@ export default class Import extends Plugin { const t = editor.t; const model = editor.model; - // Add the "importButton" to feature components. - editor.ui.componentFactory.add( 'importDocx', locale => { - const view = new ButtonView( locale ); - - view.set( { - label: t( 'Import Docx' ), - icon: ckeditor5Iconn, - tooltip: true - } ); - - // POST fetch html from docx - this.listenTo( view, 'execute', async () => { - // curl -X 'POST' \ - // 'http://localhost:8080/aspose/word-to-html' \ - // -H 'accept: text/plain' \ - // -H 'Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document' \ - // --data-binary '@stou(207).docx' - - // file accpt docx - const input = document.createElement('input'); - input.type = 'file'; - input.accept = '.docx'; - input.click(); - - input.addEventListener('change', async () => { - const file = input.files![0]; - const formData = new FormData(); - formData.append("file", file); - - try { - const response = await fetch("http://localhost:8080/aspose/word-to-html", { - method: "POST", - headers: { - // Do not set `Content-Type` manually when using FormData; - // the browser will automatically set the correct boundary for multipart/form-data. - }, - body: file, // directly sending file bytes as the backend expects byte[] - }); - - if (!response.ok) { - throw new Error(`Failed to upload file: ${response.statusText}`); - } - - const htmlText = await response.text(); - // console.log("Converted HTML:", htmlText); - const htmlDoc = new DOMParser().parseFromString(htmlText, "text/html"); - const body = htmlDoc.body; - // pre-process mathml to latex - // get all mathml elements - // replace all mathml element in the body with script type math/tex with inside content latex - const mathmlElements = body.querySelectorAll("math"); - mathmlElements.forEach(mathmlElement => { - const latex = MathMLToLaTeX.convert(mathmlElement.outerHTML); - const scriptElement = document.createElement("script"); - scriptElement.type = "math/tex"; - scriptElement.textContent = latex; - const spanElement = document.createElement("span"); - spanElement.appendChild(scriptElement); - mathmlElement.replaceWith(spanElement); - }); - // font-family for concat with , serif - const spans = body.querySelectorAll("span"); - spans.forEach(span => { - // span.style.fontFamily += ", serif"; - // check if it's

tag has font-size attribute, then apply to tag - const p = span.parentElement; - if (p && p.tagName === "P") { - const fontSize = p.style.fontSize; - if (fontSize && span.style.fontSize === "") { - span.setAttribute("style", `${span.getAttribute("style")}; font-size: ${fontSize}`); - } - } - const li = span.parentElement; - if (li && li.tagName === "LI") { - const outerli = li.parentElement?.parentElement; - if (outerli && outerli.tagName === "LI") { - const fontSize = outerli.style.fontSize; - if (fontSize && li.style.fontSize === "") { - li.setAttribute("style", `${span.getAttribute("style")}; font-size: ${fontSize}`); - } - } - const fontSize = li.style.fontSize; - if (fontSize && span.style.fontSize === "") { - span.setAttribute("style", `${span.getAttribute("style")}; font-size: ${fontSize}`); - } - } - const pp = span.parentElement?.parentElement; - if (pp && span.parentElement.tagName === "A") { - const fontSize = pp.style.fontSize; - if (fontSize && span.style.fontSize === "") { - span.setAttribute("style", `${span.getAttribute("style")}; font-size: ${fontSize}`); - } - } - }); - console.log("Converted HTML:", body.innerHTML); - this.editor.setData(body.innerHTML); - } catch (error) { - console.error("Error uploading file:", error); - throw error; - } - - editor.editing.view.focus(); - }); - } ); - - return view; - } ); - - // Add the "insertButton" to feature components. - editor.ui.componentFactory.add( 'insertDocx', locale => { - const view = new ButtonView( locale ); - - view.set( { - label: t( 'Insert Docx' ), + // Add the "uploadDocx" to feature components. + editor.ui.componentFactory.add( 'uploadDocx', locale => { + const dropdownView = createDropdown( locale ); + dropdownView.buttonView.set( { + label: t( 'Upload Docx' ), icon: ckeditor5Icon, - tooltip: true + tooltip: true, } ); - + const items = new Collection(); + const importDropdownItem: ListDropdownButtonDefinition = { + type: 'button', + model: { + label: t( 'Insert Docx' ), + icon: ckeditor5Icon, + tooltip: true, + isOn: false, + withText: true + }, + } as unknown as ListDropdownButtonDefinition; + const insertDropdownItem: ListDropdownButtonDefinition = { + type: 'button', + model: { + label: t( 'Import Docx' ), + icon: ckeditor5Iconn, + tooltip: true, + isOn: false, + withText: true + }, + } as unknown as ListDropdownButtonDefinition; + items.add(importDropdownItem); + items.add(insertDropdownItem); + addListToDropdown( dropdownView, items ); + + // import/insert docx // POST fetch html from docx - this.listenTo( view, 'execute', async () => { + this.listenTo( dropdownView, 'execute', async (evt) => { // curl -X 'POST' \ // 'http://localhost:8080/aspose/word-to-html' \ // -H 'accept: text/plain' \ @@ -222,9 +138,18 @@ export default class Import extends Plugin { } }); console.log("Converted HTML:", body.innerHTML); - // this.editor.setData(body.innerHTML); + + // decided by user interaction on dropdown const viewFragment = editor.data.processor.toView(body.innerHTML); const modelFragment = editor.data.toModel(viewFragment); + if ((evt.source as any).label === 'Import Docx') { + // Clear current content + model.change(writer => { + writer.remove(writer.createRangeIn(model.document.getRoot()!)); + }); + } else if ((evt.source as any).label !== 'Insert Docx') { + throw new Error("Unknown event name"); + } model.insertContent(modelFragment); } catch (error) { console.error("Error uploading file:", error); @@ -235,7 +160,7 @@ export default class Import extends Plugin { }); } ); - return view; + return dropdownView; } ); } }