Skip to content

Commit

Permalink
revert unused changes
Browse files Browse the repository at this point in the history
  • Loading branch information
GermanJablo committed Dec 19, 2024
1 parent 0372f9e commit ba2f11d
Show file tree
Hide file tree
Showing 9 changed files with 17 additions and 233 deletions.
27 changes: 12 additions & 15 deletions packages/lexical-history/src/__tests__/unit/LexicalHistory.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,52 +44,49 @@ import {createRoot, Root} from 'react-dom/client';
import * as ReactTestUtils from 'shared/react-test-utils';

type SerializedCustomTextNode = Spread<
{type: ReturnType<typeof CustomTextNode.getType>; classList: string[]},
{type: ReturnType<typeof CustomTextNode.getType>; classes: string[]},
SerializedTextNode
>;

class CustomTextNode extends TextNode {
['constructor']!: KlassConstructor<typeof CustomTextNode>;

__classlist: Set<string>;
__classes: Set<string>;
constructor(text: string, classes: Iterable<string>, key?: NodeKey) {
super(text, key);
this.__classlist = new Set(classes);
this.__classes = new Set(classes);
}
static getType(): 'custom-text' {
return 'custom-text';
}
static clone(node: CustomTextNode): CustomTextNode {
return new CustomTextNode(node.__text, node.__classlist, node.__key);
return new CustomTextNode(node.__text, node.__classes, node.__key);
}
addClass(className: string): this {
const self = this.getWritable();
self.__classlist.add(className);
self.__classes.add(className);
return self;
}
removeClass(className: string): this {
const self = this.getWritable();
self.__classlist.delete(className);
self.__classes.delete(className);
return self;
}
setClasses(classes: Iterable<string>): this {
const self = this.getWritable();
self.__classlist = new Set(classes);
self.__classes = new Set(classes);
return self;
}
getClassList(): ReadonlySet<string> {
return this.getLatest().__classlist;
getClasses(): ReadonlySet<string> {
return this.getLatest().__classes;
}
static importJSON({
text,
classList,
}: SerializedCustomTextNode): CustomTextNode {
return $createCustomTextNode(text, classList);
static importJSON({text, classes}: SerializedCustomTextNode): CustomTextNode {
return $createCustomTextNode(text, classes);
}
exportJSON(): SerializedCustomTextNode {
return {
...super.exportJSON(),
classList: Array.from(this.getClassList()),
classes: Array.from(this.getClasses()),
type: this.constructor.getType(),
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ import {
TestDecoratorNode,
} from 'lexical/src/__tests__/utils';

import {$forEachSelectedTextNode} from '../../lexical-node';
import {$setAnchorPoint, $setFocusPoint} from '../utils';

Range.prototype.getBoundingClientRect = function (): DOMRect {
Expand Down Expand Up @@ -3172,84 +3171,3 @@ describe('$patchStyleText', () => {
});
});
});

describe('classes property', () => {
test('can mutate classes using $forEachSelectedTextNode', async () => {
const editor = createTestEditor();
const element = document.createElement('div');
editor.setRootElement(element);

await editor.update(() => {
const root = $getRoot();
const paragraph = $createParagraphNode();
root.append(paragraph);
const text = $createTextNode('first').setFormat('bold');
paragraph.append(text);

const textSecond = $createTextNode('second');
paragraph.append(textSecond);

$setAnchorPoint({
key: text.getKey(),
offset: 'fir'.length,
type: 'text',
});

$setFocusPoint({
key: textSecond.getKey(),
offset: 'sec'.length,
type: 'text',
});

$forEachSelectedTextNode((textNode) => {
textNode.setClass('bg', 'red');
textNode.setClass('active', true);
textNode.setClass('highlight', 'yellow');
textNode.setClass('disabled', false);
});
});

expect(element.innerHTML).toBe(
'<p dir="ltr">' +
'<strong data-lexical-text="true">fir</strong>' +
'<strong class="bg-red active highlight-yellow" data-lexical-text="true">st</strong>' +
'<span class="bg-red active highlight-yellow" data-lexical-text="true">sec</span>' +
'<span data-lexical-text="true">ond</span>' +
'</p>',
);
});
test('exportJSON', async () => {
const editor = createTestEditor();
const element = document.createElement('div');
editor.setRootElement(element);
const getSerializedParagraph = (_editor: LexicalEditor) => {
return _editor.getEditorState().toJSON().root.children[0];
};
let p: ParagraphNode;

await editor.update(() => {
p = $createParagraphNode();
$getRoot().append(p);
});
expect('classes' in getSerializedParagraph(editor)).toBe(false);

// should ignore false, numbers or undefined
await editor.update(() => {
p.setClass('bg', 'red');
p.setClass('active', true);
p.setClass('highlight', false);
});
expect('classes' in getSerializedParagraph(editor)).toBe(true);
expect(getSerializedParagraph(editor).classes).toStrictEqual({
active: true,
bg: 'red',
});

// should not export classes if empty
await editor.update(() => {
p.setClass('bg', false);
p.setClass('active', false);
});
expect('classes' in getSerializedParagraph(editor)).toBe(false);
});
});
38 changes: 2 additions & 36 deletions packages/lexical-website/docs/concepts/node-replacement.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,12 @@

# Node Customization

Originally the only way to customize nodes was using the node replacement API. Recently we have introduced a second way with the `classes` property which is easier to implement and is sufficient for most cases.

## Classes Property (New)

Most of the time when users want to customize a node they just want to add a property to it, which ends up being reflected as a class in the dom element.

To satisfy that need we have introduced two new methods to all nodes: `getClasses` and `setClass`.

```ts
export function CoolRedPlugin() {
const [editor] = useLexicalComposerContext();

return (
<button
onClick={() => {
editor.update(() => {
$forEachSelectedTextNode((textNode) => {
// setClass mutates the classes object where the key-value pairs follow the
// format prefix-suffix for string values, or just prefix for boolean values.
textNode.setClass('bg', 'red'); // adds the class bg-red
// Perhaps you don't want to allow the same node to have
// both text and background color defined at the same time...
textNode.setClass('text', false); // ...so here you remove the class text-[color].
textNode.setClass('cool', true); // adds the class cool (true values don't add a suffix)
});
});
}}>
Make text red and cool
</button>
);
}
```

## Node Overrides / Node Replacements
# Node Overrides / Node Replacements

Some of the most commonly used Lexical Nodes are owned and maintained by the core library. For example, ParagraphNode, HeadingNode, QuoteNode, List(Item)Node etc - these are all provided by Lexical packages, which provides an easier out-of-the-box experience for some editor features, but makes it difficult to override their behavior. For instance, if you wanted to change the behavior of ListNode, you would typically extend the class and override the methods. However, how would you tell Lexical to use *your* ListNode subclass in the ListPlugin instead of using the core ListNode? That's where Node Overrides can help.

Node Overrides allow you to replace all instances of a given node in your editor with instances of a different node class. This can be done through the nodes array in the Editor config:

```ts
```
const editorConfig = {
...
nodes=[
Expand Down
3 changes: 0 additions & 3 deletions packages/lexical/src/LexicalEditorState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,6 @@ function exportNodeToJSON<SerializedNode extends SerializedLexicalNode>(
node: LexicalNode,
): SerializedNode {
const serializedNode = node.exportJSON();
if (node.__classes && Object.keys(node.__classes).length > 0) {
serializedNode.classes = node.__classes;
}
const nodeClass = node.constructor;

if (serializedNode.type !== nodeClass.getType()) {
Expand Down
60 changes: 0 additions & 60 deletions packages/lexical/src/LexicalNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ export type NodeMap = Map<NodeKey, LexicalNode>;
export type SerializedLexicalNode = {
type: string;
version: number;
classes?: ReadonlyClasses;
};

/** @internal */
Expand Down Expand Up @@ -171,8 +170,6 @@ export type DOMExportOutput = {
};

export type NodeKey = string;
export type MutableClasses = {[classSuffix: string]: true | string};
export type ReadonlyClasses = {readonly [classSuffix: string]: true | string};

export class LexicalNode {
// Allow us to look up the type including static props
Expand All @@ -188,53 +185,6 @@ export class LexicalNode {
__prev: null | NodeKey;
/** @internal */
__next: null | NodeKey;
/**
* Don't use this directly, use `this.getClasses()` and `this.mutateClasses()` instead
* @internal
*/
__classes?: ReadonlyClasses;

/**
* Returns an object of classes in the form of `prefix-suffix` for string values, or just `prefix` for true boolean values.
* @example
* const exampleClassesObject = {
* bg: 'red', // the node is rendered with class `bg-red`
* text: 'green', // node is rendered with class `text-green`,
* active: true, // node is rendered with class `active`,
* }
* // Resulting classes: 'bg-red', 'text-green', and 'active'
*
* @returns The classes object.
*/
getClasses() {
const self = this.getLatest();
return self.__classes;
}

/**
* Sets a class based on the provided key and value.
*
* @param key - The class key to set or modify.
* @param value - The class value. Possible options:
* - `true`: add the class if it doesn't exist.
* - `false`: remove the class if it exists.
* - `string`: Adds a class in the format `key-value` (e.g., `bg-blue`), useful for overwriting existing classes with the same key.
*
* @example
* node.setClass('active', true); // Adds the class `active`, replacing the one with the key `active` if it exists.
* node.setClass('active', false); // Removes any class with the key `active` (e.g., `active` or `active-blue`).
* node.setClass('bg', 'blue'); // Adds the class `bg-blue`
*/
setClass(key: string, value: boolean | string) {
const self = this.getWritable();
const classes = {...self.__classes};
if (value === false) {
delete classes[key];
} else {
classes[key] = value;
}
self.__classes = classes;
}

// Flow doesn't support abstract classes unfortunately, so we can't _force_
// subclasses of Node to implement statics. All subclasses of Node should have
Expand Down Expand Up @@ -323,7 +273,6 @@ export class LexicalNode {
this.__parent = prevNode.__parent;
this.__next = prevNode.__next;
this.__prev = prevNode.__prev;
this.__classes = prevNode.__classes;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down Expand Up @@ -909,15 +858,6 @@ export class LexicalNode {
* */
exportDOM(editor: LexicalEditor): DOMExportOutput {
const element = this.createDOM(editor._config, editor);
if (this.__classes) {
Object.entries(this.__classes).forEach(([classPrefix, classSufix]) => {
if (typeof classSufix === 'string') {
element.classList.add(`${classPrefix}-${classSufix}`);
} else if (typeof classSufix === 'boolean' && classSufix) {
element.classList.add(classPrefix);
}
});
}
return {element};
}

Expand Down
8 changes: 1 addition & 7 deletions packages/lexical/src/LexicalNormalization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,10 @@ function $canSimpleTextNodesBeMerged(
const node2Mode = node2.__mode;
const node2Format = node2.__format;
const node2Style = node2.__style;
const node1Classes = node1.__classes || {};
const node2Classes = node2.__classes || {};
return (
(node1Mode === null || node1Mode === node2Mode) &&
(node1Format === null || node1Format === node2Format) &&
(node1Style === null || node1Style === node2Style) &&
Object.keys(node1Classes).length === Object.keys(node2Classes).length &&
Object.keys(node1Classes).every(
(key) => node1Classes[key] === node2Classes[key],
)
(node1Style === null || node1Style === node2Style)
);
}

Expand Down
19 changes: 1 addition & 18 deletions packages/lexical/src/LexicalReconciler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,15 +173,6 @@ function $createNode(key: NodeKey, slot: ElementDOMSlot | null): HTMLElement {
invariant(false, 'createNode: node does not exist in nodeMap');
}
const dom = node.createDOM(activeEditorConfig, activeEditor);
if (node.__classes) {
Object.entries(node.__classes).forEach(([classPrefix, classSufix]) => {
if (typeof classSufix === 'string') {
dom.classList.add(`${classPrefix}-${classSufix}`);
} else if (typeof classSufix === 'boolean' && classSufix) {
dom.classList.add(classPrefix);
}
});
}
storeDOMWithKey(key, dom, activeEditor);

// This helps preserve the text, and stops spell check tools from
Expand Down Expand Up @@ -641,16 +632,8 @@ function $reconcileNode(
);
}

const nextClasses = nextNode.__classes || {};
const prevClasses = prevNode.__classes || {};
const classesChanged = !(
Object.keys(nextClasses).length === Object.keys(prevClasses).length &&
Object.keys(nextClasses).every(
(_key) => nextClasses[_key] === prevClasses[_key],
)
);
// Update node. If it returns true, we need to unmount and re-create the node
if (nextNode.updateDOM(prevNode, dom, activeEditorConfig) || classesChanged) {
if (nextNode.updateDOM(prevNode, dom, activeEditorConfig)) {
const replacementDOM = $createNode(key, null);

if (parentDOM === null) {
Expand Down
10 changes: 1 addition & 9 deletions packages/lexical/src/LexicalUpdates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@
*/

import type {SerializedEditorState} from './LexicalEditorState';
import type {
LexicalNode,
ReadonlyClasses,
SerializedLexicalNode,
} from './LexicalNode';
import type {LexicalNode, SerializedLexicalNode} from './LexicalNode';

import invariant from 'shared/invariant';

Expand Down Expand Up @@ -342,7 +338,6 @@ type InternalSerializedNode = {
children?: Array<InternalSerializedNode>;
type: string;
version: number;
classes?: ReadonlyClasses;
};

export function $parseSerializedNode(
Expand Down Expand Up @@ -379,9 +374,6 @@ function $parseSerializedNodeImpl<
}

const node = nodeClass.importJSON(serializedNode);
if (serializedNode.classes) {
node.__classes = serializedNode.classes;
}
const children = serializedNode.children;

if ($isElementNode(node) && Array.isArray(children)) {
Expand Down
Loading

0 comments on commit ba2f11d

Please sign in to comment.