forked from github/plane
[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:
parent
5c64933927
commit
e0a4d7a12a
@ -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) => {
|
||||||
|
@ -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;
|
||||||
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
];
|
];
|
||||||
|
@ -33,7 +33,7 @@ export const TableHeader = Node.create<TableHeaderOptions>({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
background: {
|
background: {
|
||||||
default: "rgb(var(--color-primary-100))",
|
default: "none",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -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];
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -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}
|
||||||
|
@ -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) => {
|
||||||
|
if (item.label === "Pick color") {
|
||||||
|
return h("div", { className: "flex flex-col" }, [
|
||||||
|
h("div", { className: "divider" }),
|
||||||
|
h("div", { className: "colorPickerLabel" }, item.label),
|
||||||
h(
|
h(
|
||||||
|
"div",
|
||||||
|
{ 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",
|
"div",
|
||||||
{
|
{
|
||||||
className: "toolboxItem",
|
className: "toolboxItem",
|
||||||
itemType: "button",
|
itemType: "div",
|
||||||
onClick() {
|
onClick: () => onClickItem(item),
|
||||||
onClickItem(item);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
h("div", {
|
h("div", { className: "iconContainer", innerHTML: item.icon }),
|
||||||
className: "iconContainer",
|
|
||||||
innerHTML: item.icon,
|
|
||||||
}),
|
|
||||||
h("div", { className: "label" }, item.label),
|
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,7 +420,8 @@ 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(
|
||||||
|
(acc, curr) => {
|
||||||
if (curr.spec.hoveredCell !== undefined) {
|
if (curr.spec.hoveredCell !== undefined) {
|
||||||
acc["hoveredCell"] = curr.spec.hoveredCell;
|
acc["hoveredCell"] = curr.spec.hoveredCell;
|
||||||
}
|
}
|
||||||
@ -446,7 +430,9 @@ export class TableView implements NodeView {
|
|||||||
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`;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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];
|
||||||
|
@ -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">
|
||||||
|
@ -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({
|
||||||
|
@ -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);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user