@byline/richtext-lexical 3.4.0 → 3.5.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 (32) hide show
  1. package/dist/field/extensions/admonition/admonition-commands.d.ts +25 -0
  2. package/dist/field/extensions/admonition/admonition-commands.js +4 -0
  3. package/dist/field/extensions/admonition/admonition-extension.d.ts +2 -3
  4. package/dist/field/extensions/admonition/admonition-extension.js +138 -24
  5. package/dist/field/extensions/admonition/admonition-node.css +112 -0
  6. package/dist/field/extensions/admonition/admonition-node.d.ts +26 -14
  7. package/dist/field/extensions/admonition/admonition-node.js +121 -72
  8. package/dist/field/extensions/admonition/index.js +1 -0
  9. package/dist/field/extensions/admonition/node-types.d.ts +10 -4
  10. package/dist/field/extensions/floating-text-format/index.d.ts +8 -1
  11. package/dist/field/extensions/floating-text-format/index.js +6 -4
  12. package/dist/field/markdown/transformers.js +15 -15
  13. package/package.json +5 -5
  14. package/src/field/extensions/admonition/admonition-commands.ts +31 -0
  15. package/src/field/extensions/admonition/admonition-extension.tsx +248 -37
  16. package/src/field/extensions/admonition/admonition-node.css +113 -0
  17. package/src/field/extensions/admonition/admonition-node.tsx +172 -93
  18. package/src/field/extensions/admonition/index.ts +4 -8
  19. package/src/field/extensions/admonition/node-types.ts +10 -10
  20. package/src/field/extensions/floating-text-format/index.tsx +19 -3
  21. package/src/field/markdown/admonition-roundtrip.test.node.ts +110 -0
  22. package/src/field/markdown/transformers.ts +51 -25
  23. package/dist/field/extensions/admonition/admonition-node-component.css +0 -119
  24. package/dist/field/extensions/admonition/admonition-node-component.d.ts +0 -17
  25. package/dist/field/extensions/admonition/admonition-node-component.js +0 -196
  26. package/dist/field/extensions/admonition/icons/danger-icon.d.ts +0 -7
  27. package/dist/field/extensions/admonition/icons/index.d.ts +0 -4
  28. package/dist/field/extensions/admonition/icons/note-icon.d.ts +0 -7
  29. package/dist/field/extensions/admonition/icons/tip-icon.d.ts +0 -7
  30. package/dist/field/extensions/admonition/icons/warning-icon.d.ts +0 -7
  31. package/src/field/extensions/admonition/admonition-node-component.css +0 -115
  32. package/src/field/extensions/admonition/admonition-node-component.tsx +0 -257
@@ -0,0 +1,25 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ /**
9
+ * Leaf module for the admonition Lexical commands. Lives apart from
10
+ * `admonition-extension.tsx` so the node class (`admonition-node.tsx`)
11
+ * can dispatch the "open modal" command from its `createDOM` chrome
12
+ * without importing the extension (which imports the node — a cycle).
13
+ */
14
+ import { type LexicalCommand, type NodeKey } from 'lexical';
15
+ import type { AdmonitionAttributes } from './node-types';
16
+ /**
17
+ * Opens the admonition modal. Payload of `null` means "insert a new
18
+ * admonition"; a `{ nodeKey }` payload means "edit the existing node"
19
+ * — dispatched by the per-node Edit button rendered in `createDOM`.
20
+ */
21
+ export declare const OPEN_ADMONITION_MODAL_COMMAND: LexicalCommand<{
22
+ nodeKey: NodeKey;
23
+ } | null>;
24
+ /** Inserts a new admonition (type + title come from the modal). */
25
+ export declare const INSERT_ADMONITION_COMMAND: LexicalCommand<AdmonitionAttributes>;
@@ -0,0 +1,4 @@
1
+ import { createCommand } from "lexical";
2
+ const OPEN_ADMONITION_MODAL_COMMAND = createCommand('OPEN_ADMONITION_MODAL_COMMAND');
3
+ const INSERT_ADMONITION_COMMAND = createCommand('INSERT_ADMONITION_COMMAND');
4
+ export { INSERT_ADMONITION_COMMAND, OPEN_ADMONITION_MODAL_COMMAND };
@@ -6,10 +6,9 @@
6
6
  * Copyright (c) Infonomic Company Limited
7
7
  */
8
8
  import * as React from 'react';
