Image Node for Tiptap working

pull/10/head
Jens Pelzetter 2021-10-09 21:03:26 +02:00
parent 2169e07ab2
commit 5513dc178e
8 changed files with 478 additions and 87 deletions

View File

@ -6,10 +6,10 @@
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"
@ -63,14 +63,26 @@
required="true"
shortDescription="The back URL."
type="String"
/>
/>
<cc:attribute
name="baseUrl"
required="true"
shortDescription="Base URL for generating URLs of API endpoints."
type="String"
/>
<cc:attribute
name="canEdit"
default="true"
required="false"
shortDescription="Can the current user edit the text?"
type="boolean"
/>
/>
<cc:attribute
name="contentSection"
required="true"
shortDescription="The name of the current content section. Required for generating URLs of API endpoints."
type="String"
/>
<!-- <cc:attribute
name="editButtonLabel"
default="#{CmsAdminMessages['edit_button.label']}"
@ -84,41 +96,41 @@
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"
@ -131,13 +143,13 @@
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}"
@ -150,38 +162,38 @@
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"
@ -249,7 +261,7 @@
required="true"
shortDescription="Title/Heading of the editor widget"
type="String"
/>
/>
<!-- <cc:attribute
name="unusedLocales"
required="true"
@ -267,7 +279,7 @@
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
@ -290,11 +302,13 @@
<cc:implementation>
<div
class="cms-editor"
data-baseUrl="#{cc.attrs.baseUrl}"
data-contentsection="#{cc.attrs.contentSection}"
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>
@ -376,14 +390,14 @@
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" /> -->
<librecms:remixSvgIcon icon="ri-italic" />
</button>
@ -391,7 +405,7 @@
class="btn btn-outline-dark tiptap-strong-emph"
title="#{CmsAdminMessages['cms_editor.buttons.strong_emph']}"
type="button"
>
>
<!-- <bootstrap:svgIcon icon="type-bold" />-->
<librecms:remixSvgIcon icon="ri-bold" />
</button>
@ -399,7 +413,7 @@
class="btn btn-outline-dark tiptap-code"
title="#{CmsAdminMessages['cms_editor.buttons.code']}"
type="button"
>
>
<!--<bootstrap:svgIcon icon="code" />-->
<librecms:remixSvgIcon icon="ri-code-view" />
</button>
@ -407,7 +421,7 @@
class="btn btn-outline-dark tiptap-strikethrough"
title="#{CmsAdminMessages['cms_editor.buttons.strikethrough']}"
type="button"
>
>
<!--<bootstrap:svgIcon icon="type-strikethrough" />-->
<librecms:remixSvgIcon icon="ri-strikethrough" />
</button>
@ -415,7 +429,7 @@
class="btn btn-outline-dark tiptap-subscript"
title="#{CmsAdminMessages['cms_editor.buttons.subscript']}"
type="button"
>
>
<!--<span aria-hidden="true">x<sub>n</sub></span>-->
<librecms:remixSvgIcon icon="ri-subscript" />
</button>
@ -423,7 +437,7 @@
class="btn btn-outline-dark tiptap-superscript"
title="#{CmsAdminMessages['cms_editor.buttons.superscript']}"
type="button"
>
>
<!--<span aria-hidden="true">x<sup>n</sup></span>-->
<librecms:remixSvgIcon icon="ri-superscript" />
</button>
@ -434,8 +448,8 @@
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>-->
>
<!--<span aria-hidden="true">H#{level}</span>-->
<librecms:remixSvgIcon icon="ri-h-#{level}" />
</button>
</ui:repeat>
@ -445,7 +459,7 @@
class="btn btn-outline-dark tiptap-paragraph"
title="#{CmsAdminMessages['cms_editor.buttons.paragraph']}"
type="button"
>
>
<!--<bootstrap:svgIcon icon="type" />-->
<librecms:remixSvgIcon icon="ri-text" />
</button>
@ -453,7 +467,7 @@
class="btn btn-outline-dark tiptap-blockquote"
title="#{CmsAdminMessages['cms_editor.buttons.blockquote']}"
type="button"
>
>
<!--bootstrap:svgIcon icon="blockquote-left" />-->
<librecms:remixSvgIcon icon="ri-double-quotes-l" />
</button>
@ -461,7 +475,7 @@
class="btn btn-outline-dark tiptap-codeblock"
title="#{CmsAdminMessages['cms_editor.buttons.codeblock']}"
type="button"
>
>
<!--<bootstrap:svgIcon icon="code-square" />-->
<librecms:remixSvgIcon icon="ri-code-box-line" />
</button>
@ -471,7 +485,7 @@
class="btn btn-outline-dark tiptap-ul"
title="#{CmsAdminMessages['cms_editor.buttons.ul']}"
type="button"
>
>
<!--<bootstrap:svgIcon icon="list-ul" />-->
<librecms:remixSvgIcon icon="ri-list-unordered" />
</button>
@ -479,7 +493,7 @@
class="btn btn-outline-dark tiptap-ol"
title="#{CmsAdminMessages['cms_editor.buttons.ol']}"
type="button"
>
>
<!--<bootstrap:svgIcon icon="list-ol" />-->
<librecms:remixSvgIcon icon="ri-list-ordered" />
</button>
@ -491,7 +505,7 @@
data-toggle="modal"
title="#{CmsAdminMessage['cms_editor.buttons.insert_table']}"
type="button"
>
>
<!--<bootstrap:svgIcon icon="table" />-->
<librecms:remixSvgIcon icon="ri-table-2" />
</button>
@ -501,14 +515,14 @@
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
@ -516,7 +530,7 @@
class="close"
data-dismiss="modal"
type="button"
>
>
<bootstrap:svgIcon icon="x" />
</button>
</div>
@ -526,30 +540,30 @@
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
btn btn-warning
cms-editor-cancel-button
"
data-dismiss="modal"
type="button"
>
>
#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.close']}
</button>
<button
@ -557,7 +571,7 @@
data-dismiss="modal"
data-backdrop="false"
type="submit"
>
>
#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.submit']}
</button>
</div>
@ -568,23 +582,23 @@
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" />
>
<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" />-->
>
<!--<img src="#{request.contextPath}/icons/cms-editor/table-add-row-before.svg" />-->
<librecms:remixSvgIcon icon="ri-insert-row-top" />
</button>
<button
class="btn btn-outline-dark tiptap-insert-table-row-after"
title="#{CmsAdminMessages['cms_editor.buttons.insert_table_row_after']}"
type="button"
>
<!--<img src="#{request.contextPath}/icons/cms-editor/table-add-row-after.svg" />-->
>
<!--<img src="#{request.contextPath}/icons/cms-editor/table-add-row-after.svg" />-->
<librecms:remixSvgIcon icon="ri-insert-row-bottom" />
</button>
<button
@ -598,24 +612,24 @@
class="btn btn-outline-dark tiptap-insert-table-column-before"
title="#{CmsAdminMessages['cms_editor.buttons.insert_table_column_before']}"
type="button"
>
<!--<img src="#{request.contextPath}/icons/cms-editor/table-add-column-before.svg" />-->
>
<!--<img src="#{request.contextPath}/icons/cms-editor/table-add-column-before.svg" />-->
<librecms:remixSvgIcon icon="ri-insert-column-left" />
</button>
<button
class="btn btn-outline-dark tiptap-insert-table-column-after"
title="#{CmsAdminMessages['cms_editor.buttons.insert_table_column_after']}"
type="button"
>
<!--<img src="#{request.contextPath}/icons/cms-editor/table-add-column-after.svg" />-->
<librecms:remixSvgIcon icon="ri-insert-column-right" />
>
<!--<img src="#{request.contextPath}/icons/cms-editor/table-add-column-after.svg" />-->
<librecms:remixSvgIcon icon="ri-insert-column-right" />
</button>
<button
class="btn btn-outline-dark tiptap-remove-table-column"
title="#{CmsAdminMessages['cms_editor.buttons.remove_table_column']}"
type="button"
>
<!--<img src="#{request.contextPath}/icons/cms-editor/table-remove-column.svg" />-->
>
<!--<img src="#{request.contextPath}/icons/cms-editor/table-remove-column.svg" />-->
<librecms:remixSvgIcon icon="ri-delete-column" />
</button>
<button
@ -655,6 +669,20 @@
</button>
</div>
</div>
<template id="librecms-image-node-view-row">
<tr>
<td class="col-name"></td>
<td class="col-type"></td>
<td class="col-action">
<button class="btn btn-primary"
data-assetuuid=""
data-dismiss="modal"
type="button">
#{CmsAssetsStepsDefaultMessagesBundle['assetpicker.select']}
</button>
</td>
</tr>
</template>
<template id="librecms-image-node-view">
<figure>
<!--img src="#{request.contextPath}/assets/remixicon/image-line.svg" />-->
@ -662,10 +690,59 @@
src=""
style="min-height: 4em;" />
<div class="border-light librecms-image-node-view-buttons">
<button class="btn btn-outline-dark select-image-button">
<button class="btn btn-outline-dark select-image-button"
data-toggle="modal"
data-target="#librecms-imge-node-select-image-dialog-">
<librecms:remixSvgIcon icon="ri-image-edit-line" />
<span class="sr-only">#{CmsAdminMessages['cms_editor.image_node_view.select_image.label']}</span>
</button>
<div aria-hidden="true"
aria-labelledby="librecms-image-node-select-image-dialog-title-"
class="modal fade select-image-dialog ccm-cms-asset-picker"
id="librecms-image-node-select-image-dialog-"
tabindex="-1">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title"
id="librecms-image-node-select-image-dialog-title-">
#{CmsAdminMessages['cms_editor.image_node_view.selectimage.dialog.title']}
<button aria-label="#{CmsAdminMessages['cms_editor.image_node_view.selectimage.dialog.close']}"
class="close"
data-dismiss="modal"
type="button">
<bootstrap:svgIcon icon="x-circle" />
</button>
</h3>
</div>
<div class="modal-body">
<bootstrap:formGroupText
class="assetpicker-filter"
help="#{CmsAssetsStepsDefaultMessagesBundle['assetpicker.filter.help']}"
inputId="#{cc.attrs.assetPickerId}-filter"
label="#{CmsAssetsStepsDefaultMessagesBundle['assetpicker.filter.label']}"
name="" />
<table>
<thead>
<tr>
<th>#{CmsAssetsStepsDefaultMessagesBundle['assetpicker.column.name']}</th>
<th>#{CmsAssetsStepsDefaultMessagesBundle['assetpicker.column.type']}</th>
<th>#{CmsAssetsStepsDefaultMessagesBundle['assetpicker.column.action']}</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<div class="modal-footer">
<button class="btn btn-warning"
data-dismiss="modal"
type="button">
#{CmsAdminMessages['cms_editor.image_node_view.selectimage.dialog.close']}
</button>
</div>
</div>
</div>
</div>
<button class="btn btn-outline-dark image-settings-button"
data-toggle="modal"
data-target="#librecms-image-node-view-settings-dialog-">
@ -754,6 +831,10 @@
</form>
</div>
</div>
<!--<button class="btn btn-outline-dark image-remove-button">
<librecms:remixSvgIcon icon="ri-close-line" />
<span class="sr-only">#{CmsAdminMessages['cms_editor.image_node_view.remove.label']}</span>
</button>-->
</div>
<figcaption aria-label="#{CmsAdminMessages['cms_editor.image_node_view.figcaption.label']}"
contenteditable="true">
@ -765,19 +846,19 @@
class="cms-tiptap-editor-canvas border"
data-locale="#{cc.attrs.selectedLocale}"
data-variant-url="#{cc.attrs.variantUrl}"
></div>
></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>

