editor_advanced_link-8.x-1.8/js/ckeditor5_plugins/editorAdvancedLink/src/editoradvancedlinkui.js
js/ckeditor5_plugins/editorAdvancedLink/src/editoradvancedlinkui.js
// eslint-disable-next-line import/no-extraneous-dependencies
import { Plugin } from 'ckeditor5/src/core';
// eslint-disable-next-line import/no-extraneous-dependencies
import {
LabeledFieldView,
createLabeledInputText,
CollapsibleView,
FormRowView,
} from 'ckeditor5/src/ui';
import { additionalFormElements, additionalFormGroups } from './utils';
export default class EditorAdvancedLinkUI extends Plugin {
init() {
// Using example from CKEditor5 docs:
// https://ckeditor.com/docs/ckeditor5/latest/framework/how-tos.html#how-to-add-a-custom-button-to-the-link-dialog
const editor = this.editor;
const contextualBalloonPlugin = editor.plugins.get('ContextualBalloon');
const linkUI = editor.plugins.get('LinkUI');
this.listenTo(contextualBalloonPlugin, 'change:visibleView', (evt, name, visibleView) => {
if (visibleView !== linkUI.formView) {
return;
}
// Detach the listener.
this.stopListening(contextualBalloonPlugin, 'change:visibleView');
this.linkFormView = linkUI.formView;
this._registerComponents();
});
}
/**
* Add advanced fields to link popup.
*/
_registerComponents() {
const editor = this.editor;
const linkFormView = editor.plugins.get('LinkUI').formView;
const linkCommand = editor.commands.get('link');
const { enabledModelNames } = editor.plugins.get(
'EditorAdvancedLinkEditing',
);
//Insert below CKEditor5's "Displayed text" field.
let insertIndex = 2;
let fieldCount = 0;
enabledModelNames.forEach((modelName, index) => {
// Skip if field already exists.
if (typeof linkFormView[modelName] === 'undefined') {
const options = additionalFormElements[modelName];
let parentGroup = linkFormView[options.group];
let parentForm = parentGroup ?? linkFormView;
// Skip if group already exists.
if (!!options.group && !parentGroup) {
const groupOptions = additionalFormGroups[options.group];
parentGroup = this._createGroup(options.group, groupOptions.label);
// Insert group into link form.
linkFormView.children.add(
parentGroup,
insertIndex
);
// Increase insert index for link form.
insertIndex++;
// Add group to focus array.
linkFormView._focusables.add(parentGroup);
linkFormView.focusTracker.add(parentGroup.element);
// Track group in link form object.
linkFormView[options.group] = parentGroup;
parentForm = parentGroup;
}
const newTextField = this._createTextField(options.label);
// Insert new row/field into parent form (group or link form).
parentForm.children.add(
this._createFormRow(newTextField),
parentForm === linkFormView ? insertIndex : parentForm.children.length
);
// Increase insert index if field was added to link form.
if (parentForm === linkFormView) {
insertIndex++;
}
linkFormView._focusables.add(newTextField);
linkFormView.focusTracker.add(newTextField.element);
// Track field in link form object.
linkFormView[modelName] = newTextField;
fieldCount++;
// Bind values of new fields.
linkFormView[modelName].fieldView
.bind('value').to(linkCommand, modelName);
// Note: Copy & pasted from LinkUI.
// https://github.com/ckeditor/ckeditor5/blob/f0a093339631b774b2d3422e2a579e27be79bbeb/packages/ckeditor5-link/src/linkui.js#L333-L333
linkFormView[modelName].fieldView.value = linkCommand[modelName] || '';
}
});
if (fieldCount > 0) {
this._handleExtraFormFieldSubmit(enabledModelNames);
}
}
/**
* Creates a labeled input view for text field with a label.
*/
_createTextField(label) {
const { editor } = this;
const { locale } = editor;
const t = locale.t;
const labeledInput = new LabeledFieldView(locale, createLabeledInputText);
labeledInput.label = label;
labeledInput.class = 'ck-labeled-field-view_full-width';
return labeledInput;
}
/**
* Creates a row.
*/
_createFormRow(child) {
const { editor } = this;
const { locale } = editor;
return new FormRowView(locale, {
children: [
child
],
class: [
'ck-form__row_large-bottom-padding'
]
});
}
/**
* Creates a collapsible group.
*/
_createGroup(groupName, label) {
const { editor } = this;
const { locale } = editor;
const group = new CollapsibleView(locale);
group.label = label;
group.set('isCollapsed', true);
return group;
}
/**
* Update link form state.
*/
_handleExtraFormFieldSubmit(modelNames) {
const { editor } = this;
const linkFormView = editor.plugins.get('LinkUI').formView;
const linkCommand = editor.commands.get('link');
// Attach listener to link editing form submit.
// This event listener is not executed when link properties (target) are updated.
this.listenTo(linkFormView, 'submit', () => {
if (linkFormView.isValid()) {
const advancedAttributeValues = this._getSubmittedValues(linkFormView, modelNames);
linkCommand.once('execute', (evt, args) => {
// CKEditor v45 includes a 'displayed text' input value. If present,
// send this information along so we can properly update the selection.
let displayedText = '';
if (typeof linkFormView.displayedTextInputView != 'undefined') {
displayedText = linkFormView.displayedTextInputView.fieldView.element.value;
}
// Inject advanced attributes into link command arguments.
args[1]['advanced_attributes'] = advancedAttributeValues;
args[1]['advanced_attributes']['displayedText'] = displayedText;
}, {
priority: 'highest'
});
}
}, {
priority: 'high',
});
}
_getSubmittedValues(linkFormView, modelNames) {
const values = modelNames.reduce((state, modelName) => {
const oldValue = linkFormView[modelName].fieldView.value;
const newValue = linkFormView[modelName].fieldView.element.value;
state[modelName] = linkFormView[modelName].fieldView.element.value;
return state;
}, {});
return values;
}
}
