@thangph2146/lexical-editor 0.0.3 → 0.0.5

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 (100) hide show
  1. package/dist/editor-x/editor.cjs +724 -435
  2. package/dist/editor-x/editor.cjs.map +1 -1
  3. package/dist/editor-x/editor.css +1391 -1091
  4. package/dist/editor-x/editor.css.map +1 -1
  5. package/dist/editor-x/editor.js +728 -439
  6. package/dist/editor-x/editor.js.map +1 -1
  7. package/dist/index.cjs +760 -472
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.css +1391 -1091
  10. package/dist/index.css.map +1 -1
  11. package/dist/index.js +763 -475
  12. package/dist/index.js.map +1 -1
  13. package/package.json +1 -1
  14. package/src/components/lexical-editor.tsx +138 -123
  15. package/src/editor-ui/content-editable.tsx +3 -3
  16. package/src/editor-x/editor.tsx +16 -3
  17. package/src/editor-x/plugins.tsx +385 -380
  18. package/src/nodes/list-with-color-node.tsx +160 -160
  19. package/src/plugins/autocomplete-plugin.tsx +2574 -2574
  20. package/src/plugins/context-menu-plugin.tsx +239 -9
  21. package/src/plugins/floating-text-format-plugin.tsx +84 -92
  22. package/src/plugins/images-plugin.tsx +4 -4
  23. package/src/plugins/list-color-plugin.tsx +178 -178
  24. package/src/plugins/tab-focus-plugin.tsx +66 -66
  25. package/src/plugins/table-column-resizer-plugin.tsx +329 -190
  26. package/src/plugins/toolbar/block-format/block-format-data.tsx +4 -0
  27. package/src/plugins/toolbar/block-format/format-bulleted-list.tsx +40 -40
  28. package/src/plugins/toolbar/block-format/format-list-with-marker.tsx +74 -74
  29. package/src/plugins/toolbar/block-format/format-numbered-list.tsx +40 -40
  30. package/src/plugins/toolbar/block-format-toolbar-plugin.tsx +118 -117
  31. package/src/plugins/toolbar/element-format-toolbar-plugin.tsx +37 -53
  32. package/src/plugins/toolbar/font-format-toolbar-plugin.tsx +8 -15
  33. package/src/plugins/toolbar/font-size-toolbar-plugin.tsx +2 -3
  34. package/src/plugins/toolbar/history-toolbar-plugin.tsx +2 -5
  35. package/src/plugins/toolbar/subsuper-toolbar-plugin.tsx +15 -23
  36. package/src/themes/_mixins.scss +158 -10
  37. package/src/themes/_variables.scss +168 -0
  38. package/src/themes/core/_code.scss +59 -0
  39. package/src/themes/core/_images.scss +80 -0
  40. package/src/themes/core/_lists.scss +214 -0
  41. package/src/themes/core/_misc.scss +46 -0
  42. package/src/themes/core/_reset.scss +119 -0
  43. package/src/themes/core/_tables.scss +145 -0
  44. package/src/themes/core/_text.scss +35 -0
  45. package/src/themes/core/_typography.scss +73 -0
  46. package/src/themes/editor-theme copy.scss +763 -0
  47. package/src/themes/editor-theme.scss +9 -621
  48. package/src/themes/editor-theme.ts +118 -118
  49. package/src/themes/plugins/_auto-embed.scss +11 -0
  50. package/src/themes/plugins/_color-picker.scss +103 -0
  51. package/src/themes/plugins/_draggable-block.scss +32 -0
  52. package/src/themes/plugins/_floating-link-editor.scss +47 -0
  53. package/src/themes/plugins/_floating-toolbars.scss +61 -0
  54. package/src/themes/plugins/_image-resizer.scss +38 -0
  55. package/src/themes/plugins/_image.scss +57 -0
  56. package/src/themes/plugins/_layout.scss +39 -0
  57. package/src/themes/plugins/_list-color.scss +23 -0
  58. package/src/themes/plugins/_mentions.scss +21 -0
  59. package/src/themes/plugins/_menus-and-pickers.scss +153 -0
  60. package/src/themes/plugins/_table.scss +20 -0
  61. package/src/themes/plugins/_toolbar.scss +36 -0
  62. package/src/themes/plugins/_tree-view.scss +11 -0
  63. package/src/themes/plugins copy.scss +656 -0
  64. package/src/themes/plugins.scss +20 -1165
  65. package/src/themes/ui-components/_animations.scss +31 -0
  66. package/src/themes/ui-components/_backgrounds.scss +27 -0
  67. package/src/themes/ui-components/_borders.scss +20 -0
  68. package/src/themes/ui-components/_button.scss +176 -0
  69. package/src/themes/ui-components/_checkbox.scss +14 -0
  70. package/src/themes/ui-components/_cursors.scss +31 -0
  71. package/src/themes/ui-components/_dialog.scss +86 -0
  72. package/src/themes/ui-components/_display-sizing.scss +100 -0
  73. package/src/themes/ui-components/_flex.scss +124 -0
  74. package/src/themes/ui-components/_form-layout.scss +15 -0
  75. package/src/themes/ui-components/_icons.scss +23 -0
  76. package/src/themes/ui-components/_input.scss +86 -0
  77. package/src/themes/ui-components/_label.scss +19 -0
  78. package/src/themes/ui-components/_loader.scss +9 -0
  79. package/src/themes/ui-components/_margins-paddings.scss +45 -0
  80. package/src/themes/ui-components/_popover.scss +16 -0
  81. package/src/themes/ui-components/_positioning.scss +73 -0
  82. package/src/themes/ui-components/_rounded.scss +19 -0
  83. package/src/themes/ui-components/_scroll-area.scss +11 -0
  84. package/src/themes/ui-components/_select.scss +110 -0
  85. package/src/themes/ui-components/_separator.scss +19 -0
  86. package/src/themes/ui-components/_shadow.scss +15 -0
  87. package/src/themes/ui-components/_tabs.scss +46 -0
  88. package/src/themes/ui-components/_text-utilities.scss +48 -0
  89. package/src/themes/ui-components/_toggle-toolbar.scss +128 -0
  90. package/src/themes/ui-components/_toggle.scss +80 -0
  91. package/src/themes/ui-components/_typography.scss +22 -0
  92. package/src/themes/ui-components copy.scss +1335 -0
  93. package/src/themes/ui-components.scss +27 -937
  94. package/src/transformers/markdown-list-transformer.ts +51 -51
  95. package/src/ui/button.tsx +11 -2
  96. package/src/ui/collapsible.tsx +1 -1
  97. package/src/ui/dialog.tsx +2 -2
  98. package/src/ui/flex.tsx +4 -4
  99. package/src/ui/popover.tsx +1 -1
  100. package/src/ui/tooltip.tsx +2 -2
