@thangph2146/lexical-editor 0.0.1
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 +33121 -0
- package/dist/editor-x/editor.cjs.map +1 -0
- package/dist/editor-x/editor.css +2854 -0
- package/dist/editor-x/editor.css.map +1 -0
- package/dist/editor-x/editor.d.cts +12 -0
- package/dist/editor-x/editor.d.ts +12 -0
- package/dist/editor-x/editor.js +33095 -0
- package/dist/editor-x/editor.js.map +1 -0
- package/dist/index.cjs +33210 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +2854 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.cts +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +33183 -0
- package/dist/index.js.map +1 -0
- package/package.json +84 -0
- package/src/components/lexical-editor.tsx +123 -0
- package/src/context/editor-container-context.tsx +29 -0
- package/src/context/priority-image-context.tsx +7 -0
- package/src/context/toolbar-context.tsx +60 -0
- package/src/context/uploads-context.tsx +53 -0
- package/src/editor-hooks/use-debounce.ts +80 -0
- package/src/editor-hooks/use-modal.tsx +64 -0
- package/src/editor-hooks/use-report.ts +57 -0
- package/src/editor-hooks/use-update-toolbar.ts +41 -0
- package/src/editor-ui/broken-image.tsx +18 -0
- package/src/editor-ui/caption-composer.tsx +45 -0
- package/src/editor-ui/code-button.tsx +75 -0
- package/src/editor-ui/color-picker.tsx +2010 -0
- package/src/editor-ui/content-editable.tsx +37 -0
- package/src/editor-ui/hooks/use-image-caption-controls.ts +118 -0
- package/src/editor-ui/hooks/use-image-node-interactions.ts +245 -0
- package/src/editor-ui/hooks/use-responsive-image-dimensions.ts +202 -0
- package/src/editor-ui/image-component.tsx +321 -0
- package/src/editor-ui/image-placeholder.tsx +57 -0
- package/src/editor-ui/image-resizer.tsx +499 -0
- package/src/editor-ui/image-sizing.ts +120 -0
- package/src/editor-ui/lazy-image.tsx +136 -0
- package/src/editor-x/editor.tsx +117 -0
- package/src/editor-x/nodes.ts +79 -0
- package/src/editor-x/plugins.tsx +380 -0
- package/src/hooks/use-click-outside.ts +27 -0
- package/src/hooks/use-element-size.ts +54 -0
- package/src/hooks/use-header-height.ts +95 -0
- package/src/hooks/use-isomorphic-layout-effect.ts +4 -0
- package/src/index.ts +4 -0
- package/src/lib/logger.ts +6 -0
- package/src/lib/utils.ts +19 -0
- package/src/nodes/autocomplete-node.tsx +94 -0
- package/src/nodes/embeds/tweet-node.tsx +224 -0
- package/src/nodes/embeds/youtube-node.tsx +519 -0
- package/src/nodes/emoji-node.tsx +83 -0
- package/src/nodes/image-node.tsx +328 -0
- package/src/nodes/keyword-node.tsx +58 -0
- package/src/nodes/layout-container-node.tsx +128 -0
- package/src/nodes/layout-item-node.tsx +118 -0
- package/src/nodes/list-with-color-node.tsx +160 -0
- package/src/nodes/mention-node.ts +122 -0
- package/src/plugins/actions/actions-plugin.tsx +3 -0
- package/src/plugins/actions/character-limit-plugin.tsx +27 -0
- package/src/plugins/actions/clear-editor-plugin.tsx +70 -0
- package/src/plugins/actions/counter-character-plugin.tsx +80 -0
- package/src/plugins/actions/edit-mode-toggle-plugin.tsx +49 -0
- package/src/plugins/actions/import-export-plugin.tsx +61 -0
- package/src/plugins/actions/markdown-toggle-plugin.tsx +78 -0
- package/src/plugins/actions/max-length-plugin.tsx +59 -0
- package/src/plugins/actions/share-content-plugin.tsx +72 -0
- package/src/plugins/actions/speech-to-text-plugin.tsx +159 -0
- package/src/plugins/actions/tree-view-plugin.tsx +63 -0
- package/src/plugins/align-plugin.tsx +86 -0
- package/src/plugins/auto-link-plugin.tsx +34 -0
- package/src/plugins/autocomplete-plugin.tsx +2574 -0
- package/src/plugins/code-action-menu-plugin.tsx +240 -0
- package/src/plugins/code-highlight-plugin.tsx +22 -0
- package/src/plugins/component-picker-menu-plugin.tsx +427 -0
- package/src/plugins/context-menu-plugin.tsx +311 -0
- package/src/plugins/drag-drop-paste-plugin.tsx +52 -0
- package/src/plugins/draggable-block-plugin.tsx +50 -0
- package/src/plugins/embeds/auto-embed-plugin.tsx +324 -0
- package/src/plugins/embeds/twitter-plugin.tsx +45 -0
- package/src/plugins/embeds/youtube-plugin.tsx +84 -0
- package/src/plugins/emoji-picker-plugin.tsx +206 -0
- package/src/plugins/emojis-plugin.tsx +84 -0
- package/src/plugins/floating-link-editor-plugin.tsx +791 -0
- package/src/plugins/floating-text-format-plugin.tsx +710 -0
- package/src/plugins/images-plugin.tsx +671 -0
- package/src/plugins/keywords-plugin.tsx +59 -0
- package/src/plugins/layout-plugin.tsx +658 -0
- package/src/plugins/link-plugin.tsx +18 -0
- package/src/plugins/list-color-plugin.tsx +178 -0
- package/src/plugins/list-max-indent-level-plugin.tsx +85 -0
- package/src/plugins/mentions-plugin.tsx +714 -0
- package/src/plugins/picker/alignment-picker-plugin.tsx +40 -0
- package/src/plugins/picker/bulleted-list-picker-plugin.tsx +14 -0
- package/src/plugins/picker/check-list-picker-plugin.tsx +14 -0
- package/src/plugins/picker/code-picker-plugin.tsx +30 -0
- package/src/plugins/picker/columns-layout-picker-plugin.tsx +16 -0
- package/src/plugins/picker/component-picker-option.tsx +47 -0
- package/src/plugins/picker/divider-picker-plugin.tsx +14 -0
- package/src/plugins/picker/embeds-picker-plugin.tsx +24 -0
- package/src/plugins/picker/heading-picker-plugin.tsx +32 -0
- package/src/plugins/picker/image-picker-plugin.tsx +16 -0
- package/src/plugins/picker/numbered-list-picker-plugin.tsx +14 -0
- package/src/plugins/picker/paragraph-picker-plugin.tsx +20 -0
- package/src/plugins/picker/quote-picker-plugin.tsx +21 -0
- package/src/plugins/picker/table-picker-plugin.tsx +56 -0
- package/src/plugins/tab-focus-plugin.tsx +66 -0
- package/src/plugins/table-column-resizer-plugin.tsx +309 -0
- package/src/plugins/table-plugin.tsx +299 -0
- package/src/plugins/toolbar/block-format/block-format-data.tsx +69 -0
- package/src/plugins/toolbar/block-format/format-bulleted-list.tsx +40 -0
- package/src/plugins/toolbar/block-format/format-check-list.tsx +40 -0
- package/src/plugins/toolbar/block-format/format-code-block.tsx +45 -0
- package/src/plugins/toolbar/block-format/format-heading.tsx +34 -0
- package/src/plugins/toolbar/block-format/format-list-with-marker.tsx +74 -0
- package/src/plugins/toolbar/block-format/format-numbered-list.tsx +40 -0
- package/src/plugins/toolbar/block-format/format-paragraph.tsx +31 -0
- package/src/plugins/toolbar/block-format/format-quote.tsx +32 -0
- package/src/plugins/toolbar/block-format-toolbar-plugin.tsx +117 -0
- package/src/plugins/toolbar/block-insert/insert-columns-layout.tsx +32 -0
- package/src/plugins/toolbar/block-insert/insert-embeds.tsx +31 -0
- package/src/plugins/toolbar/block-insert/insert-horizontal-rule.tsx +30 -0
- package/src/plugins/toolbar/block-insert/insert-image.tsx +32 -0
- package/src/plugins/toolbar/block-insert/insert-table.tsx +32 -0
- package/src/plugins/toolbar/block-insert-plugin.tsx +30 -0
- package/src/plugins/toolbar/clear-formatting-toolbar-plugin.tsx +92 -0
- package/src/plugins/toolbar/code-language-toolbar-plugin.tsx +121 -0
- package/src/plugins/toolbar/element-format-toolbar-plugin.tsx +251 -0
- package/src/plugins/toolbar/font-background-toolbar-plugin.tsx +179 -0
- package/src/plugins/toolbar/font-color-toolbar-plugin.tsx +101 -0
- package/src/plugins/toolbar/font-family-toolbar-plugin.tsx +91 -0
- package/src/plugins/toolbar/font-format-toolbar-plugin.tsx +85 -0
- package/src/plugins/toolbar/font-size-toolbar-plugin.tsx +177 -0
- package/src/plugins/toolbar/history-toolbar-plugin.tsx +87 -0
- package/src/plugins/toolbar/link-toolbar-plugin.tsx +90 -0
- package/src/plugins/toolbar/subsuper-toolbar-plugin.tsx +69 -0
- package/src/plugins/toolbar/toolbar-plugin.tsx +66 -0
- package/src/plugins/typing-pref-plugin.tsx +118 -0
- package/src/shared/can-use-dom.ts +4 -0
- package/src/shared/environment.ts +47 -0
- package/src/shared/invariant.ts +16 -0
- package/src/shared/use-layout-effect.ts +12 -0
- package/src/themes/_mixins.scss +107 -0
- package/src/themes/_variables.scss +33 -0
- package/src/themes/editor-theme.scss +622 -0
- package/src/themes/editor-theme.ts +118 -0
- package/src/themes/plugins.scss +1180 -0
- package/src/themes/ui-components.scss +936 -0
- package/src/transformers/markdown-emoji-transformer.ts +20 -0
- package/src/transformers/markdown-hr-transformer.ts +28 -0
- package/src/transformers/markdown-image-transformer.ts +31 -0
- package/src/transformers/markdown-list-transformer.ts +51 -0
- package/src/transformers/markdown-table-transformer.ts +200 -0
- package/src/transformers/markdown-tweet-transformer.ts +26 -0
- package/src/ui/button-group.tsx +10 -0
- package/src/ui/button.tsx +29 -0
- package/src/ui/collapsible.tsx +67 -0
- package/src/ui/command.tsx +48 -0
- package/src/ui/dialog.tsx +146 -0
- package/src/ui/flex.tsx +38 -0
- package/src/ui/input.tsx +20 -0
- package/src/ui/label.tsx +20 -0
- package/src/ui/popover.tsx +128 -0
- package/src/ui/scroll-area.tsx +17 -0
- package/src/ui/select.tsx +171 -0
- package/src/ui/separator.tsx +20 -0
- package/src/ui/slider.tsx +14 -0
- package/src/ui/slot.tsx +3 -0
- package/src/ui/tabs.tsx +87 -0
- package/src/ui/toggle-group.tsx +109 -0
- package/src/ui/toggle.tsx +28 -0
- package/src/ui/tooltip.tsx +28 -0
- package/src/ui/typography.tsx +44 -0
- package/src/utils/doc-serialization.ts +68 -0
- package/src/utils/emoji-list.ts +16604 -0
- package/src/utils/get-dom-range-rect.ts +20 -0
- package/src/utils/get-selected-node.ts +20 -0
- package/src/utils/is-mobile-width.ts +0 -0
- package/src/utils/set-floating-elem-position-for-link-editor.ts +39 -0
- package/src/utils/set-floating-elem-position.ts +44 -0
- package/src/utils/swipe.ts +119 -0
- package/src/utils/url.ts +32 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { JSX, useState, useRef, useEffect } from "react"
|
|
3
|
+
import Image from "next/image"
|
|
4
|
+
import { cn } from "../lib/utils"
|
|
5
|
+
import {
|
|
6
|
+
DimensionValue,
|
|
7
|
+
imageCache,
|
|
8
|
+
DEFAULT_WIDTH,
|
|
9
|
+
DEFAULT_HEIGHT,
|
|
10
|
+
DEFAULT_DIMENSIONS
|
|
11
|
+
} from "./hooks/use-responsive-image-dimensions"
|
|
12
|
+
|
|
13
|
+
interface LazyImageProps {
|
|
14
|
+
altText: string
|
|
15
|
+
className: string | null
|
|
16
|
+
height: DimensionValue
|
|
17
|
+
imageRef: { current: null | HTMLImageElement }
|
|
18
|
+
maxWidth: number
|
|
19
|
+
src: string
|
|
20
|
+
width: DimensionValue
|
|
21
|
+
onError: () => void
|
|
22
|
+
fetchPriority?: "high" | "low" | "auto"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* LazyImage - Handles image loading and rendering within the Lexical editor.
|
|
27
|
+
* Includes dimensions caching to prevent layout shift.
|
|
28
|
+
*/
|
|
29
|
+
export function LazyImage({
|
|
30
|
+
altText,
|
|
31
|
+
className,
|
|
32
|
+
imageRef,
|
|
33
|
+
src,
|
|
34
|
+
width,
|
|
35
|
+
height,
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- prop reserved for future use
|
|
37
|
+
maxWidth: _maxWidth,
|
|
38
|
+
onError,
|
|
39
|
+
fetchPriority = "auto",
|
|
40
|
+
}: LazyImageProps): JSX.Element {
|
|
41
|
+
// Convert DimensionValue to number for width/height attributes
|
|
42
|
+
const getNumericValue = (value: DimensionValue): number | undefined => {
|
|
43
|
+
if (typeof value === "number") return value
|
|
44
|
+
if (typeof value === "string") {
|
|
45
|
+
if (value === "inherit") return undefined
|
|
46
|
+
const num = parseInt((value as string).replace("px", ""), 10)
|
|
47
|
+
return isNaN(num) ? undefined : num
|
|
48
|
+
}
|
|
49
|
+
return undefined
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const widthAttr = getNumericValue(width)
|
|
53
|
+
const heightAttr = getNumericValue(height)
|
|
54
|
+
|
|
55
|
+
const cachedDims = imageCache.get(src)
|
|
56
|
+
const [actualDimensions, setActualDimensions] = useState<{ width?: number; height?: number; ratio?: number }>(
|
|
57
|
+
cachedDims || DEFAULT_DIMENSIONS
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
const wrapperRef = useRef<HTMLDivElement>(null)
|
|
61
|
+
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (wrapperRef.current) {
|
|
64
|
+
const imgElement = wrapperRef.current.querySelector("img") as HTMLImageElement | null
|
|
65
|
+
if (imgElement) {
|
|
66
|
+
imageRef.current = imgElement
|
|
67
|
+
if ((width === "inherit" || height === "inherit") && imgElement.complete && imgElement.naturalWidth && imgElement.naturalHeight) {
|
|
68
|
+
const ratio = imgElement.naturalWidth / imgElement.naturalHeight
|
|
69
|
+
setActualDimensions({
|
|
70
|
+
width: imgElement.naturalWidth,
|
|
71
|
+
height: imgElement.naturalHeight,
|
|
72
|
+
ratio,
|
|
73
|
+
})
|
|
74
|
+
imageCache.set(src, {
|
|
75
|
+
width: imgElement.naturalWidth,
|
|
76
|
+
height: imgElement.naturalHeight,
|
|
77
|
+
ratio,
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}, [width, height, imageRef, src])
|
|
83
|
+
|
|
84
|
+
const getSizes = (): string => {
|
|
85
|
+
if (typeof widthAttr === "number" && widthAttr > 0) {
|
|
86
|
+
if (widthAttr <= 640) return "(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
|
|
87
|
+
return "(max-width: 768px) 100vw, (max-width: 1200px) 80vw, 1200px"
|
|
88
|
+
}
|
|
89
|
+
return "(max-width: 768px) 100vw, (max-width: 1200px) 80vw, 1200px"
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const renderWidth = widthAttr || actualDimensions.width || DEFAULT_WIDTH
|
|
93
|
+
const renderHeight = heightAttr || actualDimensions.height || DEFAULT_HEIGHT
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<div ref={wrapperRef} className={cn("editor-lazy-image-wrapper", className)}>
|
|
97
|
+
<Image
|
|
98
|
+
src={src}
|
|
99
|
+
alt={altText}
|
|
100
|
+
title={altText}
|
|
101
|
+
width={renderWidth}
|
|
102
|
+
height={renderHeight}
|
|
103
|
+
sizes={getSizes()}
|
|
104
|
+
quality={75}
|
|
105
|
+
unoptimized={true}
|
|
106
|
+
style={{
|
|
107
|
+
height: height === "inherit" ? "auto" : height,
|
|
108
|
+
width: width === "inherit" ? "100%" : width,
|
|
109
|
+
}}
|
|
110
|
+
onError={onError}
|
|
111
|
+
draggable={false}
|
|
112
|
+
priority={fetchPriority === "high"}
|
|
113
|
+
loading="eager"
|
|
114
|
+
decoding="async"
|
|
115
|
+
onLoad={(e) => {
|
|
116
|
+
const img = e.currentTarget
|
|
117
|
+
if (img) {
|
|
118
|
+
imageRef.current = img
|
|
119
|
+
const ratio = img.naturalWidth / img.naturalHeight
|
|
120
|
+
imageCache.set(src, {
|
|
121
|
+
width: img.naturalWidth,
|
|
122
|
+
height: img.naturalHeight,
|
|
123
|
+
ratio,
|
|
124
|
+
})
|
|
125
|
+
if (width === "inherit" || height === "inherit") {
|
|
126
|
+
setActualDimensions({
|
|
127
|
+
width: img.naturalWidth,
|
|
128
|
+
height: img.naturalHeight,
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}}
|
|
133
|
+
/>
|
|
134
|
+
</div>
|
|
135
|
+
)
|
|
136
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from "react"
|
|
4
|
+
import {
|
|
5
|
+
InitialConfigType,
|
|
6
|
+
LexicalComposer,
|
|
7
|
+
} from "@lexical/react/LexicalComposer"
|
|
8
|
+
import { OnChangePlugin } from "@lexical/react/LexicalOnChangePlugin"
|
|
9
|
+
import { EditorState, SerializedEditorState } from "lexical"
|
|
10
|
+
|
|
11
|
+
import { editorTheme } from "../themes/editor-theme"
|
|
12
|
+
import { TooltipProvider } from "../ui/tooltip"
|
|
13
|
+
|
|
14
|
+
import { cn } from "../lib/utils"
|
|
15
|
+
import { logger } from "../lib/logger"
|
|
16
|
+
import { useElementSize } from "../hooks/use-element-size"
|
|
17
|
+
import { EditorContainerProvider } from "../context/editor-container-context"
|
|
18
|
+
|
|
19
|
+
function createEditorConfig(nodes: InitialConfigType["nodes"]) {
|
|
20
|
+
return {
|
|
21
|
+
namespace: "Editor",
|
|
22
|
+
theme: editorTheme,
|
|
23
|
+
nodes,
|
|
24
|
+
onError: (error: Error) => {
|
|
25
|
+
if (
|
|
26
|
+
error?.message?.includes("TableObserver") &&
|
|
27
|
+
error?.message?.includes("Expected to find TableElement in DOM")
|
|
28
|
+
) {
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
logger.error("[Editor] Error:", error)
|
|
32
|
+
},
|
|
33
|
+
} satisfies Omit<InitialConfigType, "editable" | "editorState">
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function Editor({
|
|
37
|
+
editorState,
|
|
38
|
+
editorSerializedState,
|
|
39
|
+
onChange,
|
|
40
|
+
onSerializedChange,
|
|
41
|
+
readOnly = false,
|
|
42
|
+
}: {
|
|
43
|
+
editorState?: EditorState
|
|
44
|
+
editorSerializedState?: SerializedEditorState
|
|
45
|
+
onChange?: (editorState: EditorState) => void
|
|
46
|
+
onSerializedChange?: (editorSerializedState: SerializedEditorState) => void
|
|
47
|
+
readOnly?: boolean
|
|
48
|
+
}) {
|
|
49
|
+
const { ref: editorRef, width: editorWidth } = useElementSize<HTMLDivElement>()
|
|
50
|
+
const editorMaxWidth = editorWidth || undefined
|
|
51
|
+
|
|
52
|
+
const [config, setConfig] = useState<{
|
|
53
|
+
nodes: InitialConfigType["nodes"]
|
|
54
|
+
Plugins: React.ComponentType<{ readOnly?: boolean }>
|
|
55
|
+
} | null>(null)
|
|
56
|
+
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
Promise.all([
|
|
59
|
+
import("./nodes").then((m) => m.nodes),
|
|
60
|
+
import("./plugins").then((m) => ({ Plugins: m.Plugins })),
|
|
61
|
+
]).then(([nodes, { Plugins }]) => setConfig({ nodes, Plugins }))
|
|
62
|
+
}, [])
|
|
63
|
+
|
|
64
|
+
if (!config) {
|
|
65
|
+
return (
|
|
66
|
+
<div
|
|
67
|
+
className={cn(
|
|
68
|
+
"editor-loading-container"
|
|
69
|
+
)}
|
|
70
|
+
id="editor-x"
|
|
71
|
+
>
|
|
72
|
+
<div className="editor-loading-text">Đang tải editor...</div>
|
|
73
|
+
</div>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const editorConfig = createEditorConfig(config.nodes)
|
|
78
|
+
const { Plugins } = config
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div
|
|
82
|
+
ref={editorRef}
|
|
83
|
+
className={cn(
|
|
84
|
+
"editor-root-container lexical-editor-root",
|
|
85
|
+
!readOnly && "editor-root-container--shadow"
|
|
86
|
+
)}
|
|
87
|
+
id="editor-x"
|
|
88
|
+
>
|
|
89
|
+
<EditorContainerProvider value={{ maxWidth: editorMaxWidth }}>
|
|
90
|
+
<LexicalComposer
|
|
91
|
+
initialConfig={{
|
|
92
|
+
...editorConfig,
|
|
93
|
+
editable: !readOnly,
|
|
94
|
+
...(editorState ? { editorState } : {}),
|
|
95
|
+
...(editorSerializedState
|
|
96
|
+
? { editorState: JSON.stringify(editorSerializedState) }
|
|
97
|
+
: {}),
|
|
98
|
+
}}
|
|
99
|
+
>
|
|
100
|
+
<TooltipProvider>
|
|
101
|
+
<Plugins readOnly={readOnly} />
|
|
102
|
+
|
|
103
|
+
{!readOnly && (
|
|
104
|
+
<OnChangePlugin
|
|
105
|
+
ignoreSelectionChange={true}
|
|
106
|
+
onChange={(editorState) => {
|
|
107
|
+
onChange?.(editorState)
|
|
108
|
+
onSerializedChange?.(editorState.toJSON())
|
|
109
|
+
}}
|
|
110
|
+
/>
|
|
111
|
+
)}
|
|
112
|
+
</TooltipProvider>
|
|
113
|
+
</LexicalComposer>
|
|
114
|
+
</EditorContainerProvider>
|
|
115
|
+
</div>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { CodeHighlightNode, CodeNode } from "@lexical/code"
|
|
2
|
+
import { HashtagNode } from "@lexical/hashtag"
|
|
3
|
+
import { AutoLinkNode, LinkNode } from "@lexical/link"
|
|
4
|
+
import { ListItemNode, ListNode, type ListType } from "@lexical/list"
|
|
5
|
+
import { OverflowNode } from "@lexical/overflow"
|
|
6
|
+
import { HorizontalRuleNode } from "@lexical/react/LexicalHorizontalRuleNode"
|
|
7
|
+
import { HeadingNode, QuoteNode } from "@lexical/rich-text"
|
|
8
|
+
import { TableCellNode, TableNode, TableRowNode } from "@lexical/table"
|
|
9
|
+
import {
|
|
10
|
+
Klass,
|
|
11
|
+
LexicalEditor,
|
|
12
|
+
LexicalNode,
|
|
13
|
+
LexicalNodeReplacement,
|
|
14
|
+
ParagraphNode,
|
|
15
|
+
TextNode,
|
|
16
|
+
} from "lexical"
|
|
17
|
+
|
|
18
|
+
import { AutocompleteNode } from "../nodes/autocomplete-node"
|
|
19
|
+
import { TweetNode } from "../nodes/embeds/tweet-node"
|
|
20
|
+
import { YouTubeNode } from "../nodes/embeds/youtube-node"
|
|
21
|
+
import { EmojiNode } from "../nodes/emoji-node"
|
|
22
|
+
import { ImageNode } from "../nodes/image-node"
|
|
23
|
+
import { KeywordNode } from "../nodes/keyword-node"
|
|
24
|
+
import { LayoutContainerNode } from "../nodes/layout-container-node"
|
|
25
|
+
import { LayoutItemNode } from "../nodes/layout-item-node"
|
|
26
|
+
import { ListWithColorNode } from "../nodes/list-with-color-node"
|
|
27
|
+
import { MentionNode } from "../nodes/mention-node"
|
|
28
|
+
|
|
29
|
+
/** Tạo ListWithColorNode dùng đúng class đã đăng ký trong editor (tránh type mismatch khi bundle trùng). */
|
|
30
|
+
export function createListWithColorNodeFromRegistry(
|
|
31
|
+
editor: LexicalEditor,
|
|
32
|
+
listType: ListType,
|
|
33
|
+
start: number
|
|
34
|
+
): InstanceType<typeof ListWithColorNode> {
|
|
35
|
+
const config = (editor as unknown as { _config?: { nodes?: Array<Klass<LexicalNode>> } })
|
|
36
|
+
._config
|
|
37
|
+
const Klass = config?.nodes?.find(
|
|
38
|
+
(K) =>
|
|
39
|
+
typeof (K as { getType?: () => string }).getType === "function" &&
|
|
40
|
+
(K as { getType: () => string }).getType() === "listwithcolor"
|
|
41
|
+
) as typeof ListWithColorNode | undefined
|
|
42
|
+
if (Klass) return new Klass(listType, start) as InstanceType<typeof ListWithColorNode>
|
|
43
|
+
return new ListWithColorNode(listType, start)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const nodes: ReadonlyArray<Klass<LexicalNode> | LexicalNodeReplacement> =
|
|
47
|
+
[
|
|
48
|
+
HeadingNode,
|
|
49
|
+
ParagraphNode,
|
|
50
|
+
TextNode,
|
|
51
|
+
QuoteNode,
|
|
52
|
+
{
|
|
53
|
+
replace: ListNode,
|
|
54
|
+
with: (node: ListNode) => {
|
|
55
|
+
return new ListWithColorNode(node.getListType(), node.getStart())
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
ListWithColorNode,
|
|
59
|
+
ListItemNode,
|
|
60
|
+
LinkNode,
|
|
61
|
+
OverflowNode,
|
|
62
|
+
HashtagNode,
|
|
63
|
+
TableNode,
|
|
64
|
+
TableCellNode,
|
|
65
|
+
TableRowNode,
|
|
66
|
+
CodeNode,
|
|
67
|
+
CodeHighlightNode,
|
|
68
|
+
HorizontalRuleNode,
|
|
69
|
+
MentionNode,
|
|
70
|
+
ImageNode,
|
|
71
|
+
EmojiNode,
|
|
72
|
+
KeywordNode,
|
|
73
|
+
LayoutContainerNode,
|
|
74
|
+
LayoutItemNode,
|
|
75
|
+
AutoLinkNode,
|
|
76
|
+
TweetNode,
|
|
77
|
+
YouTubeNode,
|
|
78
|
+
AutocompleteNode,
|
|
79
|
+
]
|