Skip to content

Commit 2cc0bb3

Browse files
author
vk
committed
fix(active video button): added videoProviders prop + validation
1 parent 8ce0a50 commit 2cc0bb3

File tree

2 files changed

+43
-3
lines changed

2 files changed

+43
-3
lines changed

src/extensions/Video/Video.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,15 @@ export interface VideoOptions extends GeneralOptions<VideoOptions> {
3535
upload?: (file: File) => Promise<string>
3636

3737
/** The source URL of the video */
38-
resourceVideo: 'upload' | 'link' | 'both'
38+
resourceVideo: 'upload' | 'link' | 'both',
39+
40+
/**
41+
* List of allowed video hosting providers
42+
* Use ['.'] to allow any URL, or specify providers like ['youtube', 'vimeo']
43+
*
44+
* @default ['.']
45+
*/
46+
videoProviders?: string[]
3947
}
4048

4149
/**
@@ -119,6 +127,7 @@ export const Video = /* @__PURE__ */ Node.create<VideoOptions>({
119127
disabled: !editor.can().setVideo?.({}),
120128
icon: 'Video',
121129
tooltip: t('editor.video.tooltip'),
130+
videoProviders: ['.'],
122131
editor,
123132
},
124133
};

src/extensions/Video/components/ActiveVideoButton.tsx

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,29 @@ import { useLocale } from '@/locales';
77
import { listenEvent } from '@/utils/customEvents/customEvents';
88
import { EVENTS } from '@/utils/customEvents/events.constant';
99

10-
function checkIsVideo(url: string) {
11-
return /\.(?:mp4|webm|ogg|mov)$/i.test(url);
10+
function checkIsVideoUrl(url: string, allowedProviders?: string[]): boolean {
11+
let urlObj: URL;
12+
try {
13+
urlObj = new URL(url);
14+
} catch {
15+
return false;
16+
}
17+
18+
// If no providers specified or wildcard ['.'] is used, allow any valid URL
19+
if (!allowedProviders?.length || (allowedProviders.length === 1 && allowedProviders[0] === '.')) {
20+
return true;
21+
}
22+
23+
return allowedProviders.some(provider => {
24+
if (provider.includes('*')) {
25+
const pattern = provider
26+
.replace(/\./g, '\\.')
27+
.replace(/\*/g, '.*');
28+
return new RegExp(`^${pattern}$`).test(urlObj.hostname);
29+
}
30+
31+
return urlObj.hostname.includes(provider);
32+
});
1233
}
1334

1435
function ActionVideoButton(props: any) {
@@ -160,6 +181,16 @@ function ActionVideoButton(props: any) {
160181
onChange={(e) => {
161182
setLink(e.target.value);
162183
}}
184+
onBlur={(e) => {
185+
const url = e.target.value;
186+
const videoProviders = uploadOptions.videoProviders || ['.'];
187+
188+
if (url && !checkIsVideoUrl(url, videoProviders)) {
189+
setError('Invalid video URL');
190+
} else {
191+
setError('');
192+
}
193+
}}
163194
/>
164195

165196
<Button type="submit">

0 commit comments

Comments
 (0)