@progress/kendo-charts
Version:
Kendo UI platform-independent Charts library
719 lines (615 loc) • 25.2 kB
JavaScript
var ActionTypes = Object.freeze({
seriesType: 0,
stacked: 1,
categoryAxisX: 2,
valueAxisY: 3,
seriesChange: 4,
areaMarginLeft: 5,
areaMarginRight: 6,
areaMarginTop: 7,
areaMarginBottom: 8,
areaBackground: 9,
titleText: 10,
titleFontName: 11,
titleFontSize: 12,
titleColor: 13,
subtitleText: 14,
subtitleFontName: 15,
subtitleFontSize: 16,
subtitleColor: 17,
seriesColor: 18,
seriesLabel: 19,
legendVisible: 20,
legendFontName: 21,
legendFontSize: 22,
legendColor: 23,
legendPosition: 24,
categoryAxisTitleText: 25,
categoryAxisTitleFontName: 26,
categoryAxisTitleFontSize: 27,
categoryAxisTitleColor: 28,
categoryAxisLabelsFontName: 29,
categoryAxisLabelsFontSize: 30,
categoryAxisLabelsColor: 31,
categoryAxisLabelsRotation: 32,
categoryAxisReverseOrder: 33,
valueAxisTitleText: 34,
valueAxisTitleFontName: 35,
valueAxisTitleFontSize: 36,
valueAxisTitleColor: 37,
valueAxisLabelsFormat: 38,
valueAxisLabelsFontName: 39,
valueAxisLabelsFontSize: 40,
valueAxisLabelsColor: 41,
valueAxisLabelsRotation: 42,
});
var fontSizes = [
{ text: "10", value: "10px" },
{ text: "12", value: "12px" },
{ text: "14", value: "14px" },
{ text: "16", value: "16px" },
{ text: "20", value: "20px" },
{ text: "28", value: "28px" },
{ text: "42", value: "42px" },
{ text: "56", value: "56px" }
];
var titleSizeDefault = '20px';
var subtitleSizeDefault = '16px';
var labelSizeDefault = '12px';
var axisTitleSizeDefault = '16px';
var fontNames = [
{
text: "Arial",
value: "Arial, Helvetica, sans-serif",
style: { fontFamily: "Arial, Helvetica, sans-serif" },
},
{
text: "Courier New",
value: "'Courier New', Courier, monospace",
style: { fontFamily: "'Courier New', Courier, monospace" },
},
{
text: "Georgia",
value: "Georgia, serif",
style: { fontFamily: "Georgia, serif" },
},
{
text: "Impact",
value: "Impact, Charcoal, sans-serif",
style: { fontFamily: "Impact, Charcoal, sans-serif" },
},
{
text: "Lucida Console",
value: "'Lucida Console', Monaco, monospace",
style: { fontFamily: "'Lucida Console', Monaco, monospace" },
},
{
text: "Tahoma",
value: "Tahoma, Geneva, sans-serif",
style: { fontFamily: "Tahoma, Geneva, sans-serif" },
},
{
text: "Times New Roman",
value: "'Times New Roman', Times,serif",
style: { fontFamily: "'Times New Roman', Times,serif" },
},
{
text: "Trebuchet MS",
value: "'Trebuchet MS', Helvetica, sans-serif",
style: { fontFamily: "'Trebuchet MS', Helvetica, sans-serif" },
},
{
text: "Verdana",
value: "Verdana, Geneva, sans-serif",
style: { fontFamily: "Verdana, Geneva, sans-serif" },
} ];
var fontNameDefault = fontNames[0].value;
var columnType = "column";
var barType = "bar";
var lineType = "line";
var pieType = "pie";
var scatterType = "scatter";
var categoricalTypes = [columnType, barType, lineType, scatterType];
var scatterSeries = {
type: lineType,
width: 0,
};
function isCategorical(type) {
return type && categoricalTypes.includes(type);
}
var categoryTypes = ["string", "date", "number"];
var valueTypes = ["number"];
var axesDefinitions = {
bar: [
{ axisType: "category", types: categoryTypes },
{ axisType: "value", types: valueTypes } ],
column: [
{ axisType: "category", types: categoryTypes },
{ axisType: "value", types: valueTypes } ],
line: [
{ axisType: "category", types: categoryTypes },
{ axisType: "value", types: valueTypes } ],
pie: [
{ axisType: "category", types: categoryTypes },
{ axisType: "value", types: valueTypes, count: 1 } ],
scatter: [
{ axisType: "category", types: categoryTypes },
{ axisType: "value", types: valueTypes } ],
};
function getFont(font, size) {
return ((size || "") + " " + (font || "")).trim();
}
function parseFont(font) {
var spaceIndex = (font || "").indexOf(" ");
var size = font && font.substring(0, spaceIndex);
var name = font && font.substring(spaceIndex + 1);
return { size: size, name: name };
}
var updateFontName = function (fontName, defaultSize, currentFont) {
var ref = parseFont(currentFont);
var size = ref.size;
return fontName ? getFont(fontName, size || defaultSize) : "";
};
var updateFontSize = function (fontSize, defaultFontName, currentFont) {
var ref = parseFont(currentFont);
var name = ref.name;
return fontSize ? getFont(name || defaultFontName, fontSize) : "";
};
var hasValue = function (value) { return value !== undefined && value !== null; };
var recordWithValues = function (data) {
var result = structuredClone(data[0]);
result.forEach(function (item, i) {
if (!hasValue(item.value)) {
for (var index = 0; index < data.length; index++) {
var value = data[index][i].value;
if (hasValue(value)) {
item.value = value;
break;
}
}
}
});
return result;
};
var getCategoryColumnIndex = function (data, categoryDef) {
var candidates = [];
var sampleRecord = recordWithValues(data);
categoryDef.types.forEach(function (type) {
sampleRecord.forEach(function (item, i) {
if (typeof item.value === type) {
candidates.push(i);
}
});
});
var result = candidates.findIndex(function (index) {
var values = data.map(function (record) { return record[index].value; });
return new Set(values).size === values.length;
});
return Math.max(result, 0);
};
var getValueColumnIndexes = function (data, valuesDef) {
var candidates = [];
var sampleRecord = recordWithValues(data);
valuesDef.forEach(function (def) {
def.types.forEach(function (type) {
sampleRecord.forEach(function (item, i) {
if (typeof item.value === type) {
candidates.push(i);
}
});
});
});
return candidates;
};
var emptyState = function () { return structuredClone({
columns: [],
data: [],
series: [],
initialSeries: [],
categoryAxis: [ { categories: [], labels: { visible: true, rotation: "auto" }, title: { text: '' } } ],
valueAxis: [{ labels: { visible: true, rotation: 'auto' } }],
area: {
margin: {
left: undefined,
right: undefined,
top: undefined,
bottom: undefined,
},
},
title: { text: '' },
subtitle: { text: '' },
stack: false,
}); };
var categoryValueChartState = function (data, seriesType, options) {
var state = emptyState();
state.seriesType = seriesType;
state.data = data || [];
state.legend = { visible: true, position: "bottom" };
var chartDef = axesDefinitions[seriesType];
if (!chartDef || !data.length) {
return state;
}
var firstRecord = data[0].slice();
state.columns = data[0].map(function (i) { return String(i.field); });
var categoryDef = chartDef.find(function (def) { return def.axisType === "category"; });
var catIndex = -1;
if (categoryDef) {
catIndex =
options && options.categoryAxis
? state.columns.indexOf(options.categoryAxis)
: getCategoryColumnIndex(data, categoryDef);
}
var valuesDef = chartDef.filter(function (def) { return def.axisType === "value"; });
var valuesIndexes = getValueColumnIndexes(data, valuesDef);
if (valuesIndexes.includes(catIndex)) {
if (valuesIndexes.length > 1) {
valuesIndexes = valuesIndexes.filter(function (index) { return index !== catIndex; });
} else {
catIndex = -1;
}
}
var series = [];
valuesIndexes.forEach(function (index) {
var valuesColumn = firstRecord[index];
var valuesResult = [];
data.forEach(function (record) {
valuesResult.push(record[index].value);
});
series.push(Object.assign({}, {name: valuesColumn.field,
type: seriesType,
data: valuesResult,
stack: false,
labels: { visible: false }},
(seriesType === scatterType ? scatterSeries : {})));
});
var categories =
catIndex > -1
? data.map(function (item) { return String(
hasValue(item[catIndex].value)
? item[catIndex].value
: " "
); }
)
: [];
if (series.length) {
state.series = series.map(function (s, i) { return (Object.assign({}, s, {id: i})); });
state.initialSeries = structuredClone(state.series);
}
state.categoryAxis = [
{ categories: categories, labels: { visible: true, rotation: "auto" } } ];
state.categoryField = state.columns[catIndex];
return state;
};
var pieChartState = function (data, seriesType, options) {
var state = emptyState();
state.seriesType = seriesType;
state.data = data || [];
var chartDef = axesDefinitions[seriesType];
if (!chartDef || !data.length) {
return state;
}
var categoriesAxis = data[0].map(function (i) { return i.field; });
var categoryDef = chartDef.find(function (def) { return def.axisType === "category"; });
var catIndex = -1;
if (categoryDef) {
catIndex =
options && options.categoryAxis
? categoriesAxis.indexOf(options.categoryAxis)
: getCategoryColumnIndex(data, categoryDef);
}
var valuesDef = chartDef.filter(function (def) { return def.axisType === "value"; });
var valuesIndexes = [];
if (options && options.valueAxis) {
valuesIndexes = [categoriesAxis.indexOf(options.valueAxis)];
} else {
valuesIndexes = getValueColumnIndexes(data, valuesDef);
}
if (valuesIndexes.includes(catIndex) && valuesIndexes.length > 1) {
valuesIndexes = valuesIndexes.filter(function (index) { return index !== catIndex; });
}
if (typeof valuesDef[0].count === "number") {
valuesIndexes = valuesIndexes.slice(0, valuesDef[0].count);
}
var categories =
catIndex > -1 ? data.map(function (item) { return String(item[catIndex].value); }) : [];
var flatData = [];
data.forEach(function (item) {
var record = {};
valuesIndexes.forEach(function (index) {
var col = item[index];
record[col.field] = col.value || 0;
record[item[catIndex].field] = item[catIndex].value || " ";
});
flatData.push(record);
});
state.columns = categoriesAxis;
state.categoryAxis = [{ categories: categories, title: { text: "" } }];
state.series = [
{
id: 0,
data: flatData,
type: seriesType,
name: categoriesAxis[catIndex],
labels: { visible: true },
categoryField: categoriesAxis[catIndex],
field: categoriesAxis[valuesIndexes[0]],
} ];
state.categoryField = categoriesAxis[catIndex];
state.valueField = categoriesAxis[valuesIndexes[0]];
state.initialSeries = structuredClone(state.series);
return state;
};
function createInitialState(data, seriesType, defaultState) {
var state = createState(
data,
(defaultState && defaultState.seriesType) || seriesType
);
return typeof (defaultState && defaultState.stack) !== "undefined"
? updateState(state, ActionTypes.stacked, defaultState.stack)
: state;
}
function createState(data, seriesType) {
return (isCategorical(seriesType) ? categoryValueChartState : pieChartState)(
data,
seriesType
);
}
function mergeStates(source, target) {
var newState = structuredClone(target);
newState.legend = source.legend;
newState.area = source.area;
newState.title = source.title;
newState.subtitle = source.subtitle;
if (newState.series.length === source.series.length) {
for (var i = 0; i < newState.series.length; i++) {
newState.series[i].color = source.series[i].color;
newState.series[i].labels = source.series[i].labels;
}
}
if (
source.series.every(function (s) { return s.labels && s.labels.visible; }) &&
isCategorical(newState.seriesType) &&
isCategorical(source.seriesType)
) {
newState.series.forEach(function (s) {
s.labels = s.labels || {};
s.labels.visible = true;
});
}
return newState;
}
/* eslint-disable complexity */
function updateState(currentState, action, value) {
var state = Object.assign({}, currentState);
switch (action) {
case ActionTypes.seriesType:
return createState(state.data, value);
case ActionTypes.stacked:
state.series = state.series.map(function (s) { return (Object.assign({}, s, {stack: value})); });
state.stack = value;
return state;
case ActionTypes.categoryAxisX: {
if (state.seriesType && isCategorical(state.seriesType)) {
var newState = categoryValueChartState(
state.data,
state.seriesType,
{ categoryAxis: value }
);
return mergeStates(state, newState);
} else if (state.seriesType === pieType) {
var newState$1 = pieChartState(state.data, state.seriesType, {
categoryAxis: value,
});
return mergeStates(state, newState$1);
}
return state;
}
case ActionTypes.valueAxisY: {
if (state.seriesType === pieType) {
var newState$2 = pieChartState(state.data, state.seriesType, {
categoryAxis: state.categoryField,
valueAxis: value,
});
return mergeStates(state, newState$2);
}
return state;
}
case ActionTypes.seriesChange:
state.series = value;
return state;
case ActionTypes.areaMarginLeft:
state.area = Object.assign({}, state.area,
{margin: Object.assign({}, ((state.area && state.area.margin) || {}),
{left: value})});
return state;
case ActionTypes.areaMarginRight:
state.area = Object.assign({}, state.area,
{margin: Object.assign({}, ((state.area && state.area.margin) || {}),
{right: value})});
return state;
case ActionTypes.areaMarginTop:
state.area = Object.assign({}, state.area,
{margin: Object.assign({}, ((state.area && state.area.margin) || {}),
{top: value})});
return state;
case ActionTypes.areaMarginBottom:
state.area = Object.assign({}, state.area,
{margin: Object.assign({}, ((state.area && state.area.margin) || {}),
{bottom: value})});
return state;
case ActionTypes.areaBackground:
state.area = Object.assign({}, state.area, {background: value});
return state;
case ActionTypes.titleText:
state.title = Object.assign({}, state.title, {text: value});
return state;
case ActionTypes.titleFontName: {
state.title = Object.assign({}, state.title,
{font: updateFontName(
value,
titleSizeDefault,
state.title && state.title.font
)});
return state;
}
case ActionTypes.titleFontSize:
state.title = Object.assign({}, state.title,
{font: updateFontSize(
value,
fontNameDefault,
state.title && state.title.font
)});
return state;
case ActionTypes.titleColor:
state.title = Object.assign({}, state.title, {color: value});
return state;
case ActionTypes.subtitleText:
state.subtitle = Object.assign({}, state.subtitle, {text: value});
return state;
case ActionTypes.subtitleFontName:
state.subtitle = Object.assign({}, state.subtitle,
{font: updateFontName(
value,
subtitleSizeDefault,
state.subtitle && state.subtitle.font
)});
return state;
case ActionTypes.subtitleFontSize:
state.subtitle = Object.assign({}, state.subtitle,
{font: updateFontSize(
value,
fontNameDefault,
state.subtitle && state.subtitle.font
)});
return state;
case ActionTypes.subtitleColor:
state.subtitle = Object.assign({}, state.subtitle, {color: value});
return state;
case ActionTypes.seriesColor:
state.series = state.series.map(function (s) { return (Object.assign({}, s,
{color: value.seriesName === s.name ? value.color : s.color})); });
return state;
case ActionTypes.seriesLabel:
state.series = state.series.map(function (s) {
if (value.all || value.seriesName === s.name) {
return Object.assign({}, s, {labels: { visible: value.visible }});
}
return s;
});
return state;
case ActionTypes.legendVisible:
state.legend = Object.assign({}, state.legend, {visible: value});
return state;
case ActionTypes.legendFontName: {
var legend = state.legend || {};
state.legend = Object.assign({}, legend,
{labels: Object.assign({}, legend.labels,
{font: updateFontName(
value,
labelSizeDefault,
legend.labels && legend.labels.font
)})});
return state;
}
case ActionTypes.legendFontSize: {
var legend$1 = state.legend || {};
state.legend = Object.assign({}, legend$1,
{labels: Object.assign({}, legend$1.labels,
{font: updateFontSize(
value,
fontNameDefault,
legend$1.labels && legend$1.labels.font
)})});
return state;
}
case ActionTypes.legendColor: {
var legend$2 = state.legend || {};
state.legend = Object.assign({}, legend$2,
{labels: Object.assign({}, legend$2.labels, {color: value})});
return state;
}
case ActionTypes.legendPosition:
state.legend = Object.assign({}, state.legend, {position: value});
return state;
case ActionTypes.categoryAxisTitleText:
state.categoryAxis = (state.categoryAxis || []).map(function (axis) { return (Object.assign({}, axis, {title: Object.assign({}, axis.title, {text: value})})); });
return state;
case ActionTypes.categoryAxisTitleFontName: {
state.categoryAxis = (state.categoryAxis || []).map(function (axis) { return (Object.assign({}, axis, {title: Object.assign({}, axis.title, {font: updateFontName(value, axisTitleSizeDefault, axis.title && axis.title.font)})})); });
return state;
}
case ActionTypes.categoryAxisTitleFontSize:
state.categoryAxis = (state.categoryAxis || []).map(function (axis) { return (Object.assign({}, axis, {title: Object.assign({}, axis.title, {font: updateFontSize(value, fontNameDefault, axis.title && axis.title.font)})})); });
return state;
case ActionTypes.categoryAxisTitleColor:
state.categoryAxis = (state.categoryAxis || []).map(function (axis) { return (Object.assign({}, axis, {title: Object.assign({}, axis.title, {color: value})})); });
return state;
case ActionTypes.categoryAxisLabelsFontName: {
state.categoryAxis = (state.categoryAxis || []).map(function (axis) { return (Object.assign({}, axis, {labels: Object.assign({}, axis.labels, {font: updateFontName(value, labelSizeDefault, axis.labels && axis.labels.font)})})); });
return state;
}
case ActionTypes.categoryAxisLabelsFontSize:
state.categoryAxis = (state.categoryAxis || []).map(function (axis) { return (Object.assign({}, axis, {labels: Object.assign({}, axis.labels, {font: updateFontSize(value, fontNameDefault, axis.labels && axis.labels.font)})})); });
return state;
case ActionTypes.categoryAxisLabelsColor:
state.categoryAxis = (state.categoryAxis || []).map(function (axis) { return (Object.assign({}, axis, {labels: Object.assign({}, axis.labels, {color: value})})); });
return state;
case ActionTypes.categoryAxisLabelsRotation: {
var rotation = hasValue(value) ? value : 'auto';
state.categoryAxis = (state.categoryAxis || []).map(function (axis) { return (Object.assign({}, axis, {labels: Object.assign({}, axis.labels, {rotation: rotation})})); });
return state;
}
case ActionTypes.categoryAxisReverseOrder:
state.categoryAxis = (state.categoryAxis || []).map(function (axis) { return (Object.assign({}, axis, {reverse: value})); });
return state;
case ActionTypes.valueAxisTitleText: {
if (!state.valueAxis || state.valueAxis.length === 0) {
state.valueAxis = [{ title: { text: value } }];
} else {
state.valueAxis = (state.valueAxis || []).map(function (axis) { return (Object.assign({}, axis, {title: Object.assign({}, axis.title, {text: value})})); });
}
return state;
}
case ActionTypes.valueAxisTitleFontName: {
state.valueAxis = (state.valueAxis || []).map(function (axis) { return (Object.assign({}, axis, {title: Object.assign({}, axis.title, {font: updateFontName(value, axisTitleSizeDefault, axis.title && axis.title.font)})})); });
return state;
}
case ActionTypes.valueAxisTitleFontSize:
state.valueAxis = (state.valueAxis || []).map(function (axis) { return (Object.assign({}, axis, {title: Object.assign({}, axis.title, {font: updateFontSize(value, fontNameDefault, axis.title && axis.title.font)})})); });
return state;
case ActionTypes.valueAxisTitleColor:
state.valueAxis = (state.valueAxis || []).map(function (axis) { return (Object.assign({}, axis, {title: Object.assign({}, axis.title, {color: value})})); });
return state;
case ActionTypes.valueAxisLabelsFormat:
state.valueAxis = (state.valueAxis || []).map(function (axis) { return (Object.assign({}, axis, {labels: Object.assign({}, axis.labels, {format: value})})); });
return state;
case ActionTypes.valueAxisLabelsFontName: {
state.valueAxis = (state.valueAxis || []).map(function (axis) { return (Object.assign({}, axis, {labels: Object.assign({}, axis.labels, {font: updateFontName(value, labelSizeDefault, axis.labels && axis.labels.font)})})); });
return state;
}
case ActionTypes.valueAxisLabelsFontSize:
state.valueAxis = (state.valueAxis || []).map(function (axis) { return (Object.assign({}, axis, {labels: Object.assign({}, axis.labels, {font: updateFontSize(value, fontNameDefault, axis.labels && axis.labels.font)})})); });
return state;
case ActionTypes.valueAxisLabelsColor:
state.valueAxis = (state.valueAxis || []).map(function (axis) { return (Object.assign({}, axis, {labels: Object.assign({}, axis.labels, {color: value})})); });
return state;
case ActionTypes.valueAxisLabelsRotation: {
var rotation$1 = hasValue(value) ? value : 'auto';
state.valueAxis = (state.valueAxis || []).map(function (axis) { return (Object.assign({}, axis, {labels: Object.assign({}, axis.labels, {rotation: rotation$1})})); });
return state;
}
default:
return state;
}
}
export {
ActionTypes,
fontSizes,
fontNames,
isCategorical,
parseFont,
createInitialState,
createState,
mergeStates,
updateState
};