diff --git a/.gitignore b/.gitignore index 98de30f..4b6c3dc 100644 --- a/.gitignore +++ b/.gitignore @@ -105,4 +105,6 @@ dist dist/ demo/ -example/main.bundle.js \ No newline at end of file +example/main.bundle.js + +.idea/ diff --git a/example/src/components/demo-home/demo-home.ts b/example/src/components/demo-home/demo-home.ts index 278c98b..e8d2b1d 100644 --- a/example/src/components/demo-home/demo-home.ts +++ b/example/src/components/demo-home/demo-home.ts @@ -3,7 +3,7 @@ import { IGridColumn } from '../../../../src/interface'; import { GridConfig, GridEvents } from '../../../../src/model'; class DemoHome { - usersList: any; + usersList: any[] = []; constructor() { const gsGridRef = gsGrid; @@ -54,8 +54,14 @@ class DemoHome { async setupGridAndApplyData() { const gridConfig = new GridConfig(); gridConfig.columnDefs = this.buildGridColumns(); + gridConfig.rowHeight = 31; + + const usersList = await this.getUsers(); + + for (let i = 0; i<=10; i ++) { + this.usersList = this.usersList.concat([... usersList]); + } - this.usersList = await this.getUsers(); gridConfig.data = this.usersList; const gridEl = document.querySelector('gs-grid'); if (gridEl) { diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000..cf71ad0 Binary files /dev/null and b/favicon.ico differ diff --git a/package.json b/package.json index a427ff8..54da3e9 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { - "name": "@gs-grid/gs-grid", - "version": "0.0.1-alpha.2", + "name": "@flatui/grid", + "version": "0.0.1-alpha.3", "description": "A new grid for evolving web", "main": "index.ts", - "repository": "https://github.com/ganesh-vellanki/gs-grid", + "repository": "https://github.com/flatui/grid", "author": "velankiganesh@gmail.com", "license": "MIT", "private": false, diff --git a/src/core/scroll.utilities.ts b/src/core/scroll.utilities.ts index 252654d..4d2f4f4 100644 --- a/src/core/scroll.utilities.ts +++ b/src/core/scroll.utilities.ts @@ -1,3 +1,7 @@ +import { BehaviorSubject } from "rxjs"; +import { IGridScrollPosition } from "../interface"; +import { GridScrollPosition } from "../model/grid-scroll-position"; + /** * Scroll utilities. */ @@ -42,12 +46,18 @@ export class ScrollUtilities { */ private dragCallback = (event: MouseEvent) => {this.drag(event)}; + /** + * Scroll move complete publisher. + */ + public scrollMoveComplete$: BehaviorSubject; + /** * Creates an instance of scroll utilities. */ constructor(shadowRoot: ShadowRoot) { this.shadowRoot = shadowRoot; this.setScrollBounds(); + this.scrollMoveComplete$ = new BehaviorSubject(new GridScrollPosition(0, 0, 100)); } /** @@ -121,6 +131,8 @@ export class ScrollUtilities { dragEnd(event: MouseEvent): void { this.isScrollBarActivated = false; this.resetScrollVisibility(); + const scrollBar = this.getGridScrollBar(); + this.scrollMoveComplete$.next(new GridScrollPosition(this.yMin, scrollBar.getBoundingClientRect().y, this.yMax)); } /** @@ -165,6 +177,7 @@ export class ScrollUtilities { if (this.isPositionInBounds(nextPosition)) { scrollBar.style.top = nextPosition - this.yMin + 'px'; + this.scrollMoveComplete$.next(new GridScrollPosition(this.yMin, nextPosition, this.yMax)); } } diff --git a/src/core/virtualize.ts b/src/core/virtualize.ts index 74be8a3..7611adc 100644 --- a/src/core/virtualize.ts +++ b/src/core/virtualize.ts @@ -1,6 +1,66 @@ +import { IGridConfig, IGridScrollPosition } from "../interface"; + /** * Virtualize core. */ export class Virtualize { - + + /** + * Available grid height of virtualize. + */ + private availableGridHeight: number; + + /** + * Data set of virtualize. + */ + private dataSet: any[]; + + /** + * Gets rows per index. + */ + private get rowsPerIndex(): number { + return this.availableGridHeight / this.gridConfig.rowHeight; + } + + /** + * Creates an instance of virtualize. + * @param gridConfig grid configuration. + * @param shadowRoot shadow root. + */ + constructor(private gridConfig: IGridConfig, private shadowRoot: ShadowRoot) { + this.availableGridHeight = 200; + this.dataSet = [...this.gridConfig.data]; + this.setGridHeight(); + } + + /** + * Callback on grid scroll position change. + * @param scrollYPos scroll position. + */ + public OnGridScrollPositionChange(scrollYPos: IGridScrollPosition) { + } + + /** + * Gets data set for index. + */ + private getDataSetForIndex(scrollIndex: number) { + const index = scrollIndex * this.rowsPerIndex; + return this.dataSet.slice(index, index + this.rowsPerIndex); + } + + /** + * Sets grid height using shadow root. + */ + private setGridHeight() { + const viewport = this.getViewport(); + viewport.style.height = `${this.availableGridHeight}px`; + } + + /** + * Gets viewport element. + * @returns viewport element. + */ + private getViewport(): HTMLElement { + return this.shadowRoot.querySelector('.data-viewport'); + } } \ No newline at end of file diff --git a/src/gs-grid.ts b/src/gs-grid.ts index 6929e47..af6a622 100644 --- a/src/gs-grid.ts +++ b/src/gs-grid.ts @@ -1,5 +1,5 @@ -import { CellUtilities } from "./core"; -import { IGridConfig, IGridRenderer } from "./interface"; +import { CellUtilities, Virtualize } from "./core"; +import { IGridConfig, IGridRenderer, IGridScrollPosition } from "./interface"; import { GridColumn } from "./model"; import { FlexHeaderRenderer, FlexDataRowRenderer, ScrollRenderer } from "./renderers"; import { ScrollUtilities } from './core'; @@ -48,6 +48,11 @@ export class GsGrid extends HTMLElement { */ private cellUtils: CellUtilities; + /** + * Virtualization core of gs grid. + */ + private virtualizationCore: Virtualize; + /** * Creates an instance of gs-grid. */ @@ -55,6 +60,7 @@ export class GsGrid extends HTMLElement { super(); this.instanceId = this.generateInstanceId(); this.registerGridEventCallback(); + this._currentScrollIndex = 0; } /** @@ -129,15 +135,26 @@ export class GsGrid extends HTMLElement { this.initializeScrollBar(); // Init smart scroll. + // TODO: move smart scroll reg to new method. + // TODO: Use Rxjs & remove timeout. var smartScroll = new ScrollUtilities(this.shadowRoot); setTimeout(() => { smartScroll.registerSmartScrollEvents(); }, 2000); + + // Init virtualization core. + // TODO: Use Rxjs & remove timeout. + setTimeout(() => { + this.initializeVirtualization(); + smartScroll.scrollMoveComplete$.subscribe((scrollPosition: IGridScrollPosition) => { + this.virtualizationCore.OnGridScrollPositionChange(scrollPosition); + }); + }, 500); } /** * Registers renderers of header & column of grid. - * @param gridConfig + * @param gridConfig grid configuration. */ registerRenderers(gridConfig: IGridConfig) { const rendererDataSet = gridConfig.columnDefs.map(x => { @@ -145,10 +162,10 @@ export class GsGrid extends HTMLElement { }); // Register header renderer. - this.headerRenderer = new FlexHeaderRenderer(rendererDataSet, this.cellUtils); + this.headerRenderer = new FlexHeaderRenderer(rendererDataSet, this.cellUtils, this.gridConfig); // Register data row renderer. - this.dataRowRenderer = new FlexDataRowRenderer(rendererDataSet, this.cellUtils); + this.dataRowRenderer = new FlexDataRowRenderer(rendererDataSet, this.cellUtils, this.gridConfig, this.shadowRoot); // Register viewport scroll renderer. this.scrollRenderer = new ScrollRenderer(); @@ -165,7 +182,7 @@ export class GsGrid extends HTMLElement { * Initializes viewport. */ private initializeViewport() { - this.shadowRoot.append(this.dataRowRenderer.render({ data: this.gridConfig.data })); + this.dataRowRenderer.renderIntoViewport({data: this.gridConfig.data}); } /** @@ -174,7 +191,7 @@ export class GsGrid extends HTMLElement { private initializeScrollBar() { const viewport = this.shadowRoot.querySelector('.data-viewport'); if (viewport) { - viewport.append(this.scrollRenderer.render()); + viewport.prepend(this.scrollRenderer.render()); } } @@ -188,6 +205,13 @@ export class GsGrid extends HTMLElement { this.shadowRoot.appendChild(styleRoot); } + /** + * Initializes virtualization. + */ + private initializeVirtualization() { + this.virtualizationCore = new Virtualize(this.gridConfig, this.shadowRoot); + } + /** * Gets available width for grid. * @returns available width for grid, either itself or parent. diff --git a/src/interface/grid-config.ts b/src/interface/grid-config.ts index 4daacfe..cd6dcae 100644 --- a/src/interface/grid-config.ts +++ b/src/interface/grid-config.ts @@ -11,10 +11,15 @@ export interface IGridConfig { columnDefs: IGridColumn[]; /** - * Instance of grid config + * Instance of grid config */ instance: IGridInstance; + /** + * Row height of grid config. + */ + rowHeight: number; + /** * Determines whether instance ready is * @returns true if instance ready diff --git a/src/interface/igrid-renderer.ts b/src/interface/igrid-renderer.ts index af1a76a..208b57b 100644 --- a/src/interface/igrid-renderer.ts +++ b/src/interface/igrid-renderer.ts @@ -12,4 +12,9 @@ export interface IGridRenderer { * @returns boolean promise, true once complete. */ queueRender(): Promise; + + /** + * Render and insert into viewport. + */ + renderIntoViewport(data?: any): void; } \ No newline at end of file diff --git a/src/interface/igrid-scroll-position.ts b/src/interface/igrid-scroll-position.ts new file mode 100644 index 0000000..f0453a8 --- /dev/null +++ b/src/interface/igrid-scroll-position.ts @@ -0,0 +1,25 @@ +/** + * grid scroll position interface. + */ +export interface IGridScrollPosition { + /** + * y minimum. + */ + yMin: number; + + /** + * y maximum. + */ + yMax: number; + + /** + * y position. + */ + y: number; + + /** + * get percentage of y position. + * @returns y percent. + */ + getYPercent(): number; +} \ No newline at end of file diff --git a/src/interface/index.ts b/src/interface/index.ts index afe45e3..c6af51c 100644 --- a/src/interface/index.ts +++ b/src/interface/index.ts @@ -5,3 +5,4 @@ export { IGridRenderer } from './igrid-renderer' export { IGridRenderColumn } from './igrid-renderer-column'; export { ICellConfig } from './icell-config'; export { IGridEvents } from './grid-events'; +export { IGridScrollPosition } from './igrid-scroll-position'; diff --git a/src/model/grid-config.ts b/src/model/grid-config.ts index 0586f2d..39cc287 100644 --- a/src/model/grid-config.ts +++ b/src/model/grid-config.ts @@ -4,7 +4,7 @@ import {Observable, Subject, BehaviorSubject} from 'rxjs'; /** * Grid configuration class. */ -export class GridConfig implements IGridConfig { +export class GridConfig implements IGridConfig { /** * Is grid instance ready. */ @@ -20,6 +20,11 @@ export class GridConfig implements IGridConfig { */ columnDefs: IGridColumn[]; + /** + * Row height of grid config. + */ + rowHeight: number; + /** * Instance of grid config */ @@ -28,13 +33,14 @@ export class GridConfig implements IGridConfig { /** * Data of grid config */ - data?: any[]; + data?: T[]; /** * Creates an instance of grid config. */ constructor(){ this.renderCompleteSubscription = new BehaviorSubject(false); + this.rowHeight = 40; } /** diff --git a/src/model/grid-scroll-position.ts b/src/model/grid-scroll-position.ts new file mode 100644 index 0000000..ab2fe59 --- /dev/null +++ b/src/model/grid-scroll-position.ts @@ -0,0 +1,39 @@ +import { IGridScrollPosition } from "../interface"; + +/** + * Grid scroll position. + */ +export class GridScrollPosition implements IGridScrollPosition { + + /** + * Creates an instance of grid scroll position. + * @param args init args. + */ + constructor(yMin: number, y:number, yMax: number) { + this.yMin = yMin; + this.y = y; + this.yMax = yMax; + } + + /** + * Y min of grid scroll position. + */ + yMin: number; + /** + * Y max of grid scroll position. + */ + yMax: number; + + /** + * Y of grid scroll position. + */ + y: number; + + /** + * Gets y percent. + * @returns y percent. + */ + getYPercent(): number { + return ((this.y - this.yMin) / (this.yMax - this.yMin)) * 100; + } +} \ No newline at end of file diff --git a/src/renderers/data-row.renderer.ts b/src/renderers/data-row.renderer.ts index 4c2ce3e..b5d67ca 100644 --- a/src/renderers/data-row.renderer.ts +++ b/src/renderers/data-row.renderer.ts @@ -1,27 +1,21 @@ import { CellUtilities } from "../core"; -import { IGridRenderColumn, IGridRenderer } from "../interface"; +import { IGridConfig, IGridRenderColumn, IGridRenderer } from "../interface"; /** * Flex data row renderer. */ export class FlexDataRowRenderer implements IGridRenderer { - /** - * Render cols of flex column renderer. - */ - private _renderCols: IGridRenderColumn[]; - - /** - * Cell utils of flex header renderer. - */ - private _cellUtils: CellUtilities; - /** * Creates an instance of flex column renderer. * @param columns grid columns. + * @param cellUtils cell utilities. + * @param gridConfig grid config. */ - constructor(columns: IGridRenderColumn[], cellUtils: CellUtilities) { - this._renderCols = columns; - this._cellUtils = cellUtils; + constructor(private _renderCols: IGridRenderColumn[], + private _cellUtils: CellUtilities, + private gridConfig: IGridConfig, + private shadowRoot: ShadowRoot) + { } /** @@ -44,6 +38,42 @@ export class FlexDataRowRenderer implements IGridRenderer { return dataViewport; } + /** + * Renders into viewport. + * @param [renderOptions] render options. + */ + renderIntoViewport(renderOptions?: any): void { + if (this.shadowRoot) { + this.shadowRoot.append(this.render({ data: this.gridConfig.data })); + } + } + + updateViewportRowsUp(renderOptions?: any): void { + const viewport = this.shadowRoot.querySelector('.data-viewport'); + + if (viewport.innerHTML.length > 0) { + viewport.classList.add('scrolling-viewport'); + viewport.innerHTML = this.renderNewRows(renderOptions.data); + viewport.classList.remove('scrolling-viewport'); + } + } + + updateViewportRowsDown(renderOptions: any): void { + } + + renderNewRows(data: any[]) { + let colTemplate = ''; + if(data && data.length > 0) { + data.forEach(dataRow => { + this._renderCols.forEach(col => { + colTemplate += this.cellTemplateFragmentFn(col.field, dataRow); + }); + }); + } + + return colTemplate; + } + /** * Queues render async. * @returns render. @@ -61,7 +91,7 @@ export class FlexDataRowRenderer implements IGridRenderer { private cellTemplateFragmentFn(field: string, data: any): string { const cellUtils = this._cellUtils.getCellUtilsByFieldName(field); const cellValue = this.getCellValue(field, data); - return `
${cellValue}
`; + return `
${cellValue}
`; } /** diff --git a/src/renderers/header.renderer.ts b/src/renderers/header.renderer.ts index fb6f000..fe20307 100644 --- a/src/renderers/header.renderer.ts +++ b/src/renderers/header.renderer.ts @@ -1,5 +1,5 @@ import { CellUtilities } from "../core"; -import { ICellConfig, IGridRenderColumn, IGridRenderer } from "../interface"; +import { ICellConfig, IGridConfig, IGridRenderColumn, IGridRenderer } from "../interface"; /** * Flex header renderer. @@ -17,14 +17,22 @@ export class FlexHeaderRenderer implements IGridRenderer { /** * Creates an instance of flex header renderer. - * @param columns - * @param cellUtils + * @param columns grid columns. + * @param cellUtils cell utilities. */ - constructor(columns: IGridRenderColumn[], cellUtils: CellUtilities) { + constructor(columns: IGridRenderColumn[], cellUtils: CellUtilities, private gridConfig: IGridConfig) { this._renderCols = columns; this._cellUtils = cellUtils; } + /** + * Renders into viewport. + * @param [data] options & data. + */ + renderIntoViewport(data?: any): void { + throw new Error("Method not implemented."); + } + /** * Renders flex header renderer. */ @@ -55,7 +63,7 @@ export class FlexHeaderRenderer implements IGridRenderer { */ private cellTemplateFragmentFn(cellValue: string, field: string): string { const cellUtil = this._cellUtils.getCellUtilsByFieldName(field); - return `
${cellValue}
`; + return `
${cellValue}
`; } /** diff --git a/src/renderers/scroll.renderer.ts b/src/renderers/scroll.renderer.ts index 56e0224..080185f 100644 --- a/src/renderers/scroll.renderer.ts +++ b/src/renderers/scroll.renderer.ts @@ -11,6 +11,14 @@ export class ScrollRenderer implements IGridRenderer { constructor() { } + /** + * Renders into viewport. + * @param [data] render rows. + */ + renderIntoViewport(data?: any): void { + throw new Error("Method not implemented."); + } + /** * Renders scroll renderer. * @param [data] render data. diff --git a/src/styles/gs-grid.scss b/src/styles/gs-grid.scss index feabe23..91b6761 100644 --- a/src/styles/gs-grid.scss +++ b/src/styles/gs-grid.scss @@ -12,6 +12,7 @@ display: flex; flex-direction: column; font-size: 14px; + justify-content: center; .cell-content { overflow: hidden; @@ -33,6 +34,8 @@ display: inline-flex; flex-direction: column; position: relative; + border-bottom: 1px solid #d3d3d3; + overflow: hidden; .data-row { display: inline-flex; @@ -41,12 +44,17 @@ border-right: 1px solid #d3d3d3; border-left: 1px solid #d3d3d3; + &:last-child { + border-bottom: none; + } + .cell-column { padding: 5px 10px; border-right: 1px solid #d3d3d3; display: flex; flex-direction: column; overflow: hidden; + justify-content: center; .cell-content { overflow: hidden;