@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,40 @@
|
|
|
1
|
+
import { FORMAT_ELEMENT_COMMAND } from "lexical"
|
|
2
|
+
import {
|
|
3
|
+
AlignCenterIcon,
|
|
4
|
+
AlignJustifyIcon,
|
|
5
|
+
AlignLeftIcon,
|
|
6
|
+
AlignRightIcon,
|
|
7
|
+
} from "lucide-react"
|
|
8
|
+
|
|
9
|
+
import { ComponentPickerOption } from "../../plugins/picker/component-picker-option"
|
|
10
|
+
import { IconSize } from "../../ui/typography"
|
|
11
|
+
|
|
12
|
+
export function AlignmentPickerPlugin({
|
|
13
|
+
alignment,
|
|
14
|
+
}: {
|
|
15
|
+
alignment: "left" | "center" | "right" | "justify"
|
|
16
|
+
}) {
|
|
17
|
+
return new ComponentPickerOption(`Align ${alignment}`, {
|
|
18
|
+
icon: <AlignIcons alignment={alignment} />,
|
|
19
|
+
keywords: ["align", "justify", alignment],
|
|
20
|
+
onSelect: (_, editor) =>
|
|
21
|
+
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, alignment),
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function AlignIcons({
|
|
26
|
+
alignment,
|
|
27
|
+
}: {
|
|
28
|
+
alignment: "left" | "center" | "right" | "justify"
|
|
29
|
+
}) {
|
|
30
|
+
switch (alignment) {
|
|
31
|
+
case "left":
|
|
32
|
+
return <IconSize size="sm"><AlignLeftIcon /></IconSize>
|
|
33
|
+
case "center":
|
|
34
|
+
return <IconSize size="sm"><AlignCenterIcon /></IconSize>
|
|
35
|
+
case "right":
|
|
36
|
+
return <IconSize size="sm"><AlignRightIcon /></IconSize>
|
|
37
|
+
case "justify":
|
|
38
|
+
return <IconSize size="sm"><AlignJustifyIcon /></IconSize>
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { INSERT_UNORDERED_LIST_COMMAND } from "@lexical/list"
|
|
2
|
+
import { ListIcon } from "lucide-react"
|
|
3
|
+
|
|
4
|
+
import { ComponentPickerOption } from "../../plugins/picker/component-picker-option"
|
|
5
|
+
import { IconSize } from "../../ui/typography"
|
|
6
|
+
|
|
7
|
+
export function BulletedListPickerPlugin() {
|
|
8
|
+
return new ComponentPickerOption("Bulleted List", {
|
|
9
|
+
icon: <IconSize size="sm"><ListIcon /></IconSize>,
|
|
10
|
+
keywords: ["bulleted list", "unordered list", "ul", "- list", "+ list"],
|
|
11
|
+
onSelect: (_, editor) =>
|
|
12
|
+
editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, undefined),
|
|
13
|
+
})
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { INSERT_CHECK_LIST_COMMAND } from "@lexical/list"
|
|
2
|
+
import { ListTodoIcon } from "lucide-react"
|
|
3
|
+
|
|
4
|
+
import { ComponentPickerOption } from "../../plugins/picker/component-picker-option"
|
|
5
|
+
import { IconSize } from "../../ui/typography"
|
|
6
|
+
|
|
7
|
+
export function CheckListPickerPlugin() {
|
|
8
|
+
return new ComponentPickerOption("Check List", {
|
|
9
|
+
icon: <IconSize size="sm"><ListTodoIcon /></IconSize>,
|
|
10
|
+
keywords: ["check list", "todo list"],
|
|
11
|
+
onSelect: (_, editor) =>
|
|
12
|
+
editor.dispatchCommand(INSERT_CHECK_LIST_COMMAND, undefined),
|
|
13
|
+
})
|
|
14
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { $createCodeNode } from "@lexical/code"
|
|
2
|
+
import { $setBlocksType } from "@lexical/selection"
|
|
3
|
+
import { $getSelection, $isRangeSelection } from "lexical"
|
|
4
|
+
import { CodeIcon } from "lucide-react"
|
|
5
|
+
|
|
6
|
+
import { ComponentPickerOption } from "../../plugins/picker/component-picker-option"
|
|
7
|
+
import { IconSize } from "../../ui/typography"
|
|
8
|
+
|
|
9
|
+
export function CodePickerPlugin() {
|
|
10
|
+
return new ComponentPickerOption("Code", {
|
|
11
|
+
icon: <IconSize size="sm"><CodeIcon /></IconSize>,
|
|
12
|
+
keywords: ["javascript", "python", "js", "codeblock"],
|
|
13
|
+
onSelect: (_, editor) =>
|
|
14
|
+
editor.update(() => {
|
|
15
|
+
const selection = $getSelection()
|
|
16
|
+
|
|
17
|
+
if ($isRangeSelection(selection)) {
|
|
18
|
+
if (selection.isCollapsed()) {
|
|
19
|
+
$setBlocksType(selection, () => $createCodeNode())
|
|
20
|
+
} else {
|
|
21
|
+
// Will this ever happen?
|
|
22
|
+
const textContent = selection.getTextContent()
|
|
23
|
+
const codeNode = $createCodeNode()
|
|
24
|
+
selection.insertNodes([codeNode])
|
|
25
|
+
selection.insertRawText(textContent)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}),
|
|
29
|
+
})
|
|
30
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Columns3Icon } from "lucide-react"
|
|
2
|
+
|
|
3
|
+
import { InsertLayoutDialog } from "../../plugins/layout-plugin"
|
|
4
|
+
import { ComponentPickerOption } from "../../plugins/picker/component-picker-option"
|
|
5
|
+
import { IconSize } from "../../ui/typography"
|
|
6
|
+
|
|
7
|
+
export function ColumnsLayoutPickerPlugin() {
|
|
8
|
+
return new ComponentPickerOption("Columns Layout", {
|
|
9
|
+
icon: <IconSize size="sm"><Columns3Icon /></IconSize>,
|
|
10
|
+
keywords: ["columns", "layout", "grid"],
|
|
11
|
+
onSelect: (_, editor, showModal) =>
|
|
12
|
+
showModal("Insert Columns Layout", (onClose) => (
|
|
13
|
+
<InsertLayoutDialog activeEditor={editor} onClose={onClose} />
|
|
14
|
+
)),
|
|
15
|
+
})
|
|
16
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { JSX } from "react"
|
|
2
|
+
import { MenuOption } from "@lexical/react/LexicalTypeaheadMenuPlugin"
|
|
3
|
+
import { LexicalEditor } from "lexical"
|
|
4
|
+
|
|
5
|
+
export class ComponentPickerOption extends MenuOption {
|
|
6
|
+
// What shows up in the editor
|
|
7
|
+
title: string
|
|
8
|
+
// Icon for display
|
|
9
|
+
icon?: JSX.Element
|
|
10
|
+
// For extra searching.
|
|
11
|
+
keywords: Array<string>
|
|
12
|
+
// TBD
|
|
13
|
+
keyboardShortcut?: string
|
|
14
|
+
// What happens when you select this option?
|
|
15
|
+
onSelect: (
|
|
16
|
+
queryString: string,
|
|
17
|
+
editor: LexicalEditor,
|
|
18
|
+
showModal: (
|
|
19
|
+
title: string,
|
|
20
|
+
showModal: (onClose: () => void) => JSX.Element
|
|
21
|
+
) => void
|
|
22
|
+
) => void
|
|
23
|
+
|
|
24
|
+
constructor(
|
|
25
|
+
title: string,
|
|
26
|
+
options: {
|
|
27
|
+
icon?: JSX.Element
|
|
28
|
+
keywords?: Array<string>
|
|
29
|
+
keyboardShortcut?: string
|
|
30
|
+
onSelect: (
|
|
31
|
+
queryString: string,
|
|
32
|
+
editor: LexicalEditor,
|
|
33
|
+
showModal: (
|
|
34
|
+
title: string,
|
|
35
|
+
showModal: (onClose: () => void) => JSX.Element
|
|
36
|
+
) => void
|
|
37
|
+
) => void
|
|
38
|
+
}
|
|
39
|
+
) {
|
|
40
|
+
super(title)
|
|
41
|
+
this.title = title
|
|
42
|
+
this.keywords = options.keywords || []
|
|
43
|
+
this.icon = options.icon
|
|
44
|
+
this.keyboardShortcut = options.keyboardShortcut
|
|
45
|
+
this.onSelect = options.onSelect.bind(this)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { INSERT_HORIZONTAL_RULE_COMMAND } from "@lexical/react/LexicalHorizontalRuleNode"
|
|
2
|
+
import { MinusIcon } from "lucide-react"
|
|
3
|
+
|
|
4
|
+
import { ComponentPickerOption } from "../../plugins/picker/component-picker-option"
|
|
5
|
+
import { IconSize } from "../../ui/typography"
|
|
6
|
+
|
|
7
|
+
export function DividerPickerPlugin() {
|
|
8
|
+
return new ComponentPickerOption("Divider", {
|
|
9
|
+
icon: <IconSize size="sm"><MinusIcon /></IconSize>,
|
|
10
|
+
keywords: ["horizontal rule", "divider", "hr"],
|
|
11
|
+
onSelect: (_, editor) =>
|
|
12
|
+
editor.dispatchCommand(INSERT_HORIZONTAL_RULE_COMMAND, undefined),
|
|
13
|
+
})
|
|
14
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { INSERT_EMBED_COMMAND } from "@lexical/react/LexicalAutoEmbedPlugin"
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
CustomEmbedConfig,
|
|
5
|
+
EmbedConfigs,
|
|
6
|
+
} from "../../plugins/embeds/auto-embed-plugin"
|
|
7
|
+
import { ComponentPickerOption } from "../../plugins/picker/component-picker-option"
|
|
8
|
+
|
|
9
|
+
export function EmbedsPickerPlugin({
|
|
10
|
+
embed,
|
|
11
|
+
}: {
|
|
12
|
+
embed: "tweet" | "youtube-video"
|
|
13
|
+
}) {
|
|
14
|
+
const embedConfig = EmbedConfigs.find(
|
|
15
|
+
(config) => config.type === embed
|
|
16
|
+
) as CustomEmbedConfig
|
|
17
|
+
|
|
18
|
+
return new ComponentPickerOption(`Embed ${embedConfig.contentName}`, {
|
|
19
|
+
icon: embedConfig.icon,
|
|
20
|
+
keywords: [...embedConfig.keywords, "embed"],
|
|
21
|
+
onSelect: (_, editor) =>
|
|
22
|
+
editor.dispatchCommand(INSERT_EMBED_COMMAND, embedConfig.type),
|
|
23
|
+
})
|
|
24
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { $createHeadingNode } from "@lexical/rich-text"
|
|
2
|
+
import { $setBlocksType } from "@lexical/selection"
|
|
3
|
+
import { $getSelection, $isRangeSelection } from "lexical"
|
|
4
|
+
import { Heading1Icon, Heading2Icon, Heading3Icon } from "lucide-react"
|
|
5
|
+
|
|
6
|
+
import { ComponentPickerOption } from "../../plugins/picker/component-picker-option"
|
|
7
|
+
import { IconSize } from "../../ui/typography"
|
|
8
|
+
|
|
9
|
+
export function HeadingPickerPlugin({ n }: { n: 1 | 2 | 3 }) {
|
|
10
|
+
return new ComponentPickerOption(`Heading ${n}`, {
|
|
11
|
+
icon: <HeadingIcons n={n} />,
|
|
12
|
+
keywords: ["heading", "header", `h${n}`],
|
|
13
|
+
onSelect: (_, editor) =>
|
|
14
|
+
editor.update(() => {
|
|
15
|
+
const selection = $getSelection()
|
|
16
|
+
if ($isRangeSelection(selection)) {
|
|
17
|
+
$setBlocksType(selection, () => $createHeadingNode(`h${n}`))
|
|
18
|
+
}
|
|
19
|
+
}),
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function HeadingIcons({ n }: { n: number }) {
|
|
24
|
+
switch (n) {
|
|
25
|
+
case 1:
|
|
26
|
+
return <IconSize size="sm"><Heading1Icon /></IconSize>
|
|
27
|
+
case 2:
|
|
28
|
+
return <IconSize size="sm"><Heading2Icon /></IconSize>
|
|
29
|
+
case 3:
|
|
30
|
+
return <IconSize size="sm"><Heading3Icon /></IconSize>
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ImageIcon } from "lucide-react"
|
|
2
|
+
|
|
3
|
+
import { InsertImageDialog } from "../../plugins/images-plugin"
|
|
4
|
+
import { ComponentPickerOption } from "../../plugins/picker/component-picker-option"
|
|
5
|
+
import { IconSize } from "../../ui/typography"
|
|
6
|
+
|
|
7
|
+
export function ImagePickerPlugin() {
|
|
8
|
+
return new ComponentPickerOption("Image", {
|
|
9
|
+
icon: <IconSize size="sm"><ImageIcon /></IconSize>,
|
|
10
|
+
keywords: ["image", "photo", "picture", "file"],
|
|
11
|
+
onSelect: (_, editor, showModal) =>
|
|
12
|
+
showModal("Insert Image", (onClose) => (
|
|
13
|
+
<InsertImageDialog activeEditor={editor} onClose={onClose} />
|
|
14
|
+
)),
|
|
15
|
+
})
|
|
16
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { INSERT_ORDERED_LIST_COMMAND } from "@lexical/list"
|
|
2
|
+
import { ListOrderedIcon } from "lucide-react"
|
|
3
|
+
|
|
4
|
+
import { ComponentPickerOption } from "../../plugins/picker/component-picker-option"
|
|
5
|
+
import { IconSize } from "../../ui/typography"
|
|
6
|
+
|
|
7
|
+
export function NumberedListPickerPlugin() {
|
|
8
|
+
return new ComponentPickerOption("Numbered List", {
|
|
9
|
+
icon: <IconSize size="sm"><ListOrderedIcon /></IconSize>,
|
|
10
|
+
keywords: ["numbered list", "ordered list", "ol"],
|
|
11
|
+
onSelect: (_, editor) =>
|
|
12
|
+
editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, undefined),
|
|
13
|
+
})
|
|
14
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { $setBlocksType } from "@lexical/selection"
|
|
2
|
+
import { $createParagraphNode, $getSelection, $isRangeSelection } from "lexical"
|
|
3
|
+
import { TextIcon } from "lucide-react"
|
|
4
|
+
|
|
5
|
+
import { ComponentPickerOption } from "../../plugins/picker/component-picker-option"
|
|
6
|
+
import { IconSize } from "../../ui/typography"
|
|
7
|
+
|
|
8
|
+
export function ParagraphPickerPlugin() {
|
|
9
|
+
return new ComponentPickerOption("Paragraph", {
|
|
10
|
+
icon: <IconSize size="sm"><TextIcon /></IconSize>,
|
|
11
|
+
keywords: ["normal", "paragraph", "p", "text"],
|
|
12
|
+
onSelect: (_, editor) =>
|
|
13
|
+
editor.update(() => {
|
|
14
|
+
const selection = $getSelection()
|
|
15
|
+
if ($isRangeSelection(selection)) {
|
|
16
|
+
$setBlocksType(selection, () => $createParagraphNode())
|
|
17
|
+
}
|
|
18
|
+
}),
|
|
19
|
+
})
|
|
20
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { $createQuoteNode } from "@lexical/rich-text"
|
|
2
|
+
import { $setBlocksType } from "@lexical/selection"
|
|
3
|
+
import { $getSelection, $isRangeSelection } from "lexical"
|
|
4
|
+
import { QuoteIcon } from "lucide-react"
|
|
5
|
+
|
|
6
|
+
import { ComponentPickerOption } from "../../plugins/picker/component-picker-option"
|
|
7
|
+
import { IconSize } from "../../ui/typography"
|
|
8
|
+
|
|
9
|
+
export function QuotePickerPlugin() {
|
|
10
|
+
return new ComponentPickerOption("Quote", {
|
|
11
|
+
icon: <IconSize size="sm"><QuoteIcon /></IconSize>,
|
|
12
|
+
keywords: ["block quote"],
|
|
13
|
+
onSelect: (_, editor) =>
|
|
14
|
+
editor.update(() => {
|
|
15
|
+
const selection = $getSelection()
|
|
16
|
+
if ($isRangeSelection(selection)) {
|
|
17
|
+
$setBlocksType(selection, () => $createQuoteNode())
|
|
18
|
+
}
|
|
19
|
+
}),
|
|
20
|
+
})
|
|
21
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { INSERT_TABLE_COMMAND } from "@lexical/table"
|
|
2
|
+
import { TableIcon } from "lucide-react"
|
|
3
|
+
|
|
4
|
+
import { ComponentPickerOption } from "../../plugins/picker/component-picker-option"
|
|
5
|
+
import { InsertTableDialog } from "../../plugins/table-plugin"
|
|
6
|
+
import { IconSize } from "../../ui/typography"
|
|
7
|
+
|
|
8
|
+
export function TablePickerPlugin() {
|
|
9
|
+
return new ComponentPickerOption("Table", {
|
|
10
|
+
icon: <IconSize size="sm"><TableIcon /></IconSize>,
|
|
11
|
+
keywords: ["table", "grid", "spreadsheet", "rows", "columns"],
|
|
12
|
+
onSelect: (_, editor, showModal) =>
|
|
13
|
+
showModal("Insert Table", (onClose) => (
|
|
14
|
+
<InsertTableDialog activeEditor={editor} onClose={onClose} />
|
|
15
|
+
)),
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function DynamicTablePickerPlugin({
|
|
20
|
+
queryString,
|
|
21
|
+
}: {
|
|
22
|
+
queryString: string
|
|
23
|
+
}) {
|
|
24
|
+
const options: Array<ComponentPickerOption> = []
|
|
25
|
+
|
|
26
|
+
if (queryString == null) {
|
|
27
|
+
return options
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const tableMatch = queryString.match(/^([1-9]\d?)(?:x([1-9]\d?)?)?$/)
|
|
31
|
+
|
|
32
|
+
if (tableMatch !== null && tableMatch[1]) {
|
|
33
|
+
const rows = tableMatch[1]
|
|
34
|
+
const colOptions = tableMatch[2]
|
|
35
|
+
? [tableMatch[2]]
|
|
36
|
+
: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(String)
|
|
37
|
+
|
|
38
|
+
options.push(
|
|
39
|
+
...colOptions.map(
|
|
40
|
+
(columns) =>
|
|
41
|
+
new ComponentPickerOption(`${rows}x${columns} Table`, {
|
|
42
|
+
icon: (
|
|
43
|
+
<IconSize size="sm">
|
|
44
|
+
<TableIcon />
|
|
45
|
+
</IconSize>
|
|
46
|
+
),
|
|
47
|
+
keywords: ["table"],
|
|
48
|
+
onSelect: (_, editor) =>
|
|
49
|
+
editor.dispatchCommand(INSERT_TABLE_COMMAND, { columns: columns || "1", rows }),
|
|
50
|
+
})
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return options
|
|
56
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the MIT license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
10
|
+
import { useEffect } from "react"
|
|
11
|
+
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"
|
|
12
|
+
import {
|
|
13
|
+
$getSelection,
|
|
14
|
+
$isRangeSelection,
|
|
15
|
+
$setSelection,
|
|
16
|
+
FOCUS_COMMAND,
|
|
17
|
+
} from "lexical"
|
|
18
|
+
|
|
19
|
+
const COMMAND_PRIORITY_LOW = 1
|
|
20
|
+
const TAB_TO_FOCUS_INTERVAL = 100
|
|
21
|
+
|
|
22
|
+
let lastTabKeyDownTimestamp = 0
|
|
23
|
+
let hasRegisteredKeyDownListener = false
|
|
24
|
+
|
|
25
|
+
function registerKeyTimeStampTracker() {
|
|
26
|
+
window.addEventListener(
|
|
27
|
+
"keydown",
|
|
28
|
+
(event: KeyboardEvent) => {
|
|
29
|
+
// Tab
|
|
30
|
+
if (event.key === "Tab") {
|
|
31
|
+
lastTabKeyDownTimestamp = event.timeStamp
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
true
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function TabFocusPlugin(): null {
|
|
39
|
+
const [editor] = useLexicalComposerContext()
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (!hasRegisteredKeyDownListener) {
|
|
43
|
+
registerKeyTimeStampTracker()
|
|
44
|
+
hasRegisteredKeyDownListener = true
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return editor.registerCommand(
|
|
48
|
+
FOCUS_COMMAND,
|
|
49
|
+
(event: FocusEvent) => {
|
|
50
|
+
const selection = $getSelection()
|
|
51
|
+
if ($isRangeSelection(selection)) {
|
|
52
|
+
if (
|
|
53
|
+
lastTabKeyDownTimestamp + TAB_TO_FOCUS_INTERVAL >
|
|
54
|
+
event.timeStamp
|
|
55
|
+
) {
|
|
56
|
+
$setSelection(selection.clone())
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return false
|
|
60
|
+
},
|
|
61
|
+
COMMAND_PRIORITY_LOW
|
|
62
|
+
)
|
|
63
|
+
}, [editor])
|
|
64
|
+
|
|
65
|
+
return null
|
|
66
|
+
}
|