9
- import { type LexicalCommand } from 'lexical';
9
+ import { INSERT_ADMONITION_COMMAND, OPEN_ADMONITION_MODAL_COMMAND } from './admonition-commands';
10
10
  import type { AdmonitionAttributes } from './node-types';
11
11
  export type InsertAdmonitionPayload = Readonly<AdmonitionAttributes>;
12
- export declare const OPEN_ADMONITION_MODAL_COMMAND: LexicalCommand<null>;
13
- export declare const INSERT_ADMONITION_COMMAND: LexicalCommand<AdmonitionAttributes>;
12
+ export { INSERT_ADMONITION_COMMAND, OPEN_ADMONITION_MODAL_COMMAND };
14
13
  export declare function AdmonitionPlugin(): React.JSX.Element;
15
14
  export declare const AdmonitionExtension: import("lexical").LexicalExtension<import("lexical").ExtensionConfigBase, "@byline/richtext-lexical/Admonition", unknown, unknown>;
@@ -2,57 +2,163 @@
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
4
4
  import { ReactExtension } from "@lexical/react/ReactExtension";
5
- import { $insertNodeToNearestRoot, mergeRegister } from "@lexical/utils";
6
- import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_NORMAL, configExtension, createCommand, declarePeerDependency, defineExtension } from "lexical";
5
+ import { useOptionalExtensionDependency } from "@lexical/react/useExtensionComponent";
6
+ import { $findMatchingParent, $insertNodeToNearestRoot, mergeRegister } from "@lexical/utils";
7
+ import { $createParagraphNode, $createTextNode, $getNodeByKey, $getSelection, $isDecoratorNode, $isElementNode, $isLineBreakNode, $isParagraphNode, $isRangeSelection, $isTextNode, COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_LOW, COMMAND_PRIORITY_NORMAL, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_ARROW_UP_COMMAND, configExtension, declarePeerDependency, defineExtension } from "lexical";
7
8
  import { useToolbarActiveEditor } from "../../plugins/toolbar-plugin/toolbar-active-editor.js";
8
9
  import { DropDownItem } from "../../ui/dropdown.js";
10
+ import { BylineFloatingUIExtension } from "../byline-floating-ui/byline-floating-ui-extension.js";
9
11
  import { BylineToolbarExtension } from "../byline-toolbar/byline-toolbar-extension.js";
12
+ import { FloatingTextFormatExtension } from "../floating-text-format/floating-text-format-extension.js";
13
+ import { FloatingTextFormatToolbarPlugin } from "../floating-text-format/index.js";
14
+ import { INSERT_ADMONITION_COMMAND, OPEN_ADMONITION_MODAL_COMMAND } from "./admonition-commands.js";
10
15
  import { AdmonitionModal } from "./admonition-modal.js";
11
- import { $createAdmonitionNode, AdmonitionNode } from "./admonition-node.js";
16
+ import { $createAdmonitionNode, $isAdmonitionNode, AdmonitionNode } from "./admonition-node.js";
12
17
  import * as __rspack_external_react from "react";
