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

ckeditor / ckeditor5 / eaaec596-6ab7-44bb-92db-7bcfef840657

19 May 2024 03:50AM CUT coverage: 100.0%. Remained the same
eaaec596-6ab7-44bb-92db-7bcfef840657

push

circleci

web-flow
Merge stable into master

13717 of 13717 branches covered (100.0%)

Branch coverage included in aggregate %.

36268 of 36268 relevant lines covered (100.0%)

11009.04 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 = 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;
264✔
70
        }
71

72
        /**
73
         * @inheritDoc
74
         */
75
        public init(): void {
76
                const editor = this.editor;
66✔
77
                const options = normalizeAlignmentOptions( editor.config.get( 'alignment.options' )! );
66✔
78

79
                options
66✔
80
                        .map( option => option.name )
254✔
81
                        .filter( isSupported )
82
                        .forEach( option => this._addButton( option ) );
254✔
83

84
                this._addToolbarDropdown( options );
66✔
85
                this._addMenuBarMenu( options );
66✔
86
        }
87

88
        /**
89
         * Helper method for initializing the button and linking it with an appropriate command.
90
         *
91
         * @param option The name of the alignment option for which the button is added.
92
         */
93
        private _addButton( option: SupportedOption ): void {
94
                const editor = this.editor;
254✔
95

96
                editor.ui.componentFactory.add( `alignment:${ option }`, locale => this._createButton( locale, option ) );
254✔
97
        }
98

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

115
                buttonView.set( {
54✔
116
                        label: this.localizedOptionTitles[ option ],
117
                        icon: iconsMap.get( option ),
118
                        tooltip: true,
119
                        isToggleable: true,
120
                        ...buttonAttrs
121
                } );
122

123
                // Bind button model to command.
124
                buttonView.bind( 'isEnabled' ).to( command );
54✔
125
                buttonView.bind( 'isOn' ).to( command, 'value', value => value === option );
73✔
126

127
                // Execute command.
128
                this.listenTo( buttonView, 'execute', () => {
54✔
129
                        editor.execute( 'alignment', { value: option } );
5✔
130
                        editor.editing.view.focus();
5✔
131
                } );
132

133
                return buttonView;
54✔
134
        }
135

136
        /**
137
         * Helper method for initializing the toolnar dropdown and linking it with an appropriate command.
138
         *
139
         * @param option The name of the alignment option for which the button is added.
140
         */
141
        private _addToolbarDropdown( options: Array<AlignmentFormat> ): void {
142
                const editor = this.editor;
66✔
143
                const factory = editor.ui.componentFactory;
66✔
144

145
                factory.add( 'alignment', locale => {
66✔
146
                        const dropdownView = createDropdown( locale );
16✔
147
                        const tooltipPosition = locale.uiLanguageDirection === 'rtl' ? 'w' : 'e';
16✔
148
                        const t = locale.t;
16✔
149

150
                        // Add existing alignment buttons to dropdown's toolbar.
151
                        addToolbarToDropdown(
16✔
152
                                dropdownView,
153
                                () => options.map( option => this._createButton( locale, option.name, { tooltipPosition } ) ) as Array<ButtonView>,
34✔
154
                                {
155
                                        enableActiveItemFocusOnDropdownOpen: true,
156
                                        isVertical: true,
157
                                        ariaLabel: t( 'Text alignment toolbar' )
158
                                }
159
                        );
160

161
                        // Configure dropdown properties an behavior.
162
                        dropdownView.buttonView.set( {
16✔
163
                                label: t( 'Text alignment' ),
164
                                tooltip: true
165
                        } );
166

167
                        dropdownView.extendTemplate( {
16✔
168
                                attributes: {
169
                                        class: 'ck-alignment-dropdown'
170
                                }
171
                        } );
172

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

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

180
                        // Enable button if any of the buttons is enabled.
181
                        dropdownView.bind( 'isEnabled' ).to( command, 'isEnabled' );
16✔
182

183
                        // Focus the editable after executing the command.
184
                        // Overrides a default behaviour where the focus is moved to the dropdown button (#12125).
185
                        this.listenTo( dropdownView, 'execute', () => {
16✔
186
                                editor.editing.view.focus();
1✔
187
                        } );
188

189
                        return dropdownView;
16✔
190
                } );
191
        }
192

193
        /**
194
         * Creates a menu for all alignment options to use either in menu bar.
195
         *
196
         * @param options Normalized alignment options from config.
197
         */
198
        private _addMenuBarMenu( options: Array<AlignmentFormat> ): void {
199
                const editor = this.editor;
66✔
200

201
                editor.ui.componentFactory.add( 'menuBar:alignment', locale => {
66✔
202
                        const command: AlignmentCommand = editor.commands.get( 'alignment' )!;
43✔
203
                        const t = locale.t;
43✔
204
                        const menuView = new MenuBarMenuView( locale );
43✔
205
                        const listView = new MenuBarMenuListView( locale );
43✔
206

207
                        menuView.bind( 'isEnabled' ).to( command );
43✔
208

209
                        listView.set( {
43✔
210
                                ariaLabel: t( 'Text alignment' ),
211
                                role: 'menu'
212
                        } );
213

214
                        menuView.buttonView.set( {
43✔
215
                                label: t( 'Text alignment' )
216
                        } );
217

218
                        for ( const option of options ) {
43✔
219
                                const listItemView = new MenuBarMenuListItemView( locale, menuView );
172✔
220
                                const buttonView = new MenuBarMenuListItemButtonView( locale );
172✔
221

222
                                buttonView.extendTemplate( {
172✔
223
                                        attributes: {
224
                                                'aria-checked': buttonView.bindTemplate.to( 'isOn' )
225
                                        }
226
                                } );
227

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

234
                                buttonView.on( 'execute', () => {
172✔
235
                                        editor.execute( 'alignment', { value: option.name } );
8✔
236

237
                                        editor.editing.view.focus();
8✔
238
                                } );
239

240
                                buttonView.bind( 'isOn' ).to( command, 'value', value => value === option.name );
268✔
241
                                buttonView.bind( 'isEnabled' ).to( command, 'isEnabled' );
172✔
242

243
                                listItemView.children.add( buttonView );
172✔
244
                                listView.items.add( listItemView );
172✔
245
                        }
246

247
                        menuView.panelView.children.add( listView );
43✔
248

249
                        return menuView;
43✔
250
                } );
251
        }
252
}
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