Skip to content

Commit 3f86937

Browse files
committed
Lexical: Made summary part of details node
To provide more control of the summary as part of details. To support, added a way to ignore elements during import DOM, allowing up to read summaries when parsing details without duplicate nodes involved.
1 parent 2f119d3 commit 3f86937

File tree

4 files changed

+54
-60
lines changed

4 files changed

+54
-60
lines changed

resources/js/wysiwyg/lexical/core/LexicalNode.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,15 @@ export type DOMConversionMap<T extends HTMLElement = HTMLElement> = Record<
142142
>;
143143
type NodeName = string;
144144

145+
/**
146+
* Output for a DOM conversion.
147+
* Node can be set to 'ignore' to ignore the conversion and handling of the DOMNode
148+
* including all its children.
149+
*/
145150
export type DOMConversionOutput = {
146151
after?: (childLexicalNodes: Array<LexicalNode>) => Array<LexicalNode>;
147152
forChild?: DOMChildConversion;
148-
node: null | LexicalNode | Array<LexicalNode>;
153+
node: null | LexicalNode | Array<LexicalNode> | 'ignore';
149154
};
150155

151156
export type DOMExportOutputMap = Map<

resources/js/wysiwyg/lexical/html/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,11 @@ function $createNodesFromDOM(
217217
if (transformOutput !== null) {
218218
postTransform = transformOutput.after;
219219
const transformNodes = transformOutput.node;
220+
221+
if (transformNodes === 'ignore') {
222+
return lexicalNodes;
223+
}
224+
220225
currentLexicalNode = Array.isArray(transformNodes)
221226
? transformNodes[transformNodes.length - 1]
222227
: transformNodes;

resources/js/wysiwyg/lexical/rich-text/LexicalDetailsNode.ts

Lines changed: 41 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,19 @@ import {
55
LexicalEditor,
66
LexicalNode,
77
SerializedElementNode, Spread,
8-
EditorConfig,
8+
EditorConfig, DOMExportOutput,
99
} from 'lexical';
1010

11-
import {el} from "../../utils/dom";
1211
import {extractDirectionFromElement} from "lexical/nodes/common";
1312

1413
export type SerializedDetailsNode = Spread<{
1514
id: string;
15+
summary: string;
1616
}, SerializedElementNode>
1717

1818
export class DetailsNode extends ElementNode {
1919
__id: string = '';
20+
__summary: string = '';
2021

2122
static getType() {
2223
return 'details';
@@ -32,10 +33,21 @@ export class DetailsNode extends ElementNode {
3233
return self.__id;
3334
}
3435

36+
setSummary(summary: string) {
37+
const self = this.getWritable();
38+
self.__summary = summary;
39+
}
40+
41+
getSummary(): string {
42+
const self = this.getLatest();
43+
return self.__summary;
44+
}
45+
3546
static clone(node: DetailsNode): DetailsNode {
3647
const newNode = new DetailsNode(node.__key);
3748
newNode.__id = node.__id;
3849
newNode.__dir = node.__dir;
50+
newNode.__summary = node.__summary;
3951
return newNode;
4052
}
4153

@@ -49,6 +61,11 @@ export class DetailsNode extends ElementNode {
4961
el.setAttribute('dir', this.__dir);
5062
}
5163

64+
const summary = document.createElement('summary');
65+
summary.textContent = this.__summary;
66+
summary.setAttribute('contenteditable', 'false');
67+
el.append(summary);
68+
5269
return el;
5370
}
5471

@@ -71,20 +88,42 @@ export class DetailsNode extends ElementNode {
7188
node.setDirection(extractDirectionFromElement(element));
7289
}
7390

91+
const summaryElem = Array.from(element.children).find(e => e.nodeName === 'SUMMARY');
92+
node.setSummary(summaryElem?.textContent || '');
93+
7494
return {node};
7595
},
7696
priority: 3,
7797
};
7898
},
99+
summary(node: HTMLElement): DOMConversion|null {
100+
return {
101+
conversion: (element: HTMLElement): DOMConversionOutput|null => {
102+
return {node: 'ignore'};
103+
},
104+
priority: 3,
105+
};
106+
},
79107
};
80108
}
81109

110+
exportDOM(editor: LexicalEditor): DOMExportOutput {
111+
const element = this.createDOM(editor._config, editor);
112+
const editable = element.querySelectorAll('[contenteditable]');
113+
for (const elem of editable) {
114+
elem.removeAttribute('contenteditable');
115+
}
116+
117+
return {element};
118+
}
119+
82120
exportJSON(): SerializedDetailsNode {
83121
return {
84122
...super.exportJSON(),
85123
type: 'details',
86124
version: 1,
87125
id: this.__id,
126+
summary: this.__summary,
88127
};
89128
}
90129

@@ -104,58 +143,3 @@ export function $createDetailsNode() {
104143
export function $isDetailsNode(node: LexicalNode | null | undefined): node is DetailsNode {
105144
return node instanceof DetailsNode;
106145
}
107-
108-
export class SummaryNode extends ElementNode {
109-
110-
static getType() {
111-
return 'summary';
112-
}
113-
114-
static clone(node: SummaryNode) {
115-
return new SummaryNode(node.__key);
116-
}
117-
118-
createDOM(_config: EditorConfig, _editor: LexicalEditor) {
119-
return el('summary');
120-
}
121-
122-
updateDOM(prevNode: DetailsNode, dom: HTMLElement) {
123-
return false;
124-
}
125-
126-
static importDOM(): DOMConversionMap|null {
127-
return {
128-
summary(node: HTMLElement): DOMConversion|null {
129-
return {
130-
conversion: (element: HTMLElement): DOMConversionOutput|null => {
131-
return {
132-
node: new SummaryNode(),
133-
};
134-
},
135-
priority: 3,
136-
};
137-
},
138-
};
139-
}
140-
141-
exportJSON(): SerializedElementNode {
142-
return {
143-
...super.exportJSON(),
144-
type: 'summary',
145-
version: 1,
146-
};
147-
}
148-
149-
static importJSON(serializedNode: SerializedElementNode): SummaryNode {
150-
return $createSummaryNode();
151-
}
152-
153-
}
154-
155-
export function $createSummaryNode(): SummaryNode {
156-
return new SummaryNode();
157-
}
158-
159-
export function $isSummaryNode(node: LexicalNode | null | undefined): node is SummaryNode {
160-
return node instanceof SummaryNode;
161-
}

resources/js/wysiwyg/nodes.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
} from "lexical";
99
import {LinkNode} from "@lexical/link";
1010
import {ImageNode} from "@lexical/rich-text/LexicalImageNode";
11-
import {DetailsNode, SummaryNode} from "@lexical/rich-text/LexicalDetailsNode";
11+
import {DetailsNode} from "@lexical/rich-text/LexicalDetailsNode";
1212
import {ListItemNode, ListNode} from "@lexical/list";
1313
import {TableCellNode, TableNode, TableRowNode} from "@lexical/table";
1414
import {HorizontalRuleNode} from "@lexical/rich-text/LexicalHorizontalRuleNode";
@@ -34,7 +34,7 @@ export function getNodesForPageEditor(): (KlassConstructor<typeof LexicalNode> |
3434
TableCellNode,
3535
ImageNode, // TODO - Alignment
3636
HorizontalRuleNode,
37-
DetailsNode, SummaryNode,
37+
DetailsNode,
3838
CodeBlockNode,
3939
DiagramNode,
4040
MediaNode, // TODO - Alignment

0 commit comments

Comments
 (0)