github.com/ngocphuongnb/tetua@v0.0.7-alpha/packages/editor/src/extensions/iframe.ts (about)

     1  import { Node } from '@tiptap/core'
     2  import { createNodeViewBlock } from '../utils'
     3  
     4  export interface IframeOptions {
     5    allowFullscreen: boolean,
     6    HTMLAttributes: {
     7      [key: string]: any
     8    },
     9  }
    10  
    11  declare module '@tiptap/core' {
    12    interface Commands<ReturnType> {
    13      iframe: {
    14        setIframe: (options: { src: string, width?: number, height?: number }) => ReturnType,
    15      }
    16    }
    17  }
    18  
    19  export const Iframe = Node.create<IframeOptions>({
    20    name: 'iframe',
    21    group: 'block',
    22    atom: true,
    23    addOptions() {
    24      return {
    25        allowFullscreen: true,
    26        HTMLAttributes: {
    27          class: 'block-iframe',
    28        },
    29      }
    30    },
    31    addAttributes() {
    32      return {
    33        src: {
    34          default: null,
    35        },
    36        frameborder: {
    37          default: 0,
    38        },
    39        allowfullscreen: {
    40          default: this.options.allowFullscreen,
    41          parseHTML: () => this.options.allowFullscreen,
    42        },
    43        width: {
    44          default: 500,
    45        },
    46        height: {
    47          default: 315,
    48        },
    49        title: {
    50          default: null,
    51        },
    52        allow: {
    53          default: 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture',
    54        }
    55      }
    56    },
    57    parseHTML() {
    58      return [{
    59        tag: 'iframe',
    60      }]
    61    },
    62    renderHTML({ HTMLAttributes }) {
    63      return ['div', this.options.HTMLAttributes, ['iframe', HTMLAttributes]]
    64    },
    65    addCommands() {
    66      return {
    67        setIframe: (options: { src: string, width?: number, height?: number }) => ({ tr, dispatch }) => {
    68          const { selection } = tr
    69          const node = this.type.create(options)
    70  
    71          if (dispatch) {
    72            tr.replaceRangeWith(selection.from, selection.to, node)
    73          }
    74  
    75          return true
    76        },
    77      }
    78    },
    79  
    80    addNodeView() {
    81      return ({ editor, node: _node, getPos, HTMLAttributes: attrs, decorations: _decorations, extension: _extension }) => {
    82        const createImageUrlElm = () => {
    83          const iframeSrcContainer = document.createElement('div');
    84          const iframeSrcInput = document.createElement('input');
    85          const iframeSrcApplyBtn = document.createElement('button');
    86  
    87          iframeSrcContainer.className = 'mely-editor-iframe-src';
    88          iframeSrcApplyBtn.innerText = 'Insert';
    89          iframeSrcInput.setAttribute('type', 'text');
    90          iframeSrcInput.setAttribute('placeholder', 'Enter embed URL');
    91          iframeSrcApplyBtn.addEventListener('click', () => {
    92            if (typeof getPos === 'function') {
    93              let src = iframeSrcInput.value;
    94              if (src.startsWith('https://www.youtube.com/watch?v=')) {
    95                src = src.replace('https://www.youtube.com/watch?v=', 'https://www.youtube.com/embed/');
    96                src = src.split('&')[0];
    97              }
    98              editor.view.dispatch(editor.view.state.tr.setNodeMarkup(getPos(), undefined, {
    99                src: src,
   100              }))
   101              editor.commands.focus();
   102            }
   103          });
   104  
   105          iframeSrcContainer.append(iframeSrcInput, iframeSrcApplyBtn);
   106          setTimeout(() => {
   107            iframeSrcInput.focus();
   108          }, 0);
   109          return iframeSrcContainer;
   110        }
   111        const contentDomElm = document.createElement('iframe');
   112        contentDomElm.setAttribute('src', attrs.src);
   113        contentDomElm.setAttribute('width', attrs.width);
   114        contentDomElm.setAttribute('height', attrs.height);
   115        contentDomElm.setAttribute('title', attrs.title);
   116        contentDomElm.setAttribute('allow', attrs.allow);
   117        // contentDomElm.setAttribute('alt', attrs.alt);
   118        // contentDomElm.setAttribute('title', attrs.title);
   119  
   120        const viewDomElms: HTMLElement[] = [];
   121        const imageUrlElm = createImageUrlElm();
   122  
   123        viewDomElms.push(imageUrlElm);
   124        const { dom } = createNodeViewBlock(contentDomElm, viewDomElms);
   125        dom.classList.add('block-iframe');
   126  
   127        return {
   128          dom,
   129          contentDOM: contentDomElm,
   130          stopEvent: () => !attrs.src,
   131          ignoreMutation: _mutation => !attrs.src,
   132        }
   133      }
   134    },
   135  })