Merge pull request #14596 from overleaf/mj-manual-cell-widths

[visual] Manually calculate column widths in table generator

GitOrigin-RevId: ce42219098d56cca2fbdb1bef6ad33a11d79fb25
This commit is contained in:
Mathias Jakobsen 2023-09-04 09:03:01 +01:00 committed by Copybot
parent 2516f271b1
commit c8413c6cc6
2 changed files with 61 additions and 5 deletions

View file

@ -32,6 +32,9 @@ type NavigationMap = {
}
const isMac = /Mac/.test(window.navigator?.platform)
const MINIMUM_CELL_WIDTH_CHARACTERS = 15
const MINIMUM_EDITING_CELL_WIDTH_CHARACTERS = 20
const CELL_WIDTH_BUFFER = 3 // characters
export const Table: FC = () => {
const { selection, setSelection } = useSelectionContext()
@ -46,6 +49,57 @@ export const Table: FC = () => {
const { table: tableData } = useTableContext()
const tableRef = useRef<HTMLTableElement>(null)
const view = useCodeMirrorViewContext()
const cellWidths: number[] = useMemo(() => {
const columns = Array.from(
{ length: tableData.columns.length },
() => MINIMUM_CELL_WIDTH_CHARACTERS
)
// First pass, calculate the optimal width of each column. For the cell
// we're editing, make sure there's space to write into as well
// (MINIMUM_EDITING_CELL_WIDTH_CHARACTERS)
for (let row = 0; row < tableData.rows.length; ++row) {
for (
let i = 0;
i < tableData.columns.length;
i += tableData.getCell(row, i).multiColumn?.columnSpan ?? 1
) {
const columnSpan =
tableData.getCell(row, i).multiColumn?.columnSpan ?? 1
let contentLength =
tableData.getCell(row, i).content.length + CELL_WIDTH_BUFFER
if (cellData?.rowIndex === row && cellData?.cellIndex === i) {
contentLength = Math.max(
contentLength,
Math.min(
cellData.content.length + CELL_WIDTH_BUFFER,
MINIMUM_EDITING_CELL_WIDTH_CHARACTERS
)
)
}
for (let j = 0; j < columnSpan; ++j) {
columns[i + j] = Math.max(columns[i + j], contentLength / columnSpan)
}
}
}
// Second pass, use a logarithmic scale to not drown out narrow columns
// completely
const total = columns.reduce((a, b) => a + b, 0)
for (let i = 0; i < columns.length; ++i) {
columns[i] = Math.log2(columns[i])
}
// Third pass, normalize the columns to the total width of the table
const totalLog = columns.reduce((a, b) => a + b, 0)
for (let i = 0; i < columns.length; ++i) {
columns[i] = Math.round((columns[i] / totalLog) * total)
}
return columns
}, [
tableData,
cellData?.cellIndex,
cellData?.rowIndex,
cellData?.content.length,
])
const navigation: NavigationMap = useMemo(
() => ({
@ -267,13 +321,16 @@ export const Table: FC = () => {
ref={tableRef}
>
<thead>
{/* A workaround for a chrome bug where it will not respect colspan
{/* A workaround for a chrome bug where it will not respect colspan
unless there is a row filled with cells without colspan */}
<tr className="table-generator-filler-row">
{/* A td for the row selector */}
<td />
{tableData.columns.map((_, columnIndex) => (
<td key={columnIndex} />
<td
key={columnIndex}
style={{ width: `${cellWidths[columnIndex]}ch` }}
/>
))}
</tr>
<tr>

View file

@ -119,7 +119,7 @@ export const tableGeneratorTheme = EditorView.baseTheme({
'.table-generator-table': {
'table-layout': 'fixed',
'max-width': '80%',
'max-width': '95%',
margin: '0 auto',
cursor: 'default',
@ -127,7 +127,6 @@ export const tableGeneratorTheme = EditorView.baseTheme({
'&:not(.editing)': {
padding: '0 0.25em',
},
'max-width': '200px',
'vertical-align': 'top',
'&.alignment-left': {
@ -281,9 +280,9 @@ export const tableGeneratorTheme = EditorView.baseTheme({
},
'.table-generator-cell-input': {
'max-width': 'calc(200px - 0.5em)',
'background-color': 'transparent',
width: '100%',
'text-align': 'inherit',
height: '1.5em',
'min-height': '100%',
border: '1px solid var(--table-generator-toolbar-shadow-color)',