import type { EditorState } from '../richTextTypes' export function insertTable(rows: number, cols: number, state: EditorState) { if (!state.doc) { return } const table = state.doc.createElement('table') table.style.width = '100%' table.style.borderCollapse = 'collapse' table.style.marginBottom = '1rem' // Add a header row const thead = state.doc.createElement('thead') const headerRow = thead.insertRow() for (let j = 0; j < cols; j++) { const th = state.doc.createElement('th') th.innerHTML = `Header ${j + 1}` th.style.padding = '8px' th.style.border = '1px solid var(--bgl-border-color, #ddd)' th.style.backgroundColor = 'var(--bgl-gray-light, #f4f4f4)' headerRow.appendChild(th) } table.appendChild(thead) // Add body rows const tbody = state.doc.createElement('tbody') for (let i = 0; i < rows; i++) { const row = tbody.insertRow() for (let j = 0; j < cols; j++) { const cell = row.insertCell() cell.innerHTML = ' ' cell.style.padding = '8px' cell.style.border = '1px solid var(--bgl-border-color, #ddd)' } } table.appendChild(tbody) // Insert the table at the current selection const { range } = state if (range) { range.insertNode(table) // Move cursor inside first cell for convenience if (state.doc.getSelection()) { const firstCell = table.querySelector('td') if (firstCell) { range.selectNodeContents(firstCell) range.collapse(true) const selection = state.doc.getSelection() if (selection) { selection.removeAllRanges() selection.addRange(range) } } } } else { state.doc.body.appendChild(table) } state.content = state.doc.body.innerHTML } export function mergeCells(range: Range, doc: Document) { const cells = Array.from(range.cloneContents().querySelectorAll('td')) if (cells.length < 2) { return } const firstCell = range.startContainer.parentElement?.closest('td') if (!firstCell) { return } firstCell.colSpan = cells.length firstCell.innerHTML = cells.map(cell => cell.innerHTML).join(' ') cells.slice(1).forEach((cell) => { const actualCell = doc.getElementById(cell.id) actualCell?.remove() }) // Update state content const state = (doc as any).editorState as EditorState if (state) { state.content = doc.body.innerHTML } } export function splitCell(range: Range, doc: Document) { const cell = range.startContainer.parentElement?.closest('td') if (!cell || !cell.colSpan || cell.colSpan === 1) { return } const newCells = new Array(cell.colSpan - 1).fill(0).map(() => { const newCell = doc.createElement('td') newCell.style.border = '1px solid var(--bgl-border-color)' newCell.style.padding = '8px' newCell.innerHTML = ' ' return newCell }) cell.colSpan = 1 cell.insertAdjacentElement('afterend', newCells[0]) newCells.slice(1).forEach((newCell) => { newCells[0].insertAdjacentElement('afterend', newCell) }) // Update state content const state = (doc as any).editorState as EditorState if (state) { state.content = doc.body.innerHTML } } export function addRow(position: 'before' | 'after', range: Range, doc: Document) { const cell = range.startContainer.parentElement?.closest('td') if (!cell) { return } const row = cell.parentElement const table = row?.parentElement if (!row || !table) { return } const newRow = row.cloneNode(true) as HTMLTableRowElement Array.from(newRow.cells).forEach(cell => cell.innerHTML = ' ') row.insertAdjacentElement(position === 'before' ? 'beforebegin' : 'afterend', newRow) // Update state content const state = (doc as any).editorState as EditorState if (state) { state.content = doc.body.innerHTML } } export function deleteRow(range: Range) { const cell = range.startContainer.parentElement?.closest('td') if (!cell) { return } const row = cell.parentElement if (!row) { return } const doc = row.ownerDocument row.remove() // Update state content const state = (doc as any).editorState as EditorState if (state) { state.content = doc.body.innerHTML } } export function deleteTable(range: Range) { const cell = range.startContainer.parentElement?.closest('td') if (!cell) { return } const table = cell.closest('table') if (!table) { return } const doc = table.ownerDocument table.remove() // Update state content const state = (doc as any).editorState as EditorState if (state) { state.content = doc.body.innerHTML } } export function insertColumn(position: 'before' | 'after', range: Range) { const cell = range.startContainer.parentElement?.closest('td') if (!cell) { return } const table = cell.closest('table') if (!table) { return } const columnIndex = cell.cellIndex const { rows } = table for (let i = 0; i < rows.length; i++) { const newCell = rows[i].insertCell(position === 'after' ? columnIndex + 1 : columnIndex) newCell.innerHTML = ' ' newCell.style.border = '1px solid var(--bgl-border-color)' newCell.style.padding = '8px' } // Update state content const state = (table.ownerDocument as any).editorState as EditorState if (state) { state.content = table.ownerDocument.body.innerHTML } } export function deleteColumn(range: Range) { const cell = range.startContainer.parentElement?.closest('td') if (!cell) { return } const table = cell.closest('table') if (!table) { return } const columnIndex = cell.cellIndex const { rows } = table const doc = table.ownerDocument for (let i = 0; i < rows.length; i++) { rows[i].deleteCell(columnIndex) } // If no columns left, remove the table if (rows[0].cells.length === 0) { table.remove() } // Update state content const state = (doc as any).editorState as EditorState if (state) { state.content = doc.body.innerHTML } } export function alignColumn(range: Range, alignment: 'start' | 'center' | 'end' | 'justify') { const cell = range.startContainer.parentElement?.closest('td') if (!cell) { return } const table = cell.closest('table') if (!table) { return } const columnIndex = cell.cellIndex const { rows } = table for (let i = 0; i < rows.length; i++) { const cell = rows[i].cells[columnIndex] if (cell) { cell.style.textAlign = alignment } } // Update state content const state = (table.ownerDocument as any).editorState as EditorState if (state) { state.content = table.ownerDocument.body.innerHTML } }