tiptap-image-resize icon indicating copy to clipboard operation
tiptap-image-resize copied to clipboard

Doesn't work out of the box with NextJS

Open mattiastofte opened this issue 2 years ago • 12 comments

Throws this error when importing and using with NextJS: image

I'll investigate this further, but I've not touched the module itself so I'm pretty sure this is a bug.

mattiastofte avatar Aug 21 '22 15:08 mattiastofte

Sorry i didn't notice your issue! But it looks like your setup doesn't support loading typescript? (as far as is know it's included in the base NextJS setup since a few years?)

Will setup a NextJS project and try a few things. Thanks for mentioning it!

breakerh avatar Aug 29 '22 21:08 breakerh

I use NextJS with typescript configurarion so I know that it can handle typescript files:) What i did as a temporary fix was to make a similar extension under the project src and that worked fine, so I'm not sure what the issue is. 🤔

mattiastofte avatar Aug 29 '22 21:08 mattiastofte

I use NextJS with typescript configurarion so I know that it can handle typescript files:) What i did as a temporary fix was to make a similar extension under the project src and that worked fine, so I'm not sure what the issue is. 🤔

hi, im having the same issue here. could you perhaps send your solution? thanks

Neuterion avatar Sep 24 '22 15:09 Neuterion

A temporary solution was to copy the code in the node_module extension and separate it into a custom extension located under your own /src path. So under my src/components/editor/extensions folder (it could be something else for you) I have this: image

This is the extension that I import in my editor with import ImageExtension from './extensions/resize-image/index';

index.ts:

import { ImageResize } from './image'

export * from './image'

export default ImageResize

image.ts:

import React, {Component, FC, ReactElement} from "react";
import {mergeAttributes, nodeInputRule, Node} from "@tiptap/core";
import {ReactNodeViewRenderer} from "@tiptap/react";
import ImageResizeComponent from "./component/ImageResizeComponent";
  
  export interface ImageOptions {
    inline: boolean,
    allowBase64: boolean,
    HTMLAttributes: Record<string, any>,
  }
  
  declare module '@tiptap/core' {
    interface Commands<ReturnType> {
      image: {
        /**
         * Add an image
         */
        setImage: (options: { src: string, alt?: string, title?: string }) => ReturnType,
      }
    }
  }
  
  export const inputRegex = /(?:^|\s)(!\[(.+|:?)]\((\S+)(?:(?:\s+)["'](\S+)["'])?\))$/
  
  export const ImageResize = Node.create<ImageOptions>({
    name: 'resizableImage',
  
    addOptions() {
      return {
        inline: false,
        allowBase64: false,
        HTMLAttributes: {},
        resizeIcon: React.createElement("p", null, "⇲")
      }
    },
  
    inline() {
      return this.options.inline
    },
  
    group() {
      return this.options.inline ? 'inline' : 'block'
    },
  
    draggable: false,
  
    addAttributes() {
      return {
        src: {
          default: null,
        },
        alt: {
          default: null,
        },
        title: {
          default: null,
        },
            width: {
              default: '100%',
              renderHTML: (attributes) => {
                return {
                  width: attributes.width
                };
              }
            },
            height: {
              default: 'auto',
              renderHTML: (attributes) => {
                return {
                  height: attributes.height
                };
              }
            }
      }
    },
  
    parseHTML() {
        return [
          {
            tag: 'image-resizer',
          },
        ]
      },
    
      renderHTML({ HTMLAttributes }) {
        return ['image-resizer', mergeAttributes(this.options.HTMLAttributes,HTMLAttributes)]
      },
    
      addNodeView() {
        return ReactNodeViewRenderer(ImageResizeComponent)
      },
  
    addCommands() {
      return {
        setImage: options => ({ commands }) => {
          return commands.insertContent({
            type: this.name,
            attrs: options,
          })
        },
      }
    },
  
    addInputRules() {
      return [
        nodeInputRule({
          find: inputRegex,
          type: this.type,
          getAttributes: match => {
            const [,, alt, src, title] = match
  
            return { src, alt, title }
          },
        }),
      ]
    },
  })

ImageResizeComponent.tsx:

import React from 'react';
import { NodeViewWrapper } from '@tiptap/react';
import Image from 'next/image';

const ImageResizeComponent = (props: any) => {
  const handler = (mouseDownEvent: React.MouseEvent<HTMLImageElement>) => {
    const parent = (mouseDownEvent.target as HTMLElement).closest(
      '.image-resizer'
    );
    const image = parent?.querySelector('img.postimage') ?? null;
    if (image === null) return;
    const startSize = { x: image.clientWidth, y: image.clientHeight };
    const startPosition = { x: mouseDownEvent.pageX, y: mouseDownEvent.pageY };

    function onMouseMove(mouseMoveEvent: MouseEvent) {
      props.updateAttributes({
        width: startSize.x - startPosition.x + mouseMoveEvent.pageX,
        height: startSize.y - startPosition.y + mouseMoveEvent.pageY,
      });
    }
    function onMouseUp() {
      document.body.removeEventListener('mousemove', onMouseMove);
    }

    document.body.addEventListener('mousemove', onMouseMove);
    document.body.addEventListener('mouseup', onMouseUp, { once: true });
  };
  return (
    <NodeViewWrapper className="image-resizer">
      <img {...props.node.attrs} className="postimage" />
      <p></p>
      <div className="resize-trigger" onMouseDown={handler}>
        {props.extension.options.resizeIcon}
      </div>
    </NodeViewWrapper>
  );
};

export default ImageResizeComponent;

mattiastofte avatar Sep 24 '22 17:09 mattiastofte

Hey @mattiastofte Thanks for your issue! Will update the code asap to work out of the box!

breakerh avatar Sep 25 '22 13:09 breakerh

@mattiastofte thanks!

Neuterion avatar Sep 27 '22 21:09 Neuterion

Same issue, would appreciate the proper fix @breakerh !

I'll use @mattiastofte 's solution for now, thanks for posting the code :smile:

ACronje avatar Oct 26 '22 08:10 ACronje

Throws this error when importing and using with NextJS: image

I'll investigate this further, but I've not touched the module itself so I'm pretty sure this is a bug.

Same issue, Roger that

supset avatar Oct 28 '22 23:10 supset

  1. also , had to —force npm install ( perhaps updated for the latest React and tiptap versions )

supset avatar Oct 28 '22 23:10 supset

Throws this error when importing and using with NextJS: image

I'll investigate this further, but I've not touched the module itself so I'm pretty sure this is a bug.

also , had to —force npm install ( perhaps updating the extension for the latest React and tiptap versions + .js / .ts fluent would be cool ) thanks brothers

supset avatar Oct 28 '22 23:10 supset

Sorry for not responding in time!

I will update the the code to react 18.x and will update the code to behave as expected.. Again sorry for the ultra delayed response..

breakerh avatar Oct 28 '22 23:10 breakerh

worked on something similar: I added a rollup config for typescript support.

https://github.com/Habib-Shahzad/tiptap-resizable-image

Habib-Shahzad avatar Aug 22 '23 17:08 Habib-Shahzad