Image.jsx 22.9 KB
Newer Older
yakoff94's avatar
yakoff94 committed
1
import { NodeViewWrapper, ReactNodeViewRenderer } from "@tiptap/react";
2
import React, { useEffect, useRef, useState, Fragment } from "react";
yakoff94's avatar
yakoff94 committed
3
import TipTapImage from "@tiptap/extension-image";
Яков's avatar
update    
Яков committed
4
5
6
7
import { Button, Modal, Input } from 'antd';
import Icon, {FontSizeOutlined} from "@ant-design/icons";
const { TextArea } = Input;

yakoff94's avatar
yakoff94 committed
8
9
10

const MIN_WIDTH = 60;
const BORDER_COLOR = '#0096fd';
Яков's avatar
Яков committed
11
const ALIGN_OPTIONS = ['left', 'center', 'right', 'text'];
yakoff94's avatar
yakoff94 committed
12

Яков's avatar
fix    
Яков committed
13
const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, selected }) => {
yakoff94's avatar
yakoff94 committed
14
    const imgRef = useRef(null);
15
16
17
    const wrapperRef = useRef(null);
    const [showAlignMenu, setShowAlignMenu] = useState(false);
    const isInitialized = useRef(false);
Яков's avatar
update    
Яков committed
18
    const [isResizing, setIsResizing] = useState(false);
Яков's avatar
update    
Яков committed
19
20
21
    const [altModalVisible, setAltModalVisible] = useState(false);
    const [tempAlt, setTempAlt] = useState(node.attrs.alt || '');

Яков's avatar
update    
Яков committed
22

Яков's avatar
fix    
Яков committed
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
    // Добавляем прозрачный нулевой пробел после изображения
    useEffect(() => {
        if (!editor || !getPos) return;

        const pos = getPos() + 1;
        const doc = editor.state.doc;

        if (doc.nodeSize > pos && doc.nodeAt(pos)?.textContent !== '\u200B') {
            editor.commands.insertContentAt(pos, {
                type: 'text',
                text: '\u200B' // Невидимый нулевой пробел
            });
        }
    }, [editor, getPos]);

Яков's avatar
fix    
Яков committed
38
39
    // Получаем текущую ширину редактора и доступное пространство
    const getEditorDimensions = () => {
Яков's avatar
Яков committed
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
        const editorContent = editor?.options?.element?.closest('.atma-editor-content');
        if (!editorContent) return { width: Infinity, availableSpace: Infinity };

        const fullEditorWidth = editorContent.clientWidth;
        const editorStyles = window.getComputedStyle(editorContent);
        const paddingLeft = parseFloat(editorStyles.paddingLeft) || 0;
        const paddingRight = parseFloat(editorStyles.paddingRight) || 0;
        const availableEditorWidth = fullEditorWidth - paddingLeft - paddingRight;

        let container;

        // при center — всегда редактор
        if (node.attrs.align === 'center') {
            container = editorContent;
        } else {
            // при других выравниваниях — ближайший блок
            container = imgRef.current?.closest('li, blockquote, td, p, div') || editorContent;
Яков's avatar
fix    
Яков committed
57
58
        }

Яков's avatar
Яков committed
59
60
61
62
63
64
65
66
67
        const containerStyles = window.getComputedStyle(container);
        const containerPaddingLeft = parseFloat(containerStyles.paddingLeft) || 0;
        const containerPaddingRight = parseFloat(containerStyles.paddingRight) || 0;
        const containerWidth = container.clientWidth - containerPaddingLeft - containerPaddingRight;

        return {
            width: containerWidth,            // текущая ширина контейнера
            availableSpace: availableEditorWidth // фиксированная доступная ширина
        };
Яков's avatar
fix    
Яков committed
68
69
    };

Яков's avatar
Яков committed
70

Яков's avatar
fix    
Яков committed
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
    // Безопасное обновление атрибутов с учетом выравнивания и границ
    const safeUpdateAttributes = (newAttrs) => {
        const { width: editorWidth, availableSpace } = getEditorDimensions();
        let { width, height, align } = { ...node.attrs, ...newAttrs };
        const newAlign = newAttrs.align || align;

        // При изменении выравнивания проверяем доступное пространство
        if (newAlign && newAlign !== align) {
            const maxWidth = availableSpace;
            if (width > maxWidth) {
                const ratio = maxWidth / width;
                width = maxWidth;
                height = Math.round(height * ratio);
            }
        } else {
            // Для обычного обновления размеров
            const maxWidth = availableSpace;
            if (width > maxWidth) {
                const ratio = maxWidth / width;
                width = maxWidth;
                height = Math.round(height * ratio);
            }
        }

        // Проверяем минимальный размер
        if (width < MIN_WIDTH) {
            const ratio = MIN_WIDTH / width;
            width = MIN_WIDTH;
            height = Math.round(height * ratio);
        }

        updateAttributes({ width, height, ...newAttrs });
Яков's avatar
update    
Яков committed
103
    };
104

Яков's avatar
fix    
Яков committed
105
    // Инициализация изображения
106
    useEffect(() => {
Яков's avatar
fix    
Яков committed
107
        if (!node.attrs['data-node-id']) {
Яков's avatar
fix    
Яков committed
108
            safeUpdateAttributes({
Яков's avatar
fix    
Яков committed
109
110
111
                'data-node-id': `img-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
            });
        }
Яков's avatar
fix    
Яков committed
112
    }, [node.attrs['data-node-id']]);
Яков's avatar
fix    
Яков committed
113

Яков's avatar
fix    
Яков committed
114
    // Обработка кликов вне изображения
Яков's avatar
fix    
Яков committed
115
116
117
118
    useEffect(() => {
        const handleClickOutside = (event) => {
            if (wrapperRef.current && !wrapperRef.current.contains(event.target) && selected) {
                editor.commands.setNodeSelection(getPos());
Яков's avatar
fix    
Яков committed
119
120
            }
        };
Яков's avatar
fix    
Яков committed
121
122
123
        document.addEventListener('mousedown', handleClickOutside);
        return () => document.removeEventListener('mousedown', handleClickOutside);
    }, [selected, editor, getPos]);
yakoff94's avatar
yakoff94 committed
124

Яков's avatar
fix    
Яков committed
125
    // Загрузка и инициализация изображения
yakoff94's avatar
yakoff94 committed
126
    useEffect(() => {
Яков's avatar
fix    
Яков committed
127
128
129
130
        if (!imgRef.current || isInitialized.current) return;

        const initImageSize = () => {
            try {
Яков's avatar
fix    
Яков committed
131
132
133
134
135
                // Если размеры уже заданы в атрибутах - используем их сразу
                if (node.attrs.width && node.attrs.height) {
                    isInitialized.current = true;
                    return;
                }
Яков's avatar
fix    
Яков committed
136

Яков's avatar
fix    
Яков committed
137
                const { width: editorWidth } = getEditorDimensions();
Яков's avatar
fix    
Яков committed
138
139
                const naturalWidth = imgRef.current.naturalWidth;
                const naturalHeight = imgRef.current.naturalHeight;
Яков's avatar
update    
Яков committed
140

Яков's avatar
fix    
Яков committed
141
142
                if (naturalWidth <= 0 || naturalHeight <= 0) {
                    console.warn('Image has invalid natural dimensions, retrying...');
Яков's avatar
fix    
Яков committed
143
                    setTimeout(initImageSize, 100);
Яков's avatar
fix    
Яков committed
144
145
146
147
148
149
150
151
152
153
154
155
                    return;
                }

                let initialWidth = naturalWidth;
                let initialHeight = naturalHeight;

                if (initialWidth > editorWidth) {
                    const ratio = editorWidth / initialWidth;
                    initialWidth = editorWidth;
                    initialHeight = Math.round(initialHeight * ratio);
                }

Яков's avatar
fix    
Яков committed
156
                safeUpdateAttributes({
Яков's avatar
fix    
Яков committed
157
158
159
                    width: initialWidth,
                    height: initialHeight,
                    'data-node-id': node.attrs['data-node-id'] || `img-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
Яков's avatar
fix    
Яков committed
160
161
                });
                isInitialized.current = true;
Яков's avatar
fix    
Яков committed
162
163
164
165
166
            } catch (error) {
                console.warn('Error initializing image size:', error);
            }
        };

Яков's avatar
fix    
Яков committed
167
        const handleLoad = () => {
Яков's avatar
fix    
Яков committed
168
169
170
171
172
            // Если размеры уже заданы в атрибутах, пропускаем инициализацию
            if (node.attrs.width && node.attrs.height) {
                isInitialized.current = true;
                return;
            }
Яков's avatar
fix    
Яков committed
173
174
175
            setTimeout(initImageSize, 50);
        };

Яков's avatar
fix    
Яков committed
176
        if (imgRef.current.complete) {
Яков's avatar
fix    
Яков committed
177
            handleLoad();
Яков's avatar
fix    
Яков committed
178
        } else {
Яков's avatar
fix    
Яков committed
179
            imgRef.current.addEventListener('load', handleLoad);
180
        }
Яков's avatar
fix    
Яков committed
181
182

        return () => {
Яков's avatar
fix    
Яков committed
183
184
185
            if (imgRef.current) {
                imgRef.current.removeEventListener('load', handleLoad);
            }
Яков's avatar
fix    
Яков committed
186
        };
Яков's avatar
fix    
Яков committed
187
    }, [node.attrs.width, node.attrs.height, node.attrs['data-node-id']]);
