@nkzw/mdx-editor 0.1.0

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 (148) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +86 -0
  3. package/UPSTREAM.md +21 -0
  4. package/dist/EditorIcon.js +75 -0
  5. package/dist/FormatConstants.js +20 -0
  6. package/dist/MDXEditor.js +189 -0
  7. package/dist/MarkdownEditor.js +281 -0
  8. package/dist/PersistentMarkdownEditor.js +358 -0
  9. package/dist/RealmWithPlugins.js +35 -0
  10. package/dist/core.d.ts +3232 -0
  11. package/dist/core.js +354 -0
  12. package/dist/defaultSvgIcons.js +371 -0
  13. package/dist/directive-editors/AdmonitionDirectiveDescriptor.js +28 -0
  14. package/dist/directive-editors/GenericDirectiveEditor.js +37 -0
  15. package/dist/exportMarkdownFromLexical.js +262 -0
  16. package/dist/horizontalRuleShortcut.js +37 -0
  17. package/dist/importMarkdownToLexical.js +172 -0
  18. package/dist/index.d.ts +86 -0
  19. package/dist/index.js +8 -0
  20. package/dist/jsx-editors/GenericJsxEditor.js +84 -0
  21. package/dist/mdastUtilHtmlComment.js +125 -0
  22. package/dist/persistence.d.ts +128 -0
  23. package/dist/persistence.js +4 -0
  24. package/dist/plugins/codeblock/CodeBlockNode.js +183 -0
  25. package/dist/plugins/codeblock/CodeBlockVisitor.js +14 -0
  26. package/dist/plugins/codeblock/MdastCodeVisitor.js +23 -0
  27. package/dist/plugins/codeblock/findCodeBlockDescriptor.js +8 -0
  28. package/dist/plugins/codeblock/index.js +46 -0
  29. package/dist/plugins/codemirror/CodeMirrorEditor.js +145 -0
  30. package/dist/plugins/codemirror/index.js +115 -0
  31. package/dist/plugins/codemirror/useCodeMirrorRef.js +101 -0
  32. package/dist/plugins/core/GenericHTMLNode.js +118 -0
  33. package/dist/plugins/core/LexicalGenericHTMLNodeVisitor.js +15 -0
  34. package/dist/plugins/core/LexicalLinebreakVisitor.js +10 -0
  35. package/dist/plugins/core/LexicalParagraphVisitor.js +10 -0
  36. package/dist/plugins/core/LexicalRootVisitor.js +10 -0
  37. package/dist/plugins/core/LexicalTextVisitor.js +160 -0
  38. package/dist/plugins/core/MdastBreakVisitor.js +10 -0
  39. package/dist/plugins/core/MdastFormattingVisitor.js +81 -0
  40. package/dist/plugins/core/MdastHTMLNode.js +120 -0
  41. package/dist/plugins/core/MdastHTMLVisitor.js +17 -0
  42. package/dist/plugins/core/MdastParagraphVisitor.js +23 -0
  43. package/dist/plugins/core/MdastRootVisitor.js +9 -0
  44. package/dist/plugins/core/MdastTextVisitor.js +16 -0
  45. package/dist/plugins/core/NestedLexicalEditor.js +221 -0
  46. package/dist/plugins/core/PropertyPopover.js +75 -0
  47. package/dist/plugins/core/SharedHistoryPlugin.js +10 -0
  48. package/dist/plugins/core/index.js +692 -0
  49. package/dist/plugins/core/ui/DownshiftAutoComplete.js +89 -0
  50. package/dist/plugins/core/ui/PopoverUtils.js +22 -0
  51. package/dist/plugins/diff-source/DiffSourceWrapper.js +24 -0
  52. package/dist/plugins/diff-source/DiffViewer.js +84 -0
  53. package/dist/plugins/diff-source/SourceEditor.js +60 -0
  54. package/dist/plugins/diff-source/index.js +27 -0
  55. package/dist/plugins/directives/DirectiveNode.js +107 -0
  56. package/dist/plugins/directives/DirectiveVisitor.js +10 -0
  57. package/dist/plugins/directives/MdastDirectiveVisitor.js +30 -0
  58. package/dist/plugins/directives/index.js +45 -0
  59. package/dist/plugins/frontmatter/FrontmatterEditor.js +137 -0
  60. package/dist/plugins/frontmatter/FrontmatterNode.js +70 -0
  61. package/dist/plugins/frontmatter/LexicalFrontmatterVisitor.js +10 -0
  62. package/dist/plugins/frontmatter/MdastFrontmatterVisitor.js +10 -0
  63. package/dist/plugins/frontmatter/index.js +113 -0
  64. package/dist/plugins/headings/LexicalHeadingVisitor.js +11 -0
  65. package/dist/plugins/headings/MdastHeadingVisitor.js +10 -0
  66. package/dist/plugins/headings/index.js +63 -0
  67. package/dist/plugins/image/EditImageToolbar.js +58 -0
  68. package/dist/plugins/image/ImageDialog.js +132 -0
  69. package/dist/plugins/image/ImageEditor.js +279 -0
  70. package/dist/plugins/image/ImageNode.js +187 -0
  71. package/dist/plugins/image/ImagePlaceholder.js +9 -0
  72. package/dist/plugins/image/ImageResizer.js +223 -0
  73. package/dist/plugins/image/LexicalImageVisitor.js +42 -0
  74. package/dist/plugins/image/MdastImageVisitor.js +91 -0
  75. package/dist/plugins/image/index.js +364 -0
  76. package/dist/plugins/jsx/LexicalJsxNode.js +103 -0
  77. package/dist/plugins/jsx/LexicalJsxVisitor.js +27 -0
  78. package/dist/plugins/jsx/LexicalMdxExpressionNode.js +130 -0
  79. package/dist/plugins/jsx/LexicalMdxExpressionVisitor.js +14 -0
  80. package/dist/plugins/jsx/MdastMdxExpressionVisitor.js +11 -0
  81. package/dist/plugins/jsx/MdastMdxJsEsmVisitor.js +8 -0
  82. package/dist/plugins/jsx/MdastMdxJsxElementVisitor.js +28 -0
  83. package/dist/plugins/jsx/index.js +97 -0
  84. package/dist/plugins/jsx/jsxTagName.js +7 -0
  85. package/dist/plugins/link/AutoLinkPlugin.js +18 -0
  86. package/dist/plugins/link/LexicalLinkVisitor.js +10 -0
  87. package/dist/plugins/link/MdastLinkVisitor.js +14 -0
  88. package/dist/plugins/link/index.js +34 -0
  89. package/dist/plugins/link-dialog/LinkDialog.js +262 -0
  90. package/dist/plugins/link-dialog/index.js +304 -0
  91. package/dist/plugins/lists/CheckListPlugin.js +270 -0
  92. package/dist/plugins/lists/LexicalListItemVisitor.js +41 -0
  93. package/dist/plugins/lists/LexicalListVisitor.js +13 -0
  94. package/dist/plugins/lists/MdastListItemVisitor.js +11 -0
  95. package/dist/plugins/lists/MdastListVisitor.js +19 -0
  96. package/dist/plugins/lists/NotesListItemNode.js +22 -0
  97. package/dist/plugins/lists/index.js +111 -0
  98. package/dist/plugins/markdown-shortcut/index.js +114 -0
  99. package/dist/plugins/maxlength/index.js +36 -0
  100. package/dist/plugins/quote/LexicalQuoteVisitor.js +10 -0
  101. package/dist/plugins/quote/MdastBlockQuoteVisitor.js +10 -0
  102. package/dist/plugins/quote/index.js +18 -0
  103. package/dist/plugins/remote/index.js +52 -0
  104. package/dist/plugins/search/index.js +360 -0
  105. package/dist/plugins/table/LexicalTableVisitor.js +10 -0
  106. package/dist/plugins/table/MdastTableVisitor.js +10 -0
  107. package/dist/plugins/table/TableEditor.js +527 -0
  108. package/dist/plugins/table/TableNode.js +208 -0
  109. package/dist/plugins/table/index.js +66 -0
  110. package/dist/plugins/thematic-break/LexicalThematicBreakVisitor.js +10 -0
  111. package/dist/plugins/thematic-break/MdastThematicBreakVisitor.js +10 -0
  112. package/dist/plugins/thematic-break/index.js +27 -0
  113. package/dist/plugins/toolbar/components/BlockTypeSelect.js +62 -0
  114. package/dist/plugins/toolbar/components/BoldItalicUnderlineToggles.js +98 -0
  115. package/dist/plugins/toolbar/components/ChangeAdmonitionType.js +43 -0
  116. package/dist/plugins/toolbar/components/ChangeCodeMirrorLanguage.js +42 -0
  117. package/dist/plugins/toolbar/components/CodeToggle.js +21 -0
  118. package/dist/plugins/toolbar/components/CreateLink.js +24 -0
  119. package/dist/plugins/toolbar/components/DiffSourceToggleWrapper.js +42 -0
  120. package/dist/plugins/toolbar/components/HighlightToggle.js +28 -0
  121. package/dist/plugins/toolbar/components/InsertAdmonition.js +34 -0
  122. package/dist/plugins/toolbar/components/InsertCodeBlock.js +23 -0
  123. package/dist/plugins/toolbar/components/InsertFrontmatter.js +28 -0
  124. package/dist/plugins/toolbar/components/InsertImage.js +29 -0
  125. package/dist/plugins/toolbar/components/InsertTable.js +25 -0
  126. package/dist/plugins/toolbar/components/InsertThematicBreak.js +23 -0
  127. package/dist/plugins/toolbar/components/KitchenSinkToolbar.js +82 -0
  128. package/dist/plugins/toolbar/components/ListsToggle.js +29 -0
  129. package/dist/plugins/toolbar/components/UndoRedo.js +60 -0
  130. package/dist/plugins/toolbar/index.js +32 -0
  131. package/dist/plugins/toolbar/primitives/DialogButton.js +130 -0
  132. package/dist/plugins/toolbar/primitives/TooltipWrap.js +17 -0
  133. package/dist/plugins/toolbar/primitives/select.js +76 -0
  134. package/dist/plugins/toolbar/primitives/toolbar.js +144 -0
  135. package/dist/registerCodeBoundaryEscape.js +40 -0
  136. package/dist/styles/lexical-theme.module.css.js +62 -0
  137. package/dist/styles/lexicalTheme.js +32 -0
  138. package/dist/styles/ui.module.css.js +296 -0
  139. package/dist/styles.css +2838 -0
  140. package/dist/utils/detectMac.js +16 -0
  141. package/dist/utils/fp.js +44 -0
  142. package/dist/utils/isPartOftheEditorUI.js +12 -0
  143. package/dist/utils/lexicalHelpers.js +185 -0
  144. package/dist/utils/makeHslTransparent.js +6 -0
  145. package/dist/utils/mergeStyleAttributes.js +22 -0
  146. package/dist/utils/uuid4.js +10 -0
  147. package/dist/utils/voidEmitter.js +15 -0
  148. package/package.json +133 -0
