@liveblocks/react-tiptap 2.16.0-toolbars1 → 2.16.0-toolbars3

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/dist/index.d.mts CHANGED
@@ -94,11 +94,12 @@ interface ToolbarToggleProps extends ToolbarButtonProps {
94
94
  }
95
95
  interface ToolbarBlockSelectorItem {
96
96
  name: string;
97
- isActive: (editor: Editor) => boolean;
97
+ icon?: ReactNode;
98
+ isActive: ((editor: Editor) => boolean) | "default";
98
99
  setActive: (editor: Editor) => void;
99
100
  }
100
101
  interface ToolbarBlockSelectorProps extends ComponentProps<"button"> {
101
- items?: ToolbarBlockSelectorItem[];
102
+ items?: ToolbarBlockSelectorItem[] | ((defaultItems: ToolbarBlockSelectorItem[]) => ToolbarBlockSelectorItem[]);
102
103
  }
103
104
  declare function ToolbarSectionHistory(): react_jsx_runtime.JSX.Element;
104
105
  declare function ToolbarSectionInline(): react_jsx_runtime.JSX.Element;
@@ -121,7 +122,9 @@ interface FloatingToolbarProps extends Omit<ComponentProps<"div">, "children"> {
121
122
  before?: ToolbarSlot;
122
123
  after?: ToolbarSlot;
123
124
  }
124
- declare const FloatingToolbar: react.ForwardRefExoticComponent<Omit<FloatingToolbarProps, "ref"> & react.RefAttributes<HTMLDivElement>>;
125
+ declare const FloatingToolbar: react.ForwardRefExoticComponent<Omit<FloatingToolbarProps, "ref"> & react.RefAttributes<HTMLDivElement>> & {
126
+ External: react.ForwardRefExoticComponent<Omit<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
127
+ };
125
128
 
126
129
  interface HistoryVersionPreviewProps extends ComponentPropsWithoutRef<"div"> {
127
130
  version: HistoryVersion;
package/dist/index.d.ts CHANGED
@@ -94,11 +94,12 @@ interface ToolbarToggleProps extends ToolbarButtonProps {
94
94
  }
95
95
  interface ToolbarBlockSelectorItem {
96
96
  name: string;
97
- isActive: (editor: Editor) => boolean;
97
+ icon?: ReactNode;
98
+ isActive: ((editor: Editor) => boolean) | "default";
98
99
  setActive: (editor: Editor) => void;
99
100
  }
100
101
  interface ToolbarBlockSelectorProps extends ComponentProps<"button"> {
101
- items?: ToolbarBlockSelectorItem[];
102
+ items?: ToolbarBlockSelectorItem[] | ((defaultItems: ToolbarBlockSelectorItem[]) => ToolbarBlockSelectorItem[]);
102
103
  }
103
104
  declare function ToolbarSectionHistory(): react_jsx_runtime.JSX.Element;
104
105
  declare function ToolbarSectionInline(): react_jsx_runtime.JSX.Element;
@@ -121,7 +122,9 @@ interface FloatingToolbarProps extends Omit<ComponentProps<"div">, "children"> {
121
122
  before?: ToolbarSlot;
122
123
  after?: ToolbarSlot;
123
124
  }
124
- declare const FloatingToolbar: react.ForwardRefExoticComponent<Omit<FloatingToolbarProps, "ref"> & react.RefAttributes<HTMLDivElement>>;
125
+ declare const FloatingToolbar: react.ForwardRefExoticComponent<Omit<FloatingToolbarProps, "ref"> & react.RefAttributes<HTMLDivElement>> & {
126
+ External: react.ForwardRefExoticComponent<Omit<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
127
+ };
125
128
 
126
129
  interface HistoryVersionPreviewProps extends ComponentPropsWithoutRef<"div"> {
127
130
  version: HistoryVersion;
@@ -9,7 +9,7 @@ var react = require('react');
9
9
  var reactDom$1 = require('react-dom');
10
10
  var classnames = require('../classnames.js');
11
11
  var context = require('../context.js');
12
- var FloatingToolbarContext = require('./FloatingToolbarContext.js');
12
+ var shared = require('./shared.js');
13
13
  var Toolbar = require('./Toolbar.js');
14
14
 
15
15
  const FLOATING_TOOLBAR_COLLISION_PADDING = 10;
@@ -29,270 +29,290 @@ function DefaultFloatingToolbarContent({ editor }) {
29
29
  ]
30
30
  });