188

Яков's avatar
fix    
Яков committed
189
    // Обработка ресайза изображения
190
191
192
193
    const handleResizeStart = (direction) => (e) => {
        e.preventDefault();
        e.stopPropagation();

Яков's avatar
update    
Яков committed
194
        setIsResizing(true);
Яков's avatar
update    
Яков committed
195
196
        editor.commands.setNodeSelection(getPos());

Яков's avatar
fix    
Яков committed
197
198
199
200
201
        const startWidth = node.attrs.width || imgRef.current.naturalWidth;
        const startHeight = node.attrs.height || imgRef.current.naturalHeight;
        const aspectRatio = startWidth / startHeight;
        const startX = e.clientX;
        const startY = e.clientY;
Яков's avatar
Яков committed
202
        const { width: initialEditorWidth, availableSpace: initialAvailableSpace } = getEditorDimensions();
203
204

        const onMouseMove = (e) => {
Яков's avatar
Яков committed
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
            requestAnimationFrame(() => {
                const maxWidth = node.attrs.align === 'center' ? initialEditorWidth : initialAvailableSpace;

                const deltaX = e.clientX - startX;
                const deltaY = e.clientY - startY;

                let newWidth, newHeight;

                if (node.attrs.align === 'center') {
                    if (direction.includes('n') || direction.includes('s')) {
                        const scale = direction.includes('s') ? 1 : -1;
                        newHeight = Math.max(startHeight + deltaY * scale, MIN_WIDTH);
                        newWidth = Math.min(Math.round(newHeight * aspectRatio), maxWidth);
                        newHeight = Math.round(newWidth / aspectRatio);
                    } else {
                        const scale = direction.includes('e') ? 1 : -1;
                        newWidth = Math.min(
                            Math.max(startWidth + deltaX * scale, MIN_WIDTH),
                            maxWidth
                        );
                        newHeight = Math.round(newWidth / aspectRatio);
                    }
227
                } else {
Яков's avatar
Яков committed
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
                    if (direction.includes('e') || direction.includes('w')) {
                        const scale = direction.includes('e') ? 1 : -1;
                        newWidth = Math.min(
                            Math.max(startWidth + deltaX * scale, MIN_WIDTH),
                            maxWidth
                        );
                        newHeight = Math.round(newWidth / aspectRatio);
                    } else {
                        const scale = direction.includes('s') ? 1 : -1;
                        newHeight = Math.max(startHeight + deltaY * scale, MIN_WIDTH);
                        newWidth = Math.min(
                            Math.round(newHeight * aspectRatio),
                            maxWidth
                        );
                        newHeight = Math.round(newWidth / aspectRatio);
                    }
244
245
                }

Яков's avatar
Яков committed
246
247
                safeUpdateAttributes({ width: newWidth, height: newHeight });
            });
yakoff94's avatar
yakoff94 committed
248
249
        };

Яков's avatar
Яков committed
250

251
252
253
        const onMouseUp = () => {
            window.removeEventListener('mousemove', onMouseMove);
            window.removeEventListener('mouseup', onMouseUp);
Яков's avatar
update    
Яков committed
254
            setIsResizing(false);
Яков's avatar
update    
Яков committed
255
            editor.commands.setNodeSelection(getPos());
Яков's avatar
fix    
Яков committed
256
            editor.commands.focus();
yakoff94's avatar
yakoff94 committed
257
258
        };

259
260
261
        window.addEventListener('mousemove', onMouseMove);
        window.addEventListener('mouseup', onMouseUp);
    };
