mirror of
https://github.com/docmost/docmost.git
synced 2025-11-13 06:12:38 +10:00
feat: table background color, cell header and align (#1352)
* feat: add toggle header cell button to table cell menu Added ability to toggle header cells directly from the table cell menu. This enhancement includes: - New toggle header cell button with IconTableRow icon - Consistent UI/UX with existing table menu patterns - Proper internationalization support * fix: typo in aria-label for toggle header cell button * feat: add table cell background color picker - Extended TableCell and TableHeader to support backgroundColor attribute - Created TableBackgroundColor component with 21 color options - Integrated color picker into table cell menu using Mantine UI - Added support for both regular cells and header cells - Updated imports to use custom TableHeader from @docmost/editor-ext * feat: add text alignment to table cell menu - Created TableTextAlignment component with left, center, and right alignment options - Integrated alignment selector into table cell menu - Shows current alignment icon in the button - Displays checkmark next to active alignment in dropdown * background colors * table background color in dark mode * add bg color name * rename color attribute * increase minimum table width
This commit is contained in:
@ -0,0 +1,145 @@
|
|||||||
|
import React, { FC } from "react";
|
||||||
|
import { IconCheck, IconPalette } from "@tabler/icons-react";
|
||||||
|
import {
|
||||||
|
ActionIcon,
|
||||||
|
ColorSwatch,
|
||||||
|
Popover,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
Tooltip,
|
||||||
|
UnstyledButton,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import { useEditor } from "@tiptap/react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
export interface TableColorItem {
|
||||||
|
name: string;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TableBackgroundColorProps {
|
||||||
|
editor: ReturnType<typeof useEditor>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TABLE_COLORS: TableColorItem[] = [
|
||||||
|
{ name: "Default", color: "" },
|
||||||
|
{ name: "Blue", color: "#b4d5ff" },
|
||||||
|
{ name: "Green", color: "#acf5d2" },
|
||||||
|
{ name: "Yellow", color: "#fef1b4" },
|
||||||
|
{ name: "Red", color: "#ffbead" },
|
||||||
|
{ name: "Pink", color: "#ffc7fe" },
|
||||||
|
{ name: "Gray", color: "#eaecef" },
|
||||||
|
{ name: "Purple", color: "#c1b7f2" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const TableBackgroundColor: FC<TableBackgroundColorProps> = ({
|
||||||
|
editor,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [opened, setOpened] = React.useState(false);
|
||||||
|
|
||||||
|
const setTableCellBackground = (color: string, colorName: string) => {
|
||||||
|
editor
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.updateAttributes("tableCell", {
|
||||||
|
backgroundColor: color || null,
|
||||||
|
backgroundColorName: color ? colorName : null
|
||||||
|
})
|
||||||
|
.updateAttributes("tableHeader", {
|
||||||
|
backgroundColor: color || null,
|
||||||
|
backgroundColorName: color ? colorName : null
|
||||||
|
})
|
||||||
|
.run();
|
||||||
|
setOpened(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get current cell's background color
|
||||||
|
const getCurrentColor = () => {
|
||||||
|
if (editor.isActive("tableCell")) {
|
||||||
|
const attrs = editor.getAttributes("tableCell");
|
||||||
|
return attrs.backgroundColor || "";
|
||||||
|
}
|
||||||
|
if (editor.isActive("tableHeader")) {
|
||||||
|
const attrs = editor.getAttributes("tableHeader");
|
||||||
|
return attrs.backgroundColor || "";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
const currentColor = getCurrentColor();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
width={200}
|
||||||
|
position="bottom"
|
||||||
|
opened={opened}
|
||||||
|
onChange={setOpened}
|
||||||
|
withArrow
|
||||||
|
transitionProps={{ transition: "pop" }}
|
||||||
|
>
|
||||||
|
<Popover.Target>
|
||||||
|
<Tooltip label={t("Background color")} withArrow>
|
||||||
|
<ActionIcon
|
||||||
|
variant="default"
|
||||||
|
size="lg"
|
||||||
|
aria-label={t("Background color")}
|
||||||
|
onClick={() => setOpened(!opened)}
|
||||||
|
>
|
||||||
|
<IconPalette size={18} />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
</Popover.Target>
|
||||||
|
|
||||||
|
<Popover.Dropdown>
|
||||||
|
<Stack gap="xs">
|
||||||
|
<Text size="sm" c="dimmed">
|
||||||
|
{t("Background color")}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "grid",
|
||||||
|
gridTemplateColumns: "repeat(4, 1fr)",
|
||||||
|
gap: "8px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{TABLE_COLORS.map((item, index) => (
|
||||||
|
<UnstyledButton
|
||||||
|
key={index}
|
||||||
|
onClick={() => setTableCellBackground(item.color, item.name)}
|
||||||
|
style={{
|
||||||
|
position: "relative",
|
||||||
|
width: "24px",
|
||||||
|
height: "24px",
|
||||||
|
}}
|
||||||
|
title={t(item.name)}
|
||||||
|
>
|
||||||
|
<ColorSwatch
|
||||||
|
color={item.color || "#ffffff"}
|
||||||
|
size={24}
|
||||||
|
style={{
|
||||||
|
border: item.color === "" ? "1px solid #e5e7eb" : undefined,
|
||||||
|
cursor: "pointer",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{currentColor === item.color && (
|
||||||
|
<IconCheck
|
||||||
|
size={18}
|
||||||
|
style={{
|
||||||
|
color:
|
||||||
|
item.color === "" || item.color.startsWith("#F")
|
||||||
|
? "#000000"
|
||||||
|
: "#ffffff",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</ColorSwatch>
|
||||||
|
</UnstyledButton>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Stack>
|
||||||
|
</Popover.Dropdown>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -12,8 +12,11 @@ import {
|
|||||||
IconColumnRemove,
|
IconColumnRemove,
|
||||||
IconRowRemove,
|
IconRowRemove,
|
||||||
IconSquareToggle,
|
IconSquareToggle,
|
||||||
|
IconTableRow,
|
||||||
} from "@tabler/icons-react";
|
} from "@tabler/icons-react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { TableBackgroundColor } from "./table-background-color";
|
||||||
|
import { TableTextAlignment } from "./table-text-alignment";
|
||||||
|
|
||||||
export const TableCellMenu = React.memo(
|
export const TableCellMenu = React.memo(
|
||||||
({ editor, appendTo }: EditorMenuProps): JSX.Element => {
|
({ editor, appendTo }: EditorMenuProps): JSX.Element => {
|
||||||
@ -45,6 +48,10 @@ export const TableCellMenu = React.memo(
|
|||||||
editor.chain().focus().deleteRow().run();
|
editor.chain().focus().deleteRow().run();
|
||||||
}, [editor]);
|
}, [editor]);
|
||||||
|
|
||||||
|
const toggleHeaderCell = useCallback(() => {
|
||||||
|
editor.chain().focus().toggleHeaderCell().run();
|
||||||
|
}, [editor]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseBubbleMenu
|
<BaseBubbleMenu
|
||||||
editor={editor}
|
editor={editor}
|
||||||
@ -60,6 +67,9 @@ export const TableCellMenu = React.memo(
|
|||||||
shouldShow={shouldShow}
|
shouldShow={shouldShow}
|
||||||
>
|
>
|
||||||
<ActionIcon.Group>
|
<ActionIcon.Group>
|
||||||
|
<TableBackgroundColor editor={editor} />
|
||||||
|
<TableTextAlignment editor={editor} />
|
||||||
|
|
||||||
<Tooltip position="top" label={t("Merge cells")}>
|
<Tooltip position="top" label={t("Merge cells")}>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
onClick={mergeCells}
|
onClick={mergeCells}
|
||||||
@ -103,6 +113,17 @@ export const TableCellMenu = React.memo(
|
|||||||
<IconRowRemove size={18} />
|
<IconRowRemove size={18} />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip position="top" label={t("Toggle header cell")}>
|
||||||
|
<ActionIcon
|
||||||
|
onClick={toggleHeaderCell}
|
||||||
|
variant="default"
|
||||||
|
size="lg"
|
||||||
|
aria-label={t("Toggle header cell")}
|
||||||
|
>
|
||||||
|
<IconTableRow size={18} />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
</ActionIcon.Group>
|
</ActionIcon.Group>
|
||||||
</BaseBubbleMenu>
|
</BaseBubbleMenu>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -0,0 +1,109 @@
|
|||||||
|
import React, { FC } from "react";
|
||||||
|
import {
|
||||||
|
IconAlignCenter,
|
||||||
|
IconAlignLeft,
|
||||||
|
IconAlignRight,
|
||||||
|
IconCheck,
|
||||||
|
} from "@tabler/icons-react";
|
||||||
|
import {
|
||||||
|
ActionIcon,
|
||||||
|
Button,
|
||||||
|
Popover,
|
||||||
|
rem,
|
||||||
|
ScrollArea,
|
||||||
|
Tooltip,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import { useEditor } from "@tiptap/react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
interface TableTextAlignmentProps {
|
||||||
|
editor: ReturnType<typeof useEditor>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AlignmentItem {
|
||||||
|
name: string;
|
||||||
|
icon: React.ElementType;
|
||||||
|
command: () => void;
|
||||||
|
isActive: () => boolean;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TableTextAlignment: FC<TableTextAlignmentProps> = ({ editor }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [opened, setOpened] = React.useState(false);
|
||||||
|
|
||||||
|
const items: AlignmentItem[] = [
|
||||||
|
{
|
||||||
|
name: "Align left",
|
||||||
|
value: "left",
|
||||||
|
isActive: () => editor.isActive({ textAlign: "left" }),
|
||||||
|
command: () => editor.chain().focus().setTextAlign("left").run(),
|
||||||
|
icon: IconAlignLeft,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Align center",
|
||||||
|
value: "center",
|
||||||
|
isActive: () => editor.isActive({ textAlign: "center" }),
|
||||||
|
command: () => editor.chain().focus().setTextAlign("center").run(),
|
||||||
|
icon: IconAlignCenter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Align right",
|
||||||
|
value: "right",
|
||||||
|
isActive: () => editor.isActive({ textAlign: "right" }),
|
||||||
|
command: () => editor.chain().focus().setTextAlign("right").run(),
|
||||||
|
icon: IconAlignRight,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const activeItem = items.find((item) => item.isActive()) || items[0];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
opened={opened}
|
||||||
|
onChange={setOpened}
|
||||||
|
position="bottom"
|
||||||
|
withArrow
|
||||||
|
transitionProps={{ transition: 'pop' }}
|
||||||
|
>
|
||||||
|
<Popover.Target>
|
||||||
|
<Tooltip label={t("Text alignment")} withArrow>
|
||||||
|
<ActionIcon
|
||||||
|
variant="default"
|
||||||
|
size="lg"
|
||||||
|
aria-label={t("Text alignment")}
|
||||||
|
onClick={() => setOpened(!opened)}
|
||||||
|
>
|
||||||
|
<activeItem.icon size={18} />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
</Popover.Target>
|
||||||
|
|
||||||
|
<Popover.Dropdown>
|
||||||
|
<ScrollArea.Autosize type="scroll" mah={300}>
|
||||||
|
<Button.Group orientation="vertical">
|
||||||
|
{items.map((item, index) => (
|
||||||
|
<Button
|
||||||
|
key={index}
|
||||||
|
variant="default"
|
||||||
|
leftSection={<item.icon size={16} />}
|
||||||
|
rightSection={
|
||||||
|
item.isActive() && <IconCheck size={16} />
|
||||||
|
}
|
||||||
|
justify="left"
|
||||||
|
fullWidth
|
||||||
|
onClick={() => {
|
||||||
|
item.command();
|
||||||
|
setOpened(false);
|
||||||
|
}}
|
||||||
|
style={{ border: "none" }}
|
||||||
|
>
|
||||||
|
{t(item.name)}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</Button.Group>
|
||||||
|
</ScrollArea.Autosize>
|
||||||
|
</Popover.Dropdown>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -11,7 +11,6 @@ import { Typography } from "@tiptap/extension-typography";
|
|||||||
import { TextStyle } from "@tiptap/extension-text-style";
|
import { TextStyle } from "@tiptap/extension-text-style";
|
||||||
import { Color } from "@tiptap/extension-color";
|
import { Color } from "@tiptap/extension-color";
|
||||||
import Table from "@tiptap/extension-table";
|
import Table from "@tiptap/extension-table";
|
||||||
import TableHeader from "@tiptap/extension-table-header";
|
|
||||||
import SlashCommand from "@/features/editor/extensions/slash-command";
|
import SlashCommand from "@/features/editor/extensions/slash-command";
|
||||||
import { Collaboration } from "@tiptap/extension-collaboration";
|
import { Collaboration } from "@tiptap/extension-collaboration";
|
||||||
import { CollaborationCursor } from "@tiptap/extension-collaboration-cursor";
|
import { CollaborationCursor } from "@tiptap/extension-collaboration-cursor";
|
||||||
@ -25,6 +24,7 @@ import {
|
|||||||
MathInline,
|
MathInline,
|
||||||
TableCell,
|
TableCell,
|
||||||
TableRow,
|
TableRow,
|
||||||
|
TableHeader,
|
||||||
TrailingNode,
|
TrailingNode,
|
||||||
TiptapImage,
|
TiptapImage,
|
||||||
Callout,
|
Callout,
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
& table {
|
& table {
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
min-width: 700px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,8 +67,54 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Table cell background colors with dark mode support */
|
||||||
|
.ProseMirror {
|
||||||
|
table {
|
||||||
|
@mixin dark {
|
||||||
|
/* Blue */
|
||||||
|
td[data-background-color="#b4d5ff"],
|
||||||
|
th[data-background-color="#b4d5ff"] {
|
||||||
|
background-color: #1a3a5c !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Green */
|
||||||
|
td[data-background-color="#acf5d2"],
|
||||||
|
th[data-background-color="#acf5d2"] {
|
||||||
|
background-color: #1a4d3a !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Yellow */
|
||||||
|
td[data-background-color="#fef1b4"],
|
||||||
|
th[data-background-color="#fef1b4"] {
|
||||||
|
background-color: #7c5014 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Red */
|
||||||
|
td[data-background-color="#ffbead"],
|
||||||
|
th[data-background-color="#ffbead"] {
|
||||||
|
background-color: #5c2a23 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pink */
|
||||||
|
td[data-background-color="#ffc7fe"],
|
||||||
|
th[data-background-color="#ffc7fe"] {
|
||||||
|
background-color: #4d2a4d !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gray */
|
||||||
|
td[data-background-color="#eaecef"],
|
||||||
|
th[data-background-color="#eaecef"] {
|
||||||
|
background-color: #2a2e33 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Purple */
|
||||||
|
td[data-background-color="#c1b7f2"],
|
||||||
|
th[data-background-color="#c1b7f2"] {
|
||||||
|
background-color: #3a2f5c !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -11,7 +11,6 @@ import { TextStyle } from '@tiptap/extension-text-style';
|
|||||||
import { Color } from '@tiptap/extension-color';
|
import { Color } from '@tiptap/extension-color';
|
||||||
import { Youtube } from '@tiptap/extension-youtube';
|
import { Youtube } from '@tiptap/extension-youtube';
|
||||||
import Table from '@tiptap/extension-table';
|
import Table from '@tiptap/extension-table';
|
||||||
import TableHeader from '@tiptap/extension-table-header';
|
|
||||||
import {
|
import {
|
||||||
Callout,
|
Callout,
|
||||||
Comment,
|
Comment,
|
||||||
@ -22,6 +21,7 @@ import {
|
|||||||
LinkExtension,
|
LinkExtension,
|
||||||
MathBlock,
|
MathBlock,
|
||||||
MathInline,
|
MathInline,
|
||||||
|
TableHeader,
|
||||||
TableCell,
|
TableCell,
|
||||||
TableRow,
|
TableRow,
|
||||||
TiptapImage,
|
TiptapImage,
|
||||||
@ -31,7 +31,7 @@ import {
|
|||||||
Drawio,
|
Drawio,
|
||||||
Excalidraw,
|
Excalidraw,
|
||||||
Embed,
|
Embed,
|
||||||
Mention
|
Mention,
|
||||||
} from '@docmost/editor-ext';
|
} from '@docmost/editor-ext';
|
||||||
import { generateText, getSchema, JSONContent } from '@tiptap/core';
|
import { generateText, getSchema, JSONContent } from '@tiptap/core';
|
||||||
import { generateHTML } from '../common/helpers/prosemirror/html';
|
import { generateHTML } from '../common/helpers/prosemirror/html';
|
||||||
@ -46,7 +46,7 @@ export const tiptapExtensions = [
|
|||||||
codeBlock: false,
|
codeBlock: false,
|
||||||
}),
|
}),
|
||||||
Comment,
|
Comment,
|
||||||
TextAlign.configure({ types: ["heading", "paragraph"] }),
|
TextAlign.configure({ types: ['heading', 'paragraph'] }),
|
||||||
TaskList,
|
TaskList,
|
||||||
TaskItem.configure({
|
TaskItem.configure({
|
||||||
nested: true,
|
nested: true,
|
||||||
@ -66,9 +66,9 @@ export const tiptapExtensions = [
|
|||||||
DetailsContent,
|
DetailsContent,
|
||||||
DetailsSummary,
|
DetailsSummary,
|
||||||
Table,
|
Table,
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
TableCell,
|
TableCell,
|
||||||
|
TableRow,
|
||||||
|
TableHeader,
|
||||||
Youtube,
|
Youtube,
|
||||||
TiptapImage,
|
TiptapImage,
|
||||||
TiptapVideo,
|
TiptapVideo,
|
||||||
@ -78,7 +78,7 @@ export const tiptapExtensions = [
|
|||||||
Drawio,
|
Drawio,
|
||||||
Excalidraw,
|
Excalidraw,
|
||||||
Embed,
|
Embed,
|
||||||
Mention
|
Mention,
|
||||||
] as any;
|
] as any;
|
||||||
|
|
||||||
export function jsonToHtml(tiptapJson: any) {
|
export function jsonToHtml(tiptapJson: any) {
|
||||||
|
|||||||
@ -3,4 +3,35 @@ import { TableCell as TiptapTableCell } from "@tiptap/extension-table-cell";
|
|||||||
export const TableCell = TiptapTableCell.extend({
|
export const TableCell = TiptapTableCell.extend({
|
||||||
name: "tableCell",
|
name: "tableCell",
|
||||||
content: "paragraph+",
|
content: "paragraph+",
|
||||||
|
|
||||||
|
addAttributes() {
|
||||||
|
return {
|
||||||
|
...this.parent?.(),
|
||||||
|
backgroundColor: {
|
||||||
|
default: null,
|
||||||
|
parseHTML: (element) => element.style.backgroundColor || null,
|
||||||
|
renderHTML: (attributes) => {
|
||||||
|
if (!attributes.backgroundColor) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
style: `background-color: ${attributes.backgroundColor}`,
|
||||||
|
'data-background-color': attributes.backgroundColor,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
backgroundColorName: {
|
||||||
|
default: null,
|
||||||
|
parseHTML: (element) => element.getAttribute('data-background-color-name') || null,
|
||||||
|
renderHTML: (attributes) => {
|
||||||
|
if (!attributes.backgroundColorName) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
'data-background-color-name': attributes.backgroundColorName.toLowerCase(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
37
packages/editor-ext/src/lib/table/header.ts
Normal file
37
packages/editor-ext/src/lib/table/header.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { TableHeader as TiptapTableHeader } from "@tiptap/extension-table-header";
|
||||||
|
|
||||||
|
export const TableHeader = TiptapTableHeader.extend({
|
||||||
|
name: "tableHeader",
|
||||||
|
content: "paragraph+",
|
||||||
|
|
||||||
|
addAttributes() {
|
||||||
|
return {
|
||||||
|
...this.parent?.(),
|
||||||
|
backgroundColor: {
|
||||||
|
default: null,
|
||||||
|
parseHTML: (element) => element.style.backgroundColor || null,
|
||||||
|
renderHTML: (attributes) => {
|
||||||
|
if (!attributes.backgroundColor) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
style: `background-color: ${attributes.backgroundColor}`,
|
||||||
|
'data-background-color': attributes.backgroundColor,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
backgroundColorName: {
|
||||||
|
default: null,
|
||||||
|
parseHTML: (element) => element.getAttribute('data-background-color-name') || null,
|
||||||
|
renderHTML: (attributes) => {
|
||||||
|
if (!attributes.backgroundColorName) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
'data-background-color-name': attributes.backgroundColorName.toLowerCase(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -1,2 +1,3 @@
|
|||||||
export * from "./row";
|
export * from "./row";
|
||||||
export * from "./cell";
|
export * from "./cell";
|
||||||
|
export * from "./header";
|
||||||
|
|||||||
Reference in New Issue
Block a user