webui/src/components/common/ckeditor/plugins/field/fieldediting.js

128 lines
3.7 KiB
JavaScript

import {
Plugin,
Widget,
toWidget,
viewToModelPositionOutsideModelElement,
} from "ckeditor5";
import FieldCommand from "./fieldcommand";
// Helper functions to avoid extending String prototype
const rtrim = (str, s) => {
if (s === undefined) s = "\\s";
return str.replace(new RegExp("[" + s + "]*$"), "");
};
const ltrim = (str, s) => {
if (s === undefined) s = "\\s";
return str.replace(new RegExp("^[" + s + "]*"), "");
};
export class FieldConfig {
fields = [];
}
export default class FieldEditing extends Plugin {
static get requires() {
return [Widget];
}
init() {
this._defineSchema();
this._defineConverters();
this.editor.commands.add("field", new FieldCommand(this.editor));
this.editor.editing.mapper.on(
"viewToModelPosition",
viewToModelPositionOutsideModelElement(this.editor.model, (viewElement) =>
viewElement.hasClass("field"),
),
);
this.editor.config.define("fieldConfig", FieldConfig);
}
_defineSchema() {
const schema = this.editor.model.schema;
schema.register("field", {
// Behaves like a self-contained inline object (e.g. an inline image)
// allowed in places where $text is allowed (e.g. in paragraphs).
// The inline widget can have the same attributes as text (for example linkHref, bold).
inheritAllFrom: "$inlineObject",
// The field can have many types, like date, name, surname, etc:
allowAttributes: ["field"],
});
}
_defineConverters() {
const conversion = this.editor.conversion;
conversion.for("upcast").elementToElement({
view: {
name: "span",
classes: ["field"],
},
model: (viewElement, conversionApi) => {
// Extract the "name" from "{name}".
if (viewElement === undefined) {
return null;
}
const { writer } = conversionApi;
const name = viewElement.getChild(0)?._textData;
const fieldtype = viewElement.getAttribute("fieldtype");
const guid = viewElement.getAttribute("guid");
const fieldid = viewElement.getAttribute("fieldid");
const field = {
name,
type: fieldtype,
guid,
fieldid,
};
return writer.createElement("field", { field });
},
});
conversion.for("editingDowncast").elementToElement({
model: "field",
view: (modelItem, { writer: viewWriter }) => {
const widgetElement = createfieldView(modelItem, viewWriter);
// Enable widget handling on a field element inside the editing view.
return toWidget(widgetElement, viewWriter);
},
});
conversion.for("dataDowncast").elementToElement({
model: "field",
view: (modelItem, { writer: viewWriter }) =>
createfieldView(modelItem, viewWriter),
});
// Helper method for both downcast converters.
// function createfieldView( modelItem : Element, viewWriter : DowncastWriter ) {
function createfieldView(modelItem, viewWriter) {
const field = modelItem.getAttribute("field"); //todo any is a cop out.
const fieldView = viewWriter.createContainerElement("span", {
class: "field",
fieldType: field.type,
guid: field.guid,
fieldid: field.guid !== undefined ? null : field.id,
});
// Insert the field name (as a text).
const innerText = viewWriter.createText(
"{" + rtrim(ltrim(field.name, "{"), "}") + "}",
);
viewWriter.insert(viewWriter.createPositionAt(fieldView, 0), innerText);
return fieldView;
}
}
}