yakoff94's avatar
yakoff94 committed
262

Яков's avatar
fix    
Яков committed
263
    // Изменение выравнивания с автоматическим масштабированием
264
    const handleAlign = (align) => {
Яков's avatar
Яков committed
265
266
267
268
        safeUpdateAttributes({ align }); // первый вызов
        setTimeout(() => {
            safeUpdateAttributes({ align }); // повторный вызов с обновлёнными размерами
        }, 50);
269
        setShowAlignMenu(false);
Яков's avatar
fix    
Яков committed
270
        editor.commands.focus();
271
272
    };

Яков's avatar
fix    
Яков committed
273
    // Стили для обертки изображения
Яков's avatar
update    
Яков committed
274
275
    const getWrapperStyle = () => {
        const baseStyle = {
276
            display: 'inline-block',
Яков's avatar
update    
Яков committed
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
            lineHeight: 0,
            position: 'relative',
            outline: (selected || isResizing) ? `1px dashed ${BORDER_COLOR}` : 'none',
            verticalAlign: 'top',
            margin: '0.5rem 0',
        };

        if (node.attrs.align === 'center') {
            return {
                ...baseStyle,
                display: 'block',
                marginLeft: 'auto',
                marginRight: 'auto',
                width: node.attrs.width ? `${node.attrs.width}px` : 'fit-content',
                maxWidth: '100%',
                textAlign: 'center'
            };
        }

        return {
            ...baseStyle,
            ...(node.attrs.align === 'left' && {
                float: 'left',
                marginRight: '1rem',
                width: node.attrs.width ? `${node.attrs.width}px` : 'auto',
            }),
            ...(node.attrs.align === 'right' && {
                float: 'right',
                marginLeft: '1rem',
                width: node.attrs.width ? `${node.attrs.width}px` : 'auto',
            }),
            ...(node.attrs.align === 'text' && {
                display: 'inline-block',
                float: 'none',
                margin: '0 0.2rem',
                verticalAlign: 'middle',
                width: node.attrs.width ? `${node.attrs.width}px` : 'auto',
            }),
        };
    };
