@tiptap/react 2.5.8 → 3.0.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
+ import { type ArrowOptions, type AutoPlacementOptions, type FlipOptions, type HideOptions, type InlineOptions, type OffsetOptions, type Placement, type ShiftOptions, type SizeOptions, type Strategy } from '@floating-ui/dom';
1
2
  import { Editor } from '@tiptap/core';
2
3
  import { EditorState, Plugin, PluginKey } from '@tiptap/pm/state';
3
4
  import { EditorView } from '@tiptap/pm/view';
4
- import { Instance, Props } from 'tippy.js';
5
5
  export interface BubbleMenuPluginProps {
6
6
  /**
7
7
  * The plugin key.
@@ -19,11 +19,6 @@ export interface BubbleMenuPluginProps {
19
19
  * @default null
20
20
  */
21
21
  element: HTMLElement;
22
- /**
23
- * The options for the tippy.js instance.
24
- * @see https://atomiks.github.io/tippyjs/v6/all-props/
25
- */
26
- tippyOptions?: Partial<Props>;
27
22
  /**
28
23
  * The delay in milliseconds before the menu should be updated.
29
24
  * This can be useful to prevent performance issues.
@@ -31,11 +26,18 @@ export interface BubbleMenuPluginProps {
31
26
  * @default 250
32
27
  */
33
28
  updateDelay?: number;
29
+ /**
30
+ * The delay in milliseconds before the menu position should be updated on window resize.
31
+ * This can be useful to prevent performance issues.
32
+ * @type {number}
33
+ * @default 60
34
+ */
35
+ resizeDelay?: number;
34
36
  /**
35
37
  * A function that determines whether the menu should be shown or not.
36
38
  * If this function returns `false`, the menu will be hidden, otherwise it will be shown.
37
39
  */
38
- shouldShow?: ((props: {
40
+ shouldShow: ((props: {
39
41
  editor: Editor;
40
42
  view: EditorView;
41
43
  state: EditorState;
@@ -43,6 +45,21 @@ export interface BubbleMenuPluginProps {
43
45
  from: number;
44
46
  to: number;
45
47
  }) => boolean) | null;
48
+ /**
49
+ * FloatingUI options.
50
+ */
51
+ options?: {
52
+ strategy?: Strategy;
53
+ placement?: Placement;
54
+ offset?: OffsetOptions | boolean;
55
+ flip?: FlipOptions | boolean;
56
+ shift?: ShiftOptions | boolean;
57
+ arrow?: ArrowOptions | false;
58
+ size?: SizeOptions | boolean;
59
+ autoPlacement?: AutoPlacementOptions | boolean;
60
+ hide?: HideOptions | boolean;
61
+ inline?: InlineOptions | boolean;
62
+ };
46
63
  }
47
64
  export type BubbleMenuViewProps = BubbleMenuPluginProps & {
48
65
  view: EditorView;
@@ -52,22 +69,28 @@ export declare class BubbleMenuView {
52
69
  element: HTMLElement;
53
70
  view: EditorView;
54
71
  preventHide: boolean;
55
- tippy: Instance | undefined;
56
- tippyOptions?: Partial<Props>;
57
72
  updateDelay: number;
73
+ resizeDelay: number;
58
74
  private updateDebounceTimer;
75
+ private resizeDebounceTimer;
76
+ private floatingUIOptions;
59
77
  shouldShow: Exclude<BubbleMenuPluginProps['shouldShow'], null>;
60
- constructor({ editor, element, view, tippyOptions, updateDelay, shouldShow, }: BubbleMenuViewProps);
78
+ get middlewares(): {
79
+ name: string;
80
+ options?: any;
81
+ fn: (state: import("@floating-ui/dom").MiddlewareState) => import("@floating-ui/core").MiddlewareReturn | Promise<import("@floating-ui/core").MiddlewareReturn>;
82
+ }[];
83
+ constructor({ editor, element, view, updateDelay, resizeDelay, shouldShow, options, }: BubbleMenuViewProps);
61
84
  mousedownHandler: () => void;
62
85
  dragstartHandler: () => void;
63
86
  focusHandler: () => void;
64
87
  blurHandler: ({ event }: {
65
88
  event: FocusEvent;
66
89
  }) => void;
67
- tippyBlurHandler: (event: FocusEvent) => void;
68
- createTooltip(): void;
90
+ updatePosition(): void;
69
91
  update(view: EditorView, oldState?: EditorState): void;
70
92
  handleDebouncedUpdate: (view: EditorView, oldState?: EditorState) => void;
93
+ getShouldShow(oldState?: EditorState): boolean;
71
94
  updateHandler: (view: EditorView, selectionChanged: boolean, docChanged: boolean, oldState?: EditorState) => void;
72
95
  show(): void;
73
96
  hide(): void;
@@ -1,7 +1,7 @@
1
+ import { type ArrowOptions, type AutoPlacementOptions, type FlipOptions, type HideOptions, type InlineOptions, type OffsetOptions, type Placement, type ShiftOptions, type SizeOptions, type Strategy } from '@floating-ui/dom';
1
2
  import { Editor } from '@tiptap/core';
2
3
  import { EditorState, Plugin, PluginKey } from '@tiptap/pm/state';
3
4
  import { EditorView } from '@tiptap/pm/view';
4
- import { Instance, Props } from 'tippy.js';
5
5
  export interface FloatingMenuPluginProps {
6
6
  /**
7
7
  * The plugin key for the floating menu.
@@ -18,23 +18,33 @@ export interface FloatingMenuPluginProps {
18
18
  * @default null
19
19
  */
20
20
  element: HTMLElement;
21
- /**
22
- * The options for the tippy instance.
23
- * @default {}
24
- * @see https://atomiks.github.io/tippyjs/v6/all-props/
25
- */
26
- tippyOptions?: Partial<Props>;
27
21
  /**
28
22
  * A function that determines whether the menu should be shown or not.
29
23
  * If this function returns `false`, the menu will be hidden, otherwise it will be shown.
30
- * @default null
31
24
  */
32
- shouldShow?: ((props: {
25
+ shouldShow: ((props: {
33
26
  editor: Editor;
34
27
  view: EditorView;
35
28
  state: EditorState;
36
29
  oldState?: EditorState;
30
+ from: number;
31
+ to: number;
37
32
  }) => boolean) | null;
33
+ /**
34
+ * FloatingUI options.
35
+ */
36
+ options?: {
37
+ strategy?: Strategy;
38
+ placement?: Placement;
39
+ offset?: OffsetOptions | boolean;
40
+ flip?: FlipOptions | boolean;
41
+ shift?: ShiftOptions | boolean;
42
+ arrow?: ArrowOptions | false;
43
+ size?: SizeOptions | boolean;
44
+ autoPlacement?: AutoPlacementOptions | boolean;
45
+ hide?: HideOptions | boolean;
46
+ inline?: InlineOptions | boolean;
47
+ };
38
48
  }
39
49
  export type FloatingMenuViewProps = FloatingMenuPluginProps & {
40
50
  /**
@@ -47,17 +57,22 @@ export declare class FloatingMenuView {
47
57
  element: HTMLElement;
48
58
  view: EditorView;
49
59
  preventHide: boolean;
50
- tippy: Instance | undefined;
51
- tippyOptions?: Partial<Props>;
52
60
  shouldShow: Exclude<FloatingMenuPluginProps['shouldShow'], null>;
53
- constructor({ editor, element, view, tippyOptions, shouldShow, }: FloatingMenuViewProps);
61
+ private floatingUIOptions;
62
+ get middlewares(): {
63
+ name: string;
64
+ options?: any;
65
+ fn: (state: import("@floating-ui/dom").MiddlewareState) => import("@floating-ui/core").MiddlewareReturn | Promise<import("@floating-ui/core").MiddlewareReturn>;
66
+ }[];
67
+ constructor({ editor, element, view, options, shouldShow, }: FloatingMenuViewProps);
68
+ getShouldShow(oldState?: EditorState): boolean;
69
+ updateHandler: (view: EditorView, selectionChanged: boolean, docChanged: boolean, oldState?: EditorState) => void;
54
70
  mousedownHandler: () => void;
55
71
  focusHandler: () => void;
56
72
  blurHandler: ({ event }: {
57
73
  event: FocusEvent;
58
74
  }) => void;
59
- tippyBlurHandler: (event: FocusEvent) => void;
60
- createTooltip(): void;
75
+ updatePosition(): void;
61
76
  update(view: EditorView, oldState?: EditorState): void;
62
77
  show(): void;
63
78
  hide(): void;
@@ -6,6 +6,8 @@ export type BubbleMenuProps = Omit<Optional<BubbleMenuPluginProps, 'pluginKey'>,
6
6
  className?: string;
7
7
  children: React.ReactNode;
8
8
  updateDelay?: number;
9
+ resizeDelay?: number;
10
+ options?: BubbleMenuPluginProps['options'];
9
11
  };
10
12
  export declare const BubbleMenu: (props: BubbleMenuProps) => React.JSX.Element;
11
13
  export {};
@@ -5,6 +5,7 @@ export type FloatingMenuProps = Omit<Optional<FloatingMenuPluginProps, 'pluginKe
5
5
  editor: FloatingMenuPluginProps['editor'] | null;
6
6
  className?: string;
7
7
  children: React.ReactNode;
8
+ options?: FloatingMenuPluginProps['options'];
8
9
  };
9
10
  export declare const FloatingMenu: (props: FloatingMenuProps) => React.JSX.Element;
10
11
  export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tiptap/react",
3
3
  "description": "React components for tiptap",
4
- "version": "2.5.8",
4
+ "version": "3.0.0-next.0",
5
5
  "homepage": "https://tiptap.dev",
6
6
  "keywords": [
7
7
  "tiptap",
@@ -29,22 +29,22 @@
29
29
  "dist"
30
30
  ],
31
31
  "dependencies": {
32
- "@tiptap/extension-bubble-menu": "^2.5.8",
33
- "@tiptap/extension-floating-menu": "^2.5.8",
32
+ "@tiptap/extension-bubble-menu": "^3.0.0-next.0",
33
+ "@tiptap/extension-floating-menu": "^3.0.0-next.0",
34
34
  "@types/use-sync-external-store": "^0.0.6",
35
35
  "use-sync-external-store": "^1.2.2"
36
36
  },
37
37
  "devDependencies": {
38
- "@tiptap/core": "^2.5.8",
39
- "@tiptap/pm": "^2.5.8",
38
+ "@tiptap/core": "^3.0.0-next.0",
39
+ "@tiptap/pm": "^3.0.0-next.0",
40
40
  "@types/react": "^18.2.14",
41
41
  "@types/react-dom": "^18.2.6",
42
42
  "react": "^18.0.0",
43
43
  "react-dom": "^18.0.0"
44
44
  },
45
45
  "peerDependencies": {
46
- "@tiptap/core": "^2.5.8",
47
- "@tiptap/pm": "^2.5.8",
46
+ "@tiptap/core": "^3.0.0-next.0",
47
+ "@tiptap/pm": "^3.0.0-next.0",
48
48
  "react": "^17.0.0 || ^18.0.0",
49
49
  "react-dom": "^17.0.0 || ^18.0.0"
50
50
  },
@@ -1,5 +1,6 @@
1
1
  import { BubbleMenuPlugin, BubbleMenuPluginProps } from '@tiptap/extension-bubble-menu'
2
- import React, { useEffect, useState } from 'react'
2
+ import React, { useEffect, useRef } from 'react'
3
+ import { createPortal } from 'react-dom'
3
4
 
4
5
  import { useCurrentEditor } from './Context.js'
5
6
 
@@ -10,23 +11,24 @@ export type BubbleMenuProps = Omit<Optional<BubbleMenuPluginProps, 'pluginKey'>,
10
11
  className?: string;
11
12
  children: React.ReactNode;
12
13
  updateDelay?: number;
14
+ resizeDelay?: number;
15
+ options?: BubbleMenuPluginProps['options'];
13
16
  };
14
17
 
15
18
  export const BubbleMenu = (props: BubbleMenuProps) => {
16
- const [element, setElement] = useState<HTMLDivElement | null>(null)
19
+ const menuEl = useRef(document.createElement('div'))
17
20
  const { editor: currentEditor } = useCurrentEditor()
18
21
 
19
22
  useEffect(() => {
20
- if (!element) {
21
- return
22
- }
23
+ menuEl.current.style.visibility = 'hidden'
24
+ menuEl.current.style.position = 'absolute'
23
25
 
24
26
  if (props.editor?.isDestroyed || currentEditor?.isDestroyed) {
25
27
  return
26
28
  }
27
29
 
28
30
  const {
29
- pluginKey = 'bubbleMenu', editor, tippyOptions = {}, updateDelay, shouldShow = null,
31
+ pluginKey = 'bubbleMenu', editor, updateDelay, resizeDelay, shouldShow = null,
30
32
  } = props
31
33
 
32
34
  const menuEditor = editor || currentEditor
@@ -38,20 +40,35 @@ export const BubbleMenu = (props: BubbleMenuProps) => {
38
40
 
39
41
  const plugin = BubbleMenuPlugin({
40
42
  updateDelay,
43
+ resizeDelay,
41
44
  editor: menuEditor,
42
- element,
45
+ element: menuEl.current,
43
46
  pluginKey,
44
47
  shouldShow,
45
- tippyOptions,
48
+ options: props.options,
46
49
  })
47
50
 
48
51
  menuEditor.registerPlugin(plugin)
49
- return () => menuEditor.unregisterPlugin(pluginKey)
50
- }, [props.editor, currentEditor, element])
51
52
 
52
- return (
53
- <div ref={setElement} className={props.className} style={{ visibility: 'hidden' }}>
53
+ return () => {
54
+ menuEditor.unregisterPlugin(pluginKey)
55
+ window.requestAnimationFrame(() => {
56
+ if (menuEl.current.parentNode) {
57
+ menuEl.current.parentNode.removeChild(menuEl.current)
58
+ }
59
+ })
60
+ }
61
+ }, [props.editor, currentEditor])
62
+
63
+ const portal = createPortal(
64
+ (
65
+ <div className={props.className}>
54
66
  {props.children}
55
67
  </div>
68
+ ), menuEl.current,
69
+ )
70
+
71
+ return (
72
+ <>{portal}</>
56
73
  )
57
74
  }
@@ -1,7 +1,8 @@
1
1
  import { FloatingMenuPlugin, FloatingMenuPluginProps } from '@tiptap/extension-floating-menu'
2
2
  import React, {
3
- useEffect, useState,
3
+ useEffect, useRef,
4
4
  } from 'react'
5
+ import { createPortal } from 'react-dom'
5
6
 
6
7
  import { useCurrentEditor } from './Context.js'
7
8
 
@@ -11,16 +12,16 @@ export type FloatingMenuProps = Omit<Optional<FloatingMenuPluginProps, 'pluginKe
11
12
  editor: FloatingMenuPluginProps['editor'] | null;
12
13
  className?: string,
13
14
  children: React.ReactNode
15
+ options?: FloatingMenuPluginProps['options']
14
16
  }
15
17
 
16
18
  export const FloatingMenu = (props: FloatingMenuProps) => {
17
- const [element, setElement] = useState<HTMLDivElement | null>(null)
19
+ const menuEl = useRef(document.createElement('div'))
18
20
  const { editor: currentEditor } = useCurrentEditor()
19
21
 
20
22
  useEffect(() => {
21
- if (!element) {
22
- return
23
- }
23
+ menuEl.current.style.visibility = 'hidden'
24
+ menuEl.current.style.position = 'absolute'
24
25
 
25
26
  if (props.editor?.isDestroyed || currentEditor?.isDestroyed) {
26
27
  return
@@ -29,7 +30,7 @@ export const FloatingMenu = (props: FloatingMenuProps) => {
29
30
  const {
30
31
  pluginKey = 'floatingMenu',
31
32
  editor,
32
- tippyOptions = {},
33
+ options,
33
34
  shouldShow = null,
34
35
  } = props
35
36
 
@@ -43,22 +44,34 @@ export const FloatingMenu = (props: FloatingMenuProps) => {
43
44
  const plugin = FloatingMenuPlugin({
44
45
  pluginKey,
45
46
  editor: menuEditor,
46
- element,
47
- tippyOptions,
47
+ element: menuEl.current,
48
+ options,
48
49
  shouldShow,
49
50
  })
50
51
 
51
52
  menuEditor.registerPlugin(plugin)
52
- return () => menuEditor.unregisterPlugin(pluginKey)
53
+ return () => {
54
+ menuEditor.unregisterPlugin(pluginKey)
55
+ window.requestAnimationFrame(() => {
56
+ if (menuEl.current.parentNode) {
57
+ menuEl.current.parentNode.removeChild(menuEl.current)
58
+ }
59
+ })
60
+ }
53
61
  }, [
54
62
  props.editor,
55
63
  currentEditor,
56
- element,
57
64
  ])
58
65
 
59
- return (
60
- <div ref={setElement} className={props.className} style={{ visibility: 'hidden' }}>
66
+ const portal = createPortal(
67
+ (
68
+ <div className={props.className}>
61
69
  {props.children}
62
70
  </div>
71
+ ), menuEl.current,
72
+ )
73
+
74
+ return (
75
+ <>{portal}</>
63
76
  )
64
77
  }