• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ckeditor / ckeditor5 / bedc2790-b778-4c70-b6fe-562bee0edacd

26 Nov 2024 07:50AM UTC coverage: 100.0%. Remained the same
bedc2790-b778-4c70-b6fe-562bee0edacd

push

circleci

web-flow
Merge pull request #17513 from ckeditor/cc/6747

Internal: Using existing license in the ckeditor5 root while preparing zip release.

14364 of 14364 branches covered (100.0%)

Branch coverage included in aggregate %.

38246 of 38246 relevant lines covered (100.0%)

9928.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/packages/ckeditor5-alignment/src/alignmentui.ts
1
/**
2
 * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
 */
5

6
/**
7
 * @module alignment/alignmentui
8
 */
9

10
import { Plugin, icons } from 'ckeditor5/src/core.js';
11
import {
12
        type Button,
13
        ButtonView,
14
        createDropdown,
15
        addToolbarToDropdown,
16
        MenuBarMenuListItemView,
17
        MenuBarMenuListItemButtonView,
18
        MenuBarMenuView,
19
        MenuBarMenuListView
20
} from 'ckeditor5/src/ui.js';
21
import type { Locale } from 'ckeditor5/src/utils.js';
22

23
import { isSupported, normalizeAlignmentOptions } from './utils.js';
24
import type { AlignmentFormat, SupportedOption } from './alignmentconfig.js';
25
import type AlignmentCommand from './alignmentcommand.js';
26

27
const iconsMap = /* #__PURE__ */ ( () => new Map( [
1✔
28
        [ 'left', icons.alignLeft ],
29
        [ 'right', icons.alignRight ],
30
        [ 'center', icons.alignCenter ],
31
        [ 'justify', icons.alignJustify ]
32
] ) )();
33

34
/**
35
 * The default alignment UI plugin.
36
 *
37
 * It introduces the `'alignment:left'`, `'alignment:right'`, `'alignment:center'` and `'alignment:justify'` buttons
38
 * and the `'alignment'` dropdown.
39
 */
