@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.
- package/dist/editor-x/editor.cjs +724 -435
- package/dist/editor-x/editor.cjs.map +1 -1
- package/dist/editor-x/editor.css +1391 -1091
- package/dist/editor-x/editor.css.map +1 -1
- package/dist/editor-x/editor.js +728 -439
- package/dist/editor-x/editor.js.map +1 -1
- package/dist/index.cjs +760 -472
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +1391 -1091
- package/dist/index.css.map +1 -1
- package/dist/index.js +763 -475
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/lexical-editor.tsx +138 -123
- package/src/editor-ui/content-editable.tsx +3 -3
- package/src/editor-x/editor.tsx +16 -3
- package/src/editor-x/plugins.tsx +385 -380
- package/src/nodes/list-with-color-node.tsx +160 -160
- package/src/plugins/autocomplete-plugin.tsx +2574 -2574
- package/src/plugins/context-menu-plugin.tsx +239 -9
- package/src/plugins/floating-text-format-plugin.tsx +84 -92
- package/src/plugins/images-plugin.tsx +4 -4
- package/src/plugins/list-color-plugin.tsx +178 -178
- package/src/plugins/tab-focus-plugin.tsx +66 -66
- package/src/plugins/table-column-resizer-plugin.tsx +329 -190
- package/src/plugins/toolbar/block-format/block-format-data.tsx +4 -0
- package/src/plugins/toolbar/block-format/format-bulleted-list.tsx +40 -40
- package/src/plugins/toolbar/block-format/format-list-with-marker.tsx +74 -74
- package/src/plugins/toolbar/block-format/format-numbered-list.tsx +40 -40
- package/src/plugins/toolbar/block-format-toolbar-plugin.tsx +118 -117
- package/src/plugins/toolbar/element-format-toolbar-plugin.tsx +37 -53
- package/src/plugins/toolbar/font-format-toolbar-plugin.tsx +8 -15
- package/src/plugins/toolbar/font-size-toolbar-plugin.tsx +2 -3
- package/src/plugins/toolbar/history-toolbar-plugin.tsx +2 -5
- package/src/plugins/toolbar/subsuper-toolbar-plugin.tsx +15 -23
- package/src/themes/_mixins.scss +158 -10
- package/src/themes/_variables.scss +168 -0
- package/src/themes/core/_code.scss +59 -0
- package/src/themes/core/_images.scss +80 -0
- package/src/themes/core/_lists.scss +214 -0
- package/src/themes/core/_misc.scss +46 -0
- package/src/themes/core/_reset.scss +119 -0
- package/src/themes/core/_tables.scss +145 -0
- package/src/themes/core/_text.scss +35 -0
- package/src/themes/core/_typography.scss +73 -0
- package/src/themes/editor-theme copy.scss +763 -0
- package/src/themes/editor-theme.scss +9 -621
- package/src/themes/editor-theme.ts +118 -118
- package/src/themes/plugins/_auto-embed.scss +11 -0
- package/src/themes/plugins/_color-picker.scss +103 -0
- package/src/themes/plugins/_draggable-block.scss +32 -0
- package/src/themes/plugins/_floating-link-editor.scss +47 -0
- package/src/themes/plugins/_floating-toolbars.scss +61 -0
- package/src/themes/plugins/_image-resizer.scss +38 -0
- package/src/themes/plugins/_image.scss +57 -0
- package/src/themes/plugins/_layout.scss +39 -0
- package/src/themes/plugins/_list-color.scss +23 -0
- package/src/themes/plugins/_mentions.scss +21 -0
- package/src/themes/plugins/_menus-and-pickers.scss +153 -0
- package/src/themes/plugins/_table.scss +20 -0
- package/src/themes/plugins/_toolbar.scss +36 -0
- package/src/themes/plugins/_tree-view.scss +11 -0
- package/src/themes/plugins copy.scss +656 -0
- package/src/themes/plugins.scss +20 -1165
- package/src/themes/ui-components/_animations.scss +31 -0
- package/src/themes/ui-components/_backgrounds.scss +27 -0
- package/src/themes/ui-components/_borders.scss +20 -0
- package/src/themes/ui-components/_button.scss +176 -0
- package/src/themes/ui-components/_checkbox.scss +14 -0
- package/src/themes/ui-components/_cursors.scss +31 -0
- package/src/themes/ui-components/_dialog.scss +86 -0
- package/src/themes/ui-components/_display-sizing.scss +100 -0
- package/src/themes/ui-components/_flex.scss +124 -0
- package/src/themes/ui-components/_form-layout.scss +15 -0
- package/src/themes/ui-components/_icons.scss +23 -0
- package/src/themes/ui-components/_input.scss +86 -0
- package/src/themes/ui-components/_label.scss +19 -0
- package/src/themes/ui-components/_loader.scss +9 -0
- package/src/themes/ui-components/_margins-paddings.scss +45 -0
- package/src/themes/ui-components/_popover.scss +16 -0
- package/src/themes/ui-components/_positioning.scss +73 -0
- package/src/themes/ui-components/_rounded.scss +19 -0
- package/src/themes/ui-components/_scroll-area.scss +11 -0
- package/src/themes/ui-components/_select.scss +110 -0
- package/src/themes/ui-components/_separator.scss +19 -0
- package/src/themes/ui-components/_shadow.scss +15 -0
- package/src/themes/ui-components/_tabs.scss +46 -0
- package/src/themes/ui-components/_text-utilities.scss +48 -0
- package/src/themes/ui-components/_toggle-toolbar.scss +128 -0
- package/src/themes/ui-components/_toggle.scss +80 -0
- package/src/themes/ui-components/_typography.scss +22 -0
- package/src/themes/ui-components copy.scss +1335 -0
- package/src/themes/ui-components.scss +27 -937
- package/src/transformers/markdown-list-transformer.ts +51 -51
- package/src/ui/button.tsx +11 -2
- package/src/ui/collapsible.tsx +1 -1
- package/src/ui/dialog.tsx +2 -2
- package/src/ui/flex.tsx +4 -4
- package/src/ui/popover.tsx +1 -1
- 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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
<
|
|
219
|
-
|
|
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
|
-
<
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
}
|