Giter VIP home page Giter VIP logo

mui-tiptap's Introduction

mui-tiptap logo

mui-tiptap: A customizable Material UI styled WYSIWYG rich text editor, using Tiptap.

npm mui-tiptap package npm type definitions GitHub Workflow Status project license

  • ✨ Styled based on your own MUI theme (colors, fonts, light vs dark mode, etc.)
  • 🛠️ Built on powerful Tiptap and ProseMirror foundations (extensible, real-time collaborative editing, cross-platform support, etc.)

Features:

  • 🧰 An all-in-one RichTextEditor component to get started immediately (no other components or hooks needed!), or individual modular components to customize to your needs
  • 😎 Built-in styles for Tiptap’s extensions (text formatting, lists, tables, Google Docs-like collaboration cursors; you name it!)
  • ▶️ Composable and extendable menu buttons and controls for the standard Tiptap extensions
  • 🖼️ ResizableImage extension for adding and resizing images directly in the editor
  • HeadingWithAnchor extension for dynamic GitHub-like anchor links for every heading you add
  • 🔗 LinkBubbleMenu so adding and editing links is a breeze
  • 🔢 FontSize extension for controlling text sizes
  • 🔳 TableImproved extension that fixes problems in the underlying Tiptap Table extension
  • 📝 TableBubbleMenu for interactively editing your rich text tables
  • 💬 General-purpose ControlledBubbleMenu for building your own custom menus, solving some shortcomings of the Tiptap BubbleMenu
  • And more!
README Table of Contents

Demo

Try it yourself in this CodeSandbox live demo!

mui-tiptap demo

Installation

npm install mui-tiptap

or

yarn add mui-tiptap

There are peer dependencies on @mui/material and @mui/icons-material (and their @emotion/ peers), and @tiptap/ packages. These should be installed automatically by default if you’re using npm 7+ or pnpm. Otherwise, if your project doesn’t already use those, you can install them with:

npm install @mui/material @mui/icons-material @emotion/react @emotion/styled @tiptap/react @tiptap/extension-heading @tiptap/extension-image @tiptap/extension-table @tiptap/pm @tiptap/core

or

yarn add @mui/material @mui/icons-material @emotion/react @emotion/styled @tiptap/react @tiptap/extension-heading @tiptap/extension-image @tiptap/extension-table @tiptap/pm @tiptap/core

Get started

Use the all-in-one component

The simplest way to render a rich text editor is to use the RichTextEditor component:

import { Button } from "@mui/material";
import StarterKit from "@tiptap/starter-kit";
import {
  MenuButtonBold,
  MenuButtonItalic,
  MenuControlsContainer,
  MenuDivider,
  MenuSelectHeading,
  RichTextEditor,
  type RichTextEditorRef,
} from "mui-tiptap";
import { useRef } from "react";

function App() {
  const rteRef = useRef<RichTextEditorRef>(null);

  return (
    <div>
      <RichTextEditor
        ref={rteRef}
        extensions={[StarterKit]} // Or any Tiptap extensions you wish!
        content="<p>Hello world</p>" // Initial content for the editor
        // Optionally include `renderControls` for a menu-bar atop the editor:
        renderControls={() => (
          <MenuControlsContainer>
            <MenuSelectHeading />
            <MenuDivider />
            <MenuButtonBold />
            <MenuButtonItalic />
            {/* Add more controls of your choosing here */}
          </MenuControlsContainer>
        )}
      />

      <Button onClick={() => console.log(rteRef.current?.editor?.getHTML())}>
        Log HTML
      </Button>
    </div>
  );
}

Check out mui-tiptap extensions and components below to learn about extra Tiptap extensions and components (like more to include in renderControls) that you can use. See src/demo/Editor.tsx for a more thorough example of using RichTextEditor.

Create and provide the editor yourself

If you need more customization, you can instead define your editor using Tiptap’s useEditor hook, and lay out your UI using a selection of mui-tiptap components (and/or your own components).

Pass the editor to mui-tiptap’s RichTextEditorProvider component at the top of your component tree. From there, render whatever children within the provider that fit your needs.

The easiest is option is the RichTextField component, which is what RichTextEditor uses under the hood:

import { useEditor } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import {
  MenuButtonBold,
  MenuButtonItalic,
  MenuControlsContainer,
  MenuDivider,
  MenuSelectHeading,
  RichTextEditorProvider,
  RichTextField,
} from "mui-tiptap";

function App() {
  const editor = useEditor({
    extensions: [StarterKit],
    content: "<p>Hello <b>world</b>!</p>",
  });
  return (
    <RichTextEditorProvider editor={editor}>
      <RichTextField
        controls={
          <MenuControlsContainer>
            <MenuSelectHeading />
            <MenuDivider />
            <MenuButtonBold />
            <MenuButtonItalic />
            {/* Add more controls of your choosing here */}
          </MenuControlsContainer>
        }
      />
    </RichTextEditorProvider>
  );
}

Or if you want full control over the UI, instead of RichTextField, you can build the editor area yourself and then just use the <RichTextContent /> component where you want the (styled) editable rich text content to appear. RichTextContent is the MUI-themed version of Tiptap's EditorContent component.

Render read-only rich text content

Use the RichTextReadOnly component and just pass in your HTML or ProseMirror JSON and your configured Tiptap extensions, like:

<RichTextReadOnly content="<p>Hello world</p>" extensions={[StarterKit]} />

Alternatively, you can set the RichTextEditor editable prop (or useEditor editable option) to false for a more configurable read-only option. Use RichTextReadOnly when:

  • You just want to efficiently render editor HTML/JSON content directly, without any outlined field styling, controls setup, extra listener logic, access to the editor object, etc. (This component also skips creating the Tiptap editor if content is empty, which can help performance.)
  • You want a convenient way to render content that updates as the content prop changes. (RichTextEditor by contrast does not re-render automatically on content changes, as described below.)

mui-tiptap extensions and components

Tiptap extensions

HeadingWithAnchor

A modified version of Tiptap’s Heading extension, with dynamic GitHub-like anchor links for every heading you add. An anchor link button will appear to the left of a heading when hovering over it, when the editor has editable set to false. This allows users to share links and jump to specific headings within your rendered editor content.

FontSize

Sets text font size. This extension requires the @tiptap/extension-text-style package to be installed and its TextStyle mark to be included in your extensions.

Can be controlled with the MenuSelectFontSize component.

Commands
  • setFontSize(): Set the text font size (using a valid CSS font-size property). ex: "12px", "2em", "small"
  • unsetFontSize(): Remove any previously set font size, reverting to the default size for the given mark.

LinkBubbleMenuHandler

To be used in conjunction with the LinkBubbleMenu component, as this extension provides editor commands to control the state of the link bubble menu.

Commands
  • openLinkBubbleMenu(): Open/show the link bubble menu. Create a link if one doesn't exist at the current cursor selection, or edit the existing link if there is already a link at the current selection.
  • editLinkInBubbleMenu(): Edit an existing link in the bubble menu, to be used when currently viewing a link in the already-opened bubble menu.
  • closeLinkBubbleMenu(): Close/hide the link bubble menu, canceling any ongoing edits.

ResizableImage

A modified version of Tiptap’s Image extension, which adds the ability to resize images directly in the editor. A drag handle appears in the bottom right when clicking on an image, so users can interactively change the size.

TableImproved

A modified version of Tiptap’s Table extension that fixes problems related to column-resizing and editable state.

Namely, this version of the extension, coupled with the mui-tiptap CSS styles ensures that:

  1. Columns respect their resized widths even when the editor has editable=false
  2. Column resizing is possible regardless of initial editor state, when toggling from editable=false to editable=true

(Resolves these reported Tiptap issues: 1, 2, 3.)

Components

Component Description
RichTextEditor An all-in-one component to directly render a MUI-styled Tiptap rich text editor field. Utilizes many of the below components internally. See the "Get started" notes on usage above. In brief: <RichTextEditor ref={rteRef} content="<p>Hello world</p>" extensions={[...]} />
RichTextReadOnly An all-in-one component to directly render read-only Tiptap editor content. While RichTextEditor (or useEditor, RichTextEditorProvider, and RichTextContent) can be used as read-only via the editor's editable prop, this is a simpler and more efficient version that only renders content and nothing more (e.g., does not instantiate a toolbar, bubble menu, etc. that you probably wouldn’t want in a read-only context, and it skips instantiating the editor at all if there's no content to display).
RichTextEditorProvider Uses React context to make the Tiptap editor available to any nested components so that the editor does not need to be manually passed in at every level. Required as a parent for most mui-tiptap components besides the all-in-one RichTextEditor and RichTextReadOnly. Utilize the provided editor in your own components via the useRichTextEditorContext() hook.
RichTextField Renders the Tiptap rich text editor content and a controls menu bar. With the "outlined" variant, renders a bordered UI similar to the Material UI TextField. The "standard" variant does not have an outline/border.
MenuBar A collapsible, optionally-sticky container for showing editor controls atop the editor content. (This component is used to contain RichTextEditor’s renderControls and RichTextField’s controls, but can be used directly if you’re doing something more custom.)
RichTextContent Renders a Material UI styled version of Tiptap rich text editor content. Applies all CSS rules for formatting, as a styled alternative to Tiptap’s <EditorContent /> component. (Used automatically within RichTextEditor and RichTextField.)
LinkBubbleMenu Renders a bubble menu when viewing, creating, or editing a link. Requires the Tiptap Link extension (@tiptap/extension-link) and the mui-tiptap LinkBubbleMenuHandler extension. Pairs well with the <MenuButtonEditLink /> component.

If you're using RichTextEditor, include this component via RichTextEditor’s children render-prop. Otherwise, include the LinkBubbleMenu as a child of the component where you call useEditor and render your RichTextField or RichTextContent. (The bubble menu itself will be positioned appropriately as long as it is re-rendered whenever the Tiptap editor forces an update, which will happen if it's a child of the component using useEditor). See src/demo/Editor.tsx for an example of this.
TableBubbleMenu Renders a bubble menu to manipulate the contents of a Table (add or delete columns or rows, merge cells, etc.), when the user's caret/selection is inside a Table. For use with mui-tiptap’s TableImproved extension or Tiptap’s @tiptap/extension-table extension.

