Image Node settings dialog fields
parent
8da183514e
commit
d4cb8bb5bb
|
|
@ -678,7 +678,7 @@
|
|||
id="librecms-image-node-view-settings-dialog-"
|
||||
tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form action="#" class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title"
|
||||
id="librecms-image-node-view-settings-dialog-title-">
|
||||
|
|
@ -692,7 +692,52 @@
|
|||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Image Node View Settings Dialog
|
||||
<div class="form-group">
|
||||
<label for="alttext">#{CmsAdminMessages['cms_editor.image_node_view.settings.dialog.alttext.label']}</label>
|
||||
<input aria-describedby="alttext-help"
|
||||
class="form-control"
|
||||
id="alttext"
|
||||
required="true"
|
||||
type="text" />
|
||||
<small class="form-text text-muted"
|
||||
id="alttext-help">#{CmsAdminMessages['cms_editor.image_node_view.settings.dialog.alttext.help']}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="size">#{CmsAdminMessages['cms_editor.image_node_view.settings.dialog.size.label']}</label>
|
||||
<select aria-describedby="size-help"
|
||||
class="custom-select"
|
||||
id="size">
|
||||
<option value="25">25%</option>
|
||||
<option value="33">33%</option>
|
||||
<option value="50">50%</option>
|
||||
<option value="66">66%</option>
|
||||
<option value="75">75%</option>
|
||||
<option value="100">100%</option>
|
||||
</select>
|
||||
<small class="form-text text-muted"
|
||||
id="size-help">#{CmsAdminMessages['cms_editor.image_node_view.settings.dialog.size.help']}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="align">#{CmsAdminMessages['cms_editor.image_node_view.settings.dialog.align.label']}</label>
|
||||
<select aria-describedby="align-help"
|
||||
class="custom-select"
|
||||
id="align">
|
||||
<option value="floatleft">#{CmsAdminMessages['cms_editor.image_node_view.settings.dialog.align.floatleft']}</option>
|
||||
<option value="center">#{CmsAdminMessages['cms_editor.image_node_view.settings.dialog.align.center']}</option>
|
||||
<option value="floatright">#{CmsAdminMessages['cms_editor.image_node_view.settings.dialog.align.floatright']}</option>
|
||||
</select>
|
||||
<small class="form-text text-muted"
|
||||
id="align-help">#{CmsAdminMessages['cms_editor.image_node_view.settings.dialog.align.help']}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input aria-describedby="fullsizeoverlay-help"
|
||||
class="form-check-input"
|
||||
id="fullsizeoverlay"
|
||||
type="checkbox" />
|
||||
<label for="fullsizeoverlay">#{CmsAdminMessages['cms_editor.image_node_view.settings.dialog.fullsizeoverlay.label']}</label>
|
||||
<small class="form-text text-muted"
|
||||
id="fullsizeoverlay-help">#{CmsAdminMessages['cms_editor.image_node_view.settings.dialog.fullsizeoverlay.help']}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-warning"
|
||||
|
|
@ -700,13 +745,13 @@
|
|||
type="button">
|
||||
#{CmsAdminMessages['cms_editor.image_node_view.settings.dialog.close']}
|
||||
</button>
|
||||
<button class="btn btn-primary"
|
||||
<button class="btn btn-primary image-settings-dialog-save"
|
||||
data-dismiss="modal"
|
||||
type="button">
|
||||
#{CmsAdminMessages['cms_editor.image_node_view.settings.dialog.save']}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import TableRow from "@tiptap/extension-table-row";
|
|||
import TableCell from "@tiptap/extension-table-cell";
|
||||
import TableHeader from "@tiptap/extension-table-header";
|
||||
|
||||
import ImageNode from "./cms-editor/image-node";
|
||||
|
||||
const BUTTONS: CmsEditorButton[] = [
|
||||
{
|
||||
selector: ".tiptap-emph",
|
||||
|
|
@ -17,7 +19,13 @@ const BUTTONS: CmsEditorButton[] = [
|
|||
return cmsEditor.getEditor().chain().focus().toggleItalic().run();
|
||||
},
|
||||
can: (cmsEditor) => {
|
||||
return cmsEditor.getEditor().can().chain().focus().toggleItalic().run();
|
||||
return cmsEditor
|
||||
.getEditor()
|
||||
.can()
|
||||
.chain()
|
||||
.focus()
|
||||
.toggleItalic()
|
||||
.run();
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -26,7 +34,13 @@ const BUTTONS: CmsEditorButton[] = [
|
|||
return cmsEditor.getEditor().chain().focus().toggleBold().run();
|
||||
},
|
||||
can: (cmsEditor) => {
|
||||
return cmsEditor.getEditor().can().chain().focus().toggleBold().run();
|
||||
return cmsEditor
|
||||
.getEditor()
|
||||
.can()
|
||||
.chain()
|
||||
.focus()
|
||||
.toggleBold()
|
||||
.run();
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -35,7 +49,13 @@ const BUTTONS: CmsEditorButton[] = [
|
|||
return cmsEditor.getEditor().chain().focus().toggleCode().run();
|
||||
},
|
||||
can: (cmsEditor) => {
|
||||
return cmsEditor.getEditor().can().chain().focus().toggleCode().run();
|
||||
return cmsEditor
|
||||
.getEditor()
|
||||
.can()
|
||||
.chain()
|
||||
.focus()
|
||||
.toggleCode()
|
||||
.run();
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -44,13 +64,24 @@ const BUTTONS: CmsEditorButton[] = [
|
|||
return cmsEditor.getEditor().chain().focus().toggleStrike().run();
|
||||
},
|
||||
can: (cmsEditor) => {
|
||||
return cmsEditor.getEditor().can().chain().focus().toggleStrike().run();
|
||||
return cmsEditor
|
||||
.getEditor()
|
||||
.can()
|
||||
.chain()
|
||||
.focus()
|
||||
.toggleStrike()
|
||||
.run();
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: ".tiptap-subscript",
|
||||
command: (cmsEditor) => {
|
||||
return cmsEditor.getEditor().chain().focus().toggleSubscript().run();
|
||||
return cmsEditor
|
||||
.getEditor()
|
||||
.chain()
|
||||
.focus()
|
||||
.toggleSubscript()
|
||||
.run();
|
||||
},
|
||||
can: (cmsEditor) => {
|
||||
return cmsEditor
|
||||
|
|
@ -65,7 +96,12 @@ const BUTTONS: CmsEditorButton[] = [
|
|||
{
|
||||
selector: ".tiptap-superscript",
|
||||
command: (cmsEditor) => {
|
||||
return cmsEditor.getEditor().chain().focus().toggleSuperscript().run();
|
||||
return cmsEditor
|
||||
.getEditor()
|
||||
.chain()
|
||||
.focus()
|
||||
.toggleSuperscript()
|
||||
.run();
|
||||
},
|
||||
can: (cmsEditor) => {
|
||||
return cmsEditor
|
||||
|
|
@ -183,13 +219,24 @@ const BUTTONS: CmsEditorButton[] = [
|
|||
return cmsEditor.getEditor().chain().focus().clearNodes().run();
|
||||
},
|
||||
can: (cmsEditor) => {
|
||||
return cmsEditor.getEditor().can().chain().focus().clearNodes().run();
|
||||
return cmsEditor
|
||||
.getEditor()
|
||||
.can()
|
||||
.chain()
|
||||
.focus()
|
||||
.clearNodes()
|
||||
.run();
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: ".tiptap-blockquote",
|
||||
command: (cmsEditor) => {
|
||||
return cmsEditor.getEditor().chain().focus().toggleBlockquote().run();
|
||||
return cmsEditor
|
||||
.getEditor()
|
||||
.chain()
|
||||
.focus()
|
||||
.toggleBlockquote()
|
||||
.run();
|
||||
},
|
||||
can: (cmsEditor) => {
|
||||
return cmsEditor
|
||||
|
|
@ -204,7 +251,12 @@ const BUTTONS: CmsEditorButton[] = [
|
|||
{
|
||||
selector: ".tiptap-codeblock",
|
||||
command: (cmsEditor) => {
|
||||
return cmsEditor.getEditor().chain().focus().toggleCodeBlock().run();
|
||||
return cmsEditor
|
||||
.getEditor()
|
||||
.chain()
|
||||
.focus()
|
||||
.toggleCodeBlock()
|
||||
.run();
|
||||
},
|
||||
can: (cmsEditor) => {
|
||||
return cmsEditor
|
||||
|
|
@ -219,7 +271,12 @@ const BUTTONS: CmsEditorButton[] = [
|
|||
{
|
||||
selector: ".tiptap-ul",
|
||||
command: (cmsEditor) => {
|
||||
return cmsEditor.getEditor().chain().focus().toggleBulletList().run();
|
||||
return cmsEditor
|
||||
.getEditor()
|
||||
.chain()
|
||||
.focus()
|
||||
.toggleBulletList()
|
||||
.run();
|
||||
},
|
||||
can: (cmsEditor) => {
|
||||
return cmsEditor
|
||||
|
|
@ -234,7 +291,12 @@ const BUTTONS: CmsEditorButton[] = [
|
|||
{
|
||||
selector: ".tiptap-ol",
|
||||
command: (cmsEditor) => {
|
||||
return cmsEditor.getEditor().chain().focus().toggleOrderedList().run();
|
||||
return cmsEditor
|
||||
.getEditor()
|
||||
.chain()
|
||||
.focus()
|
||||
.toggleOrderedList()
|
||||
.run();
|
||||
},
|
||||
can: (cmsEditor) => {
|
||||
return cmsEditor
|
||||
|
|
@ -252,7 +314,13 @@ const BUTTONS: CmsEditorButton[] = [
|
|||
return true;
|
||||
},
|
||||
can: (cmsEditor) => {
|
||||
return cmsEditor.getEditor().can().chain().focus().insertTable().run();
|
||||
return cmsEditor
|
||||
.getEditor()
|
||||
.can()
|
||||
.chain()
|
||||
.focus()
|
||||
.insertTable()
|
||||
.run();
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -264,8 +332,12 @@ const BUTTONS: CmsEditorButton[] = [
|
|||
if (!dialog) {
|
||||
return false;
|
||||
}
|
||||
const rowsInput = dialog.querySelector("input#rows") as HTMLInputElement;
|
||||
const colsInput = dialog.querySelector("input#cols") as HTMLInputElement;
|
||||
const rowsInput = dialog.querySelector(
|
||||
"input#rows"
|
||||
) as HTMLInputElement;
|
||||
const colsInput = dialog.querySelector(
|
||||
"input#cols"
|
||||
) as HTMLInputElement;
|
||||
const headerRowInput = dialog.querySelector(
|
||||
"input#headerRow"
|
||||
) as HTMLInputElement;
|
||||
|
|
@ -301,7 +373,13 @@ const BUTTONS: CmsEditorButton[] = [
|
|||
return cmsEditor.getEditor().chain().focus().addRowBefore().run();
|
||||
},
|
||||
can: (cmsEditor) => {
|
||||
return cmsEditor.getEditor().can().chain().focus().addRowBefore().run();
|
||||
return cmsEditor
|
||||
.getEditor()
|
||||
.can()
|
||||
.chain()
|
||||
.focus()
|
||||
.addRowBefore()
|
||||
.run();
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -310,13 +388,24 @@ const BUTTONS: CmsEditorButton[] = [
|
|||
return cmsEditor.getEditor().chain().focus().addRowAfter().run();
|
||||
},
|
||||
can: (cmsEditor) => {
|
||||
return cmsEditor.getEditor().can().chain().focus().addRowAfter().run();
|
||||
return cmsEditor
|
||||
.getEditor()
|
||||
.can()
|
||||
.chain()
|
||||
.focus()
|
||||
.addRowAfter()
|
||||
.run();
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: ".tiptap-insert-table-column-before",
|
||||
command: (cmsEditor) => {
|
||||
return cmsEditor.getEditor().chain().focus().addColumnBefore().run();
|
||||
return cmsEditor
|
||||
.getEditor()
|
||||
.chain()
|
||||
.focus()
|
||||
.addColumnBefore()
|
||||
.run();
|
||||
},
|
||||
can: (cmsEditor) => {
|
||||
return cmsEditor
|
||||
|
|
@ -334,7 +423,13 @@ const BUTTONS: CmsEditorButton[] = [
|
|||
return cmsEditor.getEditor().chain().focus().addColumnAfter().run();
|
||||
},
|
||||
can: (cmsEditor) => {
|
||||
return cmsEditor.getEditor().can().chain().focus().addColumnAfter().run();
|
||||
return cmsEditor
|
||||
.getEditor()
|
||||
.can()
|
||||
.chain()
|
||||
.focus()
|
||||
.addColumnAfter()
|
||||
.run();
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -343,7 +438,13 @@ const BUTTONS: CmsEditorButton[] = [
|
|||
return cmsEditor.getEditor().chain().focus().deleteRow().run();
|
||||
},
|
||||
can: (cmsEditor) => {
|
||||
return cmsEditor.getEditor().can().chain().focus().deleteRow().run();
|
||||
return cmsEditor
|
||||
.getEditor()
|
||||
.can()
|
||||
.chain()
|
||||
.focus()
|
||||
.deleteRow()
|
||||
.run();
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -352,7 +453,13 @@ const BUTTONS: CmsEditorButton[] = [
|
|||
return cmsEditor.getEditor().chain().focus().deleteColumn().run();
|
||||
},
|
||||
can: (cmsEditor) => {
|
||||
return cmsEditor.getEditor().can().chain().focus().deleteColumn().run();
|
||||
return cmsEditor
|
||||
.getEditor()
|
||||
.can()
|
||||
.chain()
|
||||
.focus()
|
||||
.deleteColumn()
|
||||
.run();
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -361,13 +468,24 @@ const BUTTONS: CmsEditorButton[] = [
|
|||
return cmsEditor.getEditor().chain().focus().deleteTable().run();
|
||||
},
|
||||
can: (cmsEditor) => {
|
||||
return cmsEditor.getEditor().can().chain().focus().deleteTable().run();
|
||||
return cmsEditor
|
||||
.getEditor()
|
||||
.can()
|
||||
.chain()
|
||||
.focus()
|
||||
.deleteTable()
|
||||
.run();
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: ".tiptap-toggle-table-header-row",
|
||||
command: (cmsEditor) => {
|
||||
return cmsEditor.getEditor().chain().focus().toggleHeaderRow().run();
|
||||
return cmsEditor
|
||||
.getEditor()
|
||||
.chain()
|
||||
.focus()
|
||||
.toggleHeaderRow()
|
||||
.run();
|
||||
},
|
||||
can: (cmsEditor) => {
|
||||
return cmsEditor
|
||||
|
|
@ -382,7 +500,12 @@ const BUTTONS: CmsEditorButton[] = [
|
|||
{
|
||||
selector: ".tiptap-toggle-table-header-column",
|
||||
command: (cmsEditor) => {
|
||||
return cmsEditor.getEditor().chain().focus().toggleHeaderColumn().run();
|
||||
return cmsEditor
|
||||
.getEditor()
|
||||
.chain()
|
||||
.focus()
|
||||
.toggleHeaderColumn()
|
||||
.run();
|
||||
},
|
||||
can: (cmsEditor) => {
|
||||
return cmsEditor
|
||||
|
|
@ -400,7 +523,13 @@ const BUTTONS: CmsEditorButton[] = [
|
|||
return cmsEditor.getEditor().chain().focus().mergeCells().run();
|
||||
},
|
||||
can: (cmsEditor) => {
|
||||
return cmsEditor.getEditor().can().chain().focus().mergeCells().run();
|
||||
return cmsEditor
|
||||
.getEditor()
|
||||
.can()
|
||||
.chain()
|
||||
.focus()
|
||||
.mergeCells()
|
||||
.run();
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -409,13 +538,24 @@ const BUTTONS: CmsEditorButton[] = [
|
|||
return cmsEditor.getEditor().chain().focus().splitCell().run();
|
||||
},
|
||||
can: (cmsEditor) => {
|
||||
return cmsEditor.getEditor().can().chain().focus().splitCell().run();
|
||||
return cmsEditor
|
||||
.getEditor()
|
||||
.can()
|
||||
.chain()
|
||||
.focus()
|
||||
.splitCell()
|
||||
.run();
|
||||
},
|
||||
},
|
||||
{
|
||||
selector: ".tiptap-insert-image",
|
||||
command: (cmsEditor) => {
|
||||
return cmsEditor.getEditor().chain().focus().setLibreCmsImage().run();
|
||||
return cmsEditor
|
||||
.getEditor()
|
||||
.chain()
|
||||
.focus()
|
||||
.setLibreCmsImage()
|
||||
.run();
|
||||
},
|
||||
can: (cmsEditor) => {
|
||||
return cmsEditor
|
||||
|
|
@ -444,166 +584,24 @@ const BUTTONS: CmsEditorButton[] = [
|
|||
// }
|
||||
];
|
||||
|
||||
declare module "@tiptap/core" {
|
||||
interface Commands<ReturnType> {
|
||||
libreCmsImageNode: {
|
||||
setLibreCmsImage: (attributes?: { language: string }) => ReturnType;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const ImageNode = Node.create({
|
||||
name: "libreCmsImageNode",
|
||||
|
||||
content: "inline*",
|
||||
|
||||
marks: "",
|
||||
|
||||
group: "block",
|
||||
|
||||
code: false,
|
||||
|
||||
defining: true,
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
altText: {
|
||||
parseHTML: (element) => {
|
||||
const imgElem = element.querySelector("img");
|
||||
if (imgElem) {
|
||||
return imgElem.alt;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
},
|
||||
},
|
||||
figCaption: {
|
||||
parseHTML: (element) => {
|
||||
const figCaptionElem = element.querySelector("figcaption");
|
||||
if (figCaptionElem) {
|
||||
return figCaptionElem.innerHTML;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
setLibreCmsImage:
|
||||
(attributes) =>
|
||||
({ commands }) => {
|
||||
return commands.setNode("libreCmsImageNode", attributes);
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
addNodeView() {
|
||||
return ({
|
||||
editor,
|
||||
node,
|
||||
getPos,
|
||||
HTMLAttributes,
|
||||
decorations,
|
||||
extension,
|
||||
}) => {
|
||||
const templateNode = document.querySelector("#librecms-image-node-view");
|
||||
if (!templateNode) {
|
||||
const errorMsg = document.createElement("div");
|
||||
errorMsg.classList.add("alert");
|
||||
errorMsg.classList.add("alert-danger");
|
||||
errorMsg.textContent = "Failed to create image node view.";
|
||||
|
||||
return errorMsg;
|
||||
}
|
||||
|
||||
console.log("Node: ");
|
||||
console.dir(node);
|
||||
|
||||
console.log(`getPos =`);
|
||||
console.dir(getPos);
|
||||
|
||||
console.log(`HTMLAttributes =`);
|
||||
console.dir(HTMLAttributes);
|
||||
|
||||
console.log(`decorations = ${decorations}`);
|
||||
console.dir(decorations);
|
||||
|
||||
console.log(`extension = ${extension}`);
|
||||
console.dir(extension);
|
||||
|
||||
const template = templateNode as HTMLTemplateElement;
|
||||
const nodeView = template.content.cloneNode(true) as HTMLElement;
|
||||
const dialogId = `librecms-image-node-view-settings-dialog-${Math.floor(
|
||||
Math.random() * 1000000000
|
||||
)}`;
|
||||
const dialogTitleId = `${dialogId}-title`;
|
||||
const settingsButtonElem = nodeView.querySelector(
|
||||
".image-settings-button"
|
||||
);
|
||||
if (settingsButtonElem) {
|
||||
settingsButtonElem.setAttribute("data-target", `#${dialogId}`);
|
||||
}
|
||||
const settingsDialogElem = nodeView.querySelector(
|
||||
".modal.image-settings-dialog"
|
||||
);
|
||||
if (settingsDialogElem) {
|
||||
settingsDialogElem.id = dialogId;
|
||||
settingsDialogElem.setAttribute("aria-labelledby", dialogTitleId);
|
||||
|
||||
const settingsDialogTitleElem =
|
||||
settingsDialogElem.querySelector(".modal-title");
|
||||
if (settingsDialogTitleElem) {
|
||||
settingsDialogTitleElem.id = dialogTitleId;
|
||||
}
|
||||
}
|
||||
|
||||
const figCaptionElem = nodeView.querySelector("figcaption");
|
||||
if (node.attrs.figCaption !== "" && figCaptionElem) {
|
||||
figCaptionElem.innerHTML = node.attrs.figCaption;
|
||||
}
|
||||
const dom = document.createElement("div");
|
||||
dom.classList.add("librecms-image-node-view", "p-2");
|
||||
dom.appendChild(nodeView);
|
||||
|
||||
return {
|
||||
dom,
|
||||
};
|
||||
};
|
||||
},
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
tag: "figure[data-type=librecms-image-node]",
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
renderHTML({ HTMLAttributes }) {
|
||||
return [
|
||||
"figure",
|
||||
mergeAttributes(HTMLAttributes, { "data-type": "librecms-image-node" }),
|
||||
["img", { src: "/assets/remixicon/image-line.svg" }],
|
||||
["figcaption"],
|
||||
];
|
||||
},
|
||||
});
|
||||
|
||||
class CmsEditor {
|
||||
private editor: Editor;
|
||||
private editorElem: HTMLElement;
|
||||
private saveUrl: string;
|
||||
|
||||
public constructor(editor: Editor, editorElem: HTMLElement, 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");
|
||||
const buttonsElem = editorElem.querySelector(
|
||||
".cms-tiptap-editor-buttons"
|
||||
);
|
||||
if (buttonsElem) {
|
||||
for (const button of BUTTONS) {
|
||||
const buttonElem = buttonsElem.querySelector(button.selector);
|
||||
|
|
@ -667,7 +665,10 @@ class CmsEditor {
|
|||
});
|
||||
if (response.ok) {
|
||||
} else {
|
||||
this.showSaveFailedMessage(response.status, response.statusText);
|
||||
this.showSaveFailedMessage(
|
||||
response.status,
|
||||
response.statusText
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
this.showSaveFailedErrMessage(error as string);
|
||||
|
|
@ -761,7 +762,10 @@ class CmsEditorBuilder {
|
|||
if (response.ok) {
|
||||
return await response.text();
|
||||
} else {
|
||||
this.showLoadVariantFailedMessage(response.status, response.statusText);
|
||||
this.showLoadVariantFailedMessage(
|
||||
response.status,
|
||||
response.statusText
|
||||
);
|
||||
throw `Failed to load variant. Status: ${response.status}, Status Text: ${response.statusText}`;
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,235 @@
|
|||
import { Node, nodeInputRule, mergeAttributes } from "@tiptap/core";
|
||||
|
||||
declare module "@tiptap/core" {
|
||||
interface Commands<ReturnType> {
|
||||
libreCmsImageNode: {
|
||||
setLibreCmsImage: (attributes?: { language: string }) => ReturnType;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const ImageNode = Node.create({
|
||||
name: "libreCmsImageNode",
|
||||
|
||||
content: "inline*",
|
||||
|
||||
marks: "",
|
||||
|
||||
group: "block",
|
||||
|
||||
code: false,
|
||||
|
||||
defining: true,
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
align: {
|
||||
parseHTML: (element) => {
|
||||
if (element.hasAttribute("data-align")) {
|
||||
return element.getAttribute("data-align");
|
||||
} else {
|
||||
return "center";
|
||||
}
|
||||
},
|
||||
},
|
||||
altText: {
|
||||
parseHTML: (element) => {
|
||||
const imgElem = element.querySelector("img");
|
||||
if (imgElem) {
|
||||
return imgElem.alt;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
},
|
||||
},
|
||||
figCaption: {
|
||||
parseHTML: (element) => {
|
||||
const figCaptionElem = element.querySelector("figcaption");
|
||||
if (figCaptionElem) {
|
||||
return figCaptionElem.innerHTML;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
},
|
||||
},
|
||||
fullSizeOverlay: {
|
||||
parseHTML: (element) => {
|
||||
if (element.hasAttribute("data-fullsizeoverlay")) {
|
||||
return (
|
||||
element.getAttribute("data-fullsizeoverlay") ===
|
||||
"true"
|
||||
);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
},
|
||||
size: {
|
||||
parseHTML: (element) => {
|
||||
if (element.hasAttribute("data-size")) {
|
||||
return element.getAttribute("data-size");
|
||||
} else {
|
||||
return "50";
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
setLibreCmsImage:
|
||||
(attributes) =>
|
||||
({ commands }) => {
|
||||
return commands.setNode("libreCmsImageNode", attributes);
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
addNodeView() {
|
||||
return ({
|
||||
editor,
|
||||
node,
|
||||
getPos,
|
||||
HTMLAttributes,
|
||||
decorations,
|
||||
extension,
|
||||
}) => {
|
||||
const templateNode = document.querySelector(
|
||||
"#librecms-image-node-view"
|
||||
);
|
||||
if (!templateNode) {
|
||||
const errorMsg = document.createElement("div");
|
||||
errorMsg.classList.add("alert");
|
||||
errorMsg.classList.add("alert-danger");
|
||||
errorMsg.textContent = "Failed to create image node view.";
|
||||
|
||||
return errorMsg;
|
||||
}
|
||||
|
||||
// console.log("Node: ");
|
||||
// console.dir(node);
|
||||
|
||||
// console.log(`getPos =`);
|
||||
// console.dir(getPos);
|
||||
|
||||
// console.log(`HTMLAttributes =`);
|
||||
// console.dir(HTMLAttributes);
|
||||
|
||||
// console.log(`decorations = ${decorations}`);
|
||||
// console.dir(decorations);
|
||||
|
||||
// console.log(`extension = ${extension}`);
|
||||
// console.dir(extension);
|
||||
|
||||
const template = templateNode as HTMLTemplateElement;
|
||||
const nodeView = template.content.cloneNode(true) as HTMLElement;
|
||||
const dialogIdNr = Math.floor(Math.random() * 1000000000);
|
||||
const dialogId = `librecms-image-node-view-settings-dialog-${dialogIdNr}`;
|
||||
const dialogTitleId = `${dialogId}-title`;
|
||||
const settingsButtonElem = nodeView.querySelector(
|
||||
".image-settings-button"
|
||||
);
|
||||
if (settingsButtonElem) {
|
||||
settingsButtonElem.setAttribute("data-target", `#${dialogId}`);
|
||||
}
|
||||
const settingsDialogElem = nodeView.querySelector(
|
||||
".modal.image-settings-dialog"
|
||||
);
|
||||
if (settingsDialogElem) {
|
||||
settingsDialogElem.id = dialogId;
|
||||
settingsDialogElem.setAttribute(
|
||||
"aria-labelledby",
|
||||
dialogTitleId
|
||||
);
|
||||
|
||||
const settingsDialogTitleElem =
|
||||
settingsDialogElem.querySelector(".modal-title");
|
||||
if (settingsDialogTitleElem) {
|
||||
settingsDialogTitleElem.id = dialogTitleId;
|
||||
}
|
||||
|
||||
const settingDialogIds =
|
||||
settingsDialogElem.querySelectorAll("*[id]");
|
||||
for (let i = 0; i < settingDialogIds.length; i++) {
|
||||
const elemWithId = settingDialogIds.item(i);
|
||||
elemWithId.id = `${elemWithId.id}-${dialogIdNr}`;
|
||||
}
|
||||
const settingDialogLabels =
|
||||
settingsDialogElem.querySelectorAll("*[for]");
|
||||
for (let i = 0; i < settingDialogLabels.length; i++) {
|
||||
const label = settingDialogLabels.item(i);
|
||||
label.id = `${label.id}-${dialogIdNr}`;
|
||||
}
|
||||
const describedElems = settingsDialogElem.querySelectorAll(
|
||||
"*[aria-describedby]"
|
||||
);
|
||||
for (let i = 0; i < describedElems.length; i++) {
|
||||
const describedElem = describedElems.item(i);
|
||||
describedElem.id = `${describedElem.id}-${dialogIdNr}`;
|
||||
}
|
||||
|
||||
const submitButton = settingsDialogElem.querySelector(
|
||||
"button.image-settings-dialog-save"
|
||||
);
|
||||
|
||||
const altTextInput = settingsDialogElem.querySelector(
|
||||
`input#alttext-${dialogIdNr}`
|
||||
);
|
||||
const alignSelect = settingsDialogElem.querySelector(
|
||||
`input#align-${dialogIdNr}`
|
||||
);
|
||||
|
||||
// ToDo: Init inputs
|
||||
|
||||
if (submitButton) {
|
||||
submitButton.addEventListener("click", (event) => {
|
||||
// ToDo: Read values from inputs and update node.attrs
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const figCaptionElem = nodeView.querySelector("figcaption");
|
||||
if (node.attrs.figCaption !== "" && figCaptionElem) {
|
||||
figCaptionElem.innerHTML = node.attrs.figCaption;
|
||||
}
|
||||
const dom = document.createElement("div");
|
||||
dom.classList.add("librecms-image-node-view", "p-2");
|
||||
dom.appendChild(nodeView);
|
||||
|
||||
return {
|
||||
dom,
|
||||
};
|
||||
};
|
||||
},
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
tag: "figure[data-type=librecms-image-node]",
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
renderHTML({ node, HTMLAttributes }) {
|
||||
console.log("node = ");
|
||||
console.dir(node);
|
||||
return [
|
||||
"figure",
|
||||
mergeAttributes(HTMLAttributes, {
|
||||
"data-align": node.attrs.align,
|
||||
"data-fullsizeoverlay": node.attrs.fullSizeOverlay,
|
||||
"data-size": node.attrs.size,
|
||||
"data-type": "librecms-image-node",
|
||||
}),
|
||||
[
|
||||
"img",
|
||||
{
|
||||
alt: node.attrs.altText,
|
||||
src: "/assets/remixicon/image-line.svg",
|
||||
},
|
||||
],
|
||||
["figcaption"],
|
||||
];
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { ImageNode } from "./image-node";
|
||||
|
||||
export * from "./image-node";
|
||||
|
||||
export default ImageNode;
|
||||
Loading…
Reference in New Issue