@liveblocks/react-lexical 2.16.0-toolbars2 → 2.16.0-toolbars4

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.
@@ -7,7 +7,7 @@ function isParentRootOrShadowRoot(node) {
7
7
  const parent = node.getParent();
8
8
  return parent !== null && lexical.$isRootOrShadowRoot(parent);
9
9
  }
10
- function getSelectedBlockElement(editor) {
10
+ function getActiveBlockElement(editor) {
11
11
  return editor.getEditorState().read(() => {
12
12
  const selection = lexical.$getSelection();
13
13
  if (!lexical.$isRangeSelection(selection))
@@ -22,5 +22,5 @@ function getSelectedBlockElement(editor) {
22
22
  });
23
23
  }
24
24
 
25
- exports.getSelectedBlockElement = getSelectedBlockElement;
26
- //# sourceMappingURL=get-selected-block-element.js.map
25
+ exports.getActiveBlockElement = getActiveBlockElement;
26
+ //# sourceMappingURL=get-active-block-element.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-active-block-element.js","sources":["../src/get-active-block-element.ts"],"sourcesContent":["import { $findMatchingParent } from \"@lexical/utils\";\nimport type { LexicalEditor, LexicalNode } from \"lexical\";\nimport { $getSelection, $isRangeSelection, $isRootOrShadowRoot } from \"lexical\";\n\nfunction isParentRootOrShadowRoot(node: LexicalNode) {\n const parent = node.getParent();\n\n return parent !== null && $isRootOrShadowRoot(parent);\n}\n\nexport function getActiveBlockElement(editor: LexicalEditor) {\n return editor.getEditorState().read(() => {\n const selection = $getSelection();\n\n if (!$isRangeSelection(selection)) return null;\n\n const anchor = selection.anchor.getNode();\n const focus = selection.focus.getNode();\n\n const commonAncestor = anchor.getCommonAncestor(focus);\n\n if (!commonAncestor || $isRootOrShadowRoot(commonAncestor)) return null;\n\n const element = isParentRootOrShadowRoot(commonAncestor)\n ? commonAncestor\n : $findMatchingParent(commonAncestor, isParentRootOrShadowRoot);\n\n return element;\n });\n}\n"],"names":["$isRootOrShadowRoot","$getSelection","$isRangeSelection","$findMatchingParent"],"mappings":";;;;;AAIA,SAAS,yBAAyB,IAAmB,EAAA;AACnD,EAAM,MAAA,MAAA,GAAS,KAAK,SAAU,EAAA,CAAA;AAE9B,EAAO,OAAA,MAAA,KAAW,IAAQ,IAAAA,2BAAA,CAAoB,MAAM,CAAA,CAAA;AACtD,CAAA;AAEO,SAAS,sBAAsB,MAAuB,EAAA;AAC3D,EAAA,OAAO,MAAO,CAAA,cAAA,EAAiB,CAAA,IAAA,CAAK,MAAM;AACxC,IAAA,MAAM,YAAYC,qBAAc,EAAA,CAAA;AAEhC,IAAI,IAAA,CAACC,0BAAkB,SAAS,CAAA;AAAG,MAAO,OAAA,IAAA,CAAA;AAE1C,IAAM,MAAA,MAAA,GAAS,SAAU,CAAA,MAAA,CAAO,OAAQ,EAAA,CAAA;AACxC,IAAM,MAAA,KAAA,GAAQ,SAAU,CAAA,KAAA,CAAM,OAAQ,EAAA,CAAA;AAEtC,IAAM,MAAA,cAAA,GAAiB,MAAO,CAAA,iBAAA,CAAkB,KAAK,CAAA,CAAA;AAErD,IAAI,IAAA,CAAC,cAAkB,IAAAF,2BAAA,CAAoB,cAAc,CAAA;AAAG,MAAO,OAAA,IAAA,CAAA;AAEnE,IAAA,MAAM,UAAU,wBAAyB,CAAA,cAAc,IACnD,cACA,GAAAG,yBAAA,CAAoB,gBAAgB,wBAAwB,CAAA,CAAA;AAEhE,IAAO,OAAA,OAAA,CAAA;AAAA,GACR,CAAA,CAAA;AACH;;;;"}
@@ -5,7 +5,7 @@ function isParentRootOrShadowRoot(node) {
5
5
  const parent = node.getParent();
6
6
  return parent !== null && $isRootOrShadowRoot(parent);
7
7
  }
8
- function getSelectedBlockElement(editor) {
8
+ function getActiveBlockElement(editor) {
9
9
  return editor.getEditorState().read(() => {
10
10
  const selection = $getSelection();
11
11
  if (!$isRangeSelection(selection))
@@ -20,5 +20,5 @@ function getSelectedBlockElement(editor) {
20
20
  });
21
21
  }
22
22
 
23
- export { getSelectedBlockElement };
24
- //# sourceMappingURL=get-selected-block-element.mjs.map
23
+ export { getActiveBlockElement };
24
+ //# sourceMappingURL=get-active-block-element.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-active-block-element.mjs","sources":["../src/get-active-block-element.ts"],"sourcesContent":["import { $findMatchingParent } from \"@lexical/utils\";\nimport type { LexicalEditor, LexicalNode } from \"lexical\";\nimport { $getSelection, $isRangeSelection, $isRootOrShadowRoot } from \"lexical\";\n\nfunction isParentRootOrShadowRoot(node: LexicalNode) {\n const parent = node.getParent();\n\n return parent !== null && $isRootOrShadowRoot(parent);\n}\n\nexport function getActiveBlockElement(editor: LexicalEditor) {\n return editor.getEditorState().read(() => {\n const selection = $getSelection();\n\n if (!$isRangeSelection(selection)) return null;\n\n const anchor = selection.anchor.getNode();\n const focus = selection.focus.getNode();\n\n const commonAncestor = anchor.getCommonAncestor(focus);\n\n if (!commonAncestor || $isRootOrShadowRoot(commonAncestor)) return null;\n\n const element = isParentRootOrShadowRoot(commonAncestor)\n ? commonAncestor\n : $findMatchingParent(commonAncestor, isParentRootOrShadowRoot);\n\n return element;\n });\n}\n"],"names":[],"mappings":";;;AAIA,SAAS,yBAAyB,IAAmB,EAAA;AACnD,EAAM,MAAA,MAAA,GAAS,KAAK,SAAU,EAAA,CAAA;AAE9B,EAAO,OAAA,MAAA,KAAW,IAAQ,IAAA,mBAAA,CAAoB,MAAM,CAAA,CAAA;AACtD,CAAA;AAEO,SAAS,sBAAsB,MAAuB,EAAA;AAC3D,EAAA,OAAO,MAAO,CAAA,cAAA,EAAiB,CAAA,IAAA,CAAK,MAAM;AACxC,IAAA,MAAM,YAAY,aAAc,EAAA,CAAA;AAEhC,IAAI,IAAA,CAAC,kBAAkB,SAAS,CAAA;AAAG,MAAO,OAAA,IAAA,CAAA;AAE1C,IAAM,MAAA,MAAA,GAAS,SAAU,CAAA,MAAA,CAAO,OAAQ,EAAA,CAAA;AACxC,IAAM,MAAA,KAAA,GAAQ,SAAU,CAAA,KAAA,CAAM,OAAQ,EAAA,CAAA;AAEtC,IAAM,MAAA,cAAA,GAAiB,MAAO,CAAA,iBAAA,CAAkB,KAAK,CAAA,CAAA;AAErD,IAAI,IAAA,CAAC,cAAkB,IAAA,mBAAA,CAAoB,cAAc,CAAA;AAAG,MAAO,OAAA,IAAA,CAAA;AAEnE,IAAA,MAAM,UAAU,wBAAyB,CAAA,cAAc,IACnD,cACA,GAAA,mBAAA,CAAoB,gBAAgB,wBAAwB,CAAA,CAAA;AAEhE,IAAO,OAAA,OAAA,CAAA;AAAA,GACR,CAAA,CAAA;AACH;;;;"}
package/dist/index.d.mts CHANGED
@@ -4,7 +4,7 @@ import { ThreadProps, ComposerProps } from '@liveblocks/react-ui';
4
4
  import * as react from 'react';
5
5
  import { ComponentPropsWithoutRef, ComponentType, HTMLAttributes, ReactNode, ComponentProps } from 'react';
6
6
  import * as lexical from 'lexical';
7
- import { LexicalCommand, LexicalEditor, LexicalNode } from 'lexical';
7
+ import { LexicalCommand, LexicalEditor, LexicalNode, TextFormatType } from 'lexical';
8
8
  import { InitialConfigType } from '@lexical/react/LexicalComposer';
9
9
 
10
10
  type AnchoredThreadsComponents = {
@@ -77,6 +77,10 @@ interface FloatingThreadsProps<M extends BaseMetadata = DM> extends Omit<HTMLAtt
77
77
  }
78
78
  declare function FloatingThreads({ threads, components, ...props }: FloatingThreadsProps): react_jsx_runtime.JSX.Element | null;
79
79
 
80
+ declare function getActiveBlockElement(editor: LexicalEditor): LexicalNode | null;
81
+
82
+ declare function isTextFormatActive(editor: LexicalEditor, format: TextFormatType): boolean;
83
+
80
84
  /**
81
85
  * Function that takes a Lexical editor config and modifies it to add the necessary
82
86
  * `nodes` and `theme` to make `LiveblocksPlugin` works correctly.
@@ -186,47 +190,228 @@ interface ToolbarSlotProps {
186
190
  }
187
191
  type ToolbarSlot = ReactNode | ComponentType<ToolbarSlotProps>;
188
192
  interface ToolbarProps extends Omit<ComponentProps<"div">, "children"> {
193
+ /**
194
+ * The content of the toolbar, overriding the default content.
195
+ * Use the `before` and `after` props if you want to keep and extend the default content.
196
+ */
189
197
  children?: ToolbarSlot;
198
+ /**
199
+ * The content to display at the start of the toolbar.
200
+ */
190
201
  before?: ToolbarSlot;
202
+ /**
203
+ * The content to display at the end of the toolbar.
204
+ */
191
205
  after?: ToolbarSlot;
192
206
  }
193
207
  interface ToolbarButtonProps extends ComponentProps<"button"> {
194
- icon?: ReactNode;
208
+ /**
209
+ * The name of this button displayed in its tooltip.
210
+ */
195
211
  name: string;
212
+ /**
213
+ * An optional icon displayed in this button.
214
+ */
215
+ icon?: ReactNode;
216
+ /**
217
+ * An optional keyboard shortcut displayed in this button's tooltip.
218
+ *
219
+ * @example
220
+ * "Mod-Alt-B" → "⌘⌥B" in Apple environments, "⌃⌥B" otherwise
221
+ * "Ctrl-Shift-Escape" → "⌃⇧⎋"
222
+ * "Space" → "␣"
223
+ */
196
224
  shortcut?: string;
197
225
  }
198
226
  interface ToolbarToggleProps extends ToolbarButtonProps {
227
+ /**
228
+ * Whether the button is toggled.
229
+ */
199
230
  active: boolean;
200
231
  }
232
+ type ToolbarSeparatorProps = ComponentProps<"div">;
201
233
  interface ToolbarBlockSelectorItem {
234
+ /**
235
+ * The name of this block element, displayed as the label of this item.
236
+ */
202
237
  name: string;
203
- isActive: (selectedBlockElement: LexicalNode | null, editor: LexicalEditor) => boolean;
238
+ /**
239
+ * Optionally replace the name used as the label of this item by any content.
240
+ */
241
+ label?: ReactNode;
242
+ /**
243
+ * An optional icon displayed in this item.
244
+ */
245
+ icon?: ReactNode;
246
+ /**
247
+ * Whether this block element is currently active.
248
+ * Set to `"default"` to display this item when no other item is active.
249
+ */
250
+ isActive: ((element: LexicalNode | null, editor: LexicalEditor) => boolean) | "default";
251
+ /**
252
+ * A callback invoked when this item is selected.
253
+ */
204
254
  setActive: (editor: LexicalEditor) => void;
205
255
  }
206
256
  interface ToolbarBlockSelectorProps extends ComponentProps<"button"> {
207
- items?: ToolbarBlockSelectorItem[];
257
+ /**
258
+ * The items displayed in this block selector.
259
+ * When provided as an array, the default items are overridden. To avoid this,
260
+ * a function can be provided instead and it will receive the default items.
261
+ *
262
+ * @example
263
+ * <Toolbar.BlockSelector
264
+ * items={[
265
+ * {
266
+ * name: "Text",
267
+ * isActive: "default",
268
+ * setActive: () => { ... },
269
+ * },
270
+ * {
271
+ * name: "Heading 1",
272
+ * isActive: () => { ... },
273
+ * setActive: () => { ... },
274
+ * },
275
+ * ]}
276
+ * />
277
+ *
278
+ * @example
279
+ * <Toolbar.BlockSelector
280
+ * items={(defaultItems) => [
281
+ * ...defaultItems,
282
+ * {
283
+ * name: "Custom block",
284
+ * isActive: () => { ... },
285
+ * setActive: () => { ... },
286
+ * },
287
+ * ]}
288
+ * />
289
+ */
290
+ items?: ToolbarBlockSelectorItem[] | ((defaultItems: ToolbarBlockSelectorItem[]) => ToolbarBlockSelectorItem[]);
208
291
  }
209
292
  declare function ToolbarSectionHistory(): react_jsx_runtime.JSX.Element;
210
293
  declare function ToolbarSectionInline(): react_jsx_runtime.JSX.Element | null;
211
294
  declare function ToolbarSectionCollaboration(): react_jsx_runtime.JSX.Element;
295
+ /**
296
+ * A static toolbar containing actions and values related to the editor.
297
+ *
298
+ * @example
299
+ * <Toolbar />
300
+ *
301
+ * @example
302
+ * <Toolbar >
303
+ * <Toolbar.BlockSelector />
304
+ * <Toolbar.Separator />
305
+ * <Toolbar.SectionInline />
306
+ * <Toolbar.Separator />
307
+ * <Toolbar.Button name="Custom action" onClick={() => { ... }} icon={<Icon.QuestionMark />} />
308
+ * </Toolbar>
309
+ */
212
310
  declare const Toolbar: react.ForwardRefExoticComponent<Omit<ToolbarProps, "ref"> & react.RefAttributes<HTMLDivElement>> & {
311
+ /**
312
+ * A button for triggering actions.
313
+ *
314
+ * @example
315
+ * <Toolbar.Button name="Comment" shortcut="Mod-Shift-E" onClick={() => { ... }}>Comment</Toolbar.Button>
316
+ *
317
+ * @example
318
+ * <Toolbar.Button name="Mention someone" icon={<Icon.Mention />} onClick={() => { ... }} />
319
+ */
213
320
  Button: react.ForwardRefExoticComponent<Omit<ToolbarButtonProps, "ref"> & react.RefAttributes<HTMLButtonElement>>;
321
+ /**
322
+ * A toggle button for values that can be active or inactive.
323
+ *
324
+ * @example
325
+ * <Toolbar.Toggle name="Bold" active={isBold}>Bold</Toolbar.Toggle>
326
+ *
327
+ * @example
328
+ * <Toolbar.Toggle name="Italic" icon={<Icon.Italic />} shortcut="Mod-I" active={isItalic} onClick={() => { ... }} />
329
+ */
214
330
  Toggle: react.ForwardRefExoticComponent<Omit<ToolbarToggleProps, "ref"> & react.RefAttributes<HTMLButtonElement>>;
331
+ /**
332
+ * A dropdown selector to switch between different block types.
333
+ *
334
+ * @example
335
+ * <Toolbar.BlockSelector />
336
+ */
337
+ BlockSelector: react.ForwardRefExoticComponent<Omit<ToolbarBlockSelectorProps, "ref"> & react.RefAttributes<HTMLButtonElement>>;
338
+ /**
339
+ * A visual (and accessible) separator to separate sections in a toolbar.
340
+ */
215
341
  Separator: react.ForwardRefExoticComponent<Omit<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
342
+ /**
343
+ * A section containing history actions. (e.g. undo, redo)
344
+ */
216
345
  SectionHistory: typeof ToolbarSectionHistory;
346
+ /**
347
+ * A section containing inline formatting actions. (e.g. bold, italic, underline, ...)
348
+ */
217
349
  SectionInline: typeof ToolbarSectionInline;
350
+ /**
351
+ * A section containing collaborative actions. (e.g. adding a comment)
352
+ */
218
353
  SectionCollaboration: typeof ToolbarSectionCollaboration;
219
- BlockSelector: react.ForwardRefExoticComponent<Omit<ToolbarBlockSelectorProps, "ref"> & react.RefAttributes<HTMLButtonElement>>;
220
354
  };
221
355
 
222
356
  interface FloatingToolbarProps extends Omit<ComponentProps<"div">, "children"> {
357
+ /**
358
+ * The vertical position of the floating toolbar.
359
+ */
223
360
  position?: FloatingPosition;
361
+ /**
362
+ * The vertical offset of the floating toolbar from the selection.
363
+ */
224
364
  offset?: number;
365
+ /**
366
+ * The content of the floating toolbar, overriding the default content.
367
+ * Use the `before` and `after` props if you want to keep and extend the default content.
368
+ */
225
369
  children?: ToolbarSlot;
370
+ /**
371
+ * The content to display at the start of the floating toolbar.
372
+ */
226
373
  before?: ToolbarSlot;
374
+ /**
375
+ * The content to display at the end of the floating toolbar.
376
+ */
227
377
  after?: ToolbarSlot;
228
378
  }
379
+ /**
380
+ * A floating toolbar attached to the selection and containing actions and values related to the editor.
381
+ *
382
+ * @example
383
+ * <FloatingToolbar />
384
+ *
385
+ * @example
386
+ * <FloatingToolbar>
387
+ * <Toolbar.BlockSelector />
388
+ * <Toolbar.Separator />
389
+ * <Toolbar.SectionInline />
390
+ * <Toolbar.Separator />
391
+ * <Toolbar.Button name="Custom action" onClick={() => { ... }} icon={<Icon.QuestionMark />} />
392
+ * </FloatingToolbar>
393
+ */
229
394
  declare const FloatingToolbar: react.ForwardRefExoticComponent<Omit<FloatingToolbarProps, "ref"> & react.RefAttributes<HTMLDivElement>> & {
395
+ /**
396
+ * A component that can be wrapped around elements which are rendered outside of the floating
397
+ * toolbar (e.g. portals) to prevent the toolbar from closing when clicking/focusing within them.
398
+ *
399
+ * @example
400
+ * <FloatingToolbar>
401
+ * <Popover.Root>
402
+ * <Popover.Trigger asChild>
403
+ * <Toolbar.Button>Open popover</Toolbar.Button>
404
+ * </Popover.Trigger>
405
+ * <Popover.Portal>
406
+ * <FloatingToolbar.External>
407
+ * <Popover.Content>
408
+ * This popover is rendered outside of the floating toolbar, but the toolbar will not close when clicking/focusing within it.
409
+ * </Popover.Content>
410
+ * </FloatingToolbar.External>
411
+ * </Popover.Portal>
412
+ * </Popover.Root>
413
+ * </FloatingToolbar>
414
+ */
230
415
  External: react.ForwardRefExoticComponent<Omit<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
231
416
  };
232
417
 
@@ -242,4 +427,4 @@ interface HistoryVersionPreviewProps extends ComponentPropsWithoutRef<"div"> {
242
427
  */
243
428
  declare const HistoryVersionPreview: react.ForwardRefExoticComponent<HistoryVersionPreviewProps & react.RefAttributes<HTMLDivElement>>;
244
429
 
245
- export { AnchoredThreads, AnchoredThreadsProps, FloatingComposer, FloatingComposerProps, FloatingThreads, FloatingThreadsProps, FloatingToolbar, FloatingToolbarProps, HistoryVersionPreview, HistoryVersionPreviewProps, LiveblocksPlugin, OPEN_FLOATING_COMPOSER_COMMAND, Toolbar, ToolbarProps, liveblocksConfig, useEditorStatus, useIsEditorReady, useIsThreadActive };
430
+ export { AnchoredThreads, AnchoredThreadsProps, FloatingComposer, FloatingComposerProps, FloatingThreads, FloatingThreadsProps, FloatingToolbar, FloatingToolbarProps, HistoryVersionPreview, HistoryVersionPreviewProps, LiveblocksPlugin, OPEN_FLOATING_COMPOSER_COMMAND, Toolbar, ToolbarBlockSelectorItem, ToolbarBlockSelectorProps, ToolbarButtonProps, ToolbarProps, ToolbarSeparatorProps, ToolbarToggleProps, getActiveBlockElement, isTextFormatActive, liveblocksConfig, useEditorStatus, useIsEditorReady, useIsThreadActive };
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ import { ThreadProps, ComposerProps } from '@liveblocks/react-ui';
4
4
  import * as react from 'react';
5
5
  import { ComponentPropsWithoutRef, ComponentType, HTMLAttributes, ReactNode, ComponentProps } from 'react';
6
6
  import * as lexical from 'lexical';
7
- import { LexicalCommand, LexicalEditor, LexicalNode } from 'lexical';
7
+ import { LexicalCommand, LexicalEditor, LexicalNode, TextFormatType } from 'lexical';
8
8
  import { InitialConfigType } from '@lexical/react/LexicalComposer';
9
9
 
10
10
  type AnchoredThreadsComponents = {
@@ -77,6 +77,10 @@ interface FloatingThreadsProps<M extends BaseMetadata = DM> extends Omit<HTMLAtt
77
77
  }
78
78
  declare function FloatingThreads({ threads, components, ...props }: FloatingThreadsProps): react_jsx_runtime.JSX.Element | null;
79
79
 
80
+ declare function getActiveBlockElement(editor: LexicalEditor): LexicalNode | null;
81
+
82
+ declare function isTextFormatActive(editor: LexicalEditor, format: TextFormatType): boolean;
83
+
80
84
  /**
81
85
  * Function that takes a Lexical editor config and modifies it to add the necessary
82
86
  * `nodes` and `theme` to make `LiveblocksPlugin` works correctly.
@@ -186,47 +190,228 @@ interface ToolbarSlotProps {
186
190
  }
187
191
  type ToolbarSlot = ReactNode | ComponentType<ToolbarSlotProps>;
188
192
  interface ToolbarProps extends Omit<ComponentProps<"div">, "children"> {
193
+ /**
194
+ * The content of the toolbar, overriding the default content.
195
+ * Use the `before` and `after` props if you want to keep and extend the default content.
196
+ */
189
197
  children?: ToolbarSlot;
198
+ /**
199
+ * The content to display at the start of the toolbar.
200
+ */
190
201
  before?: ToolbarSlot;
202
+ /**
203
+ * The content to display at the end of the toolbar.
204
+ */
191
205
  after?: ToolbarSlot;
192
206
  }
193
207
  interface ToolbarButtonProps extends ComponentProps<"button"> {
194
- icon?: ReactNode;
208
+ /**
209
+ * The name of this button displayed in its tooltip.
210
+ */
195
211
  name: string;
212
+ /**
213
+ * An optional icon displayed in this button.
214
+ */
215
+ icon?: ReactNode;
216
+ /**
217
+ * An optional keyboard shortcut displayed in this button's tooltip.
218
+ *
219
+ * @example
220
+ * "Mod-Alt-B" → "⌘⌥B" in Apple environments, "⌃⌥B" otherwise
221
+ * "Ctrl-Shift-Escape" → "⌃⇧⎋"
222
+ * "Space" → "␣"
223
+ */
196
224
  shortcut?: string;
197
225
  }
198
226
  interface ToolbarToggleProps extends ToolbarButtonProps {
227
+ /**
228
+ * Whether the button is toggled.
229
+ */
199
230
  active: boolean;
200
231
  }
232
+ type ToolbarSeparatorProps = ComponentProps<"div">;
201
233
  interface ToolbarBlockSelectorItem {
234
+ /**
235
+ * The name of this block element, displayed as the label of this item.
236
+ */
202
237
  name: string;
203
- isActive: (selectedBlockElement: LexicalNode | null, editor: LexicalEditor) => boolean;
238
+ /**
239
+ * Optionally replace the name used as the label of this item by any content.
240
+ */
241
+ label?: ReactNode;
242
+ /**
243
+ * An optional icon displayed in this item.
244
+ */
245
+ icon?: ReactNode;
246
+ /**
247
+ * Whether this block element is currently active.
248
+ * Set to `"default"` to display this item when no other item is active.
249
+ */
250
+ isActive: ((element: LexicalNode | null, editor: LexicalEditor) => boolean) | "default";
251
+ /**
252
+ * A callback invoked when this item is selected.
253
+ */
204
254
  setActive: (editor: LexicalEditor) => void;
205
255
  }
206
256
  interface ToolbarBlockSelectorProps extends ComponentProps<"button"> {
207
- items?: ToolbarBlockSelectorItem[];
257
+ /**
258
+ * The items displayed in this block selector.
259
+ * When provided as an array, the default items are overridden. To avoid this,
260
+ * a function can be provided instead and it will receive the default items.
261
+ *
262
+ * @example
263
+ * <Toolbar.BlockSelector
264
+ * items={[
265
+ * {
266
+ * name: "Text",
267
+ * isActive: "default",
268
+ * setActive: () => { ... },
269
+ * },
270
+ * {
271
+ * name: "Heading 1",
272
+ * isActive: () => { ... },
273
+ * setActive: () => { ... },
274
+ * },
275
+ * ]}
276
+ * />
277
+ *
278
+ * @example
279
+ * <Toolbar.BlockSelector
280
+ * items={(defaultItems) => [
281
+ * ...defaultItems,
282
+ * {
283
+ * name: "Custom block",
284
+ * isActive: () => { ... },
285
+ * setActive: () => { ... },
286
+ * },
287
+ * ]}
288
+ * />
289
+ */
290
+ items?: ToolbarBlockSelectorItem[] | ((defaultItems: ToolbarBlockSelectorItem[]) => ToolbarBlockSelectorItem[]);
208
291
  }
209
292
  declare function ToolbarSectionHistory(): react_jsx_runtime.JSX.Element;
210
293
  declare function ToolbarSectionInline(): react_jsx_runtime.JSX.Element | null;
211
294
  declare function ToolbarSectionCollaboration(): react_jsx_runtime.JSX.Element;
295
+ /**
296
+ * A static toolbar containing actions and values related to the editor.
297
+ *
298
+ * @example
299
+ * <Toolbar />
300
+ *
301
+ * @example
302
+ * <Toolbar >
303
+ * <Toolbar.BlockSelector />
304
+ * <Toolbar.Separator />
305
+ * <Toolbar.SectionInline />
306
+ * <Toolbar.Separator />
307
+ * <Toolbar.Button name="Custom action" onClick={() => { ... }} icon={<Icon.QuestionMark />} />
308
+ * </Toolbar>
309
+ */
212
310
  declare const Toolbar: react.ForwardRefExoticComponent<Omit<ToolbarProps, "ref"> & react.RefAttributes<HTMLDivElement>> & {
311
+ /**
312
+ * A button for triggering actions.
313
+ *
314
+ * @example
315
+ * <Toolbar.Button name="Comment" shortcut="Mod-Shift-E" onClick={() => { ... }}>Comment</Toolbar.Button>
316
+ *
317
+ * @example
318
+ * <Toolbar.Button name="Mention someone" icon={<Icon.Mention />} onClick={() => { ... }} />
319
+ */
213
320
  Button: react.ForwardRefExoticComponent<Omit<ToolbarButtonProps, "ref"> & react.RefAttributes<HTMLButtonElement>>;
321
+ /**
322
+ * A toggle button for values that can be active or inactive.
323
+ *
324
+ * @example
325
+ * <Toolbar.Toggle name="Bold" active={isBold}>Bold</Toolbar.Toggle>
326
+ *
327
+ * @example
328
+ * <Toolbar.Toggle name="Italic" icon={<Icon.Italic />} shortcut="Mod-I" active={isItalic} onClick={() => { ... }} />
329
+ */
214
330
  Toggle: react.ForwardRefExoticComponent<Omit<ToolbarToggleProps, "ref"> & react.RefAttributes<HTMLButtonElement>>;
331
+ /**
332
+ * A dropdown selector to switch between different block types.
333
+ *
334
+ * @example
335
+ * <Toolbar.BlockSelector />
336
+ */
337
+ BlockSelector: react.ForwardRefExoticComponent<Omit<ToolbarBlockSelectorProps, "ref"> & react.RefAttributes<HTMLButtonElement>>;
338
+ /**
339
+ * A visual (and accessible) separator to separate sections in a toolbar.
340
+ */
215
341
  Separator: react.ForwardRefExoticComponent<Omit<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
342
+ /**
343
+ * A section containing history actions. (e.g. undo, redo)
344
+ */
216
345
  SectionHistory: typeof ToolbarSectionHistory;
346
+ /**
347
+ * A section containing inline formatting actions. (e.g. bold, italic, underline, ...)
348
+ */
217
349
  SectionInline: typeof ToolbarSectionInline;
350
+ /**
351
+ * A section containing collaborative actions. (e.g. adding a comment)
352
+ */
218
353
  SectionCollaboration: typeof ToolbarSectionCollaboration;
219
- BlockSelector: react.ForwardRefExoticComponent<Omit<ToolbarBlockSelectorProps, "ref"> & react.RefAttributes<HTMLButtonElement>>;
220
354
  };
221
355
 
222
356
  interface FloatingToolbarProps extends Omit<ComponentProps<"div">, "children"> {
357
+ /**
358
+ * The vertical position of the floating toolbar.
359
+ */
223
360
  position?: FloatingPosition;
361
+ /**
362
+ * The vertical offset of the floating toolbar from the selection.
363
+ */
224
364
  offset?: number;
365
+ /**
366
+ * The content of the floating toolbar, overriding the default content.
367
+ * Use the `before` and `after` props if you want to keep and extend the default content.
368
+ */
225
369
  children?: ToolbarSlot;
370
+ /**
371
+ * The content to display at the start of the floating toolbar.
372
+ */
226
373
  before?: ToolbarSlot;
374
+ /**
375
+ * The content to display at the end of the floating toolbar.
376
+ */
227
377
  after?: ToolbarSlot;
228
378
  }
379
+ /**
380
+ * A floating toolbar attached to the selection and containing actions and values related to the editor.
381
+ *
382
+ * @example
383
+ * <FloatingToolbar />
384
+ *
385
+ * @example
386
+ * <FloatingToolbar>
387
+ * <Toolbar.BlockSelector />
388
+ * <Toolbar.Separator />
389
+ * <Toolbar.SectionInline />
390
+ * <Toolbar.Separator />
391
+ * <Toolbar.Button name="Custom action" onClick={() => { ... }} icon={<Icon.QuestionMark />} />
392
+ * </FloatingToolbar>
393
+ */
229
394
  declare const FloatingToolbar: react.ForwardRefExoticComponent<Omit<FloatingToolbarProps, "ref"> & react.RefAttributes<HTMLDivElement>> & {
395
+ /**
396
+ * A component that can be wrapped around elements which are rendered outside of the floating
397
+ * toolbar (e.g. portals) to prevent the toolbar from closing when clicking/focusing within them.
398
+ *
399
+ * @example
400
+ * <FloatingToolbar>
401
+ * <Popover.Root>
402
+ * <Popover.Trigger asChild>
403
+ * <Toolbar.Button>Open popover</Toolbar.Button>
404
+ * </Popover.Trigger>
405
+ * <Popover.Portal>
406
+ * <FloatingToolbar.External>
407
+ * <Popover.Content>
408
+ * This popover is rendered outside of the floating toolbar, but the toolbar will not close when clicking/focusing within it.
409
+ * </Popover.Content>
410
+ * </FloatingToolbar.External>
411
+ * </Popover.Portal>
412
+ * </Popover.Root>
413
+ * </FloatingToolbar>
414
+ */
230
415
  External: react.ForwardRefExoticComponent<Omit<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
231
416
  };
232
417
 
@@ -242,4 +427,4 @@ interface HistoryVersionPreviewProps extends ComponentPropsWithoutRef<"div"> {
242
427
  */
243
428
  declare const HistoryVersionPreview: react.ForwardRefExoticComponent<HistoryVersionPreviewProps & react.RefAttributes<HTMLDivElement>>;
244
429
 
245
- export { AnchoredThreads, AnchoredThreadsProps, FloatingComposer, FloatingComposerProps, FloatingThreads, FloatingThreadsProps, FloatingToolbar, FloatingToolbarProps, HistoryVersionPreview, HistoryVersionPreviewProps, LiveblocksPlugin, OPEN_FLOATING_COMPOSER_COMMAND, Toolbar, ToolbarProps, liveblocksConfig, useEditorStatus, useIsEditorReady, useIsThreadActive };
430
+ export { AnchoredThreads, AnchoredThreadsProps, FloatingComposer, FloatingComposerProps, FloatingThreads, FloatingThreadsProps, FloatingToolbar, FloatingToolbarProps, HistoryVersionPreview, HistoryVersionPreviewProps, LiveblocksPlugin, OPEN_FLOATING_COMPOSER_COMMAND, Toolbar, ToolbarBlockSelectorItem, ToolbarBlockSelectorProps, ToolbarButtonProps, ToolbarProps, ToolbarSeparatorProps, ToolbarToggleProps, getActiveBlockElement, isTextFormatActive, liveblocksConfig, useEditorStatus, useIsEditorReady, useIsThreadActive };
package/dist/index.js CHANGED
@@ -6,6 +6,8 @@ var anchoredThreads = require('./comments/anchored-threads.js');
6
6
  var commentPluginProvider = require('./comments/comment-plugin-provider.js');
7
7
  var floatingComposer = require('./comments/floating-composer.js');
8
8
  var floatingThreads = require('./comments/floating-threads.js');
9
+ var getActiveBlockElement = require('./get-active-block-element.js');
10
+ var isTextFormatActive = require('./is-text-format-active.js');
9
11
  var liveblocksConfig = require('./liveblocks-config.js');
10
12
  var liveblocksPluginProvider = require('./liveblocks-plugin-provider.js');
11
13
  var floatingToolbar = require('./toolbar/floating-toolbar.js');
@@ -19,6 +21,8 @@ exports.useIsThreadActive = commentPluginProvider.useIsThreadActive;
19
21
  exports.FloatingComposer = floatingComposer.FloatingComposer;
20
22
  exports.OPEN_FLOATING_COMPOSER_COMMAND = floatingComposer.OPEN_FLOATING_COMPOSER_COMMAND;
21
23
  exports.FloatingThreads = floatingThreads.FloatingThreads;
24
+ exports.getActiveBlockElement = getActiveBlockElement.getActiveBlockElement;
25
+ exports.isTextFormatActive = isTextFormatActive.isTextFormatActive;
22
26
  exports.liveblocksConfig = liveblocksConfig.liveblocksConfig;
23
27
  exports.LiveblocksPlugin = liveblocksPluginProvider.LiveblocksPlugin;
24
28
  exports.useEditorStatus = liveblocksPluginProvider.useEditorStatus;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import { detectDupes } from \"@liveblocks/core\";\n\nimport { PKG_FORMAT, PKG_NAME, PKG_VERSION } from \"./version\";\n\ndetectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);\n\nexport type { AnchoredThreadsProps } from \"./comments/anchored-threads\";\nexport { AnchoredThreads } from \"./comments/anchored-threads\";\nexport { useIsThreadActive } from \"./comments/comment-plugin-provider\";\nexport type { FloatingComposerProps } from \"./comments/floating-composer\";\nexport {\n FloatingComposer,\n OPEN_FLOATING_COMPOSER_COMMAND,\n} from \"./comments/floating-composer\";\nexport type { FloatingThreadsProps } from \"./comments/floating-threads\";\nexport { FloatingThreads } from \"./comments/floating-threads\";\nexport { liveblocksConfig } from \"./liveblocks-config\";\nexport {\n LiveblocksPlugin,\n useEditorStatus,\n useIsEditorReady,\n} from \"./liveblocks-plugin-provider\";\nexport type { FloatingToolbarProps } from \"./toolbar/floating-toolbar\";\nexport { FloatingToolbar } from \"./toolbar/floating-toolbar\";\nexport type { ToolbarProps } from \"./toolbar/toolbar\";\nexport { Toolbar } from \"./toolbar/toolbar\";\nexport type { HistoryVersionPreviewProps } from \"./version-history/history-version-preview\";\nexport { HistoryVersionPreview } from \"./version-history/history-version-preview\";\n"],"names":["detectDupes","PKG_NAME","PKG_VERSION","PKG_FORMAT"],"mappings":";;;;;;;;;;;;;;AAIAA,gBAAY,CAAAC,gBAAA,EAAUC,qBAAaC,kBAAU,CAAA;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import { detectDupes } from \"@liveblocks/core\";\n\nimport { PKG_FORMAT, PKG_NAME, PKG_VERSION } from \"./version\";\n\ndetectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);\n\nexport type { AnchoredThreadsProps } from \"./comments/anchored-threads\";\nexport { AnchoredThreads } from \"./comments/anchored-threads\";\nexport { useIsThreadActive } from \"./comments/comment-plugin-provider\";\nexport type { FloatingComposerProps } from \"./comments/floating-composer\";\nexport {\n FloatingComposer,\n OPEN_FLOATING_COMPOSER_COMMAND,\n} from \"./comments/floating-composer\";\nexport type { FloatingThreadsProps } from \"./comments/floating-threads\";\nexport { FloatingThreads } from \"./comments/floating-threads\";\nexport { getActiveBlockElement } from \"./get-active-block-element\";\nexport { isTextFormatActive } from \"./is-text-format-active\";\nexport { liveblocksConfig } from \"./liveblocks-config\";\nexport {\n LiveblocksPlugin,\n useEditorStatus,\n useIsEditorReady,\n} from \"./liveblocks-plugin-provider\";\nexport type { FloatingToolbarProps } from \"./toolbar/floating-toolbar\";\nexport { FloatingToolbar } from \"./toolbar/floating-toolbar\";\nexport type {\n ToolbarBlockSelectorItem,\n ToolbarBlockSelectorProps,\n ToolbarButtonProps,\n ToolbarProps,\n ToolbarSeparatorProps,\n ToolbarToggleProps,\n} from \"./toolbar/toolbar\";\nexport { Toolbar } from \"./toolbar/toolbar\";\nexport type { HistoryVersionPreviewProps } from \"./version-history/history-version-preview\";\nexport { HistoryVersionPreview } from \"./version-history/history-version-preview\";\n"],"names":["detectDupes","PKG_NAME","PKG_VERSION","PKG_FORMAT"],"mappings":";;;;;;;;;;;;;;;;AAIAA,gBAAY,CAAAC,gBAAA,EAAUC,qBAAaC,kBAAU,CAAA;;;;;;;;;;;;;;;;;"}
package/dist/index.mjs CHANGED
@@ -4,6 +4,8 @@ export { AnchoredThreads } from './comments/anchored-threads.mjs';
4
4
  export { useIsThreadActive } from './comments/comment-plugin-provider.mjs';
5
5
  export { FloatingComposer, OPEN_FLOATING_COMPOSER_COMMAND } from './comments/floating-composer.mjs';
6
6
  export { FloatingThreads } from './comments/floating-threads.mjs';
7
+ export { getActiveBlockElement } from './get-active-block-element.mjs';
8
+ export { isTextFormatActive } from './is-text-format-active.mjs';
7
9
  export { liveblocksConfig } from './liveblocks-config.mjs';
8
10
  export { LiveblocksPlugin, useEditorStatus, useIsEditorReady } from './liveblocks-plugin-provider.mjs';
9
11
  export { FloatingToolbar } from './toolbar/floating-toolbar.mjs';
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../src/index.ts"],"sourcesContent":["import { detectDupes } from \"@liveblocks/core\";\n\nimport { PKG_FORMAT, PKG_NAME, PKG_VERSION } from \"./version\";\n\ndetectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);\n\nexport type { AnchoredThreadsProps } from \"./comments/anchored-threads\";\nexport { AnchoredThreads } from \"./comments/anchored-threads\";\nexport { useIsThreadActive } from \"./comments/comment-plugin-provider\";\nexport type { FloatingComposerProps } from \"./comments/floating-composer\";\nexport {\n FloatingComposer,\n OPEN_FLOATING_COMPOSER_COMMAND,\n} from \"./comments/floating-composer\";\nexport type { FloatingThreadsProps } from \"./comments/floating-threads\";\nexport { FloatingThreads } from \"./comments/floating-threads\";\nexport { liveblocksConfig } from \"./liveblocks-config\";\nexport {\n LiveblocksPlugin,\n useEditorStatus,\n useIsEditorReady,\n} from \"./liveblocks-plugin-provider\";\nexport type { FloatingToolbarProps } from \"./toolbar/floating-toolbar\";\nexport { FloatingToolbar } from \"./toolbar/floating-toolbar\";\nexport type { ToolbarProps } from \"./toolbar/toolbar\";\nexport { Toolbar } from \"./toolbar/toolbar\";\nexport type { HistoryVersionPreviewProps } from \"./version-history/history-version-preview\";\nexport { HistoryVersionPreview } from \"./version-history/history-version-preview\";\n"],"names":[],"mappings":";;;;;;;;;;;;AAIA,WAAY,CAAA,QAAA,EAAU,aAAa,UAAU,CAAA"}
1
+ {"version":3,"file":"index.mjs","sources":["../src/index.ts"],"sourcesContent":["import { detectDupes } from \"@liveblocks/core\";\n\nimport { PKG_FORMAT, PKG_NAME, PKG_VERSION } from \"./version\";\n\ndetectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);\n\nexport type { AnchoredThreadsProps } from \"./comments/anchored-threads\";\nexport { AnchoredThreads } from \"./comments/anchored-threads\";\nexport { useIsThreadActive } from \"./comments/comment-plugin-provider\";\nexport type { FloatingComposerProps } from \"./comments/floating-composer\";\nexport {\n FloatingComposer,\n OPEN_FLOATING_COMPOSER_COMMAND,\n} from \"./comments/floating-composer\";\nexport type { FloatingThreadsProps } from \"./comments/floating-threads\";\nexport { FloatingThreads } from \"./comments/floating-threads\";\nexport { getActiveBlockElement } from \"./get-active-block-element\";\nexport { isTextFormatActive } from \"./is-text-format-active\";\nexport { liveblocksConfig } from \"./liveblocks-config\";\nexport {\n LiveblocksPlugin,\n useEditorStatus,\n useIsEditorReady,\n} from \"./liveblocks-plugin-provider\";\nexport type { FloatingToolbarProps } from \"./toolbar/floating-toolbar\";\nexport { FloatingToolbar } from \"./toolbar/floating-toolbar\";\nexport type {\n ToolbarBlockSelectorItem,\n ToolbarBlockSelectorProps,\n ToolbarButtonProps,\n ToolbarProps,\n ToolbarSeparatorProps,\n ToolbarToggleProps,\n} from \"./toolbar/toolbar\";\nexport { Toolbar } from \"./toolbar/toolbar\";\nexport type { HistoryVersionPreviewProps } from \"./version-history/history-version-preview\";\nexport { HistoryVersionPreview } from \"./version-history/history-version-preview\";\n"],"names":[],"mappings":";;;;;;;;;;;;;;AAIA,WAAY,CAAA,QAAA,EAAU,aAAa,UAAU,CAAA"}
@@ -56,7 +56,6 @@ const FloatingToolbar = Object.assign(
56
56
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
57
57
  const [isFocused, setFocused] = react.useState(false);
58
58
  const [isManuallyClosed, setManuallyClosed] = react.useState(false);
59
- const isEditable = editor.isEditable();
60
59
  const [hasSelectionRange, setHasSelectionRange] = react.useState(false);
61
60
  const isOpen = isFocused && !isPointerDown && hasSelectionRange && !isManuallyClosed;
62
61
  const [delayedIsOpen, setDelayedIsOpen] = react.useState(isOpen);
@@ -181,7 +180,7 @@ const FloatingToolbar = Object.assign(
181
180
  [onPointerDown]
182
181
  );
183
182
  react.useEffect(() => {
184
- if (!editor || !isEditable) {
183
+ if (!editor) {
185
184
  return;
186
185
  }
187
186
  const handlePointerDown2 = () => {
@@ -190,15 +189,19 @@ const FloatingToolbar = Object.assign(
190
189
  const handlePointerUp = () => {
191
190
  setPointerDown(false);
192
191
  };
193
- document.addEventListener("pointerdown", handlePointerDown2);
194
- document.addEventListener("pointercancel", handlePointerUp);
195
- document.addEventListener("pointerup", handlePointerUp);
192
+ const root = editor.getRootElement();
193
+ if (!root) {
194
+ return;
195
+ }
196
+ root.addEventListener("pointerdown", handlePointerDown2);
197
+ root.addEventListener("pointercancel", handlePointerUp);
198
+ root.addEventListener("pointerup", handlePointerUp);
196
199
  return () => {
197
- document.removeEventListener("pointerdown", handlePointerDown2);
198
- document.removeEventListener("pointercancel", handlePointerUp);
199
- document.removeEventListener("pointerup", handlePointerUp);
200
+ root.removeEventListener("pointerdown", handlePointerDown2);
201
+ root.removeEventListener("pointercancel", handlePointerUp);
202
+ root.removeEventListener("pointerup", handlePointerUp);
200
203
  };
201
- }, [editor, isEditable]);
204
+ }, [editor]);
202
205
  react.useEffect(() => {
203
206
  const unregister = editor.registerUpdateListener(({ tags }) => {
204
207
  return editor.getEditorState().read(() => {