Table improvements (#6958)

* Header toggling, resizable columns

* Allow all blocks in table cells, disable column resizing in read-only

* Fixed dynamic scroll shadows

* Refactor, scroll styling

* fix scrolling, tweaks

* fix: Table layout lost on sort

* fix: Caching of grip decorators

* refactor

* stash

* fix first render shadows

* stash

* First add column grip, styles

* Just add column/row click handlers left

* fix: isTableSelected for single cell table

* Refactor mousedown handlers

* fix: 'Add row before' command missing on first row

* fix overflow on rhs

* fix: Error clicking column grip when menu is open

* Hide table controls when printing

* Restore table header background

* fix: Header behavior when adding columns and rows at the edges

* Tweak header styling

* fix: Serialize and parsing of column attributes when copy/pasting
fix: Column width is lost when changing column alignment
This commit is contained in:
Tom Moor
2024-05-31 17:52:39 -04:00
committed by GitHub
parent 1db46f4aac
commit da19054555
27 changed files with 1020 additions and 351 deletions

View File

@@ -1,6 +1,7 @@
/* eslint-disable no-irregular-whitespace */
import { lighten, transparentize } from "polished";
import styled, { DefaultTheme, css, keyframes } from "styled-components";
import { EditorStyleHelper } from "../styles/EditorStyleHelper";
import { videoStyle } from "./Video";
export type Props = {
@@ -13,6 +14,11 @@ export type Props = {
theme: DefaultTheme;
};
export const fadeIn = keyframes`
from { opacity: 0; }
to { opacity: 1; }
`;
export const pulse = keyframes`
0% { box-shadow: 0 0 0 1px rgba(255, 213, 0, 0.75) }
50% { box-shadow: 0 0 0 4px rgba(255, 213, 0, 0.75) }
@@ -267,7 +273,7 @@ const emailStyle = (props: Props) => css`
}
`;
const style = (props: Props) => `
const style = (props: Props) => css`
flex-grow: ${props.grow ? 1 : 0};
justify-content: start;
color: ${props.theme.text};
@@ -563,6 +569,40 @@ iframe.embed {
}
}
.${EditorStyleHelper.tableFullWidth} {
transform: translateX(calc(50% + ${
EditorStyleHelper.padding
}px + var(--container-width) * -0.5));
.${EditorStyleHelper.tableScrollable},
table {
width: calc(var(--container-width) - ${EditorStyleHelper.padding * 2}px);
}
&.${EditorStyleHelper.tableShadowRight}::after {
left: calc(var(--container-width) - ${EditorStyleHelper.padding * 3}px);
}
}
.column-resize-handle {
animation: ${fadeIn} 150ms ease-in-out;
${props.readOnly ? "display: none;" : ""}
position: absolute;
right: -1px;
top: 0;
bottom: -1px;
width: 2px;
z-index: 20;
background-color: ${props.theme.text};
pointer-events: none;
}
.resize-cursor {
${props.readOnly ? "pointer-events: none;" : ""}
cursor: ew-resize;
cursor: col-resize;
}
.ProseMirror-hideselection *::selection {
background: transparent;
}
@@ -1284,26 +1324,25 @@ table {
tr {
position: relative;
border-bottom: 1px solid ${props.theme.tableDivider};
}
tr:first-of-type {
background: ${props.theme.secondaryBackground};
}
th {
background: transparent;
border-bottom: 1px solid ${props.theme.divider};
}
td,
th {
position: relative;
vertical-align: top;
border: 1px solid ${props.theme.tableDivider};
border: 1px solid ${props.theme.divider};
position: relative;
padding: 4px 8px;
text-align: ${props.rtl ? "right" : "left"};
min-width: 100px;
font-weight: normal;
}
th {
background: ${transparentize(0.75, props.theme.divider)};
color: ${props.theme.textSecondary};
font-weight: 500;
}
td .component-embed {
@@ -1320,7 +1359,135 @@ table {
background-clip: padding-box;
}
.grip-column {
.${EditorStyleHelper.tableAddRow},
.${EditorStyleHelper.tableAddColumn},
.${EditorStyleHelper.tableGrip},
.${EditorStyleHelper.tableGripColumn},
.${EditorStyleHelper.tableGripRow} {
@media print {
display: none;
}
}
.${EditorStyleHelper.tableAddRow},
.${EditorStyleHelper.tableAddColumn} {
display: block;
position: absolute;
background: ${props.theme.accent};
cursor: var(--pointer);
&:hover::after {
width: 16px;
height: 16px;
z-index: 20;
background-color: ${props.theme.accent};
background-size: 16px 16px;
background-position: 50% 50%;
background-image: url("data:image/svg+xml;base64,${btoa(
'<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M12 5C11.4477 5 11 5.44772 11 6V11H6C5.44772 11 5 11.4477 5 12C5 12.5523 5.44772 13 6 13H11V18C11 18.5523 11.4477 19 12 19C12.5523 19 13 18.5523 13 18V13H18C18.5523 13 19 12.5523 19 12C19 11.4477 18.5523 11 18 11H13V6C13 5.44772 12.5523 5 12 5Z" fill="white"/></svg>'
)}")
}
// extra clickable area
&::before {
content: "";
display: block;
cursor: var(--pointer);
position: absolute;
width: 24px;
height: 24px;
}
}
.${EditorStyleHelper.tableAddRow} {
bottom: -1px;
left: -16px;
width: 0;
height: 2px;
&::after {
content: "";
position: absolute;
bottom: -1px;
left: -10px;
width: 4px;
height: 4px;
display: ${props.readOnly ? "none" : "block"};
border-radius: 100%;
background-color: ${props.theme.divider};
}
&:hover {
width: calc(var(--table-width) - ${EditorStyleHelper.padding * 1.5}px);
}
&:hover::after {
bottom: -7.5px;
left: -16px;
}
// extra clickable area
&::before {
bottom: -12px;
left: -18px;
}
&.first {
bottom: auto;
top: -1px;
&::before {
bottom: auto;
top: -12px;
}
}
}
.${EditorStyleHelper.tableAddColumn} {
top: -16px;
right: -1px;
width: 2px;
height: 0;
&::after {
content: "";
position: absolute;
top: -10px;
right: -1px;
width: 4px;
height: 4px;
display: ${props.readOnly ? "none" : "block"};
border-radius: 100%;
background-color: ${props.theme.divider};
}
&:hover {
height: calc(var(--table-height) - ${EditorStyleHelper.padding}px + 6px);
}
&:hover::after {
top: -16px;
right: -7px;
}
// extra clickable area
&::before {
top: -16px;
right: -12px;
}
&.first {
right: auto;
left: -1px;
&::before {
right: auto;
left: -12px;
}
}
}
.${EditorStyleHelper.tableGripColumn} {
/* usage of ::after for all of the table grips works around a bug in
* prosemirror-tables that causes Safari to hang when selecting a cell
* in an empty table:
@@ -1333,8 +1500,7 @@ table {
${props.rtl ? "right" : "left"}: 0;
width: 100%;
height: 12px;
background: ${props.theme.tableDivider};
border-bottom: 3px solid ${props.theme.background};
background: ${props.theme.divider};
display: ${props.readOnly ? "none" : "block"};
}
@@ -1343,16 +1509,18 @@ table {
}
&.first::after {
border-top-${props.rtl ? "right" : "left"}-radius: 3px;
border-bottom-${props.rtl ? "right" : "left"}-radius: 3px;
}
&.last::after {
border-top-${props.rtl ? "left" : "right"}-radius: 3px;
border-bottom-${props.rtl ? "left" : "right"}-radius: 3px;
}
&.selected::after {
background: ${props.theme.tableSelected};
}
}
.grip-row {
.${EditorStyleHelper.tableGripRow} {
&::after {
content: "";
cursor: var(--pointer);
@@ -1361,8 +1529,7 @@ table {
top: 0;
height: 100%;
width: 12px;
background: ${props.theme.tableDivider};
border-${props.rtl ? "left" : "right"}: 3px solid;
background: ${props.theme.divider};
border-color: ${props.theme.background};
display: ${props.readOnly ? "none" : "block"};
}
@@ -1371,21 +1538,23 @@ table {
background: ${props.theme.text};
}
&.first::after {
border-top-${props.rtl ? "right" : "left"}-radius: 3px;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
}
&.last::after {
border-bottom-${props.rtl ? "right" : "left"}-radius: 3px;
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
}
&.selected::after {
background: ${props.theme.tableSelected};
}
}
.grip-table {
.${EditorStyleHelper.tableGrip} {
&::after {
content: "";
cursor: var(--pointer);
background: ${props.theme.tableDivider};
background: ${props.theme.divider};
width: 13px;
height: 13px;
border-radius: 13px;
@@ -1394,6 +1563,7 @@ table {
top: -18px;
${props.rtl ? "right" : "left"}: -18px;
display: ${props.readOnly ? "none" : "block"};
z-index: 10;
}
&:hover::after {
@@ -1405,11 +1575,22 @@ table {
}
}
.scrollable-wrapper {
.${EditorStyleHelper.table} {
position: relative;
margin: 0.5em 0px;
}
.${EditorStyleHelper.tableScrollable} {
position: relative;
margin: -1em ${-EditorStyleHelper.padding}px -0.5em;
scrollbar-width: thin;
scrollbar-color: transparent transparent;
overflow-y: hidden;
overflow-x: auto;
padding-top: 1em;
padding-bottom: .5em;
padding-left: ${EditorStyleHelper.padding}px;
padding-right: ${EditorStyleHelper.padding}px;
transition: border 250ms ease-in-out 0s;
&:hover {
scrollbar-color: ${props.theme.scrollbarThumb} ${
@@ -1438,39 +1619,36 @@ table {
}
}
.scrollable {
overflow-y: hidden;
overflow-x: auto;
padding-${props.rtl ? "right" : "left"}: 1em;
margin-${props.rtl ? "right" : "left"}: -1em;
transition: border 250ms ease-in-out 0s;
}
.scrollable-shadow {
.${EditorStyleHelper.tableShadowLeft}::before,
.${EditorStyleHelper.tableShadowRight}::after {
content: "";
position: absolute;
top: 16px;
top: 1px;
bottom: 0;
${props.rtl ? "right" : "left"}: -1em;
width: 32px;
z-index: 1;
z-index: 20;
transition: box-shadow 250ms ease-in-out;
border: 0px solid transparent;
pointer-events: none;
}
&.left {
box-shadow: 16px 0 16px -16px inset rgba(0, 0, 0, ${
props.theme.isDark ? 1 : 0.25
});
border-left: 1em solid ${props.theme.background};
}
.${EditorStyleHelper.tableShadowLeft}::before {
left: -${EditorStyleHelper.padding}px;
right: auto;
box-shadow: 16px 0 16px -16px inset rgba(0, 0, 0, ${
props.theme.isDark ? 1 : 0.25
});
border-left: ${EditorStyleHelper.padding}px solid ${props.theme.background};
}
&.right {
right: 0;
left: auto;
box-shadow: -16px 0 16px -16px inset rgba(0, 0, 0, ${
props.theme.isDark ? 1 : 0.25
});
}
.${EditorStyleHelper.tableShadowRight}::after {
right: -${EditorStyleHelper.padding}px;
left: auto;
box-shadow: -16px 0 16px -16px inset rgba(0, 0, 0, ${
props.theme.isDark ? 1 : 0.25
});
border-right: ${EditorStyleHelper.padding}px solid ${props.theme.background};
}
.block-menu-trigger {