Files
Trilium/src/mathui.js

253 lines
6.4 KiB
JavaScript
Raw Normal View History

2019-08-31 20:48:37 +03:00
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
import ClickObserver from '@ckeditor/ckeditor5-engine/src/view/observer/clickobserver';
import ContextualBalloon from '@ckeditor/ckeditor5-ui/src/panel/balloon/contextualballoon';
import clickOutsideHandler from '@ckeditor/ckeditor5-ui/src/bindings/clickoutsidehandler';
2019-10-06 11:13:17 +03:00
import uid from '@ckeditor/ckeditor5-utils/src/uid';
2019-10-09 12:21:55 +03:00
import global from '@ckeditor/ckeditor5-utils/src/dom/global';
import { getBalloonPositionData } from './utils';
2019-08-31 20:48:37 +03:00
import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
import MainFormView from './ui/mainformview';
// Need math commands from there
import MathEditing from './mathediting';
2019-10-09 12:21:55 +03:00
import mathIcon from '../theme/icons/math.svg';
2019-08-31 20:48:37 +03:00
const mathKeystroke = 'Ctrl+M';
export default class MathUI extends Plugin {
static get requires() {
return [ ContextualBalloon, MathEditing ];
}
static get pluginName() {
return 'MathUI';
}
init() {
const editor = this.editor;
editor.editing.view.addObserver( ClickObserver );
2019-10-06 11:13:17 +03:00
this._previewUid = `math-preview-${ uid() }`;
2019-10-09 12:21:55 +03:00
this.formView = this._createFormView();
2019-08-31 20:48:37 +03:00
this._balloon = editor.plugins.get( ContextualBalloon );
this._createToolbarMathButton();
this._enableUserBalloonInteractions();
}
destroy() {
super.destroy();
2019-10-09 12:21:55 +03:00
this.formView.destroy();
2019-10-09 10:50:12 +03:00
// Destroy preview element
2021-04-16 17:00:22 -05:00
const previewEl = global.document.getElementById( this._previewUid );
if ( previewEl ) {
previewEl.parentNode.removeChild( previewEl );
2019-10-09 10:50:12 +03:00
}
2019-08-31 20:48:37 +03:00
}
_showUI() {
const editor = this.editor;
const mathCommand = editor.commands.get( 'math' );
if ( !mathCommand.isEnabled ) {
return;
}
this._addFormView();
this._balloon.showStack( 'main' );
}
_createFormView() {
const editor = this.editor;
const mathCommand = editor.commands.get( 'math' );
2019-09-17 16:19:35 +03:00
const mathConfig = editor.config.get( 'math' );
2019-08-31 20:48:37 +03:00
const formView = new MainFormView(
editor.locale,
mathConfig.engine,
mathConfig.lazyLoad,
mathConfig.enablePreview,
this._previewUid,
mathConfig.previewClassName,
mathConfig.popupClassName
);
2019-08-31 20:48:37 +03:00
formView.mathInputView.bind( 'value' ).to( mathCommand, 'value' );
formView.displayButtonView.bind( 'isOn' ).to( mathCommand, 'display' );
2019-08-31 20:48:37 +03:00
2019-10-11 19:22:03 +03:00
// Form elements should be read-only when corresponding commands are disabled.
formView.mathInputView.bind( 'isReadOnly' ).to( mathCommand, 'isEnabled', value => !value );
formView.saveButtonView.bind( 'isEnabled' ).to( mathCommand );
formView.displayButtonView.bind( 'isEnabled' ).to( mathCommand );
2019-09-17 15:47:58 +03:00
// Listen to submit button click
2019-08-31 20:48:37 +03:00
this.listenTo( formView, 'submit', () => {
2019-09-17 19:39:49 +03:00
editor.execute( 'math', formView.equation, formView.displayButtonView.isOn, mathConfig.outputType, mathConfig.forceOutputType );
2019-08-31 20:48:37 +03:00
this._closeFormView();
} );
// Listen to cancel button click
this.listenTo( formView, 'cancel', () => {
this._closeFormView();
} );
// Close plugin ui, if esc is pressed (while ui is focused)
formView.keystrokes.set( 'esc', ( data, cancel ) => {
this._closeFormView();
cancel();
} );
return formView;
}
_addFormView() {
if ( this._isFormInPanel ) {
return;
}
const editor = this.editor;
const mathCommand = editor.commands.get( 'math' );
this._balloon.add( {
2019-10-09 12:21:55 +03:00
view: this.formView,
position: getBalloonPositionData( editor )
2019-08-31 20:48:37 +03:00
} );
2019-10-09 12:21:55 +03:00
if ( this._balloon.visibleView === this.formView ) {
this.formView.mathInputView.select();
2019-08-31 20:48:37 +03:00
}
2019-10-03 03:47:26 +03:00
// Show preview element
2021-04-16 17:00:22 -05:00
const previewEl = global.document.getElementById( this._previewUid );
if ( previewEl && this.formView.previewEnabled ) {
2019-10-04 20:01:28 +03:00
// Force refresh preview
2019-10-09 12:21:55 +03:00
this.formView.mathView.updateMath();
2019-10-03 03:47:26 +03:00
}
2019-10-09 12:21:55 +03:00
this.formView.equation = mathCommand.value || '';
this.formView.displayButtonView.isOn = mathCommand.display || false;
2019-08-31 20:48:37 +03:00
}
_hideUI() {
if ( !this._isFormInPanel ) {
return;
}
const editor = this.editor;
this.stopListening( editor.ui, 'update' );
this.stopListening( this._balloon, 'change:visibleView' );
editor.editing.view.focus();
// Remove form first because it's on top of the stack.
this._removeFormView();
}
2019-09-28 13:01:08 +03:00
2019-08-31 20:48:37 +03:00
_closeFormView() {
const mathCommand = this.editor.commands.get( 'math' );
if ( mathCommand.value !== undefined ) {
this._removeFormView();
} else {
this._hideUI();
}
}
_removeFormView() {
if ( this._isFormInPanel ) {
2019-10-09 12:21:55 +03:00
this.formView.saveButtonView.focus();
2019-08-31 20:48:37 +03:00
2019-10-09 12:21:55 +03:00
this._balloon.remove( this.formView );
2019-08-31 20:48:37 +03:00
2019-10-03 03:47:26 +03:00
// Hide preview element
2021-04-16 17:00:22 -05:00
const previewEl = global.document.getElementById( this._previewUid );
if ( previewEl ) {
previewEl.style.visibility = 'hidden';
2019-10-03 03:47:26 +03:00
}
2019-08-31 20:48:37 +03:00
this.editor.editing.view.focus();
}
}
_createToolbarMathButton() {
const editor = this.editor;
const mathCommand = editor.commands.get( 'math' );
const t = editor.t;
// Handle the `Ctrl+M` keystroke and show the panel.
editor.keystrokes.set( mathKeystroke, ( keyEvtData, cancel ) => {
// Prevent focusing the search bar in FF and opening new tab in Edge. #153, #154.
cancel();
if ( mathCommand.isEnabled ) {
this._showUI();
}
} );
this.editor.ui.componentFactory.add( 'math', locale => {
const button = new ButtonView( locale );
button.isEnabled = true;
button.label = t( 'Insert math' );
2019-10-09 12:21:55 +03:00
button.icon = mathIcon;
2019-08-31 20:48:37 +03:00
button.keystroke = mathKeystroke;
button.tooltip = true;
button.isToggleable = true;
button.bind( 'isEnabled' ).to( mathCommand, 'isEnabled' );
this.listenTo( button, 'execute', () => this._showUI() );
return button;
} );
}
_enableUserBalloonInteractions() {
const editor = this.editor;
const viewDocument = this.editor.editing.view.document;
this.listenTo( viewDocument, 'click', () => {
const mathCommand = editor.commands.get( 'math' );
if ( mathCommand.value ) {
if ( mathCommand.isEnabled ) {
this._showUI();
}
}
} );
2019-08-31 20:48:37 +03:00
// Close the panel on the Esc key press when the editable has focus and the balloon is visible.
editor.keystrokes.set( 'Esc', ( data, cancel ) => {
2019-08-31 20:48:37 +03:00
if ( this._isUIVisible ) {
this._hideUI();
cancel();
}
} );
// Close on click outside of balloon panel element.
clickOutsideHandler( {
2019-10-09 12:21:55 +03:00
emitter: this.formView,
2019-08-31 20:48:37 +03:00
activator: () => this._isFormInPanel,
contextElements: [ this._balloon.view.element ],
callback: () => this._hideUI()
} );
}
get _isUIVisible() {
const visibleView = this._balloon.visibleView;
2019-10-09 12:21:55 +03:00
return visibleView == this.formView;
2019-08-31 20:48:37 +03:00
}
get _isFormInPanel() {
2019-10-09 12:21:55 +03:00
return this._balloon.hasView( this.formView );
2019-08-31 20:48:37 +03:00
}
}