yakoff94's avatar
yakoff94 committed
317

Яков's avatar
fix    
Яков committed
318
    // Стили для самого изображения
319
320
    const getImageStyle = () => ({
        width: node.attrs.width ? `${node.attrs.width}px` : 'auto',
Яков's avatar
fix    
Яков committed
321
        height: 'auto',
322
323
324
325
        maxWidth: '100%',
        display: 'block',
        cursor: 'default',
        userSelect: 'none',
Яков's avatar
Яков committed
326
        margin: node.attrs.align === 'center' ? '0 auto' : '0',
Яков's avatar
update    
Яков committed
327
        verticalAlign: node.attrs.align === 'text' ? 'middle' : 'top',
Яков's avatar
fix    
Яков committed
328
        objectFit: 'contain'
329
330
    });

yakoff94's avatar
yakoff94 committed
331
332
    return (
        <NodeViewWrapper
333
            as="div"
334
335
336
337
            style={getWrapperStyle()}
            ref={wrapperRef}
            onClick={(e) => {
                e.stopPropagation();
Яков's avatar
fix    
Яков committed
338
                editor.commands.setNodeSelection(getPos());
yakoff94's avatar
yakoff94 committed
339
            }}
340
341
            contentEditable={false}
            data-image-wrapper
yakoff94's avatar
yakoff94 committed
342
343
        >
            <img
344
345
                {...node.attrs}
                ref={imgRef}
Яков's avatar
update    
Яков committed
346
                draggable={true}
347
                style={getImageStyle()}
348
                onLoad={() => {
Яков's avatar
fix    
Яков committed
349
                    if (imgRef.current && !isInitialized.current && !node.attrs.width && !node.attrs.height) {
Яков's avatar
fix    
Яков committed
350
                        const { width: editorWidth } = getEditorDimensions();
Яков's avatar
update    
Яков committed
351
352
353
                        const naturalWidth = imgRef.current.naturalWidth;
                        const naturalHeight = imgRef.current.naturalHeight;

Яков's avatar
fix    
Яков committed
354
355
356
                        safeUpdateAttributes({
                            width: naturalWidth,
                            height: naturalHeight,
Яков's avatar
fix    
Яков committed
357
                            'data-node-id': node.attrs['data-node-id'] || Math.random().toString(36).substr(2, 9)
358
359
360
                        });
                        isInitialized.current = true;
                    }
yakoff94's avatar
yakoff94 committed
361
362
                }}
            />
363

Яков's avatar
update    
Яков committed
364
            {(selected || isResizing) && (
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
                <Fragment>
                    {['nw', 'ne', 'sw', 'se'].map(dir => (
                        <div
                            key={dir}
                            onMouseDown={handleResizeStart(dir)}
                            style={{
                                position: 'absolute',
                                width: 12,
                                height: 12,
                                backgroundColor: BORDER_COLOR,
                                border: '1px solid white',
                                [dir[0] === 'n' ? 'top' : 'bottom']: -6,
                                [dir[1] === 'w' ? 'left' : 'right']: node.attrs.align === 'center' ? '50%' : -6,
                                transform: node.attrs.align === 'center' ?
                                    `translateX(${dir[1] === 'w' ? '-100%' : '0%'})` : 'none',
                                cursor: `${dir}-resize`,
                                zIndex: 10
                            }}
                        />
yakoff94's avatar
yakoff94 committed
384
                    ))}
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400

                    {showAlignMenu && (
                        <div style={{
                            position: 'absolute',
                            top: -40,
                            left: '50%',
                            transform: 'translateX(-50%)',
                            backgroundColor: 'white',
                            boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
                            borderRadius: 4,
                            padding: 4,
                            zIndex: 20,
                            display: 'flex'
                        }}>
                            {ALIGN_OPTIONS.map(align => (
                                <button
Яков's avatar
update    
Яков committed
401
                                    type="button"
402
403
404
405
                                    key={align}
                                    onClick={() => handleAlign(align)}
                                    style={{
                                        margin: '0 2px',
406
                                        padding: '10px 8px',
407
408
409
410
411
412
413
414
415
416
417
418
419
                                        background: node.attrs.align === align ? '#e6f7ff' : 'transparent',
                                        border: '1px solid #d9d9d9',
                                        borderRadius: 2,
                                        cursor: 'pointer'
                                    }}
                                >
                                    {align}
                                </button>
                            ))}
                        </div>
                    )}

                    <button
Яков's avatar
update    
Яков committed
420
                        type="button"
421
422
423
424
425
426
427
                        onClick={(e) => {
                            e.stopPropagation();
                            setShowAlignMenu(!showAlignMenu);
                        }}
                        style={{
                            position: 'absolute',
                            top: -30,
Яков's avatar
update    
Яков committed
428
                            left: 'calc(50% - 6px)',
429
430
431
432
                            transform: 'translateX(-50%)',
                            backgroundColor: 'white',
                            border: `1px solid ${BORDER_COLOR}`,
                            borderRadius: 4,
433
                            padding: '8px 8px',
434
435
436
437
438
439
440
                            cursor: 'pointer',
                            fontSize: 12,
                            zIndex: 10
                        }}
                    >
                        Align
                    </button>
Яков's avatar
update    
Яков committed
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
                    <Button
                        size="default"
                        shape={'circle'}
                        type={node.attrs.alt?.length > 0 ? 'primary' : 'default'}
                        onClick={(e) => {
                            e.stopPropagation();
                            setTempAlt(node.attrs.alt || '');
                            setAltModalVisible(true);
                        }}
                        style={{
                            position: 'absolute',
                            top: 4,
                            right: 4,
                            zIndex: 15,
                        }}
                    >
                        <FontSizeOutlined />
                    </Button>

460
                </Fragment>
yakoff94's avatar
yakoff94 committed
461
            )}
Яков's avatar
update    
Яков committed
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
            <Modal
                title="Редактировать описание изображения (alt)"
                open={altModalVisible}
                onOk={() => {
                    updateAttributes({ alt: tempAlt });
                    setAltModalVisible(false);
                }}
                onCancel={() => setAltModalVisible(false)}
                okText="Применить"
                cancelText="Отмена"
            >
                <TextArea
                    value={tempAlt}
                    onChange={(e) => setTempAlt(e.target.value)}
                    rows={4}
                    placeholder="Введите alt-текст изображения..."
                />
            </Modal>
yakoff94's avatar
yakoff94 committed
480
481
482
483
484
485
486
487
        </NodeViewWrapper>
    );
};

