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
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