Skip to content

Commit 437adf9

Browse files
committed
feat(YfmTable): add background color picker to row and column controls
1 parent 5c21055 commit 437adf9

21 files changed

Lines changed: 424 additions & 8 deletions

File tree

demo/src/components/Playground.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ export const Playground = memo<PlaygroundProps>((props) => {
259259
table_ignoreSplittersInBlockMath: true,
260260
table_ignoreSplittersInInlineCode: true,
261261
table_ignoreSplittersInInlineMath: true,
262+
cellBackground: true,
262263
},
263264
...wysiwygConfig?.extensionOptions,
264265
},

demo/src/hocs/withLang.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {configure} from '@gravity-ui/uikit';
33
import type {Decorator} from '@storybook/react';
44

55
import '@gravity-ui/uikit/styles/styles.scss';
6+
import '@gravity-ui/markdown-editor/styles/markdown.css'; // eslint-disable-line import/order
67

78
export const withLang: Decorator = (StoryItem, context) => {
89
const lang = context.globals.lang;

demo/src/hocs/withThemeProvider.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {ThemeProvider} from '@gravity-ui/uikit';
22
import type {Decorator} from '@storybook/react';
33

44
import '@gravity-ui/uikit/styles/styles.scss';
5+
import '@gravity-ui/markdown-editor/styles/markdown.css'; // eslint-disable-line import/order
56

67
export const withThemeProvider: Decorator = (StoryItem, context) => {
78
return (

demo/src/hocs/withToaster.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {toaster} from '@gravity-ui/uikit/toaster-singleton';
33
import type {Decorator} from '@storybook/react';
44

55
import '@gravity-ui/uikit/styles/styles.scss';
6+
import '@gravity-ui/markdown-editor/styles/markdown.css'; // eslint-disable-line import/order
67

78
export const withToaster: Decorator = (StoryItem, context) => {
89
return (

packages/editor/src/extensions/yfm/YfmTable/YfmTable.test.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {EditorState} from 'prosemirror-state';
22
import {builders} from 'prosemirror-test-builder';
33
import {EditorView} from 'prosemirror-view';
4+
import dd from 'ts-dedent';
45

56
import {dispatchPasteEvent} from '../../../../tests/dispatch-event';
67
import {parseDOM} from '../../../../tests/parse-dom';
@@ -519,6 +520,87 @@ nested table
519520
);
520521
});
521522

523+
// TODO: enable when @diplodoc/transform >= 4.75.0 is released (parses ::{bg=...} cell attrs)
524+
it.skip('should serialize cell-bg on first cell (same line as ||)', () => {
525+
const markup = dd`
526+
#|
527+
|| ::{bg="info"}
528+
529+
cell11
530+
531+
||
532+
|#
533+
534+
`.trimStart();
535+
536+
same(
537+
markup,
538+
doc(table(tbody(tr(td({[YfmTableAttr.CellBg]: 'info'}, p('cell11'), p('')))))),
539+
);
540+
});
541+
542+
it.skip('should serialize cell-bg on non-first cell (same line as |)', () => {
543+
const markup = dd`
544+
#|
545+
||
546+
547+
cell11
548+
549+
|::{bg="warning"}
550+
551+
cell12
552+
553+
||
554+
|#
555+
556+
`.trimStart();
557+
558+
same(
559+
markup,
560+
doc(
561+
table(
562+
tbody(
563+
tr(
564+
td(p('cell11'), p('')),
565+
td({[YfmTableAttr.CellBg]: 'warning'}, p('cell12'), p('')),
566+
),
567+
),
568+
),
569+
),
570+
);
571+
});
572+
573+
it.skip('should serialize cell-bg on multiple cells', () => {
574+
const markup = dd`
575+
#|
576+
|| ::{bg="info"}
577+
578+
cell11
579+
580+
|::{bg="danger"}
581+
582+
cell12
583+
584+
||
585+
|#
586+
587+
`.trimStart();
588+
589+
same(
590+
markup,
591+
doc(
592+
table(
593+
tbody(
594+
tr(
595+
td({[YfmTableAttr.CellBg]: 'info'}, p('cell11'), p('')),
596+
td({[YfmTableAttr.CellBg]: 'danger'}, p('cell12'), p('')),
597+
),
598+
),
599+
),
600+
),
601+
);
602+
});
603+
522604
it('should preserve cell-align', () => {
523605
const markup = `
524606
#|

packages/editor/src/extensions/yfm/YfmTable/YfmTableSpecs/const.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ export enum YfmTableAttr {
99
Colspan = 'colspan',
1010
Rowspan = 'rowspan',
1111
CellAlign = 'data-cell-align',
12+
CellBg = 'data-bg',
1213
}

packages/editor/src/extensions/yfm/YfmTable/YfmTableSpecs/schema.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ export const getSchemaSpecs = (
7676
[YfmTableAttr.Colspan]: {default: null},
7777
[YfmTableAttr.Rowspan]: {default: null},
7878
[YfmTableAttr.CellAlign]: {default: null},
79+
[YfmTableAttr.CellBg]: {default: null},
7980
},
8081
parseDOM: [
8182
{tag: 'td', priority: 200},

packages/editor/src/extensions/yfm/YfmTable/YfmTableSpecs/serializer.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ export const serializerTokens: Record<YfmTableNode, SerializerNodeToken> = {
2020
const rowspanStack: Record<number, number> = {};
2121

2222
tbody.forEach((trow) => {
23-
state.write('||');
23+
const firstCellBg = trow.firstChild?.attrs[YfmTableAttr.CellBg];
24+
const firstCellAttrs =
25+
typeof firstCellBg === 'string' ? ` ::{bg="${firstCellBg}"}` : '';
26+
state.write(`||${firstCellAttrs}`);
2427
state.ensureNewLine();
2528
state.write('\n');
2629

@@ -39,7 +42,9 @@ export const serializerTokens: Record<YfmTableNode, SerializerNodeToken> = {
3942
}
4043

4144
if (colIndex > 0) {
42-
state.write('|');
45+
const cellBg = td.attrs[YfmTableAttr.CellBg];
46+
const cellAttrs = typeof cellBg === 'string' ? `::{bg="${cellBg}"}` : '';
47+
state.write(cellAttrs ? `|${cellAttrs}` : '|');
4348
state.ensureNewLine();
4449
state.write('\n');
4550
}

packages/editor/src/extensions/yfm/YfmTable/index.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ export type YfmTableOptions = YfmTableSpecsOptions & {
3131
* @default true
3232
*/
3333
dnd?: boolean;
34+
/**
35+
* Enables cell background color picker for table cells.
36+
* Available with @diplodoc/transform v4.75.0-beta0 or higher.
37+
* @default false
38+
*/
39+
// TODO [MAJOR]: enable by default and remove option
40+
cellBackground?: boolean;
3441
};
3542

3643
export const YfmTable: ExtensionWithOptions<YfmTableOptions> = (builder, options) => {
@@ -47,7 +54,12 @@ export const YfmTable: ExtensionWithOptions<YfmTableOptions> = (builder, options
4754
builder.addPlugin(yfmTableTransformPastedPlugin);
4855

4956
if (options.controls !== false) {
50-
builder.addPlugin(yfmTableControlsPlugins({dndEnabled: options.dnd !== false}));
57+
builder.addPlugin(
58+
yfmTableControlsPlugins({
59+
dndEnabled: options.dnd !== false,
60+
cellBackground: options.cellBackground === true,
61+
}),
62+
);
5163
}
5264
};
5365

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import type {Command} from '#pm/state';
2+
import {TableDesc} from 'src/table-utils/table-desc';
3+
4+
import {YfmTableAttr} from '../../../YfmTableSpecs/const';
5+
6+
export type SetCellBgParams = {
7+
tablePos: number;
8+
rows?: number[];
9+
cols?: number[];
10+
bg: string | null;
11+
};
12+
13+
export const setCellBg = (params: SetCellBgParams): Command => {
14+
return (state, dispatch) => {
15+
const table = state.doc.nodeAt(params.tablePos);
16+
const tableDesc = table && TableDesc.create(table)?.bind(params.tablePos);
17+
if (!tableDesc) return false;
18+
19+
if (!dispatch) return true;
20+
21+
const {tr} = state;
22+
23+
const apply = (cellPos: ReturnType<typeof tableDesc.getPosForRowCells>[number]) => {
24+
if (cellPos.type !== 'real') return;
25+
const node = state.doc.nodeAt(cellPos.from);
26+
if (!node) return;
27+
tr.setNodeAttribute(
28+
tr.mapping.map(cellPos.from),
29+
YfmTableAttr.CellBg,
30+
params.bg || null,
31+
);
32+
};
33+
34+
if (params.rows) {
35+
for (const rowIdx of params.rows) {
36+
for (const pos of tableDesc.getPosForRowCells(rowIdx)) apply(pos);
37+
}
38+
}
39+
if (params.cols) {
40+
for (const colIdx of params.cols) {
41+
for (const pos of tableDesc.getPosForColumn(colIdx)) apply(pos);
42+
}
43+
}
44+
45+
if (tr.docChanged) dispatch(tr);
46+
47+
return true;
48+
};
49+
};

0 commit comments

Comments
 (0)