@mdxeditor/editor 0.14.1 → 0.15.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.
package/dist/MDXEditor.js CHANGED
@@ -61,9 +61,16 @@ const EditorRootElement = ({ children, className }) => {
61
61
  const editorRootElementRef = React__default.useRef(null);
62
62
  const setEditorRootElementRef = corePluginHooks.usePublisher("editorRootElementRef");
63
63
  React__default.useEffect(() => {
64
+ const popupContainer = document.createElement("div");
65
+ popupContainer.classList.add(styles.editorRoot);
66
+ document.body.appendChild(popupContainer);
67
+ editorRootElementRef.current = popupContainer;
64
68
  setEditorRootElementRef(editorRootElementRef);
69
+ return () => {
70
+ document.body.removeChild(popupContainer);
71
+ };
65
72
  }, [editorRootElementRef, setEditorRootElementRef]);
66
- return /* @__PURE__ */ React__default.createElement("div", { className: classNames(styles.editorRoot, styles.editorWrapper, className, "mdxeditor"), ref: editorRootElementRef }, children);
73
+ return /* @__PURE__ */ React__default.createElement("div", { className: classNames(styles.editorRoot, styles.editorWrapper, className, "mdxeditor") }, children);
67
74
  };
68
75
  const Methods = ({ mdxRef }) => {
69
76
  const realm = corePluginHooks.useRealmContext();
@@ -16,6 +16,7 @@ const AdmonitionDirectiveDescriptor = {
16
16
  return /* @__PURE__ */ React__default.createElement("div", { className: theme.admonition[mdastNode.name] }, /* @__PURE__ */ React__default.createElement(
17
17
  NestedLexicalEditor,
18
18
  {
19
+ block: true,
19
20
  focusEmitter,
20
21
  getContent: (node) => node.children,
21
22
  getUpdatedMdastNode: (mdastNode2, children) => ({ ...mdastNode2, children })
@@ -2,7 +2,7 @@ import { LexicalNode, RootNode as LexicalRootNode } from 'lexical';
2
2
  import * as Mdast from 'mdast';
3
3
  import { fromMarkdown } from 'mdast-util-from-markdown';
4
4
  import { ParseOptions } from 'micromark-util-types';
5
- import { IS_BOLD, IS_ITALIC, IS_UNDERLINE } from './FormatConstants';
5
+ import { IS_BOLD, IS_CODE, IS_ITALIC, IS_UNDERLINE } from './FormatConstants';
6
6
  /** @internal */
7
7
  export type MdastExtensions = NonNullable<Parameters<typeof fromMarkdown>[1]>['mdastExtensions'];
8
8
  /**
@@ -21,12 +21,12 @@ export interface MdastVisitActions {
21
21
  * Adds formatting as a context for the current node and its children.
22
22
  * This is necessary due to mdast treating formatting as a node, while lexical considering it an attribute of a node.
23
23
  */
24
- addFormatting(format: typeof IS_BOLD | typeof IS_ITALIC | typeof IS_UNDERLINE, node?: Mdast.Content): void;
24
+ addFormatting(format: typeof IS_BOLD | typeof IS_ITALIC | typeof IS_UNDERLINE | typeof IS_CODE, node?: Mdast.Content): void;
25
25
  /**
26
26
  * Adds formatting as a context for the current node and its children.
27
27
  * This is necessary due to mdast treating formatting as a node, while lexical considering it an attribute of a node.
28
28
  */
29
- removeFormatting(format: typeof IS_BOLD | typeof IS_ITALIC | typeof IS_UNDERLINE, node?: Mdast.Content): void;
29
+ removeFormatting(format: typeof IS_BOLD | typeof IS_ITALIC | typeof IS_UNDERLINE | typeof IS_CODE, node?: Mdast.Content): void;
30
30
  /**
31
31
  * Access the current formatting context.
32
32
  */
@@ -1,8 +1,24 @@
1
1
  import { $createTextNode } from "lexical";
2
2
  import { IS_CODE } from "../../FormatConstants.js";
3
+ function isOpeningCodeNode(node) {
4
+ return node.type === "html" && node.value === "<code>";
5
+ }
6
+ function isClosingCodeNode(node) {
7
+ return node.type === "html" && node.value === "</code>";
8
+ }
3
9
  const MdastInlineCodeVisitor = {
4
- testNode: "inlineCode",
5
- visitNode({ mdastNode, actions }) {
10
+ testNode: (node) => {
11
+ return node.type === "inlineCode" || isOpeningCodeNode(node) || isClosingCodeNode(node);
12
+ },
13
+ visitNode({ mdastNode, actions, mdastParent }) {
14
+ if (isOpeningCodeNode(mdastNode)) {
15
+ actions.addFormatting(IS_CODE, mdastParent);
16
+ return;
17
+ }
18
+ if (isClosingCodeNode(mdastNode)) {
19
+ actions.removeFormatting(IS_CODE, mdastParent);
20
+ return;
21
+ }
6
22
  actions.addAndStepInto($createTextNode(mdastNode.value).setFormat(IS_CODE));
7
23
  }
8
24
  };
@@ -2,7 +2,7 @@ import { createEmptyHistoryState } from "@lexical/react/LexicalHistoryPlugin.js"
2
2
  import { $isHeadingNode } from "@lexical/rich-text";
3
3
  import { $setBlocksType } from "@lexical/selection";
4
4
  import { $findMatchingParent, $wrapNodeInElement, $insertNodeToNearestRoot } from "@lexical/utils";
5
- import { createCommand, SELECTION_CHANGE_COMMAND, $getRoot, COMMAND_PRIORITY_CRITICAL, BLUR_COMMAND, FORMAT_TEXT_COMMAND, $isRootOrShadowRoot, $getSelection, $isRangeSelection, $insertNodes, $createParagraphNode, ParagraphNode, TextNode } from "lexical";
5
+ import { createCommand, SELECTION_CHANGE_COMMAND, $getRoot, COMMAND_PRIORITY_CRITICAL, BLUR_COMMAND, KEY_DOWN_COMMAND, COMMAND_PRIORITY_LOW, FORMAT_TEXT_COMMAND, $isRootOrShadowRoot, $getSelection, $isRangeSelection, $insertNodes, $createParagraphNode, ParagraphNode, TextNode } from "lexical";
6
6
  import { exportMarkdownFromLexical } from "../../exportMarkdownFromLexical.js";
7
7
  import { importMarkdownToLexical } from "../../importMarkdownToLexical.js";
8
8
  import { LexicalLinebreakVisitor } from "./LexicalLinebreakVisitor.js";
@@ -16,6 +16,7 @@ import { MdastRootVisitor } from "./MdastRootVisitor.js";
16
16
  import { MdastTextVisitor } from "./MdastTextVisitor.js";
17
17
  import { SharedHistoryPlugin } from "./SharedHistoryPlugin.js";
18
18
  import { noop } from "../../utils/fp.js";
19
+ import { controlOrMeta } from "../../utils/detectMac.js";
19
20
  import { system } from "../../gurx/realmFactory.js";
20
21
  import { realmPlugin } from "../../gurx/react.js";
21
22
  const NESTED_EDITOR_UPDATED_COMMAND = createCommand("NESTED_EDITOR_UPDATED_COMMAND");
@@ -185,6 +186,30 @@ const coreSystem = system((r) => {
185
186
  COMMAND_PRIORITY_CRITICAL
186
187
  );
187
188
  });
189
+ r.pub(createRootEditorSubscription, (theRootEditor) => {
190
+ return theRootEditor.registerCommand(
191
+ KEY_DOWN_COMMAND,
192
+ (event) => {
193
+ const { keyCode, ctrlKey, metaKey } = event;
194
+ if (keyCode === 65 && controlOrMeta(metaKey, ctrlKey)) {
195
+ event.preventDefault();
196
+ theRootEditor.update(() => {
197
+ var _a;
198
+ const root = $getRoot();
199
+ const skipFirstChild = ((_a = root.getFirstChild()) == null ? void 0 : _a.getType()) === "frontmatter";
200
+ root.select(skipFirstChild ? 1 : 0, root.getChildrenSize());
201
+ const rootElement = theRootEditor.getRootElement();
202
+ rootElement.focus({
203
+ preventScroll: true
204
+ });
205
+ });
206
+ return true;
207
+ }
208
+ return false;
209
+ },
210
+ COMMAND_PRIORITY_LOW
211
+ );
212
+ });
188
213
  const composerChildren = r.node([]);
189
214
  const addComposerChild = createAppendNodeFor(composerChildren);
190
215
  const topAreaChildren = r.node([]);
@@ -64,7 +64,57 @@ export declare const headingsSystem: SystemSpec<[SystemSpec<[], (r: Realm) => {
64
64
  applyBlockType: RealmNode<BlockType>;
65
65
  convertSelectionToNode: RealmNode<() => ElementNode>;
66
66
  insertDecoratorNode: RealmNode<() => DecoratorNode<unknown>>;
67
- }>], (_: Realm) => {}>;
67
+ }>], (r: Realm, [{ createRootEditorSubscription, convertSelectionToNode }]: [{
68
+ activeEditor: RealmNode<LexicalEditor | null>;
69
+ inFocus: RealmNode<boolean>;
70
+ historyState: RealmNode<HistoryState>;
71
+ currentSelection: RealmNode<RangeSelection | null>;
72
+ jsxIsAvailable: RealmNode<boolean>;
73
+ jsxComponentDescriptors: RealmNode<JsxComponentDescriptor[]>;
74
+ initialRootEditorState: RealmNode<InitialEditorStateType>;
75
+ rootEditor: RealmNode<LexicalEditor | null>;
76
+ createRootEditorSubscription: RealmNode<EditorSubscription>;
77
+ createActiveEditorSubscription: RealmNode<EditorSubscription>;
78
+ importVisitors: RealmNode<MdastImportVisitor<Content>[]>;
79
+ syntaxExtensions: RealmNode<Extension[]>;
80
+ mdastExtensions: RealmNode<(Partial<Config> | Partial<Config>[])[] | null | undefined>;
81
+ usedLexicalNodes: RealmNode<Klass<LexicalNode>[]>;
82
+ addImportVisitor: RealmNode<MdastImportVisitor<Content>>;
83
+ addLexicalNode: RealmNode<Klass<LexicalNode>>;
84
+ addSyntaxExtension: RealmNode<Extension>;
85
+ addMdastExtension: RealmNode<Partial<Config> | Partial<Config>[]>;
86
+ toMarkdownExtensions: RealmNode<Options[]>;
87
+ toMarkdownOptions: RealmNode<Options>;
88
+ addToMarkdownExtension: RealmNode<Options>;
89
+ addExportVisitor: RealmNode<LexicalVisitor>;
90
+ exportVisitors: RealmNode<LexicalVisitor[]>;
91
+ initialMarkdown: RealmNode<string>;
92
+ setMarkdown: RealmNode<string>;
93
+ markdown: RealmNode<string>;
94
+ editorRootElementRef: RealmNode<RefObject<HTMLDivElement> | null>;
95
+ contentEditableClassName: RealmNode<string>;
96
+ placeholder: RealmNode<ReactNode>;
97
+ autoFocus: RealmNode<boolean>;
98
+ composerChildren: RealmNode<ComponentType<{}>[]>;
99
+ addComposerChild: RealmNode<ComponentType<{}>>;
100
+ topAreaChildren: RealmNode<ComponentType<{}>[]>;
101
+ addTopAreaChild: RealmNode<ComponentType<{}>>;
102
+ nestedEditorChildren: RealmNode<ComponentType<{}>[]>;
103
+ addNestedEditorChild: RealmNode<ComponentType<{}>>;
104
+ editorWrappers: RealmNode<ComponentType<{
105
+ children: ReactNode;
106
+ }>[]>;
107
+ addEditorWrapper: RealmNode<ComponentType<{
108
+ children: ReactNode;
109
+ }>>;
110
+ currentFormat: RealmNode<number>;
111
+ editorInFocus: RealmNode<EditorInFocus | null>;
112
+ applyFormat: RealmNode<TextFormatType>;
113
+ currentBlockType: RealmNode<BlockType>;
114
+ applyBlockType: RealmNode<BlockType>;
115
+ convertSelectionToNode: RealmNode<() => ElementNode>;
116
+ insertDecoratorNode: RealmNode<() => DecoratorNode<unknown>>;
117
+ }]) => {}>;
68
118
  /**
69
119
  * @internal
70
120
  */
@@ -118,4 +168,54 @@ export declare const headingsPlugin: PluginConstructor<SystemSpec<[SystemSpec<[]
118
168
  applyBlockType: RealmNode<BlockType>;
119
169
  convertSelectionToNode: RealmNode<() => ElementNode>;
120
170
  insertDecoratorNode: RealmNode<() => DecoratorNode<unknown>>;
121
- }>], (_: Realm) => {}>, object>;
171
+ }>], (r: Realm, [{ createRootEditorSubscription, convertSelectionToNode }]: [{
172
+ activeEditor: RealmNode<LexicalEditor | null>;
173
+ inFocus: RealmNode<boolean>;
174
+ historyState: RealmNode<HistoryState>;
175
+ currentSelection: RealmNode<RangeSelection | null>;
176
+ jsxIsAvailable: RealmNode<boolean>;
177
+ jsxComponentDescriptors: RealmNode<JsxComponentDescriptor[]>;
178
+ initialRootEditorState: RealmNode<InitialEditorStateType>;
179
+ rootEditor: RealmNode<LexicalEditor | null>;
180
+ createRootEditorSubscription: RealmNode<EditorSubscription>;
181
+ createActiveEditorSubscription: RealmNode<EditorSubscription>;
182
+ importVisitors: RealmNode<MdastImportVisitor<Content>[]>;
183
+ syntaxExtensions: RealmNode<Extension[]>;
184
+ mdastExtensions: RealmNode<(Partial<Config> | Partial<Config>[])[] | null | undefined>;
185
+ usedLexicalNodes: RealmNode<Klass<LexicalNode>[]>;
186
+ addImportVisitor: RealmNode<MdastImportVisitor<Content>>;
187
+ addLexicalNode: RealmNode<Klass<LexicalNode>>;
188
+ addSyntaxExtension: RealmNode<Extension>;
189
+ addMdastExtension: RealmNode<Partial<Config> | Partial<Config>[]>;
190
+ toMarkdownExtensions: RealmNode<Options[]>;
191
+ toMarkdownOptions: RealmNode<Options>;
192
+ addToMarkdownExtension: RealmNode<Options>;
193
+ addExportVisitor: RealmNode<LexicalVisitor>;
194
+ exportVisitors: RealmNode<LexicalVisitor[]>;
195
+ initialMarkdown: RealmNode<string>;
196
+ setMarkdown: RealmNode<string>;
197
+ markdown: RealmNode<string>;
198
+ editorRootElementRef: RealmNode<RefObject<HTMLDivElement> | null>;
199
+ contentEditableClassName: RealmNode<string>;
200
+ placeholder: RealmNode<ReactNode>;
201
+ autoFocus: RealmNode<boolean>;
202
+ composerChildren: RealmNode<ComponentType<{}>[]>;
203
+ addComposerChild: RealmNode<ComponentType<{}>>;
204
+ topAreaChildren: RealmNode<ComponentType<{}>[]>;
205
+ addTopAreaChild: RealmNode<ComponentType<{}>>;
206
+ nestedEditorChildren: RealmNode<ComponentType<{}>[]>;
207
+ addNestedEditorChild: RealmNode<ComponentType<{}>>;
208
+ editorWrappers: RealmNode<ComponentType<{
209
+ children: ReactNode;
210
+ }>[]>;
211
+ addEditorWrapper: RealmNode<ComponentType<{
212
+ children: ReactNode;
213
+ }>>;
214
+ currentFormat: RealmNode<number>;
215
+ editorInFocus: RealmNode<EditorInFocus | null>;
216
+ applyFormat: RealmNode<TextFormatType>;
217
+ currentBlockType: RealmNode<BlockType>;
218
+ applyBlockType: RealmNode<BlockType>;
219
+ convertSelectionToNode: RealmNode<() => ElementNode>;
220
+ insertDecoratorNode: RealmNode<() => DecoratorNode<unknown>>;
221
+ }]) => {}>, object>;
@@ -1,10 +1,47 @@
1
1
  import { coreSystem } from "../core/index.js";
