| Server IP : 54.233.248.239 / Your IP : 172.28.20.13 Web Server : Apache System : Linux ip-172-28-29-189 6.5.0-1014-aws #14~22.04.1-Ubuntu SMP Thu Feb 15 15:27:06 UTC 2024 x86_64 User : www-data ( 33) PHP Version : 7.2.34-43+ubuntu22.04.1+deb.sury.org+1 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals, MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /var/www/html/vinumday2_0/public/plugins/@ckeditor/ckeditor5-ui/src/toolbar/block/ |
Upload File : |
/**
* @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 ui/toolbar/block/blocktoolbar
*/
/* global window */
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
import BlockButtonView from './blockbuttonview';
import BalloonPanelView from '../../panel/balloon/balloonpanelview';
import ToolbarView from '../toolbarview';
import clickOutsideHandler from '../../bindings/clickoutsidehandler';
import { getOptimalPosition } from '@ckeditor/ckeditor5-utils/src/dom/position';
import Rect from '@ckeditor/ckeditor5-utils/src/dom/rect';
import normalizeToolbarConfig from '../normalizetoolbarconfig';
import ResizeObserver from '@ckeditor/ckeditor5-utils/src/dom/resizeobserver';
import toUnit from '@ckeditor/ckeditor5-utils/src/dom/tounit';
import iconPilcrow from '@ckeditor/ckeditor5-core/theme/icons/pilcrow.svg';
const toPx = toUnit( 'px' );
/**
* The block toolbar plugin.
*
* This plugin provides a button positioned next to the block of content where the selection is anchored.
* Upon clicking the button, a dropdown providing access to editor features shows up, as configured in
* {@link module:core/editor/editorconfig~EditorConfig#blockToolbar}.
*
* By default, the button is displayed next to all elements marked in {@link module:engine/model/schema~Schema}
* as `$block` for which the toolbar provides at least one option.
*
* By default, the button is attached so its right boundary is touching the
* {@link module:engine/view/editableelement~EditableElement}:
*
* __ |
* | || This is a block of content that the
* ¯¯ | button is attached to. This is a
* | block of content that the button is
* | attached to.
*
* The position of the button can be adjusted using the CSS `transform` property:
*
* .ck-block-toolbar-button {
* transform: translateX( -10px );
* }
*
* __ |
* | | | This is a block of content that the
* ¯¯ | button is attached to. This is a
* | block of content that the button is
* | attached to.
*
* **Note**: If you plan to run the editor in a right–to–left (RTL) language, keep in mind the button
* will be attached to the **right** boundary of the editable area. In that case, make sure the
* CSS position adjustment works properly by adding the following styles:
*
* .ck[dir="rtl"] .ck-block-toolbar-button {
* transform: translateX( 10px );
* }
*
* @extends module:core/plugin~Plugin
*/
export default class BlockToolbar extends Plugin {
/**
* @inheritDoc
*/
static get pluginName() {
return 'BlockToolbar';
}
/**
* @inheritDoc
*/
constructor( editor ) {
super( editor );
/**
* A cached and normalized `config.blockToolbar` object.
*
* @type {module:core/editor/editorconfig~EditorConfig#blockToolbar}
* @private
*/
this._blockToolbarConfig = normalizeToolbarConfig( this.editor.config.get( 'blockToolbar' ) );
/**
* The toolbar view.
*
* @type {module:ui/toolbar/toolbarview~ToolbarView}
*/
this.toolbarView = this._createToolbarView();
/**
* The balloon panel view, containing the {@link #toolbarView}.
*
* @type {module:ui/panel/balloon/balloonpanelview~BalloonPanelView}
*/
this.panelView = this._createPanelView();
/**
* The button view that opens the {@link #toolbarView}.
*
* @type {module:ui/toolbar/block/blockbuttonview~BlockButtonView}
*/
this.buttonView = this._createButtonView();
/**
* An instance of the resize observer that allows to respond to changes in editable's geometry
* so the toolbar can stay within its boundaries (and group toolbar items that do not fit).
*
* **Note**: Used only when `shouldNotGroupWhenFull` was **not** set in the
* {@link module:core/editor/editorconfig~EditorConfig#blockToolbar configuration}.
*
* **Note:** Created in {@link #afterInit}.
*
* @protected
* @member {module:utils/dom/resizeobserver~ResizeObserver}
*/
this._resizeObserver = null;
// Close the #panelView upon clicking outside of the plugin UI.
clickOutsideHandler( {
emitter: this.panelView,
contextElements: [ this.panelView.element, this.buttonView.element ],
activator: () => this.panelView.isVisible,
callback: () => this._hidePanel()
} );
}
/**
* @inheritDoc
*/
init() {
const editor = this.editor;
// Hides panel on a direct selection change.
this.listenTo( editor.model.document.selection, 'change:range', ( evt, data ) => {
if ( data.directChange ) {
this._hidePanel();
}
} );
this.listenTo( editor.ui, 'update', () => this._updateButton() );
// `low` priority is used because of https://github.com/ckeditor/ckeditor5-core/issues/133.
this.listenTo( editor, 'change:isReadOnly', () => this._updateButton(), { priority: 'low' } );
this.listenTo( editor.ui.focusTracker, 'change:isFocused', () => this._updateButton() );
// Reposition button on resize.
this.listenTo( this.buttonView, 'change:isVisible', ( evt, name, isVisible ) => {
if ( isVisible ) {
// Keep correct position of button and panel on window#resize.
this.buttonView.listenTo( window, 'resize', () => this._updateButton() );
} else {
// Stop repositioning button when is hidden.
this.buttonView.stopListening( window, 'resize' );
// Hide the panel when the button disappears.
this._hidePanel();
}
} );
}
/**
* Fills the toolbar with its items based on the configuration.
*
* **Note:** This needs to be done after all plugins are ready.
*
* @inheritDoc
*/
afterInit() {
const factory = this.editor.ui.componentFactory;
const config = this._blockToolbarConfig;
this.toolbarView.fillFromConfig( config.items, factory );
// Hide panel before executing each button in the panel.
for ( const item of this.toolbarView.items ) {
item.on( 'execute', () => this._hidePanel( true ), { priority: 'high' } );
}
if ( !config.shouldNotGroupWhenFull ) {
this.listenTo( this.editor, 'ready', () => {
const editableElement = this.editor.ui.view.editable.element;
// Set #toolbarView's max-width just after the initialization and update it on the editable resize.
this._resizeObserver = new ResizeObserver( editableElement, () => {
this.toolbarView.maxWidth = this._getToolbarMaxWidth();
} );
} );
}
}
/**
* @inheritDoc
*/
destroy() {
super.destroy();
// Destroy created UI components as they are not automatically destroyed (see ckeditor5#1341).
this.panelView.destroy();
this.buttonView.destroy();
this.toolbarView.destroy();
if ( this._resizeObserver ) {
this._resizeObserver.destroy();
}
}
/**
* Creates the {@link #toolbarView}.
*
* @private
* @returns {module:ui/toolbar/toolbarview~ToolbarView}
*/
_createToolbarView() {
const shouldGroupWhenFull = !this._blockToolbarConfig.shouldNotGroupWhenFull;
const toolbarView = new ToolbarView( this.editor.locale, {
shouldGroupWhenFull
} );
toolbarView.extendTemplate( {
attributes: {
// https://github.com/ckeditor/ckeditor5-editor-inline/issues/11
class: [ 'ck-toolbar_floating' ]
}
} );
// When toolbar lost focus then panel should hide.
toolbarView.focusTracker.on( 'change:isFocused', ( evt, name, is ) => {
if ( !is ) {
this._hidePanel();
}
} );
return toolbarView;
}
/**
* Creates the {@link #panelView}.
*
* @private
* @returns {module:ui/panel/balloon/balloonpanelview~BalloonPanelView}
*/
_createPanelView() {
const editor = this.editor;
const panelView = new BalloonPanelView( editor.locale );
panelView.content.add( this.toolbarView );
panelView.class = 'ck-toolbar-container';
editor.ui.view.body.add( panelView );
editor.ui.focusTracker.add( panelView.element );
// Close #panelView on `Esc` press.
this.toolbarView.keystrokes.set( 'Esc', ( evt, cancel ) => {
this._hidePanel( true );
cancel();
} );
return panelView;
}
/**
* Creates the {@link #buttonView}.
*
* @private
* @returns {module:ui/toolbar/block/blockbuttonview~BlockButtonView}
*/
_createButtonView() {
const editor = this.editor;
const t = editor.t;
const buttonView = new BlockButtonView( editor.locale );
buttonView.set( {
label: t( 'Edit block' ),
icon: iconPilcrow,
withText: false
} );
// Bind the panelView observable properties to the buttonView.
buttonView.bind( 'isOn' ).to( this.panelView, 'isVisible' );
buttonView.bind( 'tooltip' ).to( this.panelView, 'isVisible', isVisible => !isVisible );
// Toggle the panelView upon buttonView#execute.
this.listenTo( buttonView, 'execute', () => {
if ( !this.panelView.isVisible ) {
this._showPanel();
} else {
this._hidePanel( true );
}
} );
editor.ui.view.body.add( buttonView );
editor.ui.focusTracker.add( buttonView.element );
return buttonView;
}
/**
* Shows or hides the button.
* When all the conditions for displaying the button are matched, it shows the button. Hides otherwise.
*
* @private
*/
_updateButton() {
const editor = this.editor;
const model = editor.model;
const view = editor.editing.view;
// Hides the button when the editor is not focused.
if ( !editor.ui.focusTracker.isFocused ) {
this._hideButton();
return;
}
// Hides the button when the editor switches to the read-only mode.
if ( editor.isReadOnly ) {
this._hideButton();
return;
}
// Get the first selected block, button will be attached to this element.
const modelTarget = Array.from( model.document.selection.getSelectedBlocks() )[ 0 ];
// Hides the button when there is no enabled item in toolbar for the current block element.
if ( !modelTarget || Array.from( this.toolbarView.items ).every( item => !item.isEnabled ) ) {
this._hideButton();
return;
}
// Get DOM target element.
const domTarget = view.domConverter.mapViewToDom( editor.editing.mapper.toViewElement( modelTarget ) );
// Show block button.
this.buttonView.isVisible = true;
// Attach block button to target DOM element.
this._attachButtonToElement( domTarget );
// When panel is opened then refresh it position to be properly aligned with block button.
if ( this.panelView.isVisible ) {
this._showPanel();
}
}
/**
* Hides the button.
*
* @private
*/
_hideButton() {
this.buttonView.isVisible = false;
}
/**
* Shows the {@link #toolbarView} attached to the {@link #buttonView}.
* If the toolbar is already visible, then it simply repositions it.
*
* @private
*/
_showPanel() {
const wasVisible = this.panelView.isVisible;
// So here's the thing: If there was no initial panelView#show() or these two were in different order, the toolbar
// positioning will break in RTL editors. Weird, right? What you show know is that the toolbar
// grouping works thanks to:
//
// * the ResizeObserver, which kicks in as soon as the toolbar shows up in DOM (becomes visible again).
// * the observable ToolbarView#maxWidth, which triggers re-grouping when changed.
//
// Here are the possible scenarios:
//
// 1. (WRONG ❌) If the #maxWidth is set when the toolbar is invisible, it won't affect item grouping (no DOMRects, no grouping).
// Then, when panelView.pin() is called, the position of the toolbar will be calculated for the old
// items grouping state, and when finally ResizeObserver kicks in (hey, the toolbar is visible now, right?)
// it will group/ungroup some items and the length of the toolbar will change. But since in RTL the toolbar
// is attached on the right side and the positioning uses CSS "left", it will result in the toolbar shifting
// to the left and being displayed in the wrong place.
// 2. (WRONG ❌) If the panelView.pin() is called first and #maxWidth set next, then basically the story repeats. The balloon
// calculates the position for the old toolbar grouping state, then the toolbar re-groups items and because
// it is positioned using CSS "left" it will move.
// 3. (RIGHT ✅) We show the panel first (the toolbar does re-grouping but it does not matter), then the #maxWidth
// is set allowing the toolbar to re-group again and finally panelView.pin() does the positioning when the
// items grouping state is stable and final.
//
// https://github.com/ckeditor/ckeditor5/issues/6449, https://github.com/ckeditor/ckeditor5/issues/6575
this.panelView.show();
this.toolbarView.maxWidth = this._getToolbarMaxWidth();
this.panelView.pin( {
target: this.buttonView.element,
limiter: this.editor.ui.getEditableElement()
} );
if ( !wasVisible ) {
this.toolbarView.items.get( 0 ).focus();
}
}
/**
* Hides the {@link #toolbarView}.
*
* @private
* @param {Boolean} [focusEditable=false] When `true`, the editable will be focused after hiding the panel.
*/
_hidePanel( focusEditable ) {
this.panelView.isVisible = false;
if ( focusEditable ) {
this.editor.editing.view.focus();
}
}
/**
* Attaches the {@link #buttonView} to the target block of content.
*
* @protected
* @param {HTMLElement} targetElement Target element.
*/
_attachButtonToElement( targetElement ) {
const contentStyles = window.getComputedStyle( targetElement );
const editableRect = new Rect( this.editor.ui.getEditableElement() );
const contentPaddingTop = parseInt( contentStyles.paddingTop, 10 );
// When line height is not an integer then thread it as "normal".
// MDN says that 'normal' == ~1.2 on desktop browsers.
const contentLineHeight = parseInt( contentStyles.lineHeight, 10 ) || parseInt( contentStyles.fontSize, 10 ) * 1.2;
const position = getOptimalPosition( {
element: this.buttonView.element,
target: targetElement,
positions: [
( contentRect, buttonRect ) => {
let left;
if ( this.editor.locale.uiLanguageDirection === 'ltr' ) {
left = editableRect.left - buttonRect.width;
} else {
left = editableRect.right;
}
return {
top: contentRect.top + contentPaddingTop + ( contentLineHeight - buttonRect.height ) / 2,
left
};
}
]
} );
this.buttonView.top = position.top;
this.buttonView.left = position.left;
}
/**
* Gets the {@link #toolbarView} max-width, based on
* editable width plus distance between farthest edge of the {@link #buttonView} and the editable.
*
* @private
* @returns {String} maxWidth A maximum width that toolbar can have, in pixels.
*/
_getToolbarMaxWidth() {
const editableElement = this.editor.ui.view.editable.element;
const editableRect = new Rect( editableElement );
const buttonRect = new Rect( this.buttonView.element );
const isRTL = this.editor.locale.uiLanguageDirection === 'rtl';
const offset = isRTL ? ( buttonRect.left - editableRect.right ) + buttonRect.width : editableRect.left - buttonRect.left;
return toPx( editableRect.width + offset );
}
}
/**
* The block toolbar configuration. Used by the {@link module:ui/toolbar/block/blocktoolbar~BlockToolbar}
* feature.
*
* const config = {
* blockToolbar: [ 'paragraph', 'heading1', 'heading2', 'bulletedList', 'numberedList' ]
* };
*
* You can also use `'|'` to create a separator between groups of items:
*
* const config = {
* blockToolbar: [ 'paragraph', 'heading1', 'heading2', '|', 'bulletedList', 'numberedList' ]
* };
*
* ## Configuring items grouping
*
* You can prevent automatic items grouping by setting the `shouldNotGroupWhenFull` option:
*
* const config = {
* blockToolbar: {
* items: [ 'paragraph', 'heading1', 'heading2', '|', 'bulletedList', 'numberedList' ],
* shouldNotGroupWhenFull: true
* },
* };
*
* Read more about configuring the main editor toolbar in {@link module:core/editor/editorconfig~EditorConfig#toolbar}.
*
* @member {Array.<String>|Object} module:core/editor/editorconfig~EditorConfig#blockToolbar
*/