Skip to content
New issue

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

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

Already on GitHub? # 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
Expand Up @@ -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) {
Expand Down Expand Up @@ -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"
Expand Down
47 changes: 46 additions & 1 deletion packages/lexical-table/src/LexicalTableCellNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -47,6 +48,7 @@ export type SerializedTableCellNode = Spread<
headerState: TableCellHeaderState;
width?: number;
backgroundColor?: null | string;
writingMode?: string;
},
SerializedElementNode
>;
Expand All @@ -63,6 +65,8 @@ export class TableCellNode extends ElementNode {
__width?: number;
/** @internal */
__backgroundColor: null | string;
/** @internal */
__writingMode?: string;

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

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

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand All @@ -186,6 +203,7 @@ export class TableCellNode extends ElementNode {
rowSpan: this.__rowSpan,
type: 'tablecell',
width: this.getWidth(),
writingMode: this.__writingMode,
};
}

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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
);
}

Expand Down Expand Up @@ -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(' ');
Expand Down
15 changes: 15 additions & 0 deletions packages/lexical-table/src/LexicalTableSelectionHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
11 changes: 10 additions & 1 deletion packages/lexical-table/src/LexicalTableUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Up @@ -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({
Expand Down Expand Up @@ -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>`,
);
});
});
});
});
Loading