ckeditor5-image-upload-base64
Version:
The development environment of CKEditor 5 – the best browser-based rich text editor.
329 lines (274 loc) • 8.73 kB
JavaScript
/**
* @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @module media-embed/ui/mediaformview
*/
import View from '@ckeditor/ckeditor5-ui/src/view';
import ViewCollection from '@ckeditor/ckeditor5-ui/src/viewcollection';
import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
import LabeledFieldView from '@ckeditor/ckeditor5-ui/src/labeledfield/labeledfieldview';
import { createLabeledInputText } from '@ckeditor/ckeditor5-ui/src/labeledfield/utils';
import submitHandler from '@ckeditor/ckeditor5-ui/src/bindings/submithandler';
import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker';
import FocusCycler from '@ckeditor/ckeditor5-ui/src/focuscycler';
import KeystrokeHandler from '@ckeditor/ckeditor5-utils/src/keystrokehandler';
import checkIcon from '@ckeditor/ckeditor5-core/theme/icons/check.svg';
import cancelIcon from '@ckeditor/ckeditor5-core/theme/icons/cancel.svg';
import '../../theme/mediaform.css';
/**
* The media form view controller class.
*
* See {@link module:media-embed/ui/mediaformview~MediaFormView}.
*
* @extends module:ui/view~View
*/
export default class MediaFormView extends View {
/**
* @param {Array.<Function>} validators Form validators used by {@link #isValid}.
* @param {module:utils/locale~Locale} [locale] The localization services instance.
*/
constructor( validators, locale ) {
super( locale );
const t = locale.t;
/**
* Tracks information about DOM focus in the form.
*
* @readonly
* @member {module:utils/focustracker~FocusTracker}
*/
this.focusTracker = new FocusTracker();
/**
* An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
*
* @readonly
* @member {module:utils/keystrokehandler~KeystrokeHandler}
*/
this.keystrokes = new KeystrokeHandler();
/**
* The URL input view.
*
* @member {module:ui/labeledfield/labeledfieldview~LabeledFieldView}
*/
this.urlInputView = this._createUrlInput();
/**
* The Save button view.
*
* @member {module:ui/button/buttonview~ButtonView}
*/
this.saveButtonView = this._createButton( t( 'Save' ), checkIcon, 'ck-button-save' );
this.saveButtonView.type = 'submit';
/**
* The Cancel button view.
*
* @member {module:ui/button/buttonview~ButtonView}
*/
this.cancelButtonView = this._createButton( t( 'Cancel' ), cancelIcon, 'ck-button-cancel', 'cancel' );
/**
* A collection of views that can be focused in the form.
*
* @readonly
* @protected
* @member {module:ui/viewcollection~ViewCollection}
*/
this._focusables = new ViewCollection();
/**
* Helps cycling over {@link #_focusables} in the form.
*
* @readonly
* @protected
* @member {module:ui/focuscycler~FocusCycler}
*/
this._focusCycler = new FocusCycler( {
focusables: this._focusables,
focusTracker: this.focusTracker,
keystrokeHandler: this.keystrokes,
actions: {
// Navigate form fields backwards using the Shift + Tab keystroke.
focusPrevious: 'shift + tab',
// Navigate form fields forwards using the Tab key.
focusNext: 'tab'
}
} );
/**
* An array of form validators used by {@link #isValid}.
*
* @readonly
* @protected
* @member {Array.<Function>}
*/
this._validators = validators;
this.setTemplate( {
tag: 'form',
attributes: {
class: [
'ck',
'ck-media-form'
],
tabindex: '-1'
},
children: [
this.urlInputView,
this.saveButtonView,
this.cancelButtonView
]
} );
/**
* The default info text for the {@link #urlInputView}.
*
* @private
* @member {String} #_urlInputViewInfoDefault
*/
/**
* The info text with an additional tip for the {@link #urlInputView},
* displayed when the input has some value.
*
* @private
* @member {String} #_urlInputViewInfoTip
*/
}
/**
* @inheritDoc
*/
render() {
super.render();
submitHandler( {
view: this
} );
const childViews = [
this.urlInputView,
this.saveButtonView,
this.cancelButtonView
];
childViews.forEach( v => {
// Register the view as focusable.
this._focusables.add( v );
// Register the view in the focus tracker.
this.focusTracker.add( v.element );
} );
// Start listening for the keystrokes coming from #element.
this.keystrokes.listenTo( this.element );
const stopPropagation = data => data.stopPropagation();
// Since the form is in the dropdown panel which is a child of the toolbar, the toolbar's
// keystroke handler would take over the key management in the URL input. We need to prevent
// this ASAP. Otherwise, the basic caret movement using the arrow keys will be impossible.
this.keystrokes.set( 'arrowright', stopPropagation );
this.keystrokes.set( 'arrowleft', stopPropagation );
this.keystrokes.set( 'arrowup', stopPropagation );
this.keystrokes.set( 'arrowdown', stopPropagation );
// Intercept the "selectstart" event, which is blocked by default because of the default behavior
// of the DropdownView#panelView.
// TODO: blocking "selectstart" in the #panelView should be configurable per–drop–down instance.
this.listenTo( this.urlInputView.element, 'selectstart', ( evt, domEvt ) => {
domEvt.stopPropagation();
}, { priority: 'high' } );
}
/**
* Focuses the fist {@link #_focusables} in the form.
*/
focus() {
this._focusCycler.focusFirst();
}
/**
* The native DOM `value` of the {@link #urlInputView} element.
*
* **Note**: Do not confuse it with the {@link module:ui/inputtext/inputtextview~InputTextView#value}
* which works one way only and may not represent the actual state of the component in the DOM.
*
* @type {String}
*/
get url() {
return this.urlInputView.fieldView.element.value.trim();
}
set url( url ) {
this.urlInputView.fieldView.element.value = url.trim();
}
/**
* Validates the form and returns `false` when some fields are invalid.
*
* @returns {Boolean}
*/
isValid() {
this.resetFormStatus();
for ( const validator of this._validators ) {
const errorText = validator( this );
// One error per field is enough.
if ( errorText ) {
// Apply updated error.
this.urlInputView.errorText = errorText;
return false;
}
}
return true;
}
/**
* Cleans up the supplementary error and information text of the {@link #urlInputView}
* bringing them back to the state when the form has been displayed for the first time.
*
* See {@link #isValid}.
*/
resetFormStatus() {
this.urlInputView.errorText = null;
this.urlInputView.infoText = this._urlInputViewInfoDefault;
}
/**
* Creates a labeled input view.
*
* @private
* @returns {module:ui/labeledfield/labeledfieldview~LabeledFieldView} Labeled input view instance.
*/
_createUrlInput() {
const t = this.locale.t;
const labeledInput = new LabeledFieldView( this.locale, createLabeledInputText );
const inputField = labeledInput.fieldView;
this._urlInputViewInfoDefault = t( 'Paste the media URL in the input.' );
this._urlInputViewInfoTip = t( 'Tip: Paste the URL into the content to embed faster.' );
labeledInput.label = t( 'Media URL' );
labeledInput.infoText = this._urlInputViewInfoDefault;
inputField.placeholder = 'https://example.com';
inputField.on( 'input', () => {
// Display the tip text only when there's some value. Otherwise fall back to the default info text.
labeledInput.infoText = inputField.element.value ? this._urlInputViewInfoTip : this._urlInputViewInfoDefault;
} );
return labeledInput;
}
/**
* Creates a button view.
*
* @private
* @param {String} label The button label.
* @param {String} icon The button icon.
* @param {String} className The additional button CSS class name.
* @param {String} [eventName] An event name that the `ButtonView#execute` event will be delegated to.
* @returns {module:ui/button/buttonview~ButtonView} The button view instance.
*/
_createButton( label, icon, className, eventName ) {
const button = new ButtonView( this.locale );
button.set( {
label,
icon,
tooltip: true
} );
button.extendTemplate( {
attributes: {
class: className
}
} );
if ( eventName ) {
button.delegate( 'execute' ).to( this, eventName );
}
return button;
}
}
/**
* Fired when the form view is submitted (when one of the children triggered the submit event),
* e.g. click on {@link #saveButtonView}.
*
* @event submit
*/
/**
* Fired when the form view is canceled, e.g. click on {@link #cancelButtonView}.
*
* @event cancel
*/