UNPKG

apexcharts

Version:

A JavaScript Chart Library

154 lines (142 loc) 5.33 kB
// @ts-check import Config from './settings/Config' import Utils from '../utils/Utils' import CoreUtils from './CoreUtils' import { Environment } from '../utils/Environment.js' /** * Deep-merge a single responsive yaxis override onto the corresponding base * yaxis entry while ignoring keys whose value is `undefined`. CoreUtils * `extendArrayProps` populates the responsive `yaxis` entry with the full * default yAxis schema, including keys whose default value is `undefined` * (e.g. `min`, `max`, `title.text`). A plain `Utils.extend` would let those * undefined defaults overwrite explicit values on the base axis. * * @param {any} base * @param {any} override */ function mergeYaxisOverride(base, override) { if (!Utils.isObject(base) || !Utils.isObject(override)) { return override !== undefined ? override : base } const out = { ...base } for (const key of Object.keys(override)) { const v = override[key] if (v === undefined) continue if (Utils.isObject(v) && Utils.isObject(base[key])) { out[key] = mergeYaxisOverride(base[key], v) } else { out[key] = v } } return out } /** * ApexCharts Responsive Class to override options for different screen sizes. * * @module Responsive **/ export default class Responsive { /** * @param {import('../types/internal').ChartStateW} w */ constructor(w) { this.w = w /** @type {number | null} tracks which breakpoint is currently active (null = none) */ this._activeBreakpoint = null } // the opts parameter if not null has to be set overriding everything // as the opts is set by user externally /** * @param {object} opts */ checkResponsiveConfig(opts) { const w = this.w const cnf = w.config // check if responsive config exists if (cnf.responsive.length === 0) return const res = cnf.responsive.slice() res .sort( ( /** @type {{ breakpoint: number }} */ a, /** @type {{ breakpoint: number }} */ b, ) => a.breakpoint > b.breakpoint ? 1 : b.breakpoint > a.breakpoint ? -1 : 0, ) .reverse() const config = new Config({}) const iterateResponsiveOptions = (newOptions = {}) => { const largestBreakpoint = res[0].breakpoint const width = Environment.isBrowser() ? window.innerWidth > 0 ? window.innerWidth : screen.width : 0 if (width > largestBreakpoint) { // Above all breakpoints — only reset config if we were previously // inside a responsive breakpoint (fixes #2056: chart breaks on // updateSeries when viewport is above the largest breakpoint) if (this._activeBreakpoint !== null) { if (!w.globals.initialConfig) return const initialConfig = Utils.clone(w.globals.initialConfig) initialConfig.series = Utils.clone(w.config.series) const options = CoreUtils.extendArrayProps(config, initialConfig, w) // Merge onto a fresh object so w.config is not used as a base newOptions = Utils.extend(options, newOptions) this.overrideResponsiveOptions(newOptions) this._activeBreakpoint = null } } else { for (let i = 0; i < res.length; i++) { if (width < res[i].breakpoint) { // Capture the user-provided yaxis override BEFORE // CoreUtils.extendArrayProps fills it with default schema values, // so we can tell which keys the user actually set. const originalUserYaxis = res[i].options?.yaxis ? Utils.clone(res[i].options.yaxis) : null newOptions = CoreUtils.extendArrayProps(config, res[i].options, w) newOptions = Utils.extend(w.config, newOptions) // Utils.extend does not deep-merge arrays, so a responsive `yaxis` // array silently replaces the entire base `yaxis`, losing base // settings not re-declared in the responsive options. Re-merge // each yaxis entry with the corresponding base entry using only // keys the user explicitly set in the responsive options. if (Array.isArray(w.config.yaxis) && originalUserYaxis) { const userYaxis = Array.isArray(originalUserYaxis) ? originalUserYaxis : [originalUserYaxis] newOptions = { ...newOptions, yaxis: w.config.yaxis.map((baseAxis, idx) => mergeYaxisOverride(baseAxis, userYaxis[idx]), ), } } this.overrideResponsiveOptions(newOptions) this._activeBreakpoint = res[i].breakpoint } } } } if (opts) { let options = CoreUtils.extendArrayProps(config, opts, w) options = Utils.extend(w.config, options) options = Utils.extend(options, opts) iterateResponsiveOptions(options) } else { iterateResponsiveOptions({}) } } /** * @param {Record<string, any>} newOptions */ overrideResponsiveOptions(newOptions) { const newConfig = new Config(newOptions).init({ responsiveOverride: true }) this.w.config = /** @type {any} */ (newConfig) } }