View File

@ -20,7 +20,9 @@
<c:if test="#{CmsArticleTextBodyStep.canEdit}">
<librecms:cmsEditor
backUrl="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@article-text"
baseUrl="#{mvc.basePath}"
canEdit="#{CmsArticleTextBodyStep.canEdit}"
contentSection="#{ContentSectionModel.sectionName}"
editMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@article-text/edit"
editorId="cms-article-text-editor"
objectIdentifier="#{CmsSelectedDocumentModel.itemPath}"

View File

@ -890,3 +890,6 @@ cms_editor.image_node_view.settings.dialog.fullsizeoverlay.help=Enables a fullsi
cms_editor.image_node_view.settings.dialog.save=Submit
cms_editor.image_node_view.figcaption.label=Caption
cms_editor.image_node_view.figcaption.placeholder=Caption
cms_editor.image_node_view.selectimage.dialog.title=Select image
cms_editor.image_node_view.selectimage.dialog.close=Cancel
cms_editor.image_node_view.remove.label=Remove image

View File

@ -891,3 +891,6 @@ cms_editor.image_node_view.settings.dialog.fullsizeoverlay.help=Aktiviert ein Gr
cms_editor.image_node_view.settings.dialog.save=\u00dcbernehmen
cms_editor.image_node_view.figcaption.label=Bildunterschrift
cms_editor.image_node_view.figcaption.placeholder=Bildunterschrift
cms_editor.image_node_view.selectimage.dialog.title=Bild ausw\u00e4hlen
cms_editor.image_node_view.selectimage.dialog.close=Abbrechen
cms_editor.image_node_view.remove.label=Bild entfernen

