@bigfishtv/cockpit
Version:
468 lines (418 loc) • 16.7 kB
JavaScript
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
/**
* Table Utilities
* @module Utilities/tableUtils
*/
import _get from 'lodash/get';
import moment from 'moment';
import * as SortTypes from '../constants/SortTypes';
import * as Conditions from '../constants/Conditions';
import { isNumeric, isString, isFunction } from './typeUtils';
import { titleCase } from './stringUtils';
import { getSchemaWithAssociations } from './formUtils';
import FixedDataTableCheckboxCell from '../components/table/cell/FixedDataTableCheckboxCell';
import FixedDataTableDateCell from '../components/table/cell/FixedDataTableDateCell';
import FixedDataTableHtmlCell from '../components/table/cell/FixedDataTableHtmlCell';
import FixedDataTableAssetCell from '../components/table/cell/FixedDataTableAssetCell';
import FixedDataTableDecimalCell from '../components/table/cell/FixedDataTableDecimalCell';
/**
* Returns string swapping between ASC and DESC
* @param {String} sortDir
* @return {String}
*/
export function reverseSortDirection(sortDir) {
return sortDir === SortTypes.DESC ? SortTypes.ASC : SortTypes.DESC;
}
/**
* Returns a sort function that takes into account sort direction and type
* @param {String} columnKey - key that will be in both objects to compare
* @param {String} sortDirection - direction of sort either ASC or DESC
* @param {String} [sortType=string] - sortType: string, numeric, boolean
* @return {Function} - returns sort function
*/
export function sortByObjectKey(columnKey) {
var sortDirection = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : SortTypes.ASC;
var sortType = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'string';
return function (a, b) {
var valA = _get(a, columnKey);
var valB = _get(b, columnKey);
var sortVal = 0;
if (valA === null || valA === undefined) valA = sortType == 'string' ? '' : 0;
if (valB === null || valB === undefined) valB = sortType == 'string' ? '' : 0;
if (sortType == 'string') {
valA = valA && typeof valA.toLowerCase == 'function' ? valA.toLowerCase() : valA;
valB = valB && typeof valB.toLowerCase == 'function' ? valB.toLowerCase() : valB;
sortVal = valA > valB ? 1 : valA < valB ? -1 : 0;
} else if (sortType == 'numeric' || sortType == 'boolean') {
sortVal = valB - valA > 0 ? -1 : valB - valA < 0 ? 1 : 0;
}
if (sortVal !== 0 && sortDirection === SortTypes.DESC) sortVal = sortVal * -1;
return sortVal;
};
}
/**
* Takes schema column, looks at type and resolves to javascript sort type
* @param {Object} column
* @param {String} column.type - e.g. integer, float, decimal, boolean
* @return {string}
*/
export function getSortTypeFromColumn(column) {
switch (column.type) {
case 'integer':
return 'numeric';
break;
case 'float':
return 'numeric';
break;
case 'decimal':
return 'numeric';
break;
case 'boolean':
return 'boolean';
break;
}
return 'string';
}
/**
* Takes schema column, looks at type and resolves to corresponding table cell component
* @param {Object} column
* @param {String} column.type - e.g. datetime, timestamp, boolean, text
* @return {React.Component} - Returns react component, else null
*/
export function getCellComponentFromColumn(column) {
switch (column.type) {
case 'datetime':
return FixedDataTableDateCell;
break;
case 'timestamp':
return FixedDataTableDateCell;
break;
case 'boolean':
return FixedDataTableCheckboxCell;
break;
case 'text':
return FixedDataTableHtmlCell;
break;
case 'decimal':
case 'float':
return FixedDataTableDecimalCell;
break;
}
switch (column.className) {
case 'Tank.Assets':
return FixedDataTableAssetCell;
break;
}
return null;
}
/**
* Generates default field attributes from a schema column
* @param {Object} column
* @param {String} column.type
* @param {String} column.property
* @return {Object}
*/
export function getFieldAttributesFromColumn(column) {
var attributes = {};
switch (column.property) {
case 'id':
attributes = _extends({}, attributes, { width: 40, fixed: true });
break;
}
switch (column.type) {
case 'json':
attributes = _extends({}, attributes, { sortable: false });
break;
}
switch (column.className) {
case 'Tank.Assets':
attributes = _extends({}, attributes, { width: 80 });
break;
}
return attributes;
}
/**
* Generates default table fields from schema
* @param {Array} schema
* @param {Function} componentResolver - precursor function to resolve component based on scema column
* @param {Function} attributeModifier - precursor function to resolve additional field attributes based on scema column
* @return {Array} - returns array of table fields
*/
export function getFieldsFromSchema() {
var schema = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
var componentResolver = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {
return null;
};
var attributeModifier = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : function () {
return {};
};
return schema.map(function (column) {
var sortType = getSortTypeFromColumn(column);
var Cell = componentResolver(column) || getCellComponentFromColumn(column);
var additionalAttributes = _extends({}, attributeModifier(column), getFieldAttributesFromColumn(column));
return _extends({
key: column.property,
value: column.title || titleCase(column.property),
resizable: true,
sortable: true,
schema: column,
sortType: sortType,
Cell: Cell
}, additionalAttributes);
});
}
/**
* Takes schema and assocations and adds 'belongsToMany' assocations to schema before returning result from 'getFieldsFromSchema'
* @param {Array} schema
* @param {Object[]} assocations
* @param {String} assocations[].type - e.g. belongsTo, belongsToMany
* @param {Function} componentResolver - precursor function to resolve component based on scema column
* @param {Function} attributeModifier - precursor function to resolve additional field attributes based on scema column
* @return {Array} - returns array of table fields
*/
export function getFieldsFromSchemaAndAssociations() {
var schema = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
var associations = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
var componentResolver = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : function () {
return null;
};
var attributeModifier = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : function () {
return {};
};
var newSchema = getSchemaWithAssociations(schema, associations);
return normalizeFields(getFieldsFromSchema(newSchema, componentResolver, attributeModifier));
}
/**
* Takes a table fields and fixes any undefined variables in each one
* @param {Object[]} fields
* @param {String} fields[].sortType
* @param {Boolean} fields[].resizable
* @param {Boolean} fields[].sortable
* @return {Array}
*/
export function normalizeFields(fields) {
var order = 0;
return fields.map(function (field) {
if (typeof field.sortType == 'undefined') field.sortType = 'string';
if (typeof field.resizable == 'undefined') field.resizable = true;
if (typeof field.sortable == 'undefined') field.sortable = true;
if (typeof field.order == 'undefined') field.order = ++order;
return field;
}).sort(function (a, b) {
return a.order < b.order ? -1 : a.order > b.order ? 1 : 0;
});
}
/**
* Filters a data set by a query string, takes schemaTypes object to smartly skip columns of certain type
* @param {Array} data
* @param {String} queryString
* @param {Object} schemaTypes - Keyed object containing schema column types, e.g. {id: 'numeric', title: 'string'}
* @return {Array} - returns filtered data array
*/
export function filterDataByQuery() {
var data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
var queryString = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
var schemaTypes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
// first check if anything is being searched
if (!queryString || queryString === '') return data;
// filter all rows
return data.filter(function (row) {
var rowPassed = false;
// map over field names for every row
Object.keys(schemaTypes).map(function (columnName) {
var rowValue = _get(row, columnName);
// if a number has been searched for then allow for searching in numeric fields
if (isNumeric(queryString) && schemaTypes[columnName] == 'numeric' && isNumeric(rowValue) && rowValue.toString().indexOf(queryString.toString()) >= 0) {
rowPassed = true;
// otherwise search for query string in all string-type columns
} else if (schemaTypes[columnName] == 'string' && isString(rowValue) && rowValue.toLowerCase().indexOf(queryString.toLowerCase()) >= 0) {
rowPassed = true;
}
});
return rowPassed;
});
}
/**
* Takes data array, schema array and query string and smarly filters data using schema
* @param {Array} data
* @param {Array} schema
* @param {String} queryString
* @return {Array} - returns filtered data
*/
export function filterDataByQueryWithSchema() {
var data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
var schema = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
var queryString = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
// create an associative object for column name => content type
var schemaTypes = {};
for (var _iterator = schema, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
var _ref;
if (_isArray) {
if (_i >= _iterator.length) break;
_ref = _iterator[_i++];
} else {
_i = _iterator.next();
if (_i.done) break;
_ref = _i.value;
}
var column = _ref;
schemaTypes[column.property] = getSortTypeFromColumn(column);
}
return filterDataByQuery(data, queryString, schemaTypes);
}
/**
* Takes data array, table fields and query string and smarly filters data using table fields
* @param {Array} data
* @param {Array} fields
* @param {String} queryString
* @return {Array} - returns filtered data
*/
export function filterDataByQueryWithFields() {
var data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
var fields = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
var queryString = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
// create an associative object for column name => content type
var schemaTypes = {};
for (var _iterator2 = fields, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) {
var _ref2;
if (_isArray2) {
if (_i2 >= _iterator2.length) break;
_ref2 = _iterator2[_i2++];
} else {
_i2 = _iterator2.next();
if (_i2.done) break;
_ref2 = _i2.value;
}
var column = _ref2;
schemaTypes[column.key] = column.sortType;
}
return filterDataByQuery(data, queryString, schemaTypes);
}
/**
* Takes data array and a keyed object of filters that correspond to keys in data array objects
* @param {Array} data
* @param {Object} filterset
* @return {Array} - returns filtered data
*/
export function filterDataByFilterset() {
var data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
var filterset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var condition = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : Conditions.OR;
var properties = Object.keys(filterset);
if (!properties.length) return data;
return data.filter(function (row) {
var passes = 0;
for (var _iterator3 = properties, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) {
var _ref3;
if (_isArray3) {
if (_i3 >= _iterator3.length) break;
_ref3 = _iterator3[_i3++];
} else {
_i3 = _iterator3.next();
if (_i3.done) break;
_ref3 = _i3.value;
}
var property = _ref3;
if (filterset[property] === null) {
passes++;
} else {
var rowValue = _get(row, property, undefined);
if (rowValue !== undefined) {
if (isFunction(filterset[property]) && filterset[property](rowValue, row)) passes++;else if (rowValue === filterset[property]) passes++;
}
}
}
if (condition == Conditions.OR && passes > 0 || condition == Conditions.AND && passes === properties.length) return true;
return false;
});
}
/**
* Filters out data not within the date range.
*
* @param {Array} data
* @param {String} property
* @param {String|Date} fromDate
* @param {String|Date} toDate
* @return {Array} filtered data
*/
export function filterDataByDateRange(data, property, fromDate, toDate) {
if (fromDate) {
fromDate = moment(fromDate);
data = data.filter(function (row) {
var value = _get(row, property, undefined);
return value && moment(value) >= fromDate;
});
}
if (toDate) {
toDate = moment(toDate);
data = data.filter(function (row) {
var value = _get(row, property, undefined);
return value && moment(value) <= toDate;
});
}
return data;
}
/**
* Used for creating compact filterset rules
* Returns a function that takes override values and calls createFilterset the default values, override values, and callback function
* @param {Object} defaultValues Default filterset values
* @param {Function} callback Typically 'handleFilterChange' passed in from AutoTableIndex
* @return {Function} Returns function that takes override values
*/
export function createFiltersetGenerator() {
var defaultValues = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var callback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {};
return function () {
var values = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
return createFilterset(defaultValues, values, callback);
};
}
/**
* Takes default values, override values and a callback function
* Returns an empty function that calls the callback function with the combined values
* @param {Object} defaultValues
* @param {Object} values
* @param {Function} callback Typically 'handleFilterChange' passed in from AutoTableIndex
* @return {Function} Returns function that can be directly called in on onClick prop
*/
export function createFilterset() {
var defaultValues = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var values = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var callback = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : function () {};
return function () {
return callback(_extends({}, defaultValues, values));
};
}
/**
* Used for getting a currently selected filterset label based off rules provided
* Note that the order in which the rules are provided is important as the loop short circuits as soon as a checker returns true
* @param {Object} filterset Current filterset, typically provided by AutoTableIndex
* @param {Object} rules Rules object where key is label and value is a checker function that should return a boolean
* @param {String} defaultLabel Default label to return if no rules are matched
* @return {String}
*/
export function getFiltersetLabel() {
var filterset = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var rules = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var defaultLabel = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'All';
var filtersetLabel = defaultLabel;
for (var _iterator4 = Object.entries(rules), _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) {
var _ref4;
if (_isArray4) {
if (_i4 >= _iterator4.length) break;
_ref4 = _iterator4[_i4++];
} else {
_i4 = _iterator4.next();
if (_i4.done) break;
_ref4 = _i4.value;
}
var _ref5 = _ref4,
label = _ref5[0],
checker = _ref5[1];
if (checker && checker(filterset)) {
filtersetLabel = label;
break;
}
}
return filtersetLabel;
}