import {EventCollection, ValueType, KeyCollection} from "./common"; //literal import { Column, IColumnStyle } from "./model"; import {Cell, HeaderRow, BodyRows} from "./cells"; import {DataCollector, IRowData} from "./dataControl"; import {EventDriver, EventRecognizer, ClickEventRecognizer, DoubleClickEventRecognizer, KeyDownEventRecognizer, MouseOverEventRecognizer} from "./event"; import { EditBox } from "./edit"; class EventObject { private _listener:{[key:string]:EventRecognizer}; constructor() { this._listener = {}; } addListener(event:string, listener:EventRecognizer){ this._listener[event] = listener; } removeListener(event:string){ delete this._listener[event]; } fireListener(eventType:string, event:Event, grid?:Grid){ let callback = this._listener[eventType]["handle"]; callback.call(this._listener[eventType], {event, grid}); } } interface IDataConfig { columns: Array; data: Array; } export class Grid extends EventObject { private _elementID: string; private _cols: Array; private _dataCollector: DataCollector; private _headerRow: HeaderRow; private _bodyRows: BodyRows; private _table: HTMLElement; private _domElement: HTMLElement | null; private _selectedCellInfo: {fieldName: string, rowIndex: number} = {fieldName: "", rowIndex: NaN} private _mousePos: {X: number, Y: number} = {X: NaN, Y: NaN} constructor(_elementID: string, dataCon: IDataConfig) { //수정 super(); //properties this._elementID = _elementID; this._cols = []; this._dataCollector = new DataCollector(); //element this._table = this._createTableElement(); this._domElement = this._createElement(); this._headerRow = new HeaderRow(this._table); this._bodyRows = new BodyRows(this._table); //parsing this._parse(dataCon); if (this._domElement) this._domElement.appendChild(this._table); //Event let grid = this; let table = this._table this._giveAddListener(); table.addEventListener(EventCollection.EVENT_CLICK, function(event:MouseEvent){ grid.fireListener(EventCollection.EVENT_CLICK, event, grid); }); //상수 table.addEventListener(EventCollection.EVENT_MOUSEOVER, function(event:MouseEvent){ grid.fireListener(EventCollection.EVENT_MOUSEOVER, event, grid); }); table.addEventListener(EventCollection.EVENT_DOUBLECLICK, function(event:MouseEvent){ grid.fireListener(EventCollection.EVENT_DOUBLECLICK, event, grid); }); document.addEventListener(EventCollection.EVENT_KEYDOWN, function(event:KeyboardEvent){ grid.fireListener(EventCollection.EVENT_KEYDOWN, event, grid); }); } //basic method private _createTableElement(): HTMLElement { let table = document.createElement("table"); table.className = "gridTable"; return table; } private _createElement(): HTMLElement { //get let element = document.getElementById(this._elementID); //container; if (!element){ throw new Error("Container Element Not Found"); //Error handling; } //union type exception throws return element; } //basic parsing method private _parse(dataCon: IDataConfig):void { this._createColumns(dataCon.columns); this.dataBind(dataCon.data); //dataclass } //getter public getColumn(index: number): Column { if (this._cols[index]) { return this._cols[index]; } throw new Error("Corresponding column doesn't exist"); } public get bodyRowsLen(): number { return this._bodyRows.rowCount; } public get tableDOMElement(): HTMLElement { return this._table; } public get selectedCellInfo() { return this._selectedCellInfo; } public get mousePos() { return this._mousePos } //method in api public setColumns(cols: Array): void { let colsLen = this._cols.length; for (let i = colsLen - 1; i > -1; i--) { this.deleteColumn(this._cols[i].fieldName); } this.addColumns(cols); } public addColumns(cols: Array): void { let newColLen = cols.length; let colLen = this._cols.length; for (let num = 0; num < newColLen; num++) { let isRepeated = false; for (let i = 0; i < colLen; i++) { if (this._cols[i].fieldName == cols[num].fieldName) { isRepeated = true; break; } } if (isRepeated) { continue; } let addedColumn = this._parseColumnStyle(cols[num]); this._cols.push(addedColumn); this._createHeaderCell(addedColumn); //making empty cells this._bodyRows.buildColumnCell(addedColumn.fieldName, addedColumn, this._dataCollector); } } public deleteColumns(colNames: Array): void { let len = colNames.length; for (let i = 0; i < len; i++) { this.deleteColumn(colNames[i]); } } public dataBind(data: Array): void { this._dataCollector.setData(data); this._bodyRows.setRows(this._dataCollector, this._cols); } public addRows(rows: Array): void { let previousRowCount = this._dataCollector.dataLength; this._dataCollector.addRowsData(rows); this._bodyRows.addRows(this._dataCollector, this._cols, previousRowCount, this._dataCollector.dataLength); } public deleteRows(indexes: Array): void { indexes.sort((a, b)=>b-a); this._dataCollector.deleteRowsData(indexes); this._bodyRows.removeRows(indexes); } public updateCell(rowIndex: number, fieldName: string, value: ValueType): void { this._dataCollector.updateDatum(rowIndex, fieldName, value); this._bodyRows.updateCell(rowIndex, fieldName, value); } public setProperties(fieldNames: Array, propertyStyle: string, value: ValueType): void { let nameLen = fieldNames.length; for (let i = 0; i < nameLen; i++) { let index = this.indexOfColumn(fieldNames[i]); let parsedStyle = propertyStyle.split("."); if (parsedStyle[0] == "header") { this._cols[index].header[parsedStyle[1]] = value; this._headerRow.assignStyle(fieldNames[i], parsedStyle[1], value); } else if (parsedStyle[0] == "body") { this._cols[index].body[parsedStyle[1]] = value; this._bodyRows.assignStyle(fieldNames[i], parsedStyle[1], value); } else { this._cols[index][parsedStyle[0]] = value; this._headerRow.assignStyle(fieldNames[i], parsedStyle[0], value); this._bodyRows.assignStyle(fieldNames[i], parsedStyle[0], value); } } } public setColumnsStyle(fieldNames: Array, propertyStyle: IColumnStyle) { let nameLen = fieldNames.length for (let i = 0; i < nameLen; i++) { let index = this.indexOfColumn(fieldNames[i]); this._cols[index].parse(propertyStyle); // column change this._bodyRows.setStyle(fieldNames[i], this._cols[index]); this._headerRow.setStyle(fieldNames[i], this._cols[index]); // header, body change } } //event method private _giveAddListener(): void { //bindListener this.addListener(EventCollection.EVENT_CLICK, new ClickEventRecognizer()); this.addListener(EventCollection.EVENT_MOUSEOVER, new MouseOverEventRecognizer()); this.addListener(EventCollection.EVENT_DOUBLECLICK, new DoubleClickEventRecognizer()); this.addListener(EventCollection.EVENT_KEYDOWN, new KeyDownEventRecognizer()); // meaningful } //inner method private _parseColumnStyle(column: IColumnStyle): Column { let col = new Column(); col.parse(column); return col; } private _createColumns(cols: Array): void { let colLen = cols.length for (let colIndex = 0; colIndex < colLen; colIndex++) { let addedColumn = this._parseColumnStyle(cols[colIndex]); this._cols.push(addedColumn); this._createHeaderCell(addedColumn); } } private _createHeaderCell(column: Column): void { let value = column.header.text; this._headerRow.addCell(column.fieldName, value, column); } public deleteColumn(fieldName: string): void { this._headerRow.removeCell(fieldName); //remove corresponding header let colLen = this._cols.length; for (let j = colLen - 1; j > -1; j--) {//뒤에서 부터 앞으로 if (fieldName == this._cols[j].fieldName) { this._cols.splice(j, 1); this._bodyRows.removeCellsByColumn(fieldName); break; } } } public indexOfColumn(fieldName: string): number { // 셀단위 찾을 때 // index error let len = this._cols.length; for (let i = 0; i < len; i++) { if (this._cols[i].fieldName == fieldName) { return i; } } throw new Error("Correspoding fieldName of column doesn't exist"); } //additional method public setGridSize(gridSize: { width: number, height: number }): void { //초기설정 this._bodyRows.domElement.style.height = gridSize.height + "px"; if (this._domElement) { this._domElement.style.overflow = "auto"; this._domElement.style.width = gridSize.width + "px"; } } //for event private _getCell(rowIdx: number, fieldName: string):Cell { return this._bodyRows.getCell(rowIdx, fieldName); } public findMousePos(X:number, Y:number) { // 나누기 let rect = this._table.getBoundingClientRect(); this._mousePos.X = X - rect.left; this._mousePos.Y = Y - rect.top; } private _getInRangeCell(preX: number, preY: number, X:number, Y:number):Array { let searchedCells = []; //find current position this.findMousePos(X, Y); let x = this._mousePos.X; let y = this._mousePos.Y; //set start point let startRowIdx = this._selectedCellInfo.rowIndex; let fieldName = this._selectedCellInfo.fieldName; let startColumnIdx = this.indexOfColumn(fieldName); //set end point this._searchCell(); let endIndex = this.indexOfColumn(this._selectedCellInfo.fieldName); let endRowIndex = this._selectedCellInfo.rowIndex; //set first cell let firstRowIdx = startRowIdx; let firstColumnIdx = startColumnIdx; //get endCell let endCell = this._getSearchedCell(); if (startRowIdx != NaN) { if (preX > x) { let tmp = startColumnIdx; startColumnIdx = endIndex; endIndex = tmp; } if (preY > y) { let tmp = startRowIdx; startRowIdx = endRowIndex; endRowIndex = tmp; } if (startColumnIdx != undefined && endIndex != undefined) { for (let i = startRowIdx; i <= endRowIndex; i++) { for (let j = startColumnIdx; endIndex && j <= endIndex; j++) { if (firstColumnIdx == j && firstRowIdx == i) { continue; } searchedCells.push(this._getCell(i, this._cols[j].fieldName)); } } } } else { searchedCells.push(endCell); } return searchedCells; } public _getSearchedCell():Cell{ return this._getCell(this._selectedCellInfo.rowIndex, this._selectedCellInfo.fieldName); } private _searchCell(): void { let x = this._mousePos.X; // parameter; let y = this._mousePos.Y; // mousePointer; let colLen = this._cols.length; let rowLen = this._bodyRows.rowCount; let targetFieldName; let targetRowIndex; let count = 0; for (let i = 0; i < colLen; i++) { let width = this._cols[i].width; if (count <= x && x < count + width + 1) { targetFieldName = this._cols[i].fieldName; break; } count += width + 1; } count = 21; if(0<=y && y< 21){ targetRowIndex = NaN; } for (let i = 0; i < rowLen; i++) { if (count <= y && y < count + 21) { targetRowIndex = i; break; } count += 21; } if (targetFieldName != undefined && targetRowIndex != undefined) { this._selectedCellInfo.fieldName = targetFieldName; this._selectedCellInfo.rowIndex = targetRowIndex; } } public toggleClicked(mousePosX:number, mousePosY:number, target:HTMLElement): void{ this.findMousePos(mousePosX, mousePosY); this._searchCell(); EventDriver.toggleSelect(target); } public rangeClicked(preX:number, preY:number, X:number, Y:number): void{ let selectedCells = this._getInRangeCell(preX, preY, X, Y); for (let cell of selectedCells) { if (cell.divDOMElement) EventDriver.toggleSelect(cell.divDOMElement); } } public singleClicked(X:number, Y:number, target:HTMLElement): void{ this.findMousePos(X, Y); this._searchCell(); //dependencies EventDriver.singleSelect(target, this); } public findEditBoxPosition(X:number, Y:number): void{ this.findMousePos(X, Y); this._searchCell() this._createEditBox(); } private _createEditBox(){ let cell = this._getSearchedCell(); if(!cell.isEditBoxCreated){ let editor = new EditBox(); editor.createEditText(cell, this); } } public keycodeSeparator(keycode: number){ if(keycode == KeyCollection.ENTER){ // this._createEditBox(); } else if(KeyCollection.ARROWLEFT <= keycode && keycode <= KeyCollection.ARROWDOWN){ this._movedByKeyBoard(keycode); } } private _movedByKeyBoard(keycode:number){ if(this._selectedCellInfo.fieldName && this._selectedCellInfo.rowIndex != NaN){ let done = false; if (keycode == KeyCollection.ARROWLEFT) { //keycode enum let nextIdx =this.indexOfColumn(this._selectedCellInfo.fieldName) - 1; if(nextIdx >= 0){ let nextField = this.getColumn(nextIdx).fieldName this._selectedCellInfo.fieldName = nextField; done = true; } } else if(keycode == KeyCollection.ARROWUP){ let nextIdx = this._selectedCellInfo.rowIndex -1; if(0<=nextIdx && nextIdx