mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
Lexical: Added block indenting capability
Needed a custom implementation due to hardcoded defaults for Lexical default indenting.
This commit is contained in:
parent
2036438203
commit
5083188ed8
@ -1,5 +1,6 @@
|
|||||||
import {LexicalNode, Spread} from "lexical";
|
import {LexicalNode, Spread} from "lexical";
|
||||||
import type {SerializedElementNode} from "lexical/nodes/LexicalElementNode";
|
import type {SerializedElementNode} from "lexical/nodes/LexicalElementNode";
|
||||||
|
import {sizeToPixels} from "../utils/dom";
|
||||||
|
|
||||||
export type CommonBlockAlignment = 'left' | 'right' | 'center' | 'justify' | '';
|
export type CommonBlockAlignment = 'left' | 'right' | 'center' | 'justify' | '';
|
||||||
const validAlignments: CommonBlockAlignment[] = ['left', 'right', 'center', 'justify'];
|
const validAlignments: CommonBlockAlignment[] = ['left', 'right', 'center', 'justify'];
|
||||||
@ -7,6 +8,7 @@ const validAlignments: CommonBlockAlignment[] = ['left', 'right', 'center', 'jus
|
|||||||
export type SerializedCommonBlockNode = Spread<{
|
export type SerializedCommonBlockNode = Spread<{
|
||||||
id: string;
|
id: string;
|
||||||
alignment: CommonBlockAlignment;
|
alignment: CommonBlockAlignment;
|
||||||
|
inset: number;
|
||||||
}, SerializedElementNode>
|
}, SerializedElementNode>
|
||||||
|
|
||||||
export interface NodeHasAlignment {
|
export interface NodeHasAlignment {
|
||||||
@ -21,7 +23,13 @@ export interface NodeHasId {
|
|||||||
getId(): string;
|
getId(): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CommonBlockInterface extends NodeHasId, NodeHasAlignment {}
|
export interface NodeHasInset {
|
||||||
|
readonly __inset: number;
|
||||||
|
setInset(inset: number): void;
|
||||||
|
getInset(): number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CommonBlockInterface extends NodeHasId, NodeHasAlignment, NodeHasInset {}
|
||||||
|
|
||||||
export function extractAlignmentFromElement(element: HTMLElement): CommonBlockAlignment {
|
export function extractAlignmentFromElement(element: HTMLElement): CommonBlockAlignment {
|
||||||
const textAlignStyle: string = element.style.textAlign || '';
|
const textAlignStyle: string = element.style.textAlign || '';
|
||||||
@ -42,17 +50,24 @@ export function extractAlignmentFromElement(element: HTMLElement): CommonBlockAl
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function extractInsetFromElement(element: HTMLElement): number {
|
||||||
|
const elemPadding: string = element.style.paddingLeft || '0';
|
||||||
|
return sizeToPixels(elemPadding);
|
||||||
|
}
|
||||||
|
|
||||||
export function setCommonBlockPropsFromElement(element: HTMLElement, node: CommonBlockInterface): void {
|
export function setCommonBlockPropsFromElement(element: HTMLElement, node: CommonBlockInterface): void {
|
||||||
if (element.id) {
|
if (element.id) {
|
||||||
node.setId(element.id);
|
node.setId(element.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
node.setAlignment(extractAlignmentFromElement(element));
|
node.setAlignment(extractAlignmentFromElement(element));
|
||||||
|
node.setInset(extractInsetFromElement(element));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function commonPropertiesDifferent(nodeA: CommonBlockInterface, nodeB: CommonBlockInterface): boolean {
|
export function commonPropertiesDifferent(nodeA: CommonBlockInterface, nodeB: CommonBlockInterface): boolean {
|
||||||
return nodeA.__id !== nodeB.__id ||
|
return nodeA.__id !== nodeB.__id ||
|
||||||
nodeA.__alignment !== nodeB.__alignment;
|
nodeA.__alignment !== nodeB.__alignment ||
|
||||||
|
nodeA.__inset !== nodeB.__inset;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateElementWithCommonBlockProps(element: HTMLElement, node: CommonBlockInterface): void {
|
export function updateElementWithCommonBlockProps(element: HTMLElement, node: CommonBlockInterface): void {
|
||||||
@ -63,6 +78,16 @@ export function updateElementWithCommonBlockProps(element: HTMLElement, node: Co
|
|||||||
if (node.__alignment) {
|
if (node.__alignment) {
|
||||||
element.classList.add('align-' + node.__alignment);
|
element.classList.add('align-' + node.__alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (node.__inset) {
|
||||||
|
element.style.paddingLeft = `${node.__inset}px`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deserializeCommonBlockNode(serializedNode: SerializedCommonBlockNode, node: CommonBlockInterface): void {
|
||||||
|
node.setId(serializedNode.id);
|
||||||
|
node.setAlignment(serializedNode.alignment);
|
||||||
|
node.setInset(serializedNode.inset);
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NodeHasSize {
|
export interface NodeHasSize {
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
import type {EditorConfig} from "lexical/LexicalEditor";
|
import type {EditorConfig} from "lexical/LexicalEditor";
|
||||||
import type {RangeSelection} from "lexical/LexicalSelection";
|
import type {RangeSelection} from "lexical/LexicalSelection";
|
||||||
import {
|
import {
|
||||||
CommonBlockAlignment, commonPropertiesDifferent,
|
CommonBlockAlignment, commonPropertiesDifferent, deserializeCommonBlockNode,
|
||||||
SerializedCommonBlockNode,
|
SerializedCommonBlockNode,
|
||||||
setCommonBlockPropsFromElement,
|
setCommonBlockPropsFromElement,
|
||||||
updateElementWithCommonBlockProps
|
updateElementWithCommonBlockProps
|
||||||
@ -26,6 +26,7 @@ export class CalloutNode extends ElementNode {
|
|||||||
__id: string = '';
|
__id: string = '';
|
||||||
__category: CalloutCategory = 'info';
|
__category: CalloutCategory = 'info';
|
||||||
__alignment: CommonBlockAlignment = '';
|
__alignment: CommonBlockAlignment = '';
|
||||||
|
__inset: number = 0;
|
||||||
|
|
||||||
static getType() {
|
static getType() {
|
||||||
return 'callout';
|
return 'callout';
|
||||||
@ -35,6 +36,7 @@ export class CalloutNode extends ElementNode {
|
|||||||
const newNode = new CalloutNode(node.__category, node.__key);
|
const newNode = new CalloutNode(node.__category, node.__key);
|
||||||
newNode.__id = node.__id;
|
newNode.__id = node.__id;
|
||||||
newNode.__alignment = node.__alignment;
|
newNode.__alignment = node.__alignment;
|
||||||
|
newNode.__inset = node.__inset;
|
||||||
return newNode;
|
return newNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,6 +75,16 @@ export class CalloutNode extends ElementNode {
|
|||||||
return self.__alignment;
|
return self.__alignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setInset(size: number) {
|
||||||
|
const self = this.getWritable();
|
||||||
|
self.__inset = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
getInset(): number {
|
||||||
|
const self = this.getLatest();
|
||||||
|
return self.__inset;
|
||||||
|
}
|
||||||
|
|
||||||
createDOM(_config: EditorConfig, _editor: LexicalEditor) {
|
createDOM(_config: EditorConfig, _editor: LexicalEditor) {
|
||||||
const element = document.createElement('p');
|
const element = document.createElement('p');
|
||||||
element.classList.add('callout', this.__category || '');
|
element.classList.add('callout', this.__category || '');
|
||||||
@ -141,13 +153,13 @@ export class CalloutNode extends ElementNode {
|
|||||||
category: this.__category,
|
category: this.__category,
|
||||||
id: this.__id,
|
id: this.__id,
|
||||||
alignment: this.__alignment,
|
alignment: this.__alignment,
|
||||||
|
inset: this.__inset,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static importJSON(serializedNode: SerializedCalloutNode): CalloutNode {
|
static importJSON(serializedNode: SerializedCalloutNode): CalloutNode {
|
||||||
const node = $createCalloutNode(serializedNode.category);
|
const node = $createCalloutNode(serializedNode.category);
|
||||||
node.setId(serializedNode.id);
|
deserializeCommonBlockNode(serializedNode, node);
|
||||||
node.setAlignment(serializedNode.alignment);
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
import {EditorConfig} from "lexical/LexicalEditor";
|
import {EditorConfig} from "lexical/LexicalEditor";
|
||||||
import {HeadingNode, HeadingTagType, SerializedHeadingNode} from "@lexical/rich-text";
|
import {HeadingNode, HeadingTagType, SerializedHeadingNode} from "@lexical/rich-text";
|
||||||
import {
|
import {
|
||||||
CommonBlockAlignment, commonPropertiesDifferent,
|
CommonBlockAlignment, commonPropertiesDifferent, deserializeCommonBlockNode,
|
||||||
SerializedCommonBlockNode,
|
SerializedCommonBlockNode,
|
||||||
setCommonBlockPropsFromElement,
|
setCommonBlockPropsFromElement,
|
||||||
updateElementWithCommonBlockProps
|
updateElementWithCommonBlockProps
|
||||||
@ -19,6 +19,7 @@ export type SerializedCustomHeadingNode = Spread<SerializedCommonBlockNode, Seri
|
|||||||
export class CustomHeadingNode extends HeadingNode {
|
export class CustomHeadingNode extends HeadingNode {
|
||||||
__id: string = '';
|
__id: string = '';
|
||||||
__alignment: CommonBlockAlignment = '';
|
__alignment: CommonBlockAlignment = '';
|
||||||
|
__inset: number = 0;
|
||||||
|
|
||||||
static getType() {
|
static getType() {
|
||||||
return 'custom-heading';
|
return 'custom-heading';
|
||||||
@ -44,9 +45,20 @@ export class CustomHeadingNode extends HeadingNode {
|
|||||||
return self.__alignment;
|
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: CustomHeadingNode) {
|
static clone(node: CustomHeadingNode) {
|
||||||
const newNode = new CustomHeadingNode(node.__tag, node.__key);
|
const newNode = new CustomHeadingNode(node.__tag, node.__key);
|
||||||
newNode.__alignment = node.__alignment;
|
newNode.__alignment = node.__alignment;
|
||||||
|
newNode.__inset = node.__inset;
|
||||||
return newNode;
|
return newNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,13 +80,13 @@ export class CustomHeadingNode extends HeadingNode {
|
|||||||
version: 1,
|
version: 1,
|
||||||
id: this.__id,
|
id: this.__id,
|
||||||
alignment: this.__alignment,
|
alignment: this.__alignment,
|
||||||
|
inset: this.__inset,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static importJSON(serializedNode: SerializedCustomHeadingNode): CustomHeadingNode {
|
static importJSON(serializedNode: SerializedCustomHeadingNode): CustomHeadingNode {
|
||||||
const node = $createCustomHeadingNode(serializedNode.tag);
|
const node = $createCustomHeadingNode(serializedNode.tag);
|
||||||
node.setId(serializedNode.id);
|
deserializeCommonBlockNode(serializedNode, node);
|
||||||
node.setAlignment(serializedNode.alignment);
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
} from "lexical";
|
} from "lexical";
|
||||||
import {EditorConfig} from "lexical/LexicalEditor";
|
import {EditorConfig} from "lexical/LexicalEditor";
|
||||||
import {
|
import {
|
||||||
CommonBlockAlignment, commonPropertiesDifferent,
|
CommonBlockAlignment, commonPropertiesDifferent, deserializeCommonBlockNode,
|
||||||
SerializedCommonBlockNode,
|
SerializedCommonBlockNode,
|
||||||
setCommonBlockPropsFromElement,
|
setCommonBlockPropsFromElement,
|
||||||
updateElementWithCommonBlockProps
|
updateElementWithCommonBlockProps
|
||||||
@ -18,6 +18,7 @@ export type SerializedCustomParagraphNode = Spread<SerializedCommonBlockNode, Se
|
|||||||
export class CustomParagraphNode extends ParagraphNode {
|
export class CustomParagraphNode extends ParagraphNode {
|
||||||
__id: string = '';
|
__id: string = '';
|
||||||
__alignment: CommonBlockAlignment = '';
|
__alignment: CommonBlockAlignment = '';
|
||||||
|
__inset: number = 0;
|
||||||
|
|
||||||
static getType() {
|
static getType() {
|
||||||
return 'custom-paragraph';
|
return 'custom-paragraph';
|
||||||
@ -43,10 +44,21 @@ export class CustomParagraphNode extends ParagraphNode {
|
|||||||
return self.__alignment;
|
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 {
|
static clone(node: CustomParagraphNode): CustomParagraphNode {
|
||||||
const newNode = new CustomParagraphNode(node.__key);
|
const newNode = new CustomParagraphNode(node.__key);
|
||||||
newNode.__id = node.__id;
|
newNode.__id = node.__id;
|
||||||
newNode.__alignment = node.__alignment;
|
newNode.__alignment = node.__alignment;
|
||||||
|
newNode.__inset = node.__inset;
|
||||||
return newNode;
|
return newNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,13 +80,13 @@ export class CustomParagraphNode extends ParagraphNode {
|
|||||||
version: 1,
|
version: 1,
|
||||||
id: this.__id,
|
id: this.__id,
|
||||||
alignment: this.__alignment,
|
alignment: this.__alignment,
|
||||||
|
inset: this.__inset,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static importJSON(serializedNode: SerializedCustomParagraphNode): CustomParagraphNode {
|
static importJSON(serializedNode: SerializedCustomParagraphNode): CustomParagraphNode {
|
||||||
const node = $createCustomParagraphNode();
|
const node = $createCustomParagraphNode();
|
||||||
node.setId(serializedNode.id);
|
deserializeCommonBlockNode(serializedNode, node);
|
||||||
node.setAlignment(serializedNode.alignment);
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
import {EditorConfig} from "lexical/LexicalEditor";
|
import {EditorConfig} from "lexical/LexicalEditor";
|
||||||
import {QuoteNode, SerializedQuoteNode} from "@lexical/rich-text";
|
import {QuoteNode, SerializedQuoteNode} from "@lexical/rich-text";
|
||||||
import {
|
import {
|
||||||
CommonBlockAlignment, commonPropertiesDifferent,
|
CommonBlockAlignment, commonPropertiesDifferent, deserializeCommonBlockNode,
|
||||||
SerializedCommonBlockNode,
|
SerializedCommonBlockNode,
|
||||||
setCommonBlockPropsFromElement,
|
setCommonBlockPropsFromElement,
|
||||||
updateElementWithCommonBlockProps
|
updateElementWithCommonBlockProps
|
||||||
@ -19,6 +19,7 @@ export type SerializedCustomQuoteNode = Spread<SerializedCommonBlockNode, Serial
|
|||||||
export class CustomQuoteNode extends QuoteNode {
|
export class CustomQuoteNode extends QuoteNode {
|
||||||
__id: string = '';
|
__id: string = '';
|
||||||
__alignment: CommonBlockAlignment = '';
|
__alignment: CommonBlockAlignment = '';
|
||||||
|
__inset: number = 0;
|
||||||
|
|
||||||
static getType() {
|
static getType() {
|
||||||
return 'custom-quote';
|
return 'custom-quote';
|
||||||
@ -44,10 +45,21 @@ export class CustomQuoteNode extends QuoteNode {
|
|||||||
return self.__alignment;
|
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: CustomQuoteNode) {
|
static clone(node: CustomQuoteNode) {
|
||||||
const newNode = new CustomQuoteNode(node.__key);
|
const newNode = new CustomQuoteNode(node.__key);
|
||||||
newNode.__id = node.__id;
|
newNode.__id = node.__id;
|
||||||
newNode.__alignment = node.__alignment;
|
newNode.__alignment = node.__alignment;
|
||||||
|
newNode.__inset = node.__inset;
|
||||||
return newNode;
|
return newNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,13 +80,13 @@ export class CustomQuoteNode extends QuoteNode {
|
|||||||
version: 1,
|
version: 1,
|
||||||
id: this.__id,
|
id: this.__id,
|
||||||
alignment: this.__alignment,
|
alignment: this.__alignment,
|
||||||
|
inset: this.__inset,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static importJSON(serializedNode: SerializedCustomQuoteNode): CustomQuoteNode {
|
static importJSON(serializedNode: SerializedCustomQuoteNode): CustomQuoteNode {
|
||||||
const node = $createCustomQuoteNode();
|
const node = $createCustomQuoteNode();
|
||||||
node.setId(serializedNode.id);
|
deserializeCommonBlockNode(serializedNode, node);
|
||||||
node.setAlignment(serializedNode.alignment);
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import {EditorConfig} from "lexical/LexicalEditor";
|
|||||||
import {el, extractStyleMapFromElement, StyleMap} from "../utils/dom";
|
import {el, extractStyleMapFromElement, StyleMap} from "../utils/dom";
|
||||||
import {getTableColumnWidths} from "../utils/tables";
|
import {getTableColumnWidths} from "../utils/tables";
|
||||||
import {
|
import {
|
||||||
CommonBlockAlignment,
|
CommonBlockAlignment, deserializeCommonBlockNode,
|
||||||
SerializedCommonBlockNode,
|
SerializedCommonBlockNode,
|
||||||
setCommonBlockPropsFromElement,
|
setCommonBlockPropsFromElement,
|
||||||
updateElementWithCommonBlockProps
|
updateElementWithCommonBlockProps
|
||||||
@ -21,6 +21,7 @@ export class CustomTableNode extends TableNode {
|
|||||||
__colWidths: string[] = [];
|
__colWidths: string[] = [];
|
||||||
__styles: StyleMap = new Map;
|
__styles: StyleMap = new Map;
|
||||||
__alignment: CommonBlockAlignment = '';
|
__alignment: CommonBlockAlignment = '';
|
||||||
|
__inset: number = 0;
|
||||||
|
|
||||||
static getType() {
|
static getType() {
|
||||||
return 'custom-table';
|
return 'custom-table';
|
||||||
@ -46,6 +47,16 @@ export class CustomTableNode extends TableNode {
|
|||||||
return self.__alignment;
|
return self.__alignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setInset(size: number) {
|
||||||
|
const self = this.getWritable();
|
||||||
|
self.__inset = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
getInset(): number {
|
||||||
|
const self = this.getLatest();
|
||||||
|
return self.__inset;
|
||||||
|
}
|
||||||
|
|
||||||
setColWidths(widths: string[]) {
|
setColWidths(widths: string[]) {
|
||||||
const self = this.getWritable();
|
const self = this.getWritable();
|
||||||
self.__colWidths = widths;
|
self.__colWidths = widths;
|
||||||
@ -72,6 +83,7 @@ export class CustomTableNode extends TableNode {
|
|||||||
newNode.__colWidths = node.__colWidths;
|
newNode.__colWidths = node.__colWidths;
|
||||||
newNode.__styles = new Map(node.__styles);
|
newNode.__styles = new Map(node.__styles);
|
||||||
newNode.__alignment = node.__alignment;
|
newNode.__alignment = node.__alignment;
|
||||||
|
newNode.__inset = node.__inset;
|
||||||
return newNode;
|
return newNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,15 +124,15 @@ export class CustomTableNode extends TableNode {
|
|||||||
colWidths: this.__colWidths,
|
colWidths: this.__colWidths,
|
||||||
styles: Object.fromEntries(this.__styles),
|
styles: Object.fromEntries(this.__styles),
|
||||||
alignment: this.__alignment,
|
alignment: this.__alignment,
|
||||||
|
inset: this.__inset,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static importJSON(serializedNode: SerializedCustomTableNode): CustomTableNode {
|
static importJSON(serializedNode: SerializedCustomTableNode): CustomTableNode {
|
||||||
const node = $createCustomTableNode();
|
const node = $createCustomTableNode();
|
||||||
node.setId(serializedNode.id);
|
deserializeCommonBlockNode(serializedNode, node);
|
||||||
node.setColWidths(serializedNode.colWidths);
|
node.setColWidths(serializedNode.colWidths);
|
||||||
node.setStyles(new Map(Object.entries(serializedNode.styles)));
|
node.setStyles(new Map(Object.entries(serializedNode.styles)));
|
||||||
node.setAlignment(serializedNode.alignment);
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import type {EditorConfig} from "lexical/LexicalEditor";
|
|||||||
|
|
||||||
import {el, setOrRemoveAttribute, sizeToPixels} from "../utils/dom";
|
import {el, setOrRemoveAttribute, sizeToPixels} from "../utils/dom";
|
||||||
import {
|
import {
|
||||||
CommonBlockAlignment,
|
CommonBlockAlignment, deserializeCommonBlockNode,
|
||||||
SerializedCommonBlockNode,
|
SerializedCommonBlockNode,
|
||||||
setCommonBlockPropsFromElement,
|
setCommonBlockPropsFromElement,
|
||||||
updateElementWithCommonBlockProps
|
updateElementWithCommonBlockProps
|
||||||
@ -80,6 +80,7 @@ export class MediaNode extends ElementNode {
|
|||||||
__tag: MediaNodeTag;
|
__tag: MediaNodeTag;
|
||||||
__attributes: Record<string, string> = {};
|
__attributes: Record<string, string> = {};
|
||||||
__sources: MediaNodeSource[] = [];
|
__sources: MediaNodeSource[] = [];
|
||||||
|
__inset: number = 0;
|
||||||
|
|
||||||
static getType() {
|
static getType() {
|
||||||
return 'media';
|
return 'media';
|
||||||
@ -91,6 +92,7 @@ export class MediaNode extends ElementNode {
|
|||||||
newNode.__sources = node.__sources.map(s => Object.assign({}, s));
|
newNode.__sources = node.__sources.map(s => Object.assign({}, s));
|
||||||
newNode.__id = node.__id;
|
newNode.__id = node.__id;
|
||||||
newNode.__alignment = node.__alignment;
|
newNode.__alignment = node.__alignment;
|
||||||
|
newNode.__inset = node.__inset;
|
||||||
return newNode;
|
return newNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,6 +170,16 @@ export class MediaNode extends ElementNode {
|
|||||||
return self.__alignment;
|
return self.__alignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setInset(size: number) {
|
||||||
|
const self = this.getWritable();
|
||||||
|
self.__inset = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
getInset(): number {
|
||||||
|
const self = this.getLatest();
|
||||||
|
return self.__inset;
|
||||||
|
}
|
||||||
|
|
||||||
setHeight(height: number): void {
|
setHeight(height: number): void {
|
||||||
if (!height) {
|
if (!height) {
|
||||||
return;
|
return;
|
||||||
@ -251,6 +263,10 @@ export class MediaNode extends ElementNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (prevNode.__inset !== this.__inset) {
|
||||||
|
dom.style.paddingLeft = `${this.__inset}px`;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,6 +306,7 @@ export class MediaNode extends ElementNode {
|
|||||||
version: 1,
|
version: 1,
|
||||||
id: this.__id,
|
id: this.__id,
|
||||||
alignment: this.__alignment,
|
alignment: this.__alignment,
|
||||||
|
inset: this.__inset,
|
||||||
tag: this.__tag,
|
tag: this.__tag,
|
||||||
attributes: this.__attributes,
|
attributes: this.__attributes,
|
||||||
sources: this.__sources,
|
sources: this.__sources,
|
||||||
@ -298,8 +315,7 @@ export class MediaNode extends ElementNode {
|
|||||||
|
|
||||||
static importJSON(serializedNode: SerializedMediaNode): MediaNode {
|
static importJSON(serializedNode: SerializedMediaNode): MediaNode {
|
||||||
const node = $createMediaNode(serializedNode.tag);
|
const node = $createMediaNode(serializedNode.tag);
|
||||||
node.setId(serializedNode.id);
|
deserializeCommonBlockNode(serializedNode, node);
|
||||||
node.setAlignment(serializedNode.alignment);
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
## Main Todo
|
## Main Todo
|
||||||
|
|
||||||
|
- Align list nesting with old editor
|
||||||
- Mac: Shortcut support via command.
|
- Mac: Shortcut support via command.
|
||||||
|
|
||||||
## Secondary Todo
|
## Secondary Todo
|
||||||
|
@ -1,12 +1,24 @@
|
|||||||
import {$isListNode, ListNode, ListType} from "@lexical/list";
|
import {$isListNode, ListNode, ListType} from "@lexical/list";
|
||||||
import {EditorButtonDefinition} from "../../framework/buttons";
|
import {EditorButtonDefinition} from "../../framework/buttons";
|
||||||
import {EditorUiContext} from "../../framework/core";
|
import {EditorUiContext} from "../../framework/core";
|
||||||
import {BaseSelection, LexicalNode} from "lexical";
|
import {
|
||||||
|
BaseSelection,
|
||||||
|
LexicalEditor,
|
||||||
|
LexicalNode,
|
||||||
|
} from "lexical";
|
||||||
import listBulletIcon from "@icons/editor/list-bullet.svg";
|
import listBulletIcon from "@icons/editor/list-bullet.svg";
|
||||||
import listNumberedIcon from "@icons/editor/list-numbered.svg";
|
import listNumberedIcon from "@icons/editor/list-numbered.svg";
|
||||||
import listCheckIcon from "@icons/editor/list-check.svg";
|
import listCheckIcon from "@icons/editor/list-check.svg";
|
||||||
import {$selectionContainsNodeType} from "../../../utils/selection";
|
import indentIncreaseIcon from "@icons/editor/indent-increase.svg";
|
||||||
|
import indentDecreaseIcon from "@icons/editor/indent-decrease.svg";
|
||||||
|
import {
|
||||||
|
$getBlockElementNodesInSelection,
|
||||||
|
$selectionContainsNodeType,
|
||||||
|
$toggleSelection,
|
||||||
|
getLastSelection
|
||||||
|
} from "../../../utils/selection";
|
||||||
import {toggleSelectionAsList} from "../../../utils/formats";
|
import {toggleSelectionAsList} from "../../../utils/formats";
|
||||||
|
import {nodeHasInset} from "../../../utils/nodes";
|
||||||
|
|
||||||
|
|
||||||
function buildListButton(label: string, type: ListType, icon: string): EditorButtonDefinition {
|
function buildListButton(label: string, type: ListType, icon: string): EditorButtonDefinition {
|
||||||
@ -27,3 +39,45 @@ function buildListButton(label: string, type: ListType, icon: string): EditorBut
|
|||||||
export const bulletList: EditorButtonDefinition = buildListButton('Bullet list', 'bullet', listBulletIcon);
|
export const bulletList: EditorButtonDefinition = buildListButton('Bullet list', 'bullet', listBulletIcon);
|
||||||
export const numberList: EditorButtonDefinition = buildListButton('Numbered list', 'number', listNumberedIcon);
|
export const numberList: EditorButtonDefinition = buildListButton('Numbered list', 'number', listNumberedIcon);
|
||||||
export const taskList: EditorButtonDefinition = buildListButton('Task list', 'check', listCheckIcon);
|
export const taskList: EditorButtonDefinition = buildListButton('Task list', 'check', listCheckIcon);
|
||||||
|
|
||||||
|
|
||||||
|
function setInsetForSelection(editor: LexicalEditor, change: number): void {
|
||||||
|
const selection = getLastSelection(editor);
|
||||||
|
|
||||||
|
const elements = $getBlockElementNodesInSelection(selection);
|
||||||
|
for (const node of elements) {
|
||||||
|
if (nodeHasInset(node)) {
|
||||||
|
const currentInset = node.getInset();
|
||||||
|
const newInset = Math.min(Math.max(currentInset + change, 0), 500);
|
||||||
|
node.setInset(newInset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$toggleSelection(editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const indentIncrease: EditorButtonDefinition = {
|
||||||
|
label: 'Increase indent',
|
||||||
|
icon: indentIncreaseIcon,
|
||||||
|
action(context: EditorUiContext) {
|
||||||
|
context.editor.update(() => {
|
||||||
|
setInsetForSelection(context.editor, 40);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
isActive() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const indentDecrease: EditorButtonDefinition = {
|
||||||
|
label: 'Decrease indent',
|
||||||
|
icon: indentDecreaseIcon,
|
||||||
|
action(context: EditorUiContext) {
|
||||||
|
context.editor.update(() => {
|
||||||
|
setInsetForSelection(context.editor, -40);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
isActive() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
@ -52,7 +52,13 @@ import {
|
|||||||
underline
|
underline
|
||||||
} from "./defaults/buttons/inline-formats";
|
} from "./defaults/buttons/inline-formats";
|
||||||
import {alignCenter, alignJustify, alignLeft, alignRight} from "./defaults/buttons/alignments";
|
import {alignCenter, alignJustify, alignLeft, alignRight} from "./defaults/buttons/alignments";
|
||||||
import {bulletList, numberList, taskList} from "./defaults/buttons/lists";
|
import {
|
||||||
|
bulletList,
|
||||||
|
indentDecrease,
|
||||||
|
indentIncrease,
|
||||||
|
numberList,
|
||||||
|
taskList
|
||||||
|
} from "./defaults/buttons/lists";
|
||||||
import {
|
import {
|
||||||
codeBlock,
|
codeBlock,
|
||||||
details,
|
details,
|
||||||
@ -119,10 +125,12 @@ export function getMainEditorFullToolbar(): EditorContainerUiElement {
|
|||||||
]),
|
]),
|
||||||
|
|
||||||
// Lists
|
// Lists
|
||||||
new EditorOverflowContainer(3, [
|
new EditorOverflowContainer(5, [
|
||||||
new EditorButton(bulletList),
|
new EditorButton(bulletList),
|
||||||
new EditorButton(numberList),
|
new EditorButton(numberList),
|
||||||
new EditorButton(taskList),
|
new EditorButton(taskList),
|
||||||
|
new EditorButton(indentDecrease),
|
||||||
|
new EditorButton(indentIncrease),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
// Insert types
|
// Insert types
|
||||||
|
@ -11,7 +11,7 @@ import {LexicalNodeMatcher} from "../nodes";
|
|||||||
import {$createCustomParagraphNode} from "../nodes/custom-paragraph";
|
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} from "../nodes/_common";
|
import {NodeHasAlignment, NodeHasInset} from "../nodes/_common";
|
||||||
import {$findMatchingParent} from "@lexical/utils";
|
import {$findMatchingParent} from "@lexical/utils";
|
||||||
|
|
||||||
function wrapTextNodes(nodes: LexicalNode[]): LexicalNode[] {
|
function wrapTextNodes(nodes: LexicalNode[]): LexicalNode[] {
|
||||||
@ -96,4 +96,8 @@ export function $getNearestNodeBlockParent(node: LexicalNode): LexicalNode|null
|
|||||||
|
|
||||||
export function nodeHasAlignment(node: object): node is NodeHasAlignment {
|
export function nodeHasAlignment(node: object): node is NodeHasAlignment {
|
||||||
return '__alignment' in node;
|
return '__alignment' in node;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function nodeHasInset(node: object): node is NodeHasInset {
|
||||||
|
return '__inset' in node;
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user