webui/src/components/common/ckeditor/plugins/field/fieldediting.js
2026-01-20 21:48:51 +00:00

119 lines
4.1 KiB
JavaScript

import { Plugin, Widget, toWidget, viewToModelPositionOutsideModelElement } from 'ckeditor5';
import FieldCommand from './fieldcommand';
String.prototype.rtrim = function (s) {
if (s === undefined)
s = '\\s';
return this.replace(new RegExp("[" + s + "]*$"), '');
};
String.prototype.ltrim = function (s) {
if (s === undefined)
s = '\\s';
return this.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( '{' + field.name.ltrim('{').rtrim('}') + '}' );
viewWriter.insert( viewWriter.createPositionAt( fieldView, 0 ), innerText );
return fieldView;
}
}
}