If you're using RichTextEditor, include this component via RichTextEditor’s children render-prop. Otherwise, include the TableBubbleMenu as a child of the component where you call useEditor and render your RichTextField or RichTextContent. (The bubble menu itself will be positioned appropriately as long as it is re-rendered whenever the Tiptap editor forces an update, which will happen if it's a child of the component using useEditor). See src/demo/Editor.tsx for an example of this.
ControlledBubbleMenu General-purpose component for building your own custom bubble menus, solving some shortcomings of Tiptap’s BubbleMenu. This is what both LinkBubbleMenu and TableBubbleMenu use under the hood.
ColorPicker A color-picker that includes hue/saturation/alpha gradient selectors (via react-colorful), plus a text input to enter a color directly, and optional swatchColors for color presets. Used by MenuButtonColorPicker/MenuButtonTextColor/MenuButtonHighlightColor under the hood. Important props:
swatchColors: Array of colors to show as preset buttons.
colorToHex: Override the default implementation for converting a given CSS color string to a string in hex format (e.g. "#ff0000"). Should return null if the given color cannot be parsed as valid. See ColorPickerProps definition for more details, such as examples using more full-featured libraries like colord or tinycolor2.
disableAlpha: If true, disables the alpha/transparency slider.
value/onChange: controlled color value string (empty string if unset) and its change callback.
ColorSwatchButton Renders a button that shows and allows selecting a color preset. Utilized by ColorPicker for its swatchColors.

Controls components

These controls components help you quickly put together your menu bar, for each of the various Tiptap extensions you may want to use.

You can override all props for these components (e.g. to change the IconComponent, tooltipLabel, tooltipShortcutKeys for which shortcut is shown, onClick behavior, etc.). Or easily create controls for your own extensions and use-cases with the base MenuButton and MenuSelect components.

Extension mui-tiptap component(s)
@tiptap/extension-blockquote MenuButtonBlockquote
@tiptap/extension-bold MenuButtonBold
@tiptap/extension-bullet-list MenuButtonBulletedList
@tiptap/extension-color MenuButtonTextColor (Takes optional defaultTextColor prop. See MenuButtonColorPicker below for other customization details.)
@tiptap/extension-code MenuButtonCode
@tiptap/extension-code-block MenuButtonCodeBlock
@tiptap/extension-font-family MenuSelectFontFamily (use the options prop to specify which font families can be selected, like [{ label: "Monospace", value: "monospace" }, ...] )
mui-tiptap’s FontSize MenuSelectFontSize (use the options prop to override the default size options)
mui-tiptap’s HeadingWithAnchor
or @tiptap/extension-heading
MenuSelectHeading
@tiptap/extension-highlight MenuButtonHighlightColor: For Highlight’s multicolor: true mode. Takes optional defaultMarkColor. See MenuButtonColorPicker below for other customization details.
MenuButtonHighlightToggle: For Highlight’s default multicolor: false mode
@tiptap/extension-history MenuButtonRedo, MenuButtonUndo
@tiptap/extension-horizontal-rule MenuButtonHorizontalRule
mui-tiptap’s ResizableImage
or @tiptap/extension-image
MenuButtonAddImage: General purpose button. Provide your own onClick behavior (e.g. like this for a user to provide an image URL).
MenuButtonImageUpload: Upload an image. Provide onUploadFiles prop to handle uploading files and returning servable URLs.
See also mui-tiptap’s insertImages util for inserting images into the Tiptap editor content.
@tiptap/extension-italic MenuButtonItalic
@tiptap/extension-link MenuButtonEditLink (requires the mui-tiptap LinkBubbleMenuHandler extension and <LinkBubbleMenu /> component)
@tiptap/extension-list-item MenuButtonIndent, MenuButtonUnindent
@tiptap/extension-ordered-list MenuButtonOrderedList
@tiptap/extension-paragraph MenuSelectHeading
@tiptap/extension-strike MenuButtonStrikethrough
@tiptap/extension-subscript MenuButtonSubscript
@tiptap/extension-superscript MenuButtonSuperscript
mui-tiptap’s TableImproved
or @tiptap/extension-table
• Insert new table: MenuButtonAddTable
• Edit a table (add columns, merge cells, etc.): TableBubbleMenu, or TableMenuControls if you need an alternative UI to the bubble menu
@tiptap/extension-task-list MenuButtonTaskList
@tiptap/extension-text-align MenuSelectTextAlign (all-in-one select)
or MenuButtonAlignLeft, MenuButtonAlignCenter, MenuButtonAlignRight, MenuButtonAlignJustify (individual buttons)
@tiptap/extension-underline MenuButtonUnderline

Other controls components:

  • MenuButtonColorPicker: a button which can be used to bring up a mui-tiptap ColorPicker, letting the user select a color. Used by MenuButtonTextColor and MenuButtonHighlightColor, the props of which extend MenuButtonColorPicker. Important props:
    • swatchColors: Array of color options to show as "preset" color buttons.
    • value/onChange: Current color (CSS string) and its change callback. This is a controlled component, so these props must be provided, unless you're using MenuButtonTextColor or MenuButtonHighlightColor, which handle that logic.
    • popperId: Unique HTML ID for the color picker popper that will be shown when clicking this button (used for aria-describedby for accessibility).
    • PopperProps: Override the props for the popper that houses the color picker.
    • ColorPickerProps: Override the props for mui-tiptap ColorPicker, such as colorToHex to customize the color-parsing logic.
  • MenuButtonRemoveFormatting: a control button that removes all inline formatting of marks (calling Tiptap’s unsetAllMarks())
  • MenuDivider: renders a vertical line divider to separate different sections of your menu bar and implicitly group separate controls.
  • MenuControlsContainer: provides consistent spacing between different editor controls components provided as children.

Typically you will define your controls (for RichTextEditor’s renderControls or RichTextField’s controls) like:

<MenuControlsContainer>
  <MenuSelectHeading />
  <MenuDivider />
  <MenuButtonBold />
  <MenuButtonItalic />
  {/* Add more controls of your choosing here */}
</MenuControlsContainer>

Localization

All of the menu buttons, select components, and bubble menus allow you to override their default labels and content via props. Examples below.

Buttons

In general, use tooltipLabel:

<MenuButtonBold tooltipLabel="Toggle bold" />

The MenuButtonTextColor and MenuButtonHighlightColor components also have a labels prop for overriding content of the color picker popper.

<MenuButtonTextColor
  tooltipLabel="Text color"
  labels={{
    cancelButton: "Cancel",
    removeColorButton: "Reset",
    removeColorButtonTooltipTitle: "Remove the color",
    saveButton: "OK",
    textFieldPlaceholder: 'Ex: "#7cb5ec"',
  }}
/>
Selects
<MenuSelectFontFamily
  options={[
    { label: "Monospace", value: "monospace" },
    { label: "Serif", value: "serif" },
  ]}
  aria-label="Font families"
  emptyLabel="Font family"
  tooltipTitle="Change font family"
  unsetOptionLabel="Reset"
/>
<MenuSelectFontSize
  aria-label="Font sizes"
  tooltipTitle="Change font size"
  unsetOptionLabel="Reset"
/>
<MenuSelectHeading
  aria-label="Heading types"
  tooltipTitle="Change heading type"
  labels={{
    empty: "Change to…",
    paragraph: "Normal text",
    heading1: "H1",
    heading2: "H2",
    heading3: "H3",
    heading4: "H4",
    heading5: "H5",
    heading6: "H6",
  }}
/>
<MenuSelectTextAlign
  aria-label="Text alignments"
  tooltipTitle="Change text alignment"
  options={[
    {
      value: "left",
      label: "Text-align left",
      shortcutKeys: ["mod", "Shift", "L"],
      IconComponent: MyCustomLeftAlignIcon,
    },
    {
      value: "right",
      label: "Text-align right",
      shortcutKeys: ["mod", "Shift", "R"],
      IconComponent: MyCustomRightAlignIcon,
    },
  ]}
/>
Bubble menus
<LinkBubbleMenu
  labels={{
    viewLinkEditButtonLabel: "Edit link",
    viewLinkRemoveButtonLabel: "Remove link",
    editLinkAddTitle: "Add new link",
    editLinkEditTitle: "Update this link",
    editLinkCancelButtonLabel: "Cancel changes",
    editLinkTextInputLabel: "Text content",
    editLinkHrefInputLabel: "URL",
    editLinkSaveButtonLabel: "Save changes",
  }}
/>
<TableBubbleMenu
  labels={{
    insertColumnBefore: "Add a new column before this",
    insertColumnAfter: "Add a new column after this",
    deleteColumn: "Remove current column",
    // And several more. Check props type definition for details!
  }}
/>

Tips and suggestions

Choosing your editor extensions

Browse the official Tiptap extensions, and check out mui-tiptap’s additional extensions. The easiest way to get started is to install and use Tiptap’s StarterKit extension, which bundles several common Tiptap extensions.

To use an extension, you need to (1) install its package and (2) include the extension in the extensions array when instantiating your editor (either with <RichTextEditor extensions={[]} /> or useEditor({ extensions: [] })).

Extension precedence and ordering

