Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add defaultContent to summary options #252

Merged
merged 5 commits into from
Jan 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions src/js/grid.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,22 @@ var instanceMap = {};
* @param {Object} [options.summary] - The object for configuring summary area.
* @param {number} [options.summary.height] - The height of the summary area.
* @param {string} [options.summary.position='bottom'] - The position of the summary area. ('bottom', 'top')
* @param {Object.<string, Object>} [options.summary.columnContent]
* The object for configuring each column in the summary.
* @param {(string|Object)} [options.summary.defaultContent]
* The configuring of summary cell for every column.
* This options can be overriden for each column by columnContent options.
* If type is string, the value is used as HTML of summary cell for every columns
* without auto-calculation.
* @param {boolean} [options.summary.defaultContent.useAutoSummary=true]
* If set to true, the summary value of every column is served as a paramater to the template
* function whenever data is changed.
* @param {function} [options.summary.defaultContent.template] - Template function which returns the
* content(HTML) of the column of the summary. This function takes an K-V object as a parameter
* which contains a summary values keyed by 'sum', 'avg', 'min', 'max' and 'cnt'.
* @param {Object} [options.summary.columnContent]
* The configuring of summary cell for each column.
* Sub options below are keyed by each column name.
* If type of value of this object is string, the value is used as HTML of summary cell for
* the column without auto-calculation.
* @param {boolean} [options.summary.columnContent.useAutoSummary=true]
* If set to true, the summary value of each column is served as a paramater to the template
* function whenever data is changed.
Expand Down Expand Up @@ -909,11 +922,12 @@ var Grid = View.extend(/** @lends Grid.prototype */{

/**
* Sets the HTML string of given column summary.
* @param {string} columnName - column name
* @param {string} contents - HTML string
* The type of content is the same as the options.summary.columnContent of the constructor.
* @param {string} columnName - columnName
* @param {string|object} content - HTML string or options object.
*/
setSummaryColumnContent: function(columnName, contents) {
this.modelManager.columnModel.setSummaryContent(columnName, contents);
setSummaryColumnContent: function(columnName, content) {
this.modelManager.summaryModel.setColumnContent(columnName, content, true);
},

/**
Expand Down
10 changes: 0 additions & 10 deletions src/js/model/data/columnModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -506,16 +506,6 @@ var ColumnModel = Model.extend(/** @lends module:model/data/columnModel.prototyp
return _.extend({}, this.get('copyOptions'), columnModel.copyOptions);
},

/**
* Set summary contents.
* (Just trigger 'setSummaryContent')
* @param {string} columnName - columnName
* @param {string} contents - HTML string
*/
setSummaryContent: function(columnName, contents) {
this.trigger('setSummaryContent', columnName, contents);
},

/**
* Get name of tree column
* @returns {string} column name
Expand Down
14 changes: 4 additions & 10 deletions src/js/model/manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -305,21 +305,15 @@ var ModelManager = snippet.defineClass(/** @lends module:modelManager.prototype
* @private
*/
_createSummaryModel: function(summaryOptions) {
var autoColumnNames = [];

if (!summaryOptions || !summaryOptions.columnContent) {
if (!summaryOptions) {
return null;
}

_.each(summaryOptions.columnContent, function(options, columnName) {
if (_.isFunction(options.template) && options.useAutoSummary !== false) {
autoColumnNames.push(columnName);
}
});

return new SummaryModel(null, {
dataModel: this.dataModel,
autoColumnNames: autoColumnNames
columnModel: this.columnModel,
columnContent: summaryOptions.columnContent,
defaultContent: summaryOptions.defaultContent
});
},

Expand Down
178 changes: 151 additions & 27 deletions src/js/model/summary.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,32 @@ var typeConst = require('../common/constMap').summaryType;
var Summary = Model.extend(/** @lends module:model/summary.prototype */{
initialize: function(attr, options) {
this.dataModel = options.dataModel;
this.columnModel = options.columnModel;

/**
* An array of columnNames using auto calculation
* @type {Array.<string>}
* Set for storing names of auto-calculate column
* The value is always 'true'
* @type {Object}
* @example
* {
* c1: true
* c2: true
* }
*/
this.autoColumnNameSet = {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

결국 value는 true인 것들만 있는 것인데, 배열로 가지고 있으면 안되나요??
auto로 할 column들의 의미로.. [c1, c2] 이렇게 가지고 있도록이요.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

음.. 연산이 복잡해져서 맵으로 관리하는거 아닐까요?
배열로 관리하면 값을 제거할 때 filterslice를 써야할테니까요 ;ㅅ;

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네. 원래 배열이었는데, 순회할 일이 없고, 순서도 관계 없기 때문에 객체 형태로 사용하도록 변경했습니다.
배열은 아무래도 추가/삭제/중복확인 등을 할 때 불편해서요. (성능도 아주 미묘하게 느리구요)
ES6 환경이면 Set을 쓸텐데, Set이 없어서.. 그냥 객체 형태로 처리했습니다.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아하! 거기까진 생각 못했네요! 👍


/**
* Store template functions of each column
* K: column name
* V: template function
* @example
* {
* c1: function() {},
* c2: function() {}
* }
* @type {Object}
*/
this.autoColumnNames = options.autoColumnNames;
this.columnTemplateMap = {};

/**
* Summary value map (KV)
Expand All @@ -47,11 +67,50 @@ var Summary = Model.extend(/** @lends module:model/summary.prototype */{
*/
this.columnSummaryMap = {};

this.listenTo(this.dataModel, 'add remove reset', this._resetSummaryMap);
this.listenTo(this.dataModel, 'change', this._onChangeData);
// store defaultContent option for future reset
this.defaultContent = options.defaultContent;

// store columnContent option for future reset
this.columnContent = options.columnContent;

this.listenTo(this.dataModel, 'add remove reset', this._onChangeDataRows);
this.listenTo(this.dataModel, 'change', this._onChangeDataCells);
this.listenTo(this.dataModel, 'deleteRange', this._onDeleteRangeData);
this.listenTo(this.columnModel, 'columnModelChange', this._resetAll);

this._resetSummaryMap();
this._resetAll();
},

/**
* Reset autoColumnNames and columnTemplateMap based on columnContent options.
* @param {Object} columnContent - summary.columnContent options
* @private
*/
_resetColumnContent: function() {
var columnContentMap = {};
var defaultContent = this.defaultContent;
var columnContent = this.columnContent || {};

if (defaultContent) {
_.forEach(this.columnModel.getVisibleColumns(), function(column) {
columnContentMap[column.name] = columnContent[column.name] || defaultContent;
});
} else {
columnContentMap = columnContent;
}

_.each(columnContentMap, function(options, columnName) {
this.setColumnContent(columnName, options);
}, this);
},

/**
* Reset autoColumnNameSet, columnTemplateMap, columnSummaryMap
* @private
*/
_resetAll: function() {
this._resetColumnContent();
this._resetColumnSummaryMap();
},

/**
Expand All @@ -64,8 +123,9 @@ var Summary = Model.extend(/** @lends module:model/summary.prototype */{
_calculate: function(values) {
var min = Number.MAX_VALUE;
var max = Number.MIN_VALUE;
var sum = 0;
var count = values.length;
var sum = 0;
var avg = 0;
var resultMap = {};
var i, value;

Expand All @@ -84,50 +144,66 @@ var Summary = Model.extend(/** @lends module:model/summary.prototype */{
}
}

if (!count) {
max = min = avg = 0;
} else {
avg = sum / count;
}

resultMap[typeConst.SUM] = sum;
resultMap[typeConst.MIN] = min;
resultMap[typeConst.MAX] = max;
resultMap[typeConst.AVG] = count ? (sum / count) : 0;
resultMap[typeConst.AVG] = avg;
resultMap[typeConst.CNT] = count;

return resultMap;
},

/**
* Initialize summary map of columns specified in 'columnSummries' property.
* @private
*/
_resetSummaryMap: function() {
this._resetSummarySummaryValue();
},

/**
* Reset summary values of given columnName
* @param {Array.<string>} columnNames - An array of column names
* @private
*/
_resetSummarySummaryValue: function(columnNames) {
var targetColumnNames = this.autoColumnNames;
_resetColumnSummaryMap: function(columnNames) {
var targetColumnNames = _.keys(this.autoColumnNameSet);

if (columnNames) {
targetColumnNames = _.intersection(columnNames, this.autoColumnNames);
targetColumnNames = _.intersection(columnNames, targetColumnNames);
}
_.each(targetColumnNames, function(columnName) {
var values = this.dataModel.getColumnValues(columnName);
var valueMap = this._calculate(values);

this.columnSummaryMap[columnName] = valueMap;
this.trigger('change', columnName, valueMap);
_.each(targetColumnNames, function(columnName) {
this._changeColumnSummaryValue(columnName);
}, this);
},

/**
* Change Summary Value
* @param {string} columnName - column name
* @private
*/
_changeColumnSummaryValue: function(columnName) {
var values = this.dataModel.getColumnValues(columnName);
var valueMap = this._calculate(values);

this.columnSummaryMap[columnName] = valueMap;
this.trigger('change', columnName, valueMap);
},

/**
* Event handler for 'add', 'append', 'remove' event on dataModel
* @private
*/
_onChangeDataRows: function() {
this._resetColumnSummaryMap();
},

/**
* Event handler for 'change' event on dataModel
* @param {object} model - row model
* @private
*/
_onChangeData: function(model) {
this._resetSummarySummaryValue(_.keys(model.changed));
_onChangeDataCells: function(model) {
this._resetColumnSummaryMap(_.keys(model.changed));
},

/**
Expand All @@ -136,7 +212,7 @@ var Summary = Model.extend(/** @lends module:model/summary.prototype */{
* @private
*/
_onDeleteRangeData: function(ev) {
this._resetSummarySummaryValue(ev.columnNames);
this._resetColumnSummaryMap(ev.columnNames);
},

/**
Expand All @@ -157,6 +233,54 @@ var Summary = Model.extend(/** @lends module:model/summary.prototype */{
value = snippet.pick(valueMap, summaryType);

return _.isUndefined(value) ? null : value;
},

/**
* Returns whether given column is visible.
* @param {string} columnName - Parameter description.
* @returns {boolean}
* @private
*/
_isVisibleColumn: function(columnName) {
return this.columnModel.getVisibleColumns().indexOf(columnName) !== -1;
},

/**
* Return template function of given column name
* @param {string} columnName - column name
* @returns {function}
*/
getTemplate: function(columnName) {
var template = this.columnTemplateMap[columnName];

if (!template && this.defaultContent && this._isVisibleColumn(columnName)) {
template = this.defaultContent.template;
}

return template;
},

/**
* Set summary contents.
* (Just trigger 'setSummaryContent')
* @param {string} columnName - columnName
* @param {string|object} content - HTML string or Options Object
* @param {boolean} shouldChangeValue - If set to true, summary value is re-calculated
*/
setColumnContent: function(columnName, content, shouldChangeValue) { // eslint-disable-line complexity
if (_.isObject(content) && _.isFunction(content.template)) {
this.columnTemplateMap[columnName] = content.template;
if (content.useAutoSummary !== false) {
this.autoColumnNameSet[columnName] = true;
}
} else if (_.isString(content)) {
delete this.autoColumnNameSet[columnName];
this.columnTemplateMap[columnName] = content;
}

if (shouldChangeValue) {
this._changeColumnSummaryValue(columnName);
}
}
});

Expand Down
12 changes: 1 addition & 11 deletions src/js/view/factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

'use strict';

var _ = require('underscore');
var snippet = require('tui-code-snippet');
var DatePicker = require('tui-date-picker');

Expand Down Expand Up @@ -172,26 +171,17 @@ var ViewFactory = snippet.defineClass({
* @returns {object}
*/
createSummary: function(whichSide) {
var templateMap = {};

if (!this.summaryOptions) {
return null;
}

_.each(this.summaryOptions.columnContent, function(options, columnName) {
if (_.isFunction(options.template)) {
templateMap[columnName] = options.template;
}
});

return new SummaryView({
whichSide: whichSide,
columnModel: this.modelManager.columnModel,
renderModel: this.modelManager.renderModel,
dimensionModel: this.modelManager.dimensionModel,
coordColumnModel: this.modelManager.coordColumnModel,
summaryModel: this.modelManager.summaryModel,
columnTemplateMap: templateMap
summaryModel: this.modelManager.summaryModel
});
},

Expand Down
Loading