facebook / lexical

Lexical is an extensible text editor framework that provides excellent reliability, accessibility and performance.
https://lexical.dev
MIT License
17.5k stars 1.45k forks source link

How to keep styles between lines? #5958

Open nickschinestzki opened 3 weeks ago

nickschinestzki commented 3 weeks ago

Lexical version: 0.14.5

The current behavior

As mentioned in #5620, I am looking for a way to keep both formats and styles between lines. There was an update in #5822 with logic to persist formats (bold, italic, underline) between paragraphs and it works great. One part of the issue remains that I can't persist CSS styles in new paragraphs.

So in my use case, if the user selects a "body" font family different than the default one, or maybe a different text color, he needs to reapply them in every new paragraph after an empty line (See #5620 to understand this).

The expected behavior

When user breaks no matter how much lines, the CSS styles applied to a paragraph persist between lines.

Attempt to fix the issue

As an attempt to address this, I have created a CustomParagraphNode which is generating a new performance problem.

import { $applyNodeReplacement, ParagraphNode } from 'lexical';
import { $patchStyleText } from '@lexical/selection';

export class CustomParagraphNode extends ParagraphNode {
  constructor(cssStyle, key) {
    super(key);
    this.__cssStyle = cssStyle;
  }
  static getType() {
    return 'custom-paragraph';
  }
  getCssStyle() {
    const self = this.getLatest();
    return self.__cssStyle;
  }
  setCssStyle(style) {
    const self = this.getWritable();
    self.__cssStyle = style;
    return self;
  }

  static clone(node) {
    return new CustomParagraphNode(node.__cssStyle, node.__key);
  }

  // View
  static importJSON(serializedNode) {
    const node = $createCustomParagraphNode(serializedNode.cssStyle);
    node.setFormat(serializedNode.format);
    node.setIndent(serializedNode.indent);
    node.setDirection(serializedNode.direction);
    node.setTextFormat(serializedNode.textFormat);
    node.setCssStyle(serializedNode.cssStyle);
    return node;
  }

  createDOM(config) {
    const element = super.createDOM(config);
    element.style.cssText = this.__cssStyle;
    return element;
  }

  exportJSON() {
    return {
      ...super.exportJSON(),
      cssStyle: this.getCssStyle(),
      textFormat: this.getTextFormat(),
      type: 'custom-paragraph',
      version: 1,
    };
  }

  // Mutation

  insertNewAfter(rangeSelection, restoreSelection) {
    const newElement = $createCustomParagraphNode(rangeSelection.style);
    newElement.setTextFormat(rangeSelection.format);
    newElement.setCssStyle(rangeSelection.style);
    const direction = this.getDirection();
    newElement.setDirection(direction);
    newElement.setFormat(this.getFormatType());
    this.insertAfter(newElement, restoreSelection);
    $patchStyleText(rangeSelection, rangeSelection.style);
    return newElement;
  }
}

function $createCustomParagraphNode(cssStyle = '') {
  return new CustomParagraphNode(cssStyle);
}

The $patchStyleText(rangeSelection, rangeSelection.style) line is what reinstate the selection to keep having the same style, but that creates a performance issue that after creating some lines, the application will totally freeze.

ivailop7 commented 3 weeks ago

Have you tried using the example from the docs for the extendedTextNode: https://lexical.dev/docs/concepts/serialization#handling-extended-html-styling

nickschinestzki commented 2 weeks ago

This doesn't seem to solve the issue. I'm now trying to create some solution to apply the new "cssStyle" parameter that I created on CustomParagraphNode to apply to this ExtendedTextNode on creation, but I don't know if that's the right way of doing it.

nickschinestzki commented 1 week ago

Hey @trueadm is it possible to give some attention to this issue? It seems like an important issue from a use-case and developers demand point of view. There's a discussion on discord about this: https://discord.com/channels/953974421008293909/1210630834458206312

moy2010 commented 1 week ago

Hey @trueadm is it possible to give some attention to this issue? It seems like an important issue from a use-case and developers demand point of view. There's a discussion on discord about this: https://discord.com/channels/953974421008293909/1210630834458206312

Trueadm is no longer working on Lexical.

Regarding your use case, I think that a PR was recently merged that should help. Not sure if it has already been released or not.

nickschinestzki commented 1 week ago

Hey @trueadm is it possible to give some attention to this issue? It seems like an important issue from a use-case and developers demand point of view. There's a discussion on discord about this: https://discord.com/channels/953974421008293909/1210630834458206312

Trueadm is no longer working on Lexical.

Regarding your use case, I think that a PR was recently merged that should help. Not sure if it has already been released or not.

Are you talking about this PR? It certainly is an advance on this matter. But the issue is that it only persists the formatting (bold, italic, underline, etc) between paragraphs. Any very common use cases of persisting styles (font-family, color, font-size, etc) will be lost between spaced paragraphs.

moy2010 commented 1 week ago

Are you talking about this PR?

Yep, that one. Now I see that it doesn't maintain the styles across paragraphs, thanks for pointing that out.