lucid-ui
Version:
A UI component library from AppNexus.
208 lines • 6.99 kB
JavaScript
import _ from 'lodash';
import * as d3TimeFormat from 'd3-time-format';
import * as d3Time from 'd3-time';
/**
* stackByFields
*
* D3's `stack` groups each series' data together but we sometimes we want the
* stacked groups to remain grouped as in the original normalized data. This
* function helps achieve that.
*
* @param {object[]} collection - normalized data you want to operate on
* @param {string[]} fields - fields to pluck off for the y data
* @return {array[]} - array of arrays, one for row in the original `collection`
*/
export function stackByFields(collection, fields) {
const fieldsArray = _.castArray(fields);
return _.map(collection, (d) => {
return _.reduce(fieldsArray, (acc, field) => {
const dataPoint = _.get(d, field, 0);
if (_.isEmpty(acc)) {
return acc.concat([[0, dataPoint]]);
}
const maybeLast = _.last(_.last(acc));
const last = maybeLast === undefined ? 0 : maybeLast;
return acc.concat([[last, last + dataPoint]]);
}, []);
});
}
/**
* extractFields
*
* This will return the data in a similar format to stackByFields but without
* the stacking.
*
* @param {object[]} collection - normalized data you want to operate on
* @param {string[]} fields - fields to pluck off for the y data
* @return {array[]} - array of arrays, one for each field
*/
export function extractFields(collection, fields, minDomainValue = 0) {
const fieldsArray = _.castArray(fields);
return _.map(collection, (d) => {
return _.map(fieldsArray, (field) => [minDomainValue, _.get(d, field, 0)]);
});
}
/**
* groupByFields
*
* This will return the data in a similar format to d3Shape.stack
* but without the stacking of the data.
*
* @param {object[]} collection - normalized data you want to operate on
* @param {string[]} fields - fields to pluck off for the y data
* @return {array[]} - array of arrays, one for each field
*/
export function groupByFields(collection, fields) {
const fieldsArray = _.castArray(fields);
return _.map(fieldsArray, (field) => {
return _.map(collection, field);
});
}
/**
* byFields
*
* Takes a collection of data and returns an array of all the fields off that
* collection.
*
* @param {object[]} collection
* @param {string[]} fields
* @return {array}
*/
export function byFields(collection, fields) {
const fieldArray = _.castArray(fields);
return _.reduce(fieldArray, (acc, field) => {
return acc.concat(_.map(collection, field));
}, []);
}
/**
* nearest
*
* Divide and conquer algorithm that helps find the nearest element to `value`
* in `nums`
*
* @param {number[]} nums - sorted array of numbers to search through
* @param {number} value - value you're trying to locate the nearest array element for
* @return {number} - the nearest array element to the value
*/
export function nearest(nums, value) {
if (nums.length < 2) {
return _.first(nums);
}
if (nums.length === 2) {
return value > (nums[0] + nums[1]) / 2 ? nums[1] : nums[0];
}
const mid = nums.length >>> 1;
return nums[mid] > value
? nearest(nums.slice(0, mid + 1), value)
: nearest(nums.slice(mid), value);
}
/**
* minByFields
*
* Returns the minimum element from a collection by a set of fields.
*
* @param {object[]} collection
* @param {string[]} fields
* @return {any}
*/
export function minByFields(collection, fields) {
return _.min(byFields(collection, fields));
}
/**
* maxByFields
*
* Returns the maximum element from a collection by a set of fields.
*
* @param {object[]} collection
* @param {string[]} fields
* @return {any}
*/
export function maxByFields(collection, fields) {
return _.max(byFields(collection, fields));
}
/**
* maxByFieldsStacked
*
* Returns the max sum of a set of fields from a collection
*
* @param {object[]} collection
* @param {string[]} fields
* @return {any}
*/
export function maxByFieldsStacked(collection, fields) {
const fieldArray = _.castArray(fields);
const sums = _.reduce(collection, (acc, item) => {
return acc.concat(_.sum(_.toArray(_.pick(item, fieldArray))));
}, []);
return _.max(sums);
}
/**
* discreteTicks
*
* Returns `count` evenly spaced, representative values from the `array`.
*
* @param {array} array
* @param {number} size - should be greater than 1
* @return {array}
*/
export function discreteTicks(array, count = null) {
if (!array || _.isNil(count) || array.length <= count) {
return array;
}
const step = (array.length - 1) / Math.max(1, count - 1);
return _.reduce(_.times(count), (acc, n) => {
return acc.concat(array[Math.round(n * step)]);
}, []);
}
/**
* transformFromCenter
*
* Scaling paths from their center is tricky. This function
* helps do that be generating a translate/scale transform
* string with the correct numbers.
*
* @param {number} x - the x data point where you want the path to be centered at
* @param {number} y - the y data point where you want the path to be centered at
* @param {number} xCenter - the x coordinate of the center of the path you're trying to transform
* @param {number} yCenter - the x coordinate of the center of the path you're trying to transform
* @param {number} scale - number to scale to, 2 would be 2x bigger
* @return {string} - transform string
*/
export function transformFromCenter(x, y, xCenter, yCenter, scale) {
return `translate(${(1 - scale) * xCenter + (x - xCenter)}, ${(1 - scale) * yCenter + (y - yCenter)}) scale(${scale})`;
}
const FORMAT_MILLISECOND = d3TimeFormat.timeFormat('.%L');
const FORMAT_SECOND = d3TimeFormat.timeFormat(':%S');
const FORMAT_MINUTE = d3TimeFormat.timeFormat('%I:%M');
const FORMAT_HOUR = d3TimeFormat.timeFormat('%I %p');
const FORMAT_DAY = d3TimeFormat.timeFormat('%a %d');
const FORMAT_WEEK = d3TimeFormat.timeFormat('%b %d');
const FORMAT_MONTH = d3TimeFormat.timeFormat('%b');
const FORMAT_YEAR = d3TimeFormat.timeFormat('%Y');
/**
* formatDate
*
* This function was written to be used for tick formatting with d3 time
* scales.
*
* @param {date} date - input date
* @return {string} - formatted date
*/
export function formatDate(date) {
return (d3Time.timeSecond(date) < date
? FORMAT_MILLISECOND
: d3Time.timeMinute(date) < date
? FORMAT_SECOND
: d3Time.timeHour(date) < date
? FORMAT_MINUTE
: d3Time.timeDay(date) < date
? FORMAT_HOUR
: d3Time.timeMonth(date) < date
? d3Time.timeWeek(date) < date
? FORMAT_DAY
: FORMAT_WEEK
: d3Time.timeYear(date) < date
? FORMAT_MONTH
: FORMAT_YEAR)(date);
}
//# sourceMappingURL=chart-helpers.js.map