diff --git a/src/components/scroller.ts b/src/components/scroller.ts index a09dcca..6d5d7ce 100644 --- a/src/components/scroller.ts +++ b/src/components/scroller.ts @@ -1,5 +1,12 @@ import { Spreadsheet } from "../main" +export interface ViewportRect { + top: number + left: number + right: number + bottom: number +} + export class Scroller { element: HTMLDivElement private verticalScroller: HTMLDivElement @@ -8,7 +15,7 @@ export class Scroller { constructor(root: Spreadsheet) { this.root = root - const {horizontalScroller, scroller, verticalScroller} = this.buildComponent() + const { horizontalScroller, scroller, verticalScroller } = this.buildComponent() this.element = scroller this.verticalScroller = verticalScroller this.horizontalScroller = horizontalScroller @@ -17,6 +24,29 @@ export class Scroller { this.element.style.width = this.root.config.view.width + 'px' this.updateScrollerSize() //* Init size set + + this.element.addEventListener('scroll', this.handleScroll) + } + + handleScroll = () => { + const rect = this.getViewportBoundlingRect() + this.root.viewport.updateValues(rect) + + this.root.renderSheet() + } + + getViewportBoundlingRect(): ViewportRect { + const { scrollTop, scrollLeft } = this.element + const { height, width } = this.element.getBoundingClientRect() + const bottom = scrollTop + height + const right = scrollLeft + width + + return { + top: scrollTop, + left: scrollLeft, + bottom, + right + } } buildComponent() { @@ -30,18 +60,18 @@ export class Scroller { verticalScroller.style.pointerEvents = 'none' horizontalScroller.style.pointerEvents = 'none' - + groupScrollers.style.display = 'flex' stack.appendChild(verticalScroller) stack.appendChild(horizontalScroller) groupScrollers.appendChild(stack) this.verticalScroller = verticalScroller - this.horizontalScroller = horizontalScroller + this.horizontalScroller = horizontalScroller scroller.appendChild(groupScrollers) scroller.classList.add('scroller') - return {scroller, verticalScroller, horizontalScroller} + return { scroller, verticalScroller, horizontalScroller } } getActualHeight() { diff --git a/src/components/sheet.ts b/src/components/sheet.ts index 75722e2..f8f8c3f 100644 --- a/src/components/sheet.ts +++ b/src/components/sheet.ts @@ -33,15 +33,27 @@ export class Sheet { } renderSheet() { - const {columns, rows} = this.root.config - const lastColIdx = columns.length - 1 - const lastRowIdx = rows.length - 1 + const firstRowIdx = this.root.viewport.firstRow + const lastColIdx = this.root.viewport.lastCol + 3 + const lastRowIdx = this.root.viewport.lastRow + 3 + const firstColIdx = this.root.viewport.firstCol + + console.log() + + let rowsCount = 0 + + for(let row = firstRowIdx; row <= lastRowIdx; row++) { + + for(let col = firstColIdx; col <= lastColIdx; col++ ) { + if(!this.root.config.columns[col] || !this.root.config.rows[row]) break; //* Prevent read undefined - for(let row = 0; row <= lastRowIdx; row++) { - for(let col = 0; col <= lastColIdx; col++ ) { this.renderCell({column: col, row}) } + + rowsCount++; } + + console.log(`Rendered ${rowsCount} rows!`) } } \ No newline at end of file diff --git a/src/components/viewport.ts b/src/components/viewport.ts deleted file mode 100644 index 0366bc8..0000000 --- a/src/components/viewport.ts +++ /dev/null @@ -1,3 +0,0 @@ -export class Viewport { - -} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 5d5faf5..4a0b2fd 100644 --- a/src/main.ts +++ b/src/main.ts @@ -7,6 +7,7 @@ import { Toolbar } from "./components/toolbar"; import { Cell } from "./modules/cell"; import { Config, ViewProperties } from "./modules/config"; import { Styles } from "./modules/styles"; +import { Viewport } from "./modules/viewport"; import './scss/main.scss' import { createSampleConfig, createSampleData } from "./utils/createData"; @@ -37,22 +38,25 @@ export class Spreadsheet { public styles: Styles public config: Config public data: Cell[][] + public viewport: Viewport constructor(target: string | HTMLElement, props?: SpreadsheetConstructorProperties) { - const config = createSampleConfig(40, 40) + const config = createSampleConfig(150, 150) if(props?.view) { config.view = props.view } this.config = new Config(config) this.sheet = new Sheet(this) - const data = createSampleData(40, 40) + const data = createSampleData(150, 150) this.table = new Table(this) this.scroller = new Scroller(this) this.toolbar = new Toolbar(this) this.header = new Header(this) this.editor = new Editor(this) - + this.viewport = new Viewport(this, this.scroller.getViewportBoundlingRect()) + + this.data = data this.styles = new Styles() this.buildComponent() diff --git a/src/modules/cell.ts b/src/modules/cell.ts index 1d2d494..5ee3e56 100644 --- a/src/modules/cell.ts +++ b/src/modules/cell.ts @@ -31,11 +31,22 @@ export class Cell { } render(root: Spreadsheet) { - const {height, width, x, y} = new RenderBox(root.config, this.position) + let {height, width, x, y} = new RenderBox(root.config, this.position) const { ctx } = root + + y -= root.viewport.top + x -= root.viewport.left + + ctx.clearRect(x, y, width, height) ctx.fillStyle = 'white' ctx.strokeStyle = 'black' ctx.fillRect(x + 1, y + 1, width - 1, height - 1) ctx.strokeRect(x, y, width, height) + + ctx.fillStyle = 'black' + ctx.textAlign = 'left' + ctx.font = '16px Arial' + ctx.textBaseline = 'middle' + ctx.fillText(this.displayValue, x + 2, y + height / 2, width) } } \ No newline at end of file diff --git a/src/modules/viewport.ts b/src/modules/viewport.ts new file mode 100644 index 0000000..ca8d331 --- /dev/null +++ b/src/modules/viewport.ts @@ -0,0 +1,107 @@ +import { Spreadsheet } from "../main" + +export type ViewportConstructorProps = { + top: number + left: number + right: number + bottom: number +} + +export class Viewport { + root: Spreadsheet + + top: number + left: number + right: number + bottom: number + + firstRow: number + lastRow: number + firstCol: number + lastCol: number + + constructor(root: Spreadsheet, props: ViewportConstructorProps) { + this.root = root + + this.top = props.top + this.left = props.left + this.right = props.right + this.bottom = props.bottom + + this.firstRow = this.getFirstRow() + this.lastCol = this.getFirstRow() //!Temp + this.firstCol = this.getFirstRow() //!Temp + this.lastRow = this.getLastRow() + + this.updateValues({ + top: 0, + left: 0, + right: this.root.viewProps.width, + bottom: this.root.viewProps.height + }) + } + + updateValues(props: ViewportConstructorProps) { + this.top = props.top + this.left = props.left + this.right = props.right + this.bottom = props.bottom + + this.firstRow = this.getFirstRow() + this.lastRow = this.getLastRow() + this.firstCol = this.getFirstCol() + this.lastCol = this.getLastCol() + + console.log({ first: this.firstCol, last: this.lastCol }) + } + + /** Get index of first row in viewport */ + private getFirstRow(): number { + let rowIdx = 0 + for (let idx = 0, currHeight = 0; currHeight <= this.top; idx++) { + currHeight += this.root.config.rows[idx].height + rowIdx = idx + } + return rowIdx + } + + private getLastRow(): number { + let rowIdx = this.getFirstRow() + let height = this.top + + while (height <= this.bottom) { + height += this.root.config.rows[rowIdx].height + if (height >= this.bottom) break; + rowIdx++; + } + + return rowIdx + } + + private getFirstCol(): number { + let colIdx = 0; + let currWidth = 0 + + while (currWidth <= this.left) { + currWidth += this.root.config.columns[colIdx].width + if (currWidth >= this.left) break; + colIdx += 1 + } + + return colIdx + } + + private getLastCol(): number { + let colIdx = this.getFirstCol() + let width = this.left + + while (width <= this.right) { + width += this.root.config.columns[colIdx].width + if (width >= this.right) break; + colIdx++; + } + + return colIdx + } + +} \ No newline at end of file