13
- const OPEN_ADMONITION_MODAL_COMMAND = createCommand('OPEN_ADMONITION_MODAL_COMMAND');
14
- const INSERT_ADMONITION_COMMAND = createCommand('INSERT_ADMONITION_COMMAND');
18
+ function $selectionInsideAdmonition() {
19
+ const selection = $getSelection();
20
+ if (!$isRangeSelection(selection)) return false;
21
+ return null != $findMatchingParent(selection.anchor.getNode(), $isAdmonitionNode);
22
+ }
23
+ function $flattenToParagraph(element) {
24
+ const paragraph = $createParagraphNode();
25
+ const children = element.getChildren();
26
+ const allInline = children.length > 0 && children.every((child)=>$isTextNode(child) || $isLineBreakNode(child) || child.isInline());
27
+ if (allInline) paragraph.append(...children);
28
+ else {
29
+ const text = element.getTextContent();
30
+ if (text.length > 0) paragraph.append($createTextNode(text));
31
+ }
32
+ element.replace(paragraph);
33
+ }
34
+ function $normalizeAdmonition(node) {
35
+ const parent = node.getParent();
36
+ const ancestorAdmonition = null != parent ? $findMatchingParent(parent, (candidate)=>$isAdmonitionNode(candidate)) : null;
37
+ if (null != ancestorAdmonition) {
38
+ for (const child of node.getChildren())node.insertBefore(child);
39
+ node.remove();
40
+ return;
41
+ }
42
+ if (0 === node.getChildrenSize()) return void node.append($createParagraphNode());
43
+ for (const child of node.getChildren())if (!$isParagraphNode(child)) {
44
+ if ($isTextNode(child) || $isLineBreakNode(child) || child.isInline()) {
45
+ const paragraph = $createParagraphNode();
46
+ child.insertBefore(paragraph);
47
+ paragraph.append(child);
48
+ continue;
49
+ }
50
+ if ($isDecoratorNode(child)) {
51
+ child.remove();
52
+ continue;
53
+ }
54
+ if ($isElementNode(child)) $flattenToParagraph(child);
55
+ }
56
+ }
57
+ function $onEscapeUp() {
58
+ const selection = $getSelection();
59
+ if ($isRangeSelection(selection) && selection.isCollapsed() && 0 === selection.anchor.offset) {
60
+ const admonition = $findMatchingParent(selection.anchor.getNode(), $isAdmonitionNode);
61
+ if ($isAdmonitionNode(admonition)) {
62
+ const parent = admonition.getParent();
63
+ if (null != parent && parent.getFirstChild() === admonition && selection.anchor.key === admonition.getFirstDescendant()?.getKey()) admonition.insertBefore($createParagraphNode());
64
+ }
65
+ }
66
+ return false;
67
+ }
68
+ function $onEscapeDown() {
69
+ const selection = $getSelection();
70
+ if ($isRangeSelection(selection) && selection.isCollapsed()) {
71
+ const admonition = $findMatchingParent(selection.anchor.getNode(), $isAdmonitionNode);
72
+ if ($isAdmonitionNode(admonition)) {
73
+ const parent = admonition.getParent();
74
+ if (null != parent && parent.getLastChild() === admonition) {
75
+ const lastParagraph = admonition.getLastDescendant();
76
+ if (null != lastParagraph && selection.anchor.key === lastParagraph.getKey() && selection.anchor.offset === lastParagraph.getTextContentSize()) admonition.insertAfter($createParagraphNode());
77
+ }
78
+ }
79
+ }
80
+ return false;
81
+ }
15
82
  function AdmonitionPlugin() {
16
83
  const [editor] = useLexicalComposerContext();
17
84
  const [open, setOpen] = __rspack_external_react.useState(false);
85
+ const [editNodeKey, setEditNodeKey] = __rspack_external_react.useState(null);
86
+ const [modalData, setModalData] = __rspack_external_react.useState({
87
+ title: '',
88
+ admonitionType: void 0
89
+ });
18
90
  (0, __rspack_external_react.useEffect)(()=>{
19
91
  if (!editor.hasNodes([
20
92
  AdmonitionNode
21
93
  ])) throw new Error('AdmonitionPlugin: AdmonitionNode not registered on editor');
22
- return mergeRegister(editor.registerCommand(OPEN_ADMONITION_MODAL_COMMAND, ()=>{
94
+ return mergeRegister(editor.registerCommand(OPEN_ADMONITION_MODAL_COMMAND, (payload)=>{
95
+ if (null == payload) {
96
+ setEditNodeKey(null);
97
+ setModalData({
98
+ title: '',
99
+ admonitionType: void 0
100
+ });
101
+ setOpen(true);
102
+ return true;
103
+ }
104
+ const data = editor.getEditorState().read(()=>{
105
+ const node = $getNodeByKey(payload.nodeKey);
106
+ return $isAdmonitionNode(node) ? {
107
+ title: node.getTitle(),
108
+ admonitionType: node.getAdmonitionType()
109
+ } : null;
110
+ });
111
+ if (null == data) return false;
112
+ setEditNodeKey(payload.nodeKey);
113
+ setModalData(data);
23
114
  setOpen(true);
24
115
  return true;
25
116
  }, COMMAND_PRIORITY_NORMAL), editor.registerCommand(INSERT_ADMONITION_COMMAND, (payload)=>{
26
117
  const selection = $getSelection();
27
118
  if (!$isRangeSelection(selection)) return false;
28
- const focusNode = selection.focus.getNode();
29
- if (null !== focusNode) {
30
- const admonitionNode = $createAdmonitionNode(payload);
31
- $insertNodeToNearestRoot(admonitionNode);
32
- }
119
+ if ($selectionInsideAdmonition()) return true;
120
+ const admonition = $createAdmonitionNode(payload);
121
+ admonition.append($createParagraphNode());
122
+ $insertNodeToNearestRoot(admonition);
123
+ admonition.selectStart();
33
124
  return true;
34
- }, COMMAND_PRIORITY_EDITOR));
125
+ }, COMMAND_PRIORITY_EDITOR), editor.registerNodeTransform(AdmonitionNode, $normalizeAdmonition), editor.registerCommand(KEY_ARROW_DOWN_COMMAND, $onEscapeDown, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_ARROW_RIGHT_COMMAND, $onEscapeDown, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_ARROW_UP_COMMAND, $onEscapeUp, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_ARROW_LEFT_COMMAND, $onEscapeUp, COMMAND_PRIORITY_LOW));
35
126
  }, [
36
127
  editor
37
128
  ]);
38
- const handleInsertAdmonition = ({ admonitionType, title })=>{
39
- if (null != title && null != admonitionType) {
40
- const admonitionPayload = {
129
+ const handleSubmit = ({ admonitionType, title })=>{
130
+ setOpen(false);
131
+ if (null == admonitionType) return;
132
+ const key = editNodeKey;
133
+ if (null != key) editor.update(()=>{
134
+ const node = $getNodeByKey(key);
135
+ if ($isAdmonitionNode(node)) node.update({
41
136
  admonitionType,
42
137
  title
43
- };
44
- editor.dispatchCommand(INSERT_ADMONITION_COMMAND, admonitionPayload);
45
- } else console.error('Error: missing title or type for admonition.');
46
- setOpen(false);
138
+ });
139
+ });
140
+ else editor.dispatchCommand(INSERT_ADMONITION_COMMAND, {
141
+ admonitionType,
142
+ title
143
+ });
144
+ setEditNodeKey(null);
47
145
  };
48
146
  return /*#__PURE__*/ jsx(AdmonitionModal, {
49
147
  open: open,
50
- data: {
51
- title: '',
52
- admonitionType: void 0
148
+ data: modalData,
149
+ onClose: ()=>{
150
+ setOpen(false);
151
+ setEditNodeKey(null);
53
152
  },
54
- onClose: ()=>setOpen(false),
55
- onSubmit: handleInsertAdmonition
153
+ onSubmit: handleSubmit
154
+ });
155
+ }
156
+ function AdmonitionFloatingTextFormat({ anchorElem }) {
157
+ const hasGlobalToolbar = void 0 !== useOptionalExtensionDependency(FloatingTextFormatExtension);
158
+ if (hasGlobalToolbar) return null;
159
+ return /*#__PURE__*/ jsx(FloatingTextFormatToolbarPlugin, {
160
+ anchorElem: anchorElem,
161
+ shouldShow: $selectionInsideAdmonition
56
162
  });
57
163
  }
58
164
  function AdmonitionInsertItem() {
@@ -95,6 +201,14 @@ const AdmonitionExtension = defineExtension({
95
201
  node: /*#__PURE__*/ jsx(AdmonitionInsertItem, {})
96
202
  }
97
203
  ]
204
+ }),
205
+ declarePeerDependency(BylineFloatingUIExtension.name, {
206
+ items: [
207
+ {
208
+ id: '@byline/richtext-lexical/Admonition/floating-text-format',
209
+ Component: AdmonitionFloatingTextFormat
210
+ }
211
+ ]
98
212
  })
99
213
  ]