@@ -1,40 +1,40 @@
1
- import { INSERT_UNORDERED_LIST_COMMAND } from "@lexical/list"
2
- import { $setBlocksType } from "@lexical/selection"
3
- import { $createParagraphNode, $getSelection, $isRangeSelection } from "lexical"
4
-
5
- import { useToolbarContext } from "../../../context/toolbar-context"
6
- import { blockTypeToBlockName } from "../../../plugins/toolbar/block-format/block-format-data"
7
- import { SelectItem } from "../../../ui/select"
8
- import { Flex } from "../../../ui/flex"
9
-
10
- const BLOCK_FORMAT_VALUE = "bullet"
11
-
12
- export function FormatBulletedList() {
13
- const { activeEditor, blockType } = useToolbarContext()
14
-
15
- const formatParagraph = () => {
16
- activeEditor.update(() => {
17
- const selection = $getSelection()
18
- if ($isRangeSelection(selection)) {
19
- $setBlocksType(selection, () => $createParagraphNode())
20
- }
21
- })
22
- }
23
-
24
- const formatBulletedList = () => {
25
- if (blockType !== "bullet") {
26
- activeEditor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, undefined)
27
- } else {
28
- formatParagraph()
29
- }
30
- }
31
-
32
- return (
33
- <SelectItem value={BLOCK_FORMAT_VALUE} onPointerDown={formatBulletedList}>
34
- <Flex align="center" gap={2}>
35
- {blockTypeToBlockName[BLOCK_FORMAT_VALUE]?.icon}
36
- {blockTypeToBlockName[BLOCK_FORMAT_VALUE]?.label}
37
- </Flex>
38
- </SelectItem>
39
- )
40
- }
1
+ import { INSERT_UNORDERED_LIST_COMMAND } from "@lexical/list"
2
+ import { $setBlocksType } from "@lexical/selection"
3
+ import { $createParagraphNode, $getSelection, $isRangeSelection } from "lexical"
4
+
5
+ import { useToolbarContext } from "../../../context/toolbar-context"
6
+ import { blockTypeToBlockName } from "../../../plugins/toolbar/block-format/block-format-data"
7
+ import { SelectItem } from "../../../ui/select"
8
+ import { Flex } from "../../../ui/flex"
9
+
10
+ const BLOCK_FORMAT_VALUE = "bullet"
11
+
12
+ export function FormatBulletedList() {
13
+ const { activeEditor, blockType } = useToolbarContext()
14
+
15
+ const formatParagraph = () => {
16
+ activeEditor.update(() => {
17
+ const selection = $getSelection()
18
+ if ($isRangeSelection(selection)) {
19
+ $setBlocksType(selection, () => $createParagraphNode())
20
+ }
21
+ })
22
+ }
23
+
24
+ const formatBulletedList = () => {
25
+ if (blockType !== "bullet") {
26
+ activeEditor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, undefined)
27
+ } else {
28
+ formatParagraph()
29
+ }
30
+ }
31
+
32
+ return (
33
+ <SelectItem value={BLOCK_FORMAT_VALUE} onPointerDown={formatBulletedList}>
34
+ <Flex align="center" gap={2}>
35
+ {blockTypeToBlockName[BLOCK_FORMAT_VALUE]?.icon}
36
+ {blockTypeToBlockName[BLOCK_FORMAT_VALUE]?.label}
37
+ </Flex>
38
+ </SelectItem>
39
+ )
40
+ }
@@ -1,74 +1,74 @@
1
- import {
2
- INSERT_ORDERED_LIST_COMMAND,
3
- INSERT_UNORDERED_LIST_COMMAND,
4
- } from "@lexical/list"
5
- import { $setBlocksType } from "@lexical/selection"
6
- import { $getNearestNodeOfType } from "@lexical/utils"
7
- import {
8
- $createParagraphNode,
9
- $getSelection,
10
- $isRangeSelection,
11
- } from "lexical"
12
-
13
- import { useToolbarContext } from "../../../context/toolbar-context"
14
- import { ListWithColorNode } from "../../../nodes/list-with-color-node"
15
- import { blockTypeToBlockName } from "../../../plugins/toolbar/block-format/block-format-data"
16
- import { Flex } from "../../../ui/flex"
17
- import { SelectItem } from "../../../ui/select"
18
-
19
- interface FormatListWithMarkerProps {
20
- blockFormatValue: string
21
- listType: "bullet" | "number"
22
- markerType: string
23
- }
24
-
25
- export function FormatListWithMarker({
26
- blockFormatValue,
27
- listType,
28
- markerType,
29
- }: FormatListWithMarkerProps) {
30
- const { activeEditor, blockType } = useToolbarContext()
31
-
32
- const formatParagraph = () => {
33
- activeEditor.update(() => {
34
- const selection = $getSelection()
35
- if ($isRangeSelection(selection)) {
36
- $setBlocksType(selection, () => $createParagraphNode())
37
- }
38
- })
39
- }
40
-
41
- const formatList = () => {
42
- if (blockType !== blockFormatValue) {
43
- const command =
44
- listType === "bullet"
45
- ? INSERT_UNORDERED_LIST_COMMAND
46
- : INSERT_ORDERED_LIST_COMMAND
47
-
48
- activeEditor.dispatchCommand(command, undefined)
49
-
50
- // Update marker type immediately after
51
- activeEditor.update(() => {
52
- const selection = $getSelection()
53
- if ($isRangeSelection(selection)) {
54
- const anchorNode = selection.anchor.getNode()
55
- const listNode = $getNearestNodeOfType(anchorNode, ListWithColorNode)
56
- if (listNode) {
57
- listNode.setMarkerType(markerType)
58
- }
59
- }
60
- })
61
- } else {
62
- formatParagraph()
63
- }
64
- }
65
-
66
- return (
67
- <SelectItem value={blockFormatValue} onPointerDown={formatList}>
68
- <Flex align="center" gap={2}>
69
- {blockTypeToBlockName[blockFormatValue]?.icon}
70
- {blockTypeToBlockName[blockFormatValue]?.label}
71
- </Flex>
72
- </SelectItem>
73
- )
74
- }
1
+ import {
2
+ INSERT_ORDERED_LIST_COMMAND,
3
+ INSERT_UNORDERED_LIST_COMMAND,
4
+ } from "@lexical/list"
5
+ import { $setBlocksType } from "@lexical/selection"
6
+ import { $getNearestNodeOfType } from "@lexical/utils"
7
+ import {
8
+ $createParagraphNode,
9
+ $getSelection,
10
+ $isRangeSelection,
11
+ } from "lexical"
12
+
13
+ import { useToolbarContext } from "../../../context/toolbar-context"
14
+ import { ListWithColorNode } from "../../../nodes/list-with-color-node"
15
+ import { blockTypeToBlockName } from "../../../plugins/toolbar/block-format/block-format-data"
16
+ import { Flex } from "../../../ui/flex"
17
+ import { SelectItem } from "../../../ui/select"
18
+
19
+ interface FormatListWithMarkerProps {
20
+ blockFormatValue: string
21
+ listType: "bullet" | "number"
22
+ markerType: string
23
+ }
24
+
25
+ export function FormatListWithMarker({
26
+ blockFormatValue,
27
+ listType,
28
+ markerType,
29
+ }: FormatListWithMarkerProps) {
30
+ const { activeEditor, blockType } = useToolbarContext()
31
+
32
+ const formatParagraph = () => {
33
+ activeEditor.update(() => {
34
+ const selection = $getSelection()
35
+ if ($isRangeSelection(selection)) {
36
+ $setBlocksType(selection, () => $createParagraphNode())
37
+ }
38
+ })
39
+ }
40
+
41
+ const formatList = () => {
42
+ if (blockType !== blockFormatValue) {
43
+ const command =
44
+ listType === "bullet"
45
+ ? INSERT_UNORDERED_LIST_COMMAND
46
+ : INSERT_ORDERED_LIST_COMMAND
47
+
48
+ activeEditor.dispatchCommand(command, undefined)
49
+
50
+ // Update marker type immediately after
51
+ activeEditor.update(() => {
52
+ const selection = $getSelection()
53
+ if ($isRangeSelection(selection)) {
54
+ const anchorNode = selection.anchor.getNode()
55
+ const listNode = $getNearestNodeOfType(anchorNode, ListWithColorNode)
56
+ if (listNode) {
57
+ listNode.setMarkerType(markerType)
58
+ }
59
+ }
60
+ })
61
+ } else {
62
+ formatParagraph()
63
+ }
64
+ }
65
+
66
+ return (
67
+ <SelectItem value={blockFormatValue} onPointerDown={formatList}>
68
+ <Flex align="center" gap={2}>
69
+ {blockTypeToBlockName[blockFormatValue]?.icon}
70
+ {blockTypeToBlockName[blockFormatValue]?.label}
71
+ </Flex>
72
+ </SelectItem>
73
+ )
74
+ }
@@ -1,40 +1,40 @@
1
- import { INSERT_ORDERED_LIST_COMMAND } from "@lexical/list"
2
- import { $setBlocksType } from "@lexical/selection"
3
- import { $createParagraphNode, $getSelection, $isRangeSelection } from "lexical"
4
-
5
- import { useToolbarContext } from "../../../context/toolbar-context"
6
- import { blockTypeToBlockName } from "../../../plugins/toolbar/block-format/block-format-data"
7
- import { SelectItem } from "../../../ui/select"
8
- import { Flex } from "../../../ui/flex"
9
-
10
- const BLOCK_FORMAT_VALUE = "number"
11
-
12
- export function FormatNumberedList() {
13
- const { activeEditor, blockType } = useToolbarContext()
14
-
15
- const formatParagraph = () => {
16
- activeEditor.update(() => {
17
- const selection = $getSelection()
18
- if ($isRangeSelection(selection)) {
19
- $setBlocksType(selection, () => $createParagraphNode())
20
- }
21
- })
22
- }
23
-
24
- const formatNumberedList = () => {
25
- if (blockType !== "number") {
26
- activeEditor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, undefined)
27
- } else {
28
- formatParagraph()
29
- }
30
- }
31
-
32
- return (
33
- <SelectItem value={BLOCK_FORMAT_VALUE} onPointerDown={formatNumberedList}>
34
- <Flex align="center" gap={2}>
35
- {blockTypeToBlockName[BLOCK_FORMAT_VALUE]?.icon}
36
- {blockTypeToBlockName[BLOCK_FORMAT_VALUE]?.label}
37
- </Flex>
38
- </SelectItem>
39
- )
40
- }
1
+ import { INSERT_ORDERED_LIST_COMMAND } from "@lexical/list"
2
+ import { $setBlocksType } from "@lexical/selection"
3
+ import { $createParagraphNode, $getSelection, $isRangeSelection } from "lexical"
4
+
5
+ import { useToolbarContext } from "../../../context/toolbar-context"
6
+ import { blockTypeToBlockName } from "../../../plugins/toolbar/block-format/block-format-data"
7
+ import { SelectItem } from "../../../ui/select"
8
+ import { Flex } from "../../../ui/flex"
9
+
10
+ const BLOCK_FORMAT_VALUE = "number"
11
+
12
+ export function FormatNumberedList() {
13
+ const { activeEditor, blockType } = useToolbarContext()
14
+
15
+ const formatParagraph = () => {
16
+ activeEditor.update(() => {
17
+ const selection = $getSelection()
18
+ if ($isRangeSelection(selection)) {
19
+ $setBlocksType(selection, () => $createParagraphNode())
20
+ }
21
+ })
22
+ }
23
+
24
+ const formatNumberedList = () => {
25
+ if (blockType !== "number") {
26
+ activeEditor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, undefined)
27
+ } else {
28
+ formatParagraph()
29
+ }
30
+ }
31
+
32
+ return (
33
+ <SelectItem value={BLOCK_FORMAT_VALUE} onPointerDown={formatNumberedList}>
34
+ <Flex align="center" gap={2}>
35
+ {blockTypeToBlockName[BLOCK_FORMAT_VALUE]?.icon}
36
+ {blockTypeToBlockName[BLOCK_FORMAT_VALUE]?.label}
37
+ </Flex>
38
+ </SelectItem>
39
+ )
40
+ }
@@ -1,117 +1,118 @@
1
- "use client"
2
-
3
- import { $isListNode, ListNode } from "@lexical/list"
4
- import { $isHeadingNode } from "@lexical/rich-text"
5
- import { $findMatchingParent, $getNearestNodeOfType } from "@lexical/utils"
6
- import {
7
- $isRangeSelection,
8
- $isRootOrShadowRoot,
9
- BaseSelection,
10
- } from "lexical"
11
- import { useCallback } from "react"
12
-
13
- import { useToolbarContext } from "../../context/toolbar-context"
14
- import { useUpdateToolbarHandler } from "../../editor-hooks/use-update-toolbar"
15
- import { ListWithColorNode } from "../../nodes/list-with-color-node"
16
- import { blockTypeToBlockName } from "../../plugins/toolbar/block-format/block-format-data"
17
- import {
18
- Select,
19
- SelectContent,
20
- SelectGroup,
21
- SelectTrigger,
22
- } from "../../ui/select"
23
-
24
- export function BlockFormatDropDown({
25
- children,
26
- }: {
27
- children: React.ReactNode
28
- }) {
29
- const { activeEditor, blockType, setBlockType } = useToolbarContext()
30
- const currentBlockType = blockTypeToBlockName[blockType]
31
- ? blockType
32
- : "paragraph"
33
- const currentBlockMeta = blockTypeToBlockName[currentBlockType]
34
-
35
- /**
36
- * Updates the toolbar state based on the current selection.
37
- * Identifies if the cursor is in a List, Heading, or other block type.
38
- */
39
- const $updateToolbar = useCallback(
40
- (selection: BaseSelection) => {
41
- if (!$isRangeSelection(selection)) return
42
-
43
- const anchorNode = selection.anchor.getNode()
44
-
45
- // Find the top-level element or the nearest parent that is a root/shadow root
46
- let element =
47
- anchorNode.getKey() === "root"
48
- ? anchorNode
49
- : $findMatchingParent(anchorNode, (e) => {
50
- const parent = e.getParent()
51
- return parent !== null && $isRootOrShadowRoot(parent)
52
- })
53
-
54
- if (element === null) {
55
- element = anchorNode.getTopLevelElementOrThrow()
56
- }
57
-
58
- const elementKey = element.getKey()
59
- const elementDOM = activeEditor.getElementByKey(elementKey)
60
-
61
- if (elementDOM !== null) {
62
- if ($isListNode(element)) {
63
- const parentList = $getNearestNodeOfType<ListNode>(
64
- anchorNode,
65
- ListNode
66
- )
67
- const listNode = parentList || element
68
- let type: string = listNode.getListType()
69
-
70
- if (listNode instanceof ListWithColorNode) {
71
- const markerType = listNode.getMarkerType()
72
-
73
- if (type === "bullet") {
74
- if (markerType === "-") type = "bullet-dash"
75
- else if (markerType === "+") type = "bullet-plus"
76
- } else if (type === "number") {
77
- if (markerType === "alpha") type = "number-alpha"
78
- }
79
- }
80
- setBlockType(type)
81
- } else {
82
- const type = $isHeadingNode(element)
83
- ? element.getTag()
84
- : element.getType()
85
- if (type in blockTypeToBlockName) {
86
- setBlockType(type as keyof typeof blockTypeToBlockName)
87
- }
88
- }
89
- }
90
- },
91
- [activeEditor, setBlockType]
92
- )
93
-
94
- useUpdateToolbarHandler($updateToolbar)
95
-
96
- return (
97
- <Select
98
- modal={false}
99
- value={currentBlockType}
100
- onValueChange={(value) => {
101
- if (value in blockTypeToBlockName) {
102
- setBlockType(value as keyof typeof blockTypeToBlockName)
103
- }
104
- }}
105
- >
106
- <SelectTrigger className="editor-toolbar-select-trigger editor-toolbar-select-trigger--w-md">
107
- <div className="editor-toolbar-select-icon">
108
- {currentBlockMeta?.icon}
109
- </div>
110
- <span className="editor-truncate editor-block-format-label">{currentBlockMeta?.label}</span>
111
- </SelectTrigger>
112
- <SelectContent>
113
- <SelectGroup>{children}</SelectGroup>
114
- </SelectContent>
115
- </Select>
116
- )
117
- }
1
+ "use client"
2
+
3
+ import { $isListNode, ListNode } from "@lexical/list"
4
+ import { $isHeadingNode } from "@lexical/rich-text"
5
+ import { $findMatchingParent, $getNearestNodeOfType } from "@lexical/utils"
6
+ import {
7
+ $isRangeSelection,
8
+ $isRootOrShadowRoot,
9
+ BaseSelection,
10
+ } from "lexical"
11
+ import { useCallback } from "react"
12
+
13
+ import { useToolbarContext } from "../../context/toolbar-context"
14
+ import { useUpdateToolbarHandler } from "../../editor-hooks/use-update-toolbar"
15
+ import { ListWithColorNode } from "../../nodes/list-with-color-node"
16
+ import { blockTypeToBlockName } from "../../plugins/toolbar/block-format/block-format-data"
17
+ import {
18
+ Select,
19
+ SelectContent,
20
+ SelectGroup,
21
+ SelectTrigger,
22
+ } from "../../ui/select"
23
+
24
+ export function BlockFormatDropDown({
25
+ children,
26
+ }: {
27
+ children: React.ReactNode
28
+ }) {
29
+ const { activeEditor, blockType, setBlockType } = useToolbarContext()
30
+ const currentBlockType = blockTypeToBlockName[blockType]
31
+ ? blockType
32
+ : "paragraph"
33
+ const currentBlockMeta = blockTypeToBlockName[currentBlockType]
34
+
35
+ /**
36
+ * Updates the toolbar state based on the current selection.
37
+ * Identifies if the cursor is in a List, Heading, or other block type.
38
+ */
39
+ const $updateToolbar = useCallback(
40
+ (selection: BaseSelection) => {
41
+ if (!$isRangeSelection(selection)) return
42
+
43
+ const anchorNode = selection.anchor.getNode()
44
+
45
+ // Find the top-level element or the nearest parent that is a root/shadow root
46
+ let element =
47
+ anchorNode.getKey() === "root"
48
+ ? anchorNode
49
+ : $findMatchingParent(anchorNode, (e) => {
50
+ const parent = e.getParent()
51
+ return parent !== null && $isRootOrShadowRoot(parent)
52
+ })
53
+
54
+ if (element === null) {
55
+ element = anchorNode.getTopLevelElementOrThrow()
56
+ }
57
+
58
+ const elementKey = element.getKey()
59
+ const elementDOM = activeEditor.getElementByKey(elementKey)
60
+
61
+ if (elementDOM !== null) {
62
+ if ($isListNode(element)) {
63
+ const parentList = $getNearestNodeOfType<ListNode>(
64
+ anchorNode,
65
+ ListNode
66
+ )
67
+ const listNode = parentList || element
68
+ let type: string = listNode.getListType()
69
+
70
+ if (listNode instanceof ListWithColorNode) {
71
+ const markerType = listNode.getMarkerType()
72
+
73
+ if (type === "bullet") {
74
+ if (markerType === "-") type = "bullet-dash"
75
+ else if (markerType === "+") type = "bullet-plus"
76
+ } else if (type === "number") {
77
+ if (markerType === "alpha") type = "number-alpha"
78
+ else if (markerType === "multi-level") type = "number-multi-level"
79
+ }
80
+ }
81
+ setBlockType(type)
82
+ } else {
83
+ const type = $isHeadingNode(element)
84
+ ? element.getTag()
85
+ : element.getType()
86
+ if (type in blockTypeToBlockName) {
87
+ setBlockType(type as keyof typeof blockTypeToBlockName)
88
+ }
89
+ }
90
+ }
91
+ },
92
+ [activeEditor, setBlockType]
93
+ )
94
+
95
+ useUpdateToolbarHandler($updateToolbar)
96
+
97
+ return (
98
+ <Select
99
+ modal={false}
100
+ value={currentBlockType}
101
+ onValueChange={(value) => {
102
+ if (value in blockTypeToBlockName) {
103
+ setBlockType(value as keyof typeof blockTypeToBlockName)
104
+ }
105
+ }}
106
+ >
107
+ <SelectTrigger className="editor-toolbar-select-trigger editor-toolbar-select-trigger--w-md">
108
+ <div className="editor-toolbar-select-icon">
109
+ {currentBlockMeta?.icon}
110
+ </div>
111
+ <span className="editor-truncate editor-block-format-label">{currentBlockMeta?.label}</span>
112
+ </SelectTrigger>
113
+ <SelectContent>
114
+ <SelectGroup>{children}</SelectGroup>
115
+ </SelectContent>
116
+ </Select>
117
+ )
118
+ }
@@ -28,11 +28,8 @@ import { useToolbarContext } from "../../context/toolbar-context"
28
28
  import { $isLayoutItemNode } from "../../nodes/layout-item-node"