40
export default class AlignmentUI extends Plugin {
41
        /**
42
         * Returns the localized option titles provided by the plugin.
43
         *
44
         * The following localized titles corresponding with
45
         * {@link module:alignment/alignmentconfig~AlignmentConfig#options} are available:
46
         *
47
         * * `'left'`,
48
         * * `'right'`,
49
         * * `'center'`,
50
         * * `'justify'`.
51
         *
52
         * @readonly
53
         */
54
        public get localizedOptionTitles(): Record<SupportedOption, string> {
55
                const t = this.editor.t;
227✔
56

57
                return {
227✔
58
                        'left': t( 'Align left' ),
59
                        'right': t( 'Align right' ),
60
                        'center': t( 'Align center' ),
61
                        'justify': t( 'Justify' )
62
                };
63
        }
64

65
        /**
66
         * @inheritDoc
67
         */
68
        public static get pluginName() {
69
                return 'AlignmentUI' as const;
272✔
70
        }
71

72
        /**
73
         * @inheritDoc
74
         */
75
        public static override get isOfficialPlugin(): true {
76
                return true;
1✔
77
        }
78

79
        /**
80
         * @inheritDoc
81
         */
82
        public init(): void {
83
                const editor = this.editor;
68✔
84
                const options = normalizeAlignmentOptions( editor.config.get( 'alignment.options' )! );
68✔
85

86
                options
68✔
87
                        .map( option => option.name )
262✔
88
                        .filter( isSupported )
89
                        .forEach( option => this._addButton( option ) );
262✔
90

91
                this._addToolbarDropdown( options );
68✔
92
                this._addMenuBarMenu( options );
68✔
93
        }
94

95
        /**
96
         * Helper method for initializing the button and linking it with an appropriate command.
97
         *
98
         * @param option The name of the alignment option for which the button is added.
99
         */
100
        private _addButton( option: SupportedOption ): void {
101
                const editor = this.editor;
262✔
102

103
                editor.ui.componentFactory.add( `alignment:${ option }`, locale => this._createButton( locale, option ) );
262✔
104
        }
105

106
        /**
107
         * Helper method for creating the button view element.
108
         *
109
         * @param locale Editor locale.
110
         * @param option The name of the alignment option for which the button is added.
111
         * @param buttonAttrs Optional parameters passed to button view instance.
112
         */
113
        private _createButton(
114
                locale: Locale,
115
                option: SupportedOption,
116
                buttonAttrs: Partial<Button> = {}
20✔
117
        ): ButtonView {
118
                const editor = this.editor;
54✔
119
                const command: AlignmentCommand = editor.commands.get( 'alignment' )!;
54✔
120
                const buttonView = new ButtonView( locale );
54✔
121

122
                buttonView.set( {
54✔
123
                        label: this.localizedOptionTitles[ option ],
124
                        icon: iconsMap.get( option ),
125
                        tooltip: true,
126
                        isToggleable: true,
127
                        ...buttonAttrs
128
                } );
129

130
                // Bind button model to command.
131
                buttonView.bind( 'isEnabled' ).to( command );
54✔
132
                buttonView.bind( 'isOn' ).to( command, 'value', value => value === option );
73✔
133

134
                // Execute command.
135
                this.listenTo( buttonView, 'execute', () => {
54✔
136
                        editor.execute( 'alignment', { value: option } );
5✔
137
                        editor.editing.view.focus();
5✔
138
                } );
139

140
                return buttonView;
54✔
141
        }
142

143
        /**
144
         * Helper method for initializing the toolnar dropdown and linking it with an appropriate command.
145
         *
146
         * @param option The name of the alignment option for which the button is added.
147
         */
148
        private _addToolbarDropdown( options: Array<AlignmentFormat> ): void {
149
                const editor = this.editor;
68✔
150
                const factory = editor.ui.componentFactory;
68✔
151

152
                factory.add( 'alignment', locale => {
68✔
153
                        const dropdownView = createDropdown( locale );
16✔
154
                        const tooltipPosition = locale.uiLanguageDirection === 'rtl' ? 'w' : 'e';
16✔
155
                        const t = locale.t;
16✔
156

157
                        // Add existing alignment buttons to dropdown's toolbar.
158
                        addToolbarToDropdown(
16✔
159
                                dropdownView,
160
                                () => options.map( option => this._createButton( locale, option.name, { tooltipPosition } ) ) as Array<ButtonView>,
34✔
161
                                {
162
                                        enableActiveItemFocusOnDropdownOpen: true,
163
                                        isVertical: true,
164
                                        ariaLabel: t( 'Text alignment toolbar' )
165
                                }
166
                        );
167

168
                        // Configure dropdown properties an behavior.
169
                        dropdownView.buttonView.set( {
16✔
170
                                label: t( 'Text alignment' ),
171
                                tooltip: true
172
                        } );
173

174
                        dropdownView.extendTemplate( {
16✔
175
                                attributes: {
176
                                        class: 'ck-alignment-dropdown'
177
                                }
178
                        } );
179

180
                        // The default icon depends on the direction of the content.
181
                        const defaultIcon = locale.contentLanguageDirection === 'rtl' ? iconsMap.get( 'right' ) : iconsMap.get( 'left' );
16✔
182
                        const command: AlignmentCommand = editor.commands.get( 'alignment' )!;
16✔
183

184
                        // Change icon to reflect current selection's alignment.
185
                        dropdownView.buttonView.bind( 'icon' ).to( command, 'value', value => iconsMap.get( value ) || defaultIcon );
20✔
186

187
                        // Enable button if any of the buttons is enabled.
188
                        dropdownView.bind( 'isEnabled' ).to( command, 'isEnabled' );
16✔
189

190
                        // Focus the editable after executing the command.
191
                        // Overrides a default behaviour where the focus is moved to the dropdown button (#12125).
192
                        this.listenTo( dropdownView, 'execute', () => {
16✔
193
                                editor.editing.view.focus();
1✔
194
                        } );
195

196
                        return dropdownView;
16✔
197
                } );
198
        }
199

200
        /**
201
         * Creates a menu for all alignment options to use either in menu bar.
202
         *
203
         * @param options Normalized alignment options from config.
204
         */
205
        private _addMenuBarMenu( options: Array<AlignmentFormat> ): void {
206
                const editor = this.editor;
68✔
207

208
                editor.ui.componentFactory.add( 'menuBar:alignment', locale => {
68✔
209
                        const command: AlignmentCommand = editor.commands.get( 'alignment' )!;
43✔
210
                        const t = locale.t;
43✔
211
                        const menuView = new MenuBarMenuView( locale );
43✔
212
                        const listView = new MenuBarMenuListView( locale );
43✔
213

214
                        menuView.bind( 'isEnabled' ).to( command );
43✔
215

216
                        listView.set( {
43✔
217
                                ariaLabel: t( 'Text alignment' ),
218
                                role: 'menu'
219
                        } );
220

221
                        menuView.buttonView.set( {
43✔
222
                                label: t( 'Text alignment' )
223
                        } );
224

225
                        for ( const option of options ) {
43✔
226
                                const listItemView = new MenuBarMenuListItemView( locale, menuView );
172✔
227
                                const buttonView = new MenuBarMenuListItemButtonView( locale );
172✔
228

229
                                buttonView.delegate( 'execute' ).to( menuView );
172✔
230
                                buttonView.set( {
172✔
231
                                        label: this.localizedOptionTitles[ option.name ],
232
                                        icon: iconsMap.get( option.name ),
233
                                        role: 'menuitemcheckbox',
234
                                        isToggleable: true
235
                                } );
236

237
                                buttonView.on( 'execute', () => {
172✔
238
                                        editor.execute( 'alignment', { value: option.name } );
8✔
239

240
                                        editor.editing.view.focus();
8✔
241
                                } );
242

243
                                buttonView.bind( 'isOn' ).to( command, 'value', value => value === option.name );
268✔
244
                                buttonView.bind( 'isEnabled' ).to( command, 'isEnabled' );
172✔
245

246
                                listItemView.children.add( buttonView );
172✔
247
                                listView.items.add( listItemView );
172✔
248
                        }
249

250
                        menuView.panelView.children.add( listView );
43✔
251

252
                        return menuView;
43✔
253
                } );
254
        }
255
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc