From c12a35c7bafe8d700a54a78cc0592611521d912d Mon Sep 17 00:00:00 2001 From: ImJeremyHe Date: Thu, 2 Jan 2025 12:22:44 +0800 Subject: [PATCH] fix: scrollbar --- crates/buildtools/src/generate.rs | 20 +++--- crates/controller/src/api/types.rs | 12 ++++ crates/controller/src/api/worksheet.rs | 57 +++++++++++------ crates/wasms/server/src/controller.rs | 14 ++++ packages/web/src/api/worksheet.ts | 8 ++- packages/web/src/bindings/index.ts | 1 + packages/web/src/bindings/sheet_dimension.ts | 8 +++ packages/web/src/index.ts | 1 + src/components/canvas/component.tsx | 1 + src/components/canvas/store/render.ts | 2 + src/components/canvas/store/scrollbar.ts | 67 ++++++++++++++++---- src/components/scrollbar/index.tsx | 14 ++-- src/components/sheets-tab/index.tsx | 1 - src/core/data2/service.ts | 10 ++- src/core/data2/workbook/client.ts | 33 +++++++--- src/core/data2/workbook/types.ts | 14 ++++ src/core/data2/workbook/workbook.worker.ts | 35 ++++++---- tests/test.rs | 16 ++++- 18 files changed, 235 insertions(+), 79 deletions(-) create mode 100644 packages/web/src/bindings/sheet_dimension.ts diff --git a/crates/buildtools/src/generate.rs b/crates/buildtools/src/generate.rs index 81be228b..c8795050 100644 --- a/crates/buildtools/src/generate.rs +++ b/crates/buildtools/src/generate.rs @@ -1,15 +1,12 @@ -fn main() { - use gents::FileGroup; - use logisheets_controller::controller::display::CellPosition; - use logisheets_controller::controller::display::DisplayWindowWithStartPoint; - use logisheets_controller::controller::display::{ - DisplayResponse, DisplaySheetRequest, DisplayWindowRequest, SheetInfo, - }; - use logisheets_controller::edit_action::AsyncFuncResult; - use logisheets_controller::edit_action::{ActionEffect, EditAction}; - use logisheets_controller::CellInfo; - use logisheets_controller::ErrorMessage; +use gents::FileGroup; +use logisheets_controller::controller::display::{ + CellPosition, DisplayResponse, DisplaySheetRequest, DisplayWindowRequest, + DisplayWindowWithStartPoint, SheetInfo, +}; +use logisheets_controller::edit_action::{ActionEffect, AsyncFuncResult, EditAction}; +use logisheets_controller::{CellInfo, ErrorMessage, SheetDimension}; +fn main() { let path = "packages/web/src/bindings"; let mut file_group = FileGroup::new(); file_group.add::(); @@ -21,6 +18,7 @@ fn main() { file_group.add::(); file_group.add::(); file_group.add::(); + file_group.add::(); file_group.add::(); file_group.add::(); diff --git a/crates/controller/src/api/types.rs b/crates/controller/src/api/types.rs index 33c9d2f8..4f728e80 100644 --- a/crates/controller/src/api/types.rs +++ b/crates/controller/src/api/types.rs @@ -22,3 +22,15 @@ pub struct CellInfo { pub style: Style, pub block_id: Option, } + +#[cfg_attr( + feature = "gents", + gents_derives::gents_header(file_name = "sheet_dimension.ts") +)] +#[derive(Debug, Clone)] +pub struct SheetDimension { + pub max_row: usize, + pub max_col: usize, + pub height: f64, + pub width: f64, +} diff --git a/crates/controller/src/api/worksheet.rs b/crates/controller/src/api/worksheet.rs index db0774c4..9487e9bf 100644 --- a/crates/controller/src/api/worksheet.rs +++ b/crates/controller/src/api/worksheet.rs @@ -18,6 +18,7 @@ use logisheets_base::{BlockId, CellId, ColId, RowId, SheetId}; use logisheets_parser::unparse; use super::workbook::CellPositionerDefault; +use super::SheetDimension; // Use a cache to record the coordinate pub struct Worksheet<'a> { @@ -473,35 +474,51 @@ impl<'a> Worksheet<'a> { } /// Get the dimension of the sheet. - pub fn get_sheet_dimension(&self) -> (usize, usize) { + pub fn get_sheet_dimension(&self) -> Result { let sheet_container = self .controller .status .container .get_sheet_container(self.sheet_id); if sheet_container.is_none() { - return (0, 0); + return Ok(SheetDimension { + max_row: 0, + max_col: 0, + height: 0., + width: 0., + }); } let sheet_container = sheet_container.unwrap(); - sheet_container - .cells - .clone() - .iter() - .fold((0, 0), |(r, c), (id, _)| { - let cell_idx = self - .controller - .status - .navigator - .fetch_cell_idx(&self.sheet_id, id); - match cell_idx { - Ok((row, col)) => { - let r = if r > row { r } else { row }; - let c = if c > col { c } else { col }; - (r, c) + let (max_row, max_col) = + sheet_container + .cells + .clone() + .iter() + .fold((0, 0), |(r, c), (id, _)| { + let cell_idx = self + .controller + .status + .navigator + .fetch_cell_idx(&self.sheet_id, id); + match cell_idx { + Ok((row, col)) => { + let r = if r > row { r } else { row }; + let c = if c > col { c } else { col }; + (r, c) + } + Err(_) => (r, c), } - Err(_) => (r, c), - } - }) + }); + let CellPosition { + y: start_row, + x: start_col, + } = self.get_cell_position(max_row, max_col)?; + Ok(SheetDimension { + max_row, + max_col, + height: start_row, + width: start_col, + }) } pub fn get_row_info(&self, row: usize) -> Option { diff --git a/crates/wasms/server/src/controller.rs b/crates/wasms/server/src/controller.rs index 10178581..3cf20cc6 100644 --- a/crates/wasms/server/src/controller.rs +++ b/crates/wasms/server/src/controller.rs @@ -169,6 +169,20 @@ pub fn get_cell_position(id: usize, sheet_idx: usize, row: usize, col: usize) -> serde_wasm_bindgen::to_value(&result).unwrap() } +#[wasm_bindgen] +pub fn get_sheet_dimension(id: usize, sheet_idx: usize) -> JsValue { + init(); + let manager = MANAGER.get(); + let result = manager + .get_workbook(&id) + .unwrap() + .get_sheet_by_idx(sheet_idx) + .unwrap() + .get_sheet_dimension(); + handle_result!(result); + serde_wasm_bindgen::to_value(&result).unwrap() +} + #[wasm_bindgen] /// logisheets_controller::DisplayResponse pub fn get_patches(id: usize, sheet_idx: u32, version: u32) -> JsValue { diff --git a/packages/web/src/api/worksheet.ts b/packages/web/src/api/worksheet.ts index d6341824..5db51baa 100644 --- a/packages/web/src/api/worksheet.ts +++ b/packages/web/src/api/worksheet.ts @@ -12,6 +12,7 @@ import { get_display_window_within_cell, get_cell_position, get_all_fully_covered_blocks, + get_sheet_dimension, } from '../../wasm' import { BlockInfo, @@ -22,8 +23,9 @@ import { RowInfo, Style, Value, + CellInfo, + SheetDimension, } from '../bindings' -import {CellInfo} from '../bindings/cell_info' import {Cell} from './cell' import {isErrorMessage, Result} from './utils' @@ -33,6 +35,10 @@ export class Worksheet { this._sheetIdx = idx } + public getSheetDimension(): Result { + return get_sheet_dimension(this._id, this._sheetIdx) + } + public getDisplayWindow( startRow: number, endRow: number, diff --git a/packages/web/src/bindings/index.ts b/packages/web/src/bindings/index.ts index 39deeaee..463f3671 100644 --- a/packages/web/src/bindings/index.ts +++ b/packages/web/src/bindings/index.ts @@ -67,6 +67,7 @@ export * from './set_visible' export * from './sheet_blocks' export * from './sheet_col_info' export * from './sheet_comments' +export * from './sheet_dimension' export * from './sheet_info' export * from './sheet_merge_cells' export * from './sheet_names' diff --git a/packages/web/src/bindings/sheet_dimension.ts b/packages/web/src/bindings/sheet_dimension.ts new file mode 100644 index 00000000..c6e62769 --- /dev/null +++ b/packages/web/src/bindings/sheet_dimension.ts @@ -0,0 +1,8 @@ +// DO NOT EDIT. CODE GENERATED BY gents. + +export interface SheetDimension { + maxRow: number + maxCol: number + height: number + width: number +} diff --git a/packages/web/src/index.ts b/packages/web/src/index.ts index fb02ce19..2eeffd0c 100644 --- a/packages/web/src/index.ts +++ b/packages/web/src/index.ts @@ -33,6 +33,7 @@ export type { SheetBlocks, SheetColInfo, SheetComments, + SheetDimension, SheetInfo, SheetMergeCells, SheetNames, diff --git a/src/components/canvas/component.tsx b/src/components/canvas/component.tsx index a36248cb..9416efc4 100644 --- a/src/components/canvas/component.tsx +++ b/src/components/canvas/component.tsx @@ -119,6 +119,7 @@ const Internal: FC = observer((props: CanvasProps) => { lastScrollTime = now store.setAnchor(store.anchorX, store.anchorY + e.deltaY) store.render.render() + store.scrollbar.update('y') store.scroll() } diff --git a/src/components/canvas/store/render.ts b/src/components/canvas/store/render.ts index 7ed3aa23..2086c913 100644 --- a/src/components/canvas/store/render.ts +++ b/src/components/canvas/store/render.ts @@ -110,6 +110,8 @@ export class Render { ) this.render() this._jumpToCellInCurrentView(row, col) + this.store.scrollbar.update('x') + this.store.scrollbar.update('y') }) } diff --git a/src/components/canvas/store/scrollbar.ts b/src/components/canvas/store/scrollbar.ts index 456b3a25..0dcf4e2d 100644 --- a/src/components/canvas/store/scrollbar.ts +++ b/src/components/canvas/store/scrollbar.ts @@ -1,7 +1,9 @@ import {action, makeObservable, observable} from 'mobx' import {CanvasStore} from './store' -import {ScrollbarAttr, ScrollbarType} from '@/components/scrollbar' +import type {ScrollbarAttr, ScrollbarType} from '@/components/scrollbar' import {CANVAS_OFFSET} from '@/core/data2' +import {isErrorMessage} from 'logisheets-web' +import {ptToPx, widthToPx} from '@/core' export class ScrollBar { constructor(public readonly store: CanvasStore) { @@ -16,25 +18,65 @@ export class ScrollBar { @action init() { + this.store.dataSvc + .getSheetDimension(this.store.currSheetIdx) + .then((v) => { + if (isErrorMessage(v)) { + throw Error( + `failed to get sheet dimension: ${this.store.currSheetIdx}` + ) + } + + this._init(ptToPx(v.height), widthToPx(v.width)) + }) + } + + @action + _init(dimensionHeight: number, dimensionWidth: number) { const {offsetHeight: height, offsetWidth: width} = this.store.render.canvas + const h = Math.max(dimensionHeight, height + CANVAS_OFFSET) + const w = Math.max(dimensionWidth, width + CANVAS_OFFSET) + this.xScrollbar.scrollTop = this.store.anchorX + this.xScrollbar.scrollHeight = w this.xScrollbar.offsetHeight = width - this.xScrollbar.scrollHeight = width + CANVAS_OFFSET + this.yScrollbar.scrollTop = this.store.anchorY + this.yScrollbar.scrollHeight = h this.yScrollbar.offsetHeight = height - this.yScrollbar.scrollHeight = height + CANVAS_OFFSET + + this._appropriateHeight = h + this._appropriateWidth = w } @action onResize() { - const {offsetHeight: canvasHeight, offsetWidth: canvasWidth} = - this.store.render.canvas - const scrollWidth = canvasWidth + CANVAS_OFFSET - const scrollHeight = +canvasHeight + CANVAS_OFFSET + this.init() + } - this.xScrollbar.offsetHeight = canvasWidth - this.xScrollbar.scrollHeight = scrollWidth - this.yScrollbar.offsetHeight = canvasHeight - this.yScrollbar.scrollHeight = scrollHeight + /** + * Make sure this happens after the anchor X or Y is updated. + */ + @action + update(type: ScrollbarType) { + const {offsetHeight: height, offsetWidth: width} = + this.store.render.canvas + if (type === 'x') { + this.xScrollbar.scrollTop = this.store.anchorX + const viewLength = this.store.anchorX + width + if (this.store.anchorX + width > this.xScrollbar.scrollHeight) { + this.xScrollbar.scrollHeight = this.store.anchorX + width + } else if (viewLength < this._appropriateWidth) { + this.xScrollbar.scrollHeight = this._appropriateWidth + } + } else { + this.yScrollbar.scrollTop = this.store.anchorY + const viewLength = this.store.anchorY + height + if (viewLength > this.yScrollbar.scrollHeight) { + this.yScrollbar.scrollHeight = viewLength + CANVAS_OFFSET + } else if (viewLength < this._appropriateHeight) { + this.yScrollbar.scrollHeight = this._appropriateHeight + } + } } @action @@ -46,4 +88,7 @@ export class ScrollBar { } this.store.render.render() } + + private _appropriateHeight: number = 0 + private _appropriateWidth: number = 0 } diff --git a/src/components/scrollbar/index.tsx b/src/components/scrollbar/index.tsx index b22b8b34..66a7ee17 100644 --- a/src/components/scrollbar/index.tsx +++ b/src/components/scrollbar/index.tsx @@ -15,7 +15,7 @@ export * from './scroll_event' export type ScrollbarType = 'x' | 'y' export interface ScrollbarAttr { offsetHeight?: number - scrollHeight?: number + scrollHeight: number scrollTop?: number minThumbLength?: number direction: ScrollbarType @@ -48,29 +48,29 @@ export const ScrollbarComponent: FC = ({ * The difference between clientHeight, offsetHeight, scrollHeight, offsetTop, and scrollTop * https://www.programmersought.com/article/76801676023/ */ - const scrollRadio = + const scrollRatio = scrollHeight === 0 ? 0 : scrollTop / scrollHeight - const thumbRadio = + const thumbRatio = scrollHeight === 0 ? 0 : offsetHeight / scrollHeight const containerStyle = getComputedStyle(thumbContainer) const newThumbStyle: CSSProperties = {} if (xScrollbar()) { const containerLength = parseFloat(containerStyle.width) - const thumbWidth = containerLength * thumbRadio + const thumbWidth = containerLength * thumbRatio const thumbLength = Math.max(thumbWidth, minThumbLength) newThumbStyle.width = thumbLength === containerLength ? 0 : toPx(thumbLength) - const left = scrollRadio * containerLength + const left = scrollRatio * containerLength if (left + thumbLength > containerLength) { newThumbStyle.right = 0 } else newThumbStyle.left = toPx(left) } else { const containerLength = parseFloat(containerStyle.height) - const thumbHeight = thumbContainer.offsetHeight * thumbRadio + const thumbHeight = thumbContainer.offsetHeight * thumbRatio const thumbLength = Math.max(thumbHeight, minThumbLength) newThumbStyle.height = thumbLength === containerLength ? 0 : toPx(thumbLength) - const top = scrollRadio * containerLength + const top = scrollRatio * containerLength if (top + thumbLength > containerLength) { newThumbStyle.bottom = 0 } else newThumbStyle.top = toPx(top) diff --git a/src/components/sheets-tab/index.tsx b/src/components/sheets-tab/index.tsx index d87cf640..64bc8be0 100644 --- a/src/components/sheets-tab/index.tsx +++ b/src/components/sheets-tab/index.tsx @@ -56,7 +56,6 @@ export const SheetsTabComponent: FC = ({ const onTabChange = (key: string) => { const i = sheets.findIndex((s) => s === key) activeSheet$(i) - DATA_SERVICE.setCurrentSheetIdx(i) } const add = () => { diff --git a/src/core/data2/service.ts b/src/core/data2/service.ts index 9343341b..385976a7 100644 --- a/src/core/data2/service.ts +++ b/src/core/data2/service.ts @@ -2,15 +2,15 @@ import {inject, injectable} from 'inversify' import {getID} from '../ioc/id' import {TYPES} from '../ioc/types' import { - ActionEffect, CustomFunc, Transaction, SheetInfo, Cell, BlockInfo, + SheetDimension, } from 'logisheets-web' import {CellViewResponse, ViewManager} from './view_manager' -import {CellView, CellViewData} from './types' +import {CellView} from './types' import {Resp, WorkbookClient} from './workbook' export const MAX_COUNT = 100000000 @@ -50,6 +50,8 @@ export interface DataService { endCol: number ): Resp + getSheetDimension(sheetIdx: number): Resp + getCurrentCellView(sheetIdx: number): CellView getCurrentSheetIdx(): number @@ -66,6 +68,10 @@ export class DataServiceImpl implements DataService { this._init() } + public getSheetDimension(sheetIdx: number): Resp { + return this._workbook.getSheetDimension(sheetIdx) + } + public getFullyCoveredBlocks( sheetIdx: number, row: number, diff --git a/src/core/data2/workbook/client.ts b/src/core/data2/workbook/client.ts index 5b4bbc0f..546171d5 100644 --- a/src/core/data2/workbook/client.ts +++ b/src/core/data2/workbook/client.ts @@ -1,7 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import {injectable} from 'inversify' import { - ActionEffect, Cell, CustomFunc, DisplayWindowWithStartPoint, @@ -10,6 +9,7 @@ import { ErrorMessage, BlockInfo, isErrorMessage, + SheetDimension, } from 'logisheets-web' import { Callback, @@ -20,12 +20,15 @@ import { LoadWorkbookParams, HandleTransactionParams, GetFullyCoveredBlocksParams, + MethodName, } from './types' import {CellInfo} from 'packages/web' export type Resp = Promise export interface IWorkbookClient { + isReady(): Promise + getSheetDimension(sheetIdx: number): Resp getAllSheetInfo(params: GetAllSheetInfoParams): Resp getDisplayWindow( params: GetDisplayWindowParams @@ -93,10 +96,17 @@ export class WorkbookClient implements IWorkbookClient { return this._readyPromise } + getSheetDimension(sheetIdx: number): Resp { + return this._call( + MethodName.GetSheetDimension, + sheetIdx + ) as Resp + } + getFullyCoveredBlocks( params: GetFullyCoveredBlocksParams ): Resp { - return this._call('getFullyCoveredBlocks', params) as Resp< + return this._call(MethodName.GetFullyCoveredBlocks, params) as Resp< readonly BlockInfo[] > } @@ -116,7 +126,7 @@ export class WorkbookClient implements IWorkbookClient { } getAllSheetInfo(): Resp { - return this._call('getAllSheetInfo', undefined) as Resp< + return this._call(MethodName.GetAllSheetInfo, undefined) as Resp< readonly SheetInfo[] > } @@ -125,13 +135,13 @@ export class WorkbookClient implements IWorkbookClient { params: GetDisplayWindowParams ): Resp { return this._call( - 'getDisplayWindow', + MethodName.GetDisplayWindow, params ) as Resp } async getCell(params: GetCellParams): Resp { - const result = this._call('getCell', params) as Resp + const result = this._call(MethodName.GetCell, params) as Resp return result.then((v) => { if (!isErrorMessage(v)) return new Cell(v) return v @@ -139,23 +149,26 @@ export class WorkbookClient implements IWorkbookClient { } async getCellPosition(params: GetCellParams): Resp { - return this._call('getCellPosition', params) as Resp + return this._call( + MethodName.GetCellPosition, + params + ) as Resp } async undo(): Resp { - return this._call('undo', undefined) as Resp + return this._call(MethodName.Undo, undefined) as Resp } redo(): Resp { - return this._call('redo', undefined) as Resp + return this._call(MethodName.Redo, undefined) as Resp } async handleTransaction(params: HandleTransactionParams): Resp { - return this._call('handleTransaction', params) as Resp + return this._call(MethodName.HandleTransaction, params) as Resp } loadWorkbook(params: LoadWorkbookParams): Resp { - return this._call('loadWorkbook', params) as Resp + return this._call(MethodName.LoadWorkbook, params) as Resp } private _call(method: string, params?: any) { diff --git a/src/core/data2/workbook/types.ts b/src/core/data2/workbook/types.ts index c137b0e1..d391ea95 100644 --- a/src/core/data2/workbook/types.ts +++ b/src/core/data2/workbook/types.ts @@ -54,3 +54,17 @@ export const enum WorkerUpdate { } export type Callback = () => void + +export enum MethodName { + GetSheetDimension = 'getSheetDimension', + GetFullyCoveredBlocks = 'getFullyCoveredBlocks', + GetAllSheetInfo = 'getAllSheetInfo', + GetDisplayWindow = 'getDisplayWindow', + GetCell = 'getCell', + GetCellPosition = 'getCellPosition', + Undo = 'undo', + Redo = 'redo', + HandleTransaction = 'handleTransaction', + LoadWorkbook = 'loadWorkbook', + IsReady = 'isReady', +} diff --git a/src/core/data2/workbook/workbook.worker.ts b/src/core/data2/workbook/workbook.worker.ts index c395a8d2..4781c3af 100644 --- a/src/core/data2/workbook/workbook.worker.ts +++ b/src/core/data2/workbook/workbook.worker.ts @@ -4,13 +4,13 @@ const ctx: Worker = self as any import { - ActionEffect, BlockInfo, CellInfo, CellPosition, DisplayWindowWithStartPoint, ErrorMessage, initWasm, + SheetDimension, SheetInfo, Workbook, Worksheet, @@ -23,6 +23,7 @@ import { GetFullyCoveredBlocksParams, HandleTransactionParams, LoadWorkbookParams, + MethodName, WorkerUpdate, } from './types' @@ -36,6 +37,7 @@ interface IWorkbookWorker { ): Result getCell(params: GetCellParams): Result getCellPosition(params: GetCellParams): Result + getSheetDimension(sheetIdx: number): Result getFullyCoveredBlocks( params: GetFullyCoveredBlocksParams ): Result @@ -48,6 +50,11 @@ interface IWorkbookWorker { } class WorkerService implements IWorkbookWorker { + public getSheetDimension(sheetIdx: number): Result { + const ws = this.getSheet(sheetIdx) + return ws.getSheetDimension() + } + public isReady(): Result { return this._workbookImpl != undefined } @@ -151,39 +158,39 @@ class WorkerService implements IWorkbookWorker { let result switch (m) { - case 'isReady': + case MethodName.IsReady: result = this.isReady() break - case 'handleTransaction': + case MethodName.HandleTransaction: result = this.handleTransaction(args) break - case 'getAllSheetInfo': + case MethodName.GetAllSheetInfo: result = this.getAllSheetInfo() break - case 'undo': + case MethodName.Undo: result = this.undo() break - case 'redo': + case MethodName.Redo: result = this.redo() break - case 'getDisplayWindow': + case MethodName.GetDisplayWindow: result = this.getDisplayWindow(args) break - case 'getDisplayWindowWithCellPosition': - result = this.getDisplayWindowWithCellPosition(args) - break - case 'getCellPosition': + case MethodName.GetCellPosition: result = this.getCellPosition(args) break - case 'getCell': + case MethodName.GetCell: result = this.getCell(args) break - case 'loadWorkbook': + case MethodName.LoadWorkbook: result = this.loadWorkbook(args) break - case 'getFullyCoveredBlocks': + case MethodName.GetFullyCoveredBlocks: result = this.getFullyCoveredBlocks(args) break + case MethodName.GetSheetDimension: + result = this.getSheetDimension(args) + break default: throw new Error(`Unknown method: ${m}`) } diff --git a/tests/test.rs b/tests/test.rs index 36aa2999..767abfef 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -58,6 +58,8 @@ mod test_builtin_style { #[cfg(test)] mod test_6 { + use logisheets::SheetDimension; + #[test] fn test_value1() { use logisheets::{Value, Workbook}; @@ -115,7 +117,12 @@ mod test_6 { let style = ws.get_style(9, 1).unwrap(); let underline = style.font.underline.unwrap().val; assert!(matches!(underline, StUnderlineValues::Single)); - let (row_cnt, col_cnt) = ws.get_sheet_dimension(); + let SheetDimension { + max_row: row_cnt, + max_col: col_cnt, + height: _, + width: _, + } = ws.get_sheet_dimension().unwrap(); for r in 0..row_cnt { for c in 0..col_cnt { let _ = ws.get_style(r, c).unwrap(); @@ -130,7 +137,12 @@ mod test_6 { let mut buf = fs::read("tests/builtin_style.xlsx").unwrap(); let wb = Workbook::from_file(&mut buf, String::from("builtin_style")).unwrap(); let ws = wb.get_sheet_by_idx(0).unwrap(); - let (row_cnt, col_cnt) = ws.get_sheet_dimension(); + let SheetDimension { + max_row: row_cnt, + max_col: col_cnt, + height: _, + width: _, + } = ws.get_sheet_dimension().unwrap(); for r in 0..row_cnt { for c in 0..col_cnt { let _ = ws.get_style(r, c).unwrap();