From 9af05d684209da80501e175ff6ef362ca0ae2969 Mon Sep 17 00:00:00 2001 From: ImJeremyHe Date: Mon, 9 Dec 2024 11:15:41 +0800 Subject: [PATCH] refactor: data service --- crates/wasms/server/src/controller.rs | 14 ++ packages/node/package.json | 2 +- packages/web/package.json | 2 +- packages/web/src/api/worksheet.ts | 6 + src/components/canvas/component.tsx | 9 +- src/components/canvas/defs/match.ts | 17 +- src/components/canvas/store/highlights.ts | 20 +- src/components/canvas/store/render.ts | 138 ++++++----- src/components/canvas/store/resizer.ts | 87 ++++--- src/components/canvas/store/scrollbar.ts | 27 +-- src/components/canvas/store/selector.ts | 1 - src/components/canvas/store/start-cell.ts | 121 --------- src/components/canvas/store/store.ts | 31 ++- src/components/resize/index.tsx | 4 +- src/components/scrollbar/index.tsx | 2 +- src/components/selector/index.tsx | 4 +- src/components/top-bar/content/index.tsx | 4 +- src/core/data2/index.ts | 3 +- src/core/data2/render.ts | 128 ---------- src/core/data2/service.ts | 194 ++++++--------- src/core/data2/view_manager.test.ts | 33 +++ src/core/data2/view_manager.ts | 283 ++++++++++++++++++++++ src/core/data2/workbook.ts | 25 +- src/core/ioc/config.ts | 8 +- src/core/ioc/types.ts | 3 - 25 files changed, 605 insertions(+), 561 deletions(-) delete mode 100644 src/components/canvas/store/start-cell.ts create mode 100644 src/core/data2/view_manager.test.ts create mode 100644 src/core/data2/view_manager.ts diff --git a/crates/wasms/server/src/controller.rs b/crates/wasms/server/src/controller.rs index 31824e74..d9eaf956 100644 --- a/crates/wasms/server/src/controller.rs +++ b/crates/wasms/server/src/controller.rs @@ -155,6 +155,20 @@ pub fn input_async_result(id: usize, result: JsValue) -> JsValue { serde_wasm_bindgen::to_value(&result).unwrap() } +#[wasm_bindgen] +pub fn get_cell_position(id: usize, sheet_idx: usize, row: usize, col: usize) -> JsValue { + init(); + let manager = MANAGER.get(); + let result = manager + .get_workbook(&id) + .unwrap() + .get_sheet_by_idx(sheet_idx) + .unwrap() + .get_cell_position(row, col); + 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/node/package.json b/packages/node/package.json index 1f30033c..6b2fff38 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -1,6 +1,6 @@ { "name": "logisheets", - "version": "0.7.0", + "version": "0.8.0", "description": "Read and write the xlsx files", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/web/package.json b/packages/web/package.json index 2cb015ae..146c9684 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,6 +1,6 @@ { "name": "logisheets-web", - "version": "0.7.0", + "version": "0.8.0", "description": "Read and write the xlsx files", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/packages/web/src/api/worksheet.ts b/packages/web/src/api/worksheet.ts index 1c1b907f..2a81d8c0 100644 --- a/packages/web/src/api/worksheet.ts +++ b/packages/web/src/api/worksheet.ts @@ -10,8 +10,10 @@ import { get_display_window, get_display_window_with_start_point, get_display_window_within_cell, + get_cell_position, } from '../../wasm' import { + CellPosition, ColInfo, DisplayWindow, DisplayWindowWithStartPoint, @@ -60,6 +62,10 @@ export class Worksheet { ) } + public getCellPosition(row: number, col: number): CellPosition { + return get_cell_position(this._id, this._sheetIdx, row, col) + } + public getDisplayWindowWithCellPosition( row: number, col: number, diff --git a/src/components/canvas/component.tsx b/src/components/canvas/component.tsx index b5d9ae75..ee2c1c2f 100644 --- a/src/components/canvas/component.tsx +++ b/src/components/canvas/component.tsx @@ -24,7 +24,7 @@ import {InvalidFormulaComponent} from './invalid-formula' import {Buttons, simpleUuid} from '@/core' import {DialogComponent} from '@/ui/dialog' import {useInjection} from '@/core/ioc/provider' -import {DataService, MAX_COUNT, RenderCell} from '@/core/data2' +import {DataServiceImpl, MAX_COUNT, RenderCell} from '@/core/data2' import {TYPES} from '@/core/ioc/types' import {CANVAS_ID, CanvasStore, CanvasStoreContext} from './store' import {observer} from 'mobx-react' @@ -38,7 +38,7 @@ export interface CanvasProps { selectedCell$: (e: SelectedCell) => void } export const CanvasComponent = (props: CanvasProps) => { - const DATA_SERVICE = useInjection(TYPES.Data) + const DATA_SERVICE = useInjection(TYPES.Data) const store = useRef(new CanvasStore(DATA_SERVICE)) return ( @@ -127,11 +127,6 @@ const Internal: FC = observer(({selectedCell, selectedCell$}) => { const onMouseWheel = (e: WheelEvent) => { // only support y scrollbar currently - const delta = e.deltaY - const newScroll = store.scrollbar.mouseWheelScrolling(delta, 'y') ?? 0 - const oldScroll = store.dataSvc.getScroll()?.y - if (oldScroll === newScroll) return - store.dataSvc.updateScroll('y', newScroll) store.render.render() store.scroll() } diff --git a/src/components/canvas/defs/match.ts b/src/components/canvas/defs/match.ts index 0c264ba1..53317328 100644 --- a/src/components/canvas/defs/match.ts +++ b/src/components/canvas/defs/match.ts @@ -1,7 +1,7 @@ import {SETTINGS} from '@/core/settings' import {Range} from '@/core/standable' import {Cell} from './cell' -import {ViewRange} from '@/core/data2' +import {CellViewData} from '@/core/data2' export function getOffset( clientX: number, @@ -19,7 +19,7 @@ export function match( clientX: number, clientY: number, canvas: HTMLCanvasElement, - {rows, cols, cells}: ViewRange + data: readonly CellViewData[] ) { const {x, y} = getOffset(clientX, clientY, canvas) const clickPosition = new Range() @@ -28,9 +28,16 @@ export function match( .setEndCol(x) .setEndRow(y) const {width: leftTopWidth, height: leftTopHeight} = SETTINGS.leftTop - const col = cols.find((c) => c.position.cover(clickPosition)) - const row = rows.find((r) => r.position.cover(clickPosition)) - const renderCell = cells.find((c) => c.position.cover(clickPosition)) + const col = data + .flatMap((d) => d.cols) + .find((c) => c.position.cover(clickPosition)) + const row = data + .flatMap((d) => d.rows) + .find((r) => r.position.cover(clickPosition)) + const renderCell = data + .flatMap((d) => d.cols) + .flat() + .find((c) => c.position.cover(clickPosition)) if (x <= leftTopWidth && y <= leftTopHeight) return new Cell('LeftTop') else if (row) return new Cell('FixedLeftHeader').copyByRenderCell(row) else if (col) return new Cell('FixedTopHeader').copyByRenderCell(col) diff --git a/src/components/canvas/store/highlights.ts b/src/components/canvas/store/highlights.ts index 3195166a..f27e6d53 100644 --- a/src/components/canvas/store/highlights.ts +++ b/src/components/canvas/store/highlights.ts @@ -37,7 +37,7 @@ export class Highlights { update(text: string) { const ranges = getRanges(text) const newCells: HighlightCell[] = [] - const viewRange = this.store.dataSvc.cachedViewRange + const viewRange = this.store.getCurrentCellView() const find = (cell: HighlightCell) => { return this.highlightCells.find((c) => equal(c, cell)) } @@ -52,14 +52,16 @@ export class Highlights { colEnd, style: new HighlightCellStyle(), } - const cell = viewRange.cells.find((c) => { - const range = new Range() - .setStartCol(newCell.colStart) - .setStartRow(newCell.rowStart) - .setEndCol(newCell.colEnd ?? newCell.colStart) - .setEndRow(newCell.rowEnd ?? newCell.rowStart) - return c.coordinate.cover(range) - }) + const cell = viewRange + .flatMap((d) => d.cells) + .find((c) => { + const range = new Range() + .setStartCol(newCell.colStart) + .setStartRow(newCell.rowStart) + .setEndCol(newCell.colEnd ?? newCell.colStart) + .setEndRow(newCell.rowEnd ?? newCell.rowStart) + return c.coordinate.cover(range) + }) if (!cell) return const oldCell = find(newCell) if (oldCell) newCell.style = oldCell.style diff --git a/src/components/canvas/store/render.ts b/src/components/canvas/store/render.ts index 732245d2..e568481b 100644 --- a/src/components/canvas/store/render.ts +++ b/src/components/canvas/store/render.ts @@ -2,12 +2,13 @@ import {makeObservable} from 'mobx' import {CanvasStore} from './store' import {Box, CanvasAttr, PainterService, TextAttr} from '@/core/painter' import {simpleUuid, toA1notation} from '@/core' -import {RenderCell} from '@/core/data2' +import {CellViewData, CellViewRespType, RenderCell} from '@/core/data2' import {SETTINGS} from '@/core/settings' import {StandardColor, Range, StandardCell} from '@/core/standable' import {StandardStyle} from '@/core/standable/style' import {PatternFill} from '@logisheets_bg' export const CANVAS_ID = simpleUuid() +const BUFFER_SIZE = 200 export class Render { constructor(public readonly store: CanvasStore) { @@ -18,16 +19,26 @@ export class Render { } render() { - this._painterService.setupCanvas(this.canvas) - this._painterService.clear() const rect = this.canvas.getBoundingClientRect() - this.store.dataSvc.setWindowSize(rect.height, rect.width) - this.store.dataSvc.getCellViewData() - this._renderGrid() - this._renderContent() - this._renderLeftHeader() - this._renderTopHeader() - this._renderLeftTop() + const resp = this.store.dataSvc.getCellView( + this.store.currSheetIdx, + rect.x - BUFFER_SIZE, + rect.y - BUFFER_SIZE, + rect.height + BUFFER_SIZE * 3, + rect.width + BUFFER_SIZE * 1.5 + ) + if (resp.type === CellViewRespType.New) { + this._painterService.setupCanvas(this.canvas) + this._painterService.clear() + } + if (resp.type !== CellViewRespType.Existed) { + const data = resp.data + this._renderGrid(data) + this._renderContent(data) + this._renderLeftHeader(data) + this._renderTopHeader(data) + this._renderLeftTop() + } // rerender resizer this.store.resizer.init() @@ -51,50 +62,56 @@ export class Render { /** * main content + freeze content. */ - private _renderContent() { - this.store.dataSvc.getCellViewData().cells.forEach((cell) => { - this._painterService.save() - this._renderCell(cell) - this._painterService.restore() + private _renderContent(data: readonly CellViewData[]) { + this._painterService.save() + data.forEach((d) => { + d.cells.forEach((cell) => { + this._renderCell(cell) + }) }) + this._painterService.restore() } - private _renderLeftHeader() { + private _renderLeftHeader(data: readonly CellViewData[]) { this._painterService.save() - this.store.dataSvc.getCellViewData().rows.forEach((r) => { - const {startRow, startCol, endRow, endCol} = r.position - this._painterService.line([ - [startCol, startRow], - [endCol, startRow], - [endCol, endRow], - [startCol, endRow], - ]) - const box = new Box() - box.position = r.position - const attr = new TextAttr() - attr.font = SETTINGS.fixedHeader.font - const position = (r.coordinate.startRow + 1).toString() - this._painterService.text(position, attr, box) + data.forEach((d) => { + d.rows.forEach((r) => { + const {startRow, startCol, endRow, endCol} = r.position + this._painterService.line([ + [startCol, startRow], + [endCol, startRow], + [endCol, endRow], + [startCol, endRow], + ]) + const box = new Box() + box.position = r.position + const attr = new TextAttr() + attr.font = SETTINGS.fixedHeader.font + const position = (r.coordinate.startRow + 1).toString() + this._painterService.text(position, attr, box) + }) }) this._painterService.restore() } - private _renderTopHeader() { + private _renderTopHeader(data: readonly CellViewData[]) { this._painterService.save() - this.store.dataSvc.getCellViewData().cols.forEach((c) => { - const {startRow, startCol, endRow, endCol} = c.position - this._painterService.line([ - [endCol, startRow], - [endCol, endRow], - [startCol, endRow], - [startCol, startRow], - ]) - const a1Notation = toA1notation(c.coordinate.startCol) - const box = new Box() - box.position = c.position - const attr = new TextAttr() - attr.font = SETTINGS.fixedHeader.font - this._painterService.text(a1Notation, attr, box) + data.forEach((d) => { + d.cols.forEach((c) => { + const {startRow, startCol, endRow, endCol} = c.position + this._painterService.line([ + [endCol, startRow], + [endCol, endRow], + [startCol, endRow], + [startCol, startRow], + ]) + const a1Notation = toA1notation(c.coordinate.startCol) + const box = new Box() + box.position = c.position + const attr = new TextAttr() + attr.font = SETTINGS.fixedHeader.font + this._painterService.text(a1Notation, attr, box) + }) }) this._painterService.restore() } @@ -157,8 +174,7 @@ export class Render { ]) } - private _renderGrid() { - const viewRange = this.store.dataSvc.getCellViewData() + private _renderGrid(data: readonly CellViewData[]) { const {grid, leftTop} = SETTINGS this._painterService.save() const attr = new CanvasAttr() @@ -166,20 +182,24 @@ export class Render { this._painterService.attr(attr) const rect = this.canvas.getBoundingClientRect() if (grid.showHorizontal) - viewRange.rows.forEach((r) => { - const y = r.position.startRow - this._painterService.line([ - [leftTop.width, y], - [rect.width, y], - ]) + data.forEach((d) => { + d.rows.forEach((r) => { + const y = r.position.startRow + this._painterService.line([ + [leftTop.width, y], + [rect.width, y], + ]) + }) }) if (grid.showVertical) - viewRange.cols.forEach((c) => { - const x = c.position.startCol - this._painterService.line([ - [x, leftTop.height], - [x, rect.height], - ]) + data.forEach((d) => { + d.cols.forEach((c) => { + const x = c.position.startCol + this._painterService.line([ + [x, leftTop.height], + [x, rect.height], + ]) + }) }) this._painterService.restore() } diff --git a/src/components/canvas/store/resizer.ts b/src/components/canvas/store/resizer.ts index 36cdfceb..b55f4830 100644 --- a/src/components/canvas/store/resizer.ts +++ b/src/components/canvas/store/resizer.ts @@ -1,20 +1,19 @@ import {action, makeObservable, observable} from 'mobx' import {CanvasStore} from './store' -import {RenderCell} from '@/core/data' +import {RenderCell} from '@/core/data2' import {Range} from '@/core/standable' import {pxToPt} from '@/core' -import {SetColWidthBuilder, SetRowHeightBuilder} from '@logisheets_bg' +import { + SetColWidthBuilder, + SetRowHeightBuilder, + Transaction, +} from '@logisheets_bg' import {getOffset} from '../defs' +import {isErrorMessage} from 'packages/web/src/api/utils' interface ResizerProps { - /** - * Resizer的位置信息 - */ readonly range: Range readonly isRow: boolean - /** - * resizer绑定的左边cell信息。从viewRange取,resizer只对左边的cell作用 - */ readonly leftCell: RenderCell } const RESIZER_SIZE = 4 @@ -40,29 +39,33 @@ export class Resizer { movingStart?: {x: number; y: number} init() { - const viewRange = this.store.dataSvc.cachedViewRange - const rowResizers = viewRange.rows.map((cell) => { - return { - range: new Range() - .setStartRow(cell.position.endRow) - .setEndRow(cell.position.endRow + RESIZER_SIZE) - .setStartCol(cell.position.startCol) - .setEndCol(cell.position.endCol), - isRow: true, - leftCell: cell, - } - }) - const colResizers = viewRange.cols.map((cell) => { - return { - range: new Range() - .setStartCol(cell.position.endCol) - .setStartRow(cell.position.startRow) - .setEndCol(cell.position.endCol + RESIZER_SIZE) - .setEndRow(cell.position.endRow), - isRow: false, - leftCell: cell, - } - }) + const data = this.store.getCurrentCellView() + const rowResizers = data + .flatMap((d) => d.rows) + .map((cell) => { + return { + range: new Range() + .setStartRow(cell.position.endRow) + .setEndRow(cell.position.endRow + RESIZER_SIZE) + .setStartCol(cell.position.startCol) + .setEndCol(cell.position.endCol), + isRow: true, + leftCell: cell, + } + }) + const colResizers = data + .flatMap((d) => d.cols) + .map((cell) => { + return { + range: new Range() + .setStartCol(cell.position.endCol) + .setStartRow(cell.position.startRow) + .setEndCol(cell.position.endCol + RESIZER_SIZE) + .setEndRow(cell.position.endRow), + isRow: false, + leftCell: cell, + } + }) this.updateResizers(rowResizers.concat(colResizers)) } @@ -80,12 +83,17 @@ export class Resizer { const activeResizer = this.resizers[i] if (!activeResizer) return false this.movingStart = {x: e.clientX, y: e.clientY} - const sheet = this.store.sheetSvc + const sheet = this.store.dataSvc + .getWorkbook() + .getWorksheet(this.store.currSheetIdx) const {startCol, startRow} = activeResizer.leftCell.coordinate const info = activeResizer.isRow - ? sheet.getRowInfo(startCol) - : sheet.getColInfo(startRow) - this.hoverText = `${info.px}px` + ? sheet.getRowHeight(startCol) + : sheet.getColWidth(startRow) + if (isErrorMessage(info)) { + throw Error(info.msg) + } + const value = (this.hoverText = `${info}px`) this.active = activeResizer this.moving = {x: 0, y: 0} return true @@ -119,7 +127,6 @@ export class Resizer { @action mouseup() { - const sheet = this.store.sheetSvc if (this.active) { const { isRow, @@ -127,16 +134,18 @@ export class Resizer { } = this.active const payload = !isRow ? new SetColWidthBuilder() - .sheetIdx(sheet.getActiveSheet()) + .sheetIdx(this.store.currSheetIdx) .col(coodinate.startCol) .width(pxToPt(this.moving.x + width)) .build() : new SetRowHeightBuilder() - .sheetIdx(sheet.getActiveSheet()) + .sheetIdx(this.store.currSheetIdx) .row(coodinate.startRow) .height(pxToPt(this.moving.y + height)) .build() - this.store.backendSvc.sendTransaction([payload]) + this.store.dataSvc.handleTransaction( + new Transaction([payload], true) + ) } this.active = undefined this.moving = {x: 0, y: 0} diff --git a/src/components/canvas/store/scrollbar.ts b/src/components/canvas/store/scrollbar.ts index 47cac9b2..456b3a25 100644 --- a/src/components/canvas/store/scrollbar.ts +++ b/src/components/canvas/store/scrollbar.ts @@ -26,11 +26,10 @@ export class ScrollBar { @action onResize() { - const scroll = this.store.dataSvc.getScroll() const {offsetHeight: canvasHeight, offsetWidth: canvasWidth} = this.store.render.canvas - const scrollWidth = scroll.x + canvasWidth + CANVAS_OFFSET - const scrollHeight = scroll.y + canvasHeight + CANVAS_OFFSET + const scrollWidth = canvasWidth + CANVAS_OFFSET + const scrollHeight = +canvasHeight + CANVAS_OFFSET this.xScrollbar.offsetHeight = canvasWidth this.xScrollbar.scrollHeight = scrollWidth @@ -45,28 +44,6 @@ export class ScrollBar { } else if (type === 'y') { this.yScrollbar.scrollTop = scrollTop } - this.store.dataSvc.updateScroll(type, scrollTop) this.store.render.render() } - - @action - mouseWheelScrolling = (delta: number, type: ScrollbarType) => { - const scroll = this.store.dataSvc.getScroll() - const oldScroll = type === 'x' ? scroll.x : scroll.y - let newScroll = oldScroll + delta - const canvas = this.store.render.canvas - if (delta === 0) return - if (newScroll < 0) newScroll = 0 - if (newScroll === oldScroll) return - if (type === 'y') { - const max = newScroll + canvas.offsetHeight + CANVAS_OFFSET - this.yScrollbar.scrollTop = newScroll - this.yScrollbar.scrollHeight = max - } else if (type === 'x') { - const max = newScroll + canvas.offsetWidth + CANVAS_OFFSET - this.xScrollbar.scrollTop = newScroll - this.xScrollbar.scrollHeight = max - } - return newScroll - } } diff --git a/src/components/canvas/store/selector.ts b/src/components/canvas/store/selector.ts index d0e143ee..e3aaf5f0 100644 --- a/src/components/canvas/store/selector.ts +++ b/src/components/canvas/store/selector.ts @@ -158,7 +158,6 @@ export const getSelector = ( const selector = new SelectorProps() selector.width = width selector.height = height - // 在单元格内框选 if (endPos.startRow < startPos.startRow) { selector.borderTopWidth = startPos.startRow - endPos.startRow selector.y = endPos.startRow diff --git a/src/components/canvas/store/start-cell.ts b/src/components/canvas/store/start-cell.ts deleted file mode 100644 index f3cf49df..00000000 --- a/src/components/canvas/store/start-cell.ts +++ /dev/null @@ -1,121 +0,0 @@ -import {Cell} from '../defs' -import {DataService, RenderCell} from '@/core/data' -import {MouseEvent, useRef} from 'react' -import {Buttons} from '@/core' -import {SelectorProps} from '@/components/selector' -import {Range} from '@/core/standable' -import {useInjection} from '@/core/ioc/provider' -import {TYPES} from '@/core/ioc/types' -export type StartCellType = - | 'mousedown' - | 'contextmenu' - | 'render' - | 'unknown' - | 'scroll' -export class StartCellEvent { - constructor( - public readonly cell?: Cell, - public readonly from: StartCellType = 'unknown' - ) {} - public event = new Event('') - public same = false -} - -interface StartCellProps { - readonly startCellChange: (e: StartCellEvent) => void -} - -export const useStartCell = ({startCellChange}: StartCellProps) => { - const DATA_SERVICE = useInjection(TYPES.Data) - const startCell = useRef() - const isMouseDown = useRef(false) - - const scroll = () => { - const oldStartCell = startCell.current - if ( - !oldStartCell || - oldStartCell.type === 'LeftTop' || - oldStartCell.type === 'unknown' - ) - return - let renderCell: RenderCell | undefined - const viewRange = DATA_SERVICE.cachedViewRange - if (oldStartCell.type === 'FixedLeftHeader') - renderCell = viewRange.rows.find((r) => - r.coordinate.cover(oldStartCell.coordinate) - ) - else if (oldStartCell.type === 'FixedTopHeader') - renderCell = viewRange.cols.find((c) => - c.coordinate.cover(oldStartCell.coordinate) - ) - else if (oldStartCell.type === 'Cell') - renderCell = viewRange.cells.find((c) => - c.coordinate.cover(oldStartCell.coordinate) - ) - else return - if (!renderCell) { - startCellChange(new StartCellEvent()) - return - } - const newStartCell = new Cell(oldStartCell.type).copyByRenderCell( - renderCell - ) - startCell.current = newStartCell - const e = new StartCellEvent(newStartCell, 'scroll') - e.same = true - startCellChange(e) - } - - const canvasChange = () => { - const viewRange = DATA_SERVICE.cachedViewRange - const oldStartCell = startCell.current - const row = viewRange.rows.find((r) => oldStartCell?.cover(r)) - if (row === undefined) { - startCell.current = undefined - return - } - const col = viewRange.cols.find((c) => oldStartCell?.cover(c)) - if (col === undefined) { - startCell.current = undefined - return - } - const cell = new Cell(oldStartCell?.type ?? 'unknown') - const event = new StartCellEvent(cell, 'render') - event.same = false - startCell.current = cell - startCellChange(event) - } - const mousedown = ( - e: MouseEvent, - matchCell: Cell, - selector?: SelectorProps - ) => { - const buttons = e.buttons - if (buttons !== Buttons.LEFT && buttons !== Buttons.RIGHT) return - // 如果是在选中区域内右键,则不触发新的start cell - if (selector && buttons === Buttons.RIGHT) { - const range = new Range() - .setStartRow(selector.y - selector.borderTopWidth) - .setEndRow(selector.y + selector.borderBottomWidth) - .setStartCol(selector.x - selector.borderLeftWidth) - .setEndCol(selector.x + selector.borderRightWidth) - if (range.cover(matchCell.position)) return - } - isMouseDown.current = true - const event = new StartCellEvent( - matchCell, - buttons === Buttons.LEFT ? 'mousedown' : 'contextmenu' - ) - if (startCell.current && matchCell.equals(startCell.current)) - event.same = true - startCell.current = matchCell - startCellChange(event) - } - return { - startCell, - isMouseDown, - scroll, - canvasChange, - mousedown, - } -} diff --git a/src/components/canvas/store/store.ts b/src/components/canvas/store/store.ts index 6a6c4407..f6b79110 100644 --- a/src/components/canvas/store/store.ts +++ b/src/components/canvas/store/store.ts @@ -8,7 +8,7 @@ import {Selector} from './selector' import {Dnd} from './dnd' import {ScrollBar} from './scrollbar' import {Textarea} from './textarea' -import {RenderCell, DataService} from '@/core/data2' +import {RenderCell, DataService, CellViewData} from '@/core/data2' export class CanvasStore { constructor(public readonly dataSvc: DataService) { @@ -29,6 +29,7 @@ export class CanvasStore { * left mousedown the same cell */ same = false + currSheetIdx = 0 render: Render resizer: Resizer @@ -37,6 +38,10 @@ export class CanvasStore { dnd: Dnd scrollbar: ScrollBar textarea: Textarea + + getCurrentCellView(): readonly CellViewData[] { + return this.dataSvc.getCurrentCellView(this.currSheetIdx) + } reset() { this.highlights.reset() this.selector.reset() @@ -45,8 +50,8 @@ export class CanvasStore { } match(clientX: number, clientY: number) { - const viewRange = this.dataSvc.getCellViewData() - return match(clientX, clientY, this.render.canvas, viewRange) + const data = this.getCurrentCellView() + return match(clientX, clientY, this.render.canvas, data) } @action @@ -77,19 +82,19 @@ export class CanvasStore { if (!startCell) return if (startCell.type === 'LeftTop' || startCell.type === 'unknown') return let renderCell: RenderCell | undefined - const cellView = this.dataSvc.getCellViewData() + const cellView = this.getCurrentCellView() if (startCell.type === 'FixedLeftHeader') - renderCell = cellView.rows.find((r) => - r.coordinate.cover(startCell.coordinate) - ) + renderCell = cellView + .flatMap((d) => d.rows) + .find((r) => r.coordinate.cover(startCell.coordinate)) else if (startCell.type === 'FixedTopHeader') - renderCell = cellView.cols.find((c) => - c.coordinate.cover(startCell.coordinate) - ) + renderCell = cellView + .flatMap((d) => d.cols) + .find((c) => c.coordinate.cover(startCell.coordinate)) else if (startCell.type === 'Cell') - renderCell = cellView.cells.find((c) => - c.coordinate.cover(startCell.coordinate) - ) + renderCell = cellView + .flatMap((d) => d.cells) + .find((c) => c.coordinate.cover(startCell.coordinate)) else return if (!renderCell) { this.reset() diff --git a/src/components/resize/index.tsx b/src/components/resize/index.tsx index e58ab0ff..11f36835 100644 --- a/src/components/resize/index.tsx +++ b/src/components/resize/index.tsx @@ -44,7 +44,7 @@ export const ResizerComponent: FC = ({ top: type === 'row' ? `${-height / 2}px` : 0, cursor: type === 'row' ? 'row-resize' : 'col-resize', }} - > + />
= ({ left: `${movingX}px`, top: `${movingY}px`, }} - >
+ /> ) } diff --git a/src/components/scrollbar/index.tsx b/src/components/scrollbar/index.tsx index 75a85a3a..b22b8b34 100644 --- a/src/components/scrollbar/index.tsx +++ b/src/components/scrollbar/index.tsx @@ -160,7 +160,7 @@ export const ScrollbarComponent: FC = ({ ref={_thumbEl} style={thumbStyle} onMouseDown={(e) => thumbMouseDown(e)} - > + /> diff --git a/src/components/selector/index.tsx b/src/components/selector/index.tsx index 2e1f00b4..b608e0a3 100644 --- a/src/components/selector/index.tsx +++ b/src/components/selector/index.tsx @@ -1,4 +1,4 @@ -import { makeAutoObservable } from 'mobx' +import {makeAutoObservable} from 'mobx' import {observer} from 'mobx-react' export class SelectorProps { constructor() { @@ -56,7 +56,7 @@ export const SelectorComponent = observer((props: ISelectorProps) => { borderTopWidth: `${borderTopWidth}px`, borderBottomWidth: `${borderBottomWidth}px`, }} - > + /> ) }) diff --git a/src/components/top-bar/content/index.tsx b/src/components/top-bar/content/index.tsx index 1db0723a..050ca87a 100644 --- a/src/components/top-bar/content/index.tsx +++ b/src/components/top-bar/content/index.tsx @@ -3,7 +3,7 @@ import {ColorResult, SketchPicker} from 'react-color' import styles from './start.module.scss' import {useEffect, useState} from 'react' import {StandardColor} from '@/core/standable' -import {SheetService} from '@/core/data' +import {DataService} from '@/core/data2' import {useInjection} from '@/core/ioc/provider' import {TYPES} from '@/core/ioc/types' @@ -17,7 +17,7 @@ export interface StartProps { } export const StartComponent = ({selectedCell}: StartProps) => { - const SHEET_SERVICE = useInjection(TYPES.Sheet) + const SHEET_SERVICE = useInjection(TYPES.Data) const [openSketchPicker, setOpenSketchPicker] = useState(false) const [fontColor, setFontColor] = useState('#000') diff --git a/src/core/data2/index.ts b/src/core/data2/index.ts index de5cfaba..9310244f 100644 --- a/src/core/data2/index.ts +++ b/src/core/data2/index.ts @@ -1,3 +1,4 @@ -export * from './workbook' export * from './render' export * from './service' +export * from './view_manager' +export * from './workbook' diff --git a/src/core/data2/render.ts b/src/core/data2/render.ts index 3dae5375..ef0b17b9 100644 --- a/src/core/data2/render.ts +++ b/src/core/data2/render.ts @@ -1,116 +1,7 @@ -import {getID} from '@/core/ioc/id' import {Range, StandardCell} from '@/core/standable' -import {inject, injectable} from 'inversify' -import {WorkbookService} from './workbook' -import {TYPES} from '../ioc/types' import {CellInfo} from '@logisheets_bg' import {StandardValue} from '../standable/value' -@injectable() -export class RenderDataProvider { - readonly id = getID() - constructor(@inject(TYPES.Data) private _workbook: WorkbookService) {} - - public viewRange = new ViewRange() - - public getRenderData( - sheetIdx: number, - startX: number, - startY: number, - height: number, - width: number - ): RenderCell[] { - const window = this._workbook.getDisplayWindow( - sheetIdx, - startX, - startY, - height, - width - ) - - let y = window.startY - const rows = window.window.rows.map((r) => { - const renderRow = new RenderCell() - .setCoordinate(new Range().setStartRow(r.idx).setEndRow(r.idx)) - .setPosition( - new Range() - .setStartRow(y) - .setEndRow(y + r.height) - .setEndCol(window.startX) - ) - y += r.height - return renderRow - }) - - let x = window.startX - const cols = window.window.cols.map((c) => { - const renderCol = new RenderCell() - .setCoordinate(new Range().setStartCol(c.idx).setEndCol(c.idx)) - .setPosition( - new Range() - .setStartCol(x) - .setEndCol(x + c.width) - .setEndRow(window.startY) - ) - x += renderCol.width - return renderCol - }) - - const cells: RenderCell[] = [] - let idx = 0 - for (let r = 0; r < rows.length; r += 1) { - for (let c = 0; c < cols.length; c += 1) { - const row = rows[r] - const col = cols[c] - const corrdinate = new Range() - .setStartRow(row.coordinate.startRow) - .setEndRow(row.coordinate.endRow) - .setStartCol(col.coordinate.startCol) - .setEndCol(col.coordinate.endCol) - - const position = new Range() - .setStartRow(row.position.startRow) - .setEndRow(row.position.endRow) - .setStartCol(col.position.startCol) - .setEndCol(col.position.endCol) - const renderCell = new RenderCell() - .setPosition(position) - .setCoordinate(corrdinate) - .setInfo(window.window.cells[idx]) - cells.push(renderCell) - idx += 1 - } - } - - window.window.mergeCells.forEach((m) => { - let s: RenderCell | undefined - for (const i in cells) { - const cell = cells[i] - if ( - cell.coordinate.startRow == m.rowStart && - cell.coordinate.startCol == m.colStart - ) { - s = cell - } else if ( - cell.coordinate.endRow == m.rowEnd && - cell.coordinate.endCol == m.colEnd - ) { - if (s) s.setPosition(cell.position) - return - } else if ( - cell.coordinate.endRow < m.rowEnd && - cell.coordinate.endCol < m.colEnd && - cell.coordinate.startRow > m.rowStart && - cell.coordinate.startCol > m.colStart - ) { - cell.skipRender = true - } - } - }) - return cells - } -} - export class RenderCell { get width() { return this.position.width @@ -164,22 +55,3 @@ export class RenderCellSegment { public cells: readonly RenderCell[] ) {} } - -export class ViewRange { - public fromRow = 0 - public toRow = 0 - public fromCol = 0 - public toCol = 0 - /** - * visible rows. - */ - public rows: readonly RenderCell[] = [] - /** - * visible cols. - */ - public cols: readonly RenderCell[] = [] - /** - * visible cells - */ - public cells: readonly RenderCell[] = [] -} diff --git a/src/core/data2/service.ts b/src/core/data2/service.ts index b79de9cc..db3f82d7 100644 --- a/src/core/data2/service.ts +++ b/src/core/data2/service.ts @@ -2,12 +2,17 @@ import {inject, injectable} from 'inversify' import {getID} from '../ioc/id' import {TYPES} from '../ioc/types' import {WorkbookService} from './workbook' -import {RenderCell, RenderDataProvider, ViewRange} from './render' -import {ActionEffect, CustomFunc, Transaction, Comment} from '@logisheets_bg' -import {Scroll, ScrollImpl} from '../standable' +import {RenderCell} from './render' +import { + ActionEffect, + CustomFunc, + Transaction, + Comment, + Workbook, +} from '@logisheets_bg' import {Range} from '@/core/standable' -import {ScrollbarType} from '@/components/scrollbar' import {DisplayWindowWithStartPoint} from '@logisheets_bg' +import {CellViewResponse, ViewManager} from './view_manager' export const MAX_COUNT = 100000000 export const CANVAS_OFFSET = 100 @@ -15,32 +20,40 @@ export const CANVAS_OFFSET = 100 export interface DataService { registryCustomFunc: (f: CustomFunc) => void registryCellUpdatedCallback: (f: () => void) => void - handleTransaction: (t: Transaction, undoable: boolean) => ActionEffect + handleTransaction: (t: Transaction) => ActionEffect undo: () => void redo: () => void - getCurrentSheet: () => number - setCurrentSheet: (sheet: number) => void - updateScroll: (type: ScrollbarType, top: number) => void - getScroll: () => Scroll + getWorkbook: () => Workbook - setWindowSize: (height: number, width: number) => void - // According to the sheet scroll and the currentthe current window size, - // fetch the cache or load data from WASM - getCellViewData: () => CellViewData - // Given the cell coordinate, get the cell view data - jumpTo: (row: number, col: number) => CellViewData + getCellView: ( + sheetIdx: number, + startX: number, + startY: number, + height: number, + width: number + ) => CellViewResponse + + getCellViewWithCell: ( + sheetIdx: number, + row: number, + col: number, + height: number, + width: number + ) => CellViewResponse + + getCurrentCellView: (sheetIdx: number) => readonly CellViewData[] } @injectable() export class DataServiceImpl implements DataService { readonly id = getID() - constructor( - @inject(TYPES.Data) private _workbook: WorkbookService, - @inject(TYPES.Render) private _render: RenderDataProvider - ) { + constructor(@inject(TYPES.Data) private _workbook: WorkbookService) { this._init() } + public getWorkbook(): Workbook { + return this._workbook.workbook + } public registryCustomFunc(f: CustomFunc) { return this._workbook.registryCustomFunc(f) @@ -50,11 +63,8 @@ export class DataServiceImpl implements DataService { return this._workbook.registryCellUpdatedCallback(f) } - public handleTransaction( - transaction: Transaction, - undoable: boolean - ): ActionEffect { - return this._workbook.handleTransaction(transaction, undoable) + public handleTransaction(transaction: Transaction): ActionEffect { + return this._workbook.handleTransaction(transaction, true) } public undo(): void { @@ -65,76 +75,44 @@ export class DataServiceImpl implements DataService { return this._workbook.redo() } - public setWindowSize(height: number, width: number): void { - this._windowSize = {height, width} - } - - public getCellViewData(): CellViewData { - const sheet = this._currentSheet - let cellView = this._cellViews.get(sheet) - if (cellView?.data) return cellView.data - if (!cellView) { - cellView = new CellView() + public getCurrentCellView(sheetIdx: number): readonly CellViewData[] { + const cacheManager = this._cellViews.get(sheetIdx) + if (cacheManager) { + return cacheManager.dataChunks } - const data = this._getCellViewData( - sheet, - cellView.scroll.x, - cellView.scroll.y, - this._windowSize.height, - this._windowSize.width - ) - cellView.data = data - this._cellViews.set(sheet, cellView) - return data + throw Error('trying to get cell view before rendering a sheet') } - public updateScroll(type: ScrollbarType, top: number): void { - const sheet = this._currentSheet - let cellView = this._cellViews.get(sheet) - if (!cellView) { - cellView = new CellView() + public getCellViewWithCell( + sheetIdx: number, + row: number, + col: number, + height: number, + width: number + ): CellViewResponse { + const cacheManager = this._cellViews.get(sheetIdx) + if (!cacheManager) { + const manager = new ViewManager(this._workbook, sheetIdx) + this._cellViews.set(sheetIdx, manager) } - cellView.scroll.update(type, top) - this._cellViews.set(sheet, cellView) - return + const viewManager = this._cellViews.get(sheetIdx) as ViewManager + return viewManager.getViewResponseWithCell(row, col, height, width) } - public getScroll(): Scroll { - const sheet = this._currentSheet - let cellView = this._cellViews.get(sheet) - if (!cellView) { - cellView = new CellView() - this._cellViews.set(this._currentSheet, cellView) + public getCellView( + sheetIdx: number, + startX: number, + startY: number, + height: number, + width: number + ): CellViewResponse { + const cacheManager = this._cellViews.get(sheetIdx) + if (!cacheManager) { + const manager = new ViewManager(this._workbook, sheetIdx) + this._cellViews.set(sheetIdx, manager) } - return cellView.scroll - } - - public getCurrentSheet(): number { - return this._currentSheet - } - - public setCurrentSheet(sheet: number): void { - this._currentSheet = sheet - return - } - - public jumpTo(row: number, col: number): CellViewData { - const window = this._workbook.getDisplayWindowWithCellPosition( - this._currentSheet, - row, - col, - this._windowSize.height, - this._windowSize.width - ) - const scroll = new ScrollImpl() - scroll.update('x', window.startX) - scroll.update('y', window.startY) - - const result = parseDisplayWindow(window) - - const cellView = new CellView(result, scroll) - this._cellViews.set(this._currentSheet, cellView) - return result + const viewManager = this._cellViews.get(sheetIdx) as ViewManager + return viewManager.getViewResponse(startX, startY, height, width) } private _init() { @@ -143,43 +121,7 @@ export class DataServiceImpl implements DataService { this._cellViews = new Map() }) } - - private _getCellViewData( - sheetIdx: number, - startX: number, - startY: number, - height: number, - width: number - ): CellViewData { - const window = this._workbook.getDisplayWindow( - sheetIdx, - startX, - startY, - height, - width - ) - const result = parseDisplayWindow(window) - return result - } - - private _windowSize: {height: number; width: number} = {height: 0, width: 0} - - private _cellViews: Map = new Map() - private _currentSheet = 0 -} - -export class CellView { - public constructor(data?: CellViewData, scroll?: ScrollImpl) { - if (data) { - this.data = data - } - if (scroll) { - this.scroll = scroll - } - } - - public data: CellViewData | null = null - public scroll = new ScrollImpl() + private _cellViews: Map = new Map() } export class CellViewData { @@ -201,7 +143,9 @@ export class CellViewData { } } -function parseDisplayWindow(window: DisplayWindowWithStartPoint): CellViewData { +export function parseDisplayWindow( + window: DisplayWindowWithStartPoint +): CellViewData { let y = window.startY const rows = window.window.rows.map((r) => { const renderRow = new RenderCell() diff --git a/src/core/data2/view_manager.test.ts b/src/core/data2/view_manager.test.ts new file mode 100644 index 00000000..ce26d159 --- /dev/null +++ b/src/core/data2/view_manager.test.ts @@ -0,0 +1,33 @@ +import {overlap, OverlapType, Rect} from './view_manager' + +describe('view manager', () => { + it('overlap single target partially covered', () => { + const target = new Rect(0, 0, 10, 10) + const cache = new Rect(0, 0, 10, 5) + const result = overlap([target], cache) + expect(result.ty).toEqual(OverlapType.PartiallyCovered) + expect(result.targets).toEqual([new Rect(0, 5, 10, 5)]) + }) + it('overlap single target uncovered', () => { + const target = new Rect(0, 0, 10, 10) + const cache = new Rect(11, 11, 10, 5) + const result = overlap([target], cache) + expect(result.ty).toEqual(OverlapType.Uncovered) + expect(result.targets.length).toEqual(1) + expect(result.targets[0]).toEqual(target) + }) + it('overlap multiple targets partially covered', () => { + const targets = [new Rect(0, 0, 10, 10), new Rect(15, 15, 10, 10)] + const cache = new Rect(5, 5, 15, 15) + const result = overlap(targets, cache) + expect(result.ty).toEqual(OverlapType.PartiallyCovered) + expect(result.targets.length).toEqual(4) + }) + it('overlap miltiple targets fully covered', () => { + const targets = [new Rect(0, 0, 10, 10), new Rect(10, 10, 10, 10)] + const cache = new Rect(0, 0, 20, 20) + const result = overlap(targets, cache) + expect(result.ty).toEqual(OverlapType.FullyCovered) + expect(result.targets.length).toEqual(0) + }) +}) diff --git a/src/core/data2/view_manager.ts b/src/core/data2/view_manager.ts new file mode 100644 index 00000000..b5d964f5 --- /dev/null +++ b/src/core/data2/view_manager.ts @@ -0,0 +1,283 @@ +import {CellViewData, parseDisplayWindow} from './service' +import {WorkbookService} from './workbook' + +/** + * The `ViewManager` is responsible for efficiently and seamlessly generating `CellViewData`. + * + * In scenarios such as scrolling, the `ViewManager` provides incremental data for the Canvas, + * minimizing unnecessary repaints. This optimization reduces the workload for both the Canvas + * and the WASM module, ensuring smooth performance by avoiding redundant calculations. + */ +export class ViewManager { + constructor( + private _workbook: WorkbookService, + private _sheetIdx: number + ) {} + + public getViewResponseWithCell( + row: number, + col: number, + height: number, + width: number + ): CellViewResponse { + const sheet = this._workbook.getSheetByIdx(this._sheetIdx) + const {x, y} = sheet.getCellPosition(row, col) + return this.getViewResponse( + x - 0.5 * width, + y - 0.5 * height, + height, + width + ) + } + + public getViewResponse( + startX: number, + startY: number, + height: number, + width: number + ): CellViewResponse { + let targets = [new Rect(startX, startY, width, height)] + const newChunks: CellViewData[] = [] + let uncovered = true + let fullCovered = false + for (let i = 0; i < this.dataChunks.length; i += 1) { + const v = this.dataChunks[i] + const rect = Rect.fromCellViewData(v) + const result = overlap(targets, rect) + if (result.ty === OverlapType.Uncovered) { + continue + } + + uncovered = false + + newChunks.push(v) + targets = result.targets + + if (result.ty === OverlapType.FullyCovered) { + fullCovered = true + break + } + } + const type = uncovered + ? CellViewRespType.New + : fullCovered + ? CellViewRespType.Existed + : CellViewRespType.Incremental + + let data = targets.map((t) => { + const window = this._workbook.getDisplayWindow( + this._sheetIdx, + t.startX, + t.startY, + t.height, + t.width + ) + return parseDisplayWindow(window) + }) + + if (type === CellViewRespType.New) { + this.dataChunks = data + } else if (type === CellViewRespType.Incremental) { + newChunks.push(...data) + this.dataChunks = newChunks + } else if (type === CellViewRespType.Existed) { + data = newChunks + this.dataChunks = newChunks + } + + return {type, data} + } + + /** + * An array that stores a continuous sequence of `CellViewData` objects. + * Overlap is allowed between neighboring views. + */ + public dataChunks: CellViewData[] = [] +} + +export enum CellViewRespType { + Existed, + Incremental, + New, +} + +export interface CellViewResponse { + readonly type: CellViewRespType + readonly data: CellViewData[] +} + +export class Rect { + constructor( + public readonly startX: number, + public readonly startY: number, + public readonly width: number, + public readonly height: number + ) {} + + get endX(): number { + return this.startX + this.width + } + + get endY(): number { + return this.startY + this.height + } + + /** + * Checks if this rectangle fully contains another rectangle. + */ + contains(other: Rect): boolean { + return ( + this.startX <= other.startX && + this.endX >= other.endX && + this.startY <= other.startY && + this.endY >= other.endY + ) + } + + public static fromCellViewData(data: CellViewData): Rect { + const rowLen = data.rows.length + const colLen = data.cols.length + if (rowLen == 0 || colLen == 0) + throw Error('cell view data should not have empty row or col') + + const startRow = data.rows[0].position.startRow + const startCol = data.cols[0].position.startCol + const endRow = data.rows[rowLen - 1].position.endRow + const endCol = data.cols[colLen - 1].position.endCol + return new Rect( + startRow, + startCol, + endRow - startRow, + endCol - startCol + ) + } +} + +export class OverlapResult { + constructor( + public readonly targets: Rect[], + public readonly ty: OverlapType + ) {} +} + +export enum OverlapType { + FullyCovered, + PartiallyCovered, + Uncovered, +} + +export function merge(targets: Rect[]): Rect { + if (targets.length === 0) { + throw new Error('No rectangles to merge') + } + + // Initialize bounds with the first rectangle + let minX = targets[0].startX + let minY = targets[0].startY + let maxX = targets[0].endX + let maxY = targets[0].endY + + // Iterate over remaining rectangles to determine bounds + for (const rect of targets) { + minX = Math.min(minX, rect.startX) + minY = Math.min(minY, rect.startY) + maxX = Math.max(maxX, rect.endX) + maxY = Math.max(maxY, rect.endY) + } + + // Calculate the width and height of the merged rectangle + const width = maxX - minX + const height = maxY - minY + + return new Rect(minX, minY, width, height) +} + +export function overlap(targets: Rect[], cache: Rect): OverlapResult { + const uncoveredRects: Rect[] = [] + let fullyCovered = true + let hasOverlap = false + + for (const target of targets) { + // Calculate the intersection area + const overlapStartX = Math.max(target.startX, cache.startX) + const overlapEndX = Math.min(target.endX, cache.endX) + const overlapStartY = Math.max(target.startY, cache.startY) + const overlapEndY = Math.min(target.endY, cache.endY) + + // Check if there's any overlap + if (overlapStartX >= overlapEndX || overlapStartY >= overlapEndY) { + // No overlap + uncoveredRects.push(target) + fullyCovered = false + continue + } + + // If overlap exists + hasOverlap = true + + // Check if cache fully contains the target + if (cache.contains(target)) { + continue // Fully covered, don't add to uncoveredRects + } + + // Partially covered: Calculate the remaining uncovered parts of the target + fullyCovered = false + + // Top segment + if (overlapStartY > target.startY) { + uncoveredRects.push( + new Rect( + target.startX, + target.startY, + target.width, + overlapStartY - target.startY + ) + ) + } + + // Bottom segment + if (overlapEndY < target.endY) { + uncoveredRects.push( + new Rect( + target.startX, + overlapEndY, + target.width, + target.endY - overlapEndY + ) + ) + } + + // Left segment + if (overlapStartX > target.startX) { + uncoveredRects.push( + new Rect( + target.startX, + overlapStartY, + overlapStartX - target.startX, + overlapEndY - overlapStartY + ) + ) + } + + // Right segment + if (overlapEndX < target.endX) { + uncoveredRects.push( + new Rect( + overlapEndX, + overlapStartY, + target.endX - overlapEndX, + overlapEndY - overlapStartY + ) + ) + } + } + + // Determine the overall overlap type + const overlapType = fullyCovered + ? OverlapType.FullyCovered + : !hasOverlap + ? OverlapType.Uncovered + : OverlapType.PartiallyCovered + + return new OverlapResult(uncoveredRects, overlapType) +} diff --git a/src/core/data2/workbook.ts b/src/core/data2/workbook.ts index 0f85386a..59f5b3fa 100644 --- a/src/core/data2/workbook.ts +++ b/src/core/data2/workbook.ts @@ -7,6 +7,7 @@ import { initWasm, DisplayWindowWithStartPoint, } from '@logisheets_bg' +import {Worksheet} from '@logisheets_bg' @injectable() export class WorkbookService { @@ -15,18 +16,18 @@ export class WorkbookService { } public registryCustomFunc(f: CustomFunc) { - this._workbook.registryCustomFunc(f) + this.workbook.registryCustomFunc(f) } public registryCellUpdatedCallback(f: () => void) { - this._workbook.registerCellUpdatedCallback(f) + this.workbook.registerCellUpdatedCallback(f) } public handleTransaction( transaction: Transaction, undoable: boolean ): ActionEffect { - return this._workbook.execTransaction(transaction, undoable) + return this.workbook.execTransaction(transaction, undoable) } public getDisplayWindow( @@ -36,7 +37,7 @@ export class WorkbookService { height: number, width: number ): DisplayWindowWithStartPoint { - const ws = this._workbook.getWorksheet(sheetIdx) + const ws = this.workbook.getWorksheet(sheetIdx) return ws.getDisplayWindowWithStartPoint(startX, startY, height, width) } @@ -47,32 +48,36 @@ export class WorkbookService { height: number, width: number ): DisplayWindowWithStartPoint { - const ws = this._workbook.getWorksheet(sheetIdx) + const ws = this.workbook.getWorksheet(sheetIdx) return ws.getDisplayWindowWithCellPosition(row, col, height, width) } + public getSheetByIdx(sheetIdx: number): Worksheet { + return this.workbook.getWorksheet(sheetIdx) + } + public undo() { - this._workbook.undo() + this.workbook.undo() return } public redo() { - this._workbook.redo() + this.workbook.redo() } public loadWorkbook(content: Uint8Array, name: string) { - this._workbook.load(content, name) + this.workbook.load(content, name) } private async _init(funcs: readonly CustomFunc[]) { await initWasm() this._workbookImpl = new Workbook() funcs.forEach((f) => { - this._workbook.registryCustomFunc(f) + this.workbook.registryCustomFunc(f) }) } - private get _workbook(): Workbook { + public get workbook(): Workbook { if (!this._workbookImpl) throw Error('') return this._workbookImpl } diff --git a/src/core/ioc/config.ts b/src/core/ioc/config.ts index 17e32ba1..8f5f6952 100644 --- a/src/core/ioc/config.ts +++ b/src/core/ioc/config.ts @@ -2,12 +2,8 @@ import {Container} from 'inversify' //reflect-metadata should be imported before any interface or other imports also //it should be imported only once so that a singleton is created. import 'reflect-metadata' -import {Backend} from '@/core/data/backend' -import {SheetService} from '@/core/data/sheet' -import {DataService} from '@/core/data/service' +import {DataService, DataServiceImpl} from '@/core/data2' import {TYPES} from './types' export const CONTAINER = new Container() -CONTAINER.bind(TYPES.Backend).to(Backend).inSingletonScope() -CONTAINER.bind(TYPES.Sheet).to(SheetService).inSingletonScope() -CONTAINER.bind(TYPES.Data).to(DataService).inSingletonScope() +CONTAINER.bind(TYPES.Data).to(DataServiceImpl).inSingletonScope() diff --git a/src/core/ioc/types.ts b/src/core/ioc/types.ts index d5b543b7..85e13301 100644 --- a/src/core/ioc/types.ts +++ b/src/core/ioc/types.ts @@ -1,6 +1,3 @@ export const TYPES = { - Backend: Symbol.for('Backend'), Data: Symbol.for('Data'), - Sheet: Symbol.for('Sheet'), - Render: Symbol.for('Render'), }