100
214
  });
@@ -0,0 +1,112 @@
1
+ .LexicalEditor__admonition {
2
+ border: 1px solid #666;
3
+ border-left-width: 4px;
4
+ border-radius: 3px;
5
+ margin-top: .5rem;
6
+ margin-bottom: 1.5rem;
7
+ padding: 8px 12px 4px;
8
+ position: relative;
9
+ }
10
+
11
+ .LexicalEditor__admonition.admonition-note {
12
+ border-left-color: #4a90d9;
13
+ }
14
+
15
+ .LexicalEditor__admonition.admonition-tip {
16
+ border-left-color: #3fa45b;
17
+ }
18
+
19
+ .LexicalEditor__admonition.admonition-warning {
20
+ border-left-color: #d9a334;
21
+ }
22
+
23
+ .LexicalEditor__admonition.admonition-danger {
24
+ border-left-color: #d94a4a;
25
+ }
26
+
27
+ .LexicalEditor__admonition .AdmonitionNode__header {
28
+ text-transform: capitalize;
29
+ -webkit-user-select: none;
30
+ user-select: none;
31
+ align-items: center;
32
+ gap: 6px;
33
+ margin-bottom: .25rem;
34
+ font-size: 1rem;
35
+ font-weight: bold;
36
+ display: flex;
37
+ }
38
+
39
+ .LexicalEditor__admonition .AdmonitionNode__icon {
40
+ align-items: center;
41
+ display: inline-flex;
42
+ }
43
+
44
+ .LexicalEditor__admonition .AdmonitionNode__icon svg {
45
+ fill: currentColor;
46
+ width: 20px;
47
+ height: 20px;
48
+ }
49
+
50
+ .LexicalEditor__admonition.admonition-note .AdmonitionNode__icon {
51
+ color: #4a90d9;
52
+ }
53
+
54
+ .LexicalEditor__admonition.admonition-tip .AdmonitionNode__icon {
55
+ color: #3fa45b;
56
+ }
57
+
58
+ .LexicalEditor__admonition.admonition-warning .AdmonitionNode__icon {
59
+ color: #d9a334;
60
+ }
61
+
62
+ .LexicalEditor__admonition.admonition-danger .AdmonitionNode__icon {
63
+ color: #d94a4a;
64
+ }
65
+
66
+ .LexicalEditor__admonition .AdmonitionNode__title {
67
+ flex: auto;
68
+ }
69
+
70
+ .LexicalEditor__admonition .AdmonitionNode__edit-button {
71
+ color: inherit;
72
+ cursor: pointer;
73
+ -webkit-user-select: none;
74
+ user-select: none;
75
+ background-color: #0000000a;
76
+ border: 1px solid #0003;
77
+ border-radius: 5px;
78
+ flex: none;
79
+ padding: 2px 8px;
80
+ font-size: .8rem;
81
+ }
82
+
83
+ .LexicalEditor__admonition .AdmonitionNode__edit-button:hover {
84
+ background-color: #3c84f42e;
85
+ }
86
+
87
+ .LexicalEditor__admonition .AdmonitionNode__content {
88
+ color: #4b5563;
89
+ font-size: .925em;
90
+ }
91
+
92
+ .LexicalEditor__admonition .AdmonitionNode__content > * {
93
+ margin-bottom: .25em;
94
+ }
95
+
96
+ .LexicalEditor__admonition .AdmonitionNode__content > :last-child {
97
+ margin-bottom: 0;
98
+ }
99
+
100
+ .dark .LexicalEditor__admonition {
101
+ border-color: #444;
102
+ }
103
+
104
+ .dark .LexicalEditor__admonition .AdmonitionNode__content {
105
+ color: #9ca3af;
106
+ }
107
+
108
+ .dark .LexicalEditor__admonition .AdmonitionNode__edit-button {
109
+ background-color: #ffffff14;
110
+ border-color: #fff3;
111
+ }
112
+
@@ -5,30 +5,42 @@
5
5
  *
