@blockslides/react-prebuilts 0.1.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.
- package/LICENSE.md +36 -0
- package/dist/index.cjs +381 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +86 -0
- package/dist/index.d.ts +86 -0
- package/dist/index.js +354 -0
- package/dist/index.js.map +1 -0
- package/dist/menus/index.cjs +281 -0
- package/dist/menus/index.cjs.map +1 -0
- package/dist/menus/index.d.cts +28 -0
- package/dist/menus/index.d.ts +28 -0
- package/dist/menus/index.js +252 -0
- package/dist/menus/index.js.map +1 -0
- package/package.json +76 -0
- package/src/SlideEditor.tsx +73 -0
- package/src/index.ts +8 -0
- package/src/menus/BubbleMenu.tsx +89 -0
- package/src/menus/BubbleMenuPreset.tsx +144 -0
- package/src/menus/FloatingMenu.tsx +69 -0
- package/src/menus/index.ts +3 -0
- package/src/useSlideEditor.ts +250 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/menus/index.ts","../../src/menus/BubbleMenu.tsx","../../src/menus/FloatingMenu.tsx","../../src/menus/BubbleMenuPreset.tsx"],"sourcesContent":["export * from './BubbleMenu.js'\nexport * from './FloatingMenu.js'\nexport * from './BubbleMenuPreset.js'\n","import { type BubbleMenuPluginProps, BubbleMenuPlugin } from '@blockslides/extension-bubble-menu'\nimport { useCurrentEditor } from '@blockslides/react'\nimport React, { useEffect, useRef } from 'react'\nimport { createPortal } from 'react-dom'\n\ntype Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>\n\nexport type BubbleMenuProps = Optional<Omit<Optional<BubbleMenuPluginProps, 'pluginKey'>, 'element'>, 'editor'> &\n React.HTMLAttributes<HTMLDivElement>\n\nexport const BubbleMenu = React.forwardRef<HTMLDivElement, BubbleMenuProps>(\n (\n {\n pluginKey = 'bubbleMenu',\n editor,\n updateDelay,\n resizeDelay,\n appendTo,\n shouldShow = null,\n getReferencedVirtualElement,\n options,\n children,\n ...restProps\n },\n ref,\n ) => {\n const menuEl = useRef(document.createElement('div'))\n\n if (typeof ref === 'function') {\n ref(menuEl.current)\n } else if (ref) {\n ref.current = menuEl.current\n }\n\n const { editor: currentEditor } = useCurrentEditor()\n\n useEffect(() => {\n const bubbleMenuElement = menuEl.current\n bubbleMenuElement.style.visibility = 'hidden'\n bubbleMenuElement.style.position = 'absolute'\n\n if (editor?.isDestroyed || (currentEditor as any)?.isDestroyed) {\n return\n }\n\n const attachToEditor = editor || currentEditor\n\n if (!attachToEditor) {\n console.warn('BubbleMenu component is not rendered inside of an editor component or does not have editor prop.')\n return\n }\n\n const plugin = BubbleMenuPlugin({\n updateDelay,\n resizeDelay,\n editor: attachToEditor,\n element: bubbleMenuElement,\n appendTo,\n pluginKey,\n shouldShow,\n getReferencedVirtualElement,\n options,\n })\n\n attachToEditor.registerPlugin(plugin)\n\n return () => {\n attachToEditor.unregisterPlugin(pluginKey)\n window.requestAnimationFrame(() => {\n if (bubbleMenuElement.parentNode) {\n bubbleMenuElement.parentNode.removeChild(bubbleMenuElement)\n }\n })\n }\n }, [\n editor,\n currentEditor,\n pluginKey,\n updateDelay,\n resizeDelay,\n appendTo,\n shouldShow,\n getReferencedVirtualElement,\n options,\n ])\n\n return createPortal(<div {...restProps}>{children}</div>, menuEl.current)\n },\n)\n","import type { FloatingMenuPluginProps } from '@blockslides/extension-floating-menu'\nimport { FloatingMenuPlugin } from '@blockslides/extension-floating-menu'\nimport { useCurrentEditor } from '@blockslides/react'\nimport React, { useEffect, useRef } from 'react'\nimport { createPortal } from 'react-dom'\n\ntype Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>\n\nexport type FloatingMenuProps = Omit<Optional<FloatingMenuPluginProps, 'pluginKey'>, 'element' | 'editor'> & {\n editor: FloatingMenuPluginProps['editor'] | null\n options?: FloatingMenuPluginProps['options']\n} & React.HTMLAttributes<HTMLDivElement>\n\nexport const FloatingMenu = React.forwardRef<HTMLDivElement, FloatingMenuProps>(\n ({ pluginKey = 'floatingMenu', editor, appendTo, shouldShow = null, options, children, ...restProps }, ref) => {\n const menuEl = useRef(document.createElement('div'))\n\n if (typeof ref === 'function') {\n ref(menuEl.current)\n } else if (ref) {\n ref.current = menuEl.current\n }\n\n const { editor: currentEditor } = useCurrentEditor()\n\n useEffect(() => {\n const floatingMenuElement = menuEl.current\n\n floatingMenuElement.style.visibility = 'hidden'\n floatingMenuElement.style.position = 'absolute'\n\n if (editor?.isDestroyed || (currentEditor as any)?.isDestroyed) {\n return\n }\n\n const attachToEditor = editor || currentEditor\n\n if (!attachToEditor) {\n console.warn(\n 'FloatingMenu component is not rendered inside of an editor component or does not have editor prop.',\n )\n return\n }\n\n const plugin = FloatingMenuPlugin({\n editor: attachToEditor,\n element: floatingMenuElement,\n pluginKey,\n appendTo,\n shouldShow,\n options,\n })\n\n attachToEditor.registerPlugin(plugin)\n\n return () => {\n attachToEditor.unregisterPlugin(pluginKey)\n window.requestAnimationFrame(() => {\n if (floatingMenuElement.parentNode) {\n floatingMenuElement.parentNode.removeChild(floatingMenuElement)\n }\n })\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [editor, currentEditor, appendTo, pluginKey, shouldShow, options])\n\n return createPortal(<div {...restProps}>{children}</div>, menuEl.current)\n },\n)\n","import React, { useEffect, useRef } from 'react'\nimport { createPortal } from 'react-dom'\nimport { useCurrentEditor } from '@blockslides/react'\nimport {\n BubbleMenuPlugin,\n type BubbleMenuPluginProps,\n} from '@blockslides/extension-bubble-menu'\nimport { NodeSelection } from '@blockslides/pm/state'\nimport {\n type BubbleMenuPresetOptions,\n buildMenuElement,\n DEFAULT_ITEMS,\n DEFAULT_COLOR_PALETTE,\n DEFAULT_HIGHLIGHT_PALETTE,\n DEFAULT_FONTS,\n DEFAULT_FONT_SIZES,\n DEFAULT_ALIGNMENTS,\n} from '@blockslides/extension-bubble-menu-preset'\nimport { isNodeSelection, isTextSelection, type Editor } from '@blockslides/core'\n\nexport type BubbleMenuPresetProps = Omit<BubbleMenuPresetOptions, 'element' | 'pluginKey'> &\n React.HTMLAttributes<HTMLElement> & {\n editor?: Editor | null\n }\n\nexport const BubbleMenuPreset = React.forwardRef<HTMLElement, BubbleMenuPresetProps>(\n (\n {\n editor,\n updateDelay,\n resizeDelay,\n appendTo,\n shouldShow,\n getReferencedVirtualElement,\n options,\n items,\n className,\n injectStyles,\n textColors,\n highlightColors,\n fonts,\n fontSizes,\n alignments,\n onTextAction,\n onImageReplace,\n ...rest\n },\n ref,\n ) => {\n const pluginKey = 'bubbleMenuPreset'\n const { editor: currentEditor } = useCurrentEditor()\n const menuEl = useRef<HTMLElement | null>(null)\n\n useEffect(() => {\n const attachToEditor = (editor || currentEditor) as Editor | null\n if (!attachToEditor || (attachToEditor as any).isDestroyed) {\n return\n }\n\n const { element, cleanup } = buildMenuElement(attachToEditor, {\n items: items ?? DEFAULT_ITEMS,\n className: className ?? '',\n injectStyles: injectStyles !== false,\n textColors: textColors ?? DEFAULT_COLOR_PALETTE,\n highlightColors: highlightColors ?? DEFAULT_HIGHLIGHT_PALETTE,\n fonts: fonts ?? DEFAULT_FONTS,\n fontSizes: fontSizes ?? DEFAULT_FONT_SIZES,\n alignments: alignments ?? DEFAULT_ALIGNMENTS,\n onTextAction,\n onImageReplace,\n })\n\n menuEl.current = element\n\n if (typeof ref === 'function') {\n ref(element)\n } else if (ref) {\n ;(ref as React.MutableRefObject<HTMLElement | null>).current = element\n }\n\n const defaultShouldShow: Exclude<BubbleMenuPluginProps['shouldShow'], null> = ({\n state,\n editor,\n }) => {\n const sel = state.selection\n const imageSelection =\n (sel instanceof NodeSelection &&\n ['image', 'imageBlock'].includes((sel as any).node?.type?.name)) ||\n editor.isActive('image') ||\n editor.isActive('imageBlock')\n\n if (imageSelection) return true\n if (isTextSelection(sel) && !sel.empty && !imageSelection) return true\n return false\n }\n\n const plugin = BubbleMenuPlugin({\n editor: attachToEditor,\n element,\n updateDelay,\n resizeDelay,\n appendTo,\n pluginKey,\n shouldShow: shouldShow ?? defaultShouldShow,\n getReferencedVirtualElement,\n options,\n })\n\n attachToEditor.registerPlugin(plugin)\n\n return () => {\n attachToEditor.unregisterPlugin(pluginKey)\n cleanup?.()\n if (element.parentNode) {\n element.parentNode.removeChild(element)\n }\n }\n }, [\n editor,\n currentEditor,\n updateDelay,\n resizeDelay,\n appendTo,\n shouldShow,\n getReferencedVirtualElement,\n options,\n items,\n className,\n injectStyles,\n textColors,\n highlightColors,\n fonts,\n fontSizes,\n alignments,\n onTextAction,\n onImageReplace,\n ref,\n ])\n\n return menuEl.current ? createPortal(<div {...rest} />, menuEl.current) : null\n },\n)\n\nBubbleMenuPreset.displayName = 'BubbleMenuPreset'\n"],"mappings":"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","names":["import_react","React","import_react","import_react_dom","import_jsx_runtime","React","import_react","import_react_dom","import_extension_bubble_menu","import_jsx_runtime","React","editor"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { BubbleMenuPluginProps } from '@blockslides/extension-bubble-menu';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { FloatingMenuPluginProps } from '@blockslides/extension-floating-menu';
|
|
4
|
+
import { BubbleMenuPresetOptions } from '@blockslides/extension-bubble-menu-preset';
|
|
5
|
+
import { Editor } from '@blockslides/core';
|
|
6
|
+
|
|
7
|
+
type Optional$1<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
|
|
8
|
+
type BubbleMenuProps = Optional$1<Omit<Optional$1<BubbleMenuPluginProps, 'pluginKey'>, 'element'>, 'editor'> & React.HTMLAttributes<HTMLDivElement>;
|
|
9
|
+
declare const BubbleMenu: React.ForwardRefExoticComponent<Pick<Partial<Omit<Optional$1<BubbleMenuPluginProps, "pluginKey">, "element">>, "editor"> & Omit<Omit<Optional$1<BubbleMenuPluginProps, "pluginKey">, "element">, "editor"> & React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
|
|
10
|
+
|
|
11
|
+
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
|
|
12
|
+
type FloatingMenuProps = Omit<Optional<FloatingMenuPluginProps, 'pluginKey'>, 'element' | 'editor'> & {
|
|
13
|
+
editor: FloatingMenuPluginProps['editor'] | null;
|
|
14
|
+
options?: FloatingMenuPluginProps['options'];
|
|
15
|
+
} & React.HTMLAttributes<HTMLDivElement>;
|
|
16
|
+
declare const FloatingMenu: React.ForwardRefExoticComponent<Omit<Optional<FloatingMenuPluginProps, "pluginKey">, "element" | "editor"> & {
|
|
17
|
+
editor: FloatingMenuPluginProps["editor"] | null;
|
|
18
|
+
options?: FloatingMenuPluginProps["options"];
|
|
19
|
+
} & React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
|
|
20
|
+
|
|
21
|
+
type BubbleMenuPresetProps = Omit<BubbleMenuPresetOptions, 'element' | 'pluginKey'> & React.HTMLAttributes<HTMLElement> & {
|
|
22
|
+
editor?: Editor | null;
|
|
23
|
+
};
|
|
24
|
+
declare const BubbleMenuPreset: React.ForwardRefExoticComponent<Omit<BubbleMenuPresetOptions, "element" | "pluginKey"> & React.HTMLAttributes<HTMLElement> & {
|
|
25
|
+
editor?: Editor | null;
|
|
26
|
+
} & React.RefAttributes<HTMLElement>>;
|
|
27
|
+
|
|
28
|
+
export { BubbleMenu, BubbleMenuPreset, type BubbleMenuPresetProps, type BubbleMenuProps, FloatingMenu, type FloatingMenuProps };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { BubbleMenuPluginProps } from '@blockslides/extension-bubble-menu';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { FloatingMenuPluginProps } from '@blockslides/extension-floating-menu';
|
|
4
|
+
import { BubbleMenuPresetOptions } from '@blockslides/extension-bubble-menu-preset';
|
|
5
|
+
import { Editor } from '@blockslides/core';
|
|
6
|
+
|
|
7
|
+
type Optional$1<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
|
|
8
|
+
type BubbleMenuProps = Optional$1<Omit<Optional$1<BubbleMenuPluginProps, 'pluginKey'>, 'element'>, 'editor'> & React.HTMLAttributes<HTMLDivElement>;
|
|
9
|
+
declare const BubbleMenu: React.ForwardRefExoticComponent<Pick<Partial<Omit<Optional$1<BubbleMenuPluginProps, "pluginKey">, "element">>, "editor"> & Omit<Omit<Optional$1<BubbleMenuPluginProps, "pluginKey">, "element">, "editor"> & React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
|
|
10
|
+
|
|
11
|
+
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
|
|
12
|
+
type FloatingMenuProps = Omit<Optional<FloatingMenuPluginProps, 'pluginKey'>, 'element' | 'editor'> & {
|
|
13
|
+
editor: FloatingMenuPluginProps['editor'] | null;
|
|
14
|
+
options?: FloatingMenuPluginProps['options'];
|
|
15
|
+
} & React.HTMLAttributes<HTMLDivElement>;
|
|
16
|
+
declare const FloatingMenu: React.ForwardRefExoticComponent<Omit<Optional<FloatingMenuPluginProps, "pluginKey">, "element" | "editor"> & {
|
|
17
|
+
editor: FloatingMenuPluginProps["editor"] | null;
|
|
18
|
+
options?: FloatingMenuPluginProps["options"];
|
|
19
|
+
} & React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
|
|
20
|
+
|
|
21
|
+
type BubbleMenuPresetProps = Omit<BubbleMenuPresetOptions, 'element' | 'pluginKey'> & React.HTMLAttributes<HTMLElement> & {
|
|
22
|
+
editor?: Editor | null;
|
|
23
|
+
};
|
|
24
|
+
declare const BubbleMenuPreset: React.ForwardRefExoticComponent<Omit<BubbleMenuPresetOptions, "element" | "pluginKey"> & React.HTMLAttributes<HTMLElement> & {
|
|
25
|
+
editor?: Editor | null;
|
|
26
|
+
} & React.RefAttributes<HTMLElement>>;
|
|
27
|
+
|
|
28
|
+
export { BubbleMenu, BubbleMenuPreset, type BubbleMenuPresetProps, type BubbleMenuProps, FloatingMenu, type FloatingMenuProps };
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
// src/menus/BubbleMenu.tsx
|
|
2
|
+
import { BubbleMenuPlugin } from "@blockslides/extension-bubble-menu";
|
|
3
|
+
import { useCurrentEditor } from "@blockslides/react";
|
|
4
|
+
import React, { useEffect, useRef } from "react";
|
|
5
|
+
import { createPortal } from "react-dom";
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
var BubbleMenu = React.forwardRef(
|
|
8
|
+
({
|
|
9
|
+
pluginKey = "bubbleMenu",
|
|
10
|
+
editor,
|
|
11
|
+
updateDelay,
|
|
12
|
+
resizeDelay,
|
|
13
|
+
appendTo,
|
|
14
|
+
shouldShow = null,
|
|
15
|
+
getReferencedVirtualElement,
|
|
16
|
+
options,
|
|
17
|
+
children,
|
|
18
|
+
...restProps
|
|
19
|
+
}, ref) => {
|
|
20
|
+
const menuEl = useRef(document.createElement("div"));
|
|
21
|
+
if (typeof ref === "function") {
|
|
22
|
+
ref(menuEl.current);
|
|
23
|
+
} else if (ref) {
|
|
24
|
+
ref.current = menuEl.current;
|
|
25
|
+
}
|
|
26
|
+
const { editor: currentEditor } = useCurrentEditor();
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
const bubbleMenuElement = menuEl.current;
|
|
29
|
+
bubbleMenuElement.style.visibility = "hidden";
|
|
30
|
+
bubbleMenuElement.style.position = "absolute";
|
|
31
|
+
if ((editor == null ? void 0 : editor.isDestroyed) || (currentEditor == null ? void 0 : currentEditor.isDestroyed)) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const attachToEditor = editor || currentEditor;
|
|
35
|
+
if (!attachToEditor) {
|
|
36
|
+
console.warn("BubbleMenu component is not rendered inside of an editor component or does not have editor prop.");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const plugin = BubbleMenuPlugin({
|
|
40
|
+
updateDelay,
|
|
41
|
+
resizeDelay,
|
|
42
|
+
editor: attachToEditor,
|
|
43
|
+
element: bubbleMenuElement,
|
|
44
|
+
appendTo,
|
|
45
|
+
pluginKey,
|
|
46
|
+
shouldShow,
|
|
47
|
+
getReferencedVirtualElement,
|
|
48
|
+
options
|
|
49
|
+
});
|
|
50
|
+
attachToEditor.registerPlugin(plugin);
|
|
51
|
+
return () => {
|
|
52
|
+
attachToEditor.unregisterPlugin(pluginKey);
|
|
53
|
+
window.requestAnimationFrame(() => {
|
|
54
|
+
if (bubbleMenuElement.parentNode) {
|
|
55
|
+
bubbleMenuElement.parentNode.removeChild(bubbleMenuElement);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
}, [
|
|
60
|
+
editor,
|
|
61
|
+
currentEditor,
|
|
62
|
+
pluginKey,
|
|
63
|
+
updateDelay,
|
|
64
|
+
resizeDelay,
|
|
65
|
+
appendTo,
|
|
66
|
+
shouldShow,
|
|
67
|
+
getReferencedVirtualElement,
|
|
68
|
+
options
|
|
69
|
+
]);
|
|
70
|
+
return createPortal(/* @__PURE__ */ jsx("div", { ...restProps, children }), menuEl.current);
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
// src/menus/FloatingMenu.tsx
|
|
75
|
+
import { FloatingMenuPlugin } from "@blockslides/extension-floating-menu";
|
|
76
|
+
import { useCurrentEditor as useCurrentEditor2 } from "@blockslides/react";
|
|
77
|
+
import React2, { useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
78
|
+
import { createPortal as createPortal2 } from "react-dom";
|
|
79
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
80
|
+
var FloatingMenu = React2.forwardRef(
|
|
81
|
+
({ pluginKey = "floatingMenu", editor, appendTo, shouldShow = null, options, children, ...restProps }, ref) => {
|
|
82
|
+
const menuEl = useRef2(document.createElement("div"));
|
|
83
|
+
if (typeof ref === "function") {
|
|
84
|
+
ref(menuEl.current);
|
|
85
|
+
} else if (ref) {
|
|
86
|
+
ref.current = menuEl.current;
|
|
87
|
+
}
|
|
88
|
+
const { editor: currentEditor } = useCurrentEditor2();
|
|
89
|
+
useEffect2(() => {
|
|
90
|
+
const floatingMenuElement = menuEl.current;
|
|
91
|
+
floatingMenuElement.style.visibility = "hidden";
|
|
92
|
+
floatingMenuElement.style.position = "absolute";
|
|
93
|
+
if ((editor == null ? void 0 : editor.isDestroyed) || (currentEditor == null ? void 0 : currentEditor.isDestroyed)) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const attachToEditor = editor || currentEditor;
|
|
97
|
+
if (!attachToEditor) {
|
|
98
|
+
console.warn(
|
|
99
|
+
"FloatingMenu component is not rendered inside of an editor component or does not have editor prop."
|
|
100
|
+
);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const plugin = FloatingMenuPlugin({
|
|
104
|
+
editor: attachToEditor,
|
|
105
|
+
element: floatingMenuElement,
|
|
106
|
+
pluginKey,
|
|
107
|
+
appendTo,
|
|
108
|
+
shouldShow,
|
|
109
|
+
options
|
|
110
|
+
});
|
|
111
|
+
attachToEditor.registerPlugin(plugin);
|
|
112
|
+
return () => {
|
|
113
|
+
attachToEditor.unregisterPlugin(pluginKey);
|
|
114
|
+
window.requestAnimationFrame(() => {
|
|
115
|
+
if (floatingMenuElement.parentNode) {
|
|
116
|
+
floatingMenuElement.parentNode.removeChild(floatingMenuElement);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
};
|
|
120
|
+
}, [editor, currentEditor, appendTo, pluginKey, shouldShow, options]);
|
|
121
|
+
return createPortal2(/* @__PURE__ */ jsx2("div", { ...restProps, children }), menuEl.current);
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// src/menus/BubbleMenuPreset.tsx
|
|
126
|
+
import React3, { useEffect as useEffect3, useRef as useRef3 } from "react";
|
|
127
|
+
import { createPortal as createPortal3 } from "react-dom";
|
|
128
|
+
import { useCurrentEditor as useCurrentEditor3 } from "@blockslides/react";
|
|
129
|
+
import {
|
|
130
|
+
BubbleMenuPlugin as BubbleMenuPlugin2
|
|
131
|
+
} from "@blockslides/extension-bubble-menu";
|
|
132
|
+
import { NodeSelection } from "@blockslides/pm/state";
|
|
133
|
+
import {
|
|
134
|
+
buildMenuElement,
|
|
135
|
+
DEFAULT_ITEMS,
|
|
136
|
+
DEFAULT_COLOR_PALETTE,
|
|
137
|
+
DEFAULT_HIGHLIGHT_PALETTE,
|
|
138
|
+
DEFAULT_FONTS,
|
|
139
|
+
DEFAULT_FONT_SIZES,
|
|
140
|
+
DEFAULT_ALIGNMENTS
|
|
141
|
+
} from "@blockslides/extension-bubble-menu-preset";
|
|
142
|
+
import { isTextSelection } from "@blockslides/core";
|
|
143
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
144
|
+
var BubbleMenuPreset = React3.forwardRef(
|
|
145
|
+
({
|
|
146
|
+
editor,
|
|
147
|
+
updateDelay,
|
|
148
|
+
resizeDelay,
|
|
149
|
+
appendTo,
|
|
150
|
+
shouldShow,
|
|
151
|
+
getReferencedVirtualElement,
|
|
152
|
+
options,
|
|
153
|
+
items,
|
|
154
|
+
className,
|
|
155
|
+
injectStyles,
|
|
156
|
+
textColors,
|
|
157
|
+
highlightColors,
|
|
158
|
+
fonts,
|
|
159
|
+
fontSizes,
|
|
160
|
+
alignments,
|
|
161
|
+
onTextAction,
|
|
162
|
+
onImageReplace,
|
|
163
|
+
...rest
|
|
164
|
+
}, ref) => {
|
|
165
|
+
const pluginKey = "bubbleMenuPreset";
|
|
166
|
+
const { editor: currentEditor } = useCurrentEditor3();
|
|
167
|
+
const menuEl = useRef3(null);
|
|
168
|
+
useEffect3(() => {
|
|
169
|
+
const attachToEditor = editor || currentEditor;
|
|
170
|
+
if (!attachToEditor || attachToEditor.isDestroyed) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const { element, cleanup } = buildMenuElement(attachToEditor, {
|
|
174
|
+
items: items != null ? items : DEFAULT_ITEMS,
|
|
175
|
+
className: className != null ? className : "",
|
|
176
|
+
injectStyles: injectStyles !== false,
|
|
177
|
+
textColors: textColors != null ? textColors : DEFAULT_COLOR_PALETTE,
|
|
178
|
+
highlightColors: highlightColors != null ? highlightColors : DEFAULT_HIGHLIGHT_PALETTE,
|
|
179
|
+
fonts: fonts != null ? fonts : DEFAULT_FONTS,
|
|
180
|
+
fontSizes: fontSizes != null ? fontSizes : DEFAULT_FONT_SIZES,
|
|
181
|
+
alignments: alignments != null ? alignments : DEFAULT_ALIGNMENTS,
|
|
182
|
+
onTextAction,
|
|
183
|
+
onImageReplace
|
|
184
|
+
});
|
|
185
|
+
menuEl.current = element;
|
|
186
|
+
if (typeof ref === "function") {
|
|
187
|
+
ref(element);
|
|
188
|
+
} else if (ref) {
|
|
189
|
+
;
|
|
190
|
+
ref.current = element;
|
|
191
|
+
}
|
|
192
|
+
const defaultShouldShow = ({
|
|
193
|
+
state,
|
|
194
|
+
editor: editor2
|
|
195
|
+
}) => {
|
|
196
|
+
var _a, _b;
|
|
197
|
+
const sel = state.selection;
|
|
198
|
+
const imageSelection = sel instanceof NodeSelection && ["image", "imageBlock"].includes((_b = (_a = sel.node) == null ? void 0 : _a.type) == null ? void 0 : _b.name) || editor2.isActive("image") || editor2.isActive("imageBlock");
|
|
199
|
+
if (imageSelection) return true;
|
|
200
|
+
if (isTextSelection(sel) && !sel.empty && !imageSelection) return true;
|
|
201
|
+
return false;
|
|
202
|
+
};
|
|
203
|
+
const plugin = BubbleMenuPlugin2({
|
|
204
|
+
editor: attachToEditor,
|
|
205
|
+
element,
|
|
206
|
+
updateDelay,
|
|
207
|
+
resizeDelay,
|
|
208
|
+
appendTo,
|
|
209
|
+
pluginKey,
|
|
210
|
+
shouldShow: shouldShow != null ? shouldShow : defaultShouldShow,
|
|
211
|
+
getReferencedVirtualElement,
|
|
212
|
+
options
|
|
213
|
+
});
|
|
214
|
+
attachToEditor.registerPlugin(plugin);
|
|
215
|
+
return () => {
|
|
216
|
+
attachToEditor.unregisterPlugin(pluginKey);
|
|
217
|
+
cleanup == null ? void 0 : cleanup();
|
|
218
|
+
if (element.parentNode) {
|
|
219
|
+
element.parentNode.removeChild(element);
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
}, [
|
|
223
|
+
editor,
|
|
224
|
+
currentEditor,
|
|
225
|
+
updateDelay,
|
|
226
|
+
resizeDelay,
|
|
227
|
+
appendTo,
|
|
228
|
+
shouldShow,
|
|
229
|
+
getReferencedVirtualElement,
|
|
230
|
+
options,
|
|
231
|
+
items,
|
|
232
|
+
className,
|
|
233
|
+
injectStyles,
|
|
234
|
+
textColors,
|
|
235
|
+
highlightColors,
|
|
236
|
+
fonts,
|
|
237
|
+
fontSizes,
|
|
238
|
+
alignments,
|
|
239
|
+
onTextAction,
|
|
240
|
+
onImageReplace,
|
|
241
|
+
ref
|
|
242
|
+
]);
|
|
243
|
+
return menuEl.current ? createPortal3(/* @__PURE__ */ jsx3("div", { ...rest }), menuEl.current) : null;
|
|
244
|
+
}
|
|
245
|
+
);
|
|
246
|
+
BubbleMenuPreset.displayName = "BubbleMenuPreset";
|
|
247
|
+
export {
|
|
248
|
+
BubbleMenu,
|
|
249
|
+
BubbleMenuPreset,
|
|
250
|
+
FloatingMenu
|
|
251
|
+
};
|
|
252
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/menus/BubbleMenu.tsx","../../src/menus/FloatingMenu.tsx","../../src/menus/BubbleMenuPreset.tsx"],"sourcesContent":["import { type BubbleMenuPluginProps, BubbleMenuPlugin } from '@blockslides/extension-bubble-menu'\nimport { useCurrentEditor } from '@blockslides/react'\nimport React, { useEffect, useRef } from 'react'\nimport { createPortal } from 'react-dom'\n\ntype Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>\n\nexport type BubbleMenuProps = Optional<Omit<Optional<BubbleMenuPluginProps, 'pluginKey'>, 'element'>, 'editor'> &\n React.HTMLAttributes<HTMLDivElement>\n\nexport const BubbleMenu = React.forwardRef<HTMLDivElement, BubbleMenuProps>(\n (\n {\n pluginKey = 'bubbleMenu',\n editor,\n updateDelay,\n resizeDelay,\n appendTo,\n shouldShow = null,\n getReferencedVirtualElement,\n options,\n children,\n ...restProps\n },\n ref,\n ) => {\n const menuEl = useRef(document.createElement('div'))\n\n if (typeof ref === 'function') {\n ref(menuEl.current)\n } else if (ref) {\n ref.current = menuEl.current\n }\n\n const { editor: currentEditor } = useCurrentEditor()\n\n useEffect(() => {\n const bubbleMenuElement = menuEl.current\n bubbleMenuElement.style.visibility = 'hidden'\n bubbleMenuElement.style.position = 'absolute'\n\n if (editor?.isDestroyed || (currentEditor as any)?.isDestroyed) {\n return\n }\n\n const attachToEditor = editor || currentEditor\n\n if (!attachToEditor) {\n console.warn('BubbleMenu component is not rendered inside of an editor component or does not have editor prop.')\n return\n }\n\n const plugin = BubbleMenuPlugin({\n updateDelay,\n resizeDelay,\n editor: attachToEditor,\n element: bubbleMenuElement,\n appendTo,\n pluginKey,\n shouldShow,\n getReferencedVirtualElement,\n options,\n })\n\n attachToEditor.registerPlugin(plugin)\n\n return () => {\n attachToEditor.unregisterPlugin(pluginKey)\n window.requestAnimationFrame(() => {\n if (bubbleMenuElement.parentNode) {\n bubbleMenuElement.parentNode.removeChild(bubbleMenuElement)\n }\n })\n }\n }, [\n editor,\n currentEditor,\n pluginKey,\n updateDelay,\n resizeDelay,\n appendTo,\n shouldShow,\n getReferencedVirtualElement,\n options,\n ])\n\n return createPortal(<div {...restProps}>{children}</div>, menuEl.current)\n },\n)\n","import type { FloatingMenuPluginProps } from '@blockslides/extension-floating-menu'\nimport { FloatingMenuPlugin } from '@blockslides/extension-floating-menu'\nimport { useCurrentEditor } from '@blockslides/react'\nimport React, { useEffect, useRef } from 'react'\nimport { createPortal } from 'react-dom'\n\ntype Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>\n\nexport type FloatingMenuProps = Omit<Optional<FloatingMenuPluginProps, 'pluginKey'>, 'element' | 'editor'> & {\n editor: FloatingMenuPluginProps['editor'] | null\n options?: FloatingMenuPluginProps['options']\n} & React.HTMLAttributes<HTMLDivElement>\n\nexport const FloatingMenu = React.forwardRef<HTMLDivElement, FloatingMenuProps>(\n ({ pluginKey = 'floatingMenu', editor, appendTo, shouldShow = null, options, children, ...restProps }, ref) => {\n const menuEl = useRef(document.createElement('div'))\n\n if (typeof ref === 'function') {\n ref(menuEl.current)\n } else if (ref) {\n ref.current = menuEl.current\n }\n\n const { editor: currentEditor } = useCurrentEditor()\n\n useEffect(() => {\n const floatingMenuElement = menuEl.current\n\n floatingMenuElement.style.visibility = 'hidden'\n floatingMenuElement.style.position = 'absolute'\n\n if (editor?.isDestroyed || (currentEditor as any)?.isDestroyed) {\n return\n }\n\n const attachToEditor = editor || currentEditor\n\n if (!attachToEditor) {\n console.warn(\n 'FloatingMenu component is not rendered inside of an editor component or does not have editor prop.',\n )\n return\n }\n\n const plugin = FloatingMenuPlugin({\n editor: attachToEditor,\n element: floatingMenuElement,\n pluginKey,\n appendTo,\n shouldShow,\n options,\n })\n\n attachToEditor.registerPlugin(plugin)\n\n return () => {\n attachToEditor.unregisterPlugin(pluginKey)\n window.requestAnimationFrame(() => {\n if (floatingMenuElement.parentNode) {\n floatingMenuElement.parentNode.removeChild(floatingMenuElement)\n }\n })\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [editor, currentEditor, appendTo, pluginKey, shouldShow, options])\n\n return createPortal(<div {...restProps}>{children}</div>, menuEl.current)\n },\n)\n","import React, { useEffect, useRef } from 'react'\nimport { createPortal } from 'react-dom'\nimport { useCurrentEditor } from '@blockslides/react'\nimport {\n BubbleMenuPlugin,\n type BubbleMenuPluginProps,\n} from '@blockslides/extension-bubble-menu'\nimport { NodeSelection } from '@blockslides/pm/state'\nimport {\n type BubbleMenuPresetOptions,\n buildMenuElement,\n DEFAULT_ITEMS,\n DEFAULT_COLOR_PALETTE,\n DEFAULT_HIGHLIGHT_PALETTE,\n DEFAULT_FONTS,\n DEFAULT_FONT_SIZES,\n DEFAULT_ALIGNMENTS,\n} from '@blockslides/extension-bubble-menu-preset'\nimport { isNodeSelection, isTextSelection, type Editor } from '@blockslides/core'\n\nexport type BubbleMenuPresetProps = Omit<BubbleMenuPresetOptions, 'element' | 'pluginKey'> &\n React.HTMLAttributes<HTMLElement> & {\n editor?: Editor | null\n }\n\nexport const BubbleMenuPreset = React.forwardRef<HTMLElement, BubbleMenuPresetProps>(\n (\n {\n editor,\n updateDelay,\n resizeDelay,\n appendTo,\n shouldShow,\n getReferencedVirtualElement,\n options,\n items,\n className,\n injectStyles,\n textColors,\n highlightColors,\n fonts,\n fontSizes,\n alignments,\n onTextAction,\n onImageReplace,\n ...rest\n },\n ref,\n ) => {\n const pluginKey = 'bubbleMenuPreset'\n const { editor: currentEditor } = useCurrentEditor()\n const menuEl = useRef<HTMLElement | null>(null)\n\n useEffect(() => {\n const attachToEditor = (editor || currentEditor) as Editor | null\n if (!attachToEditor || (attachToEditor as any).isDestroyed) {\n return\n }\n\n const { element, cleanup } = buildMenuElement(attachToEditor, {\n items: items ?? DEFAULT_ITEMS,\n className: className ?? '',\n injectStyles: injectStyles !== false,\n textColors: textColors ?? DEFAULT_COLOR_PALETTE,\n highlightColors: highlightColors ?? DEFAULT_HIGHLIGHT_PALETTE,\n fonts: fonts ?? DEFAULT_FONTS,\n fontSizes: fontSizes ?? DEFAULT_FONT_SIZES,\n alignments: alignments ?? DEFAULT_ALIGNMENTS,\n onTextAction,\n onImageReplace,\n })\n\n menuEl.current = element\n\n if (typeof ref === 'function') {\n ref(element)\n } else if (ref) {\n ;(ref as React.MutableRefObject<HTMLElement | null>).current = element\n }\n\n const defaultShouldShow: Exclude<BubbleMenuPluginProps['shouldShow'], null> = ({\n state,\n editor,\n }) => {\n const sel = state.selection\n const imageSelection =\n (sel instanceof NodeSelection &&\n ['image', 'imageBlock'].includes((sel as any).node?.type?.name)) ||\n editor.isActive('image') ||\n editor.isActive('imageBlock')\n\n if (imageSelection) return true\n if (isTextSelection(sel) && !sel.empty && !imageSelection) return true\n return false\n }\n\n const plugin = BubbleMenuPlugin({\n editor: attachToEditor,\n element,\n updateDelay,\n resizeDelay,\n appendTo,\n pluginKey,\n shouldShow: shouldShow ?? defaultShouldShow,\n getReferencedVirtualElement,\n options,\n })\n\n attachToEditor.registerPlugin(plugin)\n\n return () => {\n attachToEditor.unregisterPlugin(pluginKey)\n cleanup?.()\n if (element.parentNode) {\n element.parentNode.removeChild(element)\n }\n }\n }, [\n editor,\n currentEditor,\n updateDelay,\n resizeDelay,\n appendTo,\n shouldShow,\n getReferencedVirtualElement,\n options,\n items,\n className,\n injectStyles,\n textColors,\n highlightColors,\n fonts,\n fontSizes,\n alignments,\n onTextAction,\n onImageReplace,\n ref,\n ])\n\n return menuEl.current ? createPortal(<div {...rest} />, menuEl.current) : null\n },\n)\n\nBubbleMenuPreset.displayName = 'BubbleMenuPreset'\n"],"mappings":"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","names":["useCurrentEditor","React","useEffect","useRef","createPortal","jsx","React","useEffect","useRef","createPortal","useCurrentEditor","BubbleMenuPlugin","jsx","editor"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@blockslides/react-prebuilts",
|
|
3
|
+
"description": "Pre-built React components for blockslides - includes SlideEditor, useSlideEditor, and menu components",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"homepage": "https://github.com/keivanmojmali/blockslides",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"blockslides",
|
|
8
|
+
"blockslides react components",
|
|
9
|
+
"editor",
|
|
10
|
+
"wysiwyg",
|
|
11
|
+
"slide editor",
|
|
12
|
+
"prebuilt components"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": {
|
|
18
|
+
"import": "./dist/index.d.ts",
|
|
19
|
+
"require": "./dist/index.d.cts"
|
|
20
|
+
},
|
|
21
|
+
"import": "./dist/index.js",
|
|
22
|
+
"require": "./dist/index.cjs"
|
|
23
|
+
},
|
|
24
|
+
"./menus": {
|
|
25
|
+
"types": {
|
|
26
|
+
"import": "./dist/menus/index.d.ts",
|
|
27
|
+
"require": "./dist/menus/index.d.cts"
|
|
28
|
+
},
|
|
29
|
+
"import": "./dist/menus/index.js",
|
|
30
|
+
"require": "./dist/menus/index.cjs"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"main": "dist/index.cjs",
|
|
34
|
+
"module": "dist/index.js",
|
|
35
|
+
"types": "dist/index.d.ts",
|
|
36
|
+
"type": "module",
|
|
37
|
+
"files": [
|
|
38
|
+
"src",
|
|
39
|
+
"dist"
|
|
40
|
+
],
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@blockslides/react": "^0.4.0",
|
|
43
|
+
"@blockslides/ai-context": "^0.3.1",
|
|
44
|
+
"@blockslides/extension-kit": "^0.7.3",
|
|
45
|
+
"@blockslides/extension-bubble-menu": "^0.1.1",
|
|
46
|
+
"@blockslides/extension-bubble-menu-preset": "^0.3.1",
|
|
47
|
+
"@blockslides/extension-floating-menu": "^0.1.1"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/react": "^19.0.0",
|
|
51
|
+
"@types/react-dom": "^19.0.0",
|
|
52
|
+
"react": "^19.0.0",
|
|
53
|
+
"react-dom": "^19.0.0",
|
|
54
|
+
"@blockslides/core": "^0.3.3",
|
|
55
|
+
"@blockslides/pm": "^0.1.1"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
|
59
|
+
"@types/react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
|
60
|
+
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
|
61
|
+
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
|
62
|
+
"@blockslides/core": "^0.3.3",
|
|
63
|
+
"@blockslides/pm": "^0.1.1"
|
|
64
|
+
},
|
|
65
|
+
"repository": {
|
|
66
|
+
"type": "git",
|
|
67
|
+
"url": "https://github.com/keivanmojmali/blockslides",
|
|
68
|
+
"directory": "packages/react-prebuilts"
|
|
69
|
+
},
|
|
70
|
+
"sideEffects": false,
|
|
71
|
+
"author": "keivanmojmali",
|
|
72
|
+
"scripts": {
|
|
73
|
+
"build": "tsup",
|
|
74
|
+
"lint": "prettier ./src/ --check && eslint --cache --quiet --no-error-on-unmatched-pattern ./src/"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import React, { forwardRef, useMemo } from "react";
|
|
2
|
+
import type { CSSProperties } from "react";
|
|
3
|
+
|
|
4
|
+
import { EditorContent, type EditorContentProps } from "@blockslides/react";
|
|
5
|
+
import {
|
|
6
|
+
BubbleMenuPreset,
|
|
7
|
+
type BubbleMenuPresetProps,
|
|
8
|
+
} from "./menus/BubbleMenuPreset.js";
|
|
9
|
+
import {
|
|
10
|
+
useSlideEditor,
|
|
11
|
+
type UseSlideEditorProps,
|
|
12
|
+
} from "./useSlideEditor.js";
|
|
13
|
+
|
|
14
|
+
export type SlideEditorProps = UseSlideEditorProps & {
|
|
15
|
+
/**
|
|
16
|
+
* Toggle or customize the built-in BubbleMenuPreset.
|
|
17
|
+
* - true (default): render with defaults
|
|
18
|
+
* - false: disable entirely
|
|
19
|
+
* - object: pass through to BubbleMenuPreset
|
|
20
|
+
*/
|
|
21
|
+
bubbleMenuPreset?: boolean | BubbleMenuPresetProps;
|
|
22
|
+
/**
|
|
23
|
+
* Additional props forwarded to EditorContent.
|
|
24
|
+
*/
|
|
25
|
+
editorContentProps?: Omit<EditorContentProps, "editor">;
|
|
26
|
+
/**
|
|
27
|
+
* Viewport scale factor applied to the slide viewport.
|
|
28
|
+
* @default 1
|
|
29
|
+
*/
|
|
30
|
+
viewportScale?: number;
|
|
31
|
+
className?: string;
|
|
32
|
+
style?: CSSProperties;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const SlideEditor = forwardRef<HTMLDivElement, SlideEditorProps>(
|
|
36
|
+
(
|
|
37
|
+
{
|
|
38
|
+
bubbleMenuPreset = true,
|
|
39
|
+
editorContentProps,
|
|
40
|
+
viewportScale = 1,
|
|
41
|
+
className,
|
|
42
|
+
style,
|
|
43
|
+
...hookProps
|
|
44
|
+
},
|
|
45
|
+
ref
|
|
46
|
+
) => {
|
|
47
|
+
const { editor } = useSlideEditor(hookProps);
|
|
48
|
+
|
|
49
|
+
const bubbleMenuProps = useMemo(() => {
|
|
50
|
+
if (bubbleMenuPreset === false) return null;
|
|
51
|
+
if (bubbleMenuPreset === true) return {};
|
|
52
|
+
return bubbleMenuPreset;
|
|
53
|
+
}, [bubbleMenuPreset]);
|
|
54
|
+
|
|
55
|
+
if (!editor) return null;
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<div ref={ref} className={className} style={style}>
|
|
59
|
+
<div
|
|
60
|
+
className="bs-viewport"
|
|
61
|
+
style={{ ["--zoom" as any]: viewportScale }}
|
|
62
|
+
>
|
|
63
|
+
<EditorContent editor={editor} {...editorContentProps} />
|
|
64
|
+
{bubbleMenuProps && (
|
|
65
|
+
<BubbleMenuPreset editor={editor} {...bubbleMenuProps} />
|
|
66
|
+
)}
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
SlideEditor.displayName = "SlideEditor";
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { SlideEditor as ReactSlideEditor } from "./SlideEditor.js";
|
|
2
|
+
export * from "./useSlideEditor.js";
|
|
3
|
+
export * from "./menus/BubbleMenuPreset.js";
|
|
4
|
+
|
|
5
|
+
// Re-export everything from @blockslides/react core
|
|
6
|
+
export * from "@blockslides/react";
|
|
7
|
+
|
|
8
|
+
// Note: @blockslides/core is already re-exported by @blockslides/react
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { type BubbleMenuPluginProps, BubbleMenuPlugin } from '@blockslides/extension-bubble-menu'
|
|
2
|
+
import { useCurrentEditor } from '@blockslides/react'
|
|
3
|
+
import React, { useEffect, useRef } from 'react'
|
|
4
|
+
import { createPortal } from 'react-dom'
|
|
5
|
+
|
|
6
|
+
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>
|
|
7
|
+
|
|
8
|
+
export type BubbleMenuProps = Optional<Omit<Optional<BubbleMenuPluginProps, 'pluginKey'>, 'element'>, 'editor'> &
|
|
9
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
10
|
+
|
|
11
|
+
export const BubbleMenu = React.forwardRef<HTMLDivElement, BubbleMenuProps>(
|
|
12
|
+
(
|
|
13
|
+
{
|
|
14
|
+
pluginKey = 'bubbleMenu',
|
|
15
|
+
editor,
|
|
16
|
+
updateDelay,
|
|
17
|
+
resizeDelay,
|
|
18
|
+
appendTo,
|
|
19
|
+
shouldShow = null,
|
|
20
|
+
getReferencedVirtualElement,
|
|
21
|
+
options,
|
|
22
|
+
children,
|
|
23
|
+
...restProps
|
|
24
|
+
},
|
|
25
|
+
ref,
|
|
26
|
+
) => {
|
|
27
|
+
const menuEl = useRef(document.createElement('div'))
|
|
28
|
+
|
|
29
|
+
if (typeof ref === 'function') {
|
|
30
|
+
ref(menuEl.current)
|
|
31
|
+
} else if (ref) {
|
|
32
|
+
ref.current = menuEl.current
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const { editor: currentEditor } = useCurrentEditor()
|
|
36
|
+
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
const bubbleMenuElement = menuEl.current
|
|
39
|
+
bubbleMenuElement.style.visibility = 'hidden'
|
|
40
|
+
bubbleMenuElement.style.position = 'absolute'
|
|
41
|
+
|
|
42
|
+
if (editor?.isDestroyed || (currentEditor as any)?.isDestroyed) {
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const attachToEditor = editor || currentEditor
|
|
47
|
+
|
|
48
|
+
if (!attachToEditor) {
|
|
49
|
+
console.warn('BubbleMenu component is not rendered inside of an editor component or does not have editor prop.')
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const plugin = BubbleMenuPlugin({
|
|
54
|
+
updateDelay,
|
|
55
|
+
resizeDelay,
|
|
56
|
+
editor: attachToEditor,
|
|
57
|
+
element: bubbleMenuElement,
|
|
58
|
+
appendTo,
|
|
59
|
+
pluginKey,
|
|
60
|
+
shouldShow,
|
|
61
|
+
getReferencedVirtualElement,
|
|
62
|
+
options,
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
attachToEditor.registerPlugin(plugin)
|
|
66
|
+
|
|
67
|
+
return () => {
|
|
68
|
+
attachToEditor.unregisterPlugin(pluginKey)
|
|
69
|
+
window.requestAnimationFrame(() => {
|
|
70
|
+
if (bubbleMenuElement.parentNode) {
|
|
71
|
+
bubbleMenuElement.parentNode.removeChild(bubbleMenuElement)
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
}, [
|
|
76
|
+
editor,
|
|
77
|
+
currentEditor,
|
|
78
|
+
pluginKey,
|
|
79
|
+
updateDelay,
|
|
80
|
+
resizeDelay,
|
|
81
|
+
appendTo,
|
|
82
|
+
shouldShow,
|
|
83
|
+
getReferencedVirtualElement,
|
|
84
|
+
options,
|
|
85
|
+
])
|
|
86
|
+
|
|
87
|
+
return createPortal(<div {...restProps}>{children}</div>, menuEl.current)
|
|
88
|
+
},
|
|
89
|
+
)
|