29
29
  import { useUpdateToolbarHandler } from "../../editor-hooks/use-update-toolbar"
30
30
  import { getSelectedNode } from "../../utils/get-selected-node"
31
+ import { Button } from "../../ui/button"
31
32
  import { Separator } from "../../ui/separator"
32
- import {
33
- ToggleGroup,
34
- ToggleGroupItem,
35
- } from "../../ui/toggle-group"
36
33
  import { IconSize } from "../../ui/typography"
37
34
 
38
35
  const ELEMENT_FORMAT_OPTIONS: {
@@ -192,60 +189,47 @@ export function ElementFormatToolbarPlugin({
192
189
 
193
190
  return (
194
191
  <>
195
- <ToggleGroup
196
- type="single"
197
- value={elementFormat}
198
- onValueChange={handleValueChange}
199
- size="sm"
200
- variant="outline"
201
- >
202
- {/* Alignment toggles */}
203
- {Object.entries(ELEMENT_FORMAT_OPTIONS).map(([value, option]) => (
204
- <ToggleGroupItem
205
- key={value}
206
- value={value}
207
- variant={"outline"}
208
- size="sm"
209
- aria-label={option.name}
210
- className="editor-toolbar-item"
211
- >
212
- {option.icon}
213
- </ToggleGroupItem>
214
- ))}
215
- </ToggleGroup>
192
+ {/* Alignment toggles */}
193
+ {Object.entries(ELEMENT_FORMAT_OPTIONS).map(([value, option]) => (
194
+ <Button
195
+ key={value}
196
+ variant="outline"
197
+ size="sm"
198
+ aria-label={option.name}
199
+ data-state={elementFormat === value ? "on" : "off"}
200
+ onClick={() => handleValueChange(value)}
201
+ className="editor-toolbar-item"
202
+ >
203
+ {option.icon}
204
+ </Button>
205
+ ))}
206
+
216
207
  {separator && <Separator orientation="vertical" className="editor-toolbar-separator" />}
208
+
217
209
  {/* Indentation toggles */}
218
- <ToggleGroup
219
- type="single"
220
- value={elementFormat}
221
- onValueChange={handleValueChange}
210
+ <Button
211
+ variant="outline"
222
212
  size="sm"
213
+ aria-label="Outdent"
214
+ onClick={() => handleValueChange("outdent")}
215
+ className="editor-toolbar-item"
216
+ >
217
+ <IconSize size="sm">
218
+ <IndentDecreaseIcon />
219
+ </IconSize>
220
+ </Button>
221
+
222
+ <Button
223
223
  variant="outline"
224
+ size="sm"
225
+ aria-label="Indent"
226
+ onClick={() => handleValueChange("indent")}
227
+ className="editor-toolbar-item"
224
228
  >
225
- <ToggleGroupItem
226
- value="outdent"
227
- aria-label="Outdent"
228
- variant={"outline"}
229
- size="sm"
230
- className="editor-toolbar-item"
231
- >
232
- <IconSize size="sm">
233
- <IndentDecreaseIcon />
234
- </IconSize>
235
- </ToggleGroupItem>
236
-
237
- <ToggleGroupItem
238
- value="indent"
239
- variant={"outline"}
240
- aria-label="Indent"
241
- size="sm"
242
- className="editor-toolbar-item"
243
- >
244
- <IconSize size="sm">
245
- <IndentIncreaseIcon />
246
- </IconSize>
247
- </ToggleGroupItem>
248
- </ToggleGroup>
229
+ <IconSize size="sm">
230
+ <IndentIncreaseIcon />
231
+ </IconSize>
232
+ </Button>
249
233
  </>
250
234
  )
251
235
  }