Lexical: Merged custom paragraph node, removed old format/indent refs
Start of work to merge custom nodes into lexical, removing old unused format/indent core logic while extending common block elements where possible.
This commit is contained in:
parent
5164375b18
commit
f3fa63a5ae
|
@ -355,7 +355,6 @@ function onSelectionChange(
|
||||||
lastNode instanceof ParagraphNode &&
|
lastNode instanceof ParagraphNode &&
|
||||||
lastNode.getChildrenSize() === 0
|
lastNode.getChildrenSize() === 0
|
||||||
) {
|
) {
|
||||||
selection.format = lastNode.getTextFormat();
|
|
||||||
selection.style = lastNode.getTextStyle();
|
selection.style = lastNode.getTextStyle();
|
||||||
} else {
|
} else {
|
||||||
selection.format = 0;
|
selection.format = 0;
|
||||||
|
@ -578,7 +577,6 @@ function onBeforeInput(event: InputEvent, editor: LexicalEditor): void {
|
||||||
if ($isRangeSelection(selection)) {
|
if ($isRangeSelection(selection)) {
|
||||||
const anchorNode = selection.anchor.getNode();
|
const anchorNode = selection.anchor.getNode();
|
||||||
anchorNode.markDirty();
|
anchorNode.markDirty();
|
||||||
selection.format = anchorNode.getFormat();
|
|
||||||
invariant(
|
invariant(
|
||||||
$isTextNode(anchorNode),
|
$isTextNode(anchorNode),
|
||||||
'Anchor node must be a TextNode',
|
'Anchor node must be a TextNode',
|
||||||
|
@ -912,7 +910,6 @@ function onCompositionStart(
|
||||||
// need to invoke the empty space heuristic below.
|
// need to invoke the empty space heuristic below.
|
||||||
anchor.type === 'element' ||
|
anchor.type === 'element' ||
|
||||||
!selection.isCollapsed() ||
|
!selection.isCollapsed() ||
|
||||||
node.getFormat() !== selection.format ||
|
|
||||||
($isTextNode(node) && node.getStyle() !== selection.style)
|
($isTextNode(node) && node.getStyle() !== selection.style)
|
||||||
) {
|
) {
|
||||||
// We insert a zero width character, ready for the composition
|
// We insert a zero width character, ready for the composition
|
||||||
|
|
|
@ -96,15 +96,6 @@ function shouldUpdateTextNodeFromMutation(
|
||||||
targetDOM: Node,
|
targetDOM: Node,
|
||||||
targetNode: TextNode,
|
targetNode: TextNode,
|
||||||
): boolean {
|
): boolean {
|
||||||
if ($isRangeSelection(selection)) {
|
|
||||||
const anchorNode = selection.anchor.getNode();
|
|
||||||
if (
|
|
||||||
anchorNode.is(targetNode) &&
|
|
||||||
selection.format !== anchorNode.getFormat()
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return targetDOM.nodeType === DOM_TEXT_TYPE && targetNode.isAttached();
|
return targetDOM.nodeType === DOM_TEXT_TYPE && targetNode.isAttached();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@ import type {NodeKey, NodeMap} from './LexicalNode';
|
||||||
import type {ElementNode} from './nodes/LexicalElementNode';
|
import type {ElementNode} from './nodes/LexicalElementNode';
|
||||||
|
|
||||||
import invariant from 'lexical/shared/invariant';
|
import invariant from 'lexical/shared/invariant';
|
||||||
import normalizeClassNames from 'lexical/shared/normalizeClassNames';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
$isDecoratorNode,
|
$isDecoratorNode,
|
||||||
|
@ -117,51 +116,6 @@ function setTextAlign(domStyle: CSSStyleDeclaration, value: string): void {
|
||||||
domStyle.setProperty('text-align', value);
|
domStyle.setProperty('text-align', value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_INDENT_VALUE = '40px';
|
|
||||||
|
|
||||||
function setElementIndent(dom: HTMLElement, indent: number): void {
|
|
||||||
const indentClassName = activeEditorConfig.theme.indent;
|
|
||||||
|
|
||||||
if (typeof indentClassName === 'string') {
|
|
||||||
const elementHasClassName = dom.classList.contains(indentClassName);
|
|
||||||
|
|
||||||
if (indent > 0 && !elementHasClassName) {
|
|
||||||
dom.classList.add(indentClassName);
|
|
||||||
} else if (indent < 1 && elementHasClassName) {
|
|
||||||
dom.classList.remove(indentClassName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const indentationBaseValue =
|
|
||||||
getComputedStyle(dom).getPropertyValue('--lexical-indent-base-value') ||
|
|
||||||
DEFAULT_INDENT_VALUE;
|
|
||||||
|
|
||||||
dom.style.setProperty(
|
|
||||||
'padding-inline-start',
|
|
||||||
indent === 0 ? '' : `calc(${indent} * ${indentationBaseValue})`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setElementFormat(dom: HTMLElement, format: number): void {
|
|
||||||
const domStyle = dom.style;
|
|
||||||
|
|
||||||
if (format === 0) {
|
|
||||||
setTextAlign(domStyle, '');
|
|
||||||
} else if (format === IS_ALIGN_LEFT) {
|
|
||||||
setTextAlign(domStyle, 'left');
|
|
||||||
} else if (format === IS_ALIGN_CENTER) {
|
|
||||||
setTextAlign(domStyle, 'center');
|
|
||||||
} else if (format === IS_ALIGN_RIGHT) {
|
|
||||||
setTextAlign(domStyle, 'right');
|
|
||||||
} else if (format === IS_ALIGN_JUSTIFY) {
|
|
||||||
setTextAlign(domStyle, 'justify');
|
|
||||||
} else if (format === IS_ALIGN_START) {
|
|
||||||
setTextAlign(domStyle, 'start');
|
|
||||||
} else if (format === IS_ALIGN_END) {
|
|
||||||
setTextAlign(domStyle, 'end');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function $createNode(
|
function $createNode(
|
||||||
key: NodeKey,
|
key: NodeKey,
|
||||||
parentDOM: null | HTMLElement,
|
parentDOM: null | HTMLElement,
|
||||||
|
@ -185,22 +139,14 @@ function $createNode(
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($isElementNode(node)) {
|
if ($isElementNode(node)) {
|
||||||
const indent = node.__indent;
|
|
||||||
const childrenSize = node.__size;
|
const childrenSize = node.__size;
|
||||||
|
|
||||||
if (indent !== 0) {
|
|
||||||
setElementIndent(dom, indent);
|
|
||||||
}
|
|
||||||
if (childrenSize !== 0) {
|
if (childrenSize !== 0) {
|
||||||
const endIndex = childrenSize - 1;
|
const endIndex = childrenSize - 1;
|
||||||
const children = createChildrenArray(node, activeNextNodeMap);
|
const children = createChildrenArray(node, activeNextNodeMap);
|
||||||
$createChildren(children, node, 0, endIndex, dom, null);
|
$createChildren(children, node, 0, endIndex, dom, null);
|
||||||
}
|
}
|
||||||
const format = node.__format;
|
|
||||||
|
|
||||||
if (format !== 0) {
|
|
||||||
setElementFormat(dom, format);
|
|
||||||
}
|
|
||||||
if (!node.isInline()) {
|
if (!node.isInline()) {
|
||||||
reconcileElementTerminatingLineBreak(null, node, dom);
|
reconcileElementTerminatingLineBreak(null, node, dom);
|
||||||
}
|
}
|
||||||
|
@ -349,10 +295,8 @@ function reconcileParagraphFormat(element: ElementNode): void {
|
||||||
if (
|
if (
|
||||||
$isParagraphNode(element) &&
|
$isParagraphNode(element) &&
|
||||||
subTreeTextFormat != null &&
|
subTreeTextFormat != null &&
|
||||||
subTreeTextFormat !== element.__textFormat &&
|
|
||||||
!activeEditorStateReadOnly
|
!activeEditorStateReadOnly
|
||||||
) {
|
) {
|
||||||
element.setTextFormat(subTreeTextFormat);
|
|
||||||
element.setTextStyle(subTreeTextStyle);
|
element.setTextStyle(subTreeTextStyle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -563,17 +507,6 @@ function $reconcileNode(
|
||||||
|
|
||||||
if ($isElementNode(prevNode) && $isElementNode(nextNode)) {
|
if ($isElementNode(prevNode) && $isElementNode(nextNode)) {
|
||||||
// Reconcile element children
|
// Reconcile element children
|
||||||
const nextIndent = nextNode.__indent;
|
|
||||||
|
|
||||||
if (nextIndent !== prevNode.__indent) {
|
|
||||||
setElementIndent(dom, nextIndent);
|
|
||||||
}
|
|
||||||
|
|
||||||
const nextFormat = nextNode.__format;
|
|
||||||
|
|
||||||
if (nextFormat !== prevNode.__format) {
|
|
||||||
setElementFormat(dom, nextFormat);
|
|
||||||
}
|
|
||||||
if (isDirty) {
|
if (isDirty) {
|
||||||
$reconcileChildrenWithDirection(prevNode, nextNode, dom);
|
$reconcileChildrenWithDirection(prevNode, nextNode, dom);
|
||||||
if (!$isRootNode(nextNode) && !nextNode.isInline()) {
|
if (!$isRootNode(nextNode) && !nextNode.isInline()) {
|
||||||
|
|
|
@ -129,8 +129,6 @@ export class TestElementNode extends ElementNode {
|
||||||
serializedNode: SerializedTestElementNode,
|
serializedNode: SerializedTestElementNode,
|
||||||
): TestInlineElementNode {
|
): TestInlineElementNode {
|
||||||
const node = $createTestInlineElementNode();
|
const node = $createTestInlineElementNode();
|
||||||
node.setFormat(serializedNode.format);
|
|
||||||
node.setIndent(serializedNode.indent);
|
|
||||||
node.setDirection(serializedNode.direction);
|
node.setDirection(serializedNode.direction);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
@ -195,8 +193,6 @@ export class TestInlineElementNode extends ElementNode {
|
||||||
serializedNode: SerializedTestInlineElementNode,
|
serializedNode: SerializedTestInlineElementNode,
|
||||||
): TestInlineElementNode {
|
): TestInlineElementNode {
|
||||||
const node = $createTestInlineElementNode();
|
const node = $createTestInlineElementNode();
|
||||||
node.setFormat(serializedNode.format);
|
|
||||||
node.setIndent(serializedNode.indent);
|
|
||||||
node.setDirection(serializedNode.direction);
|
node.setDirection(serializedNode.direction);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
@ -241,8 +237,6 @@ export class TestShadowRootNode extends ElementNode {
|
||||||
serializedNode: SerializedTestShadowRootNode,
|
serializedNode: SerializedTestShadowRootNode,
|
||||||
): TestShadowRootNode {
|
): TestShadowRootNode {
|
||||||
const node = $createTestShadowRootNode();
|
const node = $createTestShadowRootNode();
|
||||||
node.setFormat(serializedNode.format);
|
|
||||||
node.setIndent(serializedNode.indent);
|
|
||||||
node.setDirection(serializedNode.direction);
|
node.setDirection(serializedNode.direction);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
@ -322,8 +316,6 @@ export class TestExcludeFromCopyElementNode extends ElementNode {
|
||||||
serializedNode: SerializedTestExcludeFromCopyElementNode,
|
serializedNode: SerializedTestExcludeFromCopyElementNode,
|
||||||
): TestExcludeFromCopyElementNode {
|
): TestExcludeFromCopyElementNode {
|
||||||
const node = $createTestExcludeFromCopyElementNode();
|
const node = $createTestExcludeFromCopyElementNode();
|
||||||
node.setFormat(serializedNode.format);
|
|
||||||
node.setIndent(serializedNode.indent);
|
|
||||||
node.setDirection(serializedNode.direction);
|
node.setDirection(serializedNode.direction);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
import {ElementNode} from "./LexicalElementNode";
|
||||||
|
import {CommonBlockAlignment, SerializedCommonBlockNode} from "../../../nodes/_common";
|
||||||
|
|
||||||
|
|
||||||
|
export class CommonBlockNode extends ElementNode {
|
||||||
|
__id: string = '';
|
||||||
|
__alignment: CommonBlockAlignment = '';
|
||||||
|
__inset: number = 0;
|
||||||
|
|
||||||
|
setId(id: string) {
|
||||||
|
const self = this.getWritable();
|
||||||
|
self.__id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
getId(): string {
|
||||||
|
const self = this.getLatest();
|
||||||
|
return self.__id;
|
||||||
|
}
|
||||||
|
|
||||||
|
setAlignment(alignment: CommonBlockAlignment) {
|
||||||
|
const self = this.getWritable();
|
||||||
|
self.__alignment = alignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAlignment(): CommonBlockAlignment {
|
||||||
|
const self = this.getLatest();
|
||||||
|
return self.__alignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
setInset(size: number) {
|
||||||
|
const self = this.getWritable();
|
||||||
|
self.__inset = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
getInset(): number {
|
||||||
|
const self = this.getLatest();
|
||||||
|
return self.__inset;
|
||||||
|
}
|
||||||
|
|
||||||
|
exportJSON(): SerializedCommonBlockNode {
|
||||||
|
return {
|
||||||
|
...super.exportJSON(),
|
||||||
|
id: this.__id,
|
||||||
|
alignment: this.__alignment,
|
||||||
|
inset: this.__inset,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function copyCommonBlockProperties(from: CommonBlockNode, to: CommonBlockNode): void {
|
||||||
|
to.__id = from.__id;
|
||||||
|
to.__alignment = from.__alignment;
|
||||||
|
to.__inset = from.__inset;
|
||||||
|
}
|
|
@ -42,8 +42,6 @@ export type SerializedElementNode<
|
||||||
{
|
{
|
||||||
children: Array<T>;
|
children: Array<T>;
|
||||||
direction: 'ltr' | 'rtl' | null;
|
direction: 'ltr' | 'rtl' | null;
|
||||||
format: ElementFormatType;
|
|
||||||
indent: number;
|
|
||||||
},
|
},
|
||||||
SerializedLexicalNode
|
SerializedLexicalNode
|
||||||
>;
|
>;
|
||||||
|
@ -74,12 +72,8 @@ export class ElementNode extends LexicalNode {
|
||||||
/** @internal */
|
/** @internal */
|
||||||
__size: number;
|
__size: number;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
__format: number;
|
|
||||||
/** @internal */
|
|
||||||
__style: string;
|
__style: string;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
__indent: number;
|
|
||||||
/** @internal */
|
|
||||||
__dir: 'ltr' | 'rtl' | null;
|
__dir: 'ltr' | 'rtl' | null;
|
||||||
|
|
||||||
constructor(key?: NodeKey) {
|
constructor(key?: NodeKey) {
|
||||||
|
@ -87,9 +81,7 @@ export class ElementNode extends LexicalNode {
|
||||||
this.__first = null;
|
this.__first = null;
|
||||||
this.__last = null;
|
this.__last = null;
|
||||||
this.__size = 0;
|
this.__size = 0;
|
||||||
this.__format = 0;
|
|
||||||
this.__style = '';
|
this.__style = '';
|
||||||
this.__indent = 0;
|
|
||||||
this.__dir = null;
|
this.__dir = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,28 +90,14 @@ export class ElementNode extends LexicalNode {
|
||||||
this.__first = prevNode.__first;
|
this.__first = prevNode.__first;
|
||||||
this.__last = prevNode.__last;
|
this.__last = prevNode.__last;
|
||||||
this.__size = prevNode.__size;
|
this.__size = prevNode.__size;
|
||||||
this.__indent = prevNode.__indent;
|
|
||||||
this.__format = prevNode.__format;
|
|
||||||
this.__style = prevNode.__style;
|
this.__style = prevNode.__style;
|
||||||
this.__dir = prevNode.__dir;
|
this.__dir = prevNode.__dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
getFormat(): number {
|
|
||||||
const self = this.getLatest();
|
|
||||||
return self.__format;
|
|
||||||
}
|
|
||||||
getFormatType(): ElementFormatType {
|
|
||||||
const format = this.getFormat();
|
|
||||||
return ELEMENT_FORMAT_TO_TYPE[format] || '';
|
|
||||||
}
|
|
||||||
getStyle(): string {
|
getStyle(): string {
|
||||||
const self = this.getLatest();
|
const self = this.getLatest();
|
||||||
return self.__style;
|
return self.__style;
|
||||||
}
|
}
|
||||||
getIndent(): number {
|
|
||||||
const self = this.getLatest();
|
|
||||||
return self.__indent;
|
|
||||||
}
|
|
||||||
getChildren<T extends LexicalNode>(): Array<T> {
|
getChildren<T extends LexicalNode>(): Array<T> {
|
||||||
const children: Array<T> = [];
|
const children: Array<T> = [];
|
||||||
let child: T | null = this.getFirstChild();
|
let child: T | null = this.getFirstChild();
|
||||||
|
@ -301,13 +279,6 @@ export class ElementNode extends LexicalNode {
|
||||||
const self = this.getLatest();
|
const self = this.getLatest();
|
||||||
return self.__dir;
|
return self.__dir;
|
||||||
}
|
}
|
||||||
hasFormat(type: ElementFormatType): boolean {
|
|
||||||
if (type !== '') {
|
|
||||||
const formatFlag = ELEMENT_TYPE_TO_FORMAT[type];
|
|
||||||
return (this.getFormat() & formatFlag) !== 0;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mutators
|
// Mutators
|
||||||
|
|
||||||
|
@ -378,21 +349,11 @@ export class ElementNode extends LexicalNode {
|
||||||
self.__dir = direction;
|
self.__dir = direction;
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
setFormat(type: ElementFormatType): this {
|
|
||||||
const self = this.getWritable();
|
|
||||||
self.__format = type !== '' ? ELEMENT_TYPE_TO_FORMAT[type] : 0;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
setStyle(style: string): this {
|
setStyle(style: string): this {
|
||||||
const self = this.getWritable();
|
const self = this.getWritable();
|
||||||
self.__style = style || '';
|
self.__style = style || '';
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
setIndent(indentLevel: number): this {
|
|
||||||
const self = this.getWritable();
|
|
||||||
self.__indent = indentLevel;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
splice(
|
splice(
|
||||||
start: number,
|
start: number,
|
||||||
deleteCount: number,
|
deleteCount: number,
|
||||||
|
@ -528,8 +489,6 @@ export class ElementNode extends LexicalNode {
|
||||||
return {
|
return {
|
||||||
children: [],
|
children: [],
|
||||||
direction: this.getDirection(),
|
direction: this.getDirection(),
|
||||||
format: this.getFormatType(),
|
|
||||||
indent: this.getIndent(),
|
|
||||||
type: 'element',
|
type: 'element',
|
||||||
version: 1,
|
version: 1,
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,39 +19,36 @@ import type {
|
||||||
LexicalNode,
|
LexicalNode,
|
||||||
NodeKey,
|
NodeKey,
|
||||||
} from '../LexicalNode';
|
} from '../LexicalNode';
|
||||||
import type {
|
|
||||||
ElementFormatType,
|
|
||||||
SerializedElementNode,
|
|
||||||
} from './LexicalElementNode';
|
|
||||||
import type {RangeSelection} from 'lexical';
|
import type {RangeSelection} from 'lexical';
|
||||||
|
|
||||||
import {TEXT_TYPE_TO_FORMAT} from '../LexicalConstants';
|
|
||||||
import {
|
import {
|
||||||
$applyNodeReplacement,
|
$applyNodeReplacement,
|
||||||
getCachedClassNameArray,
|
getCachedClassNameArray,
|
||||||
isHTMLElement,
|
isHTMLElement,
|
||||||
} from '../LexicalUtils';
|
} from '../LexicalUtils';
|
||||||
import {ElementNode} from './LexicalElementNode';
|
import {$isTextNode} from './LexicalTextNode';
|
||||||
import {$isTextNode, TextFormatType} from './LexicalTextNode';
|
import {
|
||||||
|
commonPropertiesDifferent, deserializeCommonBlockNode,
|
||||||
|
SerializedCommonBlockNode, setCommonBlockPropsFromElement,
|
||||||
|
updateElementWithCommonBlockProps
|
||||||
|
} from "../../../nodes/_common";
|
||||||
|
import {CommonBlockNode, copyCommonBlockProperties} from "lexical/nodes/CommonBlockNode";
|
||||||
|
|
||||||
export type SerializedParagraphNode = Spread<
|
export type SerializedParagraphNode = Spread<
|
||||||
{
|
{
|
||||||
textFormat: number;
|
|
||||||
textStyle: string;
|
textStyle: string;
|
||||||
},
|
},
|
||||||
SerializedElementNode
|
SerializedCommonBlockNode
|
||||||
>;
|
>;
|
||||||
|
|
||||||
/** @noInheritDoc */
|
/** @noInheritDoc */
|
||||||
export class ParagraphNode extends ElementNode {
|
export class ParagraphNode extends CommonBlockNode {
|
||||||
['constructor']!: KlassConstructor<typeof ParagraphNode>;
|
['constructor']!: KlassConstructor<typeof ParagraphNode>;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
__textFormat: number;
|
|
||||||
__textStyle: string;
|
__textStyle: string;
|
||||||
|
|
||||||
constructor(key?: NodeKey) {
|
constructor(key?: NodeKey) {
|
||||||
super(key);
|
super(key);
|
||||||
this.__textFormat = 0;
|
|
||||||
this.__textStyle = '';
|
this.__textStyle = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,22 +56,6 @@ export class ParagraphNode extends ElementNode {
|
||||||
return 'paragraph';
|
return 'paragraph';
|
||||||
}
|
}
|
||||||
|
|
||||||
getTextFormat(): number {
|
|
||||||
const self = this.getLatest();
|
|
||||||
return self.__textFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTextFormat(type: number): this {
|
|
||||||
const self = this.getWritable();
|
|
||||||
self.__textFormat = type;
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasTextFormat(type: TextFormatType): boolean {
|
|
||||||
const formatFlag = TEXT_TYPE_TO_FORMAT[type];
|
|
||||||
return (this.getTextFormat() & formatFlag) !== 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
getTextStyle(): string {
|
getTextStyle(): string {
|
||||||
const self = this.getLatest();
|
const self = this.getLatest();
|
||||||
return self.__textStyle;
|
return self.__textStyle;
|
||||||
|
@ -92,8 +73,8 @@ export class ParagraphNode extends ElementNode {
|
||||||
|
|
||||||
afterCloneFrom(prevNode: this) {
|
afterCloneFrom(prevNode: this) {
|
||||||
super.afterCloneFrom(prevNode);
|
super.afterCloneFrom(prevNode);
|
||||||
this.__textFormat = prevNode.__textFormat;
|
|
||||||
this.__textStyle = prevNode.__textStyle;
|
this.__textStyle = prevNode.__textStyle;
|
||||||
|
copyCommonBlockProperties(prevNode, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// View
|
// View
|
||||||
|
@ -105,6 +86,9 @@ export class ParagraphNode extends ElementNode {
|
||||||
const domClassList = dom.classList;
|
const domClassList = dom.classList;
|
||||||
domClassList.add(...classNames);
|
domClassList.add(...classNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateElementWithCommonBlockProps(dom, this);
|
||||||
|
|
||||||
return dom;
|
return dom;
|
||||||
}
|
}
|
||||||
updateDOM(
|
updateDOM(
|
||||||
|
@ -112,7 +96,7 @@ export class ParagraphNode extends ElementNode {
|
||||||
dom: HTMLElement,
|
dom: HTMLElement,
|
||||||
config: EditorConfig,
|
config: EditorConfig,
|
||||||
): boolean {
|
): boolean {
|
||||||
return false;
|
return commonPropertiesDifferent(prevNode, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
static importDOM(): DOMConversionMap | null {
|
static importDOM(): DOMConversionMap | null {
|
||||||
|
@ -131,16 +115,6 @@ export class ParagraphNode extends ElementNode {
|
||||||
if (this.isEmpty()) {
|
if (this.isEmpty()) {
|
||||||
element.append(document.createElement('br'));
|
element.append(document.createElement('br'));
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatType = this.getFormatType();
|
|
||||||
element.style.textAlign = formatType;
|
|
||||||
|
|
||||||
const indent = this.getIndent();
|
|
||||||
if (indent > 0) {
|
|
||||||
// padding-inline-start is not widely supported in email HTML, but
|
|
||||||
// Lexical Reconciler uses padding-inline-start. Using text-indent instead.
|
|
||||||
element.style.textIndent = `${indent * 20}px`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -150,16 +124,13 @@ export class ParagraphNode extends ElementNode {
|
||||||
|
|
||||||
static importJSON(serializedNode: SerializedParagraphNode): ParagraphNode {
|
static importJSON(serializedNode: SerializedParagraphNode): ParagraphNode {
|
||||||
const node = $createParagraphNode();
|
const node = $createParagraphNode();
|
||||||
node.setFormat(serializedNode.format);
|
deserializeCommonBlockNode(serializedNode, node);
|
||||||
node.setIndent(serializedNode.indent);
|
|
||||||
node.setTextFormat(serializedNode.textFormat);
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
exportJSON(): SerializedParagraphNode {
|
exportJSON(): SerializedParagraphNode {
|
||||||
return {
|
return {
|
||||||
...super.exportJSON(),
|
...super.exportJSON(),
|
||||||
textFormat: this.getTextFormat(),
|
|
||||||
textStyle: this.getTextStyle(),
|
textStyle: this.getTextStyle(),
|
||||||
type: 'paragraph',
|
type: 'paragraph',
|
||||||
version: 1,
|
version: 1,
|
||||||
|
@ -173,11 +144,9 @@ export class ParagraphNode extends ElementNode {
|
||||||
restoreSelection: boolean,
|
restoreSelection: boolean,
|
||||||
): ParagraphNode {
|
): ParagraphNode {
|
||||||
const newElement = $createParagraphNode();
|
const newElement = $createParagraphNode();
|
||||||
newElement.setTextFormat(rangeSelection.format);
|
|
||||||
newElement.setTextStyle(rangeSelection.style);
|
newElement.setTextStyle(rangeSelection.style);
|
||||||
const direction = this.getDirection();
|
const direction = this.getDirection();
|
||||||
newElement.setDirection(direction);
|
newElement.setDirection(direction);
|
||||||
newElement.setFormat(this.getFormatType());
|
|
||||||
newElement.setStyle(this.getTextStyle());
|
newElement.setStyle(this.getTextStyle());
|
||||||
this.insertAfter(newElement, restoreSelection);
|
this.insertAfter(newElement, restoreSelection);
|
||||||
return newElement;
|
return newElement;
|
||||||
|
@ -210,13 +179,7 @@ export class ParagraphNode extends ElementNode {
|
||||||
|
|
||||||
function $convertParagraphElement(element: HTMLElement): DOMConversionOutput {
|
function $convertParagraphElement(element: HTMLElement): DOMConversionOutput {
|
||||||
const node = $createParagraphNode();
|
const node = $createParagraphNode();
|
||||||
if (element.style) {
|
setCommonBlockPropsFromElement(element, node);
|
||||||
node.setFormat(element.style.textAlign as ElementFormatType);
|
|
||||||
const indent = parseInt(element.style.textIndent, 10) / 20;
|
|
||||||
if (indent > 0) {
|
|
||||||
node.setIndent(indent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {node};
|
return {node};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -99,8 +99,6 @@ export class RootNode extends ElementNode {
|
||||||
static importJSON(serializedNode: SerializedRootNode): RootNode {
|
static importJSON(serializedNode: SerializedRootNode): RootNode {
|
||||||
// We don't create a root, and instead use the existing root.
|
// We don't create a root, and instead use the existing root.
|
||||||
const node = $getRoot();
|
const node = $getRoot();
|
||||||
node.setFormat(serializedNode.format);
|
|
||||||
node.setIndent(serializedNode.indent);
|
|
||||||
node.setDirection(serializedNode.direction);
|
node.setDirection(serializedNode.direction);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
@ -109,8 +107,6 @@ export class RootNode extends ElementNode {
|
||||||
return {
|
return {
|
||||||
children: [],
|
children: [],
|
||||||
direction: this.getDirection(),
|
direction: this.getDirection(),
|
||||||
format: this.getFormatType(),
|
|
||||||
indent: this.getIndent(),
|
|
||||||
type: 'root',
|
type: 'root',
|
||||||
version: 1,
|
version: 1,
|
||||||
};
|
};
|
||||||
|
|
|
@ -327,9 +327,6 @@ function wrapContinuousInlines(
|
||||||
for (let i = 0; i < nodes.length; i++) {
|
for (let i = 0; i < nodes.length; i++) {
|
||||||
const node = nodes[i];
|
const node = nodes[i];
|
||||||
if ($isBlockElementNode(node)) {
|
if ($isBlockElementNode(node)) {
|
||||||
if (textAlign && !node.getFormat()) {
|
|
||||||
node.setFormat(textAlign);
|
|
||||||
}
|
|
||||||
out.push(node);
|
out.push(node);
|
||||||
} else {
|
} else {
|
||||||
continuousInlines.push(node);
|
continuousInlines.push(node);
|
||||||
|
@ -338,7 +335,6 @@ function wrapContinuousInlines(
|
||||||
(i < nodes.length - 1 && $isBlockElementNode(nodes[i + 1]))
|
(i < nodes.length - 1 && $isBlockElementNode(nodes[i + 1]))
|
||||||
) {
|
) {
|
||||||
const wrapper = createWrapperFn();
|
const wrapper = createWrapperFn();
|
||||||
wrapper.setFormat(textAlign);
|
|
||||||
wrapper.append(...continuousInlines);
|
wrapper.append(...continuousInlines);
|
||||||
out.push(wrapper);
|
out.push(wrapper);
|
||||||
continuousInlines = [];
|
continuousInlines = [];
|
||||||
|
|
|
@ -162,8 +162,6 @@ export class LinkNode extends ElementNode {
|
||||||
target: serializedNode.target,
|
target: serializedNode.target,
|
||||||
title: serializedNode.title,
|
title: serializedNode.title,
|
||||||
});
|
});
|
||||||
node.setFormat(serializedNode.format);
|
|
||||||
node.setIndent(serializedNode.indent);
|
|
||||||
node.setDirection(serializedNode.direction);
|
node.setDirection(serializedNode.direction);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
@ -402,8 +400,6 @@ export class AutoLinkNode extends LinkNode {
|
||||||
target: serializedNode.target,
|
target: serializedNode.target,
|
||||||
title: serializedNode.title,
|
title: serializedNode.title,
|
||||||
});
|
});
|
||||||
node.setFormat(serializedNode.format);
|
|
||||||
node.setIndent(serializedNode.indent);
|
|
||||||
node.setDirection(serializedNode.direction);
|
node.setDirection(serializedNode.direction);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,14 +126,12 @@ export class ListItemNode extends ElementNode {
|
||||||
const node = $createListItemNode();
|
const node = $createListItemNode();
|
||||||
node.setChecked(serializedNode.checked);
|
node.setChecked(serializedNode.checked);
|
||||||
node.setValue(serializedNode.value);
|
node.setValue(serializedNode.value);
|
||||||
node.setFormat(serializedNode.format);
|
|
||||||
node.setDirection(serializedNode.direction);
|
node.setDirection(serializedNode.direction);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
exportDOM(editor: LexicalEditor): DOMExportOutput {
|
exportDOM(editor: LexicalEditor): DOMExportOutput {
|
||||||
const element = this.createDOM(editor._config);
|
const element = this.createDOM(editor._config);
|
||||||
element.style.textAlign = this.getFormatType();
|
|
||||||
return {
|
return {
|
||||||
element,
|
element,
|
||||||
};
|
};
|
||||||
|
@ -172,7 +170,6 @@ export class ListItemNode extends ElementNode {
|
||||||
if ($isListItemNode(replaceWithNode)) {
|
if ($isListItemNode(replaceWithNode)) {
|
||||||
return super.replace(replaceWithNode);
|
return super.replace(replaceWithNode);
|
||||||
}
|
}
|
||||||
this.setIndent(0);
|
|
||||||
const list = this.getParentOrThrow();
|
const list = this.getParentOrThrow();
|
||||||
if (!$isListNode(list)) {
|
if (!$isListNode(list)) {
|
||||||
return replaceWithNode;
|
return replaceWithNode;
|
||||||
|
@ -351,41 +348,6 @@ export class ListItemNode extends ElementNode {
|
||||||
this.setChecked(!this.__checked);
|
this.setChecked(!this.__checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
getIndent(): number {
|
|
||||||
// If we don't have a parent, we are likely serializing
|
|
||||||
const parent = this.getParent();
|
|
||||||
if (parent === null) {
|
|
||||||
return this.getLatest().__indent;
|
|
||||||
}
|
|
||||||
// ListItemNode should always have a ListNode for a parent.
|
|
||||||
let listNodeParent = parent.getParentOrThrow();
|
|
||||||
let indentLevel = 0;
|
|
||||||
while ($isListItemNode(listNodeParent)) {
|
|
||||||
listNodeParent = listNodeParent.getParentOrThrow().getParentOrThrow();
|
|
||||||
indentLevel++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return indentLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
setIndent(indent: number): this {
|
|
||||||
invariant(typeof indent === 'number', 'Invalid indent value.');
|
|
||||||
indent = Math.floor(indent);
|
|
||||||
invariant(indent >= 0, 'Indent value must be non-negative.');
|
|
||||||
let currentIndent = this.getIndent();
|
|
||||||
while (currentIndent !== indent) {
|
|
||||||
if (currentIndent < indent) {
|
|
||||||
$handleIndent(this);
|
|
||||||
currentIndent++;
|
|
||||||
} else {
|
|
||||||
$handleOutdent(this);
|
|
||||||
currentIndent--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @deprecated @internal */
|
/** @deprecated @internal */
|
||||||
canInsertAfter(node: LexicalNode): boolean {
|
canInsertAfter(node: LexicalNode): boolean {
|
||||||
return $isListItemNode(node);
|
return $isListItemNode(node);
|
||||||
|
|
|
@ -84,10 +84,6 @@ export function insertList(editor: LexicalEditor, listType: ListType): void {
|
||||||
if ($isRootOrShadowRoot(anchorNodeParent)) {
|
if ($isRootOrShadowRoot(anchorNodeParent)) {
|
||||||
anchorNode.replace(list);
|
anchorNode.replace(list);
|
||||||
const listItem = $createListItemNode();
|
const listItem = $createListItemNode();
|
||||||
if ($isElementNode(anchorNode)) {
|
|
||||||
listItem.setFormat(anchorNode.getFormatType());
|
|
||||||
listItem.setIndent(anchorNode.getIndent());
|
|
||||||
}
|
|
||||||
list.append(listItem);
|
list.append(listItem);
|
||||||
} else if ($isListItemNode(anchorNode)) {
|
} else if ($isListItemNode(anchorNode)) {
|
||||||
const parent = anchorNode.getParentOrThrow();
|
const parent = anchorNode.getParentOrThrow();
|
||||||
|
@ -157,8 +153,6 @@ function $createListOrMerge(node: ElementNode, listType: ListType): ListNode {
|
||||||
const previousSibling = node.getPreviousSibling();
|
const previousSibling = node.getPreviousSibling();
|
||||||
const nextSibling = node.getNextSibling();
|
const nextSibling = node.getNextSibling();
|
||||||
const listItem = $createListItemNode();
|
const listItem = $createListItemNode();
|
||||||
listItem.setFormat(node.getFormatType());
|
|
||||||
listItem.setIndent(node.getIndent());
|
|
||||||
append(listItem, node.getChildren());
|
append(listItem, node.getChildren());
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -155,9 +155,6 @@ export class QuoteNode extends ElementNode {
|
||||||
if (this.isEmpty()) {
|
if (this.isEmpty()) {
|
||||||
element.append(document.createElement('br'));
|
element.append(document.createElement('br'));
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatType = this.getFormatType();
|
|
||||||
element.style.textAlign = formatType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -167,8 +164,6 @@ export class QuoteNode extends ElementNode {
|
||||||
|
|
||||||
static importJSON(serializedNode: SerializedQuoteNode): QuoteNode {
|
static importJSON(serializedNode: SerializedQuoteNode): QuoteNode {
|
||||||
const node = $createQuoteNode();
|
const node = $createQuoteNode();
|
||||||
node.setFormat(serializedNode.format);
|
|
||||||
node.setIndent(serializedNode.indent);
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,9 +310,6 @@ export class HeadingNode extends ElementNode {
|
||||||
if (this.isEmpty()) {
|
if (this.isEmpty()) {
|
||||||
element.append(document.createElement('br'));
|
element.append(document.createElement('br'));
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatType = this.getFormatType();
|
|
||||||
element.style.textAlign = formatType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -326,10 +318,7 @@ export class HeadingNode extends ElementNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
static importJSON(serializedNode: SerializedHeadingNode): HeadingNode {
|
static importJSON(serializedNode: SerializedHeadingNode): HeadingNode {
|
||||||
const node = $createHeadingNode(serializedNode.tag);
|
return $createHeadingNode(serializedNode.tag);
|
||||||
node.setFormat(serializedNode.format);
|
|
||||||
node.setIndent(serializedNode.indent);
|
|
||||||
return node;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exportJSON(): SerializedHeadingNode {
|
exportJSON(): SerializedHeadingNode {
|
||||||
|
@ -402,18 +391,12 @@ function $convertHeadingElement(element: HTMLElement): DOMConversionOutput {
|
||||||
nodeName === 'h6'
|
nodeName === 'h6'
|
||||||
) {
|
) {
|
||||||
node = $createHeadingNode(nodeName);
|
node = $createHeadingNode(nodeName);
|
||||||
if (element.style !== null) {
|
|
||||||
node.setFormat(element.style.textAlign as ElementFormatType);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return {node};
|
return {node};
|
||||||
}
|
}
|
||||||
|
|
||||||
function $convertBlockquoteElement(element: HTMLElement): DOMConversionOutput {
|
function $convertBlockquoteElement(element: HTMLElement): DOMConversionOutput {
|
||||||
const node = $createQuoteNode();
|
const node = $createQuoteNode();
|
||||||
if (element.style !== null) {
|
|
||||||
node.setFormat(element.style.textAlign as ElementFormatType);
|
|
||||||
}
|
|
||||||
return {node};
|
return {node};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -651,9 +634,6 @@ export function registerRichText(editor: LexicalEditor): () => void {
|
||||||
(parentNode): parentNode is ElementNode =>
|
(parentNode): parentNode is ElementNode =>
|
||||||
$isElementNode(parentNode) && !parentNode.isInline(),
|
$isElementNode(parentNode) && !parentNode.isInline(),
|
||||||
);
|
);
|
||||||
if (element !== null) {
|
|
||||||
element.setFormat(format);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
@ -691,28 +671,6 @@ export function registerRichText(editor: LexicalEditor): () => void {
|
||||||
},
|
},
|
||||||
COMMAND_PRIORITY_EDITOR,
|
COMMAND_PRIORITY_EDITOR,
|
||||||
),
|
),
|
||||||
editor.registerCommand(
|
|
||||||
INDENT_CONTENT_COMMAND,
|
|
||||||
() => {
|
|
||||||
return $handleIndentAndOutdent((block) => {
|
|
||||||
const indent = block.getIndent();
|
|
||||||
block.setIndent(indent + 1);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
COMMAND_PRIORITY_EDITOR,
|
|
||||||
),
|
|
||||||
editor.registerCommand(
|
|
||||||
OUTDENT_CONTENT_COMMAND,
|
|
||||||
() => {
|
|
||||||
return $handleIndentAndOutdent((block) => {
|
|
||||||
const indent = block.getIndent();
|
|
||||||
if (indent > 0) {
|
|
||||||
block.setIndent(indent - 1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
COMMAND_PRIORITY_EDITOR,
|
|
||||||
),
|
|
||||||
editor.registerCommand<KeyboardEvent>(
|
editor.registerCommand<KeyboardEvent>(
|
||||||
KEY_ARROW_UP_COMMAND,
|
KEY_ARROW_UP_COMMAND,
|
||||||
(event) => {
|
(event) => {
|
||||||
|
@ -846,19 +804,7 @@ export function registerRichText(editor: LexicalEditor): () => void {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const {anchor} = selection;
|
|
||||||
const anchorNode = anchor.getNode();
|
|
||||||
|
|
||||||
if (
|
|
||||||
selection.isCollapsed() &&
|
|
||||||
anchor.offset === 0 &&
|
|
||||||
!$isRootNode(anchorNode)
|
|
||||||
) {
|
|
||||||
const element = $getNearestBlockElementAncestorOrThrow(anchorNode);
|
|
||||||
if (element.getIndent() > 0) {
|
|
||||||
return editor.dispatchCommand(OUTDENT_CONTENT_COMMAND, undefined);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return editor.dispatchCommand(DELETE_CHARACTER_COMMAND, true);
|
return editor.dispatchCommand(DELETE_CHARACTER_COMMAND, true);
|
||||||
},
|
},
|
||||||
COMMAND_PRIORITY_EDITOR,
|
COMMAND_PRIORITY_EDITOR,
|
||||||
|
|
|
@ -81,8 +81,6 @@ export function $setBlocksType(
|
||||||
invariant($isElementNode(node), 'Expected block node to be an ElementNode');
|
invariant($isElementNode(node), 'Expected block node to be an ElementNode');
|
||||||
|
|
||||||
const targetElement = createElement();
|
const targetElement = createElement();
|
||||||
targetElement.setFormat(node.getFormatType());
|
|
||||||
targetElement.setIndent(node.getIndent());
|
|
||||||
node.replace(targetElement, true);
|
node.replace(targetElement, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,8 +134,6 @@ export function $wrapNodes(
|
||||||
: anchor.getNode();
|
: anchor.getNode();
|
||||||
const children = target.getChildren();
|
const children = target.getChildren();
|
||||||
let element = createElement();
|
let element = createElement();
|
||||||
element.setFormat(target.getFormatType());
|
|
||||||
element.setIndent(target.getIndent());
|
|
||||||
children.forEach((child) => element.append(child));
|
children.forEach((child) => element.append(child));
|
||||||
|
|
||||||
if (wrappingElement) {
|
if (wrappingElement) {
|
||||||
|
@ -277,8 +273,6 @@ export function $wrapNodesImpl(
|
||||||
|
|
||||||
if (elementMapping.get(parentKey) === undefined) {
|
if (elementMapping.get(parentKey) === undefined) {
|
||||||
const targetElement = createElement();
|
const targetElement = createElement();
|
||||||
targetElement.setFormat(parent.getFormatType());
|
|
||||||
targetElement.setIndent(parent.getIndent());
|
|
||||||
elements.push(targetElement);
|
elements.push(targetElement);
|
||||||
elementMapping.set(parentKey, targetElement);
|
elementMapping.set(parentKey, targetElement);
|
||||||
// Move node and its siblings to the new
|
// Move node and its siblings to the new
|
||||||
|
@ -299,8 +293,6 @@ export function $wrapNodesImpl(
|
||||||
'Expected node in emptyElements to be an ElementNode',
|
'Expected node in emptyElements to be an ElementNode',
|
||||||
);
|
);
|
||||||
const targetElement = createElement();
|
const targetElement = createElement();
|
||||||
targetElement.setFormat(node.getFormatType());
|
|
||||||
targetElement.setIndent(node.getIndent());
|
|
||||||
elements.push(targetElement);
|
elements.push(targetElement);
|
||||||
node.remove(true);
|
node.remove(true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,123 +0,0 @@
|
||||||
import {
|
|
||||||
DOMConversion,
|
|
||||||
DOMConversionMap,
|
|
||||||
DOMConversionOutput,
|
|
||||||
LexicalNode,
|
|
||||||
ParagraphNode, SerializedParagraphNode, Spread,
|
|
||||||
} from "lexical";
|
|
||||||
import {EditorConfig} from "lexical/LexicalEditor";
|
|
||||||
import {
|
|
||||||
CommonBlockAlignment, commonPropertiesDifferent, deserializeCommonBlockNode,
|
|
||||||
SerializedCommonBlockNode,
|
|
||||||
setCommonBlockPropsFromElement,
|
|
||||||
updateElementWithCommonBlockProps
|
|
||||||
} from "./_common";
|
|
||||||
|
|
||||||
export type SerializedCustomParagraphNode = Spread<SerializedCommonBlockNode, SerializedParagraphNode>
|
|
||||||
|
|
||||||
export class CustomParagraphNode extends ParagraphNode {
|
|
||||||
__id: string = '';
|
|
||||||
__alignment: CommonBlockAlignment = '';
|
|
||||||
__inset: number = 0;
|
|
||||||
|
|
||||||
static getType() {
|
|
||||||
return 'custom-paragraph';
|
|
||||||
}
|
|
||||||
|
|
||||||
setId(id: string) {
|
|
||||||
const self = this.getWritable();
|
|
||||||
self.__id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
getId(): string {
|
|
||||||
const self = this.getLatest();
|
|
||||||
return self.__id;
|
|
||||||
}
|
|
||||||
|
|
||||||
setAlignment(alignment: CommonBlockAlignment) {
|
|
||||||
const self = this.getWritable();
|
|
||||||
self.__alignment = alignment;
|
|
||||||
}
|
|
||||||
|
|
||||||
getAlignment(): CommonBlockAlignment {
|
|
||||||
const self = this.getLatest();
|
|
||||||
return self.__alignment;
|
|
||||||
}
|
|
||||||
|
|
||||||
setInset(size: number) {
|
|
||||||
const self = this.getWritable();
|
|
||||||
self.__inset = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
getInset(): number {
|
|
||||||
const self = this.getLatest();
|
|
||||||
return self.__inset;
|
|
||||||
}
|
|
||||||
|
|
||||||
static clone(node: CustomParagraphNode): CustomParagraphNode {
|
|
||||||
const newNode = new CustomParagraphNode(node.__key);
|
|
||||||
newNode.__id = node.__id;
|
|
||||||
newNode.__alignment = node.__alignment;
|
|
||||||
newNode.__inset = node.__inset;
|
|
||||||
return newNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
createDOM(config: EditorConfig): HTMLElement {
|
|
||||||
const dom = super.createDOM(config);
|
|
||||||
updateElementWithCommonBlockProps(dom, this);
|
|
||||||
return dom;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateDOM(prevNode: CustomParagraphNode, dom: HTMLElement, config: EditorConfig): boolean {
|
|
||||||
return super.updateDOM(prevNode, dom, config)
|
|
||||||
|| commonPropertiesDifferent(prevNode, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
exportJSON(): SerializedCustomParagraphNode {
|
|
||||||
return {
|
|
||||||
...super.exportJSON(),
|
|
||||||
type: 'custom-paragraph',
|
|
||||||
version: 1,
|
|
||||||
id: this.__id,
|
|
||||||
alignment: this.__alignment,
|
|
||||||
inset: this.__inset,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static importJSON(serializedNode: SerializedCustomParagraphNode): CustomParagraphNode {
|
|
||||||
const node = $createCustomParagraphNode();
|
|
||||||
deserializeCommonBlockNode(serializedNode, node);
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
static importDOM(): DOMConversionMap|null {
|
|
||||||
return {
|
|
||||||
p(node: HTMLElement): DOMConversion|null {
|
|
||||||
return {
|
|
||||||
conversion: (element: HTMLElement): DOMConversionOutput|null => {
|
|
||||||
const node = $createCustomParagraphNode();
|
|
||||||
if (element.style.textIndent) {
|
|
||||||
const indent = parseInt(element.style.textIndent, 10) / 20;
|
|
||||||
if (indent > 0) {
|
|
||||||
node.setIndent(indent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setCommonBlockPropsFromElement(element, node);
|
|
||||||
|
|
||||||
return {node};
|
|
||||||
},
|
|
||||||
priority: 1,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function $createCustomParagraphNode(): CustomParagraphNode {
|
|
||||||
return new CustomParagraphNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function $isCustomParagraphNode(node: LexicalNode | null | undefined): node is CustomParagraphNode {
|
|
||||||
return node instanceof CustomParagraphNode;
|
|
||||||
}
|
|
|
@ -7,7 +7,6 @@ import {
|
||||||
LexicalNodeReplacement, NodeMutation,
|
LexicalNodeReplacement, NodeMutation,
|
||||||
ParagraphNode
|
ParagraphNode
|
||||||
} from "lexical";
|
} from "lexical";
|
||||||
import {CustomParagraphNode} from "./custom-paragraph";
|
|
||||||
import {LinkNode} from "@lexical/link";
|
import {LinkNode} from "@lexical/link";
|
||||||
import {ImageNode} from "./image";
|
import {ImageNode} from "./image";
|
||||||
import {DetailsNode, SummaryNode} from "./details";
|
import {DetailsNode, SummaryNode} from "./details";
|
||||||
|
@ -45,14 +44,8 @@ export function getNodesForPageEditor(): (KlassConstructor<typeof LexicalNode> |
|
||||||
CodeBlockNode,
|
CodeBlockNode,
|
||||||
DiagramNode,
|
DiagramNode,
|
||||||
MediaNode, // TODO - Alignment
|
MediaNode, // TODO - Alignment
|
||||||
CustomParagraphNode,
|
ParagraphNode,
|
||||||
LinkNode,
|
LinkNode,
|
||||||
{
|
|
||||||
replace: ParagraphNode,
|
|
||||||
with: (node: ParagraphNode) => {
|
|
||||||
return new CustomParagraphNode();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
replace: HeadingNode,
|
replace: HeadingNode,
|
||||||
with: (node: HeadingNode) => {
|
with: (node: HeadingNode) => {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
$createParagraphNode,
|
||||||
$insertNodes,
|
$insertNodes,
|
||||||
$isDecoratorNode, COMMAND_PRIORITY_HIGH, DROP_COMMAND,
|
$isDecoratorNode, COMMAND_PRIORITY_HIGH, DROP_COMMAND,
|
||||||
LexicalEditor,
|
LexicalEditor,
|
||||||
|
@ -8,7 +9,6 @@ import {$insertNewBlockNodesAtSelection, $selectSingleNode} from "../utils/selec
|
||||||
import {$getNearestBlockNodeForCoords, $htmlToBlockNodes} from "../utils/nodes";
|
import {$getNearestBlockNodeForCoords, $htmlToBlockNodes} from "../utils/nodes";
|
||||||
import {Clipboard} from "../../services/clipboard";
|
import {Clipboard} from "../../services/clipboard";
|
||||||
import {$createImageNode} from "../nodes/image";
|
import {$createImageNode} from "../nodes/image";
|
||||||
import {$createCustomParagraphNode} from "../nodes/custom-paragraph";
|
|
||||||
import {$createLinkNode} from "@lexical/link";
|
import {$createLinkNode} from "@lexical/link";
|
||||||
import {EditorImageData, uploadImageFile} from "../utils/images";
|
import {EditorImageData, uploadImageFile} from "../utils/images";
|
||||||
import {EditorUiContext} from "../ui/framework/core";
|
import {EditorUiContext} from "../ui/framework/core";
|
||||||
|
@ -67,7 +67,7 @@ function handleMediaInsert(data: DataTransfer, context: EditorUiContext): boolea
|
||||||
for (const imageFile of images) {
|
for (const imageFile of images) {
|
||||||
const loadingImage = window.baseUrl('/loading.gif');
|
const loadingImage = window.baseUrl('/loading.gif');
|
||||||
const loadingNode = $createImageNode(loadingImage);
|
const loadingNode = $createImageNode(loadingImage);
|
||||||
const imageWrap = $createCustomParagraphNode();
|
const imageWrap = $createParagraphNode();
|
||||||
imageWrap.append(loadingNode);
|
imageWrap.append(loadingNode);
|
||||||
$insertNodes([imageWrap]);
|
$insertNodes([imageWrap]);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import {EditorUiContext} from "../ui/framework/core";
|
import {EditorUiContext} from "../ui/framework/core";
|
||||||
import {
|
import {
|
||||||
|
$createParagraphNode,
|
||||||
$getSelection,
|
$getSelection,
|
||||||
$isDecoratorNode,
|
$isDecoratorNode,
|
||||||
COMMAND_PRIORITY_LOW,
|
COMMAND_PRIORITY_LOW,
|
||||||
|
@ -13,7 +14,6 @@ import {$isImageNode} from "../nodes/image";
|
||||||
import {$isMediaNode} from "../nodes/media";
|
import {$isMediaNode} from "../nodes/media";
|
||||||
import {getLastSelection} from "../utils/selection";
|
import {getLastSelection} from "../utils/selection";
|
||||||
import {$getNearestNodeBlockParent} from "../utils/nodes";
|
import {$getNearestNodeBlockParent} from "../utils/nodes";
|
||||||
import {$createCustomParagraphNode} from "../nodes/custom-paragraph";
|
|
||||||
import {$isCustomListItemNode} from "../nodes/custom-list-item";
|
import {$isCustomListItemNode} from "../nodes/custom-list-item";
|
||||||
import {$setInsetForSelection} from "../utils/lists";
|
import {$setInsetForSelection} from "../utils/lists";
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ function insertAfterSingleSelectedNode(editor: LexicalEditor, event: KeyboardEve
|
||||||
if (nearestBlock) {
|
if (nearestBlock) {
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
editor.update(() => {
|
editor.update(() => {
|
||||||
const newParagraph = $createCustomParagraphNode();
|
const newParagraph = $createParagraphNode();
|
||||||
nearestBlock.insertAfter(newParagraph);
|
nearestBlock.insertAfter(newParagraph);
|
||||||
newParagraph.select();
|
newParagraph.select();
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,7 +2,11 @@
|
||||||
|
|
||||||
## In progress
|
## In progress
|
||||||
|
|
||||||
//
|
Reorg
|
||||||
|
- Merge custom nodes into original nodes
|
||||||
|
- Reduce down to use CommonBlockNode where possible
|
||||||
|
- Remove existing formatType/ElementFormatType references (replaced with alignment).
|
||||||
|
- Remove existing indent references (replaced with inset).
|
||||||
|
|
||||||
## Main Todo
|
## Main Todo
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
import {$isQuoteNode, HeadingNode, HeadingTagType} from "@lexical/rich-text";
|
import {$isQuoteNode, HeadingNode, HeadingTagType} from "@lexical/rich-text";
|
||||||
import {$createTextNode, $getSelection, $insertNodes, LexicalEditor, LexicalNode} from "lexical";
|
import {
|
||||||
|
$createParagraphNode,
|
||||||
|
$createTextNode,
|
||||||
|
$getSelection,
|
||||||
|
$insertNodes,
|
||||||
|
$isParagraphNode,
|
||||||
|
LexicalEditor,
|
||||||
|
LexicalNode
|
||||||
|
} from "lexical";
|
||||||
import {
|
import {
|
||||||
$getBlockElementNodesInSelection,
|
$getBlockElementNodesInSelection,
|
||||||
$getNodeFromSelection,
|
$getNodeFromSelection,
|
||||||
|
@ -8,7 +16,6 @@ import {
|
||||||
getLastSelection
|
getLastSelection
|
||||||
} from "./selection";
|
} from "./selection";
|
||||||
import {$createCustomHeadingNode, $isCustomHeadingNode} from "../nodes/custom-heading";
|
import {$createCustomHeadingNode, $isCustomHeadingNode} from "../nodes/custom-heading";
|
||||||
import {$createCustomParagraphNode, $isCustomParagraphNode} from "../nodes/custom-paragraph";
|
|
||||||
import {$createCustomQuoteNode} from "../nodes/custom-quote";
|
import {$createCustomQuoteNode} from "../nodes/custom-quote";
|
||||||
import {$createCodeBlockNode, $isCodeBlockNode, $openCodeEditorForNode, CodeBlockNode} from "../nodes/code-block";
|
import {$createCodeBlockNode, $isCodeBlockNode, $openCodeEditorForNode, CodeBlockNode} from "../nodes/code-block";
|
||||||
import {$createCalloutNode, $isCalloutNode, CalloutCategory} from "../nodes/callout";
|
import {$createCalloutNode, $isCalloutNode, CalloutCategory} from "../nodes/callout";
|
||||||
|
@ -31,7 +38,7 @@ export function toggleSelectionAsHeading(editor: LexicalEditor, tag: HeadingTagT
|
||||||
|
|
||||||
export function toggleSelectionAsParagraph(editor: LexicalEditor) {
|
export function toggleSelectionAsParagraph(editor: LexicalEditor) {
|
||||||
editor.update(() => {
|
editor.update(() => {
|
||||||
$toggleSelectionBlockNodeType($isCustomParagraphNode, $createCustomParagraphNode);
|
$toggleSelectionBlockNodeType($isParagraphNode, $createParagraphNode);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
$createParagraphNode,
|
||||||
$getRoot,
|
$getRoot,
|
||||||
$isDecoratorNode,
|
$isDecoratorNode,
|
||||||
$isElementNode, $isRootNode,
|
$isElementNode, $isRootNode,
|
||||||
|
@ -8,7 +9,6 @@ import {
|
||||||
LexicalNode
|
LexicalNode
|
||||||
} from "lexical";
|
} from "lexical";
|
||||||
import {LexicalNodeMatcher} from "../nodes";
|
import {LexicalNodeMatcher} from "../nodes";
|
||||||
import {$createCustomParagraphNode} from "../nodes/custom-paragraph";
|
|
||||||
import {$generateNodesFromDOM} from "@lexical/html";
|
import {$generateNodesFromDOM} from "@lexical/html";
|
||||||
import {htmlToDom} from "./dom";
|
import {htmlToDom} from "./dom";
|
||||||
import {NodeHasAlignment, NodeHasInset} from "../nodes/_common";
|
import {NodeHasAlignment, NodeHasInset} from "../nodes/_common";
|
||||||
|
@ -17,7 +17,7 @@ import {$findMatchingParent} from "@lexical/utils";
|
||||||
function wrapTextNodes(nodes: LexicalNode[]): LexicalNode[] {
|
function wrapTextNodes(nodes: LexicalNode[]): LexicalNode[] {
|
||||||
return nodes.map(node => {
|
return nodes.map(node => {
|
||||||
if ($isTextNode(node)) {
|
if ($isTextNode(node)) {
|
||||||
const paragraph = $createCustomParagraphNode();
|
const paragraph = $createParagraphNode();
|
||||||
paragraph.append(node);
|
paragraph.append(node);
|
||||||
return paragraph;
|
return paragraph;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,17 +7,15 @@ import {
|
||||||
$isTextNode,
|
$isTextNode,
|
||||||
$setSelection,
|
$setSelection,
|
||||||
BaseSelection, DecoratorNode,
|
BaseSelection, DecoratorNode,
|
||||||
ElementFormatType,
|
|
||||||
ElementNode, LexicalEditor,
|
ElementNode, LexicalEditor,
|
||||||
LexicalNode,
|
LexicalNode,
|
||||||
TextFormatType, TextNode
|
TextFormatType, TextNode
|
||||||
} from "lexical";
|
} from "lexical";
|
||||||
import {$findMatchingParent, $getNearestBlockElementAncestorOrThrow} from "@lexical/utils";
|
import {$getNearestBlockElementAncestorOrThrow} from "@lexical/utils";
|
||||||
import {LexicalElementNodeCreator, LexicalNodeMatcher} from "../nodes";
|
import {LexicalElementNodeCreator, LexicalNodeMatcher} from "../nodes";
|
||||||
import {$setBlocksType} from "@lexical/selection";
|
import {$setBlocksType} from "@lexical/selection";
|
||||||
|
|
||||||
import {$getNearestNodeBlockParent, $getParentOfType, nodeHasAlignment} from "./nodes";
|
import {$getNearestNodeBlockParent, $getParentOfType, nodeHasAlignment} from "./nodes";
|
||||||
import {$createCustomParagraphNode} from "../nodes/custom-paragraph";
|
|
||||||
import {CommonBlockAlignment} from "../nodes/_common";
|
import {CommonBlockAlignment} from "../nodes/_common";
|
||||||
|
|
||||||
const lastSelectionByEditor = new WeakMap<LexicalEditor, BaseSelection|null>;
|
const lastSelectionByEditor = new WeakMap<LexicalEditor, BaseSelection|null>;
|
||||||
|
@ -71,7 +69,7 @@ export function $toggleSelectionBlockNodeType(matcher: LexicalNodeMatcher, creat
|
||||||
const selection = $getSelection();
|
const selection = $getSelection();
|
||||||
const blockElement = selection ? $getNearestBlockElementAncestorOrThrow(selection.getNodes()[0]) : null;
|
const blockElement = selection ? $getNearestBlockElementAncestorOrThrow(selection.getNodes()[0]) : null;
|
||||||
if (selection && matcher(blockElement)) {
|
if (selection && matcher(blockElement)) {
|
||||||
$setBlocksType(selection, $createCustomParagraphNode);
|
$setBlocksType(selection, $createParagraphNode);
|
||||||
} else {
|
} else {
|
||||||
$setBlocksType(selection, creator);
|
$setBlocksType(selection, creator);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue