[WEB-459] fix: tables row color retention, images in tables and css fixes (#3748)

* fix: tables row color retention, images in tables and css fixes

* fix: border colors darker

* updated tables to new design

* removing comments
This commit is contained in:
M. Palanikannan 2024-02-23 18:51:38 +05:30 committed by GitHub
parent 5c64933927
commit e0a4d7a12a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 211 additions and 288 deletions

View File

@ -97,8 +97,8 @@ export const insertTableCommand = (editor: Editor, range?: Range) => {
} }
} }
} }
if (range) editor.chain().focus().deleteRange(range).insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run(); if (range) editor.chain().focus().deleteRange(range).insertTable({ rows: 3, cols: 3 }).run();
else editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run(); else editor.chain().focus().insertTable({ rows: 3, cols: 3 }).run();
}; };
export const unsetLinkEditor = (editor: Editor) => { export const unsetLinkEditor = (editor: Editor) => {

View File

@ -170,68 +170,6 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p {
} }
} }
#editor-container {
table {
border-collapse: collapse;
table-layout: fixed;
margin: 0.5em 0 0.5em 0;
border: 1px solid rgb(var(--color-border-200));
width: 100%;
td,
th {
min-width: 1em;
border: 1px solid rgb(var(--color-border-200));
padding: 10px 15px;
vertical-align: top;
box-sizing: border-box;
position: relative;
transition: background-color 0.3s ease;
> * {
margin-bottom: 0;
}
}
th {
font-weight: bold;
text-align: left;
background-color: rgb(var(--color-primary-100));
}
td:hover {
background-color: rgba(var(--color-primary-300), 0.1);
}
.selectedCell:after {
z-index: 2;
position: absolute;
content: "";
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: rgba(var(--color-primary-300), 0.1);
pointer-events: none;
}
.column-resize-handle {
position: absolute;
right: -2px;
top: 0;
bottom: -2px;
width: 2px;
background-color: rgb(var(--color-primary-400));
pointer-events: none;
}
}
}
.tableWrapper {
overflow-x: auto;
}
.resize-cursor { .resize-cursor {
cursor: ew-resize; cursor: ew-resize;
cursor: col-resize; cursor: col-resize;

View File

@ -9,15 +9,15 @@
border-collapse: collapse; border-collapse: collapse;
table-layout: fixed; table-layout: fixed;
margin: 0; margin: 0;
margin-bottom: 3rem; margin-bottom: 1rem;
border: 1px solid rgba(var(--color-border-200)); border: 2px solid rgba(var(--color-border-300));
width: 100%; width: 100%;
} }
.tableWrapper table td, .tableWrapper table td,
.tableWrapper table th { .tableWrapper table th {
min-width: 1em; min-width: 1em;
border: 1px solid rgba(var(--color-border-200)); border: 1px solid rgba(var(--color-border-300));
padding: 10px 15px; padding: 10px 15px;
vertical-align: top; vertical-align: top;
box-sizing: border-box; box-sizing: border-box;
@ -43,7 +43,8 @@
.tableWrapper table th { .tableWrapper table th {
font-weight: bold; font-weight: bold;
text-align: left; text-align: left;
background-color: rgba(var(--color-primary-100)); background-color: #d9e4ff;
color: #171717;
} }
.tableWrapper table th * { .tableWrapper table th * {
@ -62,6 +63,35 @@
pointer-events: none; pointer-events: none;
} }
.colorPicker {
display: grid;
padding: 8px 8px;
grid-template-columns: repeat(6, 1fr);
gap: 5px;
}
.colorPickerLabel {
font-size: 0.85rem;
color: #6b7280;
padding: 8px 8px;
padding-bottom: 0px;
}
.colorPickerItem {
margin: 2px 0px;
width: 24px;
height: 24px;
border-radius: 4px;
border: none;
cursor: pointer;
}
.divider {
background-color: #e5e7eb;
height: 1px;
margin: 3px 0;
}
.tableWrapper table .column-resize-handle { .tableWrapper table .column-resize-handle {
position: absolute; position: absolute;
right: -2px; right: -2px;
@ -69,7 +99,7 @@
bottom: -2px; bottom: -2px;
width: 4px; width: 4px;
z-index: 99; z-index: 99;
background-color: rgba(var(--color-primary-400)); background-color: #d9e4ff;
pointer-events: none; pointer-events: none;
} }
@ -112,7 +142,7 @@
} }
.tableWrapper .tableControls .rowsControlDiv { .tableWrapper .tableControls .rowsControlDiv {
background-color: rgba(var(--color-primary-100)); background-color: #d9e4ff;
border: 1px solid rgba(var(--color-border-200)); border: 1px solid rgba(var(--color-border-200));
border-radius: 2px; border-radius: 2px;
background-size: 1.25rem; background-size: 1.25rem;
@ -127,7 +157,7 @@
} }
.tableWrapper .tableControls .columnsControlDiv { .tableWrapper .tableControls .columnsControlDiv {
background-color: rgba(var(--color-primary-100)); background-color: #d9e4ff;
border: 1px solid rgba(var(--color-border-200)); border: 1px solid rgba(var(--color-border-200));
border-radius: 2px; border-radius: 2px;
background-size: 1.25rem; background-size: 1.25rem;
@ -144,10 +174,12 @@
.tableWrapper .tableControls .tableColorPickerToolbox { .tableWrapper .tableControls .tableColorPickerToolbox {
border: 1px solid rgba(var(--color-border-300)); border: 1px solid rgba(var(--color-border-300));
background-color: rgba(var(--color-background-100)); background-color: rgba(var(--color-background-100));
border-radius: 5px;
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
padding: 0.25rem; padding: 0.25rem;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 200px; width: max-content;
gap: 0.25rem; gap: 0.25rem;
} }
@ -158,7 +190,7 @@
align-items: center; align-items: center;
gap: 0.5rem; gap: 0.5rem;
border: none; border: none;
padding: 0.1rem; padding: 0.3rem 0.5rem 0.1rem 0.1rem;
border-radius: 4px; border-radius: 4px;
cursor: pointer; cursor: pointer;
transition: all 0.2s; transition: all 0.2s;
@ -173,9 +205,7 @@
.tableWrapper .tableControls .tableColorPickerToolbox .toolboxItem .iconContainer, .tableWrapper .tableControls .tableColorPickerToolbox .toolboxItem .iconContainer,
.tableWrapper .tableControls .tableToolbox .toolboxItem .colorContainer, .tableWrapper .tableControls .tableToolbox .toolboxItem .colorContainer,
.tableWrapper .tableControls .tableColorPickerToolbox .toolboxItem .colorContainer { .tableWrapper .tableControls .tableColorPickerToolbox .toolboxItem .colorContainer {
border: 1px solid rgba(var(--color-border-300)); padding: 4px 0px;
border-radius: 3px;
padding: 4px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -187,8 +217,8 @@
.tableWrapper .tableControls .tableColorPickerToolbox .toolboxItem .iconContainer svg, .tableWrapper .tableControls .tableColorPickerToolbox .toolboxItem .iconContainer svg,
.tableWrapper .tableControls .tableToolbox .toolboxItem .colorContainer svg, .tableWrapper .tableControls .tableToolbox .toolboxItem .colorContainer svg,
.tableWrapper .tableControls .tableColorPickerToolbox .toolboxItem .colorContainer svg { .tableWrapper .tableControls .tableColorPickerToolbox .toolboxItem .colorContainer svg {
width: 2rem; width: 1rem;
height: 2rem; height: 1rem;
} }
.tableToolbox { .tableToolbox {

View File

@ -13,7 +13,7 @@ export const TableCell = Node.create<TableCellOptions>({
}; };
}, },
content: "paragraph+", content: "block+",
addAttributes() { addAttributes() {
return { return {
@ -33,7 +33,10 @@ export const TableCell = Node.create<TableCellOptions>({
}, },
}, },
background: { background: {
default: "none", default: null,
},
textColor: {
default: null,
}, },
}; };
}, },
@ -50,7 +53,7 @@ export const TableCell = Node.create<TableCellOptions>({
return [ return [
"td", "td",
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, { mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
style: `background-color: ${node.attrs.background}`, style: `background-color: ${node.attrs.background}; color: ${node.attrs.textColor}`,
}), }),
0, 0,
]; ];

View File

@ -33,7 +33,7 @@ export const TableHeader = Node.create<TableHeaderOptions>({
}, },
}, },
background: { background: {
default: "rgb(var(--color-primary-100))", default: "none",
}, },
}; };
}, },

