@toast-ui/chart
Version:
TOAST UI Application: Chart
193 lines (192 loc) • 7.07 kB
JavaScript
import { extend } from "./store";
import { pickProperty, isObject, sum } from "../helpers/utils";
export function isPercentStack(stack) {
var _a;
return !!(((_a = stack) === null || _a === void 0 ? void 0 : _a.type) === 'percent');
}
export function isGroupStack(rawData) {
return !Array.isArray(rawData);
}
export function hasPercentStackSeries(stackSeries) {
if (!stackSeries) {
return false;
}
return Object.keys(stackSeries).some((seriesName) => isPercentStack(stackSeries[seriesName].stack));
}
export function pickStackOption(options) {
return (pickProperty(options, ['series', 'stack']) ||
pickProperty(options, ['series', 'column', 'stack']) ||
pickProperty(options, ['series', 'area', 'stack']));
}
function makeStackData(seriesData) {
const seriesCount = seriesData.length;
const groupCountLengths = seriesData.map(({ rawData }) => rawData.length);
const seriesGroupCount = Math.max(...groupCountLengths);
const stackData = [];
for (let i = 0; i < seriesGroupCount; i += 1) {
const stackValues = [];
for (let j = 0; j < seriesCount; j += 1) {
stackValues.push(seriesData[j].rawData[i]);
}
stackData[i] = {
values: stackValues,
sum: sum(stackValues),
total: {
positive: sum(stackValues.filter((value) => value >= 0)),
negative: sum(stackValues.filter((value) => value < 0)),
},
};
}
return stackData;
}
function makeStackGroupData(seriesData) {
const stackData = {};
const stackGroupIds = [...new Set(seriesData.map(({ stackGroup }) => stackGroup))];
stackGroupIds.forEach((groupId) => {
const filtered = seriesData.filter(({ stackGroup }) => groupId === stackGroup);
stackData[groupId] = makeStackData(filtered);
});
return stackData;
}
function initializeStack(stackOption) {
if (!stackOption) {
return;
}
const defaultStackOption = {
type: 'normal',
connector: false,
};
if (isStackObject(stackOption)) {
return Object.assign(Object.assign({}, defaultStackOption), stackOption);
}
return defaultStackOption;
}
function isStackObject(stackOption) {
return isObject(stackOption);
}
function hasStackGrouped(seriesRawData) {
return seriesRawData.some((rawData) => rawData.hasOwnProperty('stackGroup'));
}
function getStackDataRangeValues(stackData) {
let values = [];
if (Array.isArray(stackData)) {
values = [0, ...getSumValues(stackData)];
}
else {
for (const groupId in stackData) {
if (Object.prototype.hasOwnProperty.call(stackData, groupId)) {
values = [0, ...values, ...getSumValues(stackData[groupId])];
}
}
}
return values;
}
function getSumValues(stackData) {
const positiveSum = stackData.map(({ total }) => total.positive);
const negativeSum = stackData.map(({ total }) => total.negative);
return [...negativeSum, ...positiveSum];
}
function getStackDataValues(stackData) {
if (!isGroupStack(stackData)) {
return stackData;
}
let stackDataValues = [];
if (isGroupStack(stackData)) {
Object.keys(stackData).forEach((groupId) => {
stackDataValues = [...stackDataValues, ...stackData[groupId]];
});
}
return stackDataValues;
}
function checkIfNegativeAndPositiveValues(stackData) {
return {
hasNegative: stackData.map(({ total }) => total.negative).some((total) => total < 0),
hasPositive: stackData.map(({ total }) => total.positive).some((total) => total >= 0),
};
}
function getScaleType(stackData, stackType, diverging) {
const { hasPositive, hasNegative } = checkIfNegativeAndPositiveValues(stackData);
if (stackType === 'percent') {
if (diverging) {
return 'divergingPercentStack';
}
if (hasNegative && hasPositive) {
return 'dualPercentStack';
}
if (!hasNegative && hasPositive) {
return 'percentStack';
}
if (hasNegative && !hasPositive) {
return 'minusPercentStack';
}
}
}
function initStackSeries(series, options) {
const stackSeries = {};
Object.keys(series).forEach((seriesName) => {
const chartType = seriesName;
const stackOption = pickStackOption(options);
if (stackOption) {
if (!stackSeries[chartType]) {
stackSeries[chartType] = {};
}
stackSeries[chartType].stack = initializeStack(stackOption);
}
else if (seriesName === 'radialBar') {
stackSeries[seriesName] = { stack: true };
}
});
return stackSeries;
}
const stackSeriesData = {
name: 'stackSeriesData',
state: ({ series, options }) => ({
stackSeries: initStackSeries(series, options),
}),
action: {
setStackSeriesData({ state }) {
const { series, stackSeries, options } = state;
const stackOption = pickStackOption(options);
const newStackSeries = {};
Object.keys(series).forEach((seriesName) => {
var _a, _b;
const seriesData = series[seriesName];
const { data, seriesCount, seriesGroupCount } = seriesData;
const isRadialBar = seriesName === 'radialBar';
if (stackOption) {
if (!stackSeries[seriesName]) {
stackSeries[seriesName] = {};
}
stackSeries[seriesName].stack = initializeStack(stackOption);
}
else if (!isRadialBar) {
stackSeries[seriesName] = null;
delete stackSeries[seriesName];
}
const { stack } = stackSeries[seriesName] || {};
const diverging = !!((_a = options.series) === null || _a === void 0 ? void 0 : _a.diverging);
if (stack) {
const stackData = hasStackGrouped(data) ? makeStackGroupData(data) : makeStackData(data);
const stackType = (_b = stack.type, (_b !== null && _b !== void 0 ? _b : 'normal'));
const dataRangeValues = getStackDataRangeValues(stackData);
newStackSeries[seriesName] = {
data,
seriesCount,
seriesGroupCount,
stackData,
dataRangeValues,
scaleType: getScaleType(getStackDataValues(stackData), stackType, diverging),
};
state.stackSeries[seriesName].stackData = stackData;
}
extend(state.stackSeries, newStackSeries);
});
},
},
observe: {
updateStackSeriesData() {
this.dispatch('setStackSeriesData');
},
},
};
export default stackSeriesData;