2
2
  import { MdastHeadingVisitor } from "./MdastHeadingVisitor.js";
3
- import { HeadingNode } from "@lexical/rich-text";
3
+ import { $createHeadingNode, HeadingNode } from "@lexical/rich-text";
4
4
  import { LexicalHeadingVisitor } from "./LexicalHeadingVisitor.js";
5
+ import { KEY_DOWN_COMMAND, $createParagraphNode, COMMAND_PRIORITY_LOW } from "lexical";
6
+ import { controlOrMeta } from "../../utils/detectMac.js";
5
7
  import { system } from "../../gurx/realmFactory.js";
6
8
  import { realmPlugin } from "../../gurx/react.js";
7
- const headingsSystem = system((_) => ({}), [coreSystem]);
9
+ const FORMATTING_KEYS = [48, 49, 50, 51, 52, 53, 54];
10
+ const CODE_TO_HEADING_MAP = {
11
+ 49: "h1",
12
+ 50: "h2",
13
+ 51: "h3",
14
+ 52: "h4",
15
+ 53: "h5",
16
+ 54: "h6"
17
+ };
18
+ const headingsSystem = system(
19
+ (r, [{ createRootEditorSubscription, convertSelectionToNode }]) => {
20
+ r.pub(createRootEditorSubscription, (theRootEditor) => {
21
+ return theRootEditor.registerCommand(
22
+ KEY_DOWN_COMMAND,
23
+ (event) => {
24
+ const { keyCode, ctrlKey, metaKey, altKey } = event;
25
+ if (FORMATTING_KEYS.includes(keyCode) && controlOrMeta(metaKey, ctrlKey) && altKey) {
26
+ event.preventDefault();
27
+ theRootEditor.update(() => {
28
+ if (keyCode === 48) {
29
+ r.pub(convertSelectionToNode, () => $createParagraphNode());
30
+ } else {
31
+ r.pub(convertSelectionToNode, () => $createHeadingNode(CODE_TO_HEADING_MAP[keyCode]));
32
+ }
33
+ });
34
+ return true;
35
+ }
36
+ return false;
37
+ },
38
+ COMMAND_PRIORITY_LOW
39
+ );
40
+ });
41
+ return {};
42
+ },
43
+ [coreSystem]
44
+ );
8
45
  const [headingsPlugin] = realmPlugin({
9
46
  id: "headings",
10
47
  systemSpec: headingsSystem,
@@ -1,5 +1,5 @@
1
1
  import { TOGGLE_LINK_COMMAND, $isLinkNode } from "@lexical/link";
2
- import { $isRangeSelection, KEY_ESCAPE_COMMAND, COMMAND_PRIORITY_LOW, KEY_MODIFIER_COMMAND, COMMAND_PRIORITY_HIGH } from "lexical";
2
+ import { $isRangeSelection, KEY_ESCAPE_COMMAND, COMMAND_PRIORITY_LOW, KEY_MODIFIER_COMMAND, $getSelection, COMMAND_PRIORITY_HIGH } from "lexical";
3
3
  import { IS_APPLE } from "../../utils/detectMac.js";
4
4
  import { getSelectionRectangle, getSelectedNode } from "../../utils/lexicalHelpers.js";
5
5
  import { coreSystem } from "../core/index.js";
@@ -83,9 +83,14 @@ const linkDialogSystem = system(
83
83
  KEY_MODIFIER_COMMAND,
84
84
  (event) => {
85
85
  if (event.key === "k" && (IS_APPLE ? event.metaKey : event.ctrlKey)) {
86
- r.pub(openLinkEditDialog, true);
87
- event.stopPropagation();
88
- return true;
86
+ const selection = $getSelection();
87
+ if ($isRangeSelection(selection) && !selection.isCollapsed()) {
88
+ r.pub(openLinkEditDialog, true);
89
+ event.stopPropagation();
90
+ return true;
91
+ } else {
92
+ return false;
93
+ }
89
94
  }
90
95
  return false;
91
96
  },
@@ -77,6 +77,7 @@ const DialogForm = ({ autocompleteSuggestions, onSubmitCallback, dialogInputPlac
77
77
  };
78
78
  const onSubmitEH = (e) => {
79
79
  e.preventDefault();
80
+ e.stopPropagation();
80
81
  onSubmitCallback(inputProps.value);
81
82
  };
82
83
  const dropdownIsVisible = isOpen && items.length > 0;
@@ -1,2 +1,3 @@
1
1
  export declare const CAN_USE_DOM: boolean;
2
2
  export declare const IS_APPLE: boolean;
3
+ export declare function controlOrMeta(metaKey: boolean, ctrlKey: boolean): boolean;
@@ -1,6 +1,13 @@
1
1
  const CAN_USE_DOM = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined";
2
2
  const IS_APPLE = CAN_USE_DOM && /Mac|iPod|iPhone|iPad/.test(navigator.platform);
3
+ function controlOrMeta(metaKey, ctrlKey) {
4
+ if (IS_APPLE) {
5
+ return metaKey;
6
+ }
7
+ return ctrlKey;
8
+ }
3
9
  export {
4
10
  CAN_USE_DOM,
5
- IS_APPLE
11
+ IS_APPLE,
12
+ controlOrMeta
6
13
  };
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "private": false,
10
10
  "sideEffects": false,
11
11
  "type": "module",
12
- "version": "0.14.1",
12
+ "version": "0.15.0",
13
13
  "description": "React component for rich text markdown editing",
14
14
  "scripts": {
15
15
  "build": "vite build",