UNPKG

ag-charts-community

Version:

Advanced Charting / Charts supporting Javascript / Typescript / React / Angular / Vue

550 lines 21.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const chart_1 = require("./chart"); const series_1 = require("./series/series"); const legend_1 = require("./legend"); const chartTheme_1 = require("./themes/chartTheme"); const darkTheme_1 = require("./themes/darkTheme"); const materialLight_1 = require("./themes/materialLight"); const materialDark_1 = require("./themes/materialDark"); const pastelLight_1 = require("./themes/pastelLight"); const pastelDark_1 = require("./themes/pastelDark"); const solarLight_1 = require("./themes/solarLight"); const solarDark_1 = require("./themes/solarDark"); const vividLight_1 = require("./themes/vividLight"); const vividDark_1 = require("./themes/vividDark"); const array_1 = require("../util/array"); const object_1 = require("../util/object"); // In the config object consumed by the factory we don't specify the types of objects we want to create, // and in the rare cases when we do, the string type is not the same as corresponding constructor's name. // Also, the user provided config might miss certain mandatory properties. // For that reason, we must be able to tell what to instantiate and with what defaults using only // property's name and position in the config's hierarchy. To be able to do that we need the extra // information missing from the user provided config. Such information is provided by chart mappings. const agChartMappings_1 = require("./agChartMappings"); const lightTheme = new chartTheme_1.ChartTheme(); const darkTheme = new darkTheme_1.DarkTheme(); exports.lightThemes = { 'undefined': lightTheme, 'null': lightTheme, 'ag-default': lightTheme, 'ag-material': new materialLight_1.MaterialLight(), 'ag-pastel': new pastelLight_1.PastelLight(), 'ag-solar': new solarLight_1.SolarLight(), 'ag-vivid': new vividLight_1.VividLight(), }; exports.darkThemes = { 'undefined': darkTheme, 'null': darkTheme, 'ag-default-dark': darkTheme, 'ag-material-dark': new materialDark_1.MaterialDark(), 'ag-pastel-dark': new pastelDark_1.PastelDark(), 'ag-solar-dark': new solarDark_1.SolarDark(), 'ag-vivid-dark': new vividDark_1.VividDark(), }; exports.themes = Object.assign(Object.assign({}, exports.darkThemes), exports.lightThemes); function getChartTheme(value) { if (value instanceof chartTheme_1.ChartTheme) { return value; } const stockTheme = exports.themes[value]; if (stockTheme) { return stockTheme; } value = value; if (value.baseTheme || value.overrides || value.palette) { const baseTheme = getChartTheme(value.baseTheme); return new baseTheme.constructor(value); } return lightTheme; } exports.getChartTheme = getChartTheme; let firstColorIndex = 0; class AgChart { static create(options, container, data) { options = Object.create(options); // avoid mutating user provided options return AgChart.createOrUpdate({ options, container, data, operation: (options, theme) => create(options, undefined, undefined, theme), }); } static update(chart, options, container, data) { if (!(chart && options)) { return; } return AgChart.createOrUpdate({ chart, options, container, data, operation: (options, theme) => update(chart, options, undefined, theme), }); } static createOrUpdate({ chart, options, container, data, operation }) { if (container) { options.container = container; } if (data) { options.data = data; } if (options.series && options.series.length > 0) { options.series = processSeriesOptions(options.series); } // special handling when both `autoSize` and `width` / `height` are present in the options const autoSize = options && options.autoSize !== false; const theme = getChartTheme(options.theme); firstColorIndex = 0; const result = operation(options, theme); if (chart == null && result instanceof chart_1.Chart) { chart = result; } if (chart && autoSize) { // `autoSize` takes precedence over `width` / `height` chart.autoSize = true; } return result; } } exports.AgChart = AgChart; AgChart.createComponent = create; const pathToSeriesTypeMap = { 'cartesian.series': 'line', 'line.series': 'line', 'area.series': 'area', 'bar.series': 'bar', 'column.series': 'column', 'histogram.series': 'histogram', 'scatter.series': 'scatter', 'polar.series': 'pie', 'pie.series': 'pie' }; const actualSeriesTypeMap = { // Identity mappings. 'area': 'area', 'bar': 'bar', 'histogram': 'histogram', 'line': 'line', 'pie': 'pie', 'scatter': 'scatter', 'ohlc': 'ohlc', 'treemap': 'treemap', // Aliases. 'column': 'bar', }; function create(options, path, component, theme) { // Deprecate `chart.legend.item.marker.type` in integrated chart options. options = Object.create(options); if (component instanceof legend_1.LegendMarker) { if (options.type) { options.shape = options.type; } } else { options = provideDefaultType(options, path); if (path) { if (options.type) { path = path + '.' + options.type; } } else { path = options.type; } } if (!path) { return; } const mapping = object_1.getValue(agChartMappings_1.mappings, path); if (mapping) { options = provideDefaultOptions(path, options, mapping, theme); const meta = mapping.meta || {}; const constructorParams = meta.constructorParams || []; const skipKeys = ['type', 'listeners'].concat(constructorParams); // TODO: Constructor params processing could be improved, but it's good enough for current params. const constructorParamValues = constructorParams .map((param) => options[param]) .filter((value) => value !== undefined); if (!component) { component = new meta.constructor(...constructorParamValues); if (theme && component instanceof series_1.Series) { firstColorIndex = theme.setSeriesColors(component, options, firstColorIndex); } } for (const key in options) { // Process every non-special key in the config object. if (skipKeys.indexOf(key) < 0) { const value = options[key]; if (value && key in mapping && !(meta.setAsIs && meta.setAsIs.indexOf(key) >= 0)) { if (Array.isArray(value)) { const subComponents = value .map(config => { const axis = create(config, path + '.' + key, undefined, theme); if (theme && key === 'axes') { const fakeTheme = { getConfig(path) { const parts = path.split('.'); let modifiedPath = parts.slice(0, 3).join('.') + '.' + axis.position; const after = parts.slice(3); if (after.length) { modifiedPath += '.' + after.join('.'); } const config = theme.getConfig(path); const modifiedConfig = theme.getConfig(modifiedPath); object_1.isObject(theme.getConfig(modifiedPath)); if (object_1.isObject(config) && object_1.isObject(modifiedConfig)) { return object_1.deepMerge(config, modifiedConfig, chartTheme_1.mergeOptions); } return modifiedConfig; } }; update(axis, config, path + '.' + key, fakeTheme); } return axis; }) .filter(instance => !!instance); component[key] = subComponents; } else { if (mapping[key] && component[key]) { // The instance property already exists on the component (e.g. chart.legend). // Simply configure the existing instance, without creating a new one. create(value, path + '.' + key, component[key], theme); } else { const subComponent = create(value, value.type ? path : path + '.' + key, undefined, theme); if (subComponent) { component[key] = subComponent; } } } } else { // if (key in meta.constructor.defaults) { // prevent users from creating custom properties component[key] = value; } } } const listeners = options.listeners; if (component && component.addEventListener && listeners) { for (const key in listeners) { if (listeners.hasOwnProperty(key)) { const listener = listeners[key]; if (typeof listener === 'function') { component.addEventListener(key, listener); } } } } return component; } } function update(component, options, path, theme) { if (!(options && object_1.isObject(options))) { return; } // Deprecate `chart.legend.item.marker.type` in integrated chart options. if (component instanceof legend_1.LegendMarker) { if (options.type) { options.shape = options.type; } } else { options = provideDefaultType(options, path); if (path) { if (options.type) { path = path + '.' + options.type; } } else { path = options.type; } } if (!path) { return; } const chart = path in agChartMappings_1.mappings ? component : undefined; const mapping = object_1.getValue(agChartMappings_1.mappings, path); if (mapping) { options = provideDefaultOptions(path, options, mapping, theme); const meta = mapping.meta || {}; const constructorParams = meta && meta.constructorParams || []; const skipKeys = ['type'].concat(constructorParams); for (const key in options) { if (skipKeys.indexOf(key) < 0) { const value = options[key]; const keyPath = path + '.' + key; if (meta.setAsIs && meta.setAsIs.indexOf(key) >= 0) { component[key] = value; } else { const oldValue = component[key]; if (Array.isArray(oldValue) && Array.isArray(value)) { if (chart) { if (key === 'series') { updateSeries(component, value, keyPath, theme); } else if (key === 'axes') { updateAxes(component, value, keyPath, theme); } } else { component[key] = value; } } else if (object_1.isObject(oldValue)) { if (value) { update(oldValue, value, value.type ? path : keyPath, theme); } else if (key in options) { component[key] = value; } } else { const subComponent = object_1.isObject(value) && create(value, value.type ? path : keyPath, undefined, theme); if (subComponent) { component[key] = subComponent; } else { if (chart && options.autoSize && (key === 'width' || key === 'height')) { continue; } component[key] = value; } } } } } } if (chart) { chart.layoutPending = true; } } function updateSeries(chart, configs, keyPath, theme) { const allSeries = chart.series.slice(); let prevSeries; let i = 0; for (; i < configs.length; i++) { let config = configs[i]; let series = allSeries[i]; if (series) { config = provideDefaultType(config, keyPath); const type = actualSeriesTypeMap[config.type]; if (series.type === type) { if (theme) { firstColorIndex = theme.setSeriesColors(series, config, firstColorIndex); } update(series, config, keyPath, theme); } else { const newSeries = create(config, keyPath, undefined, theme); chart.removeSeries(series); chart.addSeriesAfter(newSeries, prevSeries); series = newSeries; } } else { // more new configs than existing series const newSeries = create(config, keyPath, undefined, theme); chart.addSeries(newSeries); } prevSeries = series; } // more existing series than new configs for (; i < allSeries.length; i++) { const series = allSeries[i]; if (series) { chart.removeSeries(series); } } } function updateAxes(chart, configs, keyPath, theme) { const axes = chart.axes; const axesToAdd = []; const axesToUpdate = []; for (const config of configs) { const axisToUpdate = array_1.find(axes, axis => { return axis.type === config.type && axis.position === config.position; }); if (axisToUpdate) { axesToUpdate.push(axisToUpdate); update(axisToUpdate, config, keyPath, theme); } else { const axisToAdd = create(config, keyPath, undefined, theme); if (axisToAdd) { axesToAdd.push(axisToAdd); } } } chart.axes = axesToUpdate.concat(axesToAdd); } function provideDefaultChartType(options) { if (options.type) { return options; } // If chart type is not specified, try to infer it from the type of first series. const series = options.series && options.series[0]; if (series && series.type) { options = Object.create(options); options.type = series.type; return options; } // If chart type is not specified in the first series either, set it to 'cartesian'. options = Object.create(options); options.type = 'cartesian'; return options; } function provideDefaultType(options, path) { if (!path) { // if `path` is undefined, `options` is a top-level (chart) config path = ''; options = provideDefaultChartType(options); } if (!options.type) { const seriesType = pathToSeriesTypeMap[path]; if (seriesType) { options = Object.create(options); options.type = seriesType; } } return options; } function skipThemeKey(key) { return ['axes', 'series'].indexOf(key) >= 0; } const enabledKey = 'enabled'; /** * If certain options were not provided by the user, use the defaults from the theme and the mapping. * All three objects are provided for the current path in the config tree, not necessarily top-level. */ function provideDefaultOptions(path, options, mapping, theme) { const isChartConfig = path.indexOf('.') < 0; const themeDefaults = theme && theme.getConfig(path); const defaults = mapping && mapping.meta && mapping.meta.defaults; const isExplicitlyDisabled = options.enabled === false; // by the user if (defaults || themeDefaults) { options = Object.create(options); } // Fill in the gaps for properties not configured by the user using theme provided values. for (const key in themeDefaults) { // The default values for these special chart configs always come from the mappings, not theme. if (isChartConfig && skipThemeKey(key)) { continue; } if (!(key in options)) { options[key] = themeDefaults[key]; } } // Fill in the gaps for properties not configured by the user, nor theme using chart mappings. for (const key in defaults) { if ((!themeDefaults || !(key in themeDefaults) || skipThemeKey(key)) && !(key in options)) { options[key] = defaults[key]; } } // Special handling for the 'enabled' property. For example: // title: { text: 'Quarterly Revenue' } // means title is enabled // legend: {} // means legend is enabled const hasEnabledKey = (themeDefaults && enabledKey in themeDefaults) || (defaults && enabledKey in defaults); if (hasEnabledKey && !isExplicitlyDisabled) { options[enabledKey] = true; } return options; } /** * Groups the series options objects if they are of type `column` or `bar` and places them in an array at the index where the first instance of this series type was found. * Returns an array of arrays containing the ordered and grouped series options objects. */ function groupSeriesByType(seriesOptions) { const indexMap = { column: [], bar: [], line: [], scatter: [], area: [], histogram: [], ohlc: [], pie: [], treemap: [], }; const result = []; for (const s of seriesOptions) { const isColumnOrBar = s.type === 'column' || s.type === 'bar'; const isStackedArea = s.type === 'area' && s.stacked === true; if (!isColumnOrBar && !isStackedArea) { // No need to use index for these cases. result.push([s]); continue; } const seriesType = s.type || 'line'; if (seriesType === 'pie' || seriesType === 'treemap') { throw new Error(`AG Grid - Unexpected series type of: ${seriesType}`); } else if (indexMap[seriesType].length === 0) { // Add indexed array to result on first addition. result.push(indexMap[seriesType]); } indexMap[seriesType].push(s); } return result; } exports.groupSeriesByType = groupSeriesByType; /** * Takes an array of bar or area series options objects and returns a single object with the combined area series options. */ function reduceSeries(series, enableBarSeriesSpecialCases) { let options = {}; const arrayValueProperties = ['yKeys', 'fills', 'strokes', 'yNames', 'hideInChart', 'hideInLegend']; const stringValueProperties = ['yKey', 'fill', 'stroke', 'yName']; for (const s of series) { for (const property in s) { const arrayValueProperty = arrayValueProperties.indexOf(property) > -1; const stringValueProperty = stringValueProperties.indexOf(property) > -1; if (arrayValueProperty && s[property].length > 0) { options[property] = [...(options[property] || []), ...s[property]]; } else if (stringValueProperty) { options[`${property}s`] = [...(options[`${property}s`] || []), s[property]]; } else if (enableBarSeriesSpecialCases && property === 'showInLegend') { if (s[property] === false) { options.hideInLegend = [...(options.hideInLegend || []), ...(s.yKey ? [s.yKey] : s.yKeys)]; } } else if (enableBarSeriesSpecialCases && property === 'grouped') { if (s[property] === true) { options[property] = s[property]; } } else { options[property] = s[property]; } } } return options; } exports.reduceSeries = reduceSeries; /** * Transforms provided series options array into an array containing series options which are compatible with standalone charts series options. */ function processSeriesOptions(seriesOptions) { const result = []; for (const series of groupSeriesByType(seriesOptions)) { switch (series[0].type) { case 'column': case 'bar': result.push(reduceSeries(series, true)); break; case 'area': result.push(reduceSeries(series, false)); break; case 'line': default: if (series.length > 1) { throw new Error('AG-Grid - unexpected grouping of series type: ' + series[0].type); } result.push(series[0]); break; } } return result; } exports.processSeriesOptions = processSeriesOptions; //# sourceMappingURL=agChart.js.map