ckeditor5-1.0.x-dev/js/drupal/src/drupalMedia/src/mediaimagetextalternative/mediaimagetextalternativeui.js
js/drupal/src/drupalMedia/src/mediaimagetextalternative/mediaimagetextalternativeui.js
import { Plugin, icons } from 'ckeditor5/src/core';
import {
ButtonView,
ContextualBalloon,
clickOutsideHandler,
} from 'ckeditor5/src/ui';
import { getSelectedDrupalMediaWidget } from '../utils';
import { getBalloonPositionData, repositionContextualBalloon } from '../ui/utils';
import TextAlternativeFormView from './ui/textalternativeformview';
/**
* The media image text alternative UI plugin.
*
* @see https://github.com/ckeditor/ckeditor5/blob/master/packages/ckeditor5-image/src/imagetextalternative/imagetextalternativeui.js
*/
export default class MediaImageTextAlternativeUi extends Plugin {
/**
* @inheritDoc
*/
static get requires() {
return [ContextualBalloon];
}
/**
* @inheritDoc
*/
static get pluginName() {
return 'MediaImageTextAlternativeUi';
}
/**
* @inheritDoc
*/
init() {
this._createButton();
this._createForm();
}
/**
* @inheritDoc
*/
destroy() {
super.destroy();
this._form.destroy();
}
/**
* Creates a button showing the balloon panel for changing the image text
* alternative and registers it in the editor ComponentFactory.
*/
_createButton() {
const editor = this.editor;
const t = editor.t;
editor.ui.componentFactory.add('mediaImageTextAlternative', (locale) => {
const command = editor.commands.get('mediaImageTextAlternative');
const view = new ButtonView(locale);
view.set({
label: t('Override media image text alternative'),
icon: icons.lowVision,
tooltip: true,
});
view.bind('isVisible').to(command, 'isEnabled');
this.listenTo(view, 'execute', () => {
this._showForm();
});
return view;
});
}
/**
* Creates the {@link module:image/imagetextalternative/ui/textalternativeformview~TextAlternativeFormView}
* form.
*
* @private
*/
_createForm() {
const editor = this.editor;
const view = editor.editing.view;
const viewDocument = view.document;
/**
* The contextual balloon plugin instance.
*/
this._balloon = this.editor.plugins.get('ContextualBalloon');
/**
* A form containing a textarea and buttons, used to change the `alt` text value.
*/
this._form = new TextAlternativeFormView(editor.locale);
// Render the form so its #element is available for clickOutsideHandler.
this._form.render();
this.listenTo(this._form, 'submit', () => {
editor.execute('mediaImageTextAlternative', {
newValue: this._form.labeledInput.fieldView.element.value,
});
this._hideForm(true);
});
this.listenTo(this._form, 'cancel', () => {
this._hideForm(true);
});
// Close the form on Esc key press.
this._form.keystrokes.set('Esc', (data, cancel) => {
this._hideForm(true);
cancel();
});
// Reposition the balloon or hide the form if an image widget is no longer selected.
this.listenTo(editor.ui, 'update', () => {
if (!getSelectedDrupalMediaWidget(viewDocument.selection)) {
this._hideForm(true);
} else if (this._isVisible) {
repositionContextualBalloon(editor);
}
});
// Close on click outside of balloon panel element.
clickOutsideHandler({
emitter: this._form,
activator: () => this._isVisible,
contextElements: [this._balloon.view.element],
callback: () => this._hideForm(),
});
}
/**
* Shows the form in a balloon.
*/
_showForm() {
if (this._isVisible) {
return;
}
const editor = this.editor;
const command = editor.commands.get('mediaImageTextAlternative');
const labeledInput = this._form.labeledInput;
this._form.disableCssTransitions();
if (!this._isInBalloon) {
this._balloon.add({
view: this._form,
position: getBalloonPositionData(editor),
});
}
// Make sure that each time the panel shows up, the field remains in sync with the value of
// the command. If the user typed in the input, then canceled the balloon (`labeledInput#value`
// stays unaltered) and re-opened it without changing the value of the command, they would see the
// old value instead of the actual value of the command.
// https://github.com/ckeditor/ckeditor5-image/issues/114
labeledInput.fieldView.value = labeledInput.fieldView.element.value =
command.value || '';
this._form.labeledInput.fieldView.select();
this._form.enableCssTransitions();
}
/**
* Removes the {@link #_form} from the {@link #_balloon}.
*
* @param {Boolean} [focusEditable=false] Controls whether the editing view is focused afterwards.
* @private
*/
_hideForm(focusEditable) {
if (!this._isInBalloon) {
return;
}
// Blur the input element before removing it from DOM to prevent issues in some browsers.
// See https://github.com/ckeditor/ckeditor5/issues/1501.
if (this._form.focusTracker.isFocused) {
this._form.saveButtonView.focus();
}
this._balloon.remove(this._form);
if (focusEditable) {
this.editor.editing.view.focus();
}
}
/**
* Returns `true` when the form is the visible view in the balloon.
*
* @type {Boolean}
*/
get _isVisible() {
return this._balloon.visibleView === this._form;
}
/**
* Returns `true` when the form is in the balloon.
*
* @type {Boolean}
*/
get _isInBalloon() {
return this._balloon.hasView(this._form);
}
}