const ResizableImageExtension = TipTapImage.extend({
    addAttributes() {
        return {
            ...this.parent?.(),
Яков's avatar
fix    
Яков committed
488
            src: { default: null },
Яков's avatar
update    
Яков committed
489
490
491
492
493
494
495
496
497
498
499
500
            alt: {
                default: null,
                parseHTML: element => {
                    const raw = element.getAttribute('alt')
                    return raw?.replace(/&#10;/g, '\n') || null
                },
                renderHTML: attributes => {
                    return attributes.alt
                        ? { alt: attributes.alt.replace(/\n/g, '&#10;') }
                        : {}
                }
            },
Яков's avatar
fix    
Яков committed
501
            title: { default: null },
502
503
            width: {
                default: null,
Яков's avatar
fix    
Яков committed
504
                parseHTML: element => parseInt(element.getAttribute('width'), 10) || null,
505
506
507
508
                renderHTML: attributes => attributes.width ? { width: attributes.width } : {}
            },
            height: {
                default: null,
Яков's avatar
fix    
Яков committed
509
                parseHTML: element => parseInt(element.getAttribute('height'), 10) || null,
510
511
512
513
514
515
                renderHTML: attributes => attributes.height ? { height: attributes.height } : {}
            },
            align: {
                default: 'left',
                parseHTML: element => element.getAttribute('data-align') || 'left',
                renderHTML: attributes => ({ 'data-align': attributes.align })
Яков's avatar
fix    
Яков committed
516
517
518
519
520
            },
            'data-node-id': {
                default: null,
                parseHTML: element => element.getAttribute('data-node-id'),
                renderHTML: attributes => ({ 'data-node-id': attributes['data-node-id'] })
521
            }
yakoff94's avatar
yakoff94 committed
522
523
        };
    },
Яков's avatar
update    
Яков committed
524
525
526
527
528
529
530
531
532
533
534
535
536
537
    renderHTML({ node, HTMLAttributes }) {
        const {
            src,
            alt = '',
            title = '',
            width,
            height,
            ...rest
        } = HTMLAttributes;

        const align = node.attrs.align || 'left';

        const style = [];

Яков's avatar
fix    
Яков committed
538

Яков's avatar
update    
Яков committed
539
540
541
        if (align === 'center') {
            style.push('display: block', 'margin-left: auto', 'margin-right: auto');
        } else if (align === 'left') {
Яков's avatar
fix    
Яков committed
542
            style.push('float: left', 'margin-right: 1rem');
Яков's avatar
update    
Яков committed
543
        } else if (align === 'right') {
Яков's avatar
fix    
Яков committed
544
            style.push('float: right', 'margin-left: 1rem');
Яков's avatar
update    
Яков committed
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
        } else if (align === 'text') {
            style.push('display: inline-block', 'vertical-align: middle', 'margin: 0 0.2rem');
        }

        if (width) style.push(`width: ${width}px`);
        if (height) style.push(`height: ${height}px`);

        return [
            'img',
            {
                src,
                alt,
                title,
                width,
                height,
                'data-align': align,
                style: style.join('; '),
                ...rest,
            }
        ];
    },
566

yakoff94's avatar
yakoff94 committed
567
568
    addNodeView() {
        return ReactNodeViewRenderer(ResizableImageTemplate);
Яков's avatar
fix    
Яков committed
569
570
571
572
573
574
575
576
    },

    addKeyboardShortcuts() {
        return {
            'Mod-ArrowLeft': () => this.editor.commands.updateAttributes(this.type.name, { align: 'left' }),
            'Mod-ArrowRight': () => this.editor.commands.updateAttributes(this.type.name, { align: 'right' }),
            'Mod-ArrowDown': () => this.editor.commands.updateAttributes(this.type.name, { align: 'center' }),
        };
577
578
579
580
581
    }
}).configure({
    inline: true,
    group: 'inline',
    draggable: true,
Яков's avatar
fix    
Яков committed
582
    selectable: true
583
});
yakoff94's avatar
yakoff94 committed
584
585

export default ResizableImageExtension;