diff --git a/src/lib/Behaviors/ResizeColumnBehavior.tsx b/src/lib/Behaviors/ResizeColumnBehavior.tsx index 574f8c95..4e37dce6 100644 --- a/src/lib/Behaviors/ResizeColumnBehavior.tsx +++ b/src/lib/Behaviors/ResizeColumnBehavior.tsx @@ -15,13 +15,30 @@ import { State } from "../Model/State"; import { Behavior } from "../Model/Behavior"; import { ResizeHint } from "../Components/ResizeHint"; + export class ResizeColumnBehavior extends Behavior { // TODO min / max column with on column object private resizedColumn!: GridColumn; private initialLocation!: PointerLocation; + private canvasContext = this.createCanvasContext(); autoScrollDirection: Direction = "horizontal"; isInScrollableRange!: boolean; + constructor() { + super(); + const style = getComputedStyle(document.body); + this.canvasContext.font = `${style.fontSize} ${style.fontFamily}`; + } + + createCanvasContext(): CanvasRenderingContext2D { + const canvas = document.createElement("canvas"); + const context = canvas.getContext("2d"); + if (!context) { + throw new Error("2D context not supported or canvas already initialized"); + } + return context; + } + handlePointerDown( event: PointerEvent, location: PointerLocation, @@ -127,4 +144,52 @@ export class ResizeColumnBehavior extends Behavior { } return offset; } + + handleDoubleClick(event: PointerEvent, location: PointerLocation, state: State): State { + this.initialLocation = location; + this.resizedColumn = location.column; + const newWidth = this.calculateOptimalColumnWidth(this.resizedColumn.idx, state); + + if (state.props?.onColumnResized) { + const newColWidth = + newWidth >= (state.props?.minColumnWidth ?? CellMatrix.MIN_COLUMN_WIDTH) + ? newWidth + : state.props?.minColumnWidth ?? CellMatrix.MIN_COLUMN_WIDTH; + state.props.onColumnResized(this.resizedColumn.columnId, newColWidth, state.selectedIds); + } + let focusedLocation = state.focusedLocation; + if (focusedLocation !== undefined && this.resizedColumn.columnId === focusedLocation.column.idx) { + const column = { ...focusedLocation.column, width: newWidth }; + focusedLocation = { ...focusedLocation, column }; + } + return { ...state, linePosition: -1, focusedLocation }; + } + + /** + * Iterate through all the cells in the column, finding the widest content, and return the new column width. + * @param columnId + * @param state + * @returns + */ + calculateOptimalColumnWidth(idx: number, state: State): number { + let maxWidth = 0; + + state.cellMatrix.rows.forEach((row) => { + const cell = row.cells[idx]; + const contentWidth = this.measureTextWidth(state.cellTemplates[cell.type].getCompatibleCell(cell).text); + maxWidth = Math.max(maxWidth, contentWidth); + }); + + // Add some extra space in case the text gets truncated + return maxWidth + 20; + } + + /** + * Use the Canvas API to calculate the width of a given text and font style + * @param text + * @returns + */ + measureTextWidth(text: string): number { + return this.canvasContext.measureText(text).width; + } } diff --git a/src/lib/Model/PointerEventsController.ts b/src/lib/Model/PointerEventsController.ts index 9d8000d6..bc672ebe 100644 --- a/src/lib/Model/PointerEventsController.ts +++ b/src/lib/Model/PointerEventsController.ts @@ -49,6 +49,7 @@ export class PointerEventsController extends AbstractPointerEventsController { window.addEventListener("pointermove", this.handlePointerMove); window.addEventListener("pointerup", this.handlePointerUp); + window.addEventListener("dblclick", this.handleDoubleClick); const currentLocation = getLocationFromClient( state as State, event.clientX, @@ -230,4 +231,15 @@ export class PointerEventsController extends AbstractPointerEventsController { return state; }); }; + + private handleDoubleClick = (event: MouseEvent): void => { + if ((event.target as HTMLDivElement).className === "rg-resize-handle") { + this.updateState((state) => { + const currentLocation = getLocationFromClient(state as State, event.clientX, event.clientY); + state = { ...state, currentBehavior: new ResizeColumnBehavior() }; + state = state.currentBehavior.handleDoubleClick(event as PointerEvent, currentLocation, state); + return state; + }); + } + }; } \ No newline at end of file