6
6
  * Copyright (c) Infonomic Company Limited
7
7
  */
8
- import * as React from 'react';
9
- import type { DOMConversionMap, DOMExportOutput, EditorConfig, LexicalEditor, LexicalNode, NodeKey } from 'lexical';
10
- import { DecoratorNode } from 'lexical';
8
+ import type { DOMConversionMap, DOMExportOutput, EditorConfig, ElementDOMSlot, LexicalEditor, LexicalNode, LexicalUpdateJSON, NodeKey } from 'lexical';
9
+ import { ElementNode } from 'lexical';
11
10
  import type { AdmonitionAttributes, AdmonitionType, SerializedAdmonitionNode } from './node-types';
12
- export declare class AdmonitionNode extends DecoratorNode<React.JSX.Element> {
11
+ import './admonition-node.css';
12
+ /**
13
+ * The admonition (callout) block. An `ElementNode` whose body is real
14
+ * children in the main editor tree — see the module note in
15
+ * `../../markdown/transformers.ts` for why this beats a nested editor for
16
+ * markdown round-tripping. `__admonitionType` / `__title` are attributes set
17
+ * from the Insert/Edit modal and rendered as non-editable chrome; the body
18
+ * (paragraphs + inline content) renders into the slot returned by
19
+ * `getDOMSlot`.
20
+ */
21
+ export declare class AdmonitionNode extends ElementNode {
13
22
  __admonitionType: AdmonitionType;
14
23
  __title: string;
15
- __content: LexicalEditor;
16
24
  static getType(): string;
17
25
  static clone(node: AdmonitionNode): AdmonitionNode;
18
26
  static importJSON(serializedNode: SerializedAdmonitionNode): AdmonitionNode;
19
27
  static importDOM(): DOMConversionMap | null;
20
- constructor(admonitionType: AdmonitionType, title: string, content?: LexicalEditor, key?: NodeKey);
21
- exportDOM(): DOMExportOutput;
28
+ constructor(admonitionType: AdmonitionType, title: string, key?: NodeKey);
29
+ updateFromJSON(serializedNode: LexicalUpdateJSON<SerializedAdmonitionNode>): this;
22
30
  exportJSON(): SerializedAdmonitionNode;
31
+ exportDOM(): DOMExportOutput;
32
+ isShadowRoot(): boolean;
23
33
  isInline(): false;
34
+ canBeEmpty(): boolean;
35
+ canIndent(): false;
36
+ createDOM(config: EditorConfig, editor: LexicalEditor): HTMLElement;
37
+ getDOMSlot(element: HTMLElement): ElementDOMSlot<HTMLElement>;
38
+ updateDOM(prevNode: AdmonitionNode, dom: HTMLElement): boolean;
24
39
  getTitle(): string;
25
- setTitle(title: string): void;
40
+ setTitle(title: string): this;
26
41
  getAdmonitionType(): AdmonitionType;
27
- setAdmonitionType(admonitionType: AdmonitionType): void;
28
- update(payload: AdmonitionAttributes): void;
29
- createDOM(config: EditorConfig): HTMLElement;
30
- updateDOM(prevNode: AdmonitionNode, dom: HTMLElement, config: EditorConfig): boolean;
31
- decorate(): React.JSX.Element;
42
+ setAdmonitionType(admonitionType: AdmonitionType): this;
43
+ update(payload: Partial<Pick<AdmonitionAttributes, 'admonitionType' | 'title'>>): void;
32
44
  }
33
- export declare function $createAdmonitionNode({ admonitionType, title, content, key, }: AdmonitionAttributes): AdmonitionNode;
45
+ export declare function $createAdmonitionNode({ admonitionType, title, key, }: AdmonitionAttributes): AdmonitionNode;
34
46
  export declare function $isAdmonitionNode(node: LexicalNode | null | undefined): node is AdmonitionNode;
@@ -1,127 +1,176 @@
1
1
  "use client";
2
- import { jsx } from "react/jsx-runtime";
3
- import { $applyNodeReplacement, DecoratorNode, createEditor } from "lexical";
4
- import * as __rspack_external_react from "react";
5
- const AdmonitionNodeComponent = /*#__PURE__*/ __rspack_external_react.lazy(async ()=>await import("./admonition-node-component.js"));
2
+ import { $applyNodeReplacement, ElementNode } from "lexical";
3
+ import { OPEN_ADMONITION_MODAL_COMMAND } from "./admonition-commands.js";
4
+ import "./admonition-node.css";
5
+ const ADMONITION_TYPES = new Set([
6
+ 'note',
7
+ 'tip',
8
+ 'warning',
9
+ 'danger'
10
+ ]);
11
+ function isAdmonitionType(value) {
12
+ return 'string' == typeof value && ADMONITION_TYPES.has(value);
13
+ }
14
+ const ICON_SVG = {
15
+ note: '<svg focusable="false" aria-hidden="true" viewBox="0 0 24 24"><path d="M11,9H13V7H11M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M11,17H13V11H11V17Z"></path></svg>',
16
+ tip: '<svg focusable="false" aria-hidden="true" viewBox="0 0 24 24"><path d="M20,12A8,8 0 0,1 12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4C12.76,4 13.5,4.11 14.2,4.31L15.77,2.74C14.61,2.26 13.34,2 12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12M7.91,10.08L6.5,11.5L11,16L21,6L19.59,4.58L11,13.17L7.91,10.08Z"></path></svg>',
17
+ warning: '<svg focusable="false" aria-hidden="true" viewBox="0 0 24 24"><path d="M12 5.99L19.53 19H4.47L12 5.99M12 2L1 21h22L12 2zm1 14h-2v2h2v-2zm0-6h-2v4h2v-4z"></path></svg>',
18
+ danger: '<svg focusable="false" aria-hidden="true" viewBox="0 0 24 24"><path d="M11 15h2v2h-2zm0-8h2v6h-2zm.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"></path></svg>'
19
+ };
6
20
  function convertAdmonitionElement(domNode) {
7
- if (domNode instanceof HTMLDivElement) {
21
+ if (domNode instanceof HTMLDivElement && null != domNode.dataset.type) {
8
22
  const type = domNode.dataset.type;
9
- const title = domNode.dataset.title;
10
- const node = $createAdmonitionNode({
11
- admonitionType: type,
12
- title
13
- });
23
+ if (!isAdmonitionType(type)) return null;
24
+ const title = domNode.dataset.title ?? '';
14
25
  return {
15
- node
26
+ node: $createAdmonitionNode({
27
+ admonitionType: type,
28
+ title
29
+ })
16
30
  };
17
31
  }
18
32
  return null;
19
33
  }
20
- class AdmonitionNode extends DecoratorNode {
34
+ class AdmonitionNode extends ElementNode {
21
35
  static getType() {
22
36
  return 'admonition';
23
37
  }
24
38
  static clone(node) {
25
- return new AdmonitionNode(node.__admonitionType, node.__title, node.__content, node.__key);
39
+ return new AdmonitionNode(node.__admonitionType, node.__title, node.__key);
26
40
  }
27
41
  static importJSON(serializedNode) {
28
- const { admonitionType, title, content } = serializedNode;
29
- const node = $createAdmonitionNode({
30
- admonitionType,
31
- title
32
- });
33
- const nestedEditor = node.__content;
34
- const editorState = nestedEditor.parseEditorState(content.editorState);
35
- if (!editorState.isEmpty()) nestedEditor.setEditorState(editorState);
36
- return node;
42
+ return $createAdmonitionNode({
43
+ admonitionType: serializedNode.admonitionType,
44
+ title: serializedNode.title
45
+ }).updateFromJSON(serializedNode);
37
46
  }
38
47
  static importDOM() {
39
48
  return {
40
- div: (_node)=>({
49
+ div: (node)=>{
50
+ if (null == node.dataset.type || !isAdmonitionType(node.dataset.type)) return null;
51
+ return {
41
52
  conversion: convertAdmonitionElement,
42
- priority: 0
43
- })
53
+ priority: 1
54
+ };
55
+ }
56
+ };
57
+ }
58
+ updateFromJSON(serializedNode) {
59
+ return super.updateFromJSON(serializedNode).setAdmonitionType(serializedNode.admonitionType).setTitle(serializedNode.title);
60
+ }
61
+ exportJSON() {
62
+ return {
63
+ ...super.exportJSON(),
64
+ admonitionType: this.__admonitionType,
65
+ title: this.__title
44
66
  };
45
67
  }
46
68
  exportDOM() {
47
69
  const element = document.createElement('div');
48
70
  element.setAttribute('data-type', this.__admonitionType);
49
71
  element.setAttribute('data-title', this.__title);
72
+ element.className = `admonition admonition-${this.__admonitionType}`;
50
73
  return {
51
74
  element
52
75
  };
53
76
  }
54
- exportJSON() {
55
- return {
56
- admonitionType: this.__admonitionType,
57
- title: this.__title,
58
- content: this.__content.toJSON(),
59
- type: 'admonition',
60
- version: 1
61
- };
77
+ isShadowRoot() {
78
+ return true;
62
79
  }
63
80
  isInline() {
64
81
  return false;
65
82
  }
83
+ canBeEmpty() {
84
+ return true;
85
+ }
86
+ canIndent() {
87
+ return false;
88
+ }
89
+ createDOM(config, editor) {
90
+ const key = this.getKey();
91
+ const dom = document.createElement('div');
92
+ dom.setAttribute('data-type', this.__admonitionType);
93
+ dom.setAttribute('data-title', this.__title);
94
+ dom.className = `${config.theme.admonition ?? ''} admonition-${this.__admonitionType}`.trim();
95
+ const header = document.createElement('div');
96
+ header.className = 'AdmonitionNode__header';
97
+ header.contentEditable = 'false';
98
+ header.setAttribute('data-lexical-admonition-chrome', 'true');
99
+ const icon = document.createElement('span');
100
+ icon.className = 'AdmonitionNode__icon';
101
+ icon.innerHTML = ICON_SVG[this.__admonitionType];
102
+ const titleEl = document.createElement('span');
103
+ titleEl.className = 'AdmonitionNode__title';
104
+ titleEl.textContent = this.__title;
105
+ const editButton = document.createElement('button');
106
+ editButton.type = 'button';
107
+ editButton.className = 'AdmonitionNode__edit-button';
108
+ editButton.textContent = 'Edit';
109
+ editButton.addEventListener('click', (event)=>{
110
+ event.preventDefault();
111
+ event.stopPropagation();
112
+ editor.dispatchCommand(OPEN_ADMONITION_MODAL_COMMAND, {
113
+ nodeKey: key
114
+ });
115
+ });
116
+ header.appendChild(icon);
117
+ header.appendChild(titleEl);
118
+ header.appendChild(editButton);
119
+ const content = document.createElement('div');
120
+ content.className = 'AdmonitionNode__content';
121
+ dom.appendChild(header);
122
+ dom.appendChild(content);
123
+ return dom;
124
+ }
125
+ getDOMSlot(element) {
126
+ const content = element.querySelector(':scope > .AdmonitionNode__content');
127
+ if (!(content instanceof HTMLElement)) throw new Error('AdmonitionNode: expected a .AdmonitionNode__content slot element');
128
+ return super.getDOMSlot(element).withElement(content);
129
+ }
130
+ updateDOM(prevNode, dom) {
131
+ const type = this.__admonitionType;
132
+ if (type !== prevNode.__admonitionType) {
133
+ dom.className = `${dom.className.replace(/admonition-\w+/, '').trim()} admonition-${type}`.trim();
134
+ dom.setAttribute('data-type', type);
135
+ const icon = dom.querySelector(':scope > .AdmonitionNode__header > .AdmonitionNode__icon');
136
+ if (null != icon) icon.innerHTML = ICON_SVG[type];
137
+ }
138
+ if (this.__title !== prevNode.__title) {
139
+ dom.setAttribute('data-title', this.__title);
140
+ const titleEl = dom.querySelector(':scope > .AdmonitionNode__header > .AdmonitionNode__title');
141
+ if (null != titleEl) titleEl.textContent = this.__title;
142
+ }
143
+ return false;
144
+ }
66
145
  getTitle() {
67
- return this.__title;
146
+ return this.getLatest().__title;
68
147
  }
69
148
  setTitle(title) {
70
149
  const writable = this.getWritable();
71
150
  writable.__title = title;
151
+ return writable;
72
152
  }
73
153
  getAdmonitionType() {
74
- return this.__admonitionType;
154
+ return this.getLatest().__admonitionType;
75
155
  }
76
156
  setAdmonitionType(admonitionType) {
77
157
  const writable = this.getWritable();
78
158
  writable.__admonitionType = admonitionType;
159
+ return writable;
79
160
  }
80
161
  update(payload) {
81
162
  const writable = this.getWritable();
82
- const { admonitionType, title } = payload;
83
- if (null != admonitionType) writable.__admonitionType = admonitionType;
84
- if (null != title) writable.__title = title;
85
- }
86
- createDOM(config) {
87
- const div = document.createElement('div');
88
- div.setAttribute('data-type', this.__admonitionType);
89
- div.setAttribute('data-title', this.__title);
90
- const className = `${config.theme.admonition} admonition-${this.__admonitionType}`;
91
- if (void 0 !== className) div.className = className;
92
- return div;
93
- }
94
- updateDOM(prevNode, dom, config) {
95
- const admonitionType = this.__admonitionType;
96
- if (admonitionType !== prevNode.__admonitionType) {
97
- const className = `${config.theme.admonition} admonition-${admonitionType}`;
98
- if (void 0 !== className) dom.className = className;
99
- dom.setAttribute('data-type', admonitionType);
100
- return true;
101
- }
102
- if (this.__title !== prevNode.__title) {
103
- dom.setAttribute('data-title', this.__title);
104
- return true;
105
- }
106
- return false;
107
- }
108
- decorate() {
109
- return /*#__PURE__*/ jsx(AdmonitionNodeComponent, {
110
- admonitionType: this.__admonitionType,
111
- title: this.__title,
112
- content: this.__content,
113
- nodeKey: this.getKey()
114
- });
163
+ if (null != payload.admonitionType) writable.__admonitionType = payload.admonitionType;
164
+ if (null != payload.title) writable.__title = payload.title;
115
165
  }
116
- constructor(admonitionType, title, content, key){
166
+ constructor(admonitionType, title, key){
117
167
  super(key);
118
168
  this.__admonitionType = admonitionType;
119
169
  this.__title = title;
120
- this.__content = content ?? createEditor();
121
170
  }
122
171
  }
123
- function $createAdmonitionNode({ admonitionType, title, content, key }) {
124
- return $applyNodeReplacement(new AdmonitionNode(admonitionType, title, content, key));
172
+ function $createAdmonitionNode({ admonitionType, title, key }) {
173
+ return $applyNodeReplacement(new AdmonitionNode(admonitionType, title, key));
125
174
  }
126
175
  function $isAdmonitionNode(node) {
127
176
  return node instanceof AdmonitionNode;
@@ -1,2 +1,3 @@
1
+ export * from "./admonition-commands.js";
1
2
  export * from "./admonition-node.js";
2
3
  export * from "./node-types.js";