# UI library

The standard UI library of CKEditor 5 is [`@ckeditor/ckeditor5-ui`](https://www.npmjs.com/package/@ckeditor/ckeditor5-ui). It provides base classes and helpers that allow for building a modular UI that seamlessly integrates with other components of the ecosystem.

<a id="views">

## Views

Views use [templates](#templates) to build the UI. They also provide observable interfaces that other features (like [plugins](core-editor-architecture.md#plugins) or [commands](core-editor-architecture.md#commands)) can use to change the DOM without any actual interaction with the native API.

> **Note**
>
> You can localize all views using the `locale` instance with which they were created. Check the [localization guide](../deep-dive/ui/localization.md) to see how to use the `t()` function available in the `locale` instance.

<a id="definition">

### Definition

You can define a simple input view class as follows:

```js
class SimpleInputView extends View {
	constructor( locale ) {
		super( locale );

		// An entry point to binding observables with DOM attributes,
		// events, and text nodes.
		const bind = this.bindTemplate;

		// Views define their interface (state) using observable properties.
		this.set( {
			isEnabled: false,
			placeholder: ''
		} );

		this.setTemplate( {
			tag: 'input',
			attributes: {
				class: [
					'foo',
					// The value of "view#isEnabled" will control the presence
					// of the class.
					bind.if( 'isEnabled', 'ck-enabled' ),
				],

				// The HTML "placeholder" attribute is also controlled by the observable.
				placeholder: bind.to( 'placeholder' ),
				type: 'text'
			},
			on: {
				// DOM "keydown" events will fire the "view#input" event.
				keydown: bind.to( 'input' )
			}
		} );
	}

	setValue( newValue ) {
		this.element.value = newValue;
	}
}
```

Views encapsulate the DOM they render. Because the UI is organized according to the _view-per-tree_ rule, it is clear which view is responsible for which part of the UI. It is unlikely that a collision occurs between two features writing to the same DOM node.

More often than not, views become children of other views (collections), nodes in the [UI view tree](#view-collections-and-the-ui-tree):

```js
class ParentView extends View {
	constructor( locale ) {
		super( locale );

		const childA = new SimpleInputView( locale );
		const childB = new SimpleInputView( locale );

		this.setTemplate( {
			tag: 'div',
			children: [
				childA,
				childB
			]
		} );
	}
}

const parent = new ParentView( locale );

parent.render();

// This will insert <div><input .. /><input .. /></div>.
document.body.appendChild( parent.element );
```

It is also possible to create standalone views that do not belong to any collection. They must be [rendered](../../api/module_ui_view-View.md#function-render) before injection into the DOM:

```js
const view = new SimpleInputView( locale );

view.render();

// This will insert <input class="foo" type="text" placeholder="" />
document.body.appendChild( view.element );
```

<a id="interaction">

### Interaction

Features can interact with the state of the DOM via the observable properties of the view, so the following:

```js
view.isEnabled = true;
view.placeholder = 'Type some text';
```

will result in:

```html
<input class="foo ck-enabled" type="text" placeholder="Type some text" />
```

Alternatively, they can [bind](core-editor-architecture.md#event-system-and-observables) them directly to their own observable properties:

```js
view.bind( 'placeholder', 'isEnabled' ).to( observable, 'placeholderText', 'isEnabled' );

// The following will be automatically reflected in the "view#placeholder" and
// "view.element#placeholder" HTML attribute in the DOM.
observable.placeholderText = 'Some placeholder';
```

Also, since views propagate DOM events, features can now react to the user actions:

```js
// Each "keydown" event in the input will execute a command.
view.on( 'input', () => {
	editor.execute( 'myCommand' );
} );
```

<a id="best-practices">

### Best practices

A complete view should provide an interface for the features, encapsulating DOM nodes and attributes. Features should not touch the DOM of the view using the native API. Any kind of interaction must be handled by the view that owns a [`element`](../../api/module_ui_view-View.md#member-element) to avoid collisions:

```js
// This will change the value of the input.
view.setValue( 'A new value of the input.' );

// WRONG! This is **NOT** the right way to interact with the DOM because it
// collides with an observable binding to the "#placeholderText". The value will
// be permanently overridden when the state of the observable changes.
view.element.placeholder = 'A new placeholder';
```

<a id="templates">

## Templates

[Templates](../../api/module_ui_template-Template.md) render DOM elements and text nodes in the UI library. Used primarily by [views](#views), they are the lowest layer of the UI connecting the application to the web page.

> **Note**
>
> Check out the [`TemplateDefinition`](../../api/module_ui_template-TemplateDefinition.md) to learn more about the template syntax and other advanced concepts.

Templates support [observable properties](core-editor-architecture.md#event-system-and-observables) bindings and handle native DOM events. A simple template can look like this:

```js
new Template( {
	tag: 'p',
	attributes: {
		class: [
			'foo',
			bind.to( 'class' )
		],
		style: {
			backgroundColor: 'yellow'
		}
	},
	on: {
		click: bind.to( 'clicked' )
	},
	children: [
		'A paragraph.'
	]
} ).render();
```

It renders to an HTML element:

```html
<p class="foo bar" style="background-color: yellow;">A paragraph.</p>
```

where `observable#class` is `"bar"`. The `observable` in the example above can be a [view](#views) or any object which is [observable](../../api/module_utils_observablemixin-Observable.md). When the value of the `class` attribute changes, the template updates the `class` attribute in the DOM. From now on the element is permanently bound to the state of the application.

Similarly, when rendered, the template also takes care of DOM events. A binding to the `click` event in the definition makes the `observable` always fire the `clicked` event upon an action in the DOM. This way the `observable` provides an event interface of the DOM element and all the communication should pass through it.

<a id="view-collections-and-the-ui-tree">

## View collections and the UI tree

Views are organized into [collections](../../api/module_ui_viewcollection-ViewCollection.md), which manage their elements and propagate DOM events even further. Adding or removing a view in a collection moves the [view’s element](../../api/module_ui_view-View.md#member-element) in the DOM to reflect the position.

Each editor UI has a “root view” (like [`ClassicEditor#view`](../../api/module_editor-classic_classiceditorui-ClassicEditorUI.md#member-view)), which can be found under `editor.ui.view`. Such a view usually defines the container element of the editor and the undermost view collections that other features can populate.

For instance, the `BoxedEditorUiView` class defines two collections:

* [`top`](../../api/module_ui_editorui_boxed_boxededitoruiview-BoxedEditorUIView.md#member-top) – A collection that hosts the toolbar.
* [`main`](../../api/module_ui_editorui_boxed_boxededitoruiview-BoxedEditorUIView.md#member-main) – A collection that contains the editable area of the editor.

It also inherits the [`body`](../../api/module_ui_editorui_editoruiview-EditorUIView.md#member-body) collection which resides directly in the `<body>` of the web page. It stores floating elements like [balloon panels](../../api/module_ui_panel_balloon_balloonpanelview-BalloonPanelView.md).

Plugins can populate the root view collections with their children. Such child views become a part of the UI tree and will be managed by the editor. This means that, for example, they will be initialized and destroyed along with the editor.

```js
class MyPlugin extends Plugin {
	init() {
		const editor = this.editor;
		const view = new MyPluginView();

		editor.ui.top.add( view );
	}
}
```

`MyPluginView` can [create its view collections](../../api/module_ui_view-View.md#function-createCollection) and populate them during the life cycle of the editor. There is no limit to the depth of the UI tree, which usually looks like this:

```
EditorUIView
    ├── "top" collection
    │    └── ToolbarView
    │        └── "items" collection
    │            ├── DropdownView
    │            │    ├── ButtonView
    │            │    └── PanelView
    │            ├── ButtonViewA
    │            ├── ButtonViewB
    │            └── ...
    ├── "main" collection
    │    └── InlineEditableUIView
    └── "body" collection
        ├── BalloonPanelView
        │    └── "content" collection
        │        └── ToolbarView
        ├── BalloonPanelView
        │    └── "content" collection
        │        └── ...
        └── ...
```

<a id="using-the-existing-components">

## Using the existing components

The framework provides some common [components](../../api/ui.md) like [`ButtonView`](../../api/module_ui_button_buttonview-ButtonView.md) or [`ToolbarView`](../../api/module_ui_toolbar_toolbarview-ToolbarView.md). They can be helpful when developing a new user interface.

For example, to create a toolbar with some buttons inside, you need to import the `ToolbarView` and `ButtonView` classes first:

**NPM**

```js
import { ButtonView, ToolbarView } from 'ckeditor5';
```

**CDN**

```js
const { ButtonView, ToolbarView } = CKEDITOR;
```

Create the toolbar and a couple of buttons with labels first. Then append the buttons to the toolbar:

```js
const toolbar = new ToolbarView();
const buttonFoo = new ButtonView();
const buttonBar = new ButtonView();

buttonFoo.set( {
	label: 'Foo',
	withText: true
} );

buttonBar.set( {
	label: 'Bar',
	withText: true
} );

toolbar.items.add( buttonFoo );
toolbar.items.add( buttonBar );
```

The toolbar can now join the [UI tree](#view-collections-and-the-ui-tree) or it can be injected straight into the DOM. To keep the example simple, proceed with the latter scenario:

```js
toolbar.render();

document.body.appendChild( toolbar.element );
```

The result should look like this:

The toolbar renders but it does not do much. To execute an action when the button is clicked, you must define a listener. To shorten the code and instead of two listeners define just one, the buttons can [delegate](../../api/module_utils_emittermixin-Emitter.md#function-delegate) the [`execute`](../../api/module_ui_button_button-Button.md#event-execute) event to their parent:

```js
buttonFoo.delegate( 'execute' ).to( toolbar );
buttonBar.delegate( 'execute' ).to( toolbar );

toolbar.on( 'execute', evt => {
	console.log( `The "${ evt.source.label }" button was clicked!` );
} );
```

<a id="dropdowns">

### Dropdowns

The framework implements the [dropdown](../../api/module_ui_dropdown_dropdownview-DropdownView.md) component which can host any sort of UI in its panel. It is composed of a [button](../../api/module_ui_dropdown_dropdownview-DropdownView.md#member-buttonView) (to open the dropdown) and a [panel](../../api/module_ui_dropdown_dropdownview-DropdownView.md#member-panelView) (the container).

The button can be either:

* A standard [`ButtonView`](../../api/module_ui_button_buttonview-ButtonView.md).
* A [`SplitButtonView`](../../api/module_ui_dropdown_button_splitbuttonview-SplitButtonView.md), for more complex use cases.

The dropdown panel exposes its [children](../../api/module_ui_dropdown_dropdownpanelview-DropdownPanelView.md#member-children) collection which aggregates the child [views](../../api/module_ui_view-View.md). The most common views displayed in the dropdown panel are:

* [`ListView`](../../api/module_ui_list_listview-ListView.md) - dropdown list
* [`ToolbarView`](../../api/module_ui_toolbar_toolbarview-ToolbarView.md) - dropdown toolbar
* [`DropdownMenuRootListView`](../../api/module_ui_dropdown_menu_dropdownmenurootlistview-DropdownMenuRootListView.md) - dropdown menu

The framework provides a set of helpers to make the dropdown creation process easier. It is still possible to compose a custom dropdown from scratch using the base classes. However, for most needs, we highly recommend using provided helper functions.

The [`createDropdown`](../../api/module_ui_dropdown_utils.md#function-createDropdown) helper creates a [`DropdownView`](../../api/module_ui_dropdown_dropdownview-DropdownView.md) with either a [`ButtonView`](../../api/module_ui_button_buttonview-ButtonView.md) or a [`SplitButtonView`](../../api/module_ui_dropdown_button_splitbuttonview-SplitButtonView.md).

**NPM**

```js
import { createDropdown, SplitButtonView } from 'ckeditor5';

const dropdownView = createDropdown( locale, SplitButtonView );
```

**CDN**

```js
const { createDropdown, SplitButtonView } = CKEDITOR;

const dropdownView = createDropdown( locale, SplitButtonView );
```

This kind of (default) dropdown comes with a set of behaviors:

* It closes the panel when it loses the focus, for example, when the user moved the focus elsewhere.
* It closes the panel upon the [`execute`](../../api/module_ui_dropdown_dropdownview-DropdownView.md#event-execute) event.
* It focuses the view hosted in the panel, for example, when navigating the toolbar using the keyboard.

<a id="setting-label-icon-and-tooltip">

#### Setting label, icon, and tooltip

To customize the button of the dropdown, use the [`buttonView`](../../api/module_ui_dropdown_dropdownview-DropdownView.md#member-buttonView) property. It gives direct access to the [`ButtonView` instance](../../api/module_ui_button_buttonview-ButtonView.md) used by your dropdown.

> **Note**
>
> If your dropdown was created using the [`SplitButtonView`](../../api/module_ui_dropdown_button_splitbuttonview-SplitButtonView.md), use the [`actionView`](../../api/module_ui_dropdown_button_splitbuttonview-SplitButtonView.md#member-actionView) to access its main region. For example: `dropdownView.buttonView.actionView.set( /* ... */ )`.

To control the label of the dropdown, first make it visible using the [`withText`](../../api/module_ui_button_buttonview-ButtonView.md#member-withText) property. Then set the text of the [`label`](../../api/module_ui_button_buttonview-ButtonView.md#member-label):

```js
const dropdownView = createDropdown( locale );

dropdownView.buttonView.set( {
	withText: true,
	label: 'Label of the button',
} );
```

The dropdown button can display an icon too. First, import the SVG file. Then pass it to the [`icon`](../../api/module_ui_button_buttonview-ButtonView.md#member-icon) property of the button:

```js
import iconFile from 'path/to/icon.svg';

// The code that creates a dropdown view.
// ...

dropdownView.buttonView.set( {
	icon: iconFile
} );
```

You can use one of the [icons available in the editor](ui-components.md#icons). You can also add a custom icon to the dropdown by providing the entire XML string of the icon, like in this example:

```html
<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M10.187 17H5.773c-.637 0-1.092-.138-1.364-.415-.273-.277-.409-.718-.409-1.323V4.738c0-.617.14-1.062.419-1.332.279-.27.73-.406 1.354-.406h4.68c.69 0 1.288.041 1.793.124.506.083.96.242 1.36.478.341.197.644.447.906.75a3.262 3.262 0 0 1 .808 2.162c0 1.401-.722 2.426-2.167 3.075C15.05 10.175 16 11.315 16 13.01a3.756 3.756 0 0 1-2.296 3.504 6.1 6.1 0 0 1-1.517.377c-.571.073-1.238.11-2 .11zm-.217-6.217H7v4.087h3.069c1.977 0 2.965-.69 2.965-2.072 0-.707-.256-1.22-.768-1.537-.512-.319-1.277-.478-2.296-.478zM7 5.13v3.619h2.606c.729 0 1.292-.067 1.69-.2a1.6 1.6 0 0 0 .91-.765c.165-.267.247-.566.247-.897 0-.707-.26-1.176-.778-1.409-.519-.232-1.31-.348-2.375-.348H7z"/></svg>
```

If you want to have an icon that changes its color depending on the state of the button, you should remove all of the fill and stroke attributes.

The `withText` and `icon` properties are independent so your dropdown can have:

* Just a text label.
* Just an icon.
* Both a label and an icon at the same time.

> **Note**
>
> Even if your dropdown has no visible label (`withText` is `false`), we recommend setting the `label` property anyway. Assistive technologies like screen readers need it to work correctly with the editor.

Dropdowns can also display tooltips when hovered. Use the [`tooltip`](../../api/module_ui_button_buttonview-ButtonView.md#member-tooltip) property of the button to enable this feature. You can include keystroke information in the tooltip or create custom tooltips. Check out the documentation of the property to learn more.

```js
dropdownView.buttonView.set( {
	// The tooltip text will repeat the label.
	tooltip: true
} );
```

<a id="adding-a-list-to-a-dropdown">

#### Adding a list to a dropdown

The [`ListView`](../../api/module_ui_list_listview-ListView.md) can be added to a dropdown using the [`addListToDropdown`](../../api/module_ui_dropdown_utils.md#function-addListToDropdown) helper.

**NPM**

```js
import { UIModel, addListToDropdown, createDropdown, Collection } from 'ckeditor5';

// The default dropdown.
const dropdownView = createDropdown( locale );

// The collection of the list items.
const items = new Collection();

items.add( {
	type: 'button',
	model: new UIModel( {
		withText: true,
		label: 'Foo'
	} )
} );

items.add( {
	type: 'button',
	model: new UIModel( {
		withText: true,
		label: 'Bar'
	} )
} );

// Create a dropdown with a list inside the panel.
addListToDropdown( dropdownView, items );
```

**CDN**

```js
const { UIModel, addListToDropdown, createDropdown, Collection } = CKEDITOR;

// The default dropdown.
const dropdownView = createDropdown( locale );

// The collection of the list items.
const items = new Collection();

items.add( {
	type: 'button',
	model: new UIModel( {
		withText: true,
		label: 'Foo'
	} )
} );

items.add( {
	type: 'button',
	model: new UIModel( {
		withText: true,
		label: 'Bar'
	} )
} );

// Create a dropdown with a list inside the panel.
addListToDropdown( dropdownView, items );
```

<a id="adding-a-toolbar-to-a-dropdown">

#### Adding a toolbar to a dropdown

A [`ToolbarView`](../../api/module_ui_toolbar_toolbarview-ToolbarView.md) can be added to a dropdown using the [`addToolbarToDropdown`](../../api/module_ui_dropdown_utils.md#function-addToolbarToDropdown) helper.

**NPM**

```js
import { ButtonView, SplitButtonView, addToolbarToDropdown, createDropdown } from 'ckeditor5';

const buttons = [];

// Add a simple button to the array of toolbar items.
buttons.push( new ButtonView() );

// Add another component to the array of toolbar items.
buttons.push( componentFactory.create( 'componentName' ) );

const dropdownView = createDropdown( locale, SplitButtonView );

// Create a dropdown with a toolbar inside the panel.
addToolbarToDropdown( dropdownView, buttons );
```

**CDN**

```js
const { ButtonView, SplitButtonView, addToolbarToDropdown, createDropdown } = CKEDITOR;

const buttons = [];

// Add a simple button to the array of toolbar items.
buttons.push( new ButtonView() );

// Add another component to the array of toolbar items.
buttons.push( componentFactory.create( 'componentName' ) );

const dropdownView = createDropdown( locale, SplitButtonView );

// Create a dropdown with a toolbar inside the panel.
addToolbarToDropdown( dropdownView, buttons );
```

A common practice is making the main dropdown button [enabled](../../api/module_ui_dropdown_dropdownview-DropdownView.md#member-isEnabled) when one of the toolbar items is enabled:

```js
// Enable the dropdown's button when any of the toolbar items is enabled.
dropdownView.bind( 'isEnabled' ).toMany( buttons, 'isEnabled',
	( ...areEnabled ) => areEnabled.some( isEnabled => isEnabled )
);
```

<a id="adding-a-menu-to-a-dropdown">

#### Adding a menu to a dropdown

A multi-level menu can be added to a dropdown using the [`addMenuToDropdown`](../../api/module_ui_dropdown_utils.md#function-addMenuToDropdown) helper.

**NPM**

```js
import { addMenuToDropdown, createDropdown } from 'ckeditor5';

// The default dropdown.
const dropdownView = createDropdown( editor.locale );

// The menu items definitions.
const definition = [
	{
		id: 'menu_1',
		menu: 'Menu 1',
		children: [
			{
				id: 'menu_1_a',
				label: 'Item A'
			},
			{
				id: 'menu_1_b',
				label: 'Item B'
			}
		]
	},
	{
		id: 'top_a',
		label: 'Top Item A'
	},
	{
		id: 'top_b',
		label: 'Top Item B'
	}
];

addMenuToDropdown( dropdownView, editor.body.ui.view, definition );
```

**CDN**

```js
const { addMenuToDropdown, createDropdown } = CKEDITOR;

// The default dropdown.
const dropdownView = createDropdown( editor.locale );

// The menu items definitions.
const definition = [
	{
		id: 'menu_1',
		menu: 'Menu 1',
		children: [
			{
				id: 'menu_1_a',
				label: 'Item A'
			},
			{
				id: 'menu_1_b',
				label: 'Item B'
			}
		]
	},
	{
		id: 'top_a',
		label: 'Top Item A'
	},
	{
		id: 'top_b',
		label: 'Top Item B'
	}
];

addMenuToDropdown( dropdownView, editor.body.ui.view, definition );
```

Most probably you will want to perform some action when one of the defined buttons is pressed:

```js
dropdownView.on( 'execute', evt => {
	const id = evt.source.id;

	console.log( id ); // E.g. will print "menu_1_a" when "Item A" is pressed.
} );
```

<a id="dialogs-and-modals">

### Dialogs and modals

The framework provides the UI dialog component. The dialog system in CKEditor 5 is brought by the [`Dialog` plugin](../../api/module_ui_dialog_dialog-Dialog.md). It offers API for displaying [views](#views) in dialogs. In a sense, this plugin corresponds to another one that manages views in balloons (popovers) across the UI ([`ContextualBalloon` plugin](../../api/module_ui_panel_balloon_contextualballoon-ContextualBalloon.md)).

Dialog is a pop-up window that does not close when the user clicks outside of it. It allows for interacting with the editor and its content while being open (unless it is a modal, which blocks the interaction with the rest of the page until closed). A dialog is also [draggable](../../api/module_ui_bindings_draggableviewmixin.md#function-DraggableViewMixin) with a mouse or touch if you configure it to display a [header](#header). Only one dialog can be open at a time – opening another one closes the previously visible one.

Check out these [example plugins](plugins.md) that display:

* a [dialog window](ui-components.md#dialog),
* a [modal window](ui-components.md#modal).

Learn more about the [structure and behavior](#structure-and-behavior) of dialog windows.

<a id="modals">

#### Modals

Modals are similar to the dialogs – they share the same structure rules, API, etc. The major difference is that while a modal is open, the user cannot interact with the editor or the rest of the page. They are covered with a non-transparent overlay. You can use a modal, for example, to force the user to take one of the specified actions.

To create a modal, use the optional [`isModal`](../../api/module_ui_dialog_dialog-DialogDefinition.md#member-isModal) property of the [`Dialog#show()`](../../api/module_ui_dialog_dialog-Dialog.md#function-show) method:

```js
editor.plugins.get( 'Dialog' ).show( {
	isModal: true,

	// The rest of the dialog definition.
} );
```

There are different ways to close a modal:

* Clicking the “Close” button in the corner (if the [header](#header) is visible).
* Using the `Esc` keystroke or one of the [action buttons](#action-buttons).

> **Warning**
>
> Below you will learn about the ways to disable some of these methods. Remember to always leave at least one option to close the modal. Otherwise, you will lock the users inside a modal.

<a id="structure-and-behavior">

#### Structure and behavior

A dialog can consist of three parts, each of which is optional:

* The [header](#header) (also used as a drag handler).
* The [content](#content) (the body of the dialog).
* The [action buttons](#action-buttons) area (a collection of buttons).

You cannot change the order of these parts.

<a id="header">

##### Header

A header may consist of any combination of three elements:

* The icon.
* The title.
* The “Close” button.

By default, the “Close” button (“X”) is added to the header as long as you provide an icon or a title. To hide it, set the `hasCloseButton` flag to `false`:

**NPM**

```js
import { IconPencil } from 'ckeditor5';

// ...

editor.plugins.get( 'Dialog' ).show( {
	icon: IconPencil,
	title: 'My first dialog',
	// Do not display the "Close" button.
	hasCloseButton: false,

	// The rest of the dialog definition.
} );
```

**CDN**

```js
const { IconPencil } = CKEDITOR;

// ...

editor.plugins.get( 'Dialog' ).show( {
	icon: IconPencil,
	title: 'My first dialog',
	// Do not display the "Close" button.
	hasCloseButton: false,

	// The rest of the dialog definition.
} );
```

> **Note**
>
> If you decide to hide the “Close” button, remember to leave some other way to close the dialog. The `Esc` keystroke also closes the dialog but it may not be available, for example, for touch screen users.

<a id="content">

##### Content

This part can be a single [view](#views) or a collection of views. They will be displayed directly inside the body of the dialog. Below, you can find an example of how to insert a block of text into the dialog.

```js
const textView = new View( locale );

textView.setTemplate( {
	tag: 'div',
	attributes: {
		style: {
			padding: 'var(--ck-spacing-large)',
			whiteSpace: 'initial',
			width: '100%',
			maxWidth: '500px'
		},
		tabindex: -1
	},
	children: [
		'This is a sample content of the dialog.',
		'You can put here text, images, inputs, buttons, etc.'
	]
} );

editor.plugins.get( 'Dialog' ).show( {
	title: 'Info dialog with text',
	content: textView,

	// The rest of the dialog definition.
} );
```

The content of the dialog determines its dimensions. If you want to display a dialog with a fixed size, make sure the content has a fixed size too. For displaying long text, we recommend setting `max-height: [fixed value]` together with `overflow: auto` on the content container (element). This will restrict the vertical space the dialog will use on the screen.

<a id="action-buttons">

##### Action buttons

The last segment of the dialog is the actions area, where the buttons are displayed. You can fully customize their behavior using the `onCreate()` and `onExecute()` callbacks. Below you can find an example of the configuration for the four custom buttons:

* The “OK” button that closes the dialog and has a custom CSS class set.
* The “Set custom title” button that changes the dialog title.
* The “This button will be enabled in…” button that changes its state after a few seconds.
* The “Cancel” button that closes the dialog.

```js
editor.plugins.get( 'Dialog' ).show( {
	// ...

	actionButtons: [
		{
			label: 'OK',
			class: 'ck-button-action',
			withText: true,
			onExecute: () => dialog.hide()
		},
		{
			label: 'Set custom title',
			withText: true,
			onExecute: () => {
				dialog.view.headerView.label = 'New title';
			}
		},
		{
			label: 'This button will be enabled in 5...',
			withText: true,
			onCreate: buttonView => {
				buttonView.isEnabled = false;
				let counter = 5;

				const interval = setInterval( () => {
					buttonView.label = `This button will be enabled in ${ --counter }...`;

					if ( counter === 0 ) {
						clearInterval( interval );
						buttonView.label = 'This button is now enabled!';
						buttonView.isEnabled = true;
					}
				}, 1000 );
			}
		},
		{
			label: 'Cancel',
			withText: true,
			onExecute: () => dialog.hide()
		}
	]
} );
```

You can also bind the button state to the content to ensure some required actions were taken by the user. See the example of the “Yes/No modal” definition. It requires the user to mark the checkbox to close the modal:

```js
// First, create the content to be injected into a the modal.
// In this example a simple switch button is used.
const switchButtonView = new SwitchButtonView( locale );

switchButtonView.set( {
	label: t( 'I accept the terms and conditions' ),
	withText: true
} );

// Manage the state of the switch button when the user clicks it.
switchButtonView.on( 'execute', () => {
	switchButtonView.isOn = !switchButtonView.isOn;
} );

// Then show the modal.
editor.plugins.get( 'Dialog' ).show( {
	id: 'yesNoModal',
	isModal: true,
	title: 'Accept the terms to enable the "Yes" button',
	hasCloseButton: false,
	content: switchButtonView,
	actionButtons: [
		{
			label: t( 'Yes' ),
			class: 'ck-button-action',
			withText: true,
			onExecute: () => dialog.hide(),
			onCreate: buttonView => {
				// By default, the "Yes" button in the dialog is disabled.
				buttonView.isEnabled = false;

				// The "Yes" button will be enabled as soon as the user
				// toggles the switch button.
				switchButtonView.on( 'change:isOn', () => {
					buttonView.isEnabled = switchButtonView.isOn;
				} );
			}
		},
		{
			label: t( 'No' ),
			withText: true,
			onExecute: () => dialog.hide()
		}
	],
	// Disable the "Esc" key.
	onShow: dialog => {
		dialog.view.on( 'close', ( evt, data ) => {
			if ( data.source === 'escKeyPress' ) {
				evt.stop();
			}
		}, { priority: 'high' } );
	}
} );
```

<a id="accessibility">

#### Accessibility

Dialogs provide full keyboard accessibility.

* While a dialog is open, strike the `Ctrl`+`F6` combination to move the focus between the editor and the dialog.
* You can also close a dialog at any time by pressing the `Esc` key (even if the “Close” button is hidden).
* To navigate through the dialog, use the `Tab` and `Shift`+`Tab` keystrokes.

The content of the dialog is also available for screen readers.

<a id="api">

#### API

The dialog’s lifecycle (creating and destroying) is managed by the [`Dialog` plugin](../../api/module_ui_dialog_dialog.md). It provides two public methods: [`show()`](../../api/module_ui_dialog_dialog-Dialog.md#function-show) and [`hide()`](../../api/module_ui_dialog_dialog-Dialog.md#function-hide). Both of them fire the respective events ([`show`](../../api/module_ui_dialog_dialog-DialogShowEvent.md) and [`hide`](../../api/module_ui_dialog_dialog-DialogHideEvent.md)), so it is possible to hook after or before them.

<a id="the-dialogshow-method">

##### The `Dialog#show()` method

The [`Dialog#show()`](../../api/module_ui_dialog_dialog-Dialog.md#function-show) method hides any visible dialog and displays a new one. It accepts a dialog definition that allows to shape the structure and behavior of the dialog. See the [`DialogDefinition`](../../api/module_ui_dialog_dialog-DialogDefinition.md) API to learn more about the possibilities it gives.

<a id="the-dialogshowid-event">

##### The `Dialog#show:[id]` event

When the [`Dialog#show()`](#the-dialogshow-method) function gets called, a namespaced [`show:[id]`](../../api/module_ui_dialog_dialog-DialogShowEvent.md) event is fired. This allows for customizing the dialog’s behavior.

For example, you can change the default position of the “Find and replace” dialog from the editor corner to the bottom with the following code:

**NPM**

```js
import { DialogViewPosition } from 'ckeditor5';

// ...

editor.plugins.get( 'Dialog' ).on( 'show:findAndReplace', ( evt, data ) => {
	Object.assign( data, { position: DialogViewPosition.EDITOR_BOTTOM_CENTER } );
}, { priority: 'high' } );
```

**CDN**

```js
const { DialogViewPosition } = CKEDITOR;

// ...

editor.plugins.get( 'Dialog' ).on( 'show:findAndReplace', ( evt, data ) => {
	Object.assign( data, { position: DialogViewPosition.EDITOR_BOTTOM_CENTER } );
}, { priority: 'high' } );
```

You can also listen to the general `'show'` event to customize all dialogs at once.

<a id="the-dialoghide-method">

##### The `Dialog#hide()` method

Executing the [`Dialog#hide()`](../../api/module_ui_dialog_dialog-Dialog.md#function-hide) method will hide the dialog. It will also call the [`onHide()`](#using-the-onshow-and-onhide-callbacks) callback if it was provided in the respective [`Dialog#show()`](../../api/module_ui_dialog_dialog-Dialog.md#function-show) method call.

<a id="the-dialoghideid-event">

##### The `Dialog#hide:[id]` event

Similarly to the [`show:[id]`](#the-dialogshowid-event) event, the [`hide:[id]`](../../api/module_ui_dialog_dialog-DialogHideEvent.md) event gets fired when the [`Dialog#hide()`](#the-dialoghide-method) is called. It allows for executing custom actions:

```js
// Logs after the "Find and Replace" dialog gets hidden.
editor.plugins.get( 'Dialog' ).on( 'hide:findAndReplace', () => {
	console.log( 'The "Find and Replace" dialog was hidden.' );
} );
```

You can also listen to the general `'hide'` event to react to all dialogs at once.

<a id="the-dialogviewclose-event">

##### The `DialogView#close` event

When the `Esc` key or the “Close” button is pressed with the dialog open, the [`DialogView`](../../api/module_ui_dialog_dialogview-DialogView.md) fires the [`close`](../../api/module_ui_dialog_dialogview-DialogViewCloseEvent.md) event with the according `source` parameter. Then the [`Dialog` plugin](../../api/module_ui_dialog_dialog-Dialog.md) hides (and destroys) the dialog.

You can control this behavior, for example, disabling the `Esc` key handling:

```js
editor.plugins.get( 'Dialog' ).view.on( 'close', ( evt, data ) => {
	if ( data.source === 'escKeyPress' ) {
		evt.stop();
	}
} )
```

You can also pass such code directly in the `show()` method call in the `onShow` callback:

```js
editor.plugins.get( 'Dialog' ).show( {
	onShow: dialog => {
		dialog.view.on( 'close', ( evt, data ) => {
			if ( data.source === 'escKeyPress' ) {
				evt.stop();
			}
		}, { priority: 'high' } );
	}
	// The rest of the dialog definition.
} );
```

> **Warning**
>
> Blocking the usage of the `Esc` key limits the accessibility. It is considered a bad practice. If you need to do this, remember to always leave at least one way to close the dialog or modal.

<a id="using-the-onshow-and-onhide-callbacks">

##### Using the `onShow` and `onHide` callbacks

The [`DialogDefinition`](../../api/module_ui_dialog_dialog-DialogDefinition.md) accepts two callbacks. They allow customizing the actions after the dialog is shown ([`onShow()`](../../api/module_ui_dialog_dialog-DialogDefinition.md#member-onShow)) and hidden ([`onHide()`](../../api/module_ui_dialog_dialog-DialogDefinition.md#member-onHide)).

The `onShow` callback allows you to manipulate the dialog values or set additional listeners. In the [`DialogView#event:close` event](#the-dialogviewclose-event) section you can find an example of how to disable the `Esc` key with it. The code below shows how to bootstrap the dynamic field filling.

**NPM**

```js
// Import necessary classes.
import { View, InputTextView } from 'ckeditor5';

// Create an input.
const input = new InputTextView();
const dialogBody = new View();

dialogBody.setTemplate( {
	tag: 'div',
	children: [
		// Other elements of the view.
		input
	]
	// The rest of the template.
} );

editor.plugins.get( 'Dialog' ).show( {
	onShow: () => {
		// Set the dynamic initial input value.
		input.value = getDataFromExternalSource();
	},
	// The rest of the dialog definition.
} );
```

**CDN**

```js
// Import necessary classes.
const { View, InputTextView } = CKEDITOR;

// Create an input.
const input = new InputTextView();
const dialogBody = new View();

dialogBody.setTemplate( {
	tag: 'div',
	children: [
		// Other elements of the view.
		input
	]
	// The rest of the template.
} );

editor.plugins.get( 'Dialog' ).show( {
	onShow: () => {
		// Set the dynamic initial input value.
		input.value = getDataFromExternalSource();
	},
	// The rest of the dialog definition.
} );
```

The `onHide` callback will be particularly helpful to reset the state of the component or its controller once the dialog is closed.

```js
// Executing inside a class with the "controller" property that has the state connected to the dialog.
editor.plugins.get( 'Dialog' ).show( {
	onHide: dialog => {
		this.controller.reset(); // Reset the controller state once the dialog is hidden.
	},
	// The rest of the dialog definition.
} );
```

<a id="visibility-and-positioning">

#### Visibility and positioning

Only one dialog can be visible at the same time. Opening another dialog (from the same or another editor instance) will close the previous dialog.

If not specified otherwise, the dialog will display in the center of the editor’s editing area. A modal will display in the center of the screen. Custom dialogs can have their position set to one of the pre-configured options (see [`DialogViewPosition`](../../api/module_ui_dialog_dialogview.md#constant-DialogViewPosition)). The relative positioning is disabled once the dialog is manually dragged by the user.

* When you develop your dialog, specify the [`position`](../../api/module_ui_dialog_dialog-DialogDefinition.md#member-position) property in a definition passed to the [`Dialog#show()`](../../api/module_ui_dialog_dialog-Dialog.md#function-show) method, for instance:

**NPM**

```js
import { DialogViewPosition } from 'ckeditor5';

// ...

const dialog = editor.plugins.get( 'Dialog' );

dialog.show( {
	// ...

	// Change the default position of the dialog.
	position: DialogViewPosition.EDITOR_BOTTOM_CENTER
} );
```

**CDN**

```js
const { DialogViewPosition } = CKEDITOR;

// ...

const dialog = editor.plugins.get( 'Dialog' );

dialog.show( {
	// ...

	// Change the default position of the dialog.
	position: DialogViewPosition.EDITOR_BOTTOM_CENTER
} );
```

* To change the position of an existing dialog or manage positions dynamically, use the [`show`](../../api/module_ui_dialog_dialog-DialogShowEvent.md) event listener (see the [example code](#the-dialogshowid-event)).

Sometimes, when the content of the dialog or the environment changes (for example, the editor is resized), you may want to force-update the position of the dialog. This will restore its position to the [configured default](../../api/module_ui_dialog_dialog-DialogDefinition.md#member-position). It will also reset any manual positioning (dragging) done by the user. To do so, use the [`updatePosition`](../../api/module_ui_dialog_dialogview-DialogView.md#function-updatePosition) method.

```js
editor.plugins.get( 'Dialog' ).view.updatePosition();
```

<a id="best-practices-2">

### Best practices

For the best user experience, the editing view should get [focused](../../api/module_engine_view_view-EditingView.md#function-focus) upon any user action (like executing a command) to make sure the editor retains focus:

```js
// Execute some action on the "dropdown#execute" event.
dropdownView.buttonView.on( 'execute', () => {
	editor.execute( 'command', { value: "command-value" } );
	editor.editing.view.focus();
} );
```

<a id="keystrokes-and-focus-management">

## Keystrokes and focus management

The framework offers built-in classes that help manage keystrokes and focus in the UI. They are useful when it comes to bringing accessibility features to the application.

> **Note**
>
> If you want to know how the editor handles focus under the hood and what tools make it possible, check out the [Deep dive into focus tracking](../deep-dive/ui/focus-tracking.md) guide.

<a id="focus-tracker">

### Focus tracker

The [`FocusTracker`](../../api/module_utils_focustracker-FocusTracker.md) class can observe some HTML elements and determine if one of them is focused either by the user (clicking, typing) or using the `HTMLElement.focus()` DOM method.

**NPM**

```js
import { FocusTracker } from 'ckeditor5';

// More imports.
// ...

const focusTracker = new FocusTracker();
```

**CDN**

```js
const { FocusTracker } = CKEDITOR;

// More imports.
// ...

const focusTracker = new FocusTracker();
```

To register elements in the tracker, use the [`add()`](../../api/module_utils_focustracker-FocusTracker.md#function-add) method:

```js
focusTracker.add( document.querySelector( '.some-element' ) );
focusTracker.add( viewInstance.element );
```

Observing the focus tracker’s [`isFocused`](../../api/module_utils_focustracker-FocusTracker.md#member-isFocused) observable property allows you to determine whether one of the registered elements is currently focused:

```js
focusTracker.on( 'change:isFocused', ( evt, name, isFocused ) => {
	if ( isFocused ) {
		console.log( 'The', focusTracker.focusedElement, 'is focused now.' );
	} else {
		console.log( 'The elements are blurred.' );
	}
} );
```

This information is useful when implementing a certain type of UI whose behavior depends on the focus. For example, contextual panels and floating balloons containing forms should hide when the user decides to abandon them.

Learn more about the focus tracker class in the [Deep dive into focus tracking](../deep-dive/ui/focus-tracking.md#using-the-focustracker-class) guide.

<a id="keystroke-handler">

### Keystroke handler

The [`KeystrokeHandler`](../../api/module_utils_keystrokehandler-KeystrokeHandler.md) listens to the keystroke events fired by an HTML element or any of its descendants. It executes pre-defined actions when the keystroke is pressed. Usually, each [view](#views) creates its keystroke handler instance. It takes care of the keystrokes fired by the elements the view has rendered.

**NPM**

```js
import { KeystrokeHandler } from 'ckeditor5';

// More imports.
// ...

const keystrokeHandler = new KeystrokeHandler();
```

**CDN**

```js
const { KeystrokeHandler } = CKEDITOR;

// More imports.
// ...

const keystrokeHandler = new KeystrokeHandler();
```

To define the scope of the keystroke handler in the DOM, use the [`listenTo()`](../../api/module_utils_keystrokehandler-KeystrokeHandler.md#function-listenTo) method:

```js
keystrokeHandler.listenTo( document.querySelector( '.some-element' ) );
keystrokeHandler.listenTo( viewInstance.element );
```

> **Note**
>
> Check out the list of [known key names](../../api/module_utils_keyboard.md#constant-keyCodes) supported by the keystroke handler.

Keystroke action callbacks are functions. To prevent the default action of the keystroke and stop further propagation, use the `cancel()` function provided in the callback.

```js
keystrokeHandler.set( 'Tab', ( keyEvtData, cancel ) => {
	console.log( 'Tab was pressed!' );

	// This keystroke has been handled and can be canceled.
	cancel();
} );
```

> **Note**
>
> There is also an [`EditingKeystrokeHandler`](../../api/module_core_editingkeystrokehandler-EditingKeystrokeHandler.md) class which has the same API as `KeystrokeHandler` but it offers direct keystroke bindings to editor commands.
>
> The editor provides such a keystroke handler under the [`editor.keystrokes`](../../api/module_core_editor_editor-Editor.md#member-keystrokes) property so any plugin can register keystrokes associated with editor commands. For example, the [`Undo`](../../api/module_undo_undo-Undo.md) plugin registers `editor.keystrokes.set( 'Ctrl+Z', 'undo' );` to execute its `undo` command.

When you assign multiple callbacks to the same keystroke, you can use priorities to decide which one to handle first and whether to execute other callbacks at all:

```js
keystrokeHandler.set( 'Ctrl+A', ( keyEvtData ) => {
	console.log( 'A normal priority listener.' );
} );

keystrokeHandler.set( 'Ctrl+A', ( keyEvtData ) => {
	console.log( 'A high priority listener.' );

	// The normal priority listener will not be executed.
	cancel();
}, { priority: 'high' } );
```

Pressing `Ctrl`+`A` will log:

```
"A high priority listener."
```

> **Note**
>
> Check out the [event system deep dive guide](../deep-dive/event-system.md#listener-priorities) to learn more about event listener priorities.

---

Full index of the CKEditor 5 documentation: [llms.txt](../../../llms.txt)
