/**
* Data grid is a simple widget designed to list the filtered records, providing
* a simple way to define how the items are displayed.
*
* Note: Unlike other charts, the data grid chart (and data table) use the group attribute as a keying function
* for {@link https://github.com/mbostock/d3/wiki/Arrays#-nest nesting} the data together in groups.
* Do not pass in a crossfilter group as this will not work.
*
* Examples:
* - {@link http://europarl.me/dc.js/web/ep/index.html List of members of the european parliament}
* @class dataGrid
* @memberof dc
* @mixes dc.baseMixin
* @param {String|node|d3.selection} parent - Any valid
* {@link https://github.com/mbostock/d3/wiki/Selections#selecting-elements d3 single selector} specifying
* a dom block element such as a div; or a dom element or d3 selection.
* @param {String} [chartGroup] - The name of the chart group this chart instance should be placed in.
* Interaction with a chart will only trigger events and redraws within the chart's group.
* @return {dc.dataGrid}
*/
dc.dataGrid = function (parent, chartGroup) {
var LABEL_CSS_CLASS = 'dc-grid-label';
var ITEM_CSS_CLASS = 'dc-grid-item';
var GROUP_CSS_CLASS = 'dc-grid-group';
var GRID_CSS_CLASS = 'dc-grid-top';
var _chart = dc.baseMixin({});
var _size = 999; // shouldn't be needed, but you might
var _html = function (d) { return 'you need to provide an html() handling param: ' + JSON.stringify(d); };
var _sortBy = function (d) {
return d;
};
var _order = d3.ascending;
var _beginSlice = 0, _endSlice;
var _htmlGroup = function (d) {
return '
' +
_chart.keyAccessor()(d) + '
';
};
_chart._doRender = function () {
_chart.selectAll('div.' + GRID_CSS_CLASS).remove();
renderItems(renderGroups());
return _chart;
};
function renderGroups () {
var groups = _chart.root().selectAll('div.' + GRID_CSS_CLASS)
.data(nestEntries(), function (d) {
return _chart.keyAccessor()(d);
});
var itemGroup = groups
.enter()
.append('div')
.attr('class', GRID_CSS_CLASS);
if (_htmlGroup) {
itemGroup
.html(function (d) {
return _htmlGroup(d);
});
}
groups.exit().remove();
return itemGroup;
}
function nestEntries () {
var entries = _chart.dimension().top(_size);
return d3.nest()
.key(_chart.group())
.sortKeys(_order)
.entries(entries.sort(function (a, b) {
return _order(_sortBy(a), _sortBy(b));
}).slice(_beginSlice, _endSlice));
}
function renderItems (groups) {
var items = groups.order()
.selectAll('div.' + ITEM_CSS_CLASS)
.data(function (d) {
return d.values;
});
items.enter()
.append('div')
.attr('class', ITEM_CSS_CLASS)
.html(function (d) {
return _html(d);
});
items.exit().remove();
return items;
}
_chart._doRedraw = function () {
return _chart._doRender();
};
/**
* Get or set the index of the beginning slice which determines which entries get displayed by the widget.
* Useful when implementing pagination.
* @method beginSlice
* @memberof dc.dataGrid
* @instance
* @param {Number} [beginSlice=0]
* @return {Number}
* @return {dc.dataGrid}
*/
_chart.beginSlice = function (beginSlice) {
if (!arguments.length) {
return _beginSlice;
}
_beginSlice = beginSlice;
return _chart;
};
/**
* Get or set the index of the end slice which determines which entries get displayed by the widget
* Useful when implementing pagination.
* @method endSlice
* @memberof dc.dataGrid
* @instance
* @param {Number} [endSlice]
* @return {Number}
* @return {dc.dataGrid}
*/
_chart.endSlice = function (endSlice) {
if (!arguments.length) {
return _endSlice;
}
_endSlice = endSlice;
return _chart;
};
/**
* Get or set the grid size which determines the number of items displayed by the widget.
* @method size
* @memberof dc.dataGrid
* @instance
* @param {Number} [size=999]
* @return {Number}
* @return {dc.dataGrid}
*/
_chart.size = function (size) {
if (!arguments.length) {
return _size;
}
_size = size;
return _chart;
};
/**
* Get or set the function that formats an item. The data grid widget uses a
* function to generate dynamic html. Use your favourite templating engine or
* generate the string directly.
* @method html
* @memberof dc.dataGrid
* @instance
* @example
* chart.html(function (d) { return ''+data.exampleString+'
';});
* @param {Function} [html]
* @return {Function}
* @return {dc.dataGrid}
*/
_chart.html = function (html) {
if (!arguments.length) {
return _html;
}
_html = html;
return _chart;
};
/**
* Get or set the function that formats a group label.
* @method htmlGroup
* @memberof dc.dataGrid
* @instance
* @example
* chart.htmlGroup (function (d) { return ''.d.key . 'with ' . d.values.length .' items
'});
* @param {Function} [htmlGroup]
* @return {Function}
* @return {dc.dataGrid}
*/
_chart.htmlGroup = function (htmlGroup) {
if (!arguments.length) {
return _htmlGroup;
}
_htmlGroup = htmlGroup;
return _chart;
};
/**
* Get or set sort-by function. This function works as a value accessor at the item
* level and returns a particular field to be sorted.
* @method sortBy
* @memberof dc.dataGrid
* @instance
* @example
* chart.sortBy(function(d) {
* return d.date;
* });
* @param {Function} [sortByFunction]
* @return {Function}
* @return {dc.dataGrid}
*/
_chart.sortBy = function (sortByFunction) {
if (!arguments.length) {
return _sortBy;
}
_sortBy = sortByFunction;
return _chart;
};
/**
* Get or set sort order function.
* @method order
* @memberof dc.dataGrid
* @instance
* @see {@link https://github.com/mbostock/d3/wiki/Arrays#d3_ascending d3.ascending}
* @see {@link https://github.com/mbostock/d3/wiki/Arrays#d3_descending d3.descending}
* @example
* chart.order(d3.descending);
* @param {Function} [order=d3.ascending]
* @return {Function}
* @return {dc.dataGrid}
*/
_chart.order = function (order) {
if (!arguments.length) {
return _order;
}
_order = order;
return _chart;
};
return _chart.anchor(parent, chartGroup);
};