31
31
  }
32
- const FloatingToolbar = react.forwardRef(
33
- ({
34
- children = DefaultFloatingToolbarContent,
35
- before,
36
- after,
37
- position = "top",
38
- offset: sideOffset = 6,
39
- editor,
40
- onPointerDown,
41
- onFocus,
42
- onBlur,
43
- className,
44
- ...props
45
- }, forwardedRef) => {
46
- const toolbarRef = react.useRef(null);
47
- const [isPointerDown, setPointerDown] = react.useState(false);
48
- const [isFocused, setFocused] = react.useState(false);
49
- const [isManuallyClosed, setManuallyClosed] = react.useState(false);
50
- const isEditable = react$1.useEditorState({
32
+ const FloatingToolbar = Object.assign(
33
+ react.forwardRef(
34
+ ({
35
+ children = DefaultFloatingToolbarContent,
36
+ before,
37
+ after,
38
+ position = "top",
39
+ offset: sideOffset = 6,
51
40
  editor,
52
- equalityFn: Object.is,
53
- selector: (ctx) => ctx.editor?.isEditable ?? false
54
- }) ?? false;
55
- const hasSelectionRange = react$1.useEditorState({
56
- editor,
57
- equalityFn: Object.is,
58
- selector: (ctx) => {
59
- const editor2 = ctx.editor;
60
- if (!editor2) {
61
- return false;
62
- }
63
- const { doc, selection } = editor2.state;
64
- const { empty, ranges } = selection;
65
- const from = Math.min(...ranges.map((range) => range.$from.pos));
66
- const to = Math.max(...ranges.map((range) => range.$to.pos));
67
- if (empty) {
68
- return false;
41
+ onPointerDown,
42
+ onFocus,
43
+ onBlur,
44
+ className,
45
+ ...props
46
+ }, forwardedRef) => {
47
+ const toolbarRef = react.useRef(null);
48
+ const externalIds = _private.useInitial(() => /* @__PURE__ */ new Set());
49
+ const [isPointerDown, setPointerDown] = react.useState(false);
50
+ const [isFocused, setFocused] = react.useState(false);
51
+ const [isManuallyClosed, setManuallyClosed] = react.useState(false);
52
+ const hasSelectionRange = react$1.useEditorState({
53
+ editor,
54
+ equalityFn: Object.is,
55
+ selector: (ctx) => {
56
+ const editor2 = ctx.editor;
57
+ if (!editor2) {
58
+ return false;
59
+ }
60
+ const { doc, selection } = editor2.state;
61
+ const { empty, ranges } = selection;
62
+ const from = Math.min(...ranges.map((range) => range.$from.pos));
63
+ const to = Math.max(...ranges.map((range) => range.$to.pos));
64
+ if (empty) {
65
+ return false;
66
+ }
67
+ return react$1.isTextSelection(selection) && doc.textBetween(from, to).length > 0;
69
68
  }
70
- return react$1.isTextSelection(selection) && doc.textBetween(from, to).length > 0;
71
- }
72
- }) ?? false;
73
- const isOpen = isFocused && !isPointerDown && hasSelectionRange && !isManuallyClosed;
74
- const [delayedIsOpen, setDelayedIsOpen] = react.useState(isOpen);
75
- const delayedIsOpenTimeoutRef = react.useRef();
76
- react.useEffect(() => {
77
- if (!editor) {
78
- return;
79
- }
80
- setManuallyClosed(false);
81
- const handleSelectionChange = () => {
82
- setManuallyClosed(false);
83
- };
84
- editor.on("selectionUpdate", handleSelectionChange);
85
- return () => {
86
- editor.off("selectionUpdate", handleSelectionChange);
87
- };
88
- }, [isFocused, hasSelectionRange, editor]);
89
- react.useEffect(() => {
90
- if (!editor) {
91
- return;
92
- }
93
- const handleFocus2 = () => {
94
- setFocused(true);
95
- };
96
- const handleBlur2 = (event) => {
97
- if (event.relatedTarget && toolbarRef.current?.contains(event.relatedTarget)) {
69
+ }) ?? false;
70
+ const isOpen = isFocused && !isPointerDown && hasSelectionRange && !isManuallyClosed;
71
+ const [delayedIsOpen, setDelayedIsOpen] = react.useState(isOpen);
72
+ const delayedIsOpenTimeoutRef = react.useRef();
73
+ react.useEffect(() => {
74
+ if (!editor) {
98
75
  return;
99
76
  }
100
- if (event.relatedTarget === editor.view.dom) {
77
+ setManuallyClosed(false);
78
+ const handleSelectionChange = () => {
79
+ setManuallyClosed(false);
80
+ };
81
+ editor.on("selectionUpdate", handleSelectionChange);
82
+ return () => {
83
+ editor.off("selectionUpdate", handleSelectionChange);
84
+ };
85
+ }, [isFocused, hasSelectionRange, editor]);
86
+ react.useEffect(() => {
87
+ if (!editor) {
101
88
  return;
102
89
  }
103
- setFocused(false);
104
- };
105
- editor.view.dom.addEventListener("focus", handleFocus2);
106
- editor.view.dom.addEventListener("blur", handleBlur2);
107
- return () => {
108
- editor.view.dom.removeEventListener("focus", handleFocus2);
109
- editor.view.dom.removeEventListener("blur", handleBlur2);
110
- };
111
- }, [editor]);
112
- const handleFocus = react.useCallback(
113
- (event) => {
114
- onFocus?.(event);
115
- if (!event.isDefaultPrevented()) {
90
+ const handleFocus2 = () => {
116
91
  setFocused(true);
117
- }
118
- },
119
- [onFocus]
120
- );
121
- const handleBlur = react.useCallback(
122
- (event) => {
123
- onBlur?.(event);
124
- if (!event.isDefaultPrevented()) {
92
+ };
93
+ const handleBlur2 = (event) => {
125
94
  if (event.relatedTarget && toolbarRef.current?.contains(event.relatedTarget)) {
126
95
  return;
127
96
  }
128
- if (event.relatedTarget === editor?.view.dom) {
97
+ if (event.relatedTarget === editor.view.dom) {
129
98
  return;
130
99
  }
100
+ for (const externalId of externalIds) {
101
+ if (document.getElementById(externalId)?.contains(event.relatedTarget)) {
102
+ return;
103
+ }
104
+ }
131
105
  setFocused(false);
106
+ };
107
+ editor.view.dom.addEventListener("focus", handleFocus2);
108
+ editor.view.dom.addEventListener("blur", handleBlur2);
109
+ return () => {
110
+ editor.view.dom.removeEventListener("focus", handleFocus2);
111
+ editor.view.dom.removeEventListener("blur", handleBlur2);
112
+ };
113
+ }, [editor, externalIds]);
114
+ const handleFocus = react.useCallback(
115
+ (event) => {
116
+ onFocus?.(event);
117
+ if (!event.isDefaultPrevented()) {
118
+ setFocused(true);
119
+ }
120
+ },
121
+ [onFocus]
122
+ );
123
+ const handleBlur = react.useCallback(
124
+ (event) => {
125
+ onBlur?.(event);
126
+ if (!event.isDefaultPrevented()) {
127
+ if (event.relatedTarget && toolbarRef.current?.contains(event.relatedTarget)) {
128
+ return;
129
+ }
130
+ if (event.relatedTarget === editor?.view.dom) {
131
+ return;
132
+ }
133
+ for (const externalId of externalIds) {
134
+ if (document.getElementById(externalId)?.contains(event.relatedTarget)) {
135
+ return;
136
+ }
137
+ }
138
+ setFocused(false);
139
+ }
140
+ },
141
+ [onBlur, editor, externalIds]
142
+ );
143
+ react.useEffect(() => {
144
+ if (isOpen) {
145
+ delayedIsOpenTimeoutRef.current = window.setTimeout(() => {
146
+ setDelayedIsOpen(true);
147
+ }, FLOATING_TOOLBAR_OPEN_DELAY);
148
+ } else {
149
+ setDelayedIsOpen(false);
132
150
  }
133
- },
134
- [onBlur, editor]
135
- );
136
- react.useEffect(() => {
137
- if (isOpen) {
138
- delayedIsOpenTimeoutRef.current = window.setTimeout(() => {
139
- setDelayedIsOpen(true);
140
- }, FLOATING_TOOLBAR_OPEN_DELAY);
141
- } else {
142
- setDelayedIsOpen(false);
143
- }
144
- return () => {
145
- window.clearTimeout(delayedIsOpenTimeoutRef.current);
146
- };
147
- }, [isOpen]);
148
- const floatingOptions = react.useMemo(() => {
149
- const detectOverflowOptions = {
150
- padding: FLOATING_TOOLBAR_COLLISION_PADDING
151
- };
152
- return {
153
- strategy: "fixed",
154
- placement: position,
155
- middleware: [
156
- reactDom.inline(detectOverflowOptions),
157
- reactDom.flip({ ...detectOverflowOptions, crossAxis: false }),
158
- reactDom.hide(detectOverflowOptions),
159
- reactDom.shift({
160
- ...detectOverflowOptions,
161
- limiter: reactDom.limitShift()
162
- }),
163
- reactDom.offset(sideOffset),
164
- reactDom.size(detectOverflowOptions)
165
- ],
166
- whileElementsMounted: (...args) => {
167
- return reactDom.autoUpdate(...args, {
168
- animationFrame: true
169
- });
151
+ return () => {
152
+ window.clearTimeout(delayedIsOpenTimeoutRef.current);
153
+ };
154
+ }, [isOpen]);
155
+ const floatingOptions = react.useMemo(() => {
156
+ const detectOverflowOptions = {
157
+ padding: FLOATING_TOOLBAR_COLLISION_PADDING
158
+ };
159
+ return {
160
+ strategy: "fixed",
161
+ placement: position,
162
+ middleware: [
163
+ reactDom.inline(detectOverflowOptions),
164
+ reactDom.flip({ ...detectOverflowOptions, crossAxis: false }),
165
+ reactDom.hide(detectOverflowOptions),
166
+ reactDom.shift({
167
+ ...detectOverflowOptions,
168
+ limiter: reactDom.limitShift()
169
+ }),
170
+ reactDom.offset(sideOffset),
171
+ reactDom.size(detectOverflowOptions)
172
+ ],
173
+ whileElementsMounted: (...args) => {
174
+ return reactDom.autoUpdate(...args, {
175
+ animationFrame: true
176
+ });
177
+ }
178
+ };
179
+ }, [position, sideOffset]);
180
+ const {
181
+ refs: { setReference, setFloating },
182
+ strategy,
183
+ x,
184
+ y,
185
+ isPositioned
186
+ } = reactDom.useFloating({
187
+ ...floatingOptions,
188
+ open: delayedIsOpen
189
+ });
190
+ const mergedRefs = _private.useRefs(forwardedRef, toolbarRef, setFloating);
191
+ const handlePointerDown = react.useCallback(
192
+ (event) => {
193
+ onPointerDown?.(event);
194
+ event.stopPropagation();
195
+ if (event.target === toolbarRef.current) {
196
+ event.preventDefault();
197
+ }
198
+ },
199
+ [onPointerDown]
200
+ );
201
+ react.useEffect(() => {
202
+ if (!editor) {
203
+ return;
170
204
  }
171
- };
172
- }, [position, sideOffset]);
173
- const {
174
- refs: { setReference, setFloating },
175
- strategy,
176
- x,
177
- y,
178
- isPositioned
179
- } = reactDom.useFloating({
180
- ...floatingOptions,
181
- open: delayedIsOpen
182
- });
183
- const mergedRefs = _private.useRefs(forwardedRef, toolbarRef, setFloating);
184
- const handlePointerDown = react.useCallback(
185
- (event) => {
186
- onPointerDown?.(event);
187
- event.stopPropagation();
188
- if (event.target === toolbarRef.current) {
189
- event.preventDefault();
205
+ const handlePointerDown2 = () => {
206
+ setPointerDown(true);
207
+ };
208
+ const handlePointerUp = () => {
209
+ setPointerDown(false);
210
+ };
211
+ editor.view.dom.addEventListener("pointerdown", handlePointerDown2);
212
+ editor.view.dom.addEventListener("pointercancel", handlePointerUp);
213
+ editor.view.dom.addEventListener("pointerup", handlePointerUp);
214
+ return () => {
215
+ editor.view.dom.removeEventListener("pointerdown", handlePointerDown2);
216
+ editor.view.dom.removeEventListener("pointercancel", handlePointerUp);
217
+ editor.view.dom.removeEventListener("pointerup", handlePointerUp);
218
+ };
219
+ }, [editor]);
220
+ _private$1.useLayoutEffect(() => {
221
+ if (!editor || !delayedIsOpen) {
222
+ return;
190
223
  }
191
- },
192
- [onPointerDown]
193
- );
194
- react.useEffect(() => {
195
- if (!editor || !isEditable) {
196
- return;
197
- }
198
- const handlePointerDown2 = () => {
199
- setPointerDown(true);
200
- };
201
- const handlePointerUp = () => {
202
- setPointerDown(false);
203
- };
204
- document.addEventListener("pointerdown", handlePointerDown2);
205
- document.addEventListener("pointercancel", handlePointerUp);
206
- document.addEventListener("pointerup", handlePointerUp);
207
- return () => {
208
- document.removeEventListener("pointerdown", handlePointerDown2);
209
- document.removeEventListener("pointercancel", handlePointerUp);
210
- document.removeEventListener("pointerup", handlePointerUp);
211
- };
212
- }, [editor, isEditable]);
213
- _private$1.useLayoutEffect(() => {
214
- if (!editor || !delayedIsOpen) {
215
- return;
216
- }
217
- const updateSelectionReference = () => {
218
- const domSelection = window.getSelection();
219
- if (editor.state.selection.empty || !domSelection || !domSelection.rangeCount) {
220
- setReference(null);
221
- } else {
222
- const domRange = domSelection.getRangeAt(0);
223
- setReference(domRange);
224
+ const updateSelectionReference = () => {
225
+ const domSelection = window.getSelection();
226
+ if (editor.state.selection.empty || !domSelection || !domSelection.rangeCount) {
227
+ setReference(null);
228
+ } else {
229
+ const domRange = domSelection.getRangeAt(0);
230
+ setReference(domRange);
231
+ }
232
+ };
233
+ editor.on("transaction", updateSelectionReference);
234
+ updateSelectionReference();
235
+ return () => {
236
+ editor.off("transaction", updateSelectionReference);
237
+ };
238
+ }, [editor, delayedIsOpen, setReference]);
239
+ react.useEffect(() => {
240
+ if (!editor || !delayedIsOpen) {
241
+ return;
224
242
  }
225
- };
226
- editor.on("transaction", updateSelectionReference);
227
- updateSelectionReference();
228
- return () => {
229
- editor.off("transaction", updateSelectionReference);
230
- };
231
- }, [editor, delayedIsOpen, setReference]);
232
- react.useEffect(() => {
243
+ const handleKeyDown = (event) => {
244
+ if (event.target !== editor.view.dom && event.defaultPrevented) {
245
+ return;
246
+ }
247
+ if (event.key === "Escape") {
248
+ event.preventDefault();
249
+ event.stopPropagation();
250
+ editor.commands.focus();
251
+ setManuallyClosed(true);
252
+ }
253
+ };
254
+ editor.view.dom.addEventListener("keydown", handleKeyDown);
255
+ return () => {
256
+ editor.view.dom.removeEventListener("keydown", handleKeyDown);
257
+ };
258
+ }, [editor, delayedIsOpen]);
259
+ const close = react.useCallback(() => {
260
+ setManuallyClosed(true);
261
+ }, [setManuallyClosed]);
262
+ const registerExternal = react.useCallback(
263
+ (id) => {
264
+ externalIds.add(id);
265
+ return () => {
266
+ externalIds.delete(id);
267
+ };
268
+ },
269
+ [externalIds]
270
+ );
233
271
  if (!editor || !delayedIsOpen) {
234
- return;
272
+ return null;
235
273
  }
236
- const handleKeyDown = (event) => {
237
- if (event.target !== editor.view.dom && event.defaultPrevented) {
238
- return;
239
- }
240
- if (event.key === "Escape") {
241
- event.preventDefault();
242
- event.stopPropagation();
243
- editor.commands.focus();
244
- setManuallyClosed(true);
245
- }
246
- };
247
- editor.view.dom.addEventListener("keydown", handleKeyDown);
248
- return () => {
249
- editor.view.dom.removeEventListener("keydown", handleKeyDown);
250
- };
251
- }, [editor, delayedIsOpen]);
252
- const close = react.useCallback(() => {
253
- setManuallyClosed(true);
254
- }, [setManuallyClosed]);
255
- if (!editor || !delayedIsOpen) {
256
- return null;
257
- }
258
- const slotProps = { editor };
259
- return reactDom$1.createPortal(
260
- /* @__PURE__ */ jsxRuntime.jsx(_private.TooltipProvider, {
261
- children: /* @__PURE__ */ jsxRuntime.jsx(context.EditorProvider, {
262
- editor,
263
- children: /* @__PURE__ */ jsxRuntime.jsx(FloatingToolbarContext.FloatingToolbarContext.Provider, {
264
- value: { close },
265
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", {
266
- role: "toolbar",
267
- "aria-label": "Floating toolbar",
268
- "aria-orientation": "horizontal",
269
- className: classnames.classNames(
270
- "lb-root lb-portal lb-elevation lb-tiptap-floating-toolbar lb-tiptap-toolbar",
271
- className
272
- ),
273
- ref: mergedRefs,
274
- style: {
275
- position: strategy,
276
- top: 0,
277
- left: 0,
278
- transform: isPositioned ? `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)` : "translate3d(0, -200%, 0)",
279
- minWidth: "max-content"
280
- },
281
- onPointerDown: handlePointerDown,
282
- onFocus: handleFocus,
283
- onBlur: handleBlur,
284
- ...props,
285
- children: [
286
- Toolbar.applyToolbarSlot(before, slotProps),
287
- Toolbar.applyToolbarSlot(children, slotProps),
288
- Toolbar.applyToolbarSlot(after, slotProps)
289
- ]
274
+ const slotProps = { editor };
275
+ return reactDom$1.createPortal(
276
+ /* @__PURE__ */ jsxRuntime.jsx(_private.TooltipProvider, {
277
+ children: /* @__PURE__ */ jsxRuntime.jsx(context.EditorProvider, {
278
+ editor,
279
+ children: /* @__PURE__ */ jsxRuntime.jsx(shared.FloatingToolbarContext.Provider, {
280
+ value: { close, registerExternal },
281
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", {
282
+ role: "toolbar",
283
+ "aria-label": "Floating toolbar",
284
+ "aria-orientation": "horizontal",
285
+ className: classnames.classNames(
286
+ "lb-root lb-portal lb-elevation lb-tiptap-floating-toolbar lb-tiptap-toolbar",
287
+ className
288
+ ),
289
+ ref: mergedRefs,
290
+ style: {
291
+ position: strategy,
292
+ top: 0,
293
+ left: 0,
294
+ transform: isPositioned ? `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)` : "translate3d(0, -200%, 0)",
295
+ minWidth: "max-content"
296
+ },
297
+ onPointerDown: handlePointerDown,
298
+ onFocus: handleFocus,
299
+ onBlur: handleBlur,
300
+ ...props,
301
+ children: [
302
+ Toolbar.applyToolbarSlot(before, slotProps),
303
+ Toolbar.applyToolbarSlot(children, slotProps),
304
+ Toolbar.applyToolbarSlot(after, slotProps)
305
+ ]
306
+ })
290
307
  })
291
308
  })
292
- })
293
- }),
294
- document.body
295
- );
309
+ }),
310
+ document.body
311
+ );
312
+ }
313
+ ),
314
+ {
315
+ External: shared.FloatingToolbarExternal
296
316
  }
297
317
  );
298
318