Extensions that need to be higher precedence (for their keyboard shortcuts, etc.) should come later in your extensions array. (See Tiptap's general notes on extension plugin precedence and ordering here.) For example:

  • Put the TableImproved (or Table) extension first in the array.
    • As noted in the underlying prosemirror-tables package, the table editing plugin should have the lowest precedence, since it depends on key and mouse events but other plugins will likely need to handle those first. For instance, if you want to indent or dedent a list item inside a table, you should be able to do that by pressing tab, and tab should only move between table cells if not within such a nested node.
  • Put the Blockquote extension after the Bold extension, so Blockquote’s keyboard shortcut takes precedence.
    • Otherwise, the keyboard shortcut for Blockquote (Cmd+Shift+B) will mistakenly toggle the bold mark (due to its “overlapping” Cmd+b shortcut). (See related Tiptap issues here and here.)
  • Put the Mention extension after list-related extensions (TaskList, TaskItem, BulletList, OrderedList, ListItem, etc.) so that pressing "Enter" on a mention suggestion will select it, rather than create a new list item (when trying to @mention something within an existing list item).

Other extension tips

  • If you’d like Subscript and Superscript extensions to be mutually exclusive, so that text can't be both superscript and subscript simultaneously, use the excludes configuration parameter to exclude each other.

    • As described in this Tiptap issue. For instance:

      const CustomSubscript = Subscript.extend({
        excludes: "superscript",
      });
      const CustomSuperscript = Superscript.extend({
        excludes: "subscript",
      });
  • If you’d prefer to be able to style your inline Code marks (e.g., make them bold, add links, change font size), you should extend the extension and override the excludes field, since by default it uses "_" to make it mutually exclusive from all other marks. For instance, to allow you to apply Code with any other inline mark, use excludes: "", or to make it work with all except italics, use:

    Code.extend({ excludes: "italic" });

Drag-and-drop and paste for images

You can provide editorProps to the RichTextEditor component or useEditor, and provide the handleDrop and handlePaste options to add support for drag-and-drop and paste of image files, respectively. Check out the mui-tiptap example of this in action. The mui-tiptap insertImages util is handy for this to take uploaded files and insert them into the editor content.

Re-rendering RichTextEditor when content changes

By default, RichTextEditor uses content the same way that Tiptap’s useEditor does: it sets the initial content for the editor, and subsequent changes to the content variable will not change what content is rendered. (Only the user’s editor interaction will.) This can avoid annoyances like overwriting the content while a user is actively typing or editing.

It is not efficient to use RichTextEditor/useEditor as a fully “controlled” component where you change content on each call to the editor’s onUpdate, due to the fact that editor content must be serialized to get the HTML string (getHTML()) or ProseMirror JSON (getJSON()) (see Tiptap docs and this discussion).

But if you need this behavior in certain situations, like you have changed the content external to the component and separate from the user’s editor interaction, you can call editor.commands.setContent(content) (docs) within a hook to update the editor document.

For instance, you could use something like the following, which (1) only calls setContent when the editor is either read-only or unfocused (aiming to avoid losing any in-progress changes the user is making, though keep in mind that changes to isFocused itself do not cause re-rendering and so won't re-run the effect), and (2) tries to preserve the user’s current selection/caret:

const editor = rteRef.current?.editor;
useEffect(() => {
  if (!editor || editor.isDestroyed) {
    return;
  }
  if (!editor.isFocused || !editor.isEditable) {
    // Use queueMicrotask per https://github.com/ueberdosis/tiptap/issues/3764#issuecomment-1546854730
    queueMicrotask(() => {
      const currentSelection = editor.state.selection;
      editor
        .chain()
        .setContent(content)
        .setTextSelection(currentSelection)
        .run();
    });
  }
}, [content, editor, editor?.isEditable, editor?.isFocused]);

You could also alternatively pass content as an editor dependency via <RichTextEditor … editorDependencies={[content]} /> (or equivalently include it in your useEditor dependency array), and this will force-recreate the entire editor upon changes to the value. This is a much less efficient option, and it can cause a visual “flash” as the editor is rebuilt.

Note that if these content updates are coming from changes other users are making (e.g. saved to a database), it may be better to use collaborative editing functionality with Yjs, and not rely on content at all.

Contributing

Get started here.

mui-tiptap's People

Contributors

cwierzbicki00 avatar dependabot[bot] avatar oliviertassinari avatar semanticist21 avatar sjdemartini avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

mui-tiptap's Issues

Module not found: Error: Package path ./dist is not exported from package

Hello ! Hope you're fine. I need some help on this issue I have when I try to use mui-tiptap :

Module not found: Error: Package path ./dist is not exported from package /home/philippe/stylotendu/node_modules/mui-tiptap (see exports field in /home/philippe/stylotendu/node_modules/mui-tiptap/package.json)

Here is a copy of my package.json :

{
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@emotion/react": "^11.11.3",
    "@emotion/styled": "^11.11.0",
    "@mui/icons-material": "^5.15.1",
    "@mui/material": "^5.15.1",
    "@mui/x-data-grid": "^6.18.5",
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "@tiptap/core": "^2.1.13",
    "@tiptap/extension-heading": "^2.1.13",
    "@tiptap/extension-image": "^2.1.13",
    "@tiptap/extension-table": "^2.1.13",
    "@tiptap/pm": "^2.1.13",
    "@tiptap/react": "^2.1.13",
    "@tiptap/starter-kit": "^2.1.13",
    "mui-tiptap": "^1.8.6",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router": "^6.20.1",
    "react-router-dom": "^6.21.0",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

Thank you!

Ability to change RichTextField Border color

Is your feature request related to a problem? Please describe.

In the current project that I am working on we have a error state for form fields that highlights the outside of the dropdown or text field red. We would like to continue this pattern with this rich text field. Using class overrides in the props I am able to make the bottom border of the header red but I am not able to change the outside border of the TextArea red. Attached below are pictures of what am able to do at this point and what I would like to be able to do.

Here is a snippet of the code that ive tried to override the outer border... I've tried to work with all the code commented out but with no effect

<RichTextField
      // classes={{
      //   content: styles.formError,
      //   outlined: styles.formError,
      //   standard: styles.formError,
      //   root: styles.formError,
      //   menuBar: styles.formError,
      //   menuBarContent: styles.formError,
      // }}
      // RichTextContentProps={{
      //   className: styles.formError,
      //   classes: { root: styles.formError, editable: styles.formError },
      // }}
      MenuBarProps={{ className: styles.formError }}
      // className={styles.formError}
      disabled={inputDisabled}

Describe the solution you'd like

Ideally it would be awesome to have an error prop but that seems like a huge lift. I believe the easiest solution for my problem would to be able to override the classes.notchedOutline styling in the FieldContainer.tsx file. Im not sure if that could be handled like the other fields in the RichTextField classes override that i tried above

Describe alternatives you've considered

open to any suggestions

Additional context

What ive been able to do so far
Screenshot 2023-09-18 at 14 56 45

What I am aiming to do
Screenshot 2023-09-18 at 14 57 30

Add option to configure locale

Is your feature request related to a problem? Please describe.

Mui has an option to configure the locale for their components using the global theme and importing the language tag from the locale package. The locale package exports named objects with the language tag that override the default props of the component and changes the language of the different texts that component has.
https://github.com/mui/material-ui/blob/master/packages/mui-material/src/locale/index.ts

Describe the solution you'd like

Add a locale prop to each component or configure a global locale override. Simple components like Menu Buttons have only the label but bubble menus and other extensions have multiple labels and texts.

Describe alternatives you've considered

Make each label/text prop of all extensions/controls customizable.

Why does this project use react-icons?

Wanted to use this package, but seeing react-icons being added as an extra dependency to my project is a bummer. Would love if there is an option to only use the mui icons that are already available in every mui project.

MenuButtonEditLink not displaying edit link pop-up box

Describe the bug

The pop-up box for inputting the URL for editing a link does not show up.

To Reproduce

While using the control inside of a mui-tiptap editor:

  1. Highlight text inside of the editor
  2. Click on the edit link button in the menu bar

Expected behavior

Expectation: A popup box appears allowing user to input URL
Actual: Nothing

System (please complete the following information):

  • mui-tiptap version: 1.8.6
  • tiptap version: 2.2.4
  • Browser: Chrome
  • Node version: 18.17.0
  • OS: macOS 14.1.2

Additional context

Add any other context about the problem here.

Embedding a Google Sheets preview

Is there a way to embed a preview of a Google Sheets file?
I'm imagining a user adds a link with public permissions and selects a column x row range to display.

Or how would one go about implementing this?

Improve accessibility/aria-labels on MenuSelect components

would it be possible to add a aria-label to the inputs like font family and font size? There currently is one, but it's not on the input so there is a missing label error from an accessibility standpoint

Originally posted by @sgober in #174 (comment)

Based on https://mui.com/material-ui/react-select/#accessibility, it seems we perhaps shouldn't be providing an aria-label the way we are, though it's not entirely clear to me what the right pattern is when you don't have a separate label element. (https://mui.com/material-ui/react-select/#accessibility, https://mui.com/material-ui/react-text-field/#accessibility)

For what it's worth, the Select's input itself has aria-hidden="true", so I don't think it needs the aria-label, but there may be a better way to handle a11y with these components.


Please leave a comment and/or feel free to make a PR if you have suggestions on the "right way" to improve this!

When draggable is enabled and text select at the edge of the sentence , it lose selection.

Describe the bug

When draggable is enabled on non-inline items , and text is selected , the selection is lost - randomly .

To Reproduce

Please include a CodeSandbox demo of the problem if possible. (You can fork this CodeSandbox.)

Steps to reproduce the behavior:

  1. Enable draggble
  2. Try to select Text , starting at the right most position.
  3. Text selection will be resetted.

Expected behavior

Text selectable

Screenshots

If applicable, add screenshots to help explain your problem.

System (please complete the following information):

  • mui-tiptap version: [e.g. 1.7.3]
  • tiptap version: [e.g. 2.0.1]
  • Browser: [e.g. Chrome - latest]
  • Node version: [e.g 16.4.2]
  • OS: [e.g. Ubuntu 22.04, macOS 11.4]

Question: How to skip showing the anchor for Headings in readonly mode?

Question:

I know this is a weird question, since HeadingWithAnchor has an opinion in the name itself, but is there perhaps a standard way to avoid showing those anchor links on hover?

The solutions I can think of:

  1. Implement my own custom component based on TipTaps heading
  2. A custom style override, e.g
.MuiTiptap-RichTextContent-readonly {
  .MuiTiptap-HeadingWithAnchorComponent-link {
    display: none;
  }
}

Motivation behind this is long form documents and non-unique headings.

Bump TipTap deps to latest

Is your feature request related to a problem? Please describe.

Curious what are the implications of upgrading underlying TipTap (and it's deps) to latest version (currently 2.2.6).

Describe the solution you'd like

Authors insight; Did you already go down this path? Any gotchas to be aware?

An option to apply color scheme / theme / custom styles for link bubble menu components

Is your feature request related to a problem? Please describe.

LinkBubbleMenu is very stubborn, basially I want to change the color of inputs in LinkBubbleMenu. I changed some colors like for buttons using custom CSS.

Describe the solution you'd like

I know there is always a workaround for every issue. But I think if there is an option to set the theme as Mui does. Or an option to map existing Mui config with the Mui TipTap.

Describe alternatives you've considered

I have used custom CSS for buttons but still wrapping my head with an MUI fieldset at least one should have the option to pass its own component or color as a prop.

Additional context

Add any other context or screenshots about the feature request here.
image

Thanks for the nicer solution.

onUpdate callback from useEditor hook not called when setting content to editor programmatically

Describe the bug

I am using the following code programmatically to set a new content to my editor:
const currentSelection = editor?.state.selection || 0; editor?.chain().setContent(newContent).setTextSelection(currentSelection).run();

I was expecting that the useEditor hook callback for onUpdate would be called. Is there any callback that gets executed when setting the content for the editor programmatically?

Expected behavior

Setting the content programmatically to the editor should call the useEditor hook onUpdate callback.

Editing link with LinkBubbleMenu leads to outer form submission

Describe the bug

If RichTextEditor with LinkBubbleMenu placed into an HTML form, editing link and hitting "Save" button leads to submission of this form.
I believe this happens because even despite in HTML these two forms are not connected, React bubbles submit event up through Portal to outer form component.

To Reproduce

Steps to reproduce the behavior:

Use <RichTextEditor> inside a form, start editing a link, then hit "Save" button.

const TestLinkEdit = () => {
  const handleSubmit = () => {
    alert("Gotcha!");
  };

  return (
    <form onSubmit={handleSubmit}>
      <RichTextEditor
        extensions={[StarterKit, Link, LinkBubbleMenuHandler]}
        content="<p>Hello world</p>"
        renderControls={() => (
          <MenuControlsContainer>
            <MenuButtonEditLink />
          </MenuControlsContainer>
        )}
      >
        {() => <LinkBubbleMenu />}
      </RichTextEditor>
    </form>
  );
};

Expected behavior

Saving a link from pop-up form doesn't submit outer form.

System:

  • mui-tiptap version: 1.0.0
  • tiptap version: 2.0.4

Additional context

Currently workaround is to wrap <LinkBubbleMenu /> with a dummy form, catching submit event. However, it leads to incorrect HTML structure ("form cannot appear as a descendant of form")

<RichTextEditor {...}>
  {() => (
    <form onSubmit={(e) => e.stopPropagation()}>
      <LinkBubbleMenu />
    </form>
  )}
</RichTextEditor>

Add more built-in features for "Insert image" menu button, for @tiptap/extension-image

Right now, there's a general-purpose "insert image" button via MenuButtonAddImage. You provide your own onClick (where you could use your own interface to type in a URL, or you could trigger a file upload), like in the simple example in the internal demo.

As originally mentioned/requested in #52, we might also want to add support for more functionality/UI built into mui-tiptap, with:

  • An interface for a user to provide a URL
  • A user to upload an image from their filesystem (#147, available as of mui-tiptap v1.8.0)

See example screenshots here #52 (comment)

CSS classes mangled in production build

Describe the bug

During development, all CSS classes are nicely available. For example, MuiTiptap-RichTextField-content can be found in the DOM. This is nice, because we can override some styles using CSS this way.

However, in a production build the CSS properties are minified. Now, MuiTiptap-Xy-content can be found in the DOM. This is probabily because the function names are minified, and the function names are used to generate the CSS classes.

To Reproduce

To reproduce, you can open the default demo (https://codesandbox.io/p/sandbox/mui-tiptap-demo-3zl2l6) and in the terminal, use the build step. Then, open the 'preview' URL (change port 5173 to 4173). The CSS classes are now different.

Expected behavior

I would expect the CSS classes to be the same in a development build, and in a production build.

Screenshots

If applicable, add screenshots to help explain your problem.

System (please complete the following information):

  • mui-tiptap version: v1.8.5

Additional context

Probabily, the use of function.name needs to be avoided, because the function name is minified in most production builds.
See: https://github.com/sjdemartini/mui-tiptap/blob/main/src/RichTextContent.tsx#L19C20-L19C20

Hard-coding the component name is, I think, the only option here. MUI seems to do that aswell.

Unable to use half of the mui-tiptap controls

Describe the bug

I am unable to use like half of the mui-tiptap components as they fail with errors like setColor or toggleUnderline are not functions (see screenshot below)

To Reproduce

Steps to reproduce the behavior:

This is what I am trying to use with json forms:

        <RichTextEditorProvider editor={editor}>
          <RichTextField
            controls={
              <MenuControlsContainer>
                {/* <MenuSelectFontFamily /> */}
                <MenuSelectHeading />
                {/* <MenuSelectFontSize /> */}
                <MenuDivider />
                <MenuButtonBold />
                <MenuButtonItalic />
                <MenuButtonUnderline />
                <MenuButtonStrikethrough />
                {/* <MenuButtonSubscript /> */}
                {/* <MenuButtonSuperscript /> */}
                <MenuDivider />
                {/* <MenuButtonTextColor
                  defaultTextColor="fff"
                  swatchColors={[
                    { value: '#000000', label: 'Black' },
                    { value: '#ffffff', label: 'White' },
                    { value: '#888888', label: 'Grey' },
                    { value: '#ff0000', label: 'Red' },
                    { value: '#ff9900', label: 'Orange' },
                    { value: '#ffff00', label: 'Yellow' },
                    { value: '#00d000', label: 'Green' },
                    { value: '#0000ff', label: 'Blue' }
                  ]}
                /> */}
                {/* <MenuButtonHighlightColor /> */}
                {/* <MenuDivider /> */}
                <MenuButtonEditLink />

                <MenuDivider />

                {/* <MenuSelectTextAlign /> */}
                {/* <MenuButtonAlignLeft />
                <MenuButtonAlignCenter />
                <MenuButtonAlignRight />
                <MenuButtonAlignJustify />
                <MenuDivider /> */}
                <MenuButtonOrderedList />
                <MenuButtonBulletedList />
                {/* <MenuButtonTaskList /> */}
                <MenuDivider />
                {/* TODO: why are these disabled */}
                {/* <MenuButtonIndent />
                <MenuButtonUnindent />
                <MenuDivider /> */}

                <MenuButtonBlockquote />
                <MenuDivider />
                <MenuButtonCode />
                <MenuButtonCodeBlock />
                <MenuDivider />
                <MenuButtonUndo />
                <MenuButtonRedo />
              </MenuControlsContainer>
            }
          />
        </RichTextEditorProvider>

With editor defined as follows:

  const editor = useEditor({
    extensions: [StarterKit],
    content: data,
    onUpdate({ editor }) {
      handleChange(path, editor.getHTML());
    }
  });

Expected behavior

Every mui-tiptap component above that is commented out results in an error similar to the one I described above. It is odd that some of them work and some of them don't

Screenshots

What is able to work:
Screenshot 2023-10-27 at 11 22 25 AM

Error when I try to use MenuButtonUnderline:
Screenshot 2023-10-27 at 10 46 49 AM

All other error are similar, not sure if this is a tiptap error or something specific to mui-tiptap

System (please complete the following information):

  • mui-tiptap version: 1.8.5
  • tiptap version: 2.1.12
  • Browser: Chrome
  • Node version: 16.20
  • OS: macOS 13.6.1

Additional context

Add any other context about the problem here.

Apply custom styling through MUI theme definition

Is your feature request related to a problem? Please describe.

I am not sure whether there's just documentation missing or this isn't possible yet, but I was unable to figure out how to do it.

Describe the solution you'd like

In my project I have defined a theme for MUI stylings, and the TipTap is properly using them. However, in my case, I need to be able to override styles for particular components, only when they're used inside TipTap. For example, here's how I can style a button from a theme definition file:

createTheme({ 
  components: {
      MuiInputBase: {
          styleOverrides: {
              root: {
                  height: '32px',
              },
              input: {
                  fontSize: '14px',
                  fontWeight: 400,
              },
          },
      },
  }
})

In a similar fashion, I'd like to be able to select the tiptap root and then apply relative styling for classes that are inside it, maybe something like this:

createTheme({ 
  components: {
      MuiTipTap: {
         styleOverrides: {
            // some styling for the texteditor maybe?
          },
         '& .MenuButton-root': {
            background: 'blue'
         }
      }
  }
})

I understand I can wrap the editor in a box and then put styling in there and select classes I need to change, but it would be much better to be able to select the editors components from the theme. Thank you!

Unindent button icon is wrong

Screenshot 2023-10-08 at 15 21 54

Here on the repo on src/controls/MenuButtonUnindent.tsx, the button import is correct, but when I checked my project's node_modules in node_modules/mui-tiptap/dist/controls/MenuButtonUnindent.js, there's
const FormatIndentIncrease_1 = __importDefault(require("@mui/icons-material/FormatIndentIncrease")); on line 7

Incorrect position of MuiTiptap-ControlledBubbleMenu-root

Describe the bug

When I selected the Link Menu Button it shows in the wrong place. I traced the problem to the transform that the class MuiTiptap-ControlledBubbleMenu-root has.

To Reproduce

Steps to reproduce the behavior:

  1. Select the Link Menu Button

Expected behavior

The bubble menu should be centered or below the link menu button.

Screenshots

image

System (please complete the following information):

  • mui-tiptap version: [e.g. 1.0.0-beta.1]
  • tiptap version: 2.0.3
  • Browser: Chrome
  • Node version: 18
  • OS: Windows 11

Additional context

Add any other context about the problem here.

Font sizes of headings cannot be overridden in `MenuSelectHeading`

Which packages did you experience the bug in?

starter-kit

What Tiptap version are you using?

2.1.13

What’s the bug you are facing?

I have default settings and in the heading menu select heading h2 is 0.75 rem instead of 1.75rem.

image

extensions={[StarterKit, ...extension]}

In my custom extensions is not anything, that overrides heading config.

What browser are you using?

Chrome

Code example

No response

What did you expect to happen?

I expected that font sizes go chronologically.

Anything to add? (optional)

No response

Did you update your dependencies?

  • Yes, I’ve updated my dependencies to use the latest version of all packages.

Fully customizable editor content style (margins, etc.)?

Hi, thanks for this project. Very easy to get started with and looks like a good API so far :)

Is your feature request related to a problem? Please describe.

I personally don't like the "compressed" style chosen for the editor content:

image

EDIT: I had another style which set the line-height to be smaller which exacerbate the issue. Without this using the default 1.5 line-height things looks a lot better.

I see that many styling elements can be adjusted using the MUI theme mechanism (very nice), but the margin is locked to 0 unless I'm mistaken.

Describe the solution you'd like

I'm not sure what the best solution would be, but a contentClassName prop which disables the default style if set would be simple solution. This would allow a user to provide a fully customized style. Doing slight modifications would require building a whole new set of styles (though I see the getEditorStyles is importable, so maybe possible to use that as a base), but I think that would be fine. Especially since many slight modifications can be done using the MUI theme. (or through RichTextContentProps)

Describe alternatives you've considered

I've passed in a class name to the RichTextContent using RichTextContentProps to override the margins on h1, p, etc.. The problem is that unless the specificity is perfectly balanced, such overrides mess up the table styling[1] - adding unwanted margins inside the cells. Balancing the specificity require controlling the CSS order, which in some cases is not trivial.

[1] The header margin class end up overriding .css-s26in7-RichTextField-content-RichTextContent-root-RichTextContent-editable .ProseMirror table td > * { margin-bottom: 0px; }

Blur on Tab

Is there a way to not blur on Tab key press?

Instilation failure

Fails to install with the following error:

preinstall: npx only-allow pnpm

ReadMe say use "npm install mui-tiptap"

Any way this can be installed via npm?

Color Picker not working with shadow dom

Describe the bug

Everything was working fine so far, because useRef is used for the components. But the color picker buttton works with event.currentTarget, which isn't accessible in the shadow dom. Any idea how i can solve that?

To Reproduce

<MenuButton onClick={(e) => anchorEl ? handleClose() : setAnchorEl(e.currentTarget) } aria-describedby={popperId} {...otherMenuButtonProps} >

Steps to reproduce the behavior:

  1. When implementing MenuButtonTextColor or MenuButtonHighlightColor in shadow dom, the color picker wont appear

System (please complete the following information):

  • mui-tiptap version: 1.8.5
  • tiptap version: 2.1.12
  • Browser: Chrome
  • Node version: 16.4.2
  • OS: win11

Controlled Component

You can use this easily as a controlled component like this

  const [content, setContent] = useState('');

  useEffect(() => {
   setContent(<p>Some saved content</p>)
  }, []);

<RichTextEditor
  content={content}
        onUpdate={({ editor }) => {
          setContent(editor.getHTML());
        }}
        />

Dependency conflict between tss-react and styled-components

Describe the bug

Dependencies from mui-tiptap are conflicting with other dependencies. Specifically there's a conflict when utilizing styled-components with MUI V5, and tss-react from mui-tiptap.

Error: ModuleNotFoundError: Module not found: Error: Can't resolve '@mui/material/styles'

To Reproduce

  1. Build the project with CRA using MUI, styled-components, webpack, mui-tip
  2. Copy contents from demo
  3. Run craco start

Expected behavior

Compatability between tss-react and projects utilizing styled-components

Screenshots

ModuleNotFoundError: Module not found: Error: Can't resolve '@mui/material/styles' in '/Users/me/dev/clients/node_modules/tss-react/esm'
    at /Users/me/dev/clients/node_modules/webpack/lib/Compilation.js:2014:28
    at /Users/me/dev/clients/node_modules/webpack/lib/NormalModuleFactory.js:791:13
    at eval (eval at create (/Users/me/dev/clients/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:10:1)
    at /Users/me/dev/clients/node_modules/webpack/lib/NormalModuleFactory.js:265:22
    at eval (eval at create (/Users/me/dev/clients/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:9:1)
    at /Users/me/dev/clients/node_modules/webpack/lib/NormalModuleFactory.js:427:22
    at /Users/me/dev/clients/node_modules/webpack/lib/NormalModuleFactory.js:111:11
    at /Users/me/dev/clients/node_modules/webpack/lib/NormalModuleFactory.js:663:25
    at /Users/me/dev/clients/node_modules/webpack/lib/NormalModuleFactory.js:848:8
    at /Users/me/dev/clients/node_modules/webpack/lib/NormalModuleFactory.js:968:5
    at /Users/me/dev/clients/node_modules/neo-async/async.js:6883:13
    at /Users/me/dev/clients/node_modules/webpack/lib/NormalModuleFactory.js:951:45
    at finishWithoutResolve (/Users/me/dev/clients/node_modules/enhanced-resolve/lib/Resolver.js:312:11)
    at /Users/me/dev/clients/node_modules/enhanced-resolve/lib/Resolver.js:386:15
    at /Users/me/dev/clients/node_modules/enhanced-resolve/lib/Resolver.js:435:5
    at eval (eval at create (/Users/me/dev/clients/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:16:1)
resolve '@mui/material/styles' in '/Users/me/dev/clients/node_modules/tss-react/esm'
  Parsed request is a module
  using description file: /Users/me/dev/clients/node_modules/tss-react/package.json (relative path: ./esm)
    Field 'browser' doesn't contain a valid alias configuration
    resolve as module
      /Users/me/dev/clients/node_modules/tss-react/esm/node_modules doesn't exist or is not a directory
      /Users/me/dev/clients/node_modules/tss-react/node_modules doesn't exist or is not a directory
      /Users/me/dev/clients/node_modules/node_modules doesn't exist or is not a directory
      looking for modules in /Users/me/dev/clients/node_modules
        /Users/me/dev/clients/node_modules/@mui/material doesn't exist
      looking for modules in /Users/me/dev/node_modules
        /Users/me/dev/node_modules/@mui/material doesn't exist
      looking for modules in /Users/me/node_modules
        /Users/me/node_modules/@mui/material doesn't exist
      /Users/node_modules doesn't exist or is not a directory
      /node_modules doesn't exist or is not a directory
      looking for modules in /Users/me/dev/clients/node_modules/react-scripts/node_modules
        /Users/me/dev/clients/node_modules/react-scripts/node_modules/@mui/material doesn't exist

System (please complete the following information):

  • mui-tiptap version: 1.1.0
  • tiptap version: 2.0.3
  • Browser: Chrome
  • Node version: v18.12.1
  • OS: Mac

Additional context

Add any other context about the problem here.

Set editor height to prevent dynamic height changes

I want to set the editor height by lines or by pixels.

That means the initial editor without content already has a certain height, which should also stay the same with a lot of content. If required, scrollbars should appear.

Can that be achieved already, or does that require a code improvement?

Bug issue with resizing image with option "inline: true"

Firstly, great thanks for contributors.
I'm using the editor with example here codesandbox.

"https://codesandbox.io/p/devbox/mui-tiptap-demo-forked-g6njhk?file=%2Fsrc%2FEditorMenuControls.tsx&workspaceId=383814f0-5631-4226-8011-65cb927d4daf"

Describe the bug

I had to edit ResizableImage extension options in "useExtensions.ts" to enable image paste when writing.
I don't know why but when directly copying content from page, image was not copy-pasted into the editor.

image

After I enabled options, resizing feature by dragging is not working properly.

To Reproduce

https://codesandbox.io/p/devbox/mui-tiptap-demo-forked-g6njhk?file=%2Fsrc%2FuseExtensions.ts%3A166%2C15-166%2C20&workspaceId=383814f0-5631-4226-8011-65cb927d4daf

enable inline true.

Expected behavior

Image resize by dragging is not possible.

Screenshots

image

System (please complete the following information)

  • mui-tiptap version: 1.8.6
  • tiptap version: 2.0.4
  • Browser: Chrome
  • Node version: 21.7.1
  • OS: Mac
  • Copy-paste your extensions array used for the editor:
[
      // We incorporate all of the functionality that's part of
      // https://tiptap.dev/api/extensions/starter-kit, plus a few additional
      // extensions, including mui-tiptap's

      // Note that the Table extension must come before other nodes that also have "tab"
      // shortcut keys so that when using the tab key within a table on a node that also
      // responds to that shortcut, it respects that inner node with higher precedence
      // than the Table. For instance, if you want to indent or dedent a list item
      // inside a table, you should be able to do that by pressing tab. Tab should only
      // move between table cells if not within such a nested node. See comment here for
      // notes on extension ordering
      // https://github.com/ueberdosis/tiptap/issues/1547#issuecomment-890848888, and
      // note in prosemirror-tables on the need to have these plugins be lower
      // precedence
      // https://github.com/ueberdosis/prosemirror-tables/blob/1a0428af3ca891d7db648ce3f08a2c74d47dced7/src/index.js#L26-L30
      TableImproved.configure({
        resizable: true,
      }),
      TableRow,
      TableHeader,
      TableCell,

      BulletList,
      CodeBlock,
      Document,
      HardBreak,
      ListItem,
      OrderedList,
      Paragraph,
      CustomSubscript,
      CustomSuperscript,
      Text,

      // Blockquote must come after Bold, since we want the "Cmd+B" shortcut to
      // have lower precedence than the Blockquote "Cmd+Shift+B" shortcut.
      // Otherwise using "Cmd+Shift+B" will mistakenly toggle the bold mark.
      // (See https://github.com/ueberdosis/tiptap/issues/4005,
      // https://github.com/ueberdosis/tiptap/issues/4006)
      Bold,
      Blockquote,

      Code,
      Italic,
      Underline,
      Strike,
      CustomLinkExtension.configure({
        // autolink is generally useful for changing text into links if they
        // appear to be URLs (like someone types in literally "example.com"),
        // though it comes with the caveat that if you then *remove* the link
        // from the text, and then add a space or newline directly after the
        // text, autolink will turn the text back into a link again. Not ideal,
        // but probably still overall worth having autolink enabled, and that's
        // how a lot of other tools behave as well.
        autolink: true,
        linkOnPaste: true,
        openOnClick: false,
      }),
      LinkBubbleMenuHandler,

      // Extensions
      Gapcursor,
      HeadingWithAnchor,
      TextAlign.configure({
        types: ["heading", "paragraph", "image"],
      }),
      TextStyle,
      Color,
      FontFamily,
      FontSize,
      Highlight.configure({ multicolor: true }),
      HorizontalRule,

      ResizableImage.configure({
        // allowBase64: false,
        inline: true,
      }),
      // When images are dragged, we want to show the "drop cursor" for where they'll
      // land
      Dropcursor,

      TaskList,
      TaskItem.configure({
        nested: true,
      }),

      Mention.configure({
        suggestion: mentionSuggestionOptions,
      }),

      Placeholder.configure({
        placeholder,
      }),

      // We use the regular `History` (undo/redo) extension when not using
      // collaborative editing
      History,
    ]

ediotr.can(...).setColor is not a function

Hi,

i have some problems when i try to use MenuButtonTextColor or MenuButtonUnderline.
I am getting the error
Uncaugt TypeError: editor.can(...).setColor is not a function

I have already set up a codesandsbox where you can see the error:

Codesandbox Demo

`ref` failure in Nextjs project

Describe the bug

I am importing mui-tiptap dynamically into a Nextjs component. I am receiving a Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()? error.

To Reproduce

Please include a CodeSandbox demo of the problem if possible.
Code Sandbox

Steps to reproduce the behavior:

  1. Install and dynamically load mui-tiptap in Nextjs project (app router)
  2. Try to preset content in the editor
  3. Console error about ref preventing content load.

Expected behavior

Ref should be used in RichTextEditor as expected

Screenshots

If applicable, add screenshots to help explain your problem.

System (please complete the following information)

  • mui-tiptap version: 1.8.6
  • tiptap version: 2.2.2
  • Browser: all
  • Node version: 18.19.0
  • OS: macOS 14.2.1
  • Copy-paste your extensions array used for the editor:
      BulletList,
      Document,
      ListItem,
      OrderedList,
      Paragraph,
      Text,
      Bold,
      Italic,
      Underline,
      HeadingWithAnchor,
      History,

Question: How does mui-tiptap maintain text selection when controls are clicked

Hey, sorry this isn't a feature request but I couldn't figure out the best way to ask a question!

I haven't actually been using it, but this package is great! I've been working on my own implementation of TipTap for Chakra UI, and this package has provided a lot of useful examples of how to make custom extensions and properly type some of the examples they have in the original demos.

The one thing I have not been able to figure out though, is how you manage to maintain the text selection inside the editor when other elements are focused. In my case specifically, I'm trying to prevent a selected block of text from unselecting as soon as I click on the font selector dropdown. I've found some hacky ways to do it by re-selecting the current selection after the dropdown opens, but it prevents my dropdown from being interactable (using my chakra wrapper for react select). I'd be very grateful if you could provide any info on how you managed to accomplish this, even if that's just to point me somewhere in this repo where you handle that.

Hopefully this isn't me just doing something stupid haha.

Change Remaining @mui/icons-material imports to Direct Path Imports

Describe the bug

On some platforms, such as Nextjs 13, an issue emerges due to the large bundle size of mui-tiptap / it's import method for some of "@mui/icons-material". For most of the codebase, direct path imports are used. There are some areas where the tree shaking imports are used, which bloat the package and are strangely linked to issues on Nextjs 13.

To Reproduce

Not possible to reproduce without a full Nextjs 13 deployment on Vercel. Not necessarily relevant, either.

Steps to reproduce the behavior:

  1. Deploy any NextJS13 application to Vercel which uses API features.
  2. Attempt to call the API by some page action.
  3. Receive 504 timeout errors, like discussed in (vercel/next.js#47237)

Expected behavior

The inclusion of mui-tiptap should safely import the icons from @mui/icons-material using the direct path. Then, the application runs smoothly.

Screenshots

Simply need to change from
image
to this
image

System (please complete the following information):

  • mui-tiptap version: 1.8.2
  • tiptap version: ^2.0.0
  • Browser: Chrome
  • Node version: 18.17.1
  • OS: Windows 11

Additional context

Regardless, this will reduce the bundle size and is considered best practice. (See mui docs https://mui.com/material-ui/icons/#usage )

The imports should be changed to direct path imports unless the techniques specified here are used https://mui.com/material-ui/guides/minimizing-bundle-size/#option-two-use-a-babel-plugin

It is my first time contributing on OSS, so please forgive any mishaps. I am a really big fan of this project and am impressed with the design & codebase. I will attempt to open a PR for this issue shortly.

Module build failed

this is not working on:
--- @mui/[email protected]
--- [email protected]
--- @tiptap/[email protected]

code sample:
editor.jsx

import { useEditor } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import {
  MenuButtonBold,
  MenuButtonItalic,
  MenuControlsContainer,
  MenuDivider,
  MenuSelectHeading,
  RichTextEditorProvider,
  RichTextField,
} from "mui-tiptap";

export default function Quill() {
  return <RichTextReadOnly content="<p>Hello world</p>" extensions={[StarterKit]} />
}

error:

ERROR in ./node_modules/@mui/utils/useControlled.js
Module build failed (from ./node_modules/source-map-loader/dist/cjs.js):
Error: ENOENT: no such file or directory, open '/Users/ali/Projects/Nomreyar/client/node_modules/@mui/utils/useControlled.js'
ERROR in ./node_modules/@mui/utils/useEnhancedEffect.js
Module build failed (from ./node_modules/source-map-loader/dist/cjs.js):
Error: ENOENT: no such file or directory, open '/Users/ali/Projects/Nomreyar/client/node_modules/@mui/utils/useEnhancedEffect.js'
ERROR in ./node_modules/@mui/utils/useEventCallback.js
Module build failed (from ./node_modules/source-map-loader/dist/cjs.js):
Error: ENOENT: no such file or directory, open '/Users/ali/Projects/Nomreyar/client/node_modules/@mui/utils/useEventCallback.js'
ERROR in ./node_modules/@mui/utils/useForkRef.js
Module build failed (from ./node_modules/source-map-loader/dist/cjs.js):
Error: ENOENT: no such file or directory, open '/Users/ali/Projects/Nomreyar/client/node_modules/@mui/utils/useForkRef.js'
ERROR in ./node_modules/@mui/utils/useId.js
Module build failed (from ./node_modules/source-map-loader/dist/cjs.js):
Error: ENOENT: no such file or directory, open '/Users/ali/Projects/Nomreyar/client/node_modules/@mui/utils/useId.js'
ERROR
[eslint] 
src/parts/quill.jsx
  Line 14:11:  'RichTextReadOnly' is not defined  react/jsx-no-undef

Search for the keywords to learn more about each error.

Add new menu buttons

Is your feature request related to a problem? Please describe.

To make this package a true replacement for react-mui-draft-wysiwyg there are a few menu buttons that you can add.

Describe the solution you'd like

Implement the following suggestions:

  • Undo menu button with @tiptap/extension-history (#65)
  • Redo menu button with @tiptap/extension-history (#65)
  • Highlight color menu button with @tiptap/extension-highlight (#135)
  • Text color menu button with @tiptap/extension-text-style @tiptap/extension-color (#135)
  • Add h4, h5, h6 to MenuHeadingSelect
  • Font size menu button with @tiptap/extension-text-style (new FontSize extension and MenuSelectFontSize in #94 and #95)
  • Font family menu button with @tiptap/extension-text-style @tiptap/extension-font-family (#110)
  • Align left menu button with @tiptap/extension-text-align (#69, #70)
  • Alight right menu button with @tiptap/extension-text-align (#69, #70)
  • Align center menu button with @tiptap/extension-text-align (#69, #70)
  • Justify menu button with @tiptap/extension-text-align (#69, #70)
  • Insert image menu button with @tiptap/extension-image
    • Using URL (see #136)
    • Uploading image (see #136)
  • Horizontal rule with @tiptap/extension-horizontal-rule (#66)
  • Underline with @tiptap/extension-underline (#68)

Describe alternatives you've considered

A clear and concise description of any alternative solutions or features you've considered.

Additional context

Add any other context or screenshots about the feature request here.

Live Color Preview in Text Editor

I've been using your text editor module with MUI and TipTap integration and it's been invaluable in my project.

I was wondering, is there currently a way to see the applied text color without having to save and manually deselect the text? If not, I'd like to suggest a feature. It would be great if there was an option for a preview that automatically deselects the text while it's still in focus, so users can instantly see the chosen color.
image

MenuButtonIndent not working in TouchDevice or non-touch device

Describe the bug

With the ListItem extension installed I am unable to even click the MenuButtonIndent button.

I would like to use this button to easily create paragraphs.

To Reproduce

Please include a CodeSandbox demo of the problem if possible. (You can fork this CodeSandbox.)

Steps to reproduce the behavior:

Scenario 1:

  1. Start new line
  2. Try clicking MenuButtonIndent button.

Scenario 2:

  1. Highlight a line of text
  2. Try clicking MenuButtonIndent button

Expected behavior

I expect the MenuButtonIndent button to be clickable

Screenshots

If applicable, add screenshots to help explain your problem.

System (please complete the following information)

  • mui-tiptap version: 1.8.6
  • tiptap version: 2.2.4
  • Browser: Chrome
  • Node version: 18.17.0
  • OS: Mac 14
  • Copy-paste your extensions array used for the editor:
      TableImproved.configure({
        resizable: true,
      }),
      TableRow,
      TableHeader,
      TableCell,

      BulletList.configure({
        HTMLAttributes: {
          class: "ml-5",
        },
      }),
      CodeBlock,
      Document,
      HardBreak,
      ListItem,
      OrderedList.configure({
        HTMLAttributes: {
          class: "ml-5",
        },
      }),
      Paragraph.configure({
        HTMLAttributes: {
          class: "mb-2",
        },
      }),
      CustomSubscript,
      CustomSuperscript,
      Text,

      // Blockquote must come after Bold, since we want the "Cmd+B" shortcut to
      // have lower precedence than the Blockquote "Cmd+Shift+B" shortcut.
      // Otherwise using "Cmd+Shift+B" will mistakenly toggle the bold mark.
      // (See https://github.com/ueberdosis/tiptap/issues/4005,
      // https://github.com/ueberdosis/tiptap/issues/4006)
      Bold,
      Blockquote,

      Code,
      Italic,
      Underline,
      Strike,
      CustomLinkExtension.configure({
        // autolink is generally useful for changing text into links if they
        // appear to be URLs (like someone types in literally "example.com"),
        // though it comes with the caveat that if you then *remove* the link
        // from the text, and then add a space or newline directly after the
        // text, autolink will turn the text back into a link again. Not ideal,
        // but probably still overall worth having autolink enabled, and that's
        // how a lot of other tools behave as well.
        autolink: true,
        linkOnPaste: true,
        openOnClick: false,
      }),
      LinkBubbleMenuHandler,

      // Extensions
      Gapcursor,
      HeadingWithAnchor,
      TextAlign.configure({
        types: ["heading", "paragraph", "image"],
      }),
      TextStyle,
      Color,
      FontFamily,
      FontSize,
      Highlight.configure({ multicolor: true }),
      HorizontalRule,

      ResizableImage,
      // When images are dragged, we want to show the "drop cursor" for where they'll
      // land
      Dropcursor,

      TaskList,
      TaskItem.configure({
        nested: true,
      }),

      // Mention.configure({
      //   suggestion: mentionSuggestionOptions,
      // }),

      Placeholder.configure({
        placeholder,
      }),

      // We use the regular `History` (undo/redo) extension when not using
      // collaborative editing
      History,

Additional context

Add any other context about the problem here.

How do I skip the `formatHref` behavior?

Is your feature request related to a problem? Please describe.

I found this function formatHref in file: /src/LinkBubbleMenu/EditLinkMenuContent.tsx

How can I remove or skip its behavior?
image

A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like

A clear and concise description of what you want to happen.

Describe alternatives you've considered

A clear and concise description of any alternative solutions or features you've considered.

Additional context

Add any other context or screenshots about the feature request here.

Reduce amount of peer dependencies

I just found this package. It's amazing and it's the alternative I was looking to replace react-mui-draft-wysiwyg.

Is your feature request related to a problem? Please describe.

Right now, I know this package depends on a lot of @tip/tap packages.
It creates a clutter in the package.json

    "@tiptap/core": "2.0.3",
    "@tiptap/extension-blockquote": "2.0.3",
    "@tiptap/extension-bold": "2.0.3",
    "@tiptap/extension-bubble-menu": "2.0.3",
    "@tiptap/extension-bullet-list": "2.0.3",
    "@tiptap/extension-code": "2.0.3",
    "@tiptap/extension-code-block": "2.0.3",
    "@tiptap/extension-document": "2.0.3",
    "@tiptap/extension-dropcursor": "2.0.3",
    "@tiptap/extension-floating-menu": "2.0.3",
    "@tiptap/extension-gapcursor": "2.0.3",
    "@tiptap/extension-hard-break": "2.0.3",
    "@tiptap/extension-heading": "2.0.3",
    "@tiptap/extension-history": "2.0.3",
    "@tiptap/extension-image": "2.0.3",
    "@tiptap/extension-italic": "2.0.3",
    "@tiptap/extension-link": "2.0.3",
    "@tiptap/extension-list-item": "2.0.3",
    "@tiptap/extension-ordered-list": "2.0.3",
    "@tiptap/extension-paragraph": "2.0.3",
    "@tiptap/extension-placeholder": "2.0.3",
    "@tiptap/extension-strike": "2.0.3",
    "@tiptap/extension-subscript": "2.0.3",
    "@tiptap/extension-superscript": "2.0.3",
    "@tiptap/extension-table": "2.0.3",
    "@tiptap/extension-table-cell": "2.0.3",
    "@tiptap/extension-table-header": "2.0.3",
    "@tiptap/extension-table-row": "2.0.3",
    "@tiptap/extension-task-item": "2.0.3",
    "@tiptap/extension-task-list": "2.0.3",
    "@tiptap/extension-text": "2.0.3",
    "@tiptap/pm": "^2.0.3",
    "@tiptap/react": "2.0.3",

Side note: Why do you need react-icons when you have @mui/icons-material that already works with tree-shaking? https://mui.com/material-ui/guides/minimizing-bundle-size/

Describe the solution you'd like

Remove react-icons and just use @mui/icons-material. Include @tip-tap peer depenencies as dependences in this package.

When Heading 1 is selected, all other heading levels are disabled

Describe the bug

If I style some text as Heading 1, I cannot switch it to any other heading level, because they are disabled in the MenuSelectHeading component. I have to first switch it to Paragraph and then to the desired Heading 2-6.

This only happens with Heading 1, all other heading levels are fine.

To Reproduce

I'll try to replicate it in codesandbox when I have the time.

Steps to reproduce the behaviour:

  1. write some text and style it as Heading 1
  2. with that text selected or having the cursor in it, open the MenuSelectHeading dropdown

Expected behaviour

All heading levels in the dropdown should be enabled and selectable.

E.g. it should be possible to select the option Heading 2, which should switch the selected/focused text to the Heading 2 style.

Actual behaviour

All heading levels are disabled and cannot be selected. You have to first select Paragraph, and then the desired Heading 2-6.

Screenshots

image

System (please complete the following information):

  • mui-tiptap version: ^1.8.6
  • tiptap version: ^2.2.3
  • Browser: Edge
  • Node version: 20.11.0
  • OS: Pop!_OS 22.04
  • "react": "^18.2.0"
  • "typescript": "^4.5.4",

Additional context

I compared this behaviour with your Codesandbox example, and there it works fine - you can switch heading levels directly. I tried replicating the codesandbox code as closely as possible in my project - using the same versions of the tiptap and mui-tiptap packages, using the HeadingWithAnchor instead of the basic Heading extension, same order of the extensions. Nothing helped. The older package versions caused weird internal "cannot access property of undefined" errors.

Code excerpts:

Editor definition:
const editor = useEditor({
  editorProps: {
    attributes: {
      'data-ui-input': 'rich-text-editor-input-field',
    },
  },
  extensions: [
    BulletList,
    CodeBlock,
    Document,
    HardBreak,
    ListItem,
    OrderedList,
    Paragraph,
    CustomSubscript,
    CustomSuperscript,
    Text,
    Bold,
    Blockquote,
    Code,
    Italic,
    Underline,
    Strike,
    CustomLinkExtension.configure({
      autolink: true,
      linkOnPaste: true,
      openOnClick: false,
    }),
    LinkBubbleMenuHandler,
    Gapcursor,
    Heading,
    HorizontalRule,
    Dropcursor,
    TaskList,
    TaskItem.configure({
      nested: true,
    }),
    History,
    Placeholder.configure({
      emptyEditorClass: styles.isEditorEmpty,
      placeholder,
    }),
    CharacterCount.configure({
      limit: maxLength,
    }),
  ],
  content,
}, [placeholder, content])
Component:
<ThemeProvider theme={muiTheme}>
  <RichTextEditorProvider editor={editor}>
    <RichTextField
      className={styles.editor}
      RichTextContentProps={{
        className: clsx(styles.richTextField, richTextFieldClassName),
      }}
      controls={(
        <MenuControlsContainer>
          <MenuButtonUndo
            data-ui-element="rich-text-menu-item-undo"
            tooltipLabel={t('richTextEditor.undo')}
          />
          <MenuButtonRedo
            data-ui-element="rich-text-menu-item-redo"
            tooltipLabel={t('richTextEditor.redo')}
          />
          <MenuDivider />
          <MenuSelectHeading
            data-ui-element="rich-text-menu-item-select-heading"
            className={styles.menuSelectHeading}
            tooltipTitle={t('richTextEditor.selectHeadingTooltip')}
            labels={{
              paragraph: t('richTextEditor.paragraph'),
              heading1: t('richTextEditor.heading1'),
              heading2: t('richTextEditor.heading2'),
              heading3: t('richTextEditor.heading3'),
              heading4: t('richTextEditor.heading4'),
              heading5: t('richTextEditor.heading5'),
              heading6: t('richTextEditor.heading6'),
            }}
          />
          <MenuDivider />
          <MenuButtonBold
            data-ui-element="rich-text-menu-item-bold"
            tooltipLabel={t('richTextEditor.bold')}
          />
          <MenuButtonItalic
            data-ui-element="rich-text-menu-item-italic"
            tooltipLabel={t('richTextEditor.italic')}
          />
          <MenuButtonUnderline
            data-ui-element="rich-text-menu-item-underline"
            tooltipLabel={t('richTextEditor.underline')}
          />
          <MenuButtonStrikethrough
            data-ui-element="rich-text-menu-item-strikethrough"
            tooltipLabel={t('richTextEditor.strikethrough')}
          />
          <MenuButtonSubscript
            data-ui-element="rich-text-menu-item-subscript"
            tooltipLabel={t('richTextEditor.subscript')}
          />
          <MenuButtonSuperscript
            data-ui-element="rich-text-menu-item-superscript"
            tooltipLabel={t('richTextEditor.superscript')}
          />
          <MenuDivider />
          <MenuButtonEditLink
            data-ui-element="rich-text-menu-item-edit-link"
            tooltipLabel={t('richTextEditor.link')}
          />
          <MenuDivider />
          <MenuButtonOrderedList
            data-ui-element="rich-text-menu-item-ordered-list"
            tooltipLabel={t('richTextEditor.orderedList')}
          />
          <MenuButtonBulletedList
            data-ui-element="rich-text-menu-item-bullet-list"
            tooltipLabel={t('richTextEditor.bulletedList')}
          />
          <MenuButtonTaskList
            data-ui-element="rich-text-menu-item-task-list"
            tooltipLabel={t('richTextEditor.taskChecklist')}
          />
          <MenuDivider />
          <MenuButtonBlockquote
            data-ui-element="rich-text-menu-item-blockquote"
            tooltipLabel={t('richTextEditor.blockquote')}
          />
          <MenuDivider />
          <MenuButtonCode
            data-ui-element="rich-text-menu-item-code"
            tooltipLabel={t('richTextEditor.code')}
          />
          <MenuButtonCodeBlock
            data-ui-element="rich-text-menu-item-code-block"
            tooltipLabel={t('richTextEditor.codeBlock')}
          />
          <MenuDivider />
          <MenuButtonHorizontalRule
            data-ui-element="rich-text-menu-item-horizontal-rule"
            tooltipLabel={t('richTextEditor.insertHorizontalLine')}
          />
          <MenuButtonRemoveFormatting
            data-ui-element="rich-text-menu-item-remove-formatting"
            tooltipLabel={t('richTextEditor.removeInlineFormatting')}
          />
        </MenuControlsContainer>
      )}
    />
    <LinkBubbleMenu
      labels={{
        viewLinkEditButtonLabel: t('edit'),
        viewLinkRemoveButtonLabel: t('remove'),
        editLinkAddTitle: t('richTextEditor.addLink'),
        editLinkEditTitle: t('richTextEditor.editLink'),
        editLinkCancelButtonLabel: t('cancel'),
        editLinkTextInputLabel: t('text'),
        editLinkHrefInputLabel: t('richTextEditor.link'),
        editLinkSaveButtonLabel: t('save'),
      }}
    />
  </RichTextEditorProvider>
</ThemeProvider>

Add more table personalization options

Is your feature request related to a problem? Please describe.

I want to set the border style, border color, table width to 100%, background colors for the cells.

Describe the solution you'd like

Add options to the table tooltip that allow more customization.

Describe alternatives you've considered

For know I manually edit the html output

Additional context

Here is how some of the new options can look like based on microsoft word options.
image
image
image
image
image

MenuButtonHorizontalRule producing console warnings

Describe the bug

Warnings in the console.

To Reproduce

  1. Add Menu <MenuButtonHorizontalRule />

Expected behavior

Should not have warnings.

Logs

react_devtools_backend_compact.js:13096 TextSelection endpoint not pointing into a node with inline content (doc) 
    at MenuButtonHorizontalRule (webpack-internal:///./node_modules/mui-tiptap/dist/esm/controls/MenuButtonHorizontalRule.js:16:86)
    at div
    at MenuControlsContainer (webpack-internal:///./node_modules/mui-tiptap/dist/esm/controls/MenuControlsContainer.js:26:34)
    at Menu (webpack-internal:///./src/components/RichText/Menu.tsx:16:88)
    at DebounceRender (webpack-internal:///./node_modules/mui-tiptap/dist/esm/utils/DebounceRender.js:39:9)
    at div
    at div
    at eval (webpack-internal:///./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
    at div
    at eval (webpack-internal:///./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
    at div
    at eval (webpack-internal:///./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
    at Transition (webpack-internal:///./node_modules/react-transition-group/esm/Transition.js:133:30)
    at Collapse (webpack-internal:///./node_modules/@mui/material/Collapse/Collapse.js:115:82)
    at MenuBar (webpack-internal:///./node_modules/mui-tiptap/dist/esm/MenuBar.js:43:20)
    at div
    at FieldContainer (webpack-internal:///./node_modules/mui-tiptap/dist/esm/FieldContainer.js:70:27)
    at RichTextField (webpack-internal:///./node_modules/mui-tiptap/dist/esm/RichTextField.js:67:26)
    at RichTextEditorProvider (webpack-internal:///./node_modules/mui-tiptap/dist/esm/RichTextEditorProvider.js:18:35)
    at RichTextEditor (webpack-internal:///./node_modules/mui-tiptap/dist/esm/RichTextEditor.js:27:100)
    at div
    at eval (webpack-internal:///./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
    at Box (webpack-internal:///./node_modules/@mui/system/esm/createBox.js:37:72)
    at eval (webpack-internal:///./src/components/RichText/RichText.tsx:22:13)
    at div
    at eval (webpack-internal:///./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
    at Grid (webpack-internal:///./node_modules/@mui/system/esm/Stack/createStack.js:153:24)
    at form
    at Form (webpack-internal:///./src/components/forms/Form.tsx:15:13)
    at FormProvider (webpack-internal:///./node_modules/react-hook-form/dist/index.esm.mjs:180:13)
    at FormContainer (webpack-internal:///./src/components/forms/FormContainer.tsx:53:13)
    at Form (webpack-internal:///./src/views/calendar/Form.tsx:70:13)
    at div
    at eval (webpack-internal:///./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
    at Box (webpack-internal:///./node_modules/@mui/system/esm/createBox.js:37:72)
    at div
    at eval (webpack-internal:///./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
    at Paper (webpack-internal:///./node_modules/@mui/material/Paper/Paper.js:79:83)
    at eval (webpack-internal:///./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
    at Transition (webpack-internal:///./node_modules/react-transition-group/esm/Transition.js:133:30)
    at Slide (webpack-internal:///./node_modules/@mui/material/Slide/Slide.js:98:77)
    at FocusTrap (webpack-internal:///./node_modules/@mui/base/FocusTrap/FocusTrap.js:95:5)
    at div
    at eval (webpack-internal:///./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
    at Portal (webpack-internal:///./node_modules/@mui/base/Portal/Portal.js:36:5)
    at Modal (webpack-internal:///./node_modules/@mui/base/Modal/Modal.js:81:7)
    at Modal (webpack-internal:///./node_modules/@mui/material/Modal/Modal.js:83:82)
    at eval (webpack-internal:///./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
    at Drawer (webpack-internal:///./node_modules/@mui/material/Drawer/Drawer.js:155:83)
    at SidebarRight (webpack-internal:///./src/views/calendar/SidebarRight.tsx:27:13)
    at div
    at eval (webpack-internal:///./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
    at Box (webpack-internal:///./node_modules/@mui/system/esm/createBox.js:37:72)
    at eval (webpack-internal:///./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
    at Container (webpack-internal:///./src/views/calendar/Container.tsx:36:90)
    at Calendar
    at main
    at eval (webpack-internal:///./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
    at div
    at eval (webpack-internal:///./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
    at Box (webpack-internal:///./node_modules/@mui/system/esm/createBox.js:37:72)
    at eval (webpack-internal:///./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
    at div
    at eval (webpack-internal:///./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
    at VerticalLayout (webpack-internal:///./src/@core/layouts/VerticalLayout.tsx:79:13)
    at Layout (webpack-internal:///./src/@core/layouts/Layout.tsx:18:13)
    at UserLayout (webpack-internal:///./src/layouts/UserLayout.tsx:33:11)
    at LocalizationProvider (webpack-internal:///./node_modules/@mui/x-date-pickers/LocalizationProvider/LocalizationProvider.js:32:19)
    at AuthGuard (webpack-internal:///./src/layouts/auth/AuthGuard.tsx:21:13)
    at Direction (webpack-internal:///./src/layouts/components/Direction.tsx:30:13)
    at ThemeProvider (webpack-internal:///./node_modules/@mui/private-theming/ThemeProvider/ThemeProvider.js:43:5)
    at ThemeProvider (webpack-internal:///./node_modules/@mui/system/esm/ThemeProvider/ThemeProvider.js:54:5)
    at ThemeProvider (webpack-internal:///./node_modules/@mui/material/styles/ThemeProvider.js:27:14)
    at ThemeComponent (webpack-internal:///./src/@core/theme/ThemeComponent.tsx:27:13)
    at SettingsProvider (webpack-internal:///./src/@core/context/settingsContext.tsx:76:11)
    at QueryClientProvider (webpack-internal:///./node_modules/@tanstack/react-query/build/lib/QueryClientProvider.mjs:48:3)
    at App (webpack-internal:///./pages/_app.tsx:85:13)
    at PathnameContextProviderAdapter (webpack-internal:///./node_modules/next/dist/shared/lib/router/adapters.js:74:11)
    at ErrorBoundary (webpack-internal:///./node_modules/next/dist/compiled/@next/react-dev-overlay/dist/client.js:306:63)
    at ReactDevOverlay (webpack-internal:///./node_modules/next/dist/compiled/@next/react-dev-overlay/dist/client.js:858:919)
    at Container (webpack-internal:///./node_modules/next/dist/client/index.js:92:1)
    at AppContainer (webpack-internal:///./node_modules/next/dist/client/index.js:197:11)
    at Root (webpack-internal:///./node_modules/next/dist/client/index.js:380:11)

System (please complete the following information):

  • mui-tiptap version: [1.0.0]
  • tiptap version: [2.0.3]
  • Browser: [Chrome]
  • Node version: [18.16.1]
  • OS: [Windows 11]

content string not rendered properly

Describe the bug

Ok, maybe that's also a bug that happens because of my shadow dom. But when I give an initial value to the content property, the html and the styling is not right.

To Reproduce

when I give the string
"<p><span style="color: #333333; font-family: helvetica, 'microsoft sans serif', arial, sans-serif; font-size: 12px; background-color: #ffffdd;">Der Untersuchungsrahmen nach &sect;7 NABEG weist auf die M&ouml;glichkeit von</span></p>"

to the content, the html rendered this:
<p><span style="color: #333333">Der Untersuchungsrahmen nach §7 NABEG weist auf die Möglichkeit von</span></p>
so half of the styling is missing...

System (please complete the following information):

  • mui-tiptap version: 1.8.5
  • tiptap version: 2.1.12
  • Browser: chrome
  • OS: win11

Additional context

BTW great work you're doing, the best looking and working editor I've seen so far...

Color pickers are displayed below the modal

Describe the bug

When using <MenuButtonTextColor /> and <MenuButtonHighlightColor /> color pickers in the TipTap placed in a modal, color pickers are rendered in a layer below the modal.

To Reproduce

Steps to reproduce the behavior:

  1. Use color pickers in the TipTap that is rendered in a MUI Dialog (Modal)

Expected behavior

The same is happening with editing links, however, for links there is a special component <LinkBubbleMenu /> that can be modified by passing an argument for container prop that is a reference to a container in which the popup should be rendered, if proper modal container is passed in, the link popup is rendered in a correct layer without having to mess with z-index. The expected behaviour would be the same as it is for links.

  <LinkBubbleMenu container={props.dialogRef?.current} />

Screenshots

image

System (please complete the following information)

  • mui-tiptap version: ^1.8.6
  • tiptap version: ^2.2.4
  • Browser: Chrome
  • Node version: 18
  • OS: macOS 11.4
  • Copy-paste your extensions array used for the editor:
        return [
            BulletList,
            CodeBlock,
            Document,
            HardBreak,
            ListItem,
            OrderedList,
            Paragraph,
            Text,
            Bold,
            Blockquote,
            Code,
            Italic,
            Underline,
            CustomLinkExtension.configure({
                autolink: true,
                linkOnPaste: true,
                openOnClick: false,
            }),
            LinkBubbleMenuHandler,
            Gapcursor,
            HeadingWithAnchor,
            TextAlign.configure({
                types: ['heading', 'paragraph', 'image'],
            }),
            TextStyle,
            Color,
            FontSize,
            Highlight.configure({ multicolor: true }),
            HorizontalRule,
            ResizableImage,
            Dropcursor,
            History,
        ];

Choosen color consistency

Hi,
Is there a way to prevent the choosen text color to be reseted to default on pressing return?

I find it pretty non intuitive to have to select again the color on each line change.

Color picker and other tooltip components stays behind when you put the Editor in a Dialog/Modal

Describe the bug

If you put the Editor in a Dialog/Modal the color picker will appear behind the dialog/modal popup

To Reproduce

Just add the Editor in a Dialog or Modal and the color picker tooltip will stay behind the popup

https://codesandbox.io/p/devbox/mui-tiptap-demo-forked-vmndxj

Steps to reproduce the behavior:

  1. Click on Open Dialog
  2. Try to change the text Color

Expected behavior

The tooltips should stay in front of the Dialog

Screenshots

image

System (please complete the following information)

  • mui-tiptap version: [1.8.6]
  • tiptap version: [2.3.0]
  • Browser: [Chrome]
  • Node version: [20.12.0]
  • OS: [Windows 11]

I used the Demo

The cursor jumps to the end of the content when using React Hook Form

Describe the bug

When I try to use RichTextEditor with React Hook Form and change the form value via onUpdate prop with HTML format, the cursor jumps to the end of the content each time.

To Reproduce

Steps to reproduce the behavior:

  1. start typing

Expected behavior

The content should be updated, and the cursor should stay in the same position

Screenshots

Link to Reproduce

System (please complete the following information):

  • mui-tiptap version: [1.0.0-beta.9]
  • tiptap version: [2.0.3]
  • Browser: [Chrome]
  • Node version: [16.18.1]
  • OS: [macOS 13.4]

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.