-
Notifications
You must be signed in to change notification settings - Fork 3.6k
/
toggleimagecaptioncommand.ts
170 lines (143 loc) · 5.84 KB
/
toggleimagecaptioncommand.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/**
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @module image/imagecaption/toggleimagecaptioncommand
*/
import type { Element, Writer } from 'ckeditor5/src/engine.js';
import { Command } from 'ckeditor5/src/core.js';
import ImageBlockEditing from '../image/imageblockediting.js';
import type ImageCaptionUtils from './imagecaptionutils.js';
import type ImageUtils from '../imageutils.js';
import type ImageCaptionEditing from './imagecaptionediting.js';
/**
* The toggle image caption command.
*
* This command is registered by {@link module:image/imagecaption/imagecaptionediting~ImageCaptionEditing} as the
* `'toggleImageCaption'` editor command.
*
* Executing this command:
*
* * either adds or removes the image caption of a selected image (depending on whether the caption is present or not),
* * removes the image caption if the selection is anchored in one.
*
* ```ts
* // Toggle the presence of the caption.
* editor.execute( 'toggleImageCaption' );
* ```
*
* **Note**: Upon executing this command, the selection will be set on the image if previously anchored in the caption element.
*
* **Note**: You can move the selection to the caption right away as it shows up upon executing this command by using
* the `focusCaptionOnShow` option:
*
* ```ts
* editor.execute( 'toggleImageCaption', { focusCaptionOnShow: true } );
* ```
*/
export default class ToggleImageCaptionCommand extends Command {
declare public value: boolean;
/**
* @inheritDoc
*/
public override refresh(): void {
const editor = this.editor;
const imageCaptionUtils: ImageCaptionUtils = editor.plugins.get( 'ImageCaptionUtils' );
const imageUtils: ImageUtils = editor.plugins.get( 'ImageUtils' );
// Only block images can get captions.
if ( !editor.plugins.has( ImageBlockEditing ) ) {
this.isEnabled = false;
this.value = false;
return;
}
const selection = editor.model.document.selection;
const selectedElement = selection.getSelectedElement();
if ( !selectedElement ) {
const ancestorCaptionElement = imageCaptionUtils.getCaptionFromModelSelection( selection );
this.isEnabled = !!ancestorCaptionElement;
this.value = !!ancestorCaptionElement;
return;
}
// Block images support captions by default but the command should also be enabled for inline
// images because toggling the caption when one is selected should convert it into a block image.
this.isEnabled = imageUtils.isImage( selectedElement );
if ( !this.isEnabled ) {
this.value = false;
} else {
this.value = !!imageCaptionUtils.getCaptionFromImageModelElement( selectedElement );
}
}
/**
* Executes the command.
*
* ```ts
* editor.execute( 'toggleImageCaption' );
* ```
*
* @param options Options for the executed command.
* @param options.focusCaptionOnShow When true and the caption shows up, the selection will be moved into it straight away.
* @fires execute
*/
public override execute( options: { focusCaptionOnShow?: boolean } = {} ): void {
const { focusCaptionOnShow } = options;
this.editor.model.change( writer => {
if ( this.value ) {
this._hideImageCaption( writer );
} else {
this._showImageCaption( writer, focusCaptionOnShow );
}
} );
}
/**
* Shows the caption of the `<imageBlock>` or `<imageInline>`. Also:
*
* * it converts `<imageInline>` to `<imageBlock>` to show the caption,
* * it attempts to restore the caption content from the `ImageCaptionEditing` caption registry,
* * it moves the selection to the caption right away, it the `focusCaptionOnShow` option was set.
*/
private _showImageCaption( writer: Writer, focusCaptionOnShow?: boolean ): void {
const model = this.editor.model;
const selection = model.document.selection;
const imageCaptionEditing: ImageCaptionEditing = this.editor.plugins.get( 'ImageCaptionEditing' );
const imageUtils: ImageUtils = this.editor.plugins.get( 'ImageUtils' );
let selectedImage = selection.getSelectedElement()!;
const savedCaption = imageCaptionEditing._getSavedCaption( selectedImage );
// Convert imageInline -> image first.
if ( imageUtils.isInlineImage( selectedImage ) ) {
this.editor.execute( 'imageTypeBlock' );
// Executing the command created a new model element. Let's pick it again.
selectedImage = selection.getSelectedElement()!;
}
// Try restoring the caption from the ImageCaptionEditing plugin storage.
const newCaptionElement = savedCaption || writer.createElement( 'caption' );
writer.append( newCaptionElement, selectedImage );
if ( focusCaptionOnShow ) {
writer.setSelection( newCaptionElement, 'in' );
}
}
/**
* Hides the caption of a selected image (or an image caption the selection is anchored to).
*
* The content of the caption is stored in the `ImageCaptionEditing` caption registry to make this
* a reversible action.
*/
private _hideImageCaption( writer: Writer ): void {
const editor = this.editor;
const selection = editor.model.document.selection;
const imageCaptionEditing: ImageCaptionEditing = editor.plugins.get( 'ImageCaptionEditing' );
const imageCaptionUtils: ImageCaptionUtils = editor.plugins.get( 'ImageCaptionUtils' );
let selectedImage = selection.getSelectedElement()!;
let captionElement: Element;
if ( selectedImage ) {
captionElement = imageCaptionUtils.getCaptionFromImageModelElement( selectedImage )!;
} else {
captionElement = imageCaptionUtils.getCaptionFromModelSelection( selection )!;
selectedImage = captionElement!.parent as Element;
}
// Store the caption content so it can be restored quickly if the user changes their mind even if they toggle image<->imageInline.
imageCaptionEditing._saveCaption( selectedImage, captionElement );
writer.setSelection( selectedImage, 'on' );
writer.remove( captionElement );
}
}