Added full support of rendering table

Todo: test with resize
This commit is contained in:
Eugene 2023-07-20 21:08:24 +03:00
parent 25e698a658
commit ee85d771a4
6 changed files with 177 additions and 16 deletions

View File

@ -1,5 +1,12 @@
import { Spreadsheet } from "../main" import { Spreadsheet } from "../main"
export interface ViewportRect {
top: number
left: number
right: number
bottom: number
}
export class Scroller { export class Scroller {
element: HTMLDivElement element: HTMLDivElement
private verticalScroller: HTMLDivElement private verticalScroller: HTMLDivElement
@ -8,7 +15,7 @@ export class Scroller {
constructor(root: Spreadsheet) { constructor(root: Spreadsheet) {
this.root = root this.root = root
const {horizontalScroller, scroller, verticalScroller} = this.buildComponent() const { horizontalScroller, scroller, verticalScroller } = this.buildComponent()
this.element = scroller this.element = scroller
this.verticalScroller = verticalScroller this.verticalScroller = verticalScroller
this.horizontalScroller = horizontalScroller this.horizontalScroller = horizontalScroller
@ -17,6 +24,29 @@ export class Scroller {
this.element.style.width = this.root.config.view.width + 'px' this.element.style.width = this.root.config.view.width + 'px'
this.updateScrollerSize() //* Init size set 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() { buildComponent() {
@ -37,11 +67,11 @@ export class Scroller {
stack.appendChild(horizontalScroller) stack.appendChild(horizontalScroller)
groupScrollers.appendChild(stack) groupScrollers.appendChild(stack)
this.verticalScroller = verticalScroller this.verticalScroller = verticalScroller
this.horizontalScroller = horizontalScroller this.horizontalScroller = horizontalScroller
scroller.appendChild(groupScrollers) scroller.appendChild(groupScrollers)
scroller.classList.add('scroller') scroller.classList.add('scroller')
return {scroller, verticalScroller, horizontalScroller} return { scroller, verticalScroller, horizontalScroller }
} }
getActualHeight() { getActualHeight() {

View File

@ -33,15 +33,27 @@ export class Sheet {
} }
renderSheet() { renderSheet() {
const {columns, rows} = this.root.config const firstRowIdx = this.root.viewport.firstRow
const lastColIdx = columns.length - 1 const lastColIdx = this.root.viewport.lastCol + 3
const lastRowIdx = rows.length - 1 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}) this.renderCell({column: col, row})
} }
rowsCount++;
} }
console.log(`Rendered ${rowsCount} rows!`)
} }
} }

View File

@ -1,3 +0,0 @@
export class Viewport {
}

View File

@ -7,6 +7,7 @@ import { Toolbar } from "./components/toolbar";
import { Cell } from "./modules/cell"; import { Cell } from "./modules/cell";
import { Config, ViewProperties } from "./modules/config"; import { Config, ViewProperties } from "./modules/config";
import { Styles } from "./modules/styles"; import { Styles } from "./modules/styles";
import { Viewport } from "./modules/viewport";
import './scss/main.scss' import './scss/main.scss'
import { createSampleConfig, createSampleData } from "./utils/createData"; import { createSampleConfig, createSampleData } from "./utils/createData";
@ -37,21 +38,24 @@ export class Spreadsheet {
public styles: Styles public styles: Styles
public config: Config public config: Config
public data: Cell[][] public data: Cell[][]
public viewport: Viewport
constructor(target: string | HTMLElement, props?: SpreadsheetConstructorProperties) { constructor(target: string | HTMLElement, props?: SpreadsheetConstructorProperties) {
const config = createSampleConfig(40, 40) const config = createSampleConfig(150, 150)
if(props?.view) { if(props?.view) {
config.view = props.view config.view = props.view
} }
this.config = new Config(config) this.config = new Config(config)
this.sheet = new Sheet(this) this.sheet = new Sheet(this)
const data = createSampleData(40, 40) const data = createSampleData(150, 150)
this.table = new Table(this) this.table = new Table(this)
this.scroller = new Scroller(this) this.scroller = new Scroller(this)
this.toolbar = new Toolbar(this) this.toolbar = new Toolbar(this)
this.header = new Header(this) this.header = new Header(this)
this.editor = new Editor(this) this.editor = new Editor(this)
this.viewport = new Viewport(this, this.scroller.getViewportBoundlingRect())
this.data = data this.data = data
this.styles = new Styles() this.styles = new Styles()

View File

@ -31,11 +31,22 @@ export class Cell {
} }
render(root: Spreadsheet) { 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 const { ctx } = root
y -= root.viewport.top
x -= root.viewport.left
ctx.clearRect(x, y, width, height)
ctx.fillStyle = 'white' ctx.fillStyle = 'white'
ctx.strokeStyle = 'black' ctx.strokeStyle = 'black'
ctx.fillRect(x + 1, y + 1, width - 1, height - 1) ctx.fillRect(x + 1, y + 1, width - 1, height - 1)
ctx.strokeRect(x, y, width, height) 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)
} }
} }

107
src/modules/viewport.ts Normal file
View File

@ -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
}
}