From a3ffc12f3a32e372bfe2409c11f3c676d5817f31 Mon Sep 17 00:00:00 2001 From: Tiscs Date: Tue, 4 Mar 2025 14:09:55 +0800 Subject: [PATCH] [lexical] Feature: Add listStyleType support to ListNode for customizable list styles --- packages/lexical-list/src/LexicalListNode.ts | 53 +++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/packages/lexical-list/src/LexicalListNode.ts b/packages/lexical-list/src/LexicalListNode.ts index 6fdab61c6b1..6b85a8618e4 100644 --- a/packages/lexical-list/src/LexicalListNode.ts +++ b/packages/lexical-list/src/LexicalListNode.ts @@ -41,6 +41,7 @@ import {$getListDepth, $wrapInListItem} from './utils'; export type SerializedListNode = Spread< { listType: ListType; + listStyleType?: ListStyleType; start: number; tag: ListNodeTagType; }, @@ -49,6 +50,26 @@ export type SerializedListNode = Spread< export type ListType = 'number' | 'bullet' | 'check'; +export type ListStyleType = + | undefined // default + | (string & object) // string means custom style + | 'none' + | 'disc' + | 'circle' + | 'square' + | 'decimal' + | 'decimal-leading-zero' + | 'lower-roman' + | 'upper-roman' + | 'lower-greek' + | 'lower-alpha' + | 'lower-latin' + | 'upper-alpha' + | 'upper-latin' + | 'armenian' + | 'georgian' + | 'hebrew'; + export type ListNodeTagType = 'ul' | 'ol'; /** @noInheritDoc */ @@ -59,6 +80,8 @@ export class ListNode extends ElementNode { __start: number; /** @internal */ __listType: ListType; + /** @internal */ + __listStyleType?: ListStyleType; static getType(): string { return 'list'; @@ -70,11 +93,17 @@ export class ListNode extends ElementNode { return new ListNode(listType, node.__start, node.__key); } - constructor(listType: ListType = 'number', start: number = 1, key?: NodeKey) { + constructor( + listType: ListType = 'number', + start: number = 1, + key?: NodeKey, + listStyleType?: ListStyleType, + ) { super(key); const _listType = TAG_TO_LIST_TYPE[listType] || listType; this.__listType = _listType; this.__tag = _listType === 'number' ? 'ol' : 'ul'; + this.__listStyleType = listStyleType; this.__start = start; } @@ -93,6 +122,16 @@ export class ListNode extends ElementNode { return this.__listType; } + setListStyleType(type: ListStyleType): this { + const writable = this.getWritable(); + writable.__listStyleType = type; + return writable; + } + + getListStyleType(): ListStyleType { + return this.__listStyleType; + } + getStart(): number { return this.__start; } @@ -114,13 +153,21 @@ export class ListNode extends ElementNode { } // @ts-expect-error Internal field. dom.__lexicalListType = this.__listType; + if (this.__listStyleType) { + dom.style.setProperty('list-style-type', this.__listStyleType); + } else { + dom.style.removeProperty('list-style-type'); + } $setListThemeClassNames(dom, config.theme, this); return dom; } updateDOM(prevNode: this, dom: HTMLElement, config: EditorConfig): boolean { - if (prevNode.__tag !== this.__tag) { + if ( + prevNode.__tag !== this.__tag || + prevNode.__listStyleType !== this.__listStyleType + ) { return true; } @@ -158,6 +205,7 @@ export class ListNode extends ElementNode { return super .updateFromJSON(serializedNode) .setListType(serializedNode.listType) + .setListStyleType(serializedNode.listStyleType) .setStart(serializedNode.start); } @@ -179,6 +227,7 @@ export class ListNode extends ElementNode { exportJSON(): SerializedListNode { return { ...super.exportJSON(), + listStyleType: this.getListStyleType(), listType: this.getListType(), start: this.getStart(), tag: this.getTag(),