View File

@ -13,6 +13,17 @@ export const TableRow = Node.create<TableRowOptions>({
}; };
}, },
addAttributes() {
return {
background: {
default: null,
},
textColor: {
default: null,
},
};
},
content: "(tableCell | tableHeader)*", content: "(tableCell | tableHeader)*",
tableRole: "row", tableRole: "row",
@ -22,6 +33,12 @@ export const TableRow = Node.create<TableRowOptions>({
}, },
renderHTML({ HTMLAttributes }) { renderHTML({ HTMLAttributes }) {
return ["tr", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]; const style = HTMLAttributes.background
? `background-color: ${HTMLAttributes.background}; color: ${HTMLAttributes.textColor}`
: "";
const attributes = mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, { style });
return ["tr", attributes, 0];
}, },
}); });

View File

@ -1,7 +1,7 @@
export const icons = { export const icons = {
colorPicker: `<svg xmlns="http://www.w3.org/2000/svg" length="24" viewBox="0 0 24 24" style="transform: ;msFilter:;"><path fill="rgb(var(--color-text-300))" d="M20 14c-.092.064-2 2.083-2 3.5 0 1.494.949 2.448 2 2.5.906.044 2-.891 2-2.5 0-1.5-1.908-3.436-2-3.5zM9.586 20c.378.378.88.586 1.414.586s1.036-.208 1.414-.586l7-7-.707-.707L11 4.586 8.707 2.293 7.293 3.707 9.586 6 4 11.586c-.378.378-.586.88-.586 1.414s.208 1.036.586 1.414L9.586 20zM11 7.414 16.586 13H5.414L11 7.414z"></path></svg>`, colorPicker: `<svg xmlns="http://www.w3.org/2000/svg" length="24" viewBox="0 0 24 24" style="transform: ;msFilter:;"><path fill="rgb(var(--color-text-300))" d="M20 14c-.092.064-2 2.083-2 3.5 0 1.494.949 2.448 2 2.5.906.044 2-.891 2-2.5 0-1.5-1.908-3.436-2-3.5zM9.586 20c.378.378.88.586 1.414.586s1.036-.208 1.414-.586l7-7-.707-.707L11 4.586 8.707 2.293 7.293 3.707 9.586 6 4 11.586c-.378.378-.586.88-.586 1.414s.208 1.036.586 1.414L9.586 20zM11 7.414 16.586 13H5.414L11 7.414z"></path></svg>`,
deleteColumn: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" length="24"><path fill="#e53e3e" d="M0 0H24V24H0z"/><path d="M12 3c.552 0 1 .448 1 1v8c.835-.628 1.874-1 3-1 2.761 0 5 2.239 5 5s-2.239 5-5 5c-1.032 0-1.99-.313-2.787-.848L13 20c0 .552-.448 1-1 1H6c-.552 0-1-.448-1-1V4c0-.552.448-1 1-1h6zm-1 2H7v14h4V5zm8 10h-6v2h6v-2z"/></svg>`, deleteColumn: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-trash-2"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/><line x1="10" x2="10" y1="11" y2="17"/><line x1="14" x2="14" y1="11" y2="17"/></svg>`,
deleteRow: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" length="24"><path fill="#e53e3e" d="M0 0H24V24H0z"/><path d="M20 5c.552 0 1 .448 1 1v6c0 .552-.448 1-1 1 .628.835 1 1.874 1 3 0 2.761-2.239 5-5 5s-5-2.239-5-5c0-1.126.372-2.165 1-3H4c-.552 0-1-.448-1-1V6c0-.552.448-1 1-1h16zm-7 10v2h6v-2h-6zm6-8H5v4h14V7z"/></svg>`, deleteRow: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-trash-2"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/><line x1="10" x2="10" y1="11" y2="17"/><line x1="14" x2="14" y1="11" y2="17"/></svg>`,
insertLeftTableIcon: `<svg insertLeftTableIcon: `<svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
length={24} length={24}
@ -35,6 +35,8 @@ export const icons = {
/> />
</svg> </svg>
`, `,
toggleColumnHeader: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="rgb(var(--color-text-300))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-toggle-right"><rect width="20" height="12" x="2" y="6" rx="6" ry="6"/><circle cx="16" cy="12" r="2"/></svg>`,
toggleRowHeader: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="rgb(var(--color-text-300))" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-toggle-right"><rect width="20" height="12" x="2" y="6" rx="6" ry="6"/><circle cx="16" cy="12" r="2"/></svg>`,
insertBottomTableIcon: `<svg insertBottomTableIcon: `<svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
length={24} length={24}

View File

@ -81,53 +81,75 @@ const defaultTippyOptions: Partial<Props> = {
placement: "right", placement: "right",
}; };
function setCellsBackgroundColor(editor: Editor, backgroundColor: string) { function setCellsBackgroundColor(editor: Editor, color: { backgroundColor: string; textColor: string }) {
return editor return editor
.chain() .chain()
.focus() .focus()
.updateAttributes("tableCell", { .updateAttributes("tableCell", {
background: backgroundColor, background: color.backgroundColor,
}) textColor: color.textColor,
.updateAttributes("tableHeader", {
background: backgroundColor,
}) })
.run(); .run();
} }
function setTableRowBackgroundColor(editor: Editor, color: { backgroundColor: string; textColor: string }) {
const { state, dispatch } = editor.view;
const { selection } = state;
if (!(selection instanceof CellSelection)) {
return false;
}
// Get the position of the hovered cell in the selection to determine the row.
const hoveredCell = selection.$headCell || selection.$anchorCell;
// Find the depth of the table row node
let rowDepth = hoveredCell.depth;
while (rowDepth > 0 && hoveredCell.node(rowDepth).type.name !== "tableRow") {
rowDepth--;
}
// If we couldn't find a tableRow node, we can't set the background color
if (hoveredCell.node(rowDepth).type.name !== "tableRow") {
return false;
}
// Get the position where the table row starts
const rowStartPos = hoveredCell.start(rowDepth);
// Create a transaction that sets the background color on the tableRow node.
const tr = state.tr.setNodeMarkup(rowStartPos - 1, null, {
...hoveredCell.node(rowDepth).attrs,
background: color.backgroundColor,
textColor: color.textColor,
});
dispatch(tr);
return true;
}
const columnsToolboxItems: ToolboxItem[] = [ const columnsToolboxItems: ToolboxItem[] = [
{ {
label: "Add Column Before", label: "Toggle column header",
icon: icons.toggleColumnHeader,
action: ({ editor }: { editor: Editor }) => editor.chain().focus().toggleHeaderColumn().run(),
},
{
label: "Add column before",
icon: icons.insertLeftTableIcon, icon: icons.insertLeftTableIcon,
action: ({ editor }: { editor: Editor }) => editor.chain().focus().addColumnBefore().run(), action: ({ editor }: { editor: Editor }) => editor.chain().focus().addColumnBefore().run(),
}, },
{ {
label: "Add Column After", label: "Add column after",
icon: icons.insertRightTableIcon, icon: icons.insertRightTableIcon,
action: ({ editor }: { editor: Editor }) => editor.chain().focus().addColumnAfter().run(), action: ({ editor }: { editor: Editor }) => editor.chain().focus().addColumnAfter().run(),
}, },
{ {
label: "Pick Column Color", label: "Pick color",
icon: icons.colorPicker, icon: "", // No icon needed for color picker
action: ({ action: (args: any) => {}, // Placeholder action; actual color picking is handled in `createToolbox`
editor,
triggerButton,
controlsContainer,
}: {
editor: Editor;
triggerButton: HTMLElement;
controlsContainer: Element;
}) => {
createColorPickerToolbox({
triggerButton,
tippyOptions: {
appendTo: controlsContainer,
},
onSelectColor: (color) => setCellsBackgroundColor(editor, color),
});
},
}, },
{ {
label: "Delete Column", label: "Delete column",
icon: icons.deleteColumn, icon: icons.deleteColumn,
action: ({ editor }: { editor: Editor }) => editor.chain().focus().deleteColumn().run(), action: ({ editor }: { editor: Editor }) => editor.chain().focus().deleteColumn().run(),
}, },
@ -135,35 +157,24 @@ const columnsToolboxItems: ToolboxItem[] = [
const rowsToolboxItems: ToolboxItem[] = [ const rowsToolboxItems: ToolboxItem[] = [
{ {
label: "Add Row Above", label: "Toggle row header",
icon: icons.toggleRowHeader,
action: ({ editor }: { editor: Editor }) => editor.chain().focus().toggleHeaderRow().run(),
},
{
label: "Add row above",
icon: icons.insertTopTableIcon, icon: icons.insertTopTableIcon,
action: ({ editor }: { editor: Editor }) => editor.chain().focus().addRowBefore().run(), action: ({ editor }: { editor: Editor }) => editor.chain().focus().addRowBefore().run(),
}, },
{ {
label: "Add Row Below", label: "Add row below",
icon: icons.insertBottomTableIcon, icon: icons.insertBottomTableIcon,
action: ({ editor }: { editor: Editor }) => editor.chain().focus().addRowAfter().run(), action: ({ editor }: { editor: Editor }) => editor.chain().focus().addRowAfter().run(),
}, },
{ {
label: "Pick Row Color", label: "Pick color",
icon: icons.colorPicker, icon: "",
action: ({ action: (args: any) => {}, // Placeholder action; actual color picking is handled in `createToolbox`
editor,
triggerButton,
controlsContainer,
}: {
editor: Editor;
triggerButton: HTMLButtonElement;
controlsContainer: Element | "parent" | ((ref: Element) => Element) | undefined;
}) => {
createColorPickerToolbox({
triggerButton,
tippyOptions: {
appendTo: controlsContainer,
},
onSelectColor: (color) => setCellsBackgroundColor(editor, color),
});
},
}, },
{ {
label: "Delete Row", label: "Delete Row",
@ -176,37 +187,57 @@ function createToolbox({
triggerButton, triggerButton,
items, items,
tippyOptions, tippyOptions,
onSelectColor,
onClickItem, onClickItem,
colors,
}: { }: {
triggerButton: Element | null; triggerButton: Element | null;
items: ToolboxItem[]; items: ToolboxItem[];
tippyOptions: any; tippyOptions: any;
onClickItem: (item: ToolboxItem) => void; onClickItem: (item: ToolboxItem) => void;
onSelectColor: (color: { backgroundColor: string; textColor: string }) => void;
colors: { [key: string]: { backgroundColor: string; textColor: string; icon?: string } };
}): Instance<Props> { }): Instance<Props> {
// @ts-expect-error // @ts-expect-error
const toolbox = tippy(triggerButton, { const toolbox = tippy(triggerButton, {
content: h( content: h(
"div", "div",
{ className: "tableToolbox" }, { className: "tableToolbox" },
items.map((item) => items.map((item, index) => {
h( if (item.label === "Pick color") {
"div", return h("div", { className: "flex flex-col" }, [
{ h("div", { className: "divider" }),
className: "toolboxItem", h("div", { className: "colorPickerLabel" }, item.label),
itemType: "button", h(
onClick() { "div",
onClickItem(item); { className: "colorPicker grid" },
Object.entries(colors).map(([colorName, colorValue]) =>
h("div", {
className: "colorPickerItem",
style: `background-color: ${colorValue.backgroundColor};
color: ${colorValue.textColor || "inherit"};`,
innerHTML: colorValue?.icon || "",
onClick: () => onSelectColor(colorValue),
})
)
),
h("div", { className: "divider" }),
]);
} else {
return h(
"div",
{
className: "toolboxItem",
itemType: "div",
onClick: () => onClickItem(item),
}, },
}, [
[ h("div", { className: "iconContainer", innerHTML: item.icon }),
h("div", { h("div", { className: "label" }, item.label),
className: "iconContainer", ]
innerHTML: item.icon, );
}), }
h("div", { className: "label" }, item.label), })
]
)
)
), ),
...tippyOptions, ...tippyOptions,
}); });
@ -214,71 +245,6 @@ function createToolbox({
return Array.isArray(toolbox) ? toolbox[0] : toolbox; return Array.isArray(toolbox) ? toolbox[0] : toolbox;
} }
function createColorPickerToolbox({
triggerButton,
tippyOptions,
onSelectColor = () => {},
}: {
triggerButton: HTMLElement;
tippyOptions: Partial<Props>;
onSelectColor?: (color: string) => void;
}) {
const items = {
Default: "rgb(var(--color-primary-100))",
Orange: "#FFE5D1",
Grey: "#F1F1F1",
Yellow: "#FEF3C7",
Green: "#DCFCE7",
Red: "#FFDDDD",
Blue: "#D9E4FF",
Pink: "#FFE8FA",
Purple: "#E8DAFB",
};
const colorPicker = tippy(triggerButton, {
...defaultTippyOptions,
content: h(
"div",
{ className: "tableColorPickerToolbox" },
Object.entries(items).map(([key, value]) =>
h(
"div",
{
className: "toolboxItem",
itemType: "button",
onClick: () => {
onSelectColor(value);
colorPicker.hide();
},
},
[
h("div", {
className: "colorContainer",
style: {
backgroundColor: value,
},
}),
h(
"div",
{
className: "label",
},
key
),
]
)
)
),
onHidden: (instance) => {
instance.destroy();
},
showOnCreate: true,
...tippyOptions,
});
return colorPicker;
}
export class TableView implements NodeView { export class TableView implements NodeView {
node: ProseMirrorNode; node: ProseMirrorNode;
cellMinWidth: number; cellMinWidth: number;
@ -347,10 +313,27 @@ export class TableView implements NodeView {
this.rowsControl, this.rowsControl,
this.columnsControl this.columnsControl
); );
const columnColors = {
Blue: { backgroundColor: "#D9E4FF", textColor: "#171717" },
Orange: { backgroundColor: "#FFEDD5", textColor: "#171717" },
Grey: { backgroundColor: "#F1F1F1", textColor: "#171717" },
Yellow: { backgroundColor: "#FEF3C7", textColor: "#171717" },
Green: { backgroundColor: "#DCFCE7", textColor: "#171717" },
Red: { backgroundColor: "#FFDDDD", textColor: "#171717" },
Pink: { backgroundColor: "#FFE8FA", textColor: "#171717" },
Purple: { backgroundColor: "#E8DAFB", textColor: "#171717" },
None: {
backgroundColor: "none",
textColor: "none",
icon: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="gray" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-ban"><circle cx="12" cy="12" r="10"/><path d="m4.9 4.9 14.2 14.2"/></svg>`,
},
};
this.columnsToolbox = createToolbox({ this.columnsToolbox = createToolbox({
triggerButton: this.columnsControl.querySelector(".columnsControlDiv"), triggerButton: this.columnsControl.querySelector(".columnsControlDiv"),
items: columnsToolboxItems, items: columnsToolboxItems,
colors: columnColors,
onSelectColor: (color) => setCellsBackgroundColor(this.editor, color),
tippyOptions: { tippyOptions: {
...defaultTippyOptions, ...defaultTippyOptions,
appendTo: this.controls, appendTo: this.controls,
@ -368,10 +351,12 @@ export class TableView implements NodeView {
this.rowsToolbox = createToolbox({ this.rowsToolbox = createToolbox({
triggerButton: this.rowsControl.firstElementChild, triggerButton: this.rowsControl.firstElementChild,
items: rowsToolboxItems, items: rowsToolboxItems,
colors: columnColors,
tippyOptions: { tippyOptions: {
...defaultTippyOptions, ...defaultTippyOptions,
appendTo: this.controls, appendTo: this.controls,
}, },
onSelectColor: (color) => setTableRowBackgroundColor(editor, color),
onClickItem: (item) => { onClickItem: (item) => {
item.action({ item.action({
editor: this.editor, editor: this.editor,
@ -383,8 +368,6 @@ export class TableView implements NodeView {
}); });
} }
// Table
this.colgroup = h( this.colgroup = h(
"colgroup", "colgroup",
null, null,
@ -437,16 +420,19 @@ export class TableView implements NodeView {
} }
updateControls() { updateControls() {
const { hoveredTable: table, hoveredCell: cell } = Object.values(this.decorations).reduce((acc, curr) => { const { hoveredTable: table, hoveredCell: cell } = Object.values(this.decorations).reduce(
if (curr.spec.hoveredCell !== undefined) { (acc, curr) => {
acc["hoveredCell"] = curr.spec.hoveredCell; if (curr.spec.hoveredCell !== undefined) {
} acc["hoveredCell"] = curr.spec.hoveredCell;
}
if (curr.spec.hoveredTable !== undefined) { if (curr.spec.hoveredTable !== undefined) {
acc["hoveredTable"] = curr.spec.hoveredTable; acc["hoveredTable"] = curr.spec.hoveredTable;
} }
return acc; return acc;
}, {} as Record<string, HTMLElement>) as any; },
{} as Record<string, HTMLElement>
) as any;
if (table === undefined || cell === undefined) { if (table === undefined || cell === undefined) {
return this.root.classList.add("controls--disabled"); return this.root.classList.add("controls--disabled");
@ -457,12 +443,12 @@ export class TableView implements NodeView {
const cellDom = this.editor.view.nodeDOM(cell.pos) as HTMLElement; const cellDom = this.editor.view.nodeDOM(cell.pos) as HTMLElement;
if (!this.table) { if (!this.table || !cellDom) {
return; return;
} }
const tableRect = this.table.getBoundingClientRect(); const tableRect = this.table?.getBoundingClientRect();
const cellRect = cellDom.getBoundingClientRect(); const cellRect = cellDom?.getBoundingClientRect();
if (this.columnsControl) { if (this.columnsControl) {
this.columnsControl.style.left = `${cellRect.left - tableRect.left - this.table.parentElement!.scrollLeft}px`; this.columnsControl.style.left = `${cellRect.left - tableRect.left - this.table.parentElement!.scrollLeft}px`;

View File

@ -107,10 +107,9 @@ export const Table = Node.create({
addCommands() { addCommands() {
return { return {
insertTable: insertTable:
({ rows = 3, cols = 3, withHeaderRow = true } = {}) => ({ rows = 3, cols = 3, withHeaderRow = false } = {}) =>
({ tr, dispatch, editor }) => { ({ tr, dispatch, editor }) => {
const node = createTable(editor.schema, rows, cols, withHeaderRow); const node = createTable(editor.schema, rows, cols, withHeaderRow);
if (dispatch) { if (dispatch) {
const offset = tr.selection.anchor + 1; const offset = tr.selection.anchor + 1;

View File

@ -42,15 +42,6 @@ export function CoreEditorProps(
return false; return false;
}, },
handleDrop: (view, event, _slice, moved) => { handleDrop: (view, event, _slice, moved) => {
if (typeof window !== "undefined") {
const selection: any = window?.getSelection();
if (selection.rangeCount !== 0) {
const range = selection.getRangeAt(0);
if (findTableAncestor(range.startContainer)) {
return;
}
}
}
if (!moved && event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files[0]) { if (!moved && event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files[0]) {
event.preventDefault(); event.preventDefault();
const file = event.dataTransfer.files[0]; const file = event.dataTransfer.files[0];

View File

@ -48,34 +48,12 @@ export const FixedMenu = (props: EditorBubbleMenuProps) => {
function getComplexItems(): BubbleMenuItem[] { function getComplexItems(): BubbleMenuItem[] {
const items: BubbleMenuItem[] = [TableItem(editor)]; const items: BubbleMenuItem[] = [TableItem(editor)];
if (shouldShowImageItem()) { items.push(ImageItem(editor, uploadFile, setIsSubmitting));
items.push(ImageItem(editor, uploadFile, setIsSubmitting));
}
return items; return items;
} }
const complexItems: BubbleMenuItem[] = getComplexItems(); const complexItems: BubbleMenuItem[] = getComplexItems();
function shouldShowImageItem(): boolean {
if (typeof window !== "undefined") {
const selectionRange: any = window?.getSelection();
const { selection } = props.editor.state;
if (selectionRange.rangeCount !== 0) {
const range = selectionRange.getRangeAt(0);
if (findTableAncestor(range.startContainer)) {
return false;
}
if (isCellSelection(selection)) {
return false;
}
}
return true;
}
return false;
}
return ( return (
<div className="flex flex-wrap items-center divide-x divide-custom-border-200"> <div className="flex flex-wrap items-center divide-x divide-custom-border-200">
<div className="flex items-center gap-0.5 pr-2"> <div className="flex items-center gap-0.5 pr-2">

View File

@ -35,7 +35,7 @@ export interface DragHandleOptions {
} }
function absoluteRect(node: Element) { function absoluteRect(node: Element) {
const data = node.getBoundingClientRect(); const data = node?.getBoundingClientRect();
return { return {
top: data.top, top: data.top,
@ -65,7 +65,7 @@ function nodeDOMAtCoords(coords: { x: number; y: number }) {
} }
function nodePosAtDOM(node: Element, view: EditorView) { function nodePosAtDOM(node: Element, view: EditorView) {
const boundingRect = node.getBoundingClientRect(); const boundingRect = node?.getBoundingClientRect();
if (node.nodeName === "IMG") { if (node.nodeName === "IMG") {
return view.posAtCoords({ return view.posAtCoords({

View File

@ -60,34 +60,13 @@ export const FixedMenu = (props: EditorBubbleMenuProps) => {
function getComplexItems(): BubbleMenuItem[] { function getComplexItems(): BubbleMenuItem[] {
const items: BubbleMenuItem[] = [TableItem(props.editor)]; const items: BubbleMenuItem[] = [TableItem(props.editor)];
if (shouldShowImageItem()) { items.push(ImageItem(props.editor, props.uploadFile, props.setIsSubmitting));
items.push(ImageItem(props.editor, props.uploadFile, props.setIsSubmitting));
}
return items; return items;
} }
const complexItems: BubbleMenuItem[] = getComplexItems(); const complexItems: BubbleMenuItem[] = getComplexItems();
function shouldShowImageItem(): boolean {
if (typeof window !== "undefined") {
const selectionRange: any = window?.getSelection();
const { selection } = props.editor.state;
if (selectionRange.rangeCount !== 0) {
const range = selectionRange.getRangeAt(0);
if (findTableAncestor(range.startContainer)) {
return false;
}
if (isCellSelection(selection)) {
return false;
}
}
return true;
}
return false;
}
const handleAccessChange = (accessKey: string) => { const handleAccessChange = (accessKey: string) => {
props.commentAccessSpecifier?.onAccessChange(accessKey); props.commentAccessSpecifier?.onAccessChange(accessKey);
}; };