From c8d3bd0ec5afc8fe2fbef271e797b5130547f447 Mon Sep 17 00:00:00 2001 From: Dominik Biedebach Date: Sat, 21 May 2022 00:29:25 +0200 Subject: [PATCH 1/9] feat(extension/youtube): :sparkles: new youtube embed extension --- .../src/Experiments/Youtube/React/index.html | 0 demos/src/Experiments/Youtube/React/index.jsx | 69 ++++++++ .../Experiments/Youtube/React/index.spec.js | 60 +++++++ .../src/Experiments/Youtube/React/styles.scss | 73 ++++++++ demos/src/Experiments/Youtube/Vue/index.html | 0 .../src/Experiments/Youtube/Vue/index.spec.js | 60 +++++++ demos/src/Experiments/Youtube/Vue/index.vue | 157 ++++++++++++++++++ demos/src/Experiments/Youtube/Vue/styles.scss | 73 ++++++++ docs/api/extensions/youtube.md | 112 +++++++++++++ packages/extension-youtube/CHANGELOG.md | 4 + packages/extension-youtube/README.md | 14 ++ packages/extension-youtube/package.json | 31 ++++ packages/extension-youtube/src/index.ts | 5 + packages/extension-youtube/src/utils.ts | 49 ++++++ packages/extension-youtube/src/youtube.ts | 118 +++++++++++++ 15 files changed, 825 insertions(+) create mode 100644 demos/src/Experiments/Youtube/React/index.html create mode 100644 demos/src/Experiments/Youtube/React/index.jsx create mode 100644 demos/src/Experiments/Youtube/React/index.spec.js create mode 100644 demos/src/Experiments/Youtube/React/styles.scss create mode 100644 demos/src/Experiments/Youtube/Vue/index.html create mode 100644 demos/src/Experiments/Youtube/Vue/index.spec.js create mode 100644 demos/src/Experiments/Youtube/Vue/index.vue create mode 100644 demos/src/Experiments/Youtube/Vue/styles.scss create mode 100644 docs/api/extensions/youtube.md create mode 100644 packages/extension-youtube/CHANGELOG.md create mode 100644 packages/extension-youtube/README.md create mode 100644 packages/extension-youtube/package.json create mode 100644 packages/extension-youtube/src/index.ts create mode 100644 packages/extension-youtube/src/utils.ts create mode 100644 packages/extension-youtube/src/youtube.ts diff --git a/demos/src/Experiments/Youtube/React/index.html b/demos/src/Experiments/Youtube/React/index.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/demos/src/Experiments/Youtube/React/index.jsx b/demos/src/Experiments/Youtube/React/index.jsx new file mode 100644 index 0000000000..66d90dd41a --- /dev/null +++ b/demos/src/Experiments/Youtube/React/index.jsx @@ -0,0 +1,69 @@ +import './styles.scss' + +import React from 'react' + +import YouTube from '@tiptap/extension-youtube' +import { EditorContent, useEditor } from '@tiptap/react' +import StarterKit from '@tiptap/starter-kit' + +const MenuBar = ({ editor }) => { + const widthRef = React.useRef(null) + const heightRef = React.useRef(null) + + React.useEffect(() => { + if (widthRef.current && heightRef.current) { + widthRef.current.value = 640 + heightRef.current.value = 480 + } + }, [widthRef.current, heightRef.current]) + + if (!editor) { + return null + } + + const addYouTubeVideo = () => { + const url = prompt('Enter YouTube URL') + + editor.commands.setYoutubeVideo({ + src: url, + width: Math.max(320, parseInt(widthRef.current.value, 10)) || 640, + height: Math.max(180, parseInt(heightRef.current.value, 10)) || 480, + }) + } + + return ( + <> + + + + + ) +} + +export default () => { + const editor = useEditor({ + extensions: [ + StarterKit, + YouTube, + ], + content: ` +

Tiptap now supports youtube embeds! Awesome!

+
+ +
+

Try adding your own video to this editor!

+ `, + editorProps: { + attributes: { + spellcheck: 'false', + }, + }, + }) + + return ( +
+ + +
+ ) +} diff --git a/demos/src/Experiments/Youtube/React/index.spec.js b/demos/src/Experiments/Youtube/React/index.spec.js new file mode 100644 index 0000000000..8decd9f68a --- /dev/null +++ b/demos/src/Experiments/Youtube/React/index.spec.js @@ -0,0 +1,60 @@ +context('/src/Experiments/Youtube/React/', () => { + before(() => { + cy.visit('/src/Experiments/Youtube/React/') + }) + + beforeEach(() => { + cy.get('.ProseMirror').type('{selectall}{backspace}') + }) + + it('adds a video', () => { + cy.window().then(win => { + cy.stub(win, 'prompt', () => 'https://music.youtube.com/watch?v=hBp4dgE7Bho&feature=share') + cy.get('#add').eq(0).click() + cy.get('.ProseMirror div[data-youtube-video] iframe') + .should('have.length', 1) + .should('have.attr', 'src', 'https://www.youtube.com/embed/hBp4dgE7Bho?controls=0') + }) + }) + + it('adds a video with 320 width and 240 height', () => { + cy.window().then(win => { + cy.stub(win, 'prompt', () => 'https://music.youtube.com/watch?v=hBp4dgE7Bho&feature=share') + cy.get('#width').type('{selectall}{backspace}320') + cy.get('#height').type('{selectall}{backspace}240') + cy.get('#add').eq(0).click() + cy.get('.ProseMirror div[data-youtube-video] iframe').should('have.length', 1) + .should('have.attr', 'src', 'https://www.youtube.com/embed/hBp4dgE7Bho?controls=0') + .should('have.css', 'width', '320px') + .should('have.css', 'height', '240px') + }) + }) + + it('replaces a video', () => { + cy.window().then(win => { + let runs = 0 + + cy.stub(win, 'prompt', () => { + runs += 1 + if (runs === 1) { + return 'https://music.youtube.com/watch?v=hBp4dgE7Bho&feature=share' + } + return 'https://music.youtube.com/watch?v=wRakoMYVHm8' + }) + + cy.get('#add').eq(0).click() + cy.get('.ProseMirror div[data-youtube-video] iframe') + .should('have.length', 1) + .should('have.attr', 'src', 'https://www.youtube.com/embed/hBp4dgE7Bho?controls=0') + + cy.get('.ProseMirror div[data-youtube-video] iframe') + .click() + + cy.get('#add').eq(0).click() + + cy.get('.ProseMirror div[data-youtube-video] iframe') + .should('have.length', 1) + .should('have.attr', 'src', 'https://www.youtube.com/embed/wRakoMYVHm8?controls=0') + }) + }) +}) diff --git a/demos/src/Experiments/Youtube/React/styles.scss b/demos/src/Experiments/Youtube/React/styles.scss new file mode 100644 index 0000000000..7235e0b989 --- /dev/null +++ b/demos/src/Experiments/Youtube/React/styles.scss @@ -0,0 +1,73 @@ +/* Basic editor styles */ +.ProseMirror { + > * + * { + margin-top: 0.75em; + } + + ul, + ol { + padding: 0 1rem; + } + + h1, + h2, + h3, + h4, + h5, + h6 { + line-height: 1.1; + } + + code { + background-color: rgba(#616161, 0.1); + color: #616161; + } + + pre { + background: #0D0D0D; + color: #FFF; + font-family: 'JetBrainsMono', monospace; + padding: 0.75rem 1rem; + border-radius: 0.5rem; + + code { + color: inherit; + padding: 0; + background: none; + font-size: 0.8rem; + } + } + + img { + max-width: 100%; + height: auto; + } + + hr { + margin: 1rem 0; + } + + blockquote { + padding-left: 1rem; + border-left: 2px solid rgba(#0D0D0D, 0.1); + } + + iframe { + border: 8px solid #000; + border-radius: 4px; + min-width: 200px; + min-height: 200px; + display: block; + outline: 0px solid transparent; + } + + div[data-youtube-video] { + cursor: move; + padding-right: 24px; + } + + .ProseMirror-selectednode iframe { + transition: outline 0.15s; + outline: 6px solid #ece111; + } +} diff --git a/demos/src/Experiments/Youtube/Vue/index.html b/demos/src/Experiments/Youtube/Vue/index.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/demos/src/Experiments/Youtube/Vue/index.spec.js b/demos/src/Experiments/Youtube/Vue/index.spec.js new file mode 100644 index 0000000000..40cb61d796 --- /dev/null +++ b/demos/src/Experiments/Youtube/Vue/index.spec.js @@ -0,0 +1,60 @@ +context('/src/Experiments/Youtube/Vue/', () => { + before(() => { + cy.visit('/src/Experiments/Youtube/Vue/') + }) + + beforeEach(() => { + cy.get('.ProseMirror').type('{selectall}{backspace}') + }) + + it('adds a video', () => { + cy.window().then(win => { + cy.stub(win, 'prompt', () => 'https://music.youtube.com/watch?v=hBp4dgE7Bho&feature=share') + cy.get('#add').eq(0).click() + cy.get('.ProseMirror div[data-youtube-video] iframe') + .should('have.length', 1) + .should('have.attr', 'src', 'https://www.youtube.com/embed/hBp4dgE7Bho?controls=0') + }) + }) + + it('adds a video with 320 width and 240 height', () => { + cy.window().then(win => { + cy.stub(win, 'prompt', () => 'https://music.youtube.com/watch?v=hBp4dgE7Bho&feature=share') + cy.get('#width').type('{selectall}{backspace}320') + cy.get('#height').type('{selectall}{backspace}240') + cy.get('#add').eq(0).click() + cy.get('.ProseMirror div[data-youtube-video] iframe').should('have.length', 1) + .should('have.attr', 'src', 'https://www.youtube.com/embed/hBp4dgE7Bho?controls=0') + .should('have.css', 'width', '320px') + .should('have.css', 'height', '240px') + }) + }) + + it('replaces a video', () => { + cy.window().then(win => { + let runs = 0 + + cy.stub(win, 'prompt', () => { + runs += 1 + if (runs === 1) { + return 'https://music.youtube.com/watch?v=hBp4dgE7Bho&feature=share' + } + return 'https://music.youtube.com/watch?v=wRakoMYVHm8' + }) + + cy.get('#add').eq(0).click() + cy.get('.ProseMirror div[data-youtube-video] iframe') + .should('have.length', 1) + .should('have.attr', 'src', 'https://www.youtube.com/embed/hBp4dgE7Bho?controls=0') + + cy.get('.ProseMirror div[data-youtube-video] iframe') + .click() + + cy.get('#add').eq(0).click() + + cy.get('.ProseMirror div[data-youtube-video] iframe') + .should('have.length', 1) + .should('have.attr', 'src', 'https://www.youtube.com/embed/wRakoMYVHm8?controls=0') + }) + }) +}) diff --git a/demos/src/Experiments/Youtube/Vue/index.vue b/demos/src/Experiments/Youtube/Vue/index.vue new file mode 100644 index 0000000000..037e5b0cd5 --- /dev/null +++ b/demos/src/Experiments/Youtube/Vue/index.vue @@ -0,0 +1,157 @@ + + + + + diff --git a/demos/src/Experiments/Youtube/Vue/styles.scss b/demos/src/Experiments/Youtube/Vue/styles.scss new file mode 100644 index 0000000000..7235e0b989 --- /dev/null +++ b/demos/src/Experiments/Youtube/Vue/styles.scss @@ -0,0 +1,73 @@ +/* Basic editor styles */ +.ProseMirror { + > * + * { + margin-top: 0.75em; + } + + ul, + ol { + padding: 0 1rem; + } + + h1, + h2, + h3, + h4, + h5, + h6 { + line-height: 1.1; + } + + code { + background-color: rgba(#616161, 0.1); + color: #616161; + } + + pre { + background: #0D0D0D; + color: #FFF; + font-family: 'JetBrainsMono', monospace; + padding: 0.75rem 1rem; + border-radius: 0.5rem; + + code { + color: inherit; + padding: 0; + background: none; + font-size: 0.8rem; + } + } + + img { + max-width: 100%; + height: auto; + } + + hr { + margin: 1rem 0; + } + + blockquote { + padding-left: 1rem; + border-left: 2px solid rgba(#0D0D0D, 0.1); + } + + iframe { + border: 8px solid #000; + border-radius: 4px; + min-width: 200px; + min-height: 200px; + display: block; + outline: 0px solid transparent; + } + + div[data-youtube-video] { + cursor: move; + padding-right: 24px; + } + + .ProseMirror-selectednode iframe { + transition: outline 0.15s; + outline: 6px solid #ece111; + } +} diff --git a/docs/api/extensions/youtube.md b/docs/api/extensions/youtube.md new file mode 100644 index 0000000000..c95c72dedf --- /dev/null +++ b/docs/api/extensions/youtube.md @@ -0,0 +1,112 @@ +--- +description: Your favorite videos and jams - right in your editor! +icon: align-left +--- + +# TextAlign +[![Version](https://img.shields.io/npm/v/@tiptap/extension-youtube.svg?label=version)](https://www.npmjs.com/package/@tiptap/extension-youtube) +[![Downloads](https://img.shields.io/npm/dm/@tiptap/extension-youtube.svg)](https://npmcharts.com/compare/@tiptap/extension-youtube?minimal=true) + +This extension adds a new youtube embed node to the editor. + +## Installation +```bash +npm install @tiptap/extension-youtube +``` + +## Settings + +### inline +Controls if the node should be handled inline or as a block. + +Default: `false` + +```js +YouTube.configure({ + inline: false, +}) +``` + +### width +Controls the default width of added videos + +Default: `640` + +```js +YouTube.configure({ + width: 480, +}) +``` + +### height +Controls the default height of added videos + +Default: `480` + +```js +YouTube.configure({ + height: 320, +}) +``` + +### controls +Enables or disables YouTube video controls + +Default: `true` + +```js +YouTube.configure({ + controls: false, +}) +``` + +### nocookie +Enables the nocookie mode for YouTube embeds + +Default: `false` + +```js +YouTube.configure({ + nocookie: true, +}) +``` + +### allowFullscreen +Allows the iframe to be played in fullscreen + +Default: `true` + +```js +YouTube.configure({ + allowFullscreen: false, +}) +``` + + +## Commands + +### setYoutubeVideo(options) +Inserts a YouTube iframe embed at the current position + +```js +editor.commands.setYoutubeVideo({ + src: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', + width: 640, + height: 480, +}) +``` + +#### Options + +| Option | Description | Optional | +| ---------------- | ----------------------------------------------------------------------- | -------- | +| src | The url of the youtube video. Can be a YouTube or YouTube Music link | | +| width | The embed width (overrides the default option, optional | ✅ | +| height | The embed height (overrides the default option, optional | ✅ | + + +## Source code +[packages/extension-youtube/](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-youtube/) + +## Usage +https://embed.tiptap.dev/preview/Extensions/YouTube diff --git a/packages/extension-youtube/CHANGELOG.md b/packages/extension-youtube/CHANGELOG.md new file mode 100644 index 0000000000..e4d87c4d45 --- /dev/null +++ b/packages/extension-youtube/CHANGELOG.md @@ -0,0 +1,4 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. diff --git a/packages/extension-youtube/README.md b/packages/extension-youtube/README.md new file mode 100644 index 0000000000..8006d467a7 --- /dev/null +++ b/packages/extension-youtube/README.md @@ -0,0 +1,14 @@ +# @tiptap/extension-youtube +[![Version](https://img.shields.io/npm/v/@tiptap/extension-youtube.svg?label=version)](https://www.npmjs.com/package/@tiptap/extension-youtube) +[![Downloads](https://img.shields.io/npm/dm/@tiptap/extension-youtube.svg)](https://npmcharts.com/compare/tiptap?minimal=true) +[![License](https://img.shields.io/npm/l/@tiptap/extension-youtube.svg)](https://www.npmjs.com/package/@tiptap/extension-youtube) +[![Sponsor](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub)](https://github.com/sponsors/ueberdosis) + +## Introduction +tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a toolkit for building rich text WYSIWYG editors, which is already in use at many well-known companies such as *New York Times*, *The Guardian* or *Atlassian*. + +## Official Documentation +Documentation can be found on the [tiptap website](https://tiptap.dev). + +## License +tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap/blob/main/LICENSE.md). diff --git a/packages/extension-youtube/package.json b/packages/extension-youtube/package.json new file mode 100644 index 0000000000..e1a1baa421 --- /dev/null +++ b/packages/extension-youtube/package.json @@ -0,0 +1,31 @@ +{ + "name": "@tiptap/extension-youtube", + "description": "a youtube embed extension for tiptap", + "version": "2.0.0-beta.1", + "homepage": "https://tiptap.dev", + "keywords": [ + "tiptap", + "tiptap extension" + ], + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "main": "dist/tiptap-extension-video.cjs.js", + "umd": "dist/tiptap-extension-video.umd.js", + "module": "dist/tiptap-extension-video.esm.js", + "types": "dist/packages/extension-video/src/index.d.ts", + "files": [ + "src", + "dist" + ], + "peerDependencies": { + "@tiptap/core": "^2.0.0-beta.1" + }, + "repository": { + "type": "git", + "url": "https://github.com/ueberdosis/tiptap", + "directory": "packages/extension-video" + } +} diff --git a/packages/extension-youtube/src/index.ts b/packages/extension-youtube/src/index.ts new file mode 100644 index 0000000000..30a50feffa --- /dev/null +++ b/packages/extension-youtube/src/index.ts @@ -0,0 +1,5 @@ +import { YouTube } from './youtube' + +export * from './youtube' + +export default YouTube diff --git a/packages/extension-youtube/src/utils.ts b/packages/extension-youtube/src/utils.ts new file mode 100644 index 0000000000..05c566f76d --- /dev/null +++ b/packages/extension-youtube/src/utils.ts @@ -0,0 +1,49 @@ +export const isValidYouTubeUrl = (url: string) => { + return url.match(/^(https?:\/\/)?(www\.|music\.)?(youtube\.com|youtu\.be)(.+)?$/) +} + +export interface GetEmbedUrlOptions { + url: string; + controls?: boolean; + nocookie?: boolean; + startAt?: number; +} + +export const getEmbedURLFromYouTubeURL = (options: GetEmbedUrlOptions) => { + const { + url, + controls, + nocookie, + startAt, + } = options + + // if is already an embed url, return it + if (url.includes('/embed/')) { + return url + } + + const videoIdRegex = /v=([-\w]+)/gm + const matches = videoIdRegex.exec(url) + + if (!matches || !matches[1]) { + return null + } + + let outputUrl = nocookie ? `https://www.youtube-nocookie.com/embed/${matches[1]}` : `https://www.youtube.com/embed/${matches[1]}` + + const params = [] + + if (!controls) { + params.push('controls=0') + } + + if (startAt) { + params.push(`start=${startAt}`) + } + + if (params.length) { + outputUrl += `?${params.join('&')}` + } + + return outputUrl +} diff --git a/packages/extension-youtube/src/youtube.ts b/packages/extension-youtube/src/youtube.ts new file mode 100644 index 0000000000..be247c1d27 --- /dev/null +++ b/packages/extension-youtube/src/youtube.ts @@ -0,0 +1,118 @@ +import { Node, mergeAttributes } from '@tiptap/core' + +import { getEmbedURLFromYouTubeURL, isValidYouTubeUrl } from './utils' + +export interface YoutubeOptions { + inline: boolean; + width: number; + height: number; + controls: boolean; + nocookie: boolean; + allowFullscreen: boolean; + HTMLAttributes: Record, +} + +declare module '@tiptap/core' { + interface Commands { + youtube: { + /** + * Insert a youtube video + */ + setYoutubeVideo: (options: { src: string, width?: number, height?: number, start?: number }) => ReturnType, + } + } +} + +export const YouTube = Node.create({ + name: 'youtube', + + addOptions() { + return { + inline: false, + controls: true, + HTMLAttributes: {}, + nocookie: false, + allowFullscreen: false, + width: 640, + height: 480, + } + }, + + inline() { + return this.options.inline + }, + + group() { + return this.options.inline ? 'inline' : 'block' + }, + + draggable: true, + + addAttributes() { + return { + src: { + default: null, + }, + start: { + default: 0, + }, + width: { + default: this.options.width, + }, + height: { + default: this.options.height, + }, + } + }, + + parseHTML() { + return [ + { + tag: 'div[data-youtube-video] iframe', + }, + ] + }, + + addCommands() { + return { + setYoutubeVideo: options => ({ commands }) => { + if (!isValidYouTubeUrl(options.src)) { + return false + } + + return commands.insertContent({ + type: this.name, + attrs: options, + }) + }, + } + }, + + renderHTML({ HTMLAttributes }) { + const embedUrl = getEmbedURLFromYouTubeURL({ + url: HTMLAttributes.src, + controls: this.options.controls, + nocookie: this.options.nocookie, + startAt: HTMLAttributes.start || 0, + }) + + HTMLAttributes.src = embedUrl + + return [ + 'div', + { 'data-youtube-video': '' }, + [ + 'iframe', + mergeAttributes( + this.options.HTMLAttributes, + { + width: this.options.width, + height: this.options.height, + allowfullscreen: this.options.allowFullscreen, + }, + HTMLAttributes, + ), + ], + ] + }, +}) From 5779b083f6c4b996c6193807ed97f2e5b71174a5 Mon Sep 17 00:00:00 2001 From: Dominik Biedebach Date: Sat, 21 May 2022 13:25:15 +0200 Subject: [PATCH 2/9] fix(extension/youtube): remove wrong destroy call on undefined editor --- demos/src/Experiments/Youtube/Vue/index.vue | 1 - demos/src/Experiments/Youtube/Vue/styles.scss | 73 ------------------- 2 files changed, 74 deletions(-) delete mode 100644 demos/src/Experiments/Youtube/Vue/styles.scss diff --git a/demos/src/Experiments/Youtube/Vue/index.vue b/demos/src/Experiments/Youtube/Vue/index.vue index 037e5b0cd5..2c5f13d972 100644 --- a/demos/src/Experiments/Youtube/Vue/index.vue +++ b/demos/src/Experiments/Youtube/Vue/index.vue @@ -74,7 +74,6 @@ export default { beforeUnmount() { this.editor.destroy() - this.anotherEditor.destroy() }, } diff --git a/demos/src/Experiments/Youtube/Vue/styles.scss b/demos/src/Experiments/Youtube/Vue/styles.scss deleted file mode 100644 index 7235e0b989..0000000000 --- a/demos/src/Experiments/Youtube/Vue/styles.scss +++ /dev/null @@ -1,73 +0,0 @@ -/* Basic editor styles */ -.ProseMirror { - > * + * { - margin-top: 0.75em; - } - - ul, - ol { - padding: 0 1rem; - } - - h1, - h2, - h3, - h4, - h5, - h6 { - line-height: 1.1; - } - - code { - background-color: rgba(#616161, 0.1); - color: #616161; - } - - pre { - background: #0D0D0D; - color: #FFF; - font-family: 'JetBrainsMono', monospace; - padding: 0.75rem 1rem; - border-radius: 0.5rem; - - code { - color: inherit; - padding: 0; - background: none; - font-size: 0.8rem; - } - } - - img { - max-width: 100%; - height: auto; - } - - hr { - margin: 1rem 0; - } - - blockquote { - padding-left: 1rem; - border-left: 2px solid rgba(#0D0D0D, 0.1); - } - - iframe { - border: 8px solid #000; - border-radius: 4px; - min-width: 200px; - min-height: 200px; - display: block; - outline: 0px solid transparent; - } - - div[data-youtube-video] { - cursor: move; - padding-right: 24px; - } - - .ProseMirror-selectednode iframe { - transition: outline 0.15s; - outline: 6px solid #ece111; - } -} From ca835b113adcefaf8ec6cd8d35a94679a06eefa0 Mon Sep 17 00:00:00 2001 From: Dominik Biedebach Date: Sat, 21 May 2022 13:35:04 +0200 Subject: [PATCH 3/9] fix(extension/youtube): :bug: fix youtu.be share urls not being recognized correctly --- packages/extension-youtube/src/utils.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/extension-youtube/src/utils.ts b/packages/extension-youtube/src/utils.ts index 05c566f76d..f506bb35a4 100644 --- a/packages/extension-youtube/src/utils.ts +++ b/packages/extension-youtube/src/utils.ts @@ -9,6 +9,10 @@ export interface GetEmbedUrlOptions { startAt?: number; } +export const getYoutubeEmbedUrl = (nocookie?: boolean) => { + return nocookie ? 'https://www.youtube-nocookie.com/embed/' : 'https://www.youtube.com/embed/' +} + export const getEmbedURLFromYouTubeURL = (options: GetEmbedUrlOptions) => { const { url, @@ -22,6 +26,16 @@ export const getEmbedURLFromYouTubeURL = (options: GetEmbedUrlOptions) => { return url } + // if is a youtu.be url, get the id after the / + if (url.includes('youtu.be')) { + const id = url.split('/').pop() + + if (!id) { + return null + } + return `${getYoutubeEmbedUrl(nocookie)}${id}` + } + const videoIdRegex = /v=([-\w]+)/gm const matches = videoIdRegex.exec(url) @@ -29,7 +43,9 @@ export const getEmbedURLFromYouTubeURL = (options: GetEmbedUrlOptions) => { return null } - let outputUrl = nocookie ? `https://www.youtube-nocookie.com/embed/${matches[1]}` : `https://www.youtube.com/embed/${matches[1]}` + + let outputUrl = `${getYoutubeEmbedUrl(nocookie)}${matches[1]}` + console.log(outputUrl) const params = [] From 2e9cf3022c8ab75ec7b955b5566d3f626667156d Mon Sep 17 00:00:00 2001 From: Dominik Biedebach Date: Sat, 21 May 2022 13:37:21 +0200 Subject: [PATCH 4/9] style: remove stray console.log --- packages/extension-youtube/src/utils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/extension-youtube/src/utils.ts b/packages/extension-youtube/src/utils.ts index f506bb35a4..3707328845 100644 --- a/packages/extension-youtube/src/utils.ts +++ b/packages/extension-youtube/src/utils.ts @@ -45,7 +45,6 @@ export const getEmbedURLFromYouTubeURL = (options: GetEmbedUrlOptions) => { let outputUrl = `${getYoutubeEmbedUrl(nocookie)}${matches[1]}` - console.log(outputUrl) const params = [] From 1f9f975c57001526a17a1ef2fc3c052731369040 Mon Sep 17 00:00:00 2001 From: Dominik Biedebach Date: Sat, 21 May 2022 13:38:59 +0200 Subject: [PATCH 5/9] style: remove empty line --- packages/extension-youtube/src/utils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/extension-youtube/src/utils.ts b/packages/extension-youtube/src/utils.ts index 3707328845..bdd40661b9 100644 --- a/packages/extension-youtube/src/utils.ts +++ b/packages/extension-youtube/src/utils.ts @@ -43,7 +43,6 @@ export const getEmbedURLFromYouTubeURL = (options: GetEmbedUrlOptions) => { return null } - let outputUrl = `${getYoutubeEmbedUrl(nocookie)}${matches[1]}` const params = [] From 06d5c489075f98ccd99f350c25009eb66da11f19 Mon Sep 17 00:00:00 2001 From: Dominik Biedebach Date: Mon, 30 May 2022 20:25:57 +0200 Subject: [PATCH 6/9] docs(docs): update youtube docs --- docs/api/extensions/youtube.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/extensions/youtube.md b/docs/api/extensions/youtube.md index c95c72dedf..6ed9a8998b 100644 --- a/docs/api/extensions/youtube.md +++ b/docs/api/extensions/youtube.md @@ -3,7 +3,7 @@ description: Your favorite videos and jams - right in your editor! icon: align-left --- -# TextAlign +# YouTube [![Version](https://img.shields.io/npm/v/@tiptap/extension-youtube.svg?label=version)](https://www.npmjs.com/package/@tiptap/extension-youtube) [![Downloads](https://img.shields.io/npm/dm/@tiptap/extension-youtube.svg)](https://npmcharts.com/compare/@tiptap/extension-youtube?minimal=true) From 2daeaa721232b042ba08d265880de17c607ca6b2 Mon Sep 17 00:00:00 2001 From: Markus Krause Date: Fri, 10 Jun 2022 16:29:14 +0200 Subject: [PATCH 7/9] Capitalize tiptap --- packages/extension-youtube/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/extension-youtube/README.md b/packages/extension-youtube/README.md index 8006d467a7..654705c76c 100644 --- a/packages/extension-youtube/README.md +++ b/packages/extension-youtube/README.md @@ -5,7 +5,7 @@ [![Sponsor](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub)](https://github.com/sponsors/ueberdosis) ## Introduction -tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a toolkit for building rich text WYSIWYG editors, which is already in use at many well-known companies such as *New York Times*, *The Guardian* or *Atlassian*. +Tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a toolkit for building rich text WYSIWYG editors, which is already in use at many well-known companies such as *New York Times*, *The Guardian* or *Atlassian*. ## Official Documentation Documentation can be found on the [tiptap website](https://tiptap.dev). From 7cb9bd2fc52b840bc77a57960858dfc7fbc30257 Mon Sep 17 00:00:00 2001 From: Markus Krause Date: Fri, 10 Jun 2022 16:48:09 +0200 Subject: [PATCH 8/9] Capitalize Tiptap --- packages/extension-youtube/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/extension-youtube/README.md b/packages/extension-youtube/README.md index 654705c76c..2d00b68288 100644 --- a/packages/extension-youtube/README.md +++ b/packages/extension-youtube/README.md @@ -11,4 +11,4 @@ Tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a Documentation can be found on the [tiptap website](https://tiptap.dev). ## License -tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap/blob/main/LICENSE.md). +Tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap/blob/main/LICENSE.md). From 76c3d65c800c357f63a9251f02191665c7099ffa Mon Sep 17 00:00:00 2001 From: Dominik Biedebach Date: Fri, 17 Jun 2022 05:23:17 +0200 Subject: [PATCH 9/9] style(extension/youtube): :pencil2: change youtube typing --- demos/src/Experiments/Youtube/React/index.jsx | 11 +++++------ demos/src/Experiments/Youtube/Vue/index.vue | 6 +++--- docs/api/extensions/youtube.md | 12 ++++++------ packages/extension-youtube/src/index.ts | 4 ++-- packages/extension-youtube/src/utils.ts | 4 ++-- packages/extension-youtube/src/youtube.ts | 10 +++++----- 6 files changed, 23 insertions(+), 24 deletions(-) diff --git a/demos/src/Experiments/Youtube/React/index.jsx b/demos/src/Experiments/Youtube/React/index.jsx index 66d90dd41a..948e6b8b28 100644 --- a/demos/src/Experiments/Youtube/React/index.jsx +++ b/demos/src/Experiments/Youtube/React/index.jsx @@ -1,10 +1,9 @@ import './styles.scss' -import React from 'react' - -import YouTube from '@tiptap/extension-youtube' +import Youtube from '@tiptap/extension-youtube' import { EditorContent, useEditor } from '@tiptap/react' import StarterKit from '@tiptap/starter-kit' +import React from 'react' const MenuBar = ({ editor }) => { const widthRef = React.useRef(null) @@ -21,7 +20,7 @@ const MenuBar = ({ editor }) => { return null } - const addYouTubeVideo = () => { + const addYoutubeVideo = () => { const url = prompt('Enter YouTube URL') editor.commands.setYoutubeVideo({ @@ -33,7 +32,7 @@ const MenuBar = ({ editor }) => { return ( <> - + @@ -44,7 +43,7 @@ export default () => { const editor = useEditor({ extensions: [ StarterKit, - YouTube, + Youtube, ], content: `

Tiptap now supports youtube embeds! Awesome!

diff --git a/demos/src/Experiments/Youtube/Vue/index.vue b/demos/src/Experiments/Youtube/Vue/index.vue index 2c5f13d972..203833fdd6 100644 --- a/demos/src/Experiments/Youtube/Vue/index.vue +++ b/demos/src/Experiments/Youtube/Vue/index.vue @@ -22,9 +22,9 @@