mirror of
https://github.com/chartjs/Chart.js.git
synced 2026-03-12 11:16:51 +01:00
177 lines
5.0 KiB
JavaScript
177 lines
5.0 KiB
JavaScript
/* eslint-disable import/no-namespace, import/namespace */
|
|
import defaults from './core.defaults';
|
|
import {mergeIf, merge, _merger} from '../helpers/helpers.core';
|
|
|
|
export function getIndexAxis(type, options) {
|
|
const typeDefaults = defaults.controllers[type] || {};
|
|
const datasetDefaults = typeDefaults.datasets || {};
|
|
const typeOptions = options[type] || {};
|
|
const datasetOptions = typeOptions.datasets || {};
|
|
return datasetOptions.indexAxis || options.indexAxis || datasetDefaults.indexAxis || 'x';
|
|
}
|
|
|
|
function getAxisFromDefaultScaleID(id, indexAxis) {
|
|
let axis = id;
|
|
if (id === '_index_') {
|
|
axis = indexAxis;
|
|
} else if (id === '_value_') {
|
|
axis = indexAxis === 'x' ? 'y' : 'x';
|
|
}
|
|
return axis;
|
|
}
|
|
|
|
function getDefaultScaleIDFromAxis(axis, indexAxis) {
|
|
return axis === indexAxis ? '_index_' : '_value_';
|
|
}
|
|
|
|
function axisFromPosition(position) {
|
|
if (position === 'top' || position === 'bottom') {
|
|
return 'x';
|
|
}
|
|
if (position === 'left' || position === 'right') {
|
|
return 'y';
|
|
}
|
|
}
|
|
|
|
export function determineAxis(id, scaleOptions) {
|
|
if (id === 'x' || id === 'y' || id === 'r') {
|
|
return id;
|
|
}
|
|
return scaleOptions.axis || axisFromPosition(scaleOptions.position) || id.charAt(0).toLowerCase();
|
|
}
|
|
|
|
function mergeScaleConfig(config, options) {
|
|
options = options || {};
|
|
const chartDefaults = defaults.controllers[config.type] || {scales: {}};
|
|
const configScales = options.scales || {};
|
|
const chartIndexAxis = getIndexAxis(config.type, options);
|
|
const firstIDs = Object.create(null);
|
|
const scales = Object.create(null);
|
|
|
|
// First figure out first scale id's per axis.
|
|
Object.keys(configScales).forEach(id => {
|
|
const scaleConf = configScales[id];
|
|
const axis = determineAxis(id, scaleConf);
|
|
const defaultId = getDefaultScaleIDFromAxis(axis, chartIndexAxis);
|
|
firstIDs[axis] = firstIDs[axis] || id;
|
|
scales[id] = mergeIf(Object.create(null), [{axis}, scaleConf, chartDefaults.scales[axis], chartDefaults.scales[defaultId]]);
|
|
});
|
|
|
|
// Backward compatibility
|
|
if (options.scale) {
|
|
scales[options.scale.id || 'r'] = mergeIf(Object.create(null), [{axis: 'r'}, options.scale, chartDefaults.scales.r]);
|
|
firstIDs.r = firstIDs.r || options.scale.id || 'r';
|
|
}
|
|
|
|
// Then merge dataset defaults to scale configs
|
|
config.data.datasets.forEach(dataset => {
|
|
const type = dataset.type || config.type;
|
|
const indexAxis = dataset.indexAxis || getIndexAxis(type, options);
|
|
const datasetDefaults = defaults.controllers[type] || {};
|
|
const defaultScaleOptions = datasetDefaults.scales || {};
|
|
Object.keys(defaultScaleOptions).forEach(defaultID => {
|
|
const axis = getAxisFromDefaultScaleID(defaultID, indexAxis);
|
|
const id = dataset[axis + 'AxisID'] || firstIDs[axis] || axis;
|
|
scales[id] = scales[id] || Object.create(null);
|
|
mergeIf(scales[id], [{axis}, configScales[id], defaultScaleOptions[defaultID]]);
|
|
});
|
|
});
|
|
|
|
// apply scale defaults, if not overridden by dataset defaults
|
|
Object.keys(scales).forEach(key => {
|
|
const scale = scales[key];
|
|
mergeIf(scale, [defaults.scales[scale.type], defaults.scale]);
|
|
});
|
|
|
|
return scales;
|
|
}
|
|
|
|
/**
|
|
* Recursively merge the given config objects as the root options by handling
|
|
* default scale options for the `scales` and `scale` properties, then returns
|
|
* a deep copy of the result, thus doesn't alter inputs.
|
|
*/
|
|
function mergeConfig(...args/* config objects ... */) {
|
|
return merge(Object.create(null), args, {
|
|
merger(key, target, source, options) {
|
|
if (key !== 'scales' && key !== 'scale' && key !== 'controllers') {
|
|
_merger(key, target, source, options);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function includeDefaults(config, options) {
|
|
const scaleConfig = mergeScaleConfig(config, options);
|
|
options = mergeConfig(
|
|
defaults,
|
|
defaults.controllers[config.type],
|
|
options || {});
|
|
|
|
options.hover = merge(Object.create(null), [
|
|
defaults.interaction,
|
|
defaults.hover,
|
|
options.interaction,
|
|
options.hover
|
|
]);
|
|
|
|
options.scales = scaleConfig;
|
|
|
|
options.title = (options.title !== false) && merge(Object.create(null), [
|
|
defaults.plugins.title,
|
|
options.title
|
|
]);
|
|
options.tooltips = (options.tooltips !== false) && merge(Object.create(null), [
|
|
defaults.interaction,
|
|
defaults.plugins.tooltip,
|
|
options.interaction,
|
|
options.tooltips
|
|
]);
|
|
return options;
|
|
}
|
|
|
|
function initConfig(config) {
|
|
config = config || {};
|
|
|
|
// Do NOT use mergeConfig for the data object because this method merges arrays
|
|
// and so would change references to labels and datasets, preventing data updates.
|
|
const data = config.data = config.data || {datasets: [], labels: []};
|
|
data.datasets = data.datasets || [];
|
|
data.labels = data.labels || [];
|
|
|
|
config.options = includeDefaults(config, config.options);
|
|
|
|
return config;
|
|
}
|
|
|
|
export default class Config {
|
|
constructor(config) {
|
|
this._config = initConfig(config);
|
|
}
|
|
|
|
get type() {
|
|
return this._config.type;
|
|
}
|
|
|
|
get data() {
|
|
return this._config.data;
|
|
}
|
|
|
|
set data(data) {
|
|
this._config.data = data;
|
|
}
|
|
|
|
get options() {
|
|
return this._config.options;
|
|
}
|
|
|
|
get plugins() {
|
|
return this._config.plugins;
|
|
}
|
|
|
|
update(options) {
|
|
const config = this._config;
|
|
config.options = includeDefaults(config, options);
|
|
}
|
|
}
|