View File

@ -134,6 +134,7 @@ table.wor{
.cms-tiptap-editor-canvas {
max-height: 75vh;
min-height: 35vh;
overflow: scroll;
}
@ -144,9 +145,76 @@ table.wor{
position: relative;
.librecms-image-node-view-buttons {
background-color: rgba(255,255,255,0.5);
position: absolute;
top: 0;
right: 0;
width: 3em;
top: 0.5rem;
right: 0.5rem;
width: 2.66rem;
& > button {
width: 2.66rem;
}
}
&.librecms-image-node-width-25 {
width: 25%;
}
&.librecms-image-node-width-33 {
width: 33%;
}
&.librecms-image-node-width-50 {
width: 50%;
}
&.librecms-image-node-width-66 {
width: 66%;
}
&.librecms-image-node-width-75 {
width: 75%;
}
&.librecms-image-node-width-100 {
width: 100%;
}
&.librecms-image-node-align-floatleft {
float: left;
}
&.librecms-image-node-align-center {
margin-left: auto;
margin-right: auto;
}
&.librecms-image-node-align-floatright {
float: right;
}
figure img {
max-width: 100%;
}
.select-image-dialog {
table {
border: none;
width: 100%;
thead th {
text-align: center;
}
thead tr, tbody tr:nth-child(even) {
background-color: #ededed;
}
tr, th, td {
border: none
}
}
}
}

View File

@ -16,7 +16,7 @@ async function initAssetPicker(assetPickerElem: Element) {
console.log(`assetPickerId = ${assetPickerId}`);
const fetchUrl = `${baseUrl}/content-sections/${contentSection}/assets?type=${assetType}`;
const fetchUrl = `/content-sections/${contentSection}/assets?type=${assetType}`;
try {
const response = await fetch(fetchUrl);

View File

@ -341,9 +341,9 @@ const BUTTONS: CmsEditorButton[] = [
const headerRowInput = dialog.querySelector(
"input#headerRow"
) as HTMLInputElement;
console.log(`rowsInput = ${rowsInput}`);
console.log(`colsInput = ${colsInput}`);
console.log(`headerRowInput = ${headerRowInput}`);
// 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;
@ -555,6 +555,7 @@ const BUTTONS: CmsEditorButton[] = [
.chain()
.focus()
.setLibreCmsImage()
.insertContent("<p></p>")
.run();
},
can: (cmsEditor) => {
@ -598,7 +599,7 @@ class CmsEditor {
this.editorElem = editorElem;
this.saveUrl = saveUrl;
console.log("initializing editor buttons");
// console.log("initializing editor buttons");
const buttonsElem = editorElem.querySelector(
".cms-tiptap-editor-buttons"
);
@ -620,7 +621,7 @@ class CmsEditor {
}
editor.on("selectionUpdate", ({ editor }: { editor: Editor }) => {
console.log(`checkButton - this.editorElem = ${this.editorElem}`);
// console.log(`checkButton - this.editorElem = ${this.editorElem}`);
const buttonsElem = editorElem.querySelector(
".cms-tiptap-editor-buttons"
);
@ -642,7 +643,7 @@ class CmsEditor {
}
});
console.log(`editorElem = ${editorElem}`);
// console.log(`editorElem = ${editorElem}`);
const saveButton = editorElem.querySelector(".cms-editor-save-button");
saveButton?.addEventListener("click", (event) => this.save(event));
@ -716,7 +717,7 @@ class CmsEditorBuilder {
}
public async buildEditor(): Promise<CmsEditor> {
console.log("Build CMS Editor.");
// console.log("Build CMS Editor.");
const canvasElement = this.editorElem.querySelector(
".cms-tiptap-editor-canvas"
);

View File

@ -1,4 +1,5 @@
import { Node, nodeInputRule, mergeAttributes } from "@tiptap/core";
import { Node, nodeInputRule, mergeAttributes, Range } from "@tiptap/core";
import { Node as ProsemirrorNode } from "prosemirror-model";
declare module "@tiptap/core" {
interface Commands<ReturnType> {
@ -64,6 +65,16 @@ export const ImageNode = Node.create({
}
},
},
imgSrc: {
parseHTML: (element) => {
const imgElem = element.querySelector("img");
if (imgElem) {
return imgElem.src;
} else {
return "";
}
},
},
size: {
parseHTML: (element) => {
if (element.hasAttribute("data-size")) {
@ -121,32 +132,101 @@ export const ImageNode = Node.create({
// console.log(`extension = ${extension}`);
// console.dir(extension);
const dom = document.createElement("div");
dom.classList.add("librecms-image-node-view", "p-2");
if (!node.attrs.size) {
node.attrs.size = "50";
}
dom.classList.add(`librecms-image-node-width-${node.attrs.size}`);
if (!node.attrs.align) {
node.attrs.align = "center";
}
dom.classList.add(`librecms-image-node-align-${node.attrs.align}`);
const template = templateNode as HTMLTemplateElement;
const nodeView = template.content.cloneNode(true) as HTMLElement;
const imgElem = nodeView.querySelector("img");
const dialogIdNr = Math.floor(Math.random() * 1000000000);
const dialogId = `librecms-image-node-view-settings-dialog-${dialogIdNr}`;
const dialogTitleId = `${dialogId}-title`;
const selectDialogId = `librecms-image-node-select-image-dialog-title-${dialogIdNr}`;
const selectDialogTitleId = `librecms-image-node-select-image-dialog-title-${dialogIdNr}-title`;
const selectButtonElem = nodeView.querySelector(
".select-image-button"
);
if (selectButtonElem) {
selectButtonElem.setAttribute(
"data-target",
`#${selectDialogId}`
);
}
const selectDialogElem = nodeView.querySelector(
".modal.select-image-dialog"
);
if (selectDialogElem) {
selectDialogElem.id = selectDialogId;
selectDialogElem.setAttribute(
"aria-labelledby",
selectDialogTitleId
);
const selectDialogTitleElem =
selectDialogElem.querySelector(".modal-title");
if (selectDialogTitleElem) {
selectDialogTitleElem.id = selectDialogTitleId;
}
const selectDialogIds =
selectDialogElem.querySelectorAll("*[id]");
selectDialogIds.forEach((elemWithId) => {
elemWithId.id = `${elemWithId.id}-${dialogIdNr}`;
});
const selectDialogLabels =
selectDialogElem.querySelectorAll("*[for]");
selectDialogLabels.forEach((label) => {
label.setAttribute(
"for",
`${label.getAttribute("for")}-${dialogIdNr}`
);
});
const describedElems = selectDialogElem.querySelectorAll(
"*[aria-describedby]"
);
describedElems.forEach((describedElem) => {
describedElem.setAttribute(
"aria-describedby",
`${describedElem.getAttribute(
"aria-describedby"
)}-${dialogIdNr}`
);
});
}
const settingsDialogId = `librecms-image-node-view-settings-dialog-${dialogIdNr}`;
const settingsDialogTitleId = `${settingsDialogId}-title`;
const settingsButtonElem = nodeView.querySelector(
".image-settings-button"
);
if (settingsButtonElem) {
settingsButtonElem.setAttribute("data-target", `#${dialogId}`);
settingsButtonElem.setAttribute(
"data-target",
`#${settingsDialogId}`
);
}
const settingsDialogElem = nodeView.querySelector(
".modal.image-settings-dialog"
);
if (settingsDialogElem) {
settingsDialogElem.id = dialogId;
settingsDialogElem.id = settingsDialogId;
settingsDialogElem.setAttribute(
"aria-labelledby",
dialogTitleId
settingsDialogTitleId
);
const settingsDialogTitleElem =
settingsDialogElem.querySelector(".modal-title");
if (settingsDialogTitleElem) {
settingsDialogTitleElem.id = dialogTitleId;
settingsDialogTitleElem.id = settingsDialogTitleId;
}
const settingDialogIds =
@ -245,6 +325,9 @@ export const ImageNode = Node.create({
const inputElem = altTextInput as HTMLInputElement;
if (altTextInput) {
node.attrs.altText = inputElem.value;
if (imgElem) {
imgElem.alt = inputElem.value;
}
} else {
console.warn("Input for alt text not found.");
}
@ -253,6 +336,15 @@ export const ImageNode = Node.create({
const selectElem = alignSelect as HTMLSelectElement;
node.attrs.align =
selectElem.selectedOptions.item(0)?.value;
["floatleft", "center", "floatright"].forEach(
(align) =>
dom.classList.remove(
`librecms-image-node-align-${align}`
)
);
dom.classList.add(
`librecms-image-node-align-${node.attrs.align}`
);
} else {
console.warn(
"Select for image alignment not found."
@ -263,6 +355,15 @@ export const ImageNode = Node.create({
const selectElem = sizeSelect as HTMLSelectElement;
node.attrs.size =
selectElem.selectedOptions.item(0)?.value;
["25", "33", "50", "66", "75", "100"].forEach(
(size) =>
dom.classList.remove(
`librecms-image-node-width-${size}`
)
);
dom.classList.add(
`librecms-image-node-width-${node.attrs.size}`
);
} else {
console.warn("Select for image size not found.");
}
@ -282,16 +383,43 @@ export const ImageNode = Node.create({
"Submit button for image settings dialog not found."
);
}
// const removeImageButton = nodeView.querySelector(
// ".image-remove-button"
// );
// if (removeImageButton) {
// removeImageButton.addEventListener("click", (event) => {
// if (typeof getPos === "number") {
// const from = getPos as number;
// const range: Range = {
// from,
// to: node.nodeSize,
// };
// editor.chain().focus().deleteRange(range);
// }
// });
// } else {
// console.error("removeImageButton not found.");
// }
}
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);
if (selectButtonElem) {
if (imgElem) {
selectButtonElem.addEventListener("click", (event) =>
loadImages(event, node, imgElem)
);
} else {
console.error("img elem not found.");
}
}
return {
dom,
};
@ -307,11 +435,11 @@ export const ImageNode = Node.create({
},
renderHTML({ node, HTMLAttributes }) {
console.log("node = ");
console.dir(node);
// console.log("node = ");
// console.dir(node);
return [
"figure",
mergeAttributes(HTMLAttributes, {
mergeAttributes({
"data-align": node.attrs.align,
"data-fullsizeoverlay": node.attrs.fullSizeOverlay,
"data-size": node.attrs.size,
@ -321,10 +449,115 @@ export const ImageNode = Node.create({
"img",
{
alt: node.attrs.altText,
src: "/assets/remixicon/image-line.svg",
src: node.attrs.imgSrc,
},
],
["figcaption"],
];
},
});
function loadImages(
event: Event,
node: ProsemirrorNode<any>,
imgElem: HTMLImageElement
) {
console.log("Loading images...");
const eventTarget = event.currentTarget as HTMLElement;
const editorElem = document.querySelector(".cms-editor");
if (!editorElem) {
return;
}
const baseUrl = editorElem.getAttribute("data-baseUrl");
const contentSection = editorElem.getAttribute("data-contentsection");
const dialogId = eventTarget.getAttribute("data-target");
if (!dialogId) {
console.error("data-target attribute is missing.");
return;
}
const dialog = document.querySelector(dialogId);
if (!dialog) {
console.error("dialog element not found is missing.");
return;
}
const rowTemplateResult = document.querySelector(
"#librecms-image-node-view-row"
);
if (!rowTemplateResult) {
console.error("template for result row not found.");
return;
}
const rowTemplate = rowTemplateResult as HTMLTemplateElement;
const table = dialog.querySelector("table");
if (!table) {
console.error("result table not found.");
return;
}
const tableBody = table.querySelector("tbody");
if (!tableBody) {
console.error("table body not found.");
return;
}
tableBody.innerHTML = "";
const fetchUrl = `/content-sections/${contentSection}/assets?type=org.librecms.assets.Image`;
fetch(fetchUrl)
.then((response) => {
if (response.ok) {
response
.json()
.then((data) => {
const images = data as [];
for (const image of images) {
const row = rowTemplate.content.cloneNode(
true
) as Element;
const colName = row.querySelector(".col-name");
const colType = row.querySelector(".col-type");
const selectButton =
row.querySelector(".col-action button");
if (colName) {
colName.textContent = image["name"];
}
if (colType) {
colType.textContent = image["name"];
}
if (selectButton) {
selectButton.setAttribute(
"data-imageuuid",
image["uuid"]
);
selectButton.addEventListener(
"click",
(event) => {
const imgUrl = `/content-sections/info/images/uuid-${image["uuid"]}`;
node.attrs.imgSrc = imgUrl;
if (imgElem) {
imgElem.src = imgUrl;
} else {
console.error(
"img element not found."
);
}
}
);
}
tableBody.appendChild(row);
}
})
.catch((error) => {
console.error(error);
});
} else {
console.error(
`Error. Status: ${response.status}. Status Text: ${response.statusText}`
);
}
})
.catch((error) => {
console.error(error);
});
}