Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[lexical-table] Support for vertical cell writing #6545

Draft
wants to merge 19 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -501,6 +501,28 @@ function TableActionMenu({
[editor],
);

const toggleWritingMode = useCallback(() => {
editor.update(() => {
const selection = $getSelection();
if ($isRangeSelection(selection) || $isTableSelection(selection)) {
const [cell] = $getNodeTriplet(selection.anchor);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The source for $getNodeTriplet seems a little suspect since it effectively uses selection.anchor.getNode() which could be possibly higher up in the table than inside of a cell? Seems kinda like a bad API to take a point or lexical node when we don't have an $isLexicalNode or isPoint guard for it to do that cleanly

const newDirection = cell.getWritingMode() ? 'vertical-rl' : undefined;
cell.setWritingMode(newDirection);

if ($isTableSelection(selection)) {
const nodes = selection.getNodes();

for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if ($isTableCellNode(node)) {
node.setWritingMode(newDirection);
}
}
}
}
});
}, [editor]);

let mergeCellButton: null | JSX.Element = null;
if (cellMerge) {
if (canMergeCells) {
@@ -556,6 +578,17 @@ function TableActionMenu({
data-test-id="table-row-striping">
<span className="text">Toggle Row Striping</span>
</button>
<button
type="button"
className="item"
onClick={() => toggleWritingMode()}
data-test-id="table-toggle-text-direction">
<span className="text">
{tableCellNode.__writingMode
? 'Horizontal Text Direction'
: 'Vertical Text Direction'}
</span>
</button>
<hr />
<button
type="button"
47 changes: 46 additions & 1 deletion packages/lexical-table/src/LexicalTableCellNode.ts
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@ import {
} from 'lexical';

import {COLUMN_WIDTH, PIXEL_VALUE_REG_EXP} from './constants';
import {computeVerticalFormat} from './LexicalTableUtils';

export const TableCellHeaderStates = {
BOTH: 3,
@@ -47,6 +48,7 @@ export type SerializedTableCellNode = Spread<
headerState: TableCellHeaderState;
width?: number;
backgroundColor?: null | string;
writingMode?: string;
},
SerializedElementNode
>;
@@ -63,6 +65,8 @@ export class TableCellNode extends ElementNode {
__width?: number;
/** @internal */
__backgroundColor: null | string;
/** @internal */
__writingMode?: string;

static getType(): string {
return 'tablecell';
@@ -77,6 +81,7 @@ export class TableCellNode extends ElementNode {
);
cellNode.__rowSpan = node.__rowSpan;
cellNode.__backgroundColor = node.__backgroundColor;
cellNode.__writingMode = node.__writingMode;
return cellNode;
}

@@ -103,6 +108,7 @@ export class TableCellNode extends ElementNode {
);
cellNode.__rowSpan = rowSpan;
cellNode.__backgroundColor = serializedNode.backgroundColor || null;
cellNode.__writingMode = serializedNode.writingMode;
return cellNode;
}

@@ -137,6 +143,12 @@ export class TableCellNode extends ElementNode {
if (this.__backgroundColor !== null) {
element.style.backgroundColor = this.__backgroundColor;
}
if (this.__writingMode) {
element.style.writingMode = this.__writingMode;
element.style.verticalAlign = computeVerticalFormat(this.getFormatType());
} else {
element.style.verticalAlign = '';
}

addClassNamesToElement(
element,
@@ -164,6 +176,11 @@ export class TableCellNode extends ElementNode {
element_.style.verticalAlign = 'top';
element_.style.textAlign = 'start';

const writingMode = this.getWritingMode();
if (writingMode) {
element_.style.writingMode = writingMode;
}

const backgroundColor = this.getBackgroundColor();
if (backgroundColor !== null) {
element_.style.backgroundColor = backgroundColor;
@@ -186,6 +203,7 @@ export class TableCellNode extends ElementNode {
rowSpan: this.__rowSpan,
type: 'tablecell',
width: this.getWidth(),
writingMode: this.__writingMode,
};
}

@@ -231,6 +249,27 @@ export class TableCellNode extends ElementNode {
return this.getLatest().__width;
}

/**
* Returns the current cell writing direction
* @returns undefined for horizontal, string value if vertical
*/
getWritingMode(): string | undefined {
return this.getLatest().__writingMode;
}

/**
* Update cell writing direction, set to null or undefined for horizontal (default)
* Set to 'vertical-rl' or 'vertical-lr' for vertical and paragraph order preference.
* {@link https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode}
*/
setWritingMode(direction?: null | 'vertical-rl' | 'vertical-lr'): void {
if (!direction) {
this.getWritable().__writingMode = undefined;
} else {
this.getLatest().__writingMode = direction;
}
}

getBackgroundColor(): null | string {
return this.getLatest().__backgroundColor;
}
@@ -265,7 +304,9 @@ export class TableCellNode extends ElementNode {
prevNode.__width !== this.__width ||
prevNode.__colSpan !== this.__colSpan ||
prevNode.__rowSpan !== this.__rowSpan ||
prevNode.__backgroundColor !== this.__backgroundColor
prevNode.__backgroundColor !== this.__backgroundColor ||
prevNode.__writingMode !== this.__writingMode ||
prevNode.__format !== this.__format
);
}

@@ -311,6 +352,10 @@ export function $convertTableCellNodeElement(
if (backgroundColor !== '') {
tableCellNode.__backgroundColor = backgroundColor;
}
const writingMode = domNode_.style.writingMode;
if (writingMode === 'vertical-lr' || writingMode === 'vertical-rl') {
tableCellNode.__writingMode = writingMode;
}

const style = domNode_.style;
const textDecoration = style.textDecoration.split(' ');
15 changes: 15 additions & 0 deletions packages/lexical-table/src/LexicalTableSelectionHelpers.ts
Original file line number Diff line number Diff line change
@@ -459,6 +459,21 @@ export function applyTableHandlers(
FORMAT_ELEMENT_COMMAND,
(formatType) => {
const selection = $getSelection();
if (
$isSelectionInTable(selection, tableNode) &&
selection!.getNodes().length === 1 &&
$isTextNode(selection!.getNodes()[0])
) {
const tableCellNode = $findMatchingParent(
selection!.getNodes()[0],
$isTableCellNode,
);
if (tableCellNode) {
tableCellNode.setFormat(formatType);
}
return false;
}

if (
!$isTableSelection(selection) ||
!$isSelectionInTable(selection, tableNode)
11 changes: 10 additions & 1 deletion packages/lexical-table/src/LexicalTableUtils.ts
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@
*/

import type {TableMapType, TableMapValueType} from './LexicalTableSelection';
import type {ElementNode, PointType} from 'lexical';
import type {ElementFormatType, ElementNode, PointType} from 'lexical';

import {$findMatchingParent} from '@lexical/utils';
import {
@@ -892,3 +892,12 @@ export function $getTableCellNodeRect(tableCellNode: TableCellNode): {

return null;
}

export const computeVerticalFormat = (format: ElementFormatType) => {
if (format === 'center') {
return 'middle';
} else if (format === 'right' || format === 'end') {
return 'bottom';
}
return 'top';
};
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@
*/

import {$createTableCellNode, TableCellHeaderStates} from '@lexical/table';
import {$createParagraphNode, $createTextNode} from 'lexical';
import {initializeUnitTest} from 'lexical/src/__tests__/utils';

const editorConfig = Object.freeze({
@@ -66,5 +67,21 @@ describe('LexicalTableCellNode tests', () => {
);
});
});

test('TableCellNode Toggle Writing Direction', async () => {
const {editor} = testEnv;

await editor.update(() => {
const cellNode = $createTableCellNode(TableCellHeaderStates.NO_STATUS);
const p = $createParagraphNode();
p.append($createTextNode('abc'));
cellNode.append(p);
cellNode.setWritingMode('vertical-rl');

expect(cellNode.createDOM(editorConfig).outerHTML).toBe(
`<td style="writing-mode: vertical-rl; vertical-align: top;" class="${editorConfig.theme.tableCell}"></td>`,
);
});
});
});
});