|
|
@@ -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>
|