Skip to content

Commit 6578e4c

Browse files
committed
chore: update upload image
1 parent 6b857f4 commit 6578e4c

File tree

8 files changed

+92
-73
lines changed

8 files changed

+92
-73
lines changed

docs/extensions/Image/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ next:
1414

1515
```tsx
1616
import { Image } from 'reactjs-tiptap-editor/extension-bundle'; // [!code ++]
17+
import 'react-image-crop/dist/ReactCrop.css'; // [!code ++]
1718

1819
const extensions = [
1920
...,

src/components/BubbleMenu.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { BubbleMenuIframe } from '@/components/menus/components/BubbleMenuIframe
77
import BubbleMenuKatex from '@/components/menus/components/BubbleMenuKatex';
88
import { BubbleMenuMermaid } from '@/components/menus/components/BubbleMenuMermaid';
99
import { BubbleMenuTwitter } from '@/components/menus/components/BubbleMenuTwitter';
10-
import { ImageGif } from '@/extensions';
10+
import { Image, ImageGif } from '@/extensions';
1111
import type { BubbleMenuProps as BubbleMenuPropsType } from '@/types';
1212

1313
export interface BubbleMenuComponentProps {
@@ -38,7 +38,7 @@ export function BubbleMenu({ editor, disabled, bubbleMenu }: BubbleMenuComponent
3838
editor={editor}
3939
key="link"
4040
/> : null,
41-
extensionsNames.includes('image') && !bubbleMenu?.imageConfig?.hidden ? <BubbleMenuImage disabled={disabled}
41+
extensionsNames.includes(Image.name) && !bubbleMenu?.imageConfig?.hidden ? <BubbleMenuImage disabled={disabled}
4242
editor={editor}
4343
key="image"
4444
/> : null,

src/components/menus/components/BubbleMenuMedia.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { Editor } from '@tiptap/react';
44
import { BubbleMenu as BubbleMenuReact } from '@tiptap/react';
55

66
import { Separator, getBubbleImage, getBubbleImageGif, getBubbleVideo } from '@/components';
7-
import { ImageGif } from '@/extensions';
7+
import { Image, ImageGif, Video } from '@/extensions';
88
import { useLocale } from '@/locales';
99

1010
interface IPropsBubbleMenu {
@@ -46,15 +46,15 @@ function ItemA({ item, disabled, editor }: any) {
4646
}
4747

4848
function isImageNode(node: any) {
49-
return node.type.name === 'image';
49+
return node.type.name === Image.name;
5050
}
5151

5252
function isImageGifNode(node: any) {
5353
return node.type.name === ImageGif.name;
5454
}
5555

5656
function isVideoNode(node: any) {
57-
return node.type.name === 'video';
57+
return node.type.name === Video.name;
5858
}
5959

6060
function BubbleMenuImage(props: IPropsBubbleMenu) {

src/extensions/Image/components/ActionImageButton.tsx

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
1-
import { useMemo, useRef, useState } from 'react';
1+
import { useEffect, useId, useMemo, useRef, useState } from 'react';
22

33
import { ActionButton, Button, Checkbox, Input, Label, Tabs, TabsContent, TabsList, TabsTrigger } from '@/components';
44
import { Dialog, DialogContent, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
55
import { ImageCropper } from '@/extensions/Image/components/ImageCropper';
66
import Image from '@/extensions/Image/Image';
7-
import { actionDialogImage, useDialogImage } from '@/extensions/Image/store';
7+
import { actionDialogImage } from '@/extensions/Image/store';
88
import { useLocale } from '@/locales';
9+
import { listenEvent } from '@/utils/customEvents/customEvents';
10+
import { eventName } from '@/utils/customEvents/events.constant';
911

1012
function ActionImageButton(props: any) {
1113
const { t } = useLocale();
14+
const id = useId();
15+
const [open, setOpen] = useState(false);
1216

13-
const dialogImage = useDialogImage();
17+
const handleUploadImage = (evt: any) => {
18+
setOpen(evt.detail);
19+
};
1420

1521
const [link, setLink] = useState<string>('');
1622
const fileInput = useRef<HTMLInputElement>(null);
@@ -27,6 +33,16 @@ function ActionImageButton(props: any) {
2733
return uploadOptions;
2834
}, [props.editor]);
2935

36+
useEffect(() => {
37+
eventName.setEventNameUploadImage(id);
38+
39+
const rm1 = listenEvent(eventName.getEventNameUploadImage(), handleUploadImage);
40+
41+
return () => {
42+
rm1();
43+
};
44+
}, []);
45+
3046
async function handleFile(event: any) {
3147
const files = event?.target?.files;
3248
if (!props.editor || props.editor.isDestroyed || files.length === 0) {
@@ -43,15 +59,15 @@ function ActionImageButton(props: any) {
4359
}
4460

4561
props.editor.chain().focus().setImageInline({ src, inline: imageInline }).run();
46-
actionDialogImage.setOpen(false);
62+
setOpen(false);
4763
setImageInline(false);
4864
}
4965
function handleLink(e: any) {
5066
e.preventDefault();
5167
e.stopPropagation();
5268

5369
props.editor.chain().focus().setImageInline({ src: link, inline: imageInline }).run();
54-
actionDialogImage.setOpen(false);
70+
setOpen(false);
5571
setImageInline(false);
5672
setLink('');
5773
}
@@ -62,12 +78,13 @@ function ActionImageButton(props: any) {
6278
}
6379

6480
return (
65-
<Dialog onOpenChange={actionDialogImage.setOpen}
66-
open={dialogImage}
81+
<Dialog
82+
onOpenChange={setOpen}
83+
open={open}
6784
>
6885
<DialogTrigger asChild>
6986
<ActionButton
70-
action={() => actionDialogImage.setOpen(true)}
87+
action={() => setOpen(true)}
7188
icon={props.icon}
7289
tooltip={props.tooltip}
7390
/>

src/extensions/Image/components/ImageCropper.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import ReactCrop, {
55
type PixelCrop,
66
} from 'react-image-crop';
77

8+
import { IconComponent } from '@/components';
89
import { Button } from '@/components/ui/button';
910
import {
1011
Dialog,
@@ -13,12 +14,10 @@ import {
1314
DialogTitle,
1415
DialogTrigger,
1516
} from '@/components/ui/dialog';
17+
import { Image as ExtensionImage } from '@/extensions';
1618
import { useLocale } from '@/locales';
1719
import { dataURLtoFile, readImageAsBase64 } from '@/utils/file';
1820

19-
import 'react-image-crop/dist/ReactCrop.css';
20-
import { IconComponent } from '@/components';
21-
2221
export function ImageCropper({ editor, imageInline, onClose }: any) {
2322
const { t } = useLocale();
2423

@@ -75,7 +74,7 @@ export function ImageCropper({ editor, imageInline, onClose }: any) {
7574
const fileCrop = dataURLtoFile(croppedImageUrl, urlUpload?.file?.name || 'image.png');
7675

7776
const uploadOptions = editor.extensionManager.extensions.find(
78-
(extension: any) => extension.name === 'image',
77+
(extension: any) => extension.name === ExtensionImage.name,
7978
)?.options;
8079

8180
let src = '';

src/extensions/SlashCommand/groups.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import type { Extensions } from '@tiptap/core';
22

3-
import type { Group } from './types';
4-
import type { HeadingOptions } from '@/extensions';
5-
import { localeActions } from '@/locales';
3+
import { Image, Video, type HeadingOptions } from '@/extensions';
64
import { actionDialogImage } from '@/extensions/Image/store';
75
import { actionDialogVideo } from '@/extensions/Video/store';
6+
import { localeActions } from '@/locales';
7+
8+
import type { Group } from './types';
89

910
export function renderGroups(extensions: Extensions) {
1011
const groups: Group[] = [
@@ -100,7 +101,7 @@ export function renderGroups(extensions: Extensions) {
100101
}
101102

102103
/* Insert */
103-
if (extension.name.toLowerCase() === 'image') {
104+
if (extension.name.toLowerCase() === Image.name) {
104105
groups[1].commands.push({
105106
name: 'image',
106107
label: localeActions.t('editor.image.tooltip'),
@@ -115,7 +116,7 @@ export function renderGroups(extensions: Extensions) {
115116
});
116117
}
117118

118-
if (extension.name.toLowerCase() === 'video') {
119+
if (extension.name.toLowerCase() === Video.name) {
119120
groups[1].commands.push({
120121
name: 'video',
121122
label: localeActions.t('editor.video.tooltip'),

src/extensions/Video/components/ActiveVideoButton.tsx

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { useMemo, useRef, useState } from 'react';
1+
import { useEffect, useId, useMemo, useRef, useState } from 'react';
22

33
import { ActionButton, Button, Input, Tabs, TabsContent, TabsList, TabsTrigger } from '@/components';
44
import { Dialog, DialogContent, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
5-
import { useLocale } from '@/locales';
6-
import { actionDialogVideo, useDialogVideo } from '@/extensions/Video/store';
75
import { Video } from '@/extensions/Video/Video';
6+
import { useLocale } from '@/locales';
7+
import { listenEvent } from '@/utils/customEvents/customEvents';
8+
import { eventName } from '@/utils/customEvents/events.constant';
89

910
function checkIsVideo(url: string) {
1011
return /\.(?:mp4|webm|ogg|mov)$/i.test(url);
@@ -16,8 +17,23 @@ function ActionVideoButton(props: any) {
1617
const [link, setLink] = useState<string>('');
1718
const fileInput = useRef<HTMLInputElement>(null);
1819

19-
const dialogVideo = useDialogVideo();
2020
const [error, setError] = useState<string>('');
21+
const id = useId();
22+
const [open, setOpen] = useState(false);
23+
24+
const handleUploadVideo = (evt: any) => {
25+
setOpen(evt.detail);
26+
};
27+
28+
useEffect(() => {
29+
eventName.setEventNameUploadVideo(id);
30+
31+
const rm1 = listenEvent(eventName.getEventNameUploadVideo(), handleUploadVideo);
32+
33+
return () => {
34+
rm1();
35+
};
36+
}, []);
2137

2238
const uploadOptions = useMemo(() => {
2339
const uploadOptions = props.editor.extensionManager.extensions.find(
@@ -43,13 +59,13 @@ function ActionVideoButton(props: any) {
4359

4460
props.editor
4561
.chain()
62+
.focus()
4663
.setVideo({
4764
src,
4865
width: '100%',
4966
})
50-
.focus()
5167
.run();
52-
actionDialogVideo.setOpen(false);
68+
setOpen(false);
5369
}
5470
function handleLink(e: any) {
5571
e.preventDefault();
@@ -61,13 +77,13 @@ function ActionVideoButton(props: any) {
6177

6278
props.editor
6379
.chain()
80+
.focus()
6481
.setVideo({
6582
src: link,
6683
width: '100%',
6784
})
68-
.focus()
6985
.run();
70-
actionDialogVideo.setOpen(false);
86+
setOpen(false);
7187
}
7288

7389
function handleClick(e: any) {
@@ -76,11 +92,13 @@ function ActionVideoButton(props: any) {
7692
}
7793

7894
return (
79-
<Dialog open={dialogVideo} onOpenChange={actionDialogVideo.setOpen}>
95+
<Dialog onOpenChange={setOpen}
96+
open={open}
97+
>
8098
<DialogTrigger asChild>
8199
<ActionButton
100+
action={() => setOpen(true)}
82101
icon={props.icon}
83-
action={() => actionDialogVideo.setOpen(true)}
84102
tooltip={props.tooltip}
85103
/>
86104
</DialogTrigger>
@@ -91,17 +109,18 @@ function ActionVideoButton(props: any) {
91109
</DialogTitle>
92110

93111
<Tabs
112+
activationMode="manual"
94113
defaultValue={
95114
(uploadOptions?.resourceVideo === 'both' || uploadOptions?.resourceVideo === 'upload') ? 'upload' : 'link'
96115
}
97-
activationMode="manual"
98116
>
99117
<TabsList className="richtext-grid richtext-w-full richtext-grid-cols-2">
100118
{(uploadOptions?.resourceVideo === 'both' || uploadOptions?.resourceVideo === 'upload') && (
101119
<TabsTrigger value="upload">
102120
{t('editor.video.dialog.tab.upload')}
103121
</TabsTrigger>
104122
)}
123+
105124
{(uploadOptions?.resourceVideo === 'both' || uploadOptions?.resourceVideo === 'link') && (
106125
<TabsTrigger value="link">
107126
{t('editor.video.dialog.link')}
@@ -111,27 +130,34 @@ function ActionVideoButton(props: any) {
111130

112131
<TabsContent value="upload">
113132
<div className="richtext-flex richtext-items-center richtext-gap-[10px]">
114-
<Button className="richtext-w-full richtext-mt-1" size="sm" onClick={handleClick}>
133+
<Button className="richtext-mt-1 richtext-w-full"
134+
onClick={handleClick}
135+
size="sm"
136+
>
115137
{t('editor.video.dialog.tab.upload')}
116138
</Button>
117139
</div>
140+
118141
<input
119-
type="file"
120142
accept="video/*"
121-
ref={fileInput}
122143
multiple
144+
onChange={handleFile}
145+
ref={fileInput}
146+
type="file"
123147
style={{
124148
display: 'none',
125149
}}
126-
onChange={handleFile}
127150
/>
128151
</TabsContent>
152+
129153
<TabsContent value="link">
130154
<form onSubmit={handleLink}>
131155
<div className="richtext-flex richtext-items-center richtext-gap-2">
132156
<Input
133-
type="url"
134157
autoFocus
158+
placeholder={t('editor.video.dialog.placeholder')}
159+
required
160+
type="url"
135161
value={link}
136162
onChange={(e) => {
137163
const url = e.target.value;
@@ -146,16 +172,15 @@ function ActionVideoButton(props: any) {
146172
setError('');
147173
setLink(url);
148174
}}
149-
required
150-
placeholder={t('editor.video.dialog.placeholder')}
151175
/>
152176

153177
<Button type="submit">
154178
{t('editor.video.dialog.button.apply')}
155179
</Button>
156180
</div>
157181
</form>
158-
{error && <div className="richtext-text-red-500 richtext-my-[5px]">
182+
183+
{error && <div className="richtext-my-[5px] richtext-text-red-500">
159184
{error}
160185
</div>}
161186
</TabsContent>

0 commit comments

Comments
 (0)