@@ -0,0 +1,304 @@
1
+ import { $isLinkNode, $createLinkNode, $isAutoLinkNode, TOGGLE_LINK_COMMAND, LinkNode } from "@lexical/link";
2
+ import { Action, Cell, withLatestFrom, map, Signal, filter } from "@mdxeditor/gurx";
3
+ import { KEY_ESCAPE_COMMAND, COMMAND_PRIORITY_LOW, KEY_DOWN_COMMAND, $getSelection, $isRangeSelection, COMMAND_PRIORITY_HIGH, $getNodeByKey, $createTextNode, $insertNodes, $isTextNode, $getNearestNodeFromDOMNode } from "lexical";
4
+ import { realmPlugin } from "../../RealmWithPlugins.js";
5
+ import { IS_APPLE } from "../../utils/detectMac.js";
6
+ import { getSelectionRectangle, getSelectedNode } from "../../utils/lexicalHelpers.js";
7
+ import { createActiveEditorSubscription$, viewMode$, readOnly$, activeEditor$, currentSelection$, addComposerChild$ } from "../core/index.js";
8
+ import { LinkDialog } from "./LinkDialog.js";
9
+ import { $findMatchingParent } from "@lexical/utils";
10
+ function getLinkNodeInSelection(selection) {
11
+ if (!selection) {
12
+ return null;
13
+ }
14
+ const node = getSelectedNode(selection);
15
+ if (node === null) {
16
+ return null;
17
+ }
18
+ const parent = node.getParent();
19
+ if ($isLinkNode(parent)) {
20
+ return parent;
21
+ } else if ($isLinkNode(node)) {
22
+ return node;
23
+ }
24
+ return null;
25
+ }
26
+ const onWindowChange$ = Signal();
27
+ const linkDialogState$ = Cell({ type: "inactive" }, (r) => {
28
+ r.pub(createActiveEditorSubscription$, (editor) => {
29
+ return editor.registerCommand(
30
+ KEY_ESCAPE_COMMAND,
31
+ () => {
32
+ const state = r.getValue(linkDialogState$);
33
+ if (state.type === "preview") {
34
+ r.pub(linkDialogState$, { type: "inactive" });
35
+ return true;
36
+ }
37
+ return false;
38
+ },
39
+ COMMAND_PRIORITY_LOW
40
+ );
41
+ });
42
+ r.sub(r.pipe(viewMode$), (viewMode) => {
43
+ if (viewMode !== "rich-text") {
44
+ r.pub(linkDialogState$, { type: "inactive" });
45
+ }
46
+ });
47
+ r.pub(createActiveEditorSubscription$, (editor) => {
48
+ return editor.registerCommand(
49
+ KEY_DOWN_COMMAND,
50
+ (event) => {
51
+ if (event.key === "k" && (IS_APPLE ? event.metaKey : event.ctrlKey) && !r.getValue(readOnly$)) {
52
+ const selection = $getSelection();
53
+ if ($isRangeSelection(selection)) {
54
+ r.pub(openLinkEditDialog$);
55
+ event.stopPropagation();
56
+ event.preventDefault();
57
+ return true;
58
+ } else {
59
+ return false;
60
+ }
61
+ }
62
+ return false;
63
+ },
64
+ COMMAND_PRIORITY_HIGH
65
+ );
66
+ });
67
+ r.sub(r.pipe(switchFromPreviewToLinkEdit$, withLatestFrom(linkDialogState$, activeEditor$)), ([, state, editor]) => {
68
+ if (state.type === "preview") {
69
+ setTimeout(() => {
70
+ editor?.getEditorState().read(() => {
71
+ const node = $getNodeByKey(state.linkNodeKey);
72
+ const withAnchorText = $isLinkNode(node) ? node.getTextContent().length > 0 && node.getChildrenSize() <= 1 : false;
73
+ const text = withAnchorText && node ? node.getTextContent() : "";
74
+ r.pub(linkDialogState$, {
75
+ type: "edit",
76
+ initialUrl: state.url,
77
+ url: state.url,
78
+ title: state.title,
79
+ text,
80
+ withAnchorText,
81
+ linkNodeKey: state.linkNodeKey,
82
+ rectangle: state.rectangle
83
+ });
84
+ });
85
+ });
86
+ } else {
87
+ throw new Error("Cannot switch to edit mode when not in preview mode");
88
+ }
89
+ });
90
+ r.sub(r.pipe(updateLink$, withLatestFrom(activeEditor$, linkDialogState$, currentSelection$)), ([payload, editor, state, selection]) => {
91
+ const text = payload.text?.trim() ?? "";
92
+ const url = payload.url?.trim() ?? "";
93
+ const title = payload.title?.trim() ?? "";
94
+ if (url !== "") {
95
+ if (selection?.isCollapsed()) {
96
+ const linkContent = text || title || url;
97
+ editor?.update(
98
+ () => {
99
+ const linkNode = getLinkNodeInSelection(selection);
100
+ if (!linkNode) {
101
+ const node = $createLinkNode(url, { title });
102
+ node.append($createTextNode(linkContent));
103
+ $insertNodes([node]);
104
+ node.select();
105
+ } else {
106
+ if ($isAutoLinkNode(linkNode)) {
107
+ const newLinkNode = $createLinkNode(url, { title });
108
+ newLinkNode.append($createTextNode(text));
109
+ linkNode.replace(newLinkNode);
110
+ newLinkNode.select();
111
+ } else {
112
+ linkNode.setURL(url);
113
+ linkNode.setTitle(title);
114
+ updateLinkText(linkNode.getFirstChild(), text);
115
+ }
116
+ }
117
+ },
118
+ { discrete: true }
119
+ );
120
+ } else {
121
+ editor?.update(() => {
122
+ updateLinkText(selection?.anchor.getNode(), text);
123
+ });
124
+ editor?.dispatchCommand(TOGGLE_LINK_COMMAND, { url, title });
125
+ }
126
+ r.pub(linkDialogState$, {
127
+ type: "preview",
128
+ linkNodeKey: state.linkNodeKey,
129
+ rectangle: state.rectangle,
130
+ title,
131
+ url
132
+ });
133
+ } else {
134
+ if (state.type === "edit" && state.initialUrl !== "") {
135
+ editor?.dispatchCommand(TOGGLE_LINK_COMMAND, null);
136
+ }
137
+ r.pub(linkDialogState$, {
138
+ type: "inactive"
139
+ });
140
+ }
141
+ });
142
+ r.link(
143
+ r.pipe(
144
+ cancelLinkEdit$,
145
+ withLatestFrom(linkDialogState$, activeEditor$),
146
+ map(([, state, editor]) => {
147
+ if (state.type === "edit") {
148
+ editor?.focus();
149
+ if (state.initialUrl === "") {
150
+ return {
151
+ type: "inactive"
152
+ };
153
+ } else {
154
+ return {
155
+ type: "preview",
156
+ url: state.initialUrl,
157
+ linkNodeKey: state.linkNodeKey,
158
+ rectangle: state.rectangle
159
+ };
160
+ }
161
+ } else {
162
+ throw new Error("Cannot cancel edit when not in edit mode");
163
+ }
164
+ })
165
+ ),
166
+ linkDialogState$
167
+ );
168
+ r.link(
169
+ r.pipe(
170
+ r.combine(currentSelection$, onWindowChange$),
171
+ withLatestFrom(activeEditor$, linkDialogState$, readOnly$),
172
+ map(([[selection], activeEditor, _, readOnly]) => {
173
+ if ($isRangeSelection(selection) && activeEditor && !readOnly) {
174
+ const node = getLinkNodeInSelection(selection);
175
+ if (!selection.isCollapsed()) return { type: "inactive" };
176
+ if (node) {
177
+ const rect = getSelectionRectangle(activeEditor);
178
+ if (!rect) {
179
+ return { type: "inactive" };
180
+ }
181
+ return {
182
+ type: "preview",
183
+ url: node.getURL(),
184
+ linkNodeKey: node.getKey(),
185
+ title: node.getTitle(),
186
+ rectangle: rect
187
+ };
188
+ } else {
189
+ return { type: "inactive" };
190
+ }
191
+ } else {
192
+ return { type: "inactive" };
193
+ }
194
+ })
195
+ ),
196
+ linkDialogState$
197
+ );
198
+ });
199
+ const updateLink$ = Signal();
200
+ const cancelLinkEdit$ = Action();
201
+ const applyLinkChanges$ = Action();
202
+ const switchFromPreviewToLinkEdit$ = Action();
203
+ const removeLink$ = Action((r) => {
204
+ r.sub(r.pipe(removeLink$, withLatestFrom(activeEditor$)), ([, editor]) => {
205
+ editor?.dispatchCommand(TOGGLE_LINK_COMMAND, null);
206
+ });
207
+ });
208
+ const openLinkEditDialog$ = Action((r) => {
209
+ r.sub(
210
+ r.pipe(
211
+ openLinkEditDialog$,
212
+ withLatestFrom(currentSelection$, activeEditor$),
213
+ filter(([, selection]) => $isRangeSelection(selection))
214
+ ),
215
+ ([, selection, editor]) => {
216
+ editor?.focus(() => {
217
+ setTimeout(() => {
218
+ editor.getEditorState().read(() => {
219
+ const linkNode = getLinkNodeInSelection(selection);
220
+ const rectangle = getSelectionRectangle(editor);
221
+ const initialUrl = linkNode?.getURL() ?? "";
222
+ const url = linkNode?.getURL() ?? "";
223
+ const title = linkNode?.getTitle() ?? "";
224
+ const linkNodeKey = linkNode?.getKey() ?? "";
225
+ const withAnchorText = linkNode ? linkNode.getTextContent().length > 0 && linkNode.getChildrenSize() <= 1 : Boolean(selection?.isCollapsed());
226
+ const text = withAnchorText && linkNode ? linkNode.getTextContent() : "";
227
+ r.pub(linkDialogState$, {
228
+ type: "edit",
229
+ initialUrl,
230
+ url,
231
+ title,
232
+ text,
233
+ withAnchorText,
234
+ linkNodeKey,
235
+ rectangle
236
+ });
237
+ });
238
+ });
239
+ });
240
+ }
241
+ );
242
+ });
243
+ const linkAutocompleteSuggestions$ = Cell([]);
244
+ const onClickLinkCallback$ = Cell(null);
245
+ const onReadOnlyClickLinkCallback$ = Cell(null, (r) => {
246
+ r.pub(createActiveEditorSubscription$, (editor) => {
247
+ function onClick(event) {
248
+ const [readOnly, callback] = r.getValues([readOnly$, onReadOnlyClickLinkCallback$]);
249
+ if (!readOnly || callback === null) {
250
+ return;
251
+ }
252
+ editor.update(() => {
253
+ const nearestNode = $getNearestNodeFromDOMNode(event.target);
254
+ if (nearestNode !== null) {
255
+ const targetNode = $findMatchingParent(nearestNode, (node) => node instanceof LinkNode);
256
+ if (targetNode !== null) {
257
+ callback(event, targetNode, targetNode.getURL());
258
+ }
259
+ }
260
+ });
261
+ }
262
+ return editor.registerRootListener((rootElement, prevRoot) => {
263
+ if (rootElement) {
264
+ rootElement.addEventListener("click", onClick);
265
+ }
266
+ if (prevRoot) {
267
+ prevRoot.removeEventListener("click", onClick);
268
+ }
269
+ });
270
+ });
271
+ });
272
+ function updateLinkText(node, text) {
273
+ if ($isTextNode(node) && text) {
274
+ node.setTextContent(text);
275
+ node.selectStart();
276
+ }
277
+ }
278
+ const showLinkTitleField$ = Cell(true);
279
+ const linkDialogPlugin = realmPlugin({
280
+ init(r, params) {
281
+ r.pub(addComposerChild$, params?.LinkDialog ?? LinkDialog);
282
+ r.pub(onClickLinkCallback$, params?.onClickLinkCallback ?? null);
283
+ r.pub(onReadOnlyClickLinkCallback$, params?.onReadOnlyClickLinkCallback ?? null);
284
+ r.pub(showLinkTitleField$, params?.showLinkTitleField ?? true);
285
+ },
286
+ update(r, params = {}) {
287
+ r.pub(linkAutocompleteSuggestions$, params.linkAutocompleteSuggestions ?? []);
288
+ }
289
+ });
290
+ export {
291
+ applyLinkChanges$,
292
+ cancelLinkEdit$,
293
+ linkAutocompleteSuggestions$,
294
+ linkDialogPlugin,
295
+ linkDialogState$,
296
+ onClickLinkCallback$,
297
+ onReadOnlyClickLinkCallback$,
298
+ onWindowChange$,
299
+ openLinkEditDialog$,
300
+ removeLink$,
301
+ showLinkTitleField$,
302
+ switchFromPreviewToLinkEdit$,
303
+ updateLink$
304
+ };
@@ -0,0 +1,270 @@
1
+ import { INSERT_CHECK_LIST_COMMAND, $insertList, $isListItemNode, $isListNode } from "@lexical/list";
2
+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
3
+ import { mergeRegister, $findMatchingParent, isHTMLElement, calculateZoomLevel } from "@lexical/utils";
4
+ import { COMMAND_PRIORITY_LOW, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_UP_COMMAND, KEY_ESCAPE_COMMAND, KEY_SPACE_COMMAND, $getNearestNodeFromDOMNode, KEY_ARROW_LEFT_COMMAND, $getSelection, $isRangeSelection, $isElementNode, getNearestEditorFromDOMNode, $addUpdateTag, SKIP_SELECTION_FOCUS_TAG, SKIP_DOM_SELECTION_TAG } from "lexical";
5
+ import { useEffect } from "react";
6
+ const TOUCH_CLICK_DEDUP_WINDOW_MS = 500;
7
+ const lastTouchToggleByTarget = /* @__PURE__ */ new WeakMap();
8
+ function CheckListPlugin({
9
+ disableTakeFocusOnClick = false
10
+ }) {
11
+ const [editor] = useLexicalComposerContext();
12
+ useEffect(
13
+ () => registerCheckList(editor, disableTakeFocusOnClick),
14
+ [editor, disableTakeFocusOnClick]
15
+ );
16
+ return null;
17
+ }
18
+ function registerCheckList(editor, disableTakeFocusOnClick) {
19
+ const handleMouseClick = (event) => {
20
+ const target = event.target;
21
+ if (isHTMLElement(target)) {
22
+ const lastTouchToggle = lastTouchToggleByTarget.get(target);
23
+ lastTouchToggleByTarget.delete(target);
24
+ if (lastTouchToggle !== void 0 && performance.now() - lastTouchToggle < TOUCH_CLICK_DEDUP_WINDOW_MS) {
25
+ return;
26
+ }
27
+ }
28
+ handleClick(event, disableTakeFocusOnClick);
29
+ };
30
+ const handlePointerUp = (event) => {
31
+ if (event.pointerType !== "touch") {
32
+ return;
33
+ }
34
+ if (handleClick(event, disableTakeFocusOnClick) && isHTMLElement(event.target)) {
35
+ lastTouchToggleByTarget.set(event.target, performance.now());
36
+ }
37
+ };
38
+ const handleSelect = (event) => {
39
+ handleSelectDefaults(event, disableTakeFocusOnClick);
40
+ };
41
+ return mergeRegister(
42
+ editor.registerCommand(
43
+ INSERT_CHECK_LIST_COMMAND,
44
+ () => {
45
+ $insertList("check");
46
+ return true;
47
+ },
48
+ COMMAND_PRIORITY_LOW
49
+ ),
50
+ editor.registerCommand(
51
+ KEY_ARROW_DOWN_COMMAND,
52
+ (event) => handleArrowUpOrDown(event, editor, false),
53
+ COMMAND_PRIORITY_LOW
54
+ ),
55
+ editor.registerCommand(
56
+ KEY_ARROW_UP_COMMAND,
57
+ (event) => handleArrowUpOrDown(event, editor, true),
58
+ COMMAND_PRIORITY_LOW
59
+ ),
60
+ editor.registerCommand(
61
+ KEY_ESCAPE_COMMAND,
62
+ () => {
63
+ const activeItem = getActiveCheckListItem();
64
+ if (activeItem === null) {
65
+ return false;
66
+ }
67
+ editor.getRootElement()?.focus();
68
+ return true;
69
+ },
70
+ COMMAND_PRIORITY_LOW
71
+ ),
72
+ editor.registerCommand(
73
+ KEY_SPACE_COMMAND,
74
+ (event) => {
75
+ const activeItem = getActiveCheckListItem();
76
+ if (activeItem === null || !editor.isEditable()) {
77
+ return false;
78
+ }
79
+ editor.update(() => {
80
+ const listItemNode = $getNearestNodeFromDOMNode(activeItem);
81
+ if ($isListItemNode(listItemNode)) {
82
+ event.preventDefault();
83
+ listItemNode.toggleChecked();
84
+ }
85
+ });
86
+ return true;
87
+ },
88
+ COMMAND_PRIORITY_LOW
89
+ ),
90
+ editor.registerCommand(
91
+ KEY_ARROW_LEFT_COMMAND,
92
+ (event) => editor.getEditorState().read(() => {
93
+ const selection = $getSelection();
94
+ if (!$isRangeSelection(selection) || !selection.isCollapsed()) {
95
+ return false;
96
+ }
97
+ const { anchor } = selection;
98
+ const isElement = anchor.type === "element";
99
+ if (!isElement && anchor.offset !== 0) {
100
+ return false;
101
+ }
102
+ const anchorNode = anchor.getNode();
103
+ const elementNode = $findMatchingParent(
104
+ anchorNode,
105
+ (node) => $isElementNode(node) && !node.isInline()
106
+ );
107
+ if (!$isListItemNode(elementNode)) {
108
+ return false;
109
+ }
110
+ const parent = elementNode.getParent();
111
+ if (!$isListNode(parent) || parent.getListType() !== "check" || !isElement && elementNode.getFirstDescendant() !== anchorNode) {
112
+ return false;
113
+ }
114
+ const domNode = editor.getElementByKey(elementNode.getKey());
115
+ if (domNode === null || document.activeElement === domNode) {
116
+ return false;
117
+ }
118
+ domNode.focus();
119
+ event.preventDefault();
120
+ return true;
121
+ }),
122
+ COMMAND_PRIORITY_LOW
123
+ ),
124
+ editor.registerRootListener((rootElement) => {
125
+ if (rootElement === null) {
126
+ return;
127
+ }
128
+ rootElement.addEventListener("click", handleMouseClick);
129
+ rootElement.addEventListener("pointerup", handlePointerUp);
130
+ rootElement.addEventListener("pointerdown", handleSelect, {
131
+ capture: true
132
+ });
133
+ rootElement.addEventListener("mousedown", handleSelect, {
134
+ capture: true
135
+ });
136
+ rootElement.addEventListener("touchstart", handleSelect, {
137
+ capture: true,
138
+ passive: false
139
+ });
140
+ return () => {
141
+ rootElement.removeEventListener("click", handleMouseClick);
142
+ rootElement.removeEventListener("pointerup", handlePointerUp);
143
+ rootElement.removeEventListener("pointerdown", handleSelect, {
144
+ capture: true
145
+ });
146
+ rootElement.removeEventListener("mousedown", handleSelect, {
147
+ capture: true
148
+ });
149
+ rootElement.removeEventListener("touchstart", handleSelect, {
150
+ capture: true
151
+ });
152
+ };
153
+ })
154
+ );
155
+ }
156
+ function handleCheckItemEvent(event, callback) {
157
+ const target = event.target;
158
+ if (!isHTMLElement(target)) {
159
+ return false;
160
+ }
161
+ const firstChild = target.firstChild;
162
+ if (isHTMLElement(firstChild) && (firstChild.tagName === "UL" || firstChild.tagName === "OL")) {
163
+ return false;
164
+ }
165
+ const parentNode = target.parentNode;
166
+ if (!parentNode || parentNode.__lexicalListType !== "check") {
167
+ return false;
168
+ }
169
+ let clientX = null;
170
+ let pointerType = null;
171
+ if ("clientX" in event) {
172
+ clientX = event.clientX;
173
+ } else if ("touches" in event && event.touches.length > 0) {
174
+ clientX = event.touches[0].clientX;
175
+ pointerType = "touch";
176
+ }
177
+ if (clientX === null) {
178
+ return false;
179
+ }
180
+ const rect = target.getBoundingClientRect();
181
+ const clientXInPixels = clientX / calculateZoomLevel(target);
182
+ const beforeStyles = window.getComputedStyle ? window.getComputedStyle(target, "::before") : { width: "0px" };
183
+ const beforeWidthInPixels = parseFloat(beforeStyles.width);
184
+ const isTouchEvent = pointerType === "touch" || "pointerType" in event && event.pointerType === "touch";
185
+ const clickAreaPadding = isTouchEvent ? 32 : 0;
186
+ const isMarkerHit = target.dir === "rtl" ? clientXInPixels < rect.right + clickAreaPadding && clientXInPixels > rect.right - beforeWidthInPixels - clickAreaPadding : clientXInPixels > rect.left - clickAreaPadding && clientXInPixels < rect.left + beforeWidthInPixels + clickAreaPadding;
187
+ if (!isMarkerHit) {
188
+ return false;
189
+ }
190
+ return callback() !== false;
191
+ }
192
+ function handleClick(event, disableFocusOnClick) {
193
+ return handleCheckItemEvent(event, () => {
194
+ if (!isHTMLElement(event.target)) {
195
+ return false;
196
+ }
197
+ const domNode = event.target;
198
+ const editor = getNearestEditorFromDOMNode(domNode);
199
+ if (editor === null || !editor.isEditable()) {
200
+ return false;
201
+ }
202
+ editor.update(() => {
203
+ const node = $getNearestNodeFromDOMNode(domNode);
204
+ if ($isListItemNode(node)) {
205
+ if (disableFocusOnClick) {
206
+ $addUpdateTag(SKIP_SELECTION_FOCUS_TAG);
207
+ $addUpdateTag(SKIP_DOM_SELECTION_TAG);
208
+ } else {
209
+ domNode.focus();
210
+ }
211
+ node.toggleChecked();
212
+ }
213
+ });
214
+ return true;
215
+ });
216
+ }
217
+ function handleSelectDefaults(event, disableTakeFocusOnClick) {
218
+ handleCheckItemEvent(event, () => {
219
+ event.preventDefault();
220
+ if (disableTakeFocusOnClick) {
221
+ event.stopPropagation();
222
+ }
223
+ });
224
+ }
225
+ function getActiveCheckListItem() {
226
+ const activeElement = document.activeElement;
227
+ return isHTMLElement(activeElement) && activeElement.tagName === "LI" && activeElement.parentNode !== null && activeElement.parentNode.__lexicalListType === "check" ? activeElement : null;
228
+ }
229
+ function findCheckListItemSibling(node, backward) {
230
+ let sibling = backward ? node.getPreviousSibling() : node.getNextSibling();
231
+ let parent = node;
232
+ while (sibling === null && $isListItemNode(parent)) {
233
+ parent = parent.getParentOrThrow().getParent();
234
+ if (parent !== null) {
235
+ sibling = backward ? parent.getPreviousSibling() : parent.getNextSibling();
236
+ }
237
+ }
238
+ while ($isListItemNode(sibling)) {
239
+ const firstChild = backward ? sibling.getLastChild() : sibling.getFirstChild();
240
+ if (!$isListNode(firstChild)) {
241
+ return sibling;
242
+ }
243
+ sibling = backward ? firstChild.getLastChild() : firstChild.getFirstChild();
244
+ }
245
+ return null;
246
+ }
247
+ function handleArrowUpOrDown(event, editor, backward) {
248
+ const activeItem = getActiveCheckListItem();
249
+ if (activeItem !== null) {
250
+ editor.update(() => {
251
+ const listItem = $getNearestNodeFromDOMNode(activeItem);
252
+ if (!$isListItemNode(listItem)) {
253
+ return;
254
+ }
255
+ const nextListItem = findCheckListItemSibling(listItem, backward);
256
+ if (nextListItem !== null) {
257
+ nextListItem.selectStart();
258
+ const dom = editor.getElementByKey(nextListItem.getKey());
259
+ if (dom !== null) {
260
+ event.preventDefault();
261
+ setTimeout(() => dom.focus(), 0);
262
+ }
263
+ }
264
+ });
265
+ }
266
+ return false;
267
+ }
268
+ export {
269
+ CheckListPlugin
270
+ };
@@ -0,0 +1,41 @@
1
+ import { $isListItemNode, $isListNode } from "@lexical/list";
2
+ import { $isTextNode, $isLineBreakNode, $isElementNode, $isDecoratorNode } from "lexical";
3
+ const LexicalListItemVisitor = {
4
+ testLexicalNode: $isListItemNode,
5
+ visitLexicalNode: ({ lexicalNode, mdastParent, actions }) => {
6
+ const children = lexicalNode.getChildren();
7
+ const firstChild = children[0];
8
+ if (children.length === 1 && $isListNode(firstChild)) {
9
+ const prevListItemNode = mdastParent.children.at(-1);
10
+ if (!prevListItemNode) {
11
+ actions.visitChildren(firstChild, mdastParent);
12
+ } else {
13
+ actions.visitChildren(lexicalNode, prevListItemNode);
14
+ }
15
+ } else {
16
+ const parentList = lexicalNode.getParent();
17
+ const listItem = actions.appendToParent(mdastParent, {
18
+ type: "listItem",
19
+ checked: parentList.getListType() === "check" ? Boolean(lexicalNode.getChecked()) : void 0,
20
+ spread: false,
21
+ children: []
22
+ });
23
+ let surroundingParagraph = null;
24
+ for (const child of lexicalNode.getChildren()) {
25
+ if ($isTextNode(child) || $isLineBreakNode(child) || child.isInline() && ($isElementNode(child) || $isDecoratorNode(child))) {
26
+ surroundingParagraph ??= actions.appendToParent(listItem, {
27
+ type: "paragraph",
28
+ children: []
29
+ });
30
+ actions.visit(child, surroundingParagraph);
31
+ } else {
32
+ surroundingParagraph = null;
33
+ actions.visit(child, listItem);
34
+ }
35
+ }
36
+ }
37
+ }
38
+ };
39
+ export {
40
+ LexicalListItemVisitor
41
+ };
@@ -0,0 +1,13 @@
1
+ import { $isListNode } from "@lexical/list";
2
+ const LexicalListVisitor = {
3
+ testLexicalNode: $isListNode,
4
+ visitLexicalNode: ({ lexicalNode, actions }) => {
5
+ actions.addAndStepInto("list", {
6
+ ordered: lexicalNode.getListType() === "number",
7
+ spread: false
8
+ });
9
+ }
10
+ };
11
+ export {
12
+ LexicalListVisitor
13
+ };
@@ -0,0 +1,11 @@
1
+ import { $createListItemNode } from "@lexical/list";
2
+ const MdastListItemVisitor = {
3
+ testNode: "listItem",
4
+ visitNode({ mdastNode, actions, lexicalParent }) {
5
+ const isChecked = lexicalParent.getListType() === "check" ? mdastNode.checked ?? false : void 0;
6
+ actions.addAndStepInto($createListItemNode(isChecked));
7
+ }
8
+ };
9
+ export {
10
+ MdastListItemVisitor
11
+ };
@@ -0,0 +1,19 @@
1
+ import { $createListNode, $isListItemNode, $createListItemNode } from "@lexical/list";
2
+ const MdastListVisitor = {
3
+ testNode: "list",
4
+ visitNode: function({ mdastNode, lexicalParent, actions }) {
5
+ const listType = mdastNode.children.some((e) => typeof e.checked === "boolean") ? "check" : mdastNode.ordered ? "number" : "bullet";
6
+ const lexicalNode = $createListNode(listType);
7
+ if ($isListItemNode(lexicalParent)) {
8
+ const dedicatedParent = $createListItemNode();
9
+ dedicatedParent.append(lexicalNode);
10
+ lexicalParent.insertAfter(dedicatedParent);
11
+ } else {
12
+ lexicalParent.append(lexicalNode);
13
+ }
14
+ actions.visitChildren(mdastNode, lexicalNode);
15
+ }
16
+ };
17
+ export {
18
+ MdastListVisitor
19
+ };
@@ -0,0 +1,22 @@
1
+ import { ListItemNode, $isListNode } from "@lexical/list";
2
+ import { $isParagraphNode } from "lexical";
3
+ class NotesListItemNode extends ListItemNode {
4
+ $config() {
5
+ return this.config("notes-listitem", { extends: ListItemNode });
6
+ }
7
+ collapseAtStart(selection) {
8
+ const list = this.getParent();
9
+ const shouldRestoreSelection = this.isEmpty() && $isListNode(list) && list.getChildrenSize() > 1;
10
+ const result = super.collapseAtStart(selection);
11
+ if (shouldRestoreSelection) {
12
+ const paragraph = list.getPreviousSibling();
13
+ if ($isParagraphNode(paragraph)) {
14
+ paragraph.selectStart();
15
+ }
16
+ }
17
+ return result;
18
+ }
19
+ }
20
+ export {
21
+ NotesListItemNode
22
+ };