UNPKG

tui-grid

Version:

TOAST UI Grid : Powerful data grid control supported by TOAST UI

556 lines (476 loc) 17.8 kB
/** * @fileoverview 컬럼 모델 * @author NHN. FE Development Lab <dl_javascript@nhn.com> */ 'use strict'; var $ = require('jquery'); var _ = require('underscore'); var snippet = require('tui-code-snippet'); var Model = require('../../base/model'); var frameConst = require('../../common/constMap').frame; var defaultRowHeaders = { rowNum: { type: 'rowNum', title: 'No.', name: '_number', align: 'center', fixedWidth: true, width: 50, hidden: false }, checkbox: { type: 'checkbox', title: '<input type="checkbox" />', name: '_button', align: 'center', fixedWidth: true, width: 50, hidden: false, editOptions: { type: 'mainButton' } }, radio: { type: 'radio', title: 'select', name: '_button', align: 'center', fixedWidth: true, width: 50, hidden: false, editOptions: { type: 'mainButton' } } }; /** * 컬럼 모델 데이터를 다루는 객체 * @module model/data/columnModel * @extends module:base/model * @ignore */ var ColumnModel = Model.extend(/** @lends module:model/data/columnModel.prototype */{ initialize: function() { Model.prototype.initialize.apply(this, arguments); this.textType = { normal: true, text: true, password: true }; this._setColumns(this.get('rowHeaders'), this.get('columns')); this.on('change', this._onChange, this); }, defaults: { keyColumnName: null, frozenCount: 0, rowHeaders: [], dataColumns: [], visibleColumns: [], // 이 리스트는 메타컬럼/데이터컬럼 구분하지 않고 저장 selectType: '', columnModelMap: {}, relationsMap: {}, complexHeaderColumns: [], copyOptions: { useFormattedValue: false }, treeColumnOptions: { name: null, useIcon: true, useCascadingCheckbox: true } }, /** * index 에 해당하는 columnModel 을 반환한다. * @param {Number} index 조회할 컬럼모델의 인덱스 값 * @param {Boolean} isVisible [isVisible=false] 화면에 노출되는 컬럼모델 기준으로 찾을것인지 여부. * @returns {object} 조회한 컬럼 모델 */ at: function(index, isVisible) { var columns = isVisible ? this.getVisibleColumns() : this.get('dataColumns'); return columns[index]; }, /** * columnName 에 해당하는 index를 반환한다. * @param {string} columnName 컬럼명 * @param {Boolean} isVisible [isVisible=false] 화면에 노출되는 컬럼모델 기준으로 반환할 것인지 여부. * @returns {number} index 컬럼명에 해당하는 인덱스 값 */ indexOfColumnName: function(columnName, isVisible) { var columns; if (isVisible) { columns = this.getVisibleColumns(); } else { columns = this.get('dataColumns'); } return _.findIndex(columns, {name: columnName}); }, /** * Returns state that the column is included in left side by column name * @param {String} columnName - Column name * @returns {Boolean} Whether the column is included in left side or not */ isLside: function(columnName) { var index = this.indexOfColumnName(columnName, true); var frozenCount = this.getVisibleFrozenCount(false); return (index > -1) && (index < frozenCount); }, /** * 화면에 노출되는 (!hidden) 컬럼 모델 리스트를 반환한다. * @param {String} [whichSide] 열고정 영역인지, 열고정이 아닌 영역인지 여부. 지정하지 않았을 경우 전체 visibleList를 반환한다. * @param {boolean} [withMeta=false] 메타컬럼 포함 여부. 지정하지 않으면 데이터컬럼리스트 기준으로 반환한다. * @returns {Array} 조회한 컬럼모델 배열 */ getVisibleColumns: function(whichSide, withMeta) { var startIndex = withMeta ? 0 : this.getVisibleMetaColumnCount(); var visibleColumnFixCount = this.getVisibleFrozenCount(withMeta); var columns; whichSide = whichSide && whichSide.toUpperCase(); if (whichSide === frameConst.L) { columns = this.get('visibleColumns').slice(startIndex, visibleColumnFixCount); } else if (whichSide === frameConst.R) { columns = this.get('visibleColumns').slice(visibleColumnFixCount); } else { columns = this.get('visibleColumns').slice(startIndex); } return columns; }, /** * 현재 보여지고 있는 메타컬럼의 카운트를 반환한다. * @returns {number} count */ getVisibleMetaColumnCount: function() { var models = this.get('rowHeaders'); var totalLength = models.length; var hiddenLength = _.where(models, {hidden: true}).length; return (totalLength - hiddenLength); }, /** * 현재 노출되는 컬럼들 중, 고정된 컬럼들(L-side)의 갯수를 반환한다. * @param {boolean} [withMeta=false] 현재 보여지고 있는 메타컬럼의 count를 합칠지 여부 * @returns {number} Visible frozen count */ getVisibleFrozenCount: function(withMeta) { var count = this.get('frozenCount'); var fixedColumns = _.first(this.get('dataColumns'), count); var visibleFixedColumns, visibleCount; visibleFixedColumns = _.filter(fixedColumns, function(column) { return !column.hidden; }); visibleCount = visibleFixedColumns.length; if (withMeta) { visibleCount += this.getVisibleMetaColumnCount(); } return visibleCount; }, /** * 인자로 받은 columnName 에 해당하는 columnModel 을 반환한다. * @param {String} columnName 컬럼명 * @returns {Object} 컬럼명에 해당하는 컬럼모델 */ getColumnModel: function(columnName) { return this.get('columnModelMap')[columnName]; }, /** * columnName 에 해당하는 컬럼의 타입이 textType 인지 확인한다. * 랜더링시 html 태그 문자열을 제거할때 사용됨. * @param {String} columnName 컬럼명 * @returns {boolean} text 타입인지 여부 */ isTextType: function(columnName) { return !!this.textType[this.getEditType(columnName)]; }, /** * test the column with given name is a tree column * @param {String} columnName column name to test * @returns {boolean} true if the column is tree column */ isTreeType: function(columnName) { return this.get('treeColumnOptions').name === columnName; }, /** * test if any one of columns has a tree column * @returns {boolean} true if it has a tree column */ hasTreeColumn: function() { return snippet.isString(this.get('treeColumnOptions').name); }, /** * gets treeColumnOptions.useIcon * @returns {boolean} whether use tree icon */ useTreeIcon: function() { var useIcon = this.get('treeColumnOptions').useIcon; return _.isUndefined(useIcon) ? true : useIcon; }, /** * Get cascade usage in the tree-grid's checkbox * @returns {boolean} Whether using the cascading checkbox or not */ useCascadingCheckbox: function() { var useCascadingCheckbox = this.get('treeColumnOptions').useCascadingCheckbox; return _.isUndefined(useCascadingCheckbox) ? true : useCascadingCheckbox; }, /** * 컬럼 모델로부터 editType 을 반환한다. * @param {string} columnName The name of the target column * @returns {string} 해당하는 columnName 의 editType 을 반환한다. */ getEditType: function(columnName) { var columnModel = this.getColumnModel(columnName); var editType = 'normal'; if (columnName === '_button' || columnName === '_number') { editType = columnName; } else if (columnModel && columnModel.editOptions && columnModel.editOptions.type) { editType = columnModel.editOptions.type; } return editType; }, /** * Whether copying the visible text or not * @param {string} columnName - Column name * @returns {boolena} State */ copyVisibleTextOfEditingColumn: function(columnName) { var columnModel = this.getColumnModel(columnName); if (snippet.pick(columnModel, 'editOptions', 'listItems')) { return !!snippet.pick(columnModel, 'copyOptions', 'useListItemText'); } return false; }, /** * 인자로 받은 컬럼 모델에서 !hidden을 만족하는 리스트를 추려서 반환한다. * @param {Array} rowHeaders 메타 컬럼 모델 리스트 * @param {Array} dataColumns 데이터 컬럼 모델 리스트 * @returns {Array} hidden 이 설정되지 않은 전체 컬럼 모델 리스트 * @private */ _makeVisibleColumns: function(rowHeaders, dataColumns) { rowHeaders = rowHeaders || this.get('rowHeaders'); dataColumns = dataColumns || this.get('dataColumns'); return _.filter(rowHeaders.concat(dataColumns), function(item) { return !item.hidden; }); }, /** * 각 columnModel 의 relations 를 모아 주체가 되는 columnName 기준으로 relationsMap 를 생성하여 반환한다. * @param {Array} columns - Column Model List * @returns {{}|{columnName1: Array, columnName1: Array}} columnName 기준으로 생성된 relationsMap * @private */ _getRelationListMap: function(columns) { var relationsMap = {}; _.each(columns, function(columnModel) { var columnName = columnModel.name; if (columnModel.relations) { relationsMap[columnName] = columnModel.relations; } }); return relationsMap; }, /** * ignored 가 true 로 설정된 columnName 의 list 를 반환한다. * @returns {Array} ignored 가 true 로 설정된 columnName 배열. */ getIgnoredColumnNames: function() { var dataColumns = this.get('dataColumns'); var ignoredColumnNames = []; _.each(dataColumns, function(columnModel) { if (columnModel.ignored) { ignoredColumnNames.push(columnModel.name); } }); return ignoredColumnNames; }, /** * Set column title by columns map * @param {Array} targetColumns - columns to change * @param {object} columnsMap - name and title to change * @private */ _changeColumnTitlesByName: function(targetColumns, columnsMap) { _.each(columnsMap, function(value, key) { var idx = _.findIndex(targetColumns, function(data) { return data.name === key; }); if (idx !== -1) { targetColumns[idx].title = value; } }); }, /** * Set column title by columns map * @param {object} columnsMap - name and title to change * @private */ setColumnTitles: function(columnsMap) { var dataColumns = this.get('dataColumns'); var complexHeaderColumns = this.get('complexHeaderColumns'); this._changeColumnTitlesByName(dataColumns, columnsMap); if (complexHeaderColumns.length) { this._changeColumnTitlesByName(complexHeaderColumns, columnsMap); } this.trigger('columnModelChange'); }, /** * Set column model by data * @param {array} rowHeaders - Data of row headers * @param {array} columns - Data of columns * @param {number} [frozenCount] Count of frozen column * @private */ _setColumns: function(rowHeaders, columns, frozenCount) { var relationsMap, visibleColumns, dataColumns; if (snippet.isUndefined(frozenCount)) { frozenCount = this.get('frozenCount'); } rowHeaders = this._getRowHeadersData(rowHeaders); dataColumns = $.extend(true, [], columns); relationsMap = this._getRelationListMap(dataColumns); visibleColumns = this._makeVisibleColumns(rowHeaders, dataColumns); this.set({ selectType: this._getSelectType(rowHeaders), rowHeaders: rowHeaders, dataColumns: dataColumns, columnModelMap: _.indexBy(rowHeaders.concat(dataColumns), 'name'), relationsMap: relationsMap, frozenCount: Math.max(0, frozenCount), visibleColumns: visibleColumns }, { silent: true }); this.unset('columns', { silent: true }); this.trigger('columnModelChange'); }, /** * Get data of row headers * @param {object} options - Options to set each row header * @returns {array} Row headers data * @private */ _getRowHeadersData: function(options) { var rowHeadersData = []; var type, isObject; var defaultData; var hasTitle; _.each(options, function(data) { isObject = _.isObject(data); type = isObject ? data.type : data; defaultData = defaultRowHeaders[type]; if (!isObject) { data = defaultData; } else { hasTitle = data.title; data = $.extend({}, defaultData, data); } // Customizing the cell data in the row header if (data.template && !hasTitle && type !== 'rowNum') { data.title = data.template({ className: '', name: '', disabled: '', checked: '' }); } // "checkbox" and "radio" should not exist in duplicate if (_.findIndex(rowHeadersData, {name: data.name}) === -1) { rowHeadersData.push(data); } }, this); return rowHeadersData; }, /** * Get select type in row headers * @param {array} rowHeaders - Row headers data * @returns {string} Select type * @private */ _getSelectType: function(rowHeaders) { var rowHeader = _.findWhere(rowHeaders, {name: '_button'}); return rowHeader ? rowHeader.type : ''; }, /** * change 이벤트 발생시 핸들러 * @param {Object} model change 이벤트가 발생한 model 객체 * @private */ _onChange: function(model) { var changed = model.changed; var frozenCount = changed.frozenCount; var columns = changed.columns || this.get('dataColumns'); var rowHeaders = changed.rowHeaders || this.get('rowHeaders'); this._setColumns(rowHeaders, columns, frozenCount); }, /** * Set 'hidden' property of column model to true or false * @param {Array} columnNames - Column names to set 'hidden' property * @param {boolean} hidden - Hidden flag for setting */ setHidden: function(columnNames, hidden) { var name, names, columnModel, visibleColumns; while (columnNames.length) { name = columnNames.shift(); columnModel = this.getColumnModel(name); if (columnModel) { columnModel.hidden = hidden; } else { names = this.getUnitColumnNamesIfMerged(name); columnNames.push.apply(columnNames, names); } } visibleColumns = this._makeVisibleColumns( this.get('rowHeaders'), this.get('dataColumns') ); this.set('visibleColumns', visibleColumns, { silent: true }); this.trigger('columnModelChange'); }, /** * Get unit column names * @param {string} columnName - columnName * @returns {Array.<string>} Unit column names */ getUnitColumnNamesIfMerged: function(columnName) { var complexHeaderColumns = this.get('complexHeaderColumns'); var stackForSearch = []; var searchedNames = []; var name, columnModel, complexHeaderColumn; stackForSearch.push(columnName); while (stackForSearch.length) { name = stackForSearch.shift(); columnModel = this.getColumnModel(name); if (columnModel) { searchedNames.push(name); } else { complexHeaderColumn = _.findWhere(complexHeaderColumns, { name: name }); if (complexHeaderColumn) { stackForSearch.push.apply(stackForSearch, complexHeaderColumn.childNames); } } } return _.uniq(searchedNames); }, /** * Returns the copy option of given column. * @param {string} columnName - column name * @returns {{useFormattedValue: boolean}} */ getCopyOptions: function(columnName) { var columnModel = this.getColumnModel(columnName); return _.extend({}, this.get('copyOptions'), columnModel.copyOptions); }, /** * Get name of tree column * @returns {string} column name */ getTreeColumnName: function() { return this.get('treeColumnOptions').name; } }); ColumnModel._defaultRowHeaders = defaultRowHeaders; module.exports = ColumnModel;