@liveblocks/react-lexical 2.15.2 → 2.16.0-toolbars1

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.
Files changed (47) hide show
  1. package/dist/comments/floating-composer.js +4 -2
  2. package/dist/comments/floating-composer.js.map +1 -1
  3. package/dist/comments/floating-composer.mjs +4 -2
  4. package/dist/comments/floating-composer.mjs.map +1 -1
  5. package/dist/get-selected-block-element.js +26 -0
  6. package/dist/get-selected-block-element.js.map +1 -0
  7. package/dist/get-selected-block-element.mjs +24 -0
  8. package/dist/get-selected-block-element.mjs.map +1 -0
  9. package/dist/index.d.mts +61 -12
  10. package/dist/index.d.ts +61 -12
  11. package/dist/index.js +4 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/index.mjs +2 -0
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/is-command-registered.js +11 -0
  16. package/dist/is-command-registered.js.map +1 -0
  17. package/dist/is-command-registered.mjs +9 -0
  18. package/dist/is-command-registered.mjs.map +1 -0
  19. package/dist/is-text-format-active.js +16 -0
  20. package/dist/is-text-format-active.js.map +1 -0
  21. package/dist/is-text-format-active.mjs +14 -0
  22. package/dist/is-text-format-active.mjs.map +1 -0
  23. package/dist/toolbar/floating-toolbar-context.js +8 -0
  24. package/dist/toolbar/floating-toolbar-context.js.map +1 -0
  25. package/dist/toolbar/floating-toolbar-context.mjs +6 -0
  26. package/dist/toolbar/floating-toolbar-context.mjs.map +1 -0
  27. package/dist/toolbar/floating-toolbar.js +284 -0
  28. package/dist/toolbar/floating-toolbar.js.map +1 -0
  29. package/dist/toolbar/floating-toolbar.mjs +281 -0
  30. package/dist/toolbar/floating-toolbar.mjs.map +1 -0
  31. package/dist/toolbar/toolbar.js +406 -0
  32. package/dist/toolbar/toolbar.js.map +1 -0
  33. package/dist/toolbar/toolbar.mjs +381 -0
  34. package/dist/toolbar/toolbar.mjs.map +1 -0
  35. package/dist/version-history/history-version-preview.js +3 -10
  36. package/dist/version-history/history-version-preview.js.map +1 -1
  37. package/dist/version-history/history-version-preview.mjs +3 -10
  38. package/dist/version-history/history-version-preview.mjs.map +1 -1
  39. package/dist/version.js +1 -1
  40. package/dist/version.js.map +1 -1
  41. package/dist/version.mjs +1 -1
  42. package/dist/version.mjs.map +1 -1
  43. package/package.json +12 -8
  44. package/src/styles/constants.css +1 -1
  45. package/src/styles/index.css +44 -6
  46. package/styles.css +1 -1
  47. package/styles.css.map +1 -1
@@ -0,0 +1,16 @@
1
+ 'use strict';
2
+
3
+ var lexical = require('lexical');
4
+
5
+ function isTextFormatActive(editor, format) {
6
+ return editor.getEditorState().read(() => {
7
+ const selection = lexical.$getSelection();
8
+ if (!lexical.$isRangeSelection(selection) || selection.isCollapsed()) {
9
+ return false;
10
+ }
11
+ return selection.hasFormat(format);
12
+ });
13
+ }
14
+
15
+ exports.isTextFormatActive = isTextFormatActive;
16
+ //# sourceMappingURL=is-text-format-active.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-text-format-active.js","sources":["../src/is-text-format-active.ts"],"sourcesContent":["import type { LexicalEditor, TextFormatType } from \"lexical\";\nimport { $getSelection, $isRangeSelection } from \"lexical\";\n\nexport function isTextFormatActive(\n editor: LexicalEditor,\n format: TextFormatType\n) {\n return editor.getEditorState().read(() => {\n const selection = $getSelection();\n\n if (!$isRangeSelection(selection) || selection.isCollapsed()) {\n return false;\n }\n\n return selection.hasFormat(format);\n });\n}\n"],"names":["$getSelection","$isRangeSelection"],"mappings":";;;;AAGgB,SAAA,kBAAA,CACd,QACA,MACA,EAAA;AACA,EAAA,OAAO,MAAO,CAAA,cAAA,EAAiB,CAAA,IAAA,CAAK,MAAM;AACxC,IAAA,MAAM,YAAYA,qBAAc,EAAA,CAAA;AAEhC,IAAA,IAAI,CAACC,yBAAkB,CAAA,SAAS,CAAK,IAAA,SAAA,CAAU,aAAe,EAAA;AAC5D,MAAO,OAAA,KAAA,CAAA;AAAA,KACT;AAEA,IAAO,OAAA,SAAA,CAAU,UAAU,MAAM,CAAA,CAAA;AAAA,GAClC,CAAA,CAAA;AACH;;;;"}
@@ -0,0 +1,14 @@
1
+ import { $getSelection, $isRangeSelection } from 'lexical';
2
+
3
+ function isTextFormatActive(editor, format) {
4
+ return editor.getEditorState().read(() => {
5
+ const selection = $getSelection();
6
+ if (!$isRangeSelection(selection) || selection.isCollapsed()) {
7
+ return false;
8
+ }
9
+ return selection.hasFormat(format);
10
+ });
11
+ }
12
+
13
+ export { isTextFormatActive };
14
+ //# sourceMappingURL=is-text-format-active.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-text-format-active.mjs","sources":["../src/is-text-format-active.ts"],"sourcesContent":["import type { LexicalEditor, TextFormatType } from \"lexical\";\nimport { $getSelection, $isRangeSelection } from \"lexical\";\n\nexport function isTextFormatActive(\n editor: LexicalEditor,\n format: TextFormatType\n) {\n return editor.getEditorState().read(() => {\n const selection = $getSelection();\n\n if (!$isRangeSelection(selection) || selection.isCollapsed()) {\n return false;\n }\n\n return selection.hasFormat(format);\n });\n}\n"],"names":[],"mappings":";;AAGgB,SAAA,kBAAA,CACd,QACA,MACA,EAAA;AACA,EAAA,OAAO,MAAO,CAAA,cAAA,EAAiB,CAAA,IAAA,CAAK,MAAM;AACxC,IAAA,MAAM,YAAY,aAAc,EAAA,CAAA;AAEhC,IAAA,IAAI,CAAC,iBAAkB,CAAA,SAAS,CAAK,IAAA,SAAA,CAAU,aAAe,EAAA;AAC5D,MAAO,OAAA,KAAA,CAAA;AAAA,KACT;AAEA,IAAO,OAAA,SAAA,CAAU,UAAU,MAAM,CAAA,CAAA;AAAA,GAClC,CAAA,CAAA;AACH;;;;"}
@@ -0,0 +1,8 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+
5
+ const FloatingToolbarContext = react.createContext(null);
6
+
7
+ exports.FloatingToolbarContext = FloatingToolbarContext;
8
+ //# sourceMappingURL=floating-toolbar-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"floating-toolbar-context.js","sources":["../../src/toolbar/floating-toolbar-context.tsx"],"sourcesContent":["import { createContext } from \"react\";\n\n// This file is separate to avoid circular dependencies\n\ntype FloatingToolbarContext = {\n close: () => void;\n};\n\nexport const FloatingToolbarContext =\n createContext<FloatingToolbarContext | null>(null);\n"],"names":["createContext"],"mappings":";;;;AAQa,MAAA,sBAAA,GACXA,oBAA6C,IAAI;;;;"}
@@ -0,0 +1,6 @@
1
+ import { createContext } from 'react';
2
+
3
+ const FloatingToolbarContext = createContext(null);
4
+
5
+ export { FloatingToolbarContext };
6
+ //# sourceMappingURL=floating-toolbar-context.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"floating-toolbar-context.mjs","sources":["../../src/toolbar/floating-toolbar-context.tsx"],"sourcesContent":["import { createContext } from \"react\";\n\n// This file is separate to avoid circular dependencies\n\ntype FloatingToolbarContext = {\n close: () => void;\n};\n\nexport const FloatingToolbarContext =\n createContext<FloatingToolbarContext | null>(null);\n"],"names":[],"mappings":";;AAQa,MAAA,sBAAA,GACX,cAA6C,IAAI;;;;"}
@@ -0,0 +1,284 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var reactDom = require('@floating-ui/react-dom');
5
+ var LexicalComposerContext = require('@lexical/react/LexicalComposerContext');
6
+ var _private = require('@liveblocks/react-ui/_private');
7
+ var lexical = require('lexical');
8
+ var react = require('react');
9
+ var reactDom$1 = require('react-dom');
10
+ var classnames = require('../classnames.js');
11
+ var floatingComposer = require('../comments/floating-composer.js');
12
+ var createDomRange = require('../create-dom-range.js');
13
+ var isCommandRegistered = require('../is-command-registered.js');
14
+ var floatingToolbarContext = require('./floating-toolbar-context.js');
15
+ var toolbar = require('./toolbar.js');
16
+
17
+ const FLOATING_TOOLBAR_COLLISION_PADDING = 10;
18
+ const FLOATING_TOOLBAR_OPEN_DELAY = 50;
19
+ function DefaultFloatingToolbarContent() {
20
+ const supportsTextFormat = isCommandRegistered.useIsCommandRegistered(lexical.FORMAT_TEXT_COMMAND);
21
+ const supportsThread = isCommandRegistered.useIsCommandRegistered(floatingComposer.OPEN_FLOATING_COMPOSER_COMMAND);
22
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
23
+ children: [
24
+ supportsTextFormat ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
25
+ children: [
26
+ /* @__PURE__ */ jsxRuntime.jsx(toolbar.Toolbar.BlockSelector, {}),
27
+ /* @__PURE__ */ jsxRuntime.jsx(toolbar.Toolbar.SectionInline, {})
28
+ ]
29
+ }) : null,
30
+ supportsThread ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
31
+ children: [
32
+ /* @__PURE__ */ jsxRuntime.jsx(toolbar.Toolbar.Separator, {}),
33
+ /* @__PURE__ */ jsxRuntime.jsx(toolbar.Toolbar.SectionCollaboration, {})
34
+ ]
35
+ }) : null
36
+ ]
37
+ });
38
+ }
39
+ const FloatingToolbar = react.forwardRef(
40
+ ({
41
+ children = DefaultFloatingToolbarContent,
42
+ before,
43
+ after,
44
+ position = "top",
45
+ offset: sideOffset = 6,
46
+ onPointerDown,
47
+ onFocus,
48
+ onBlur,
49
+ className,
50
+ ...props
51
+ }, forwardedRef) => {
52
+ const toolbarRef = react.useRef(null);
53
+ const [isPointerDown, setPointerDown] = react.useState(false);
54
+ const [editor] = LexicalComposerContext.useLexicalComposerContext();
55
+ const [isFocused, setFocused] = react.useState(false);
56
+ const [isManuallyClosed, setManuallyClosed] = react.useState(false);
57
+ const isEditable = editor.isEditable();
58
+ const [hasSelectionRange, setHasSelectionRange] = react.useState(false);
59
+ const isOpen = isFocused && !isPointerDown && hasSelectionRange && !isManuallyClosed;
60
+ const [delayedIsOpen, setDelayedIsOpen] = react.useState(isOpen);
61
+ const delayedIsOpenTimeoutRef = react.useRef();
62
+ react.useEffect(() => {
63
+ setManuallyClosed(false);
64
+ }, [isFocused, hasSelectionRange, editor]);
65
+ react.useEffect(() => {
66
+ const root = editor.getRootElement();
67
+ if (!root) {
68
+ return;
69
+ }
70
+ const handleFocus2 = () => {
71
+ setFocused(true);
72
+ };
73
+ const handleBlur2 = (event) => {
74
+ if (event.relatedTarget && toolbarRef.current?.contains(event.relatedTarget)) {
75
+ return;
76
+ }
77
+ if (event.relatedTarget === editor.getRootElement()) {
78
+ return;
79
+ }
80
+ setFocused(false);
81
+ };
82
+ root.addEventListener("focus", handleFocus2);
83
+ root.addEventListener("blur", handleBlur2);
84
+ return () => {
85
+ root.removeEventListener("focus", handleFocus2);
86
+ root.removeEventListener("blur", handleBlur2);
87
+ };
88
+ }, [editor]);
89
+ const handleFocus = react.useCallback(
90
+ (event) => {
91
+ onFocus?.(event);
92
+ if (!event.isDefaultPrevented()) {
93
+ setFocused(true);
94
+ }
95
+ },
96
+ [onFocus]
97
+ );
98
+ const handleBlur = react.useCallback(
99
+ (event) => {
100
+ onBlur?.(event);
101
+ if (!event.isDefaultPrevented()) {
102
+ if (event.relatedTarget && toolbarRef.current?.contains(event.relatedTarget)) {
103
+ return;
104
+ }
105
+ if (event.relatedTarget === editor?.getRootElement()) {
106
+ return;
107
+ }
108
+ setFocused(false);
109
+ }
110
+ },
111
+ [onBlur, editor]
112
+ );
113
+ react.useEffect(() => {
114
+ if (isOpen) {
115
+ delayedIsOpenTimeoutRef.current = window.setTimeout(() => {
116
+ setDelayedIsOpen(true);
117
+ }, FLOATING_TOOLBAR_OPEN_DELAY);
118
+ } else {
119
+ setDelayedIsOpen(false);
120
+ }
121
+ return () => {
122
+ window.clearTimeout(delayedIsOpenTimeoutRef.current);
123
+ };
124
+ }, [isOpen]);
125
+ const floatingOptions = react.useMemo(() => {
126
+ const detectOverflowOptions = {
127
+ padding: FLOATING_TOOLBAR_COLLISION_PADDING
128
+ };
129
+ return {
130
+ strategy: "fixed",
131
+ placement: position,
132
+ middleware: [
133
+ reactDom.inline(detectOverflowOptions),
134
+ reactDom.flip({ ...detectOverflowOptions, crossAxis: false }),
135
+ reactDom.hide(detectOverflowOptions),
136
+ reactDom.shift({
137
+ ...detectOverflowOptions,
138
+ limiter: reactDom.limitShift()
139
+ }),
140
+ reactDom.offset(sideOffset),
141
+ reactDom.size(detectOverflowOptions)
142
+ ],
143
+ whileElementsMounted: (...args) => {
144
+ return reactDom.autoUpdate(...args, {
145
+ animationFrame: true
146
+ });
147
+ }
148
+ };
149
+ }, [position, sideOffset]);
150
+ const {
151
+ refs: { setReference, setFloating },
152
+ strategy,
153
+ x,
154
+ y,
155
+ isPositioned
156
+ } = reactDom.useFloating({
157
+ ...floatingOptions,
158
+ open: delayedIsOpen
159
+ });
160
+ const mergedRefs = _private.useRefs(forwardedRef, toolbarRef, setFloating);
161
+ const handlePointerDown = react.useCallback(
162
+ (event) => {
163
+ onPointerDown?.(event);
164
+ event.stopPropagation();
165
+ if (event.target === toolbarRef.current) {
166
+ event.preventDefault();
167
+ }
168
+ },
169
+ [onPointerDown]
170
+ );
171
+ react.useEffect(() => {
172
+ if (!editor || !isEditable) {
173
+ return;
174
+ }
175
+ const handlePointerDown2 = () => {
176
+ setPointerDown(true);
177
+ };
178
+ const handlePointerUp = () => {
179
+ setPointerDown(false);
180
+ };
181
+ document.addEventListener("pointerdown", handlePointerDown2);
182
+ document.addEventListener("pointercancel", handlePointerUp);
183
+ document.addEventListener("pointerup", handlePointerUp);
184
+ return () => {
185
+ document.removeEventListener("pointerdown", handlePointerDown2);
186
+ document.removeEventListener("pointercancel", handlePointerUp);
187
+ document.removeEventListener("pointerup", handlePointerUp);
188
+ };
189
+ }, [editor, isEditable]);
190
+ react.useEffect(() => {
191
+ const unregister = editor.registerUpdateListener(({ tags }) => {
192
+ return editor.getEditorState().read(() => {
193
+ if (tags.has("collaboration"))
194
+ return;
195
+ setManuallyClosed(false);
196
+ const selection = lexical.$getSelection();
197
+ if (!lexical.$isRangeSelection(selection) || selection.isCollapsed()) {
198
+ setHasSelectionRange(false);
199
+ setReference(null);
200
+ return;
201
+ }
202
+ const { anchor, focus } = selection;
203
+ const range = createDomRange.createDOMRange(
204
+ editor,
205
+ anchor.getNode(),
206
+ anchor.offset,
207
+ focus.getNode(),
208
+ focus.offset
209
+ );
210
+ setHasSelectionRange(true);
211
+ setReference(range);
212
+ });
213
+ });
214
+ return unregister;
215
+ }, [editor, setReference]);
216
+ react.useEffect(() => {
217
+ const root = editor.getRootElement();
218
+ if (!root || !delayedIsOpen) {
219
+ return;
220
+ }
221
+ const handleKeyDown = (event) => {
222
+ if (event.target !== root && event.defaultPrevented) {
223
+ return;
224
+ }
225
+ if (event.key === "Escape") {
226
+ event.preventDefault();
227
+ event.stopPropagation();
228
+ editor.focus();
229
+ setManuallyClosed(true);
230
+ }
231
+ };
232
+ root.addEventListener("keydown", handleKeyDown);
233
+ return () => {
234
+ root.removeEventListener("keydown", handleKeyDown);
235
+ };
236
+ }, [editor, delayedIsOpen]);
237
+ const close = react.useCallback(() => {
238
+ editor.focus();
239
+ setManuallyClosed(true);
240
+ }, [editor, setManuallyClosed]);
241
+ if (!delayedIsOpen) {
242
+ return null;
243
+ }
244
+ const slotProps = { editor };
245
+ return reactDom$1.createPortal(
246
+ /* @__PURE__ */ jsxRuntime.jsx(_private.TooltipProvider, {
247
+ children: /* @__PURE__ */ jsxRuntime.jsx(floatingToolbarContext.FloatingToolbarContext.Provider, {
248
+ value: { close },
249
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", {
250
+ role: "toolbar",
251
+ "aria-label": "Floating toolbar",
252
+ "aria-orientation": "horizontal",
253
+ className: classnames.classNames(
254
+ "lb-root lb-portal lb-elevation lb-lexical-floating-toolbar lb-lexical-toolbar",
255
+ className
256
+ ),
257
+ ref: mergedRefs,
258
+ style: {
259
+ position: strategy,
260
+ top: 0,
261
+ left: 0,
262
+ transform: isPositioned ? `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)` : "translate3d(0, -200%, 0)",
263
+ minWidth: "max-content"
264
+ },
265
+ onPointerDown: handlePointerDown,
266
+ onFocus: handleFocus,
267
+ onBlur: handleBlur,
268
+ ...props,
269
+ children: [
270
+ toolbar.applyToolbarSlot(before, slotProps),
271
+ toolbar.applyToolbarSlot(children, slotProps),
272
+ toolbar.applyToolbarSlot(after, slotProps)
273
+ ]
274
+ })
275
+ })
276
+ }),
277
+ document.body
278
+ );
279
+ }
280
+ );
281
+
282
+ exports.FLOATING_TOOLBAR_COLLISION_PADDING = FLOATING_TOOLBAR_COLLISION_PADDING;
283
+ exports.FloatingToolbar = FloatingToolbar;
284
+ //# sourceMappingURL=floating-toolbar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"floating-toolbar.js","sources":["../../src/toolbar/floating-toolbar.tsx"],"sourcesContent":["import {\n autoUpdate,\n type DetectOverflowOptions,\n flip,\n hide,\n inline,\n limitShift,\n offset,\n shift,\n size,\n useFloating,\n type UseFloatingOptions,\n} from \"@floating-ui/react-dom\";\nimport { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { TooltipProvider, useRefs } from \"@liveblocks/react-ui/_private\";\nimport { $getSelection, $isRangeSelection, FORMAT_TEXT_COMMAND } from \"lexical\";\nimport type {\n ComponentProps,\n FocusEvent as ReactFocusEvent,\n PointerEvent as ReactPointerEvent,\n} from \"react\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport { classNames } from \"../classnames\";\nimport { OPEN_FLOATING_COMPOSER_COMMAND } from \"../comments/floating-composer\";\nimport { createDOMRange } from \"../create-dom-range\";\nimport { useIsCommandRegistered } from \"../is-command-registered\";\nimport type { FloatingPosition } from \"../types\";\nimport { FloatingToolbarContext } from \"./floating-toolbar-context\";\nimport {\n applyToolbarSlot,\n Toolbar,\n type ToolbarSlot,\n type ToolbarSlotProps,\n} from \"./toolbar\";\n\nexport interface FloatingToolbarProps\n extends Omit<ComponentProps<\"div\">, \"children\"> {\n position?: FloatingPosition;\n offset?: number;\n children?: ToolbarSlot;\n before?: ToolbarSlot;\n after?: ToolbarSlot;\n}\n\nexport const FLOATING_TOOLBAR_COLLISION_PADDING = 10;\nconst FLOATING_TOOLBAR_OPEN_DELAY = 50;\n\nfunction DefaultFloatingToolbarContent() {\n const supportsTextFormat = useIsCommandRegistered(FORMAT_TEXT_COMMAND);\n const supportsThread = useIsCommandRegistered(OPEN_FLOATING_COMPOSER_COMMAND);\n\n return (\n <>\n {supportsTextFormat ? (\n <>\n <Toolbar.BlockSelector />\n <Toolbar.SectionInline />\n </>\n ) : null}\n {supportsThread ? (\n <>\n <Toolbar.Separator />\n <Toolbar.SectionCollaboration />\n </>\n ) : null}\n </>\n );\n}\n\nexport const FloatingToolbar = forwardRef<HTMLDivElement, FloatingToolbarProps>(\n (\n {\n children = DefaultFloatingToolbarContent,\n before,\n after,\n position = \"top\",\n offset: sideOffset = 6,\n onPointerDown,\n onFocus,\n onBlur,\n className,\n ...props\n },\n forwardedRef\n ) => {\n const toolbarRef = useRef<HTMLDivElement>(null);\n const [isPointerDown, setPointerDown] = useState(false);\n const [editor] = useLexicalComposerContext();\n const [isFocused, setFocused] = useState(false);\n const [isManuallyClosed, setManuallyClosed] = useState(false);\n const isEditable = editor.isEditable();\n const [hasSelectionRange, setHasSelectionRange] = useState(false);\n\n const isOpen =\n isFocused && !isPointerDown && hasSelectionRange && !isManuallyClosed;\n const [delayedIsOpen, setDelayedIsOpen] = useState(isOpen);\n const delayedIsOpenTimeoutRef = useRef<number>();\n\n // Reset the manually closed state when there's another change\n useEffect(() => {\n setManuallyClosed(false);\n }, [isFocused, hasSelectionRange, editor]);\n\n // Don't close when the focus moves from the editor to the toolbar\n useEffect(() => {\n const root = editor.getRootElement();\n\n if (!root) {\n return;\n }\n\n const handleFocus = () => {\n setFocused(true);\n };\n\n const handleBlur = (event: FocusEvent) => {\n if (\n event.relatedTarget &&\n toolbarRef.current?.contains(event.relatedTarget as Node)\n ) {\n return;\n }\n\n if (event.relatedTarget === editor.getRootElement()) {\n return;\n }\n\n setFocused(false);\n };\n\n root.addEventListener(\"focus\", handleFocus);\n root.addEventListener(\"blur\", handleBlur);\n\n return () => {\n root.removeEventListener(\"focus\", handleFocus);\n root.removeEventListener(\"blur\", handleBlur);\n };\n }, [editor]);\n\n const handleFocus = useCallback(\n (event: ReactFocusEvent<HTMLDivElement>) => {\n onFocus?.(event);\n\n if (!event.isDefaultPrevented()) {\n setFocused(true);\n }\n },\n [onFocus]\n );\n\n // Close the toolbar when the it loses focus to something else than the editor\n const handleBlur = useCallback(\n (event: ReactFocusEvent<HTMLDivElement>) => {\n onBlur?.(event);\n\n if (!event.isDefaultPrevented()) {\n if (\n event.relatedTarget &&\n toolbarRef.current?.contains(event.relatedTarget as Node)\n ) {\n return;\n }\n\n if (event.relatedTarget === editor?.getRootElement()) {\n return;\n }\n\n setFocused(false);\n }\n },\n [onBlur, editor]\n );\n\n // Delay the opening of the toolbar to avoid flickering issues\n useEffect(() => {\n if (isOpen) {\n delayedIsOpenTimeoutRef.current = window.setTimeout(() => {\n setDelayedIsOpen(true);\n }, FLOATING_TOOLBAR_OPEN_DELAY);\n } else {\n setDelayedIsOpen(false);\n }\n\n return () => {\n window.clearTimeout(delayedIsOpenTimeoutRef.current);\n };\n }, [isOpen]);\n\n const floatingOptions: UseFloatingOptions = useMemo(() => {\n const detectOverflowOptions: DetectOverflowOptions = {\n padding: FLOATING_TOOLBAR_COLLISION_PADDING,\n };\n\n return {\n strategy: \"fixed\",\n placement: position,\n middleware: [\n inline(detectOverflowOptions),\n flip({ ...detectOverflowOptions, crossAxis: false }),\n hide(detectOverflowOptions),\n shift({\n ...detectOverflowOptions,\n limiter: limitShift(),\n }),\n offset(sideOffset),\n size(detectOverflowOptions),\n ],\n whileElementsMounted: (...args) => {\n return autoUpdate(...args, {\n animationFrame: true,\n });\n },\n };\n }, [position, sideOffset]);\n const {\n refs: { setReference, setFloating },\n strategy,\n x,\n y,\n isPositioned,\n } = useFloating({\n ...floatingOptions,\n open: delayedIsOpen,\n });\n const mergedRefs = useRefs(forwardedRef, toolbarRef, setFloating);\n\n const handlePointerDown = useCallback(\n (event: ReactPointerEvent<HTMLDivElement>) => {\n onPointerDown?.(event);\n\n event.stopPropagation();\n\n // Prevent the toolbar from closing when clicking on the toolbar itself\n if (event.target === toolbarRef.current) {\n event.preventDefault();\n }\n },\n [onPointerDown]\n );\n\n useEffect(() => {\n if (!editor || !isEditable) {\n return;\n }\n\n const handlePointerDown = () => {\n setPointerDown(true);\n };\n const handlePointerUp = () => {\n setPointerDown(false);\n };\n\n document.addEventListener(\"pointerdown\", handlePointerDown);\n document.addEventListener(\"pointercancel\", handlePointerUp);\n document.addEventListener(\"pointerup\", handlePointerUp);\n\n return () => {\n document.removeEventListener(\"pointerdown\", handlePointerDown);\n document.removeEventListener(\"pointercancel\", handlePointerUp);\n document.removeEventListener(\"pointerup\", handlePointerUp);\n };\n }, [editor, isEditable]);\n\n useEffect(() => {\n const unregister = editor.registerUpdateListener(({ tags }) => {\n return editor.getEditorState().read(() => {\n // Ignore selection updates related to collaboration\n if (tags.has(\"collaboration\")) return;\n\n setManuallyClosed(false);\n\n const selection = $getSelection();\n if (!$isRangeSelection(selection) || selection.isCollapsed()) {\n setHasSelectionRange(false);\n setReference(null);\n return;\n }\n\n const { anchor, focus } = selection;\n\n const range = createDOMRange(\n editor,\n anchor.getNode(),\n anchor.offset,\n focus.getNode(),\n focus.offset\n );\n\n setHasSelectionRange(true);\n setReference(range);\n });\n });\n\n return unregister;\n }, [editor, setReference]);\n\n useEffect(() => {\n const root = editor.getRootElement();\n\n if (!root || !delayedIsOpen) {\n return;\n }\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.target !== root && event.defaultPrevented) {\n return;\n }\n\n if (event.key === \"Escape\") {\n event.preventDefault();\n event.stopPropagation();\n\n editor.focus();\n setManuallyClosed(true);\n }\n };\n\n root.addEventListener(\"keydown\", handleKeyDown);\n\n return () => {\n root.removeEventListener(\"keydown\", handleKeyDown);\n };\n }, [editor, delayedIsOpen]);\n\n const close = useCallback(() => {\n editor.focus();\n setManuallyClosed(true);\n }, [editor, setManuallyClosed]);\n\n if (!delayedIsOpen) {\n return null;\n }\n\n const slotProps: ToolbarSlotProps = { editor };\n\n return createPortal(\n <TooltipProvider>\n <FloatingToolbarContext.Provider value={{ close }}>\n <div\n role=\"toolbar\"\n aria-label=\"Floating toolbar\"\n aria-orientation=\"horizontal\"\n className={classNames(\n \"lb-root lb-portal lb-elevation lb-lexical-floating-toolbar lb-lexical-toolbar\",\n className\n )}\n ref={mergedRefs}\n style={{\n position: strategy,\n top: 0,\n left: 0,\n transform: isPositioned\n ? `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`\n : \"translate3d(0, -200%, 0)\",\n minWidth: \"max-content\",\n }}\n onPointerDown={handlePointerDown}\n onFocus={handleFocus}\n onBlur={handleBlur}\n {...props}\n >\n {applyToolbarSlot(before, slotProps)}\n {applyToolbarSlot(children, slotProps)}\n {applyToolbarSlot(after, slotProps)}\n </div>\n </FloatingToolbarContext.Provider>\n </TooltipProvider>,\n document.body\n );\n }\n);\n"],"names":["useIsCommandRegistered","FORMAT_TEXT_COMMAND","OPEN_FLOATING_COMPOSER_COMMAND","jsxs","Fragment","jsx","Toolbar","forwardRef","useRef","useState","useLexicalComposerContext","useEffect","handleFocus","handleBlur","useCallback","useMemo","inline","flip","hide","shift","limitShift","offset","size","autoUpdate","useFloating","useRefs","handlePointerDown","$getSelection","$isRangeSelection","createDOMRange","createPortal","TooltipProvider","FloatingToolbarContext","classNames","applyToolbarSlot"],"mappings":"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"}
@@ -0,0 +1,281 @@
1
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
+ import { inline, flip, hide, shift, limitShift, offset, size, autoUpdate, useFloating } from '@floating-ui/react-dom';
3
+ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
4
+ import { useRefs, TooltipProvider } from '@liveblocks/react-ui/_private';
5
+ import { FORMAT_TEXT_COMMAND, $getSelection, $isRangeSelection } from 'lexical';
6
+ import { forwardRef, useRef, useState, useEffect, useCallback, useMemo } from 'react';
7
+ import { createPortal } from 'react-dom';
8
+ import { classNames } from '../classnames.mjs';
9
+ import { OPEN_FLOATING_COMPOSER_COMMAND } from '../comments/floating-composer.mjs';
10
+ import { createDOMRange } from '../create-dom-range.mjs';
11
+ import { useIsCommandRegistered } from '../is-command-registered.mjs';
12
+ import { FloatingToolbarContext } from './floating-toolbar-context.mjs';
13
+ import { Toolbar, applyToolbarSlot } from './toolbar.mjs';
14
+
15
+ const FLOATING_TOOLBAR_COLLISION_PADDING = 10;
16
+ const FLOATING_TOOLBAR_OPEN_DELAY = 50;
17
+ function DefaultFloatingToolbarContent() {
18
+ const supportsTextFormat = useIsCommandRegistered(FORMAT_TEXT_COMMAND);
19
+ const supportsThread = useIsCommandRegistered(OPEN_FLOATING_COMPOSER_COMMAND);
20
+ return /* @__PURE__ */ jsxs(Fragment, {
21
+ children: [
22
+ supportsTextFormat ? /* @__PURE__ */ jsxs(Fragment, {
23
+ children: [
24
+ /* @__PURE__ */ jsx(Toolbar.BlockSelector, {}),
25
+ /* @__PURE__ */ jsx(Toolbar.SectionInline, {})
26
+ ]
27
+ }) : null,
28
+ supportsThread ? /* @__PURE__ */ jsxs(Fragment, {
29
+ children: [
30
+ /* @__PURE__ */ jsx(Toolbar.Separator, {}),
31
+ /* @__PURE__ */ jsx(Toolbar.SectionCollaboration, {})
32
+ ]
33
+ }) : null
34
+ ]
35
+ });
36
+ }
37
+ const FloatingToolbar = forwardRef(
38
+ ({
39
+ children = DefaultFloatingToolbarContent,
40
+ before,
41
+ after,
42
+ position = "top",
43
+ offset: sideOffset = 6,
44
+ onPointerDown,
45
+ onFocus,
46
+ onBlur,
47
+ className,
48
+ ...props
49
+ }, forwardedRef) => {
50
+ const toolbarRef = useRef(null);
51
+ const [isPointerDown, setPointerDown] = useState(false);
52
+ const [editor] = useLexicalComposerContext();
53
+ const [isFocused, setFocused] = useState(false);
54
+ const [isManuallyClosed, setManuallyClosed] = useState(false);
55
+ const isEditable = editor.isEditable();
56
+ const [hasSelectionRange, setHasSelectionRange] = useState(false);
57
+ const isOpen = isFocused && !isPointerDown && hasSelectionRange && !isManuallyClosed;
58
+ const [delayedIsOpen, setDelayedIsOpen] = useState(isOpen);
59
+ const delayedIsOpenTimeoutRef = useRef();
60
+ useEffect(() => {
61
+ setManuallyClosed(false);
62
+ }, [isFocused, hasSelectionRange, editor]);
63
+ useEffect(() => {
64
+ const root = editor.getRootElement();
65
+ if (!root) {
66
+ return;
67
+ }
68
+ const handleFocus2 = () => {
69
+ setFocused(true);
70
+ };
71
+ const handleBlur2 = (event) => {
72
+ if (event.relatedTarget && toolbarRef.current?.contains(event.relatedTarget)) {
73
+ return;
74
+ }
75
+ if (event.relatedTarget === editor.getRootElement()) {
76
+ return;
77
+ }
78
+ setFocused(false);
79
+ };
80
+ root.addEventListener("focus", handleFocus2);
81
+ root.addEventListener("blur", handleBlur2);
82
+ return () => {
83
+ root.removeEventListener("focus", handleFocus2);
84
+ root.removeEventListener("blur", handleBlur2);
85
+ };
86
+ }, [editor]);
87
+ const handleFocus = useCallback(
88
+ (event) => {
89
+ onFocus?.(event);
90
+ if (!event.isDefaultPrevented()) {
91
+ setFocused(true);
92
+ }
93
+ },
94
+ [onFocus]
95
+ );
96
+ const handleBlur = useCallback(
97
+ (event) => {
98
+ onBlur?.(event);
99
+ if (!event.isDefaultPrevented()) {
100
+ if (event.relatedTarget && toolbarRef.current?.contains(event.relatedTarget)) {
101
+ return;
102
+ }
103
+ if (event.relatedTarget === editor?.getRootElement()) {
104
+ return;
105
+ }
106
+ setFocused(false);
107
+ }
108
+ },
109
+ [onBlur, editor]
110
+ );
111
+ useEffect(() => {
112
+ if (isOpen) {
113
+ delayedIsOpenTimeoutRef.current = window.setTimeout(() => {
114
+ setDelayedIsOpen(true);
115
+ }, FLOATING_TOOLBAR_OPEN_DELAY);
116
+ } else {
117
+ setDelayedIsOpen(false);
118
+ }
119
+ return () => {
120
+ window.clearTimeout(delayedIsOpenTimeoutRef.current);
121
+ };
122
+ }, [isOpen]);
123
+ const floatingOptions = useMemo(() => {
124
+ const detectOverflowOptions = {
125
+ padding: FLOATING_TOOLBAR_COLLISION_PADDING
126
+ };
127
+ return {
128
+ strategy: "fixed",
129
+ placement: position,
130
+ middleware: [
131
+ inline(detectOverflowOptions),
132
+ flip({ ...detectOverflowOptions, crossAxis: false }),
133
+ hide(detectOverflowOptions),
134
+ shift({
135
+ ...detectOverflowOptions,
136
+ limiter: limitShift()
137
+ }),
138
+ offset(sideOffset),
139
+ size(detectOverflowOptions)
140
+ ],
141
+ whileElementsMounted: (...args) => {
142
+ return autoUpdate(...args, {
143
+ animationFrame: true
144
+ });
145
+ }
146
+ };
147
+ }, [position, sideOffset]);
148
+ const {
149
+ refs: { setReference, setFloating },
150
+ strategy,
151
+ x,
152
+ y,
153
+ isPositioned
154
+ } = useFloating({
155
+ ...floatingOptions,
156
+ open: delayedIsOpen
157
+ });
158
+ const mergedRefs = useRefs(forwardedRef, toolbarRef, setFloating);
159
+ const handlePointerDown = useCallback(
160
+ (event) => {
161
+ onPointerDown?.(event);
162
+ event.stopPropagation();
163
+ if (event.target === toolbarRef.current) {
164
+ event.preventDefault();
165
+ }
166
+ },
167
+ [onPointerDown]
168
+ );
169
+ useEffect(() => {
170
+ if (!editor || !isEditable) {
171
+ return;
172
+ }
173
+ const handlePointerDown2 = () => {
174
+ setPointerDown(true);
175
+ };
176
+ const handlePointerUp = () => {
177
+ setPointerDown(false);
178
+ };
179
+ document.addEventListener("pointerdown", handlePointerDown2);
180
+ document.addEventListener("pointercancel", handlePointerUp);
181
+ document.addEventListener("pointerup", handlePointerUp);
182
+ return () => {
183
+ document.removeEventListener("pointerdown", handlePointerDown2);
184
+ document.removeEventListener("pointercancel", handlePointerUp);
185
+ document.removeEventListener("pointerup", handlePointerUp);
186
+ };
187
+ }, [editor, isEditable]);
188
+ useEffect(() => {
189
+ const unregister = editor.registerUpdateListener(({ tags }) => {
190
+ return editor.getEditorState().read(() => {
191
+ if (tags.has("collaboration"))
192
+ return;
193
+ setManuallyClosed(false);
194
+ const selection = $getSelection();
195
+ if (!$isRangeSelection(selection) || selection.isCollapsed()) {
196
+ setHasSelectionRange(false);
197
+ setReference(null);
198
+ return;
199
+ }
200
+ const { anchor, focus } = selection;
201
+ const range = createDOMRange(
202
+ editor,
203
+ anchor.getNode(),
204
+ anchor.offset,
205
+ focus.getNode(),
206
+ focus.offset
207
+ );
208
+ setHasSelectionRange(true);
209
+ setReference(range);
210
+ });
211
+ });
212
+ return unregister;
213
+ }, [editor, setReference]);
214
+ useEffect(() => {
215
+ const root = editor.getRootElement();
216
+ if (!root || !delayedIsOpen) {
217
+ return;
218
+ }
219
+ const handleKeyDown = (event) => {
220
+ if (event.target !== root && event.defaultPrevented) {
221
+ return;
222
+ }
223
+ if (event.key === "Escape") {
224
+ event.preventDefault();
225
+ event.stopPropagation();
226
+ editor.focus();
227
+ setManuallyClosed(true);
228
+ }
229
+ };
230
+ root.addEventListener("keydown", handleKeyDown);
231
+ return () => {
232
+ root.removeEventListener("keydown", handleKeyDown);
233
+ };
234
+ }, [editor, delayedIsOpen]);
235
+ const close = useCallback(() => {
236
+ editor.focus();
237
+ setManuallyClosed(true);
238
+ }, [editor, setManuallyClosed]);
239
+ if (!delayedIsOpen) {
240
+ return null;
241
+ }
242
+ const slotProps = { editor };
243
+ return createPortal(
244
+ /* @__PURE__ */ jsx(TooltipProvider, {
245
+ children: /* @__PURE__ */ jsx(FloatingToolbarContext.Provider, {
246
+ value: { close },
247
+ children: /* @__PURE__ */ jsxs("div", {
248
+ role: "toolbar",
249
+ "aria-label": "Floating toolbar",
250
+ "aria-orientation": "horizontal",
251
+ className: classNames(
252
+ "lb-root lb-portal lb-elevation lb-lexical-floating-toolbar lb-lexical-toolbar",
253
+ className
254
+ ),
255
+ ref: mergedRefs,
256
+ style: {
257
+ position: strategy,
258
+ top: 0,
259
+ left: 0,
260
+ transform: isPositioned ? `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)` : "translate3d(0, -200%, 0)",
261
+ minWidth: "max-content"
262
+ },
263
+ onPointerDown: handlePointerDown,
264
+ onFocus: handleFocus,
265
+ onBlur: handleBlur,
266
+ ...props,
267
+ children: [
268
+ applyToolbarSlot(before, slotProps),
269
+ applyToolbarSlot(children, slotProps),
270
+ applyToolbarSlot(after, slotProps)
271
+ ]
272
+ })
273
+ })
274
+ }),
275
+ document.body
276
+ );
277
+ }
278
+ );
279
+
280
+ export { FLOATING_TOOLBAR_COLLISION_PADDING, FloatingToolbar };
281
+ //# sourceMappingURL=floating-toolbar.mjs.map