UPDATE: Double click resizes columns/rows automatically

This commit is contained in:
Nicolás Hatcher
2025-02-17 20:17:57 +01:00
committed by Nicolás Hatcher Andrés
parent 4095b7db6e
commit fc7335707a
4 changed files with 102 additions and 13 deletions

View File

@@ -307,7 +307,13 @@ impl Model {
// This two are only used when we want to compute the automatic width of a column or height of a row // This two are only used when we want to compute the automatic width of a column or height of a row
#[wasm_bindgen(js_name = "getRowsWithData")] #[wasm_bindgen(js_name = "getRowsWithData")]
pub fn get_rows_with_data(&self, sheet: u32, column: i32) -> Result<Vec<i32>, JsError> { pub fn get_rows_with_data(&self, sheet: u32, column: i32) -> Result<Vec<i32>, JsError> {
let sheet_data = &self.model.get_model().workbook.worksheet(sheet).map_err(to_js_error)?.sheet_data; let sheet_data = &self
.model
.get_model()
.workbook
.worksheet(sheet)
.map_err(to_js_error)?
.sheet_data;
Ok(sheet_data Ok(sheet_data
.iter() .iter()
.filter(|(_, data)| data.contains_key(&column)) .filter(|(_, data)| data.contains_key(&column))
@@ -317,9 +323,12 @@ impl Model {
#[wasm_bindgen(js_name = "getColumnsWithData")] #[wasm_bindgen(js_name = "getColumnsWithData")]
pub fn get_columns_with_data(&self, sheet: u32, row: i32) -> Result<Vec<i32>, JsError> { pub fn get_columns_with_data(&self, sheet: u32, row: i32) -> Result<Vec<i32>, JsError> {
Ok(self.model.get_model() Ok(self
.model
.get_model()
.workbook .workbook
.worksheet(sheet).map_err(to_js_error)? .worksheet(sheet)
.map_err(to_js_error)?
.sheet_data .sheet_data
.get(&row) .get(&row)
.map(|row_data| row_data.keys().copied().collect()) .map(|row_data| row_data.keys().copied().collect())

View File

@@ -37,6 +37,7 @@ export interface CanvasSettings {
}; };
onColumnWidthChanges: (sheet: number, column: number, width: number) => void; onColumnWidthChanges: (sheet: number, column: number, width: number) => void;
onRowHeightChanges: (sheet: number, row: number, height: number) => void; onRowHeightChanges: (sheet: number, row: number, height: number) => void;
refresh: () => void;
} }
export const fonts = { export const fonts = {
@@ -106,6 +107,8 @@ export default class WorksheetCanvas {
onRowHeightChanges: (sheet: number, row: number, height: number) => void; onRowHeightChanges: (sheet: number, row: number, height: number) => void;
refresh: () => void;
constructor(options: CanvasSettings) { constructor(options: CanvasSettings) {
this.model = options.model; this.model = options.model;
this.sheetWidth = 0; this.sheetWidth = 0;
@@ -116,6 +119,7 @@ export default class WorksheetCanvas {
this.ctx = this.setContext(); this.ctx = this.setContext();
this.workbookState = options.workbookState; this.workbookState = options.workbookState;
this.editor = options.elements.editor; this.editor = options.elements.editor;
this.refresh = options.refresh;
this.cellOutline = options.elements.cellOutline; this.cellOutline = options.elements.cellOutline;
this.cellOutlineHandle = options.elements.cellOutlineHandle; this.cellOutlineHandle = options.elements.cellOutlineHandle;
@@ -580,15 +584,16 @@ export default class WorksheetCanvas {
document.removeEventListener("pointermove", resizeHandleMove); document.removeEventListener("pointermove", resizeHandleMove);
document.removeEventListener("pointerup", resizeHandleUp); document.removeEventListener("pointerup", resizeHandleUp);
const newColumnWidth = columnWidth + event.pageX - initPageX; const newColumnWidth = columnWidth + event.pageX - initPageX;
this.onColumnWidthChanges( if (newColumnWidth !== columnWidth) {
this.model.getSelectedSheet(), this.onColumnWidthChanges(
column, this.model.getSelectedSheet(),
newColumnWidth, column,
); newColumnWidth,
);
}
}; };
resizeHandleUp = resizeHandleUp.bind(this); resizeHandleUp = resizeHandleUp.bind(this);
div.addEventListener("pointerdown", (event) => { div.addEventListener("pointerdown", (event) => {
event.stopPropagation();
div.style.opacity = "1"; div.style.opacity = "1";
this.columnGuide.style.display = "block"; this.columnGuide.style.display = "block";
this.columnGuide.style.left = `${headerColumnWidth + x}px`; this.columnGuide.style.left = `${headerColumnWidth + x}px`;
@@ -596,6 +601,35 @@ export default class WorksheetCanvas {
document.addEventListener("pointermove", resizeHandleMove); document.addEventListener("pointermove", resizeHandleMove);
document.addEventListener("pointerup", resizeHandleUp); document.addEventListener("pointerup", resizeHandleUp);
}); });
div.addEventListener("dblclick", (event) => {
// This is tough. We should have a call like this.model.setAutofitColumn(sheet, column)
// but we can't do that because the back end knows nothing about the rendering engine.
const sheet = this.model.getSelectedSheet();
const rows = this.model.getRowsWithData(sheet, column);
let width = 0;
// This is a bit of a HACK. We should use the actual font size and weather is bold or not
const fontSize = 13;
this.ctx.font = `${fontSize}px ${defaultCellFontFamily}`;
for (const row of rows) {
const fullText = this.model.getFormattedCellValue(sheet, row, column);
if (fullText === "") {
continue;
}
const lines = fullText.split("\n");
for (const line of lines) {
const textWidth = this.ctx.measureText(line).width;
width = Math.max(width, textWidth);
}
}
// If the width is 0, we do nothing
if (width !== 0) {
// The +8 is so that the text is in the same position regardless of the horizontal alignment
this.model.setColumnsWidth(sheet, column, column, width + 8);
this.refresh();
}
event.stopPropagation();
});
} }
private addRowResizeHandle(y: number, row: number, rowHeight: number): void { private addRowResizeHandle(y: number, row: number, rowHeight: number): void {
@@ -618,8 +652,10 @@ export default class WorksheetCanvas {
this.rowGuide.style.display = "none"; this.rowGuide.style.display = "none";
document.removeEventListener("pointermove", resizeHandleMove); document.removeEventListener("pointermove", resizeHandleMove);
document.removeEventListener("pointerup", resizeHandleUp); document.removeEventListener("pointerup", resizeHandleUp);
const newRowHeight = rowHeight + event.pageY - initPageY - 1; const newRowHeight = rowHeight + event.pageY - initPageY;
this.onRowHeightChanges(sheet, row, newRowHeight); if (newRowHeight !== rowHeight) {
this.onRowHeightChanges(sheet, row, newRowHeight);
}
}; };
resizeHandleUp = resizeHandleUp.bind(this); resizeHandleUp = resizeHandleUp.bind(this);
/* istanbul ignore next */ /* istanbul ignore next */
@@ -632,6 +668,35 @@ export default class WorksheetCanvas {
document.addEventListener("pointermove", resizeHandleMove); document.addEventListener("pointermove", resizeHandleMove);
document.addEventListener("pointerup", resizeHandleUp); document.addEventListener("pointerup", resizeHandleUp);
}); });
div.addEventListener("dblclick", (event) => {
// This is tough. We should have a call like this.model.setAutofitRow(sheet, row)
// but we can't do that because the back end knows nothing about the rendering engine.
const sheet = this.model.getSelectedSheet();
const columns = this.model.getColumnsWithData(sheet, row);
let height = 0;
const lineHeight = 22;
// This is a bit of a HACK. We should use the actual font size and weather is bold or not
const fontSize = 13;
this.ctx.font = `${fontSize}px ${defaultCellFontFamily}`;
for (const column of columns) {
const fullText = this.model.getFormattedCellValue(sheet, row, column);
if (fullText === "") {
continue;
}
const lines = fullText.split("\n");
const lineCount = lines.length;
// This si computed so that the y position of the text is independent of the vertical alignment
const textHeight = (lineCount - 1) * lineHeight + 8 + fontSize;
height = Math.max(height, textHeight);
}
// If the height is 0, we do nothing
if (height !== 0) {
this.model.setRowsHeight(sheet, row, row, height);
this.refresh();
}
event.stopPropagation();
});
} }
private styleColumnHeader( private styleColumnHeader(

View File

@@ -119,6 +119,11 @@ const usePointer = (options: PointerSettings): PointerEvents => {
const onPointerDown = useCallback( const onPointerDown = useCallback(
(event: PointerEvent) => { (event: PointerEvent) => {
const target = event.target as HTMLElement;
if (target !== null && target.className === "column-resize-handle") {
// we are resizing a column
return;
}
let x = event.clientX; let x = event.clientX;
let y = event.clientY; let y = event.clientY;
const { const {

View File

@@ -115,7 +115,14 @@ function Worksheet(props: {
const { range } = model.getSelectedView(); const { range } = model.getSelectedView();
let columnStart = column; let columnStart = column;
let columnEnd = column; let columnEnd = column;
if (column >= range[1] && column <= range[3]) { const fullColumn = range[0] === 1 && range[2] === LAST_ROW;
const fullRow = range[1] === 1 && range[3] === LAST_COLUMN;
if (
fullColumn &&
column >= range[1] &&
column <= range[3] &&
!fullRow
) {
columnStart = Math.min(range[1], column, range[3]); columnStart = Math.min(range[1], column, range[3]);
columnEnd = Math.max(range[1], column, range[3]); columnEnd = Math.max(range[1], column, range[3]);
} }
@@ -129,13 +136,16 @@ function Worksheet(props: {
const { range } = model.getSelectedView(); const { range } = model.getSelectedView();
let rowStart = row; let rowStart = row;
let rowEnd = row; let rowEnd = row;
if (row >= range[0] && row <= range[2]) { const fullColumn = range[0] === 1 && range[2] === LAST_ROW;
const fullRow = range[1] === 1 && range[3] === LAST_COLUMN;
if (fullRow && row >= range[0] && row <= range[2] && !fullColumn) {
rowStart = Math.min(range[0], row, range[2]); rowStart = Math.min(range[0], row, range[2]);
rowEnd = Math.max(range[0], row, range[2]); rowEnd = Math.max(range[0], row, range[2]);
} }
model.setRowsHeight(sheet, rowStart, rowEnd, height); model.setRowsHeight(sheet, rowStart, rowEnd, height);
worksheetCanvas.current?.renderSheet(); worksheetCanvas.current?.renderSheet();
}, },
refresh,
}); });
const scrollX = model.getScrollX(); const scrollX = model.getScrollX();
const scrollY = model.getScrollY(); const scrollY = model.getScrollY();