(function (g, f) { if ("object" == typeof exports && "object" == typeof module) { module.exports = f(); } else if ("function" == typeof define && define.amd) { define("agCharts", [], f); } else if ("object" == typeof exports) { exports["agCharts"] = f(); } else { g["agCharts"] = f(); } }(this, () => { var exports = {}; var module = { exports }; if (typeof require === 'undefined') { function require(name) { throw new Error('Unknown module: ' + name); } } "use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from3, except, desc) => { if (from3 && typeof from3 === "object" || typeof from3 === "function") { for (let key of __getOwnPropNames(from3)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from3[key], enumerable: !(desc = __getOwnPropDesc(from3, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __decorateClass = (decorators, target, key, kind) => { var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target; for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result; if (kind && result) __defProp(target, key, result); return result; }; // packages/ag-charts-community/src/main-umd.ts var main_umd_exports = {}; __export(main_umd_exports, { AG_CHARTS_LOCALE_EN_US: () => AG_CHARTS_LOCALE_EN_US, AgCharts: () => AgCharts, AgChartsCommunityModule: () => AgChartsCommunityModule, AgTooltipAnchorToType: () => AgTooltipAnchorToType, AgTooltipPlacementType: () => AgTooltipPlacementType, AllCartesianAxesModule: () => AllCartesianAxesModule, AllCartesianModule: () => AllCartesianModule, AllCartesianSeriesModule: () => AllCartesianSeriesModule, AllCommunityModule: () => AllCommunityModule, AllPolarModule: () => AllPolarModule, AreaSeriesModule: () => AreaSeriesModule, BarSeriesModule: () => BarSeriesModule, BubbleSeriesModule: () => BubbleSeriesModule, CartesianChartModule: () => CartesianChartModule, CategoryAxisModule: () => CategoryAxisModule, DonutSeriesModule: () => DonutSeriesModule, GroupedCategoryAxisModule: () => GroupedCategoryAxisModule, HistogramSeriesModule: () => HistogramSeriesModule, LegendModule: () => LegendModule, LineSeriesModule: () => LineSeriesModule, LocaleModule: () => LocaleModule, LogAxisModule: () => LogAxisModule, ModuleRegistry: () => moduleRegistry_exports, NumberAxisModule: () => NumberAxisModule, PieSeriesModule: () => PieSeriesModule, PolarChartModule: () => PolarChartModule, ScatterSeriesModule: () => ScatterSeriesModule, SparklinePresetModule: () => SparklinePresetModule, TimeAxisModule: () => TimeAxisModule, UnitTimeAxisModule: () => UnitTimeAxisModule, VERSION: () => VERSION, _ModuleSupport: () => module_support_exports, _Scene: () => integrated_charts_scene_exports, _Theme: () => integrated_charts_theme_exports, _Util: () => integrated_charts_util_exports, _Widget: () => exports_exports, time: () => time2 }); module.exports = __toCommonJS(main_umd_exports); // packages/ag-charts-core/src/modules/moduleDefinition.ts var ModuleType = /* @__PURE__ */ ((ModuleType2) => { ModuleType2["Chart"] = "chart"; ModuleType2["Axis"] = "axis"; ModuleType2["Series"] = "series"; ModuleType2["Plugin"] = "plugin"; ModuleType2["AxisPlugin"] = "axis:plugin"; ModuleType2["SeriesPlugin"] = "series:plugin"; ModuleType2["Preset"] = "preset"; return ModuleType2; })(ModuleType || {}); // packages/ag-charts-core/src/types/scales.ts function extractDomain(value) { return value.domain; } var ScaleAlignment = /* @__PURE__ */ ((ScaleAlignment2) => { ScaleAlignment2[ScaleAlignment2["Leading"] = 0] = "Leading"; ScaleAlignment2[ScaleAlignment2["Trailing"] = 1] = "Trailing"; ScaleAlignment2[ScaleAlignment2["Interpolate"] = 2] = "Interpolate"; return ScaleAlignment2; })(ScaleAlignment || {}); // packages/ag-charts-core/src/structures/eventEmitter.ts var EventEmitter = class { constructor() { this.events = /* @__PURE__ */ new Map(); } /** * Registers an event listener. * @param eventName The event name to listen for. * @param listener The callback to be invoked on the event. * @returns A function to unregister the listener. */ on(eventName, listener) { if (!this.events.has(eventName)) { this.events.set(eventName, /* @__PURE__ */ new Set()); } this.events.get(eventName)?.add(listener); return () => this.off(eventName, listener); } /** * Unregisters an event listener. * @param eventName The event name to stop listening for. * @param listener The callback to be removed. */ off(eventName, listener) { const eventListeners = this.events.get(eventName); if (eventListeners) { eventListeners.delete(listener); if (eventListeners.size === 0) { this.events.delete(eventName); } } } /** * Emits an event to all registered listeners. * @param eventName The name of the event to emit. * @param event The event payload. */ emit(eventName, event) { const listeners = this.events.get(eventName); if (listeners) { for (const callback2 of listeners) { callback2(event); } } } /** * Clears all listeners for a specific event or all events if no event name is provided. * @param eventName (Optional) The name of the event to clear listeners for. If not provided, all listeners for all events are cleared. */ clear(eventName) { if (eventName) { this.events.delete(eventName); } else { this.events.clear(); } } }; // packages/ag-charts-core/src/structures/lruCache.ts var LRUCache = class { constructor(maxCacheSize) { this.maxCacheSize = maxCacheSize; this.store = /* @__PURE__ */ new Map(); if (maxCacheSize <= 0) { throw new Error("LRUCache size must be greater than 0"); } } get(key) { if (!this.store.has(key)) return; const value = this.store.get(key); this.store.delete(key); this.store.set(key, value); return value; } has(key) { return this.store.has(key); } set(key, value) { this.store.set(key, value); if (this.store.size > this.maxCacheSize) { this.store.delete(this.store.keys().next().value); } return value; } clear() { this.store.clear(); } }; // packages/ag-charts-core/src/logging/debugLogger.ts var debugLogger_exports = {}; __export(debugLogger_exports, { Time: () => Time, check: () => check, create: () => create, inDevelopmentMode: () => inDevelopmentMode }); // packages/ag-charts-core/src/utils/data/arrays.ts function toArray(value) { if (value === void 0) { return []; } return Array.isArray(value) ? value : [value]; } function unique(array2) { return Array.from(new Set(array2)); } function groupBy(array2, iteratee) { return array2.reduce((result, item) => { const groupKey = iteratee(item); result[groupKey] ?? (result[groupKey] = []); result[groupKey].push(item); return result; }, {}); } function arraysEqual(a, b) { if (a == null || b == null || a.length !== b.length) { return false; } for (let i = 0; i < a.length; i++) { if (Array.isArray(a[i]) && Array.isArray(b[i])) { if (!arraysEqual(a[i], b[i])) { return false; } } else if (a[i] !== b[i]) { return false; } } return true; } function circularSliceArray(data, size, offset = 0) { if (data.length === 0) { return []; } const result = []; for (let i = 0; i < size; i++) { result.push(data.at((i + offset) % data.length)); } return result; } function sortBasedOnArray(baseArray, orderArray) { const orderMap = /* @__PURE__ */ new Map(); for (const [index, item] of orderArray.entries()) { orderMap.set(item, index); } return baseArray.sort((a, b) => { const indexA = orderMap.get(a) ?? Infinity; const indexB = orderMap.get(b) ?? Infinity; return indexA - indexB; }); } function dropFirstWhile(array2, cond) { let i = 0; while (i < array2.length && cond(array2[i])) { i += 1; } const deleteCount = i; if (deleteCount !== 0) array2.splice(0, deleteCount); } function dropLastWhile(array2, cond) { let i = array2.length - 1; while (i >= 0 && cond(array2[i])) { i -= 1; } const deleteCount = array2.length - 1 - i; if (deleteCount !== 0) array2.splice(array2.length - deleteCount, deleteCount); } function distribute(min, max, maxCount) { const values = [min]; const step = Math.round((max - min) / (maxCount - 1)); if (step > 0) { for (let i = min + step; i < max; i += step) { const length2 = values.push(i); if (length2 >= maxCount - 1) break; } } values.push(max); return values; } // packages/ag-charts-core/src/utils/dom/globalsProxy.ts var verifiedGlobals = {}; if (typeof globalThis.window !== "undefined") { verifiedGlobals.window = globalThis.window; } if (typeof document !== "undefined") { verifiedGlobals.document = document; } else if (typeof globalThis.global !== "undefined") { verifiedGlobals.document = globalThis.document; } function getDocument(propertyName) { return propertyName ? verifiedGlobals.document?.[propertyName] : verifiedGlobals.document; } function getWindow(propertyName) { return propertyName ? verifiedGlobals.window?.[propertyName] : verifiedGlobals.window; } function setDocument(document2) { verifiedGlobals.document = document2; } function setWindow(window) { verifiedGlobals.window = window; } function getOffscreenCanvas() { return verifiedGlobals.window?.OffscreenCanvas ?? globalThis.OffscreenCanvas; } function getPath2D() { return verifiedGlobals.window?.Path2D ?? globalThis.Path2D; } function getDOMMatrix() { return verifiedGlobals.window?.DOMMatrix ?? globalThis.DOMMatrix; } function getImage() { return verifiedGlobals.window?.Image ?? globalThis.Image; } function getResizeObserver() { return verifiedGlobals.window?.ResizeObserver ?? globalThis.ResizeObserver; } var ELEMENT_NODE = 1; var DOCUMENT_FRAGMENT_NODE = 11; function isNode(obj) { return obj != null && typeof obj.nodeType === "number"; } function isElement(obj) { return obj != null && obj.nodeType === ELEMENT_NODE; } function isDocumentFragment(obj) { return obj != null && obj.nodeType === DOCUMENT_FRAGMENT_NODE; } function isHTMLElement(obj) { return obj != null && obj.nodeType === ELEMENT_NODE && "style" in obj; } // packages/ag-charts-core/src/logging/logger.ts var logger_exports = {}; __export(logger_exports, { error: () => error, errorOnce: () => errorOnce, log: () => log, logGroup: () => logGroup, reset: () => reset, table: () => table, warn: () => warn, warnOnce: () => warnOnce }); var doOnceCache = /* @__PURE__ */ new Set(); function log(...logContent) { console.log(...logContent); } function warn(message, ...logContent) { console.warn(`AG Charts - ${message}`, ...logContent); } function error(message, ...logContent) { if (typeof message === "object") { console.error(`AG Charts error`, message, ...logContent); } else { console.error(`AG Charts - ${message}`, ...logContent); } } function table(...logContent) { console.table(...logContent); } function guardOnce(messageOrError, prefix, cb) { let message; if (messageOrError instanceof Error) { message = messageOrError.message; } else if (typeof messageOrError === "string") { message = messageOrError; } else if (typeof messageOrError === "object") { message = JSON.stringify(messageOrError); } else { message = String(messageOrError); } const cacheKey = `${prefix}: ${message}`; if (doOnceCache.has(cacheKey)) return; cb(messageOrError); doOnceCache.add(cacheKey); } function warnOnce(messageOrError, ...logContent) { guardOnce(messageOrError, "Logger.warn", (message) => warn(message, ...logContent)); } function errorOnce(messageOrError, ...logContent) { guardOnce(messageOrError, "Logger.error", (message) => error(message, ...logContent)); } function reset() { doOnceCache.clear(); } function logGroup(name, cb) { console.groupCollapsed(name); try { return cb(); } finally { console.groupEnd(); } } // packages/ag-charts-core/src/logging/debugLogger.ts var LongTimePeriodThreshold = 2e3; var timeOfLastLog = Date.now(); function logTimeGap() { const timeSinceLastLog = Date.now() - timeOfLastLog; if (timeSinceLastLog > LongTimePeriodThreshold) { const prettyDuration = (Math.floor(timeSinceLastLog / 100) / 10).toFixed(1); log(`**** ${prettyDuration}s since last log message ****`); } timeOfLastLog = Date.now(); } function create(...debugSelectors) { const resultFn = (...logContent) => { if (check(...debugSelectors)) { if (typeof logContent[0] === "function") { logContent = toArray(logContent[0]()); } logTimeGap(); log(...logContent); } }; return Object.assign(resultFn, { check: () => check(...debugSelectors), group: (name, cb) => { if (check(...debugSelectors)) { return logGroup(name, cb); } return cb(); } }); } function check(...debugSelectors) { if (debugSelectors.length === 0) { debugSelectors.push(true); } const chartDebug = toArray(getWindow("agChartsDebug")); return chartDebug.some((selector) => debugSelectors.includes(selector)); } function inDevelopmentMode(fn) { if (check("dev")) { return fn(); } } function Time(name, opts = {}) { const { logResult = true, logStack = false, logArgs = false, logData } = opts; return function(_target, _propertyKey, descriptor) { const method = descriptor.value; descriptor.value = function(...args) { const start2 = performance.now(); const result = method.apply(this, args); const duration = performance.now() - start2; const logMessage = { duration }; if (logResult) logMessage.result = result; if (logArgs) logMessage.args = args; if (logStack) logMessage.stack = new Error("Stack trace for timing debug").stack; if (logData) logMessage.logData = logData(this); log(name, logMessage); return result; }; }; } // packages/ag-charts-core/src/logging/debugMetrics.ts var debugMetrics_exports = {}; __export(debugMetrics_exports, { flush: () => flush, record: () => record }); var metrics = /* @__PURE__ */ new Map(); function record(key, value) { if (!check("scene:stats:verbose")) return; metrics.set(key, value); } function flush() { const result = Object.fromEntries(metrics); metrics.clear(); return result; } // packages/ag-charts-core/src/modules/enterpriseRegistry.ts var enterpriseRegistry = {}; // packages/ag-charts-core/src/modules/moduleRegistry.ts var moduleRegistry_exports = {}; __export(moduleRegistry_exports, { RegistryMode: () => RegistryMode, getAxisModule: () => getAxisModule, getChartModule: () => getChartModule, getPresetModule: () => getPresetModule, getSeriesModule: () => getSeriesModule, hasModule: () => hasModule, isEnterprise: () => isEnterprise, isIntegrated: () => isIntegrated, isModuleType: () => isModuleType, isUmd: () => isUmd, listModules: () => listModules, listModulesByType: () => listModulesByType, register: () => register, registerModules: () => registerModules, reset: () => reset2, setRegistryMode: () => setRegistryMode }); var RegistryMode = /* @__PURE__ */ ((RegistryMode2) => { RegistryMode2["Enterprise"] = "enterprise"; RegistryMode2["Integrated"] = "integrated"; RegistryMode2["UMD"] = "umd"; return RegistryMode2; })(RegistryMode || {}); var registeredModes = /* @__PURE__ */ new Set(); var registeredModules = /* @__PURE__ */ new Map(); function registerModuleDefinition(def) { registeredModules.set(def.name, def); if (def.dependencies) { for (const dependency of def.dependencies) { register(dependency); } } } function register(def) { const existingDefinition = registeredModules.get(def.name); if (!existingDefinition) { registerModuleDefinition(def); return; } if (existingDefinition.version === def.version) { if (!existingDefinition.enterprise && def.enterprise) { registerModuleDefinition(def); } return; } throw new Error( [ `AG Charts - Module '${def.name}' already registered with different version:`, `${existingDefinition.version} vs ${def.version}`, ``, `Check your package.json for conflicting dependencies - depending on your package manager`, `one of these commands may help:`, `- npm ls ag-charts-community`, `- yarn why ag-charts-community` ].join("\n") ); } function registerModules(definitions) { for (const definition of definitions.flat()) { register(definition); } } function reset2() { registeredModes.clear(); registeredModules.clear(); } function hasModule(moduleName) { return registeredModules.has(moduleName); } function* listModules() { for (const definition of registeredModules.values()) { yield definition; } } function* listModulesByType(moduleType) { for (const definition of registeredModules.values()) { if (isModuleType(moduleType, definition)) { yield definition; } } } function getAxisModule(moduleName) { const definition = registeredModules.get(moduleName); if (isModuleType("axis" /* Axis */, definition)) { return definition; } } function getChartModule(moduleName) { const definition = registeredModules.get(moduleName); if (isModuleType("chart" /* Chart */, definition)) { return definition; } throw new Error( `AG Charts - Unknown chart type; Check options are correctly structured and series types are specified` ); } function getPresetModule(moduleName) { const definition = registeredModules.get(moduleName); if (isModuleType("preset" /* Preset */, definition)) { return definition; } } function getSeriesModule(moduleName) { const definition = registeredModules.get(moduleName); if (isModuleType("series" /* Series */, definition)) { return definition; } } function setRegistryMode(registryFlag) { registeredModes.add(registryFlag); } function isEnterprise() { return registeredModes.has("enterprise" /* Enterprise */); } function isIntegrated() { return registeredModes.has("integrated" /* Integrated */); } function isUmd() { return registeredModes.has("umd" /* UMD */); } function isModuleType(moduleType, definition) { return definition?.type === moduleType; } // packages/ag-charts-core/src/state/cleanupRegistry.ts var CleanupRegistry = class { constructor() { this.callbacks = /* @__PURE__ */ new Set(); } flush() { for (const cb of this.callbacks) { cb(); } this.callbacks.clear(); } merge(registry) { for (const cb of registry.callbacks) { this.callbacks.add(cb); } } register(...callbacks) { for (const cb of callbacks) { if (!cb) continue; this.callbacks.add(cb); } } }; // packages/ag-charts-core/src/modules/moduleInstance.ts var AbstractModuleInstance = class { constructor() { this.cleanup = new CleanupRegistry(); } destroy() { this.cleanup.flush(); } }; // packages/ag-charts-types/src/chart/navigatorOptions.ts var __MINI_CHART_SERIES_OPTIONS = void 0; var __VERIFY_MINI_CHART_SERIES_OPTIONS = void 0; __VERIFY_MINI_CHART_SERIES_OPTIONS = __MINI_CHART_SERIES_OPTIONS; // packages/ag-charts-types/src/chart/themeOptions.ts var __THEME_OVERRIDES = void 0; var __VERIFY_THEME_OVERRIDES = void 0; __VERIFY_THEME_OVERRIDES = __THEME_OVERRIDES; // packages/ag-charts-types/src/chart/tooltipOptions.ts var AgTooltipAnchorToType = /* @__PURE__ */ ((AgTooltipAnchorToType2) => { AgTooltipAnchorToType2["POINTER"] = "pointer"; AgTooltipAnchorToType2["NODE"] = "node"; AgTooltipAnchorToType2["CHART"] = "chart"; return AgTooltipAnchorToType2; })(AgTooltipAnchorToType || {}); var AgTooltipPlacementType = /* @__PURE__ */ ((AgTooltipPlacementType2) => { AgTooltipPlacementType2["TOP"] = "top"; AgTooltipPlacementType2["RIGHT"] = "right"; AgTooltipPlacementType2["BOTTOM"] = "bottom"; AgTooltipPlacementType2["LEFT"] = "left"; AgTooltipPlacementType2["TOP_RIGHT"] = "top-right"; AgTooltipPlacementType2["BOTTOM_RIGHT"] = "bottom-right"; AgTooltipPlacementType2["BOTTOM_LEFT"] = "bottom-left"; AgTooltipPlacementType2["TOP_LEFT"] = "top-left"; AgTooltipPlacementType2["CENTER"] = "center"; return AgTooltipPlacementType2; })(AgTooltipPlacementType || {}); // packages/ag-charts-types/src/presets/gauge/commonOptions.ts var __THEMEABLE_OPTIONS = void 0; var __VERIFY_THEMEABLE_OPTIONS = void 0; __VERIFY_THEMEABLE_OPTIONS = __THEMEABLE_OPTIONS; var __AXIS_LABEL_OPTIONS = void 0; var __VERIFY_AXIS_LABEL_OPTIONS = void 0; __VERIFY_AXIS_LABEL_OPTIONS = __AXIS_LABEL_OPTIONS; // packages/ag-charts-core/src/types/text.ts var EllipsisChar = "\u2026"; var LineSplitter = /\r?\n/g; var TrimEdgeGuard = "\u200B"; var TrimCharsRegex = /[\s.,;:-]{1,5}$/; // packages/ag-charts-core/src/utils/dom/domUtils.ts var style; function parseColor(color2) { if (style == null) { const OptionConstructor = getWindow("Option"); style = new OptionConstructor().style; } style.color = color2; const result = style.color || null; style.color = ""; return result; } // packages/ag-charts-core/src/utils/types/typeGuards.ts function isDefined(val) { return val != null; } function isArray(value) { return Array.isArray(value); } function isBoolean(value) { return typeof value === "boolean"; } function isDate(value) { return value instanceof Date; } function isValidDate(value) { return isDate(value) && !Number.isNaN(Number(value)); } function isRegExp(value) { return value instanceof RegExp; } function isFunction(value) { return typeof value === "function"; } function isObject(value) { return typeof value === "object" && value !== null && !isArray(value); } function isObjectLike(value) { return isArray(value) || isPlainObject(value); } function isPlainObject(value) { return typeof value === "object" && value !== null && value.constructor?.name === "Object"; } function isEmptyObject(value) { if (typeof value !== "object" || value === null) return false; for (const _ in value) { return false; } return true; } function isString(value) { return typeof value === "string"; } function isNumber(value) { return typeof value === "number"; } function isFiniteNumber(value) { return Number.isFinite(value); } function isHtmlElement(value) { return value != null && value.nodeType === 1 && "style" in value; } function isEnumKey(enumObject, enumKey) { return isString(enumKey) && Object.keys(enumObject).includes(enumKey); } function isEnumValue(enumObject, enumValue) { return Object.values(enumObject).includes(enumValue); } function isSymbol(value) { return typeof value === "symbol"; } function isColor(value) { return isString(value) && (value === "none" || parseColor(value) != null); } function isKeyOf(value, container) { return value in container; } // packages/ag-charts-core/src/utils/text/textUtils.ts function toFontString({ fontSize, fontStyle, fontWeight: fontWeight2, fontFamily }) { let fontString = ""; if (fontStyle && fontStyle !== "normal") { fontString += `${fontStyle} `; } if (fontWeight2 && fontWeight2 !== "normal" && fontWeight2 !== 400) { fontString += `${fontWeight2} `; } fontString += `${fontSize}px`; fontString += ` ${fontFamily}`; return fontString; } function calcLineHeight(fontSize, lineHeightRatio = 1.15) { return Math.round(fontSize * lineHeightRatio); } function toTextString(value) { return String(value ?? ""); } function appendEllipsis(text) { return text.replace(TrimCharsRegex, "") + EllipsisChar; } function guardTextEdges(str) { return TrimEdgeGuard + str + TrimEdgeGuard; } function unguardTextEdges(str) { return str.replaceAll(TrimEdgeGuard, ""); } function isTruncated(value) { return isArray(value) ? isSegmentTruncated(value.at(-1)) : isTextTruncated(toTextString(value)); } function isTextTruncated(str) { return str.endsWith(EllipsisChar); } function isSegmentTruncated(segment) { return toTextString(segment?.text).endsWith(EllipsisChar); } // packages/ag-charts-core/src/utils/data/strings.ts function joinFormatted(values, conjunction = "and", format = String, maxItems = Infinity) { if (values.length === 0) { return ""; } else if (values.length === 1) { return format(values[0]); } values = values.map(format); const lastValue = values.pop(); if (values.length >= maxItems) { const remainingCount = values.length - (maxItems - 1); return `${values.slice(0, maxItems - 1).join(", ")}, and ${remainingCount} more ${conjunction} ${lastValue}`; } return `${values.join(", ")} ${conjunction} ${lastValue}`; } function stringifyValue(value, maxLength = Infinity) { if (typeof value === "number") { if (Number.isNaN(value)) { return "NaN"; } else if (value === Infinity) { return "Infinity"; } else if (value === -Infinity) { return "-Infinity"; } } const strValue = JSON.stringify(value) ?? typeof value; if (strValue.length > maxLength) { return `${strValue.slice(0, maxLength)}... (+${strValue.length - maxLength} characters)`; } return strValue; } function countLines(text) { let count = 1; for (let i = 0; i < text.length; i++) { if (text.codePointAt(i) === 10) { count++; } } return count; } function levenshteinDistance(a, b) { if (a === b) return 0; const [shorter, longer] = a.length < b.length ? [a, b] : [b, a]; const m = shorter.length; const n = longer.length; let prevRow = new Array(m + 1).fill(0).map((_, i) => i); let currRow = new Array(m + 1); for (let i = 1; i <= n; i++) { currRow[0] = i; for (let j = 1; j <= m; j++) { const cost = longer[i - 1] === shorter[j - 1] ? 0 : 1; currRow[j] = Math.min( prevRow[j] + 1, // Deletion currRow[j - 1] + 1, // Insertion prevRow[j - 1] + cost // Substitution ); } [prevRow, currRow] = [currRow, prevRow]; } return prevRow[m]; } function kebabCase(a) { return a.replaceAll(KEBAB_CASE_REGEX, (match, offset) => (offset > 0 ? "-" : "") + match.toLowerCase()); } var KEBAB_CASE_REGEX = /[A-Z]+(?![a-z])|[A-Z]/g; function toPlainText(text, fallback = "") { if (text == null) { return fallback; } else if (isArray(text)) { return text.map((segment) => toTextString(segment.text)).join(""); } else if (isString(text)) { return text; } else { return String(text); } } // packages/ag-charts-core/src/utils/functions.ts function debounce(callback2, waitMs = 0, options) { const { leading = false, trailing = true, maxWait = Infinity } = options ?? {}; let timerId; let startTime; if (maxWait < waitMs) { throw new Error("Value of maxWait cannot be lower than waitMs."); } function debounceCallback(...args) { if (leading && !startTime) { startTime = Date.now(); timerId = setTimeout(() => startTime = null, waitMs); callback2(...args); return; } let adjustedWaitMs = waitMs; if (maxWait !== Infinity && startTime) { const elapsedTime = Date.now() - startTime; if (waitMs > maxWait - elapsedTime) { adjustedWaitMs = maxWait - elapsedTime; } } clearTimeout(timerId); startTime ?? (startTime = Date.now()); timerId = setTimeout(() => { startTime = null; if (trailing) { callback2(...args); } }, adjustedWaitMs); } return Object.assign(debounceCallback, { cancel() { clearTimeout(timerId); startTime = null; } }); } function throttle(callback2, waitMs, options) { const { leading = true, trailing = true } = options ?? {}; let timerId; let lastArgs; let shouldWait = false; function timeoutHandler() { if (trailing && lastArgs) { timerId = setTimeout(timeoutHandler, waitMs); callback2(...lastArgs); } else { shouldWait = false; } lastArgs = null; } function throttleCallback(...args) { if (shouldWait) { lastArgs = args; } else { shouldWait = true; timerId = setTimeout(timeoutHandler, waitMs); if (leading) { callback2(...args); } else { lastArgs = args; } } } return Object.assign(throttleCallback, { cancel() { clearTimeout(timerId); shouldWait = false; lastArgs = null; } }); } function safeCall(callback2, args, errorPath = "") { try { return callback2(...args); } catch (error2) { const postfix = errorPath ? ` \`${errorPath}\`` : ""; warnOnce(`Uncaught exception in user callback${postfix}`, error2); } } // packages/ag-charts-core/src/state/validation.ts var descriptionSymbol = Symbol("description"); var requiredSymbol = Symbol("required"); var markedSymbol = Symbol("marked"); var undocumentedSymbol = Symbol("undocumented"); var unionSymbol = Symbol("union"); var similarOptionsMap = [ ["placement", "position"], ["padding", "spacing", "gap"], ["color", "fill", "stroke"], ["whisker", "wick"], ["src", "url"], ["width", "thickness"] ].reduce((map, words) => { for (const word of words) { map.set(word.toLowerCase(), new Set(words.filter((w) => w !== word))); } return map; }, /* @__PURE__ */ new Map()); var ErrorType = /* @__PURE__ */ ((ErrorType2) => { ErrorType2["Invalid"] = "invalid"; ErrorType2["Required"] = "required"; ErrorType2["Unknown"] = "unknown"; return ErrorType2; })(ErrorType || {}); function extendPath(path, key) { if (isFiniteNumber(key)) { return `${path}[${key}]`; } return path ? `${path}.${key}` : key; } var ValidationError = class { constructor(type, description, value, path, key) { this.type = type; this.description = description; this.value = value; this.path = path; this.key = key; } setUnionType(unionType, path) { if (this.path.startsWith(path)) { const suffix = this.path.slice(path.length); this.altPath = `${path}[type=${unionType}]${suffix}`; } } getPrefix() { const { altPath: path = this.path, key } = this; if (!path && !key) return "Value"; return `Option \`${key ? extendPath(path, key) : path}\``; } toString() { const { description = "unknown", type, value } = this; if (type === "required" /* Required */ && value == null) { return `${this.getPrefix()} is required and has not been provided; expecting ${description}, ignoring.`; } return `${this.getPrefix()} cannot be set to \`${stringifyValue(value, 50)}\`; expecting ${description}, ignoring.`; } }; var UnknownError = class extends ValidationError { constructor(suggestions, value, path, key) { super("unknown" /* Unknown */, void 0, value, path, key); this.suggestions = suggestions; this.key = key; } getPrefix() { return `Unknown option \`${extendPath(this.altPath ?? this.path, this.key)}\``; } getPostfix() { const suggestions = joinFormatted(findSuggestions(this.key, this.suggestions), "or", (val) => `\`${val}\``); return suggestions ? `; Did you mean ${suggestions}? Ignoring.` : ", ignoring."; } toString() { return `${this.getPrefix()}${this.getPostfix()}`; } }; function validate(options, optionsDefs2, path = "") { if (!isObject(options)) { return { cleared: null, invalid: [new ValidationError("required" /* Required */, "an object", options, path)] }; } const cleared = {}; const invalid = []; const optionsKeys = new Set(Object.keys(options)); const unusedKeys = []; if (unionSymbol in optionsDefs2) { const validTypes = Object.keys(optionsDefs2); const defaultType = optionsDefs2[unionSymbol]; if (options.type != null && validTypes.includes(options.type) || options.type == null && defaultType != null) { const { type = defaultType, ...rest } = options; const nestedResult = validate(rest, optionsDefs2[type], path); Object.assign(cleared, { type }, nestedResult.cleared); for (const error2 of nestedResult.invalid) { error2.setUnionType(type, path); } invalid.push(...nestedResult.invalid); } else { const keywords = joinFormatted(validTypes, "or", (val) => `'${val}'`); invalid.push( new ValidationError("required" /* Required */, `a keyword such as ${keywords}`, options.type, path, "type") ); } return { cleared, invalid }; } for (const key of Object.keys(optionsDefs2)) { const validatorOrDefs = optionsDefs2[key]; const required3 = validatorOrDefs[requiredSymbol]; const value = options[key]; optionsKeys.delete(key); if (value === void 0) { if (!validatorOrDefs[undocumentedSymbol]) { unusedKeys.push(key); } if (!required3) continue; } const keyPath = extendPath(path, key); if (isFunction(validatorOrDefs)) { const context = { options, path: keyPath }; const validatorResult = validatorOrDefs(value, context); const objectResult = typeof validatorResult === "object"; if (objectResult) { invalid.push(...validatorResult.invalid); if (validatorResult.valid) { cleared[key] = validatorResult.cleared; continue; } else if (hasRequiredInPath(validatorResult.invalid, keyPath)) { continue; } } else if (validatorResult) { cleared[key] = value; continue; } invalid.push( new ValidationError( required3 ? "required" /* Required */ : "invalid" /* Invalid */, validatorOrDefs[descriptionSymbol], value, path, key ) ); } else { const nestedResult = validate(value, validatorOrDefs, keyPath); if (nestedResult.cleared != null) { cleared[key] = nestedResult.cleared; } invalid.push(...nestedResult.invalid); } } for (const key of optionsKeys) { const value = options[key]; if (value === void 0) continue; invalid.push(new UnknownError(unusedKeys, value, path, key)); } return { cleared, invalid }; } function findSuggestions(value, suggestions, maxDistance = 2) { const lowerCaseValue = value.toLowerCase(); const similarValues = similarOptionsMap.get(lowerCaseValue); return suggestions.filter((key) => { const lowerCaseKey = key.toLowerCase(); return similarValues?.has(key) === true || lowerCaseKey.includes(lowerCaseValue) || levenshteinDistance(lowerCaseValue, lowerCaseKey) <= maxDistance; }); } function attachDescription(validatorOrDefs, description) { if (isFunction(validatorOrDefs)) { let clonedValidator2 = function(value, context) { return validatorOrDefs(value, context); }; var clonedValidator = clonedValidator2; clonedValidator2[descriptionSymbol] = description; return clonedValidator2; } else { return { ...validatorOrDefs, [descriptionSymbol]: description }; } } function required(validatorOrDefs) { return Object.assign( isFunction(validatorOrDefs) ? (value, context) => validatorOrDefs(value, context) : optionsDefs(validatorOrDefs), { [requiredSymbol]: true, [descriptionSymbol]: validatorOrDefs[descriptionSymbol] } ); } function undocumented(validatorOrDefs) { return Object.assign( isFunction(validatorOrDefs) ? (value, context) => validatorOrDefs(value, context) : optionsDefs(validatorOrDefs), { [undocumentedSymbol]: true, [descriptionSymbol]: validatorOrDefs[descriptionSymbol] } ); } var optionsDefs = (defs, description = "an object", failAll = false) => attachDescription((value, context) => { const result = validate(value, defs, context.path); const valid = !hasRequiredInPath(result.invalid, context.path); return { valid, cleared: valid || !failAll ? result.cleared : null, invalid: result.invalid }; }, description); var typeUnion = (defs, description, defaultType) => ({ ...defs, [descriptionSymbol]: description, [unionSymbol]: defaultType }); var and = (...validators) => attachDescription( (value, context) => { const invalid = []; for (const validator of validators) { const result = validator(value, context); if (typeof result === "object") { invalid.push(...result.invalid); if (!result.valid) { return { valid: false, cleared: value, invalid }; } value = result.cleared; } else if (!result) { return false; } } return { valid: true, cleared: value, invalid }; }, joinFormatted( validators.filter((v) => !v[undocumentedSymbol]).map((v) => v[descriptionSymbol]).filter(isDefined), "and" ) ); var or = (...validators) => attachDescription( (value, context) => { for (const validator of validators) { const result = validator(value, context); if (typeof result === "object" ? result.valid : result) { return result; } } return false; }, joinFormatted( validators.filter((v) => !v[undocumentedSymbol]).map((v) => v[descriptionSymbol]).filter(isDefined), "or" ) ); var isComparable = (value) => isFiniteNumber(value) || isValidDate(value); var isValidDateValue = (value) => isDate(value) || (isFiniteNumber(value) || isString(value)) && isValidDate(new Date(value)); var array = attachDescription(isArray, "an array"); var boolean = attachDescription(isBoolean, "a boolean"); var callback = attachDescription(isFunction, "a function"); var color = attachDescription(isColor, "a color string"); var date = attachDescription(isValidDateValue, "a date"); var defined = attachDescription(isDefined, "a defined value"); var number = attachDescription(isFiniteNumber, "a number"); var object = attachDescription(isObject, "an object"); var string = attachDescription(isString, "a string"); var htmlElement = attachDescription(isHtmlElement, "an html element"); var arrayLength = (minLength, maxLength = Infinity) => { let message; if (maxLength === Infinity) { message = `an array of at least ${minLength} items`; } else if (minLength === maxLength) { message = `an array of exactly ${minLength} items`; } else if (minLength === 0) { message = `an array of no more than ${maxLength} items`; } else { message = `an array of at least ${minLength} and no more than ${maxLength} items`; } return attachDescription( (value) => isArray(value) && value.length >= minLength && value.length <= maxLength, message ); }; var stringLength = (minLength, maxLength = Infinity) => { let message; if (maxLength === Infinity) { message = `a string of at least ${minLength} characters`; } else if (minLength === maxLength) { message = `an string of exactly ${minLength} characters`; } else if (minLength === 0) { message = `an string of no more than ${maxLength} characters`; } else { message = `an string of at least ${minLength} and no more than ${maxLength} characters`; } return attachDescription( (value) => isString(value) && value.length >= minLength && value.length <= maxLength, message ); }; var numberMin = (min, inclusive = true) => attachDescription( (value) => isFiniteNumber(value) && (value > min || inclusive && value === min), `a number greater than ${inclusive ? "or equal to " : ""}${min}` ); var numberRange = (min, max) => attachDescription( (value) => isFiniteNumber(value) && value >= min && value <= max, `a number between ${min} and ${max} inclusive` ); var positiveNumber = numberMin(0); var positiveNumberNonZero = numberMin(0, false); var ratio = numberRange(0, 1); var lessThan = (otherField) => attachDescription( (value, { options }) => !isComparable(value) || !isComparable(options[otherField]) || value < options[otherField], `the value to be less than \`${otherField}\`` ); var lessThanOrEqual = (otherField) => attachDescription( (value, { options }) => !isComparable(value) || !isComparable(options[otherField]) || value <= options[otherField], `the value to be less than or equal to \`${otherField}\`` ); var greaterThan = (otherField) => attachDescription( (value, { options }) => !isComparable(value) || !isComparable(options[otherField]) || value > options[otherField], `the value to be greater than \`${otherField}\`` ); function union(...allowed) { if (isObject(allowed[0])) { allowed = Object.values(allowed[0]); } const keywords = joinFormatted(allowed, "or", (value) => `'${value}'`); return attachDescription((value) => allowed.includes(value), `a keyword such as ${keywords}`); } function strictUnion() { return union; } var constant = (allowed) => attachDescription((value) => allowed === value, `the value ${JSON.stringify(allowed)}`); var instanceOf = (instanceType, description) => attachDescription((value) => value instanceof instanceType, description ?? `an instance of ${instanceType.name}`); var arrayOf = (validator, description, strict = true) => attachDescription( (value, context) => { if (!isArray(value)) return false; let valid = strict; const cleared = []; const invalid = []; const updateValidity = (result) => { valid = strict ? valid && result : valid || result; }; if (value.length === 0) { return { valid: true, cleared, invalid }; } for (let i = 0; i < value.length; i++) { const options = value[i]; const result = validator(options, { options, path: `${context.path}[${i}]` }); if (typeof result === "object") { updateValidity(result.valid); invalid.push(...result.invalid); if (result.cleared != null) { cleared.push(result.cleared); } } else { updateValidity(result); if (result) { cleared.push(options); } } } return { valid, cleared: valid || !strict ? cleared : null, invalid }; }, description ?? `${validator[descriptionSymbol]} array` ); var arrayOfDefs = (defs, description = "an object array") => attachDescription((value, context) => { if (!isArray(value)) return false; const cleared = []; const invalid = []; for (let i = 0; i < value.length; i++) { const indexPath = `${context.path}[${i}]`; const result = validate(value[i], defs, indexPath); if (!hasRequiredInPath(result.invalid, indexPath)) { cleared.push(result.cleared); } invalid.push(...result.invalid); } return { valid: true, cleared, invalid }; }, description); var callbackOf = (validator, description) => attachDescription((value, context) => { if (!isFunction(value)) return false; if (markedSymbol in value) return true; const validatorDescription = description ?? validator[descriptionSymbol]; const cbWithValidation = Object.assign( (...args) => { const result = safeCall(value, args); if (result == null) return; const validatorResult = validator(result, { options: result, path: "" }); if (typeof validatorResult === "object") { warnCallbackErrors(validatorResult, context, validatorDescription, result); if (validatorResult.valid) { return validatorResult.cleared; } } else if (validatorResult) { return result; } else { warnOnce( `Callback \`${context.path}\` returned an invalid value \`${stringifyValue(result, 50)}\`; expecting ${validatorDescription}, ignoring.` ); } }, { [markedSymbol]: true } ); return { valid: true, cleared: cbWithValidation, invalid: [] }; }, "a function"); var callbackDefs = (defs, description = "an object") => attachDescription((value, context) => { if (!isFunction(value)) return false; if (markedSymbol in value) return true; const validatorDescription = description; const cbWithValidation = Object.assign( (...args) => { const result = safeCall(value, args, context.path); if (result == null) return; const validatorResult = validate(result, defs); warnCallbackErrors(validatorResult, context, validatorDescription, result); return validatorResult.cleared; }, { [markedSymbol]: true } ); return { valid: true, cleared: cbWithValidation, invalid: [] }; }, "a function"); function hasRequiredInPath(errors, rootPath) { return errors.some((error2) => error2.type === "required" /* Required */ && error2.path === rootPath); } function warnCallbackErrors(validatorResult, context, description, result) { if (validatorResult.invalid.length === 0) return; if (isArray(result)) { const expectedDescription = description ?? validatorResult.invalid[0]?.description ?? "a valid value"; return warnOnce( `Callback \`${context.path}\` returned an invalid value \`${stringifyValue(result, 50)}\`; expecting ${expectedDescription}, ignoring.` ); } for (const error2 of validatorResult.invalid) { if (error2 instanceof UnknownError) { return warnOnce( `Callback \`${context.path}\` returned an unknown property \`${extendPath(error2.path, error2.key)}\`${error2.getPostfix()}` ); } const errorValue = stringifyValue(error2.value, 50); warnOnce( error2.key ? `Callback \`${context.path}\` returned an invalid property \`${extendPath(error2.path, error2.key)}\`: \`${errorValue}\`; expecting ${error2.description}, ignoring.` : `Callback \`${context.path}\` returned an invalid value \`${errorValue}\`; expecting ${description ?? error2.description}, ignoring.` ); } } // packages/ag-charts-core/src/utils/data/numbers.ts function clamp(min, value, max) { return Math.min(max, Math.max(min, value)); } function inRange(value, range3, epsilon = 1e-10) { return value >= range3[0] - epsilon && value <= range3[1] + epsilon; } function isNumberEqual(a, b, epsilon = 1e-10) { return a === b || Math.abs(a - b) < epsilon; } function isNegative(value) { return Math.sign(value) === -1 || Object.is(value, -0); } function isInteger(value) { return value % 1 === 0; } function roundTo(value, decimals = 2) { const base = 10 ** decimals; return Math.round(value * base) / base; } function ceilTo(value, decimals = 2) { const base = 10 ** decimals; return Math.ceil(value * base) / base; } function modulus(n, m) { return Math.floor(n % m + (n < 0 ? Math.abs(m) : 0)); } function countFractionDigits(value) { if (Math.floor(value) === value) { return 0; } let valueString = String(value); let exponent = 0; if (value < 1e-6 || value >= 1e21) { let exponentString; [valueString, exponentString] = valueString.split("e"); if (exponentString != null) { exponent = Number(exponentString); } } const decimalPlaces2 = valueString.split(".")[1]?.length ?? 0; return Math.max(decimalPlaces2 - exponent, 0); } // packages/ag-charts-core/src/utils/format/numberFormat.ts var formatRegEx = /^(?:(.)?([<>=^]))?([+\-( ])?([$€£¥₣₹#])?(0)?(\d+)?(,)?(?:\.(\d+))?(~)?([%a-z])?$/i; var surroundedRegEx = /^((?:[^#]|#[^{])*)#{([^}]+)}(.*)$/; function isValidNumberFormat(value) { if (!isString(value)) return false; const match = surroundedRegEx.exec(value); return formatRegEx.test(match ? match[2] : value); } function parseNumberFormat(format) { let prefix; let suffix; const surrounded = surroundedRegEx.exec(format); if (surrounded) { [, prefix, format, suffix] = surrounded; } const match = formatRegEx.exec(format); if (!match) { warnOnce(`The number formatter is invalid: ${format}`); return; } const [, fill, align2, sign, symbol, zero, width2, comma, precision, trim, type] = match; return { fill, align: align2, sign, symbol, zero, width: Number.parseInt(width2), comma, precision: Number.parseInt(precision), trim: Boolean(trim), type, prefix, suffix }; } function createNumberFormatter(format) { const options = typeof format === "string" ? parseNumberFormat(format) : format; if (options == null) return; const { fill, align: align2, sign = "-", symbol, zero, width: width2, comma, type, prefix = "", suffix = "", precision } = options; let { trim } = options; const precisionIsNaN = precision == null || Number.isNaN(precision); let formatBody; if (!type) { formatBody = decimalTypes["g"]; trim = true; } else if (type in decimalTypes && type in integerTypes) { formatBody = precisionIsNaN ? integerTypes[type] : decimalTypes[type]; } else if (type in decimalTypes) { formatBody = decimalTypes[type]; } else if (type in integerTypes) { formatBody = integerTypes[type]; } else { throw new Error(`The number formatter type is invalid: ${type}`); } const defaultFormatterPrecision = type ? 6 : 12; let formatterPrecision; if (!precisionIsNaN) { formatterPrecision = precision; } let padAlign = align2; let padFill = fill; if (zero) { padFill ?? (padFill = "0"); padAlign ?? (padAlign = "="); } return (n, fractionDigits) => { let effectivePrecision; if (formatterPrecision != null) { effectivePrecision = formatterPrecision; } else if (type === "f" || type === "%") { effectivePrecision = fractionDigits ?? defaultFormatterPrecision; } else if (type) { effectivePrecision = defaultFormatterPrecision; } else { effectivePrecision = fractionDigits ?? defaultFormatterPrecision; } let result = formatBody(n, effectivePrecision); if (trim) { result = removeTrailingZeros(result); } if (comma) { result = insertSeparator(result, comma); } const symbolPrefix = getSymbolPrefix(symbol, type); const symbolPrefixLength = symbolPrefix?.length ?? 0; if (symbolPrefix) { result = `${symbolPrefix}${result}`; } if (type === "s") { result = `${result}${getSIPrefix(n)}`; } if (type === "%" || type === "p") { result = `${result}%`; } const { value: signedResult, prefixLength: signPrefixLength } = addSign(n, result, sign); const totalPrefixLength = signPrefixLength + symbolPrefixLength; let output = signedResult; if (width2 != null && !Number.isNaN(width2)) { output = addPadding(output, width2, padFill ?? " ", padAlign, totalPrefixLength); } output = `${prefix}${output}${suffix}`; return output; }; } var integerTypes = { b: (n) => absFloor(n).toString(2), c: (n) => String.fromCodePoint(n), d: (n) => Math.round(Math.abs(n)).toFixed(0), o: (n) => absFloor(n).toString(8), x: (n) => absFloor(n).toString(16), X: (n) => integerTypes.x(n).toUpperCase(), n: (n) => integerTypes.d(n), "%": (n) => `${absFloor(n * 100).toFixed(0)}` }; var decimalTypes = { e: (n, f) => Math.abs(n).toExponential(f), E: (n, f) => decimalTypes.e(n, f).toUpperCase(), f: (n, f) => Math.abs(n).toFixed(f), F: (n, f) => decimalTypes.f(n, f).toUpperCase(), g: (n, f) => { if (n === 0) { return "0"; } const a = Math.abs(n); const p = Math.floor(Math.log10(a)); if (p >= -4 && p < f) { return a.toFixed(f - 1 - p); } return a.toExponential(f - 1); }, G: (n, f) => decimalTypes.g(n, f).toUpperCase(), n: (n, f) => decimalTypes.g(n, f), p: (n, f) => decimalTypes.r(n * 100, f), r: (n, f) => { if (n === 0) { return "0"; } const a = Math.abs(n); const p = Math.floor(Math.log10(a)); const q = p - (f - 1); if (q <= 0) { return a.toFixed(-q); } const x = 10 ** q; return (Math.round(a / x) * x).toFixed(); }, s: (n, f) => { const p = getSIPrefixPower(n); return decimalTypes.r(n / 10 ** p, f); }, "%": (n, f) => decimalTypes.f(n * 100, f) }; var minSIPrefix = -24; var maxSIPrefix = 24; var siPrefixes = { [minSIPrefix]: "y", [-21]: "z", [-18]: "a", [-15]: "f", [-12]: "p", [-9]: "n", [-6]: "\xB5", [-3]: "m", [0]: "", [3]: "k", [6]: "M", [9]: "G", [12]: "T", [15]: "P", [18]: "E", [21]: "Z", [maxSIPrefix]: "Y" }; var minusSign = "\u2212"; function absFloor(n) { return Math.floor(Math.abs(n)); } function removeTrailingZeros(numString) { if (!numString.endsWith("0") || !numString.includes(".")) return numString; let endIndex = numString.length - 1; while (endIndex > 0) { if (numString[endIndex] == "0") { endIndex -= 1; } else if (numString[endIndex] == ".") { endIndex -= 1; break; } else { break; } } return numString.substring(0, endIndex + 1); } function insertSeparator(numString, separator) { let dotIndex = numString.indexOf("."); if (dotIndex < 0) { dotIndex = numString.length; } const integerChars = numString.substring(0, dotIndex).split(""); const fractionalPart = numString.substring(dotIndex); for (let i = integerChars.length - 3; i > 0; i -= 3) { integerChars.splice(i, 0, separator); } return `${integerChars.join("")}${fractionalPart}`; } function getSIPrefix(n) { return siPrefixes[getSIPrefixPower(n)]; } function getSIPrefixPower(n) { return clamp(minSIPrefix, n ? Math.floor(Math.log10(Math.abs(n)) / 3) * 3 : 0, maxSIPrefix); } function addSign(num, numString, signType = "") { if (signType === "(") { if (num >= 0) { return { value: numString, prefixLength: 0 }; } return { value: `(${numString})`, prefixLength: 1 }; } let signPrefix = ""; if (num < 0) { signPrefix = minusSign; } else if (signType === "+") { signPrefix = "+"; } else if (signType === " ") { signPrefix = " "; } return { value: `${signPrefix}${numString}`, prefixLength: signPrefix.length }; } function addPadding(numString, width2, fill = " ", align2 = ">", prefixLength = 0) { const padSize = width2 - numString.length; if (padSize <= 0) { return numString; } const padding2 = fill.repeat(padSize); if (align2 === "=") { const clampedPrefix = Math.min(Math.max(prefixLength, 0), numString.length); const start2 = numString.slice(0, clampedPrefix); const rest = numString.slice(clampedPrefix); return `${start2}${padding2}${rest}`; } if (align2 === ">" || !align2) { return padding2 + numString; } else if (align2 === "<") { return `${numString}${padding2}`; } else if (align2 === "^") { const padLeft = Math.ceil(padSize / 2); const padRight = Math.floor(padSize / 2); return `${fill.repeat(padLeft)}${numString}${fill.repeat(padRight)}`; } return padding2 + numString; } function getSymbolPrefix(symbol, type) { if (symbol === "#") { switch (type) { case "b": return "0b"; case "o": return "0o"; case "x": return "0x"; case "X": return "0X"; default: return ""; } } return symbol ?? ""; } // packages/ag-charts-core/src/config/optionsDefaults.ts var themeOperator = (value) => { if (!isObject(value)) return false; const keys = Object.keys(value); return keys.length === 1 && keys[0].startsWith("$"); }; var colorStop = optionsDefs({ color, stop: ratio }, ""); var colorStopsOrderValidator = attachDescription((value) => { let lastStop = -Infinity; for (const item of value) { if (item?.stop != null) { if (item.stop < lastStop) { return false; } lastStop = item.stop; } } return true; }, "colour stops to be defined in ascending order"); var gradientColorStops = and(arrayLength(2), arrayOf(colorStop), colorStopsOrderValidator); var gradientBounds = union("axis", "item", "series"); var gradientStrictDefs = { type: required(constant("gradient")), colorStops: required(gradientColorStops), rotation: number, // @ts-expect-error undocumented option gradient: undocumented(union("linear", "radial", "conic")), bounds: undocumented(gradientBounds), reverse: undocumented(boolean), colorSpace: undocumented(union("rgb", "oklch")) }; var gradientStrict = optionsDefs( gradientStrictDefs, "a gradient object with colour stops" ); var strokeOptionsDef = { stroke: color, strokeWidth: positiveNumber, strokeOpacity: ratio }; var fillGradientDefaults = optionsDefs({ type: required(constant("gradient")), gradient: required(union("linear", "radial", "conic")), bounds: required(gradientBounds), colorStops: required(or(gradientColorStops, and(arrayLength(2), arrayOf(color)))), rotation: required(number), reverse: required(boolean), colorSpace: required(union("rgb", "oklch")) }); var fillPatternDefaults = optionsDefs({ type: required(constant("pattern")), pattern: required( union( "vertical-lines", "horizontal-lines", "forward-slanted-lines", "backward-slanted-lines", "circles", "squares", "triangles", "diamonds", "stars", "hearts", "crosses" ) ), path: stringLength(2), width: required(positiveNumber), height: required(positiveNumber), fill: required(color), fillOpacity: required(ratio), backgroundFill: required(color), backgroundFillOpacity: required(ratio), padding: required(positiveNumber), rotation: required(number), scale: required(positiveNumber), stroke: required(color), strokeWidth: required(positiveNumber), strokeOpacity: required(ratio) }); var fillImageDefaults = optionsDefs({ type: required(constant("image")), url: string, width: positiveNumber, height: positiveNumber, rotation: required(number), backgroundFill: required(color), backgroundFillOpacity: ratio, fit: required(union("stretch", "contain", "cover")), repeat: required(union("repeat", "repeat-x", "repeat-y", "no-repeat")) }); var colorObjectDefs = { // @ts-expect-error undocumented option gradient: { colorStops: gradientColorStops, rotation: number, gradient: undocumented(union("linear", "radial", "conic")), bounds: undocumented(gradientBounds), reverse: undocumented(boolean), colorSpace: undocumented(union("rgb", "oklch")) }, pattern: { pattern: union( "vertical-lines", "horizontal-lines", "forward-slanted-lines", "backward-slanted-lines", "circles", "squares", "triangles", "diamonds", "stars", "hearts", "crosses" ), path: stringLength(2), width: positiveNumber, height: positiveNumber, rotation: number, scale: positiveNumber, fill: color, fillOpacity: ratio, backgroundFill: color, backgroundFillOpacity: ratio, ...strokeOptionsDef, padding: undocumented(positiveNumber) }, image: { url: required(string), backgroundFill: color, backgroundFillOpacity: ratio, width: positiveNumber, height: positiveNumber, fit: union("stretch", "contain", "cover", "none"), repeat: union("repeat", "repeat-x", "repeat-y", "no-repeat"), rotation: number } }; var colorObject = typeUnion(colorObjectDefs, "a color object"); var colorUnion = or(color, optionsDefs(colorObject, "a color object")); var fillOptionsDef = { fill: colorUnion, fillOpacity: ratio }; fillOptionsDef.fillGradientDefaults = undocumented(fillGradientDefaults); fillOptionsDef.fillPatternDefaults = undocumented(fillPatternDefaults); fillOptionsDef.fillImageDefaults = undocumented(fillImageDefaults); var lineDashOptionsDef = { lineDash: arrayOf(positiveNumber), lineDashOffset: number }; var barHighlightOptionsDef = { ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef, opacity: ratio, cornerRadius: positiveNumber }; var lineHighlightOptionsDef = { ...strokeOptionsDef, ...lineDashOptionsDef, opacity: ratio }; var shapeHighlightOptionsDef = { ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef, opacity: ratio }; function highlightOptionsDef(itemHighlightOptionsDef) { return { enabled: boolean, range: union("tooltip", "node"), highlightedItem: itemHighlightOptionsDef, unhighlightedItem: itemHighlightOptionsDef }; } function multiSeriesHighlightOptionsDef(itemHighlightOptionsDef, seriesHighlightOptionsDef) { return { enabled: boolean, range: union("tooltip", "node"), highlightedItem: itemHighlightOptionsDef, unhighlightedItem: itemHighlightOptionsDef, highlightedSeries: seriesHighlightOptionsDef, unhighlightedSeries: seriesHighlightOptionsDef, bringToFront: boolean }; } var shapeSegmentOptions = { start: defined, stop: defined, ...strokeOptionsDef, ...fillOptionsDef, ...lineDashOptionsDef }; var lineSegmentOptions = { start: defined, stop: defined, ...strokeOptionsDef, ...lineDashOptionsDef }; var shapeSegmentation = optionsDefs( { enabled: boolean, key: required(union("x", "y")), segments: arrayOfDefs(shapeSegmentOptions, "path segments array") }, "a segmentation object", true ); var lineSegmentation = optionsDefs( { enabled: boolean, key: required(union("x", "y")), segments: arrayOfDefs(lineSegmentOptions, "path segments array") }, "a segmentation object", true ); var googleFont = optionsDefs({ googleFont: string }, "google font"); var fontFamilyFull = or(string, themeOperator, googleFont, arrayOf(or(string, googleFont))); var fontWeight = or(positiveNumber, union("normal", "bold", "bolder", "lighter")); var fontOptionsDef = { color, fontFamily: fontFamilyFull, fontSize: positiveNumber, fontStyle: union("normal", "italic", "oblique"), fontWeight }; var paddingOptions = optionsDefs( { top: number, right: number, bottom: number, left: number }, "padding object" ); var padding = or(number, paddingOptions); var borderOptionsDef = { enabled: boolean, stroke: color, strokeWidth: positiveNumber, strokeOpacity: ratio }; var labelBoxOptionsDef = { border: borderOptionsDef, cornerRadius: number, padding, ...fillOptionsDef }; // packages/ag-charts-core/src/config/chartDefaults.ts var legendPlacementLiterals = [ "top", "top-right", "top-left", "bottom", "bottom-right", "bottom-left", "right", "right-top", "right-bottom", "left", "left-top", "left-bottom" ]; var legendPositionOptionsDef = { floating: boolean, placement: union(...legendPlacementLiterals), xOffset: number, yOffset: number }; var legendPositionValidator = attachDescription( (value, context) => { let result; if (typeof value === "string") { const allowedValues = legendPlacementLiterals; if (allowedValues.includes(value)) { result = true; } else { result = { valid: false, invalid: [], cleared: null }; result.invalid.push( new ValidationError( "invalid" /* Invalid */, `a legend placement string: ["${legendPlacementLiterals.join('", "')}"]`, value, context.path ) ); } } else { const { cleared, invalid } = validate(value, legendPositionOptionsDef); result = { valid: invalid.length === 0, cleared, invalid }; } return result; }, `a legend position object or placement string` ); var shapeValidator = or( union("circle", "cross", "diamond", "heart", "plus", "pin", "square", "star", "triangle"), callback ); var textWrapValidator = union("never", "always", "hyphenate", "on-space"); var tooltipPlacementValidator = union( "top", "right", "bottom", "left", "top-right", "bottom-right", "bottom-left", "top-left", "center" ); var rangeValidator = or(positiveNumber, union("exact", "nearest", "area")); var seriesTooltipRangeValidator = or(positiveNumber, union("exact", "nearest")); var textOrSegments = or( string, number, date, arrayOfDefs( { text: required(string), ...fontOptionsDef }, "text segments array" ) ); var chartCaptionOptionsDefs = { enabled: boolean, text: textOrSegments, textAlign: union("left", "center", "right"), wrapping: union("never", "always", "hyphenate", "on-space"), spacing: positiveNumber, maxWidth: positiveNumber, maxHeight: positiveNumber, ...fontOptionsDef }; chartCaptionOptionsDefs.padding = undocumented(positiveNumber); var chartOverlayOptionsDefs = { enabled: boolean, text: textOrSegments, renderer: callbackOf(or(string, htmlElement)) }; var contextMenuItemLiterals = [ "defaults", "download", "zoom-to-cursor", "pan-to-cursor", "reset-zoom", "toggle-series-visibility", "toggle-other-series", "separator" ]; var contextMenuItemObjectDef = { type: union("action", "separator"), showOn: union("always", "series-area", "series-node", "legend-item"), label: required(string), enabled: boolean, action: callback, items: (value, context) => contextMenuItemsArray(value, context) }; contextMenuItemObjectDef.iconUrl = undocumented(string); var contextMenuItemObjectValidator = optionsDefs(contextMenuItemObjectDef); var contextMenuItemValidator = attachDescription( (value, context) => { let result; if (typeof value === "string") { const allowedValues = contextMenuItemLiterals; if (allowedValues.includes(value)) { result = true; } else { result = { valid: false, invalid: [], cleared: null }; result.invalid.push( new ValidationError( "invalid" /* Invalid */, `a context menu item string alias: ["${contextMenuItemLiterals.join('", "')}"]`, value, context.path ) ); } } else { result = contextMenuItemObjectValidator(value, context); } return result; }, `a context menu item object or string alias: [${contextMenuItemLiterals.join(", ")}]` ); var contextMenuItemsArray = arrayOf(contextMenuItemValidator, "a menu items array", false); var toolbarButtonOptionsDefs = { label: string, ariaLabel: string, tooltip: string, icon: union( "align-center", "align-left", "align-right", "arrow-drawing", "arrow-down-drawing", "arrow-up-drawing", "callout-annotation", "candlestick-series", "close", "comment-annotation", "date-range-drawing", "date-price-range-drawing", "delete", "disjoint-channel-drawing", "drag-handle", "fill-color", "line-style-solid", "line-style-dashed", "line-style-dotted", "high-low-series", "hlc-series", "hollow-candlestick-series", "horizontal-line-drawing", "line-color", "line-series", "line-with-markers-series", "locked", "measurer-drawing", "note-annotation", "ohlc-series", "pan-end", "pan-left", "pan-right", "pan-start", "parallel-channel-drawing", "position-bottom", "position-center", "position-top", "price-label-annotation", "price-range-drawing", "reset", "settings", "step-line-series", "text-annotation", "trend-line-drawing", "fibonacci-retracement-drawing", "fibonacci-retracement-trend-based-drawing", "unlocked", "vertical-line-drawing", "zoom-in", "zoom-out" ) }; var formatter = or(string, callbackOf(textOrSegments)); var formatObjectValidator = optionsDefs({ x: formatter, y: formatter, angle: formatter, radius: formatter, size: formatter, color: formatter, label: formatter, secondaryLabel: formatter, sectorLabel: formatter, calloutLabel: formatter, legendItem: formatter }); var numberFormatValidator = attachDescription(isValidNumberFormat, "a valid number format string"); var commonChartOptionsDefs = { width: positiveNumber, height: positiveNumber, minWidth: positiveNumber, minHeight: positiveNumber, suppressFieldDotNotation: boolean, title: chartCaptionOptionsDefs, subtitle: chartCaptionOptionsDefs, footnote: chartCaptionOptionsDefs, padding: { top: positiveNumber, right: positiveNumber, bottom: positiveNumber, left: positiveNumber }, seriesArea: { border: borderOptionsDef, clip: boolean, cornerRadius: number, padding }, legend: { enabled: boolean, position: legendPositionValidator, orientation: union("horizontal", "vertical"), maxWidth: positiveNumber, maxHeight: positiveNumber, spacing: positiveNumber, border: borderOptionsDef, cornerRadius: number, padding, fill: colorUnion, fillOpacity: ratio, preventHidingAll: boolean, reverseOrder: boolean, toggleSeries: boolean, item: { marker: { size: positiveNumber, shape: shapeValidator, padding: positiveNumber, strokeWidth: positiveNumber }, line: { length: positiveNumber, strokeWidth: positiveNumber }, label: { maxLength: positiveNumber, formatter: callback, ...fontOptionsDef }, maxWidth: positiveNumber, paddingX: positiveNumber, paddingY: positiveNumber, showSeriesStroke: boolean }, pagination: { marker: { size: positiveNumber, shape: shapeValidator, padding: positiveNumber }, activeStyle: { ...fillOptionsDef, ...strokeOptionsDef }, inactiveStyle: { ...fillOptionsDef, ...strokeOptionsDef }, highlightStyle: { ...fillOptionsDef, ...strokeOptionsDef }, label: fontOptionsDef }, listeners: { legendItemClick: callback, legendItemDoubleClick: callback } }, gradientLegend: { enabled: boolean, position: legendPositionValidator, spacing: positiveNumber, reverseOrder: boolean, border: borderOptionsDef, cornerRadius: number, padding, fill: colorUnion, fillOpacity: ratio, gradient: { preferredLength: positiveNumber, thickness: positiveNumber }, scale: { label: { ...fontOptionsDef, minSpacing: positiveNumber, format: numberFormatValidator, formatter: callback }, padding: positiveNumber, interval: { step: number, values: array, minSpacing: and(positiveNumber, lessThan("maxSpacing")), maxSpacing: and(positiveNumber, greaterThan("minSpacing")) } } }, listeners: { seriesNodeClick: callback, seriesNodeDoubleClick: callback, seriesVisibilityChange: callback, activeChange: callback, click: callback, doubleClick: callback, annotations: callback, zoom: callback }, loadGoogleFonts: boolean, highlight: { drawingMode: union("overlay", "cutout"), range: union("tooltip", "node") }, overlays: { loading: chartOverlayOptionsDefs, noData: chartOverlayOptionsDefs, noVisibleSeries: chartOverlayOptionsDefs, unsupportedBrowser: chartOverlayOptionsDefs }, tooltip: { enabled: boolean, showArrow: boolean, pagination: boolean, delay: positiveNumber, range: rangeValidator, wrapping: textWrapValidator, mode: union("single", "shared", "compact"), position: { anchorTo: union("pointer", "node", "chart"), placement: or(tooltipPlacementValidator, arrayOf(tooltipPlacementValidator)), xOffset: number, yOffset: number } }, animation: defined, contextMenu: defined, context: () => true, dataSource: { getData: callback }, keyboard: { enabled: boolean, tabIndex: number }, touch: { dragAction: union("none", "drag", "hover") }, ranges: { enabled: boolean, buttons: arrayOfDefs( { ...toolbarButtonOptionsDefs, value: or(number, and(arrayOf(or(number, date)), arrayLength(2, 2)), callback) }, "range button options array" ) }, // modules locale: { localeText: object, getLocaleText: callbackOf(string) }, background: { visible: boolean, fill: color, // enterprise image: { url: required(string), top: number, right: number, bottom: number, left: number, width: positiveNumber, height: positiveNumber, opacity: ratio } }, styleNonce: string, sync: defined, zoom: defined, scrollbar: defined, formatter: or(callbackOf(textOrSegments), formatObjectValidator) }; commonChartOptionsDefs.flashOnUpdate = undocumented(defined); commonChartOptionsDefs.dataSource.requestThrottle = undocumented(positiveNumber); commonChartOptionsDefs.dataSource.updateThrottle = undocumented(positiveNumber); commonChartOptionsDefs.dataSource.updateDuringInteraction = undocumented(boolean); commonChartOptionsDefs.statusBar = undocumented(defined); commonChartOptionsDefs.foreground = undocumented({ visible: boolean, text: string, image: { url: string, top: number, right: number, bottom: number, left: number, width: positiveNumber, height: positiveNumber, opacity: ratio }, ...fillOptionsDef }); commonChartOptionsDefs.overrideDevicePixelRatio = undocumented(number); commonChartOptionsDefs.sync.domainMode = undocumented(union("direction", "position", "key")); commonChartOptionsDefs.displayNullData = undocumented(boolean); var commonSeriesThemeableOptionsDefs = { cursor: string, context: () => true, showInLegend: boolean, nodeClickRange: rangeValidator, listeners: { seriesNodeClick: callback, seriesNodeDoubleClick: callback }, highlight: highlightOptionsDef(shapeHighlightOptionsDef) }; commonSeriesThemeableOptionsDefs.allowNullKeys = undocumented(boolean); var commonSeriesOptionsDefs = { ...commonSeriesThemeableOptionsDefs, id: string, visible: boolean, context: () => true, data: array }; commonSeriesOptionsDefs.seriesGrouping = undocumented(defined); var markerStyleOptionsDefs = { shape: shapeValidator, size: positiveNumber, ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }; var markerOptionsDefs = { enabled: boolean, itemStyler: callbackDefs({ ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef, shape: shapeValidator, size: positiveNumber }), ...markerStyleOptionsDefs }; var seriesLabelOptionsDefs = { enabled: boolean, formatter: callbackOf(textOrSegments), format: numberFormatValidator, itemStyler: callbackDefs({ enabled: boolean, ...labelBoxOptionsDef, ...fontOptionsDef }), ...labelBoxOptionsDef, ...fontOptionsDef }; var autoSizedLabelOptionsDefs = { ...seriesLabelOptionsDefs, lineHeight: positiveNumber, minimumFontSize: positiveNumber, wrapping: textWrapValidator, overflowStrategy: union("ellipsis", "hide") }; var errorBarThemeableOptionsDefs = { visible: boolean, cap: { visible: boolean, length: positiveNumber, lengthRatio: ratio, ...strokeOptionsDef, ...lineDashOptionsDef }, ...strokeOptionsDef, ...lineDashOptionsDef }; var errorBarOptionsDefs = { ...errorBarThemeableOptionsDefs, xLowerKey: string, xUpperKey: string, yLowerKey: string, yUpperKey: string, xLowerName: string, xUpperName: string, yLowerName: string, yUpperName: string, itemStyler: callbackDefs({ visible: boolean, ...strokeOptionsDef, ...lineDashOptionsDef, cap: { visible: boolean, length: positiveNumber, lengthRatio: ratio, ...strokeOptionsDef, ...lineDashOptionsDef } }) }; var tooltipOptionsDefs = { enabled: boolean, showArrow: boolean, range: seriesTooltipRangeValidator, renderer: callbackOf( or( string, number, date, optionsDefs( { heading: string, title: string, symbol: { marker: { enabled: boolean, shape: shapeValidator, ...fillOptionsDef, stroke: color, strokeOpacity: ratio, strokeWidth: positiveNumber, ...lineDashOptionsDef }, line: { enabled: boolean, stroke: color, strokeWidth: positiveNumber, strokeOpacity: ratio, ...lineDashOptionsDef } }, data: arrayOfDefs({ label: required(string), value: required(or(string, number, date)) }) }, "tooltip renderer result object" ) ) ), position: { anchorTo: union("node", "pointer", "chart"), placement: or(tooltipPlacementValidator, arrayOf(tooltipPlacementValidator)), xOffset: number, yOffset: number }, interaction: { enabled: boolean } }; var tooltipOptionsDefsWithArea = { ...tooltipOptionsDefs, range: rangeValidator }; var shadowOptionsDefs = { enabled: boolean, xOffset: number, yOffset: number, blur: positiveNumber, color }; var interpolationOptionsDefs = typeUnion( { linear: {}, smooth: { tension: ratio }, step: { position: union("start", "middle", "end") } }, "interpolation line options" ); // packages/ag-charts-core/src/utils/types/decorator.ts var BREAK_TRANSFORM_CHAIN = Symbol("BREAK"); var CONFIG_KEY = "__decorator_config"; var ACCESSORS_KEY = "__decorator_accessors"; function addFakeTransformToInstanceProperty(target, propertyKeyOrSymbol) { initialiseConfig(target, propertyKeyOrSymbol).optional = true; } function initialiseConfig(target, propertyKeyOrSymbol) { if (Object.getOwnPropertyDescriptor(target, CONFIG_KEY) == null) { Object.defineProperty(target, CONFIG_KEY, { value: {} }); } if (Object.getOwnPropertyDescriptor(target, ACCESSORS_KEY) == null) { const parentAccessors = Object.getPrototypeOf(target)?.[ACCESSORS_KEY]; const accessors = parentAccessors?.slice() ?? []; Object.defineProperty(target, ACCESSORS_KEY, { value: accessors }); } const config = target[CONFIG_KEY]; const propertyKey = propertyKeyOrSymbol.toString(); if (config[propertyKey] != null) { return config[propertyKey]; } config[propertyKey] = { setters: [], getters: [], observers: [] }; const descriptor = Object.getOwnPropertyDescriptor(target, propertyKeyOrSymbol); let prevGet = descriptor?.get; let prevSet = descriptor?.set; if (prevGet == null || prevSet == null) { const accessors = target[ACCESSORS_KEY]; let index = accessors.indexOf(propertyKeyOrSymbol); if (index === -1) { index = accessors.push(propertyKeyOrSymbol) - 1; } prevGet ?? (prevGet = function() { let accessorValues = this.__accessors; if (accessorValues == null) { accessorValues = accessors.slice().fill(void 0); Object.defineProperty(this, "__accessors", { value: accessorValues }); } return accessorValues[index]; }); prevSet ?? (prevSet = function(value) { let accessorValues = this.__accessors; if (accessorValues == null) { accessorValues = accessors.slice().fill(void 0); Object.defineProperty(this, "__accessors", { value: accessorValues }); } accessorValues[index] = value; }); } const getter = function() { let value = prevGet.call(this); for (const transformFn of config[propertyKey].getters) { value = transformFn(this, propertyKeyOrSymbol, value); if (value === BREAK_TRANSFORM_CHAIN) { return; } } return value; }; const setter = function(value) { const { setters, observers } = config[propertyKey]; let oldValue; if (setters.some((f) => f.length > 2)) { oldValue = prevGet.call(this); } for (const transformFn of setters) { value = transformFn(this, propertyKeyOrSymbol, value, oldValue); if (value === BREAK_TRANSFORM_CHAIN) { return; } } prevSet.call(this, value); for (const observerFn of observers) { observerFn(this, value, oldValue); } }; Object.defineProperty(target, propertyKeyOrSymbol, { set: setter, get: getter, enumerable: true, configurable: false }); return config[propertyKey]; } function addTransformToInstanceProperty(setTransform, getTransform, configMetadata) { return (target, propertyKeyOrSymbol) => { const config = initialiseConfig(target, propertyKeyOrSymbol); config.setters.push(setTransform); if (getTransform) { config.getters.unshift(getTransform); } if (configMetadata) { Object.assign(config, configMetadata); } }; } function addObserverToInstanceProperty(setObserver) { return (target, propertyKeyOrSymbol) => { initialiseConfig(target, propertyKeyOrSymbol).observers.push(setObserver); }; } function isDecoratedObject(target) { return target !== void 0 && CONFIG_KEY in target; } function listDecoratedProperties(target) { const targets = /* @__PURE__ */ new Set(); while (isDecoratedObject(target)) { targets.add(target?.[CONFIG_KEY]); target = Object.getPrototypeOf(target); } return Array.from(targets).flatMap((configMap) => Object.keys(configMap)); } function extractDecoratedProperties(target) { return listDecoratedProperties(target).reduce((result, key) => { result[String(key)] = target[key] ?? null; return result; }, {}); } // packages/ag-charts-core/src/utils/data/iterators.ts function* iterate(...items) { for (const item of items) { if (item == null) continue; if (item[Symbol.iterator]) { yield* item; } else { yield item; } } } function toIterable(value) { return value != null && typeof value === "object" && Symbol.iterator in value ? value : [value]; } function first(iterable) { for (const value of iterable) { return value; } throw new Error("AG Charts - no first() value found"); } function* entries(obj) { const resultTuple = [void 0, void 0]; for (const key of Object.keys(obj)) { resultTuple[0] = key; resultTuple[1] = obj[key]; yield resultTuple; } } // packages/ag-charts-core/src/utils/data/object.ts function strictObjectKeys(o) { return Object.keys(o); } function objectsEqual(a, b) { if (Array.isArray(a)) { if (!Array.isArray(b)) return false; if (a.length !== b.length) return false; return a.every((av, i) => objectsEqual(av, b[i])); } else if (isPlainObject(a)) { if (!isPlainObject(b)) return false; return objectsEqualWith(a, b, objectsEqual); } return a === b; } function objectsEqualWith(a, b, cmp2) { if (Object.is(a, b)) return true; for (const key of Object.keys(b)) { if (!(key in a)) return false; } for (const key of Object.keys(a)) { if (!(key in b)) return false; if (!cmp2(a[key], b[key])) return false; } return true; } function mergeDefaults(...sources) { const target = {}; for (const source of sources) { if (!isObject(source)) continue; const keys = isDecoratedObject(source) ? listDecoratedProperties(source) : Object.keys(source); for (const key of keys) { if (isPlainObject(target[key]) && isPlainObject(source[key])) { target[key] = mergeDefaults(target[key], source[key]); } else { target[key] ?? (target[key] = source[key]); } } } return target; } function merge(...sources) { const target = {}; for (const source of sources) { if (!isObject(source)) continue; const keys = isDecoratedObject(source) ? listDecoratedProperties(source) : Object.keys(source); for (const key of keys) { if (isPlainObject(target[key]) && isPlainObject(source[key])) { target[key] = merge(target[key], source[key]); } else if (!(key in target)) { target[key] ?? (target[key] = source[key]); } } } return target; } function mergeArrayDefaults(dataArray, ...itemDefaults) { if (itemDefaults && isArray(dataArray)) { return dataArray.map((item) => mergeDefaults(item, ...itemDefaults)); } return dataArray; } function mapValues(object2, mapper) { const result = {}; for (const [key, value] of entries(object2)) { result[key] = mapper(value, key, object2); } return result; } function without(object2, keys) { const clone2 = { ...object2 }; for (const key of keys) { delete clone2[key]; } return clone2; } function pick(object2, keys) { if (object2 == null) return; const picked = {}; for (const key of keys) { if (Object.hasOwn(object2, key)) { picked[key] = object2[key]; } } return picked; } function every(object2, fn) { if (object2 == null) return true; for (const [key, value] of entries(object2)) { if (!fn(key, value)) return false; } return true; } function fromPairs(pairs) { const object2 = {}; if (pairs == null) return object2; for (const [key, value] of pairs) { object2[key] = value; } return object2; } function getPath(object2, path) { const pathArray = isArray(path) ? path : path.split("."); return pathArray.reduce((value, pathKey) => value[pathKey], object2); } var SKIP_JS_BUILTINS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]); function setPath(object2, path, newValue) { const pathArray = isArray(path) ? path.slice() : path.split("."); const lastKey = pathArray.pop(); if (pathArray.some((p) => SKIP_JS_BUILTINS.has(p))) return; const lastObject = pathArray.reduce((value, pathKey) => value[pathKey], object2); lastObject[lastKey] = newValue; return lastObject[lastKey]; } function partialAssign(keysToCopy, target, source) { if (source === void 0) { return target; } for (const key of keysToCopy) { const value = source[key]; if (value !== void 0) { target[key] = value; } } return target; } function assignIfNotStrictlyEqual(target, source, keys) { const sourceKeys = keys ?? Object.keys(source); for (let i = 0, len = sourceKeys.length; i < len; i++) { const key = sourceKeys[i]; const newValue = source[key]; if (target[key] !== newValue) { target[key] = newValue; } } return target; } function deepFreeze(obj) { if (obj == null || typeof obj !== "object" || !isPlainObject(obj)) { return obj; } Object.freeze(obj); for (const prop of Object.getOwnPropertyNames(obj)) { const value = obj[prop]; if (value !== null && (typeof value === "object" || typeof value === "function") && !Object.isFrozen(value)) { deepFreeze(value); } } return obj; } function isObjectWithProperty(obj, key) { return isPlainObject(obj) && key in obj; } function isObjectWithStringProperty(obj, key) { return isObjectWithProperty(obj, key) && typeof obj[key] === "string"; } // packages/ag-charts-core/src/config/gaugePreset.ts var fillsOptionsDef = { fills: and( arrayLength(2), arrayOf(optionsDefs({ color, stop: number }, "")), colorStopsOrderValidator ), fillMode: union("continuous", "discrete") }; var linearGaugeTargetOptionsDef = { value: required(number), text: string, shape: or( union("circle", "cross", "diamond", "heart", "plus", "pin", "square", "star", "triangle", "line"), callback ), placement: union("before", "after", "middle"), spacing: positiveNumber, size: positiveNumber, rotation: number, ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }; var radialGaugeTargetOptionsDef = { value: required(number), text: string, shape: or( union("circle", "cross", "diamond", "heart", "plus", "pin", "square", "star", "triangle", "line"), callback ), placement: union("inside", "outside", "middle"), spacing: positiveNumber, size: positiveNumber, rotation: number, label: { ...seriesLabelOptionsDefs, spacing: positiveNumber }, ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }; var linearGaugeSeriesThemeableOptionsDef = { ...without(commonSeriesThemeableOptionsDefs, ["listeners"]), direction: union("horizontal", "vertical"), cornerMode: union("container", "item"), cornerRadius: positiveNumber, thickness: positiveNumber, segmentation: { enabled: boolean, spacing: positiveNumber, interval: { values: arrayOf(number), step: number, count: number } }, bar: { enabled: boolean, thickness: positiveNumber, thicknessRatio: ratio, ...fillsOptionsDef, ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }, label: { ...autoSizedLabelOptionsDefs, text: string, spacing: positiveNumber, avoidCollisions: boolean, placement: union( "inside-start", "outside-start", "inside-end", "outside-end", "inside-center", "bar-inside", "bar-inside-end", "bar-outside-end", "bar-end" ) }, tooltip: tooltipOptionsDefs }; var linearGaugeSeriesOptionsDef = { ...without(commonSeriesOptionsDefs, ["listeners"]), ...linearGaugeSeriesThemeableOptionsDef, type: required(constant("linear-gauge")), value: required(number), scale: { min: and(number, lessThan("max")), max: and(number, greaterThan("min")), label: { enabled: boolean, formatter: callback, rotation: number, spacing: positiveNumber, minSpacing: positiveNumber, placement: union("before", "after"), avoidCollisions: boolean, format: numberFormatValidator, ...fontOptionsDef }, interval: { values: arrayOf(number), step: number }, ...fillsOptionsDef, ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }, targets: arrayOfDefs(linearGaugeTargetOptionsDef, "target options array") }; linearGaugeSeriesOptionsDef.margin = undocumented(number); linearGaugeSeriesOptionsDef.defaultColorRange = undocumented(arrayOf(color)); linearGaugeSeriesOptionsDef.defaultTarget = undocumented({ ...linearGaugeTargetOptionsDef, value: number, label: { ...seriesLabelOptionsDefs, spacing: number } }); linearGaugeSeriesOptionsDef.defaultScale = undocumented(linearGaugeSeriesOptionsDef.scale); linearGaugeSeriesOptionsDef.scale.defaultFill = undocumented(color); var radialGaugeSeriesThemeableOptionsDef = { ...without(commonSeriesThemeableOptionsDefs, ["listeners"]), outerRadius: positiveNumber, innerRadius: positiveNumber, outerRadiusRatio: ratio, innerRadiusRatio: ratio, startAngle: number, endAngle: number, spacing: positiveNumber, cornerMode: union("container", "item"), cornerRadius: positiveNumber, scale: { min: and(number, lessThan("max")), max: and(number, greaterThan("min")), label: { enabled: boolean, formatter: callback, rotation: number, spacing: positiveNumber, minSpacing: positiveNumber, avoidCollisions: boolean, format: numberFormatValidator, ...fontOptionsDef }, interval: { values: arrayOf(number), step: number }, ...fillsOptionsDef, ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }, segmentation: { enabled: boolean, spacing: positiveNumber, interval: { values: arrayOf(number), step: number, count: number } }, bar: { enabled: boolean, ...fillsOptionsDef, ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }, needle: { enabled: boolean, spacing: positiveNumber, radiusRatio: ratio, ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }, label: { text: string, spacing: positiveNumber, ...autoSizedLabelOptionsDefs }, secondaryLabel: { text: string, ...autoSizedLabelOptionsDefs }, tooltip: tooltipOptionsDefs }; var radialGaugeSeriesOptionsDef = { ...without(commonSeriesOptionsDefs, ["listeners"]), ...radialGaugeSeriesThemeableOptionsDef, type: required(constant("radial-gauge")), value: required(number), targets: arrayOfDefs(radialGaugeTargetOptionsDef, "target options array") }; radialGaugeSeriesOptionsDef.defaultColorRange = undocumented(arrayOf(color)); radialGaugeSeriesOptionsDef.defaultTarget = undocumented({ ...radialGaugeTargetOptionsDef, value: number, label: { ...seriesLabelOptionsDefs, spacing: number } }); radialGaugeSeriesOptionsDef.scale.defaultFill = undocumented(color); // packages/ag-charts-core/src/types/themeConstants.ts var FONT_SIZE = /* @__PURE__ */ ((FONT_SIZE2) => { FONT_SIZE2[FONT_SIZE2["SMALLEST"] = 8] = "SMALLEST"; FONT_SIZE2[FONT_SIZE2["SMALLER"] = 10] = "SMALLER"; FONT_SIZE2[FONT_SIZE2["SMALL"] = 12] = "SMALL"; FONT_SIZE2[FONT_SIZE2["MEDIUM"] = 13] = "MEDIUM"; FONT_SIZE2[FONT_SIZE2["LARGE"] = 14] = "LARGE"; FONT_SIZE2[FONT_SIZE2["LARGEST"] = 17] = "LARGEST"; return FONT_SIZE2; })(FONT_SIZE || {}); var BASE_FONT_SIZE = 12 /* SMALL */; var FONT_SIZE_RATIO = { SMALLEST: 8 /* SMALLEST */ / BASE_FONT_SIZE, SMALLER: 10 /* SMALLER */ / BASE_FONT_SIZE, SMALL: 12 /* SMALL */ / BASE_FONT_SIZE, MEDIUM: 13 /* MEDIUM */ / BASE_FONT_SIZE, LARGE: 14 /* LARGE */ / BASE_FONT_SIZE, LARGEST: 17 /* LARGEST */ / BASE_FONT_SIZE }; var CARTESIAN_POSITION = /* @__PURE__ */ ((CARTESIAN_POSITION2) => { CARTESIAN_POSITION2["TOP"] = "top"; CARTESIAN_POSITION2["TOP_RIGHT"] = "top-right"; CARTESIAN_POSITION2["TOP_LEFT"] = "top-left"; CARTESIAN_POSITION2["RIGHT"] = "right"; CARTESIAN_POSITION2["RIGHT_TOP"] = "right-top"; CARTESIAN_POSITION2["RIGHT_BOTTOM"] = "right-bottom"; CARTESIAN_POSITION2["BOTTOM"] = "bottom"; CARTESIAN_POSITION2["BOTTOM_RIGHT"] = "bottom-right"; CARTESIAN_POSITION2["BOTTOM_LEFT"] = "bottom-left"; CARTESIAN_POSITION2["LEFT"] = "left"; CARTESIAN_POSITION2["LEFT_TOP"] = "left-top"; CARTESIAN_POSITION2["LEFT_BOTTOM"] = "left-bottom"; return CARTESIAN_POSITION2; })(CARTESIAN_POSITION || {}); var CARTESIAN_AXIS_TYPE = /* @__PURE__ */ ((CARTESIAN_AXIS_TYPE2) => { CARTESIAN_AXIS_TYPE2["CATEGORY"] = "category"; CARTESIAN_AXIS_TYPE2["GROUPED_CATEGORY"] = "grouped-category"; CARTESIAN_AXIS_TYPE2["ORDINAL_TIME"] = "ordinal-time"; CARTESIAN_AXIS_TYPE2["UNIT_TIME"] = "unit-time"; CARTESIAN_AXIS_TYPE2["TIME"] = "time"; CARTESIAN_AXIS_TYPE2["NUMBER"] = "number"; CARTESIAN_AXIS_TYPE2["LOG"] = "log"; return CARTESIAN_AXIS_TYPE2; })(CARTESIAN_AXIS_TYPE || {}); var POLAR_AXIS_TYPE = /* @__PURE__ */ ((POLAR_AXIS_TYPE2) => { POLAR_AXIS_TYPE2["ANGLE_CATEGORY"] = "angle-category"; POLAR_AXIS_TYPE2["ANGLE_NUMBER"] = "angle-number"; POLAR_AXIS_TYPE2["RADIUS_CATEGORY"] = "radius-category"; POLAR_AXIS_TYPE2["RADIUS_NUMBER"] = "radius-number"; return POLAR_AXIS_TYPE2; })(POLAR_AXIS_TYPE || {}); var POLAR_AXIS_SHAPE = /* @__PURE__ */ ((POLAR_AXIS_SHAPE2) => { POLAR_AXIS_SHAPE2["CIRCLE"] = "circle"; POLAR_AXIS_SHAPE2["POLYGON"] = "polygon"; return POLAR_AXIS_SHAPE2; })(POLAR_AXIS_SHAPE || {}); // packages/ag-charts-core/src/utils/format/color.ts var lerp = (x, y, t) => x * (1 - t) + y * t; var srgbToLinear = (value) => { const sign = value < 0 ? -1 : 1; const abs = Math.abs(value); if (abs <= 0.04045) return value / 12.92; return sign * ((abs + 0.055) / 1.055) ** 2.4; }; var srgbFromLinear = (value) => { const sign = value < 0 ? -1 : 1; const abs = Math.abs(value); if (abs > 31308e-7) { return sign * (1.055 * abs ** (1 / 2.4) - 0.055); } return 12.92 * value; }; var _Color = class _Color { /** * Every color component should be in the [0, 1] range. * Some easing functions (such as elastic easing) can overshoot the target value by some amount. * So, when animating colors, if the source or target color components are already near * or at the edge of the allowed [0, 1] range, it is possible for the intermediate color * component value to end up outside of that range mid-animation. For this reason the constructor * performs range checking/constraining. * @param r Red component. * @param g Green component. * @param b Blue component. * @param a Alpha (opacity) component. */ constructor(r, g, b, a = 1) { this.r = clamp(0, r || 0, 1); this.g = clamp(0, g || 0, 1); this.b = clamp(0, b || 0, 1); this.a = clamp(0, a || 0, 1); } /** * A color string can be in one of the following formats to be valid: * - #rgb * - #rrggbb * - rgb(r, g, b) * - rgba(r, g, b, a) * - CSS color name such as 'white', 'orange', 'cyan', etc. */ static validColorString(str) { if (str.includes("#")) { return !!_Color.parseHex(str); } if (str.includes("rgb")) { return !!_Color.stringToRgba(str); } return _Color.nameToHex.has(str.toLowerCase()); } /** * The given string can be in one of the following formats: * - #rgb * - #rrggbb * - rgb(r, g, b) * - rgba(r, g, b, a) * - CSS color name such as 'white', 'orange', 'cyan', etc. * @param str */ static fromString(str) { if (str.includes("#")) { return _Color.fromHexString(str); } const hex = _Color.nameToHex.get(str.toLowerCase()); if (hex) { return _Color.fromHexString(hex); } if (str.includes("rgb")) { return _Color.fromRgbaString(str); } throw new Error(`Invalid color string: '${str}'`); } // See https://drafts.csswg.org/css-color/#hex-notation static parseHex(input) { input = input.replaceAll(" ", "").slice(1); let parts; switch (input.length) { case 6: case 8: parts = []; for (let i = 0; i < input.length; i += 2) { parts.push(Number.parseInt(`${input[i]}${input[i + 1]}`, 16)); } break; case 3: case 4: parts = input.split("").map((p) => Number.parseInt(p, 16)).map((p) => p + p * 16); break; } if (parts?.length >= 3 && parts.every((p) => p >= 0)) { if (parts.length === 3) { parts.push(255); } return parts; } } static fromHexString(str) { const values = _Color.parseHex(str); if (values) { const [r, g, b, a] = values; return new _Color(r / 255, g / 255, b / 255, a / 255); } throw new Error(`Malformed hexadecimal color string: '${str}'`); } static stringToRgba(str) { let po = -1; let pc = -1; for (let i = 0; i < str.length; i++) { const c = str[i]; if (po === -1 && c === "(") { po = i; } else if (c === ")") { pc = i; break; } } if (po === -1 || pc === -1) return; const contents = str.substring(po + 1, pc); const parts = contents.split(","); const rgba = []; for (let i = 0; i < parts.length; i++) { const part = parts[i]; let value = Number.parseFloat(part); if (!Number.isFinite(value)) { return; } if (part.includes("%")) { value = clamp(0, value, 100); value /= 100; } else if (i === 3) { value = clamp(0, value, 1); } else { value = clamp(0, value, 255); value /= 255; } rgba.push(value); } return rgba; } static fromRgbaString(str) { const rgba = _Color.stringToRgba(str); if (rgba) { if (rgba.length === 3) { return new _Color(rgba[0], rgba[1], rgba[2]); } else if (rgba.length === 4) { return new _Color(rgba[0], rgba[1], rgba[2], rgba[3]); } } throw new Error(`Malformed rgb/rgba color string: '${str}'`); } static fromArray(arr) { if (arr.length === 4) { return new _Color(arr[0], arr[1], arr[2], arr[3]); } if (arr.length === 3) { return new _Color(arr[0], arr[1], arr[2]); } throw new Error("The given array should contain 3 or 4 color components (numbers)."); } static fromHSB(h, s, b, alpha = 1) { const rgb = _Color.HSBtoRGB(h, s, b); return new _Color(rgb[0], rgb[1], rgb[2], alpha); } static fromHSL(h, s, l, alpha = 1) { const rgb = _Color.HSLtoRGB(h, s, l); return new _Color(rgb[0], rgb[1], rgb[2], alpha); } static fromOKLCH(l, c, h, alpha = 1) { const rgb = _Color.OKLCHtoRGB(l, c, h); return new _Color(rgb[0], rgb[1], rgb[2], alpha); } static padHex(str) { return str.length === 1 ? "0" + str : str; } toHexString() { let hex = "#" + _Color.padHex(Math.round(this.r * 255).toString(16)) + _Color.padHex(Math.round(this.g * 255).toString(16)) + _Color.padHex(Math.round(this.b * 255).toString(16)); if (this.a < 1) { hex += _Color.padHex(Math.round(this.a * 255).toString(16)); } return hex; } toRgbaString(fractionDigits = 3) { const components = [Math.round(this.r * 255), Math.round(this.g * 255), Math.round(this.b * 255)]; const k = Math.pow(10, fractionDigits); if (this.a !== 1) { components.push(Math.round(this.a * k) / k); return `rgba(${components.join(", ")})`; } return `rgb(${components.join(", ")})`; } toString() { if (this.a === 1) { return this.toHexString(); } return this.toRgbaString(); } toHSB() { return _Color.RGBtoHSB(this.r, this.g, this.b); } static RGBtoOKLCH(r, g, b) { const LSRGB0 = srgbToLinear(r); const LSRGB1 = srgbToLinear(g); const LSRGB2 = srgbToLinear(b); const LMS0 = Math.cbrt(0.4122214708 * LSRGB0 + 0.5363325363 * LSRGB1 + 0.0514459929 * LSRGB2); const LMS1 = Math.cbrt(0.2119034982 * LSRGB0 + 0.6806995451 * LSRGB1 + 0.1073969566 * LSRGB2); const LMS2 = Math.cbrt(0.0883024619 * LSRGB0 + 0.2817188376 * LSRGB1 + 0.6299787005 * LSRGB2); const OKLAB0 = 0.2104542553 * LMS0 + 0.793617785 * LMS1 - 0.0040720468 * LMS2; const OKLAB1 = 1.9779984951 * LMS0 - 2.428592205 * LMS1 + 0.4505937099 * LMS2; const OKLAB2 = 0.0259040371 * LMS0 + 0.7827717662 * LMS1 - 0.808675766 * LMS2; const hue = Math.atan2(OKLAB2, OKLAB1) * 180 / Math.PI; const OKLCH0 = OKLAB0; const OKLCH1 = Math.hypot(OKLAB1, OKLAB2); const OKLCH2 = hue >= 0 ? hue : hue + 360; return [OKLCH0, OKLCH1, OKLCH2]; } static OKLCHtoRGB(l, c, h) { const OKLAB0 = l; const OKLAB1 = c * Math.cos(h * Math.PI / 180); const OKLAB2 = c * Math.sin(h * Math.PI / 180); const LMS0 = (OKLAB0 + 0.3963377774 * OKLAB1 + 0.2158037573 * OKLAB2) ** 3; const LMS1 = (OKLAB0 - 0.1055613458 * OKLAB1 - 0.0638541728 * OKLAB2) ** 3; const LMS2 = (OKLAB0 - 0.0894841775 * OKLAB1 - 1.291485548 * OKLAB2) ** 3; const LSRGB0 = 4.0767416621 * LMS0 - 3.3077115913 * LMS1 + 0.2309699292 * LMS2; const LSRGB1 = -1.2684380046 * LMS0 + 2.6097574011 * LMS1 - 0.3413193965 * LMS2; const LSRGB2 = -0.0041960863 * LMS0 - 0.7034186147 * LMS1 + 1.707614701 * LMS2; const SRGB0 = srgbFromLinear(LSRGB0); const SRGB1 = srgbFromLinear(LSRGB1); const SRGB2 = srgbFromLinear(LSRGB2); return [SRGB0, SRGB1, SRGB2]; } static RGBtoHSL(r, g, b) { const min = Math.min(r, g, b); const max = Math.max(r, g, b); const l = (max + min) / 2; let h; let s; if (max === min) { h = 0; s = 0; } else { const delta3 = max - min; s = l > 0.5 ? delta3 / (2 - max - min) : delta3 / (max + min); if (max === r) { h = (g - b) / delta3 + (g < b ? 6 : 0); } else if (max === g) { h = (b - r) / delta3 + 2; } else { h = (r - g) / delta3 + 4; } h *= 360 / 6; } return [h, s, l]; } static HSLtoRGB(h, s, l) { h = (h % 360 + 360) % 360; if (s === 0) { return [l, l, l]; } const q = l < 0.5 ? l * (1 + s) : l + s - l * s; const p = 2 * l - q; function hueToRgb(t) { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; return p; } const r = hueToRgb(h / 360 + 1 / 3); const g = hueToRgb(h / 360); const b = hueToRgb(h / 360 - 1 / 3); return [r, g, b]; } /** * Converts the given RGB triple to an array of HSB (HSV) components. */ static RGBtoHSB(r, g, b) { const min = Math.min(r, g, b); const max = Math.max(r, g, b); const S = max === 0 ? 0 : (max - min) / max; let H = 0; if (min !== max) { const delta3 = max - min; const rc = (max - r) / delta3; const gc = (max - g) / delta3; const bc = (max - b) / delta3; if (r === max) { H = bc - gc; } else if (g === max) { H = 2 + rc - bc; } else { H = 4 + gc - rc; } H /= 6; if (H < 0) { H = H + 1; } } return [H * 360, S, max]; } /** * Converts the given HSB (HSV) triple to an array of RGB components. */ static HSBtoRGB(H, S, B) { H = (H % 360 + 360) % 360 / 360; let r = 0; let g = 0; let b = 0; if (S === 0) { r = g = b = B; } else { const h = (H - Math.floor(H)) * 6; const f = h - Math.floor(h); const p = B * (1 - S); const q = B * (1 - S * f); const t = B * (1 - S * (1 - f)); switch (Math.trunc(h)) { case 0: r = B; g = t; b = p; break; case 1: r = q; g = B; b = p; break; case 2: r = p; g = B; b = t; break; case 3: r = p; g = q; b = B; break; case 4: r = t; g = p; b = B; break; case 5: r = B; g = p; b = q; break; } } return [r, g, b]; } static mix(c0, c1, t) { return new _Color(lerp(c0.r, c1.r, t), lerp(c0.g, c1.g, t), lerp(c0.b, c1.b, t), lerp(c0.a, c1.a, t)); } static lighten(c, t) { const oklch = _Color.RGBtoOKLCH(c.r, c.g, c.b); return _Color.fromOKLCH(clamp(0, oklch[0] + t, 1), oklch[1], oklch[2]); } static darken(c, t) { const oklch = _Color.RGBtoOKLCH(c.r, c.g, c.b); return _Color.fromOKLCH(clamp(0, oklch[0] - t, 1), oklch[1], oklch[2]); } static interpolate(colors, count) { const step = 1 / (colors.length - 1); const oklchColors = colors.map((c) => _Color.RGBtoOKLCH(c.r, c.g, c.b)); return Array.from({ length: count }, (_, i) => { const t = i / (count - 1); const index = colors.length <= 2 ? 0 : Math.min(Math.floor(t * (colors.length - 1)), colors.length - 2); const q = (t - index * step) / step; const c0 = oklchColors[index]; const c1 = oklchColors[index + 1]; return _Color.fromOKLCH(lerp(c0[0], c1[0], q), lerp(c0[1], c1[1], q), lerp(c0[2], c1[2], q)); }); } }; /** * CSS Color Module Level 4: * https://drafts.csswg.org/css-color/#named-colors */ _Color.nameToHex = /* @__PURE__ */ new Map([ ["aliceblue", "#F0F8FF"], ["antiquewhite", "#FAEBD7"], ["aqua", "#00FFFF"], ["aquamarine", "#7FFFD4"], ["azure", "#F0FFFF"], ["beige", "#F5F5DC"], ["bisque", "#FFE4C4"], ["black", "#000000"], ["blanchedalmond", "#FFEBCD"], ["blue", "#0000FF"], ["blueviolet", "#8A2BE2"], ["brown", "#A52A2A"], ["burlywood", "#DEB887"], ["cadetblue", "#5F9EA0"], ["chartreuse", "#7FFF00"], ["chocolate", "#D2691E"], ["coral", "#FF7F50"], ["cornflowerblue", "#6495ED"], ["cornsilk", "#FFF8DC"], ["crimson", "#DC143C"], ["cyan", "#00FFFF"], ["darkblue", "#00008B"], ["darkcyan", "#008B8B"], ["darkgoldenrod", "#B8860B"], ["darkgray", "#A9A9A9"], ["darkgreen", "#006400"], ["darkgrey", "#A9A9A9"], ["darkkhaki", "#BDB76B"], ["darkmagenta", "#8B008B"], ["darkolivegreen", "#556B2F"], ["darkorange", "#FF8C00"], ["darkorchid", "#9932CC"], ["darkred", "#8B0000"], ["darksalmon", "#E9967A"], ["darkseagreen", "#8FBC8F"], ["darkslateblue", "#483D8B"], ["darkslategray", "#2F4F4F"], ["darkslategrey", "#2F4F4F"], ["darkturquoise", "#00CED1"], ["darkviolet", "#9400D3"], ["deeppink", "#FF1493"], ["deepskyblue", "#00BFFF"], ["dimgray", "#696969"], ["dimgrey", "#696969"], ["dodgerblue", "#1E90FF"], ["firebrick", "#B22222"], ["floralwhite", "#FFFAF0"], ["forestgreen", "#228B22"], ["fuchsia", "#FF00FF"], ["gainsboro", "#DCDCDC"], ["ghostwhite", "#F8F8FF"], ["gold", "#FFD700"], ["goldenrod", "#DAA520"], ["gray", "#808080"], ["green", "#008000"], ["greenyellow", "#ADFF2F"], ["grey", "#808080"], ["honeydew", "#F0FFF0"], ["hotpink", "#FF69B4"], ["indianred", "#CD5C5C"], ["indigo", "#4B0082"], ["ivory", "#FFFFF0"], ["khaki", "#F0E68C"], ["lavender", "#E6E6FA"], ["lavenderblush", "#FFF0F5"], ["lawngreen", "#7CFC00"], ["lemonchiffon", "#FFFACD"], ["lightblue", "#ADD8E6"], ["lightcoral", "#F08080"], ["lightcyan", "#E0FFFF"], ["lightgoldenrodyellow", "#FAFAD2"], ["lightgray", "#D3D3D3"], ["lightgreen", "#90EE90"], ["lightgrey", "#D3D3D3"], ["lightpink", "#FFB6C1"], ["lightsalmon", "#FFA07A"], ["lightseagreen", "#20B2AA"], ["lightskyblue", "#87CEFA"], ["lightslategray", "#778899"], ["lightslategrey", "#778899"], ["lightsteelblue", "#B0C4DE"], ["lightyellow", "#FFFFE0"], ["lime", "#00FF00"], ["limegreen", "#32CD32"], ["linen", "#FAF0E6"], ["magenta", "#FF00FF"], ["maroon", "#800000"], ["mediumaquamarine", "#66CDAA"], ["mediumblue", "#0000CD"], ["mediumorchid", "#BA55D3"], ["mediumpurple", "#9370DB"], ["mediumseagreen", "#3CB371"], ["mediumslateblue", "#7B68EE"], ["mediumspringgreen", "#00FA9A"], ["mediumturquoise", "#48D1CC"], ["mediumvioletred", "#C71585"], ["midnightblue", "#191970"], ["mintcream", "#F5FFFA"], ["mistyrose", "#FFE4E1"], ["moccasin", "#FFE4B5"], ["navajowhite", "#FFDEAD"], ["navy", "#000080"], ["oldlace", "#FDF5E6"], ["olive", "#808000"], ["olivedrab", "#6B8E23"], ["orange", "#FFA500"], ["orangered", "#FF4500"], ["orchid", "#DA70D6"], ["palegoldenrod", "#EEE8AA"], ["palegreen", "#98FB98"], ["paleturquoise", "#AFEEEE"], ["palevioletred", "#DB7093"], ["papayawhip", "#FFEFD5"], ["peachpuff", "#FFDAB9"], ["peru", "#CD853F"], ["pink", "#FFC0CB"], ["plum", "#DDA0DD"], ["powderblue", "#B0E0E6"], ["purple", "#800080"], ["rebeccapurple", "#663399"], ["red", "#FF0000"], ["rosybrown", "#BC8F8F"], ["royalblue", "#4169E1"], ["saddlebrown", "#8B4513"], ["salmon", "#FA8072"], ["sandybrown", "#F4A460"], ["seagreen", "#2E8B57"], ["seashell", "#FFF5EE"], ["sienna", "#A0522D"], ["silver", "#C0C0C0"], ["skyblue", "#87CEEB"], ["slateblue", "#6A5ACD"], ["slategray", "#708090"], ["slategrey", "#708090"], ["snow", "#FFFAFA"], ["springgreen", "#00FF7F"], ["steelblue", "#4682B4"], ["tan", "#D2B48C"], ["teal", "#008080"], ["thistle", "#D8BFD8"], ["tomato", "#FF6347"], ["transparent", "#00000000"], ["turquoise", "#40E0D0"], ["violet", "#EE82EE"], ["wheat", "#F5DEB3"], ["white", "#FFFFFF"], ["whitesmoke", "#F5F5F5"], ["yellow", "#FFFF00"], ["yellowgreen", "#9ACD32"] ]); var Color = _Color; // packages/ag-charts-core/src/config/themeUtil.ts var DIRECTION_SWAP_AXES = { x: { position: "bottom" /* BOTTOM */, type: { $if: [ { $eq: [{ $path: ["/series/0/direction", void 0] }, "horizontal"] }, "number" /* NUMBER */, "category" /* CATEGORY */ ] } }, y: { position: "left" /* LEFT */, type: { $if: [ { $eq: [{ $path: ["/series/0/direction", void 0] }, "horizontal"] }, "category" /* CATEGORY */, "number" /* NUMBER */ ] } } }; var SAFE_FILL_OPERATION = { $if: [ { $or: [ { $isGradient: { $palette: "fill" } }, { $isPattern: { $palette: "fill" } }, { $isImage: { $value: "$1" } } ] }, { $palette: "fillFallback" }, { $palette: "fill" } ] }; var SAFE_FILLS_OPERATION = { $if: [ { $or: [ { $isGradient: { $palette: "fill" } }, { $isPattern: { $palette: "fill" } }, { $isImage: { $value: "$1" } } ] }, { $palette: "fillsFallback" }, { $palette: "fills" } ] }; var SAFE_STROKE_FILL_OPERATION = { $if: [ { $isGradient: { $palette: "fill" } }, { $palette: "fillFallback" }, { $if: [ { $isPattern: { $palette: "fill" } }, { $path: ["/stroke", { $palette: "fillFallback" }, { $palette: "fill" }] }, { $palette: "fill" } ] } ] }; var SAFE_RANGE2_OPERATION = { $if: [ { $or: [ { $isGradient: { $palette: "fill" } }, { $isPattern: { $palette: "fill" } }, { $isImage: { $value: "$1" } } ] }, [{ $palette: "fillFallback" }, { $palette: "fillFallback" }], { $palette: "range2" } ] }; var FILL_GRADIENT_BLANK_DEFAULTS = { type: "gradient", gradient: "linear", bounds: "item", colorStops: [{ color: "black" }], rotation: 0, reverse: false, colorSpace: "rgb" }; var FILL_GRADIENT_LINEAR_DEFAULTS = { type: "gradient", gradient: "linear", bounds: "item", colorStops: { $shallow: { $map: [{ color: { $value: "$1" } }, { $palette: "gradient" }] } }, rotation: 0, reverse: false, colorSpace: "rgb" }; var FILL_GRADIENT_LINEAR_HIERARCHY_DEFAULTS = { ...FILL_GRADIENT_LINEAR_DEFAULTS, colorStops: { $shallow: [ { color: { $mix: [{ $path: ["/1", { $palette: "fill" }, { $palette: "hierarchyColors" }] }, "black", 0.15] } }, { color: { $mix: [{ $path: ["/1", { $palette: "fill" }, { $palette: "hierarchyColors" }] }, "white", 0.15] } } ] } }; var FILL_GRADIENT_LINEAR_SINGLE_DEFAULTS = { ...FILL_GRADIENT_LINEAR_DEFAULTS, colorStops: { $map: [{ color: { $value: "$1" } }, { $path: ["/0", void 0, { $palette: "gradients" }] }] } }; var FILL_GRADIENT_LINEAR_KEYED_DEFAULTS = (key) => ({ ...FILL_GRADIENT_LINEAR_DEFAULTS, colorStops: { $shallow: { $if: [ { $or: [ { $isGradient: { $palette: `${key}.fill` } }, { $isPattern: { $palette: `${key}.fill` } }, { $isImage: { $palette: `${key}.fill` } } ] }, { $path: ["/colorStops", void 0, { $palette: `${key}.fill` }] }, [ { color: { $mix: [{ $palette: `${key}.fill` }, "black", 0.15] } }, { color: { $mix: [{ $palette: `${key}.fill` }, "white", 0.15] } } ] ] } } }); var FILL_GRADIENT_RADIAL_DEFAULTS = { type: "gradient", gradient: "radial", bounds: "item", colorStops: { $shallow: { $map: [{ color: { $value: "$1" } }, { $palette: "gradient" }] } }, rotation: 0, reverse: false, colorSpace: "rgb" }; var FILL_GRADIENT_RADIAL_REVERSED_DEFAULTS = { ...FILL_GRADIENT_RADIAL_DEFAULTS, reverse: true }; var FILL_GRADIENT_RADIAL_SERIES_DEFAULTS = { ...FILL_GRADIENT_RADIAL_DEFAULTS, bounds: "series" }; var FILL_GRADIENT_RADIAL_REVERSED_SERIES_DEFAULTS = { ...FILL_GRADIENT_RADIAL_DEFAULTS, bounds: "series", reverse: true }; var FILL_GRADIENT_CONIC_SERIES_DEFAULTS = { type: "gradient", gradient: "conic", bounds: "series", colorStops: { $map: [{ color: { $value: "$1" } }, { $palette: "gradient" }] }, rotation: 0, reverse: false, colorSpace: "rgb" }; var FILL_PATTERN_DEFAULTS = { type: "pattern", pattern: "forward-slanted-lines", width: { $isUserOption: ["./height", { $path: "./height" }, 10] }, height: { $isUserOption: ["./width", { $path: "./width" }, 10] }, padding: 2, fill: { $if: [ { $or: [{ $isGradient: { $palette: "fill" } }, { $isImage: { $palette: "fill" } }] }, { $palette: "fillFallback" }, { $if: [ { $isPattern: { $palette: "fill" } }, { $path: ["/fill", { $palette: "fillFallback" }, { $palette: "fill" }] }, { $palette: "fill" } ] } ] }, fillOpacity: 1, stroke: SAFE_STROKE_FILL_OPERATION, strokeOpacity: 1, strokeWidth: { $switch: [ { $path: "./pattern" }, 0, [["backward-slanted-lines", "forward-slanted-lines", "horizontal-lines", "vertical-lines"], 4] ] }, backgroundFill: "none", backgroundFillOpacity: 1, rotation: 0, scale: 1 }; var FILL_PATTERN_SINGLE_DEFAULTS = { ...FILL_PATTERN_DEFAULTS, stroke: { $if: [ { $isGradient: { $palette: "fill" } }, { $path: ["/0", void 0, { $palette: "fillsFallback" }] }, { $if: [ { $isPattern: { $palette: "fill" } }, { $path: [ "/stroke", { $path: ["/0", void 0, { $palette: "fillsFallback" }] }, { $path: ["/0", void 0, { $palette: "fills" }] } ] }, { $path: ["/0", void 0, { $palette: "fills" }] } ] } ] }, fill: { $if: [ { $or: [{ $isGradient: { $palette: "fill" } }, { $isImage: { $palette: "fill" } }] }, { $path: ["/0", void 0, { $palette: "fillsFallback" }] }, { $if: [ { $isPattern: { $palette: "fill" } }, { $path: [ "/fill", { $path: ["/0", void 0, { $palette: "fillsFallback" }] }, { $path: ["/0", void 0, { $palette: "fills" }] } ] }, { $path: ["/0", void 0, { $palette: "fills" }] } ] } ] } }; var FILL_PATTERN_BLANK_DEFAULTS = { type: "pattern", pattern: "forward-slanted-lines", width: 8, height: 8, padding: 1, fill: "black", fillOpacity: 1, backgroundFill: "white", backgroundFillOpacity: 1, stroke: "black", strokeOpacity: 1, strokeWidth: 1, rotation: 0, scale: 1 }; var FILL_PATTERN_HIERARCHY_DEFAULTS = { ...FILL_PATTERN_DEFAULTS, fill: { $path: ["/1", { $palette: "fill" }, { $palette: "hierarchyColors" }] }, stroke: { $path: ["/1", { $palette: "fill" }, { $palette: "hierarchyColors" }] } }; var FILL_PATTERN_KEYED_DEFAULTS = (key) => ({ ...FILL_PATTERN_DEFAULTS, stroke: { $if: [ { $isGradient: { $palette: `${key}.fill` } }, { $palette: "fillFallback" }, { $if: [ { $isPattern: { $palette: `${key}.fill` } }, { $path: ["/stroke", { $palette: "fillFallback" }, { $palette: `${key}.fill` }] }, { $palette: `${key}.fill` } ] } ] } }); var FILL_IMAGE_DEFAULTS = { type: "image", backgroundFill: { $palette: "fillFallback" }, backgroundFillOpacity: 1, repeat: "no-repeat", fit: "contain", rotation: 0 }; var FILL_IMAGE_BLANK_DEFAULTS = { type: "image", backgroundFill: "black", backgroundFillOpacity: 1, rotation: 0, repeat: "no-repeat", fit: "contain", width: 8, height: 8 }; function getSequentialColors(colors) { return mapValues(colors, (value) => { const color2 = Color.fromString(value); return [Color.darken(color2, 0.15).toString(), value, Color.lighten(color2, 0.15).toString()]; }); } var LABEL_BOXING_DEFAULTS = { padding: 8, cornerRadius: 4, fill: { $if: [ { $and: [ { $eq: [{ $path: "./fill/type" }, "image"] }, { $isUserOption: ["./fill/backgroundFill", false, true] } ] }, { backgroundFill: "transparent" }, void 0 ] }, border: { enabled: { $isUserOption: ["../border", true, false] }, strokeWidth: 1, stroke: { $foregroundOpacity: 0.08 } } }; var MULTI_SERIES_HIGHLIGHT_STYLE = { enabled: true, unhighlightedItem: { opacity: 0.6 }, unhighlightedSeries: { opacity: 0.2 } }; var MARKER_SERIES_HIGHLIGHT_STYLE = { enabled: true, unhighlightedSeries: { opacity: 0.2 } }; var PART_WHOLE_HIGHLIGHT_STYLE = { enabled: true, unhighlightedItem: { opacity: 0.2 }, unhighlightedSeries: { opacity: 0.2 } }; var SINGLE_SERIES_HIGHLIGHT_STYLE = { enabled: true, unhighlightedItem: { opacity: 0.2 } }; var LEGEND_CONTAINER_THEME = { border: { enabled: false, stroke: { $foregroundBackgroundMix: 0.25 }, strokeOpacity: 1, strokeWidth: 1 }, cornerRadius: 4, fillOpacity: 1, padding: { $if: [{ $eq: [{ $path: "./border/enabled" }, true] }, 5, { $isUserOption: ["./fill", 5, 0] }] } }; var SEGMENTATION_DEFAULTS = { enabled: false, key: "x", segments: { $apply: { fill: { $applySwitch: [ { $path: "type" }, { $path: "../../../fill" }, ["gradient", FILL_GRADIENT_LINEAR_DEFAULTS], ["image", FILL_IMAGE_DEFAULTS], ["pattern", FILL_PATTERN_DEFAULTS] ] }, stroke: { $path: "../../../stroke" }, fillOpacity: { $path: "../../../fillOpacity" }, strokeWidth: { $isUserOption: [ "./stroke", { $isUserOption: [ "../../../strokeWidth", { $path: "../../../strokeWidth" }, { $if: [ { $greaterThan: [{ $path: "../../../strokeWidth" }, 0] }, { $path: "../../../strokeWidth" }, 2 ] } ] }, { $path: "../../../strokeWidth" } ] }, strokeOpacity: { $path: "../../../strokeOpacity" }, lineDash: { $path: "../../../lineDash" }, lineDashOffset: { $path: "../../../lineDashOffset" } } } }; // packages/ag-charts-core/src/state/memento.ts var MementoCaretaker = class _MementoCaretaker { constructor(version) { this.version = version.split("-")[0]; } save(...originators) { const packet = { version: this.version }; for (const originator of Object.values(originators)) { packet[originator.mementoOriginatorKey] = this.encode(originator, originator.createMemento()); } return packet; } restore(blob, ...originators) { if (typeof blob !== "object") { warnOnce(`Could not restore data of type [${typeof blob}], expecting an object, ignoring.`); return; } if (blob == null) { warnOnce(`Could not restore data of type [null], expecting an object, ignoring.`); return; } if (!("version" in blob) || typeof blob.version !== "string") { warnOnce(`Could not restore data, missing [version] string in object, ignoring.`); return; } for (const originator of originators) { const memento = this.decode(originator, blob[originator.mementoOriginatorKey]); const messages = []; if (!originator.guardMemento(memento, messages)) { const messagesString = messages.length > 0 ? ` ${messages.join("\n\n")} ` : ""; warnOnce( `Could not restore [${originator.mementoOriginatorKey}] data, value was invalid, ignoring.${messagesString}`, memento ); return; } originator.restoreMemento(this.version, blob.version, memento); } } /** * Encode a memento as a serializable object, encoding any non-serializble types. */ encode(originator, memento) { try { return JSON.parse(JSON.stringify(memento, _MementoCaretaker.encodeTypes)); } catch (error2) { throw new Error(`Failed to encode [${originator.mementoOriginatorKey}] value [${error2}].`, { cause: error2 }); } } /** * Decode an encoded memento, decoding any non-serializable types. */ decode(originator, encoded) { if (encoded == null) return encoded; try { return JSON.parse(JSON.stringify(encoded), _MementoCaretaker.decodeTypes); } catch (error2) { throw new Error(`Failed to decode [${originator.mementoOriginatorKey}] value [${error2}].`, { cause: error2 }); } } static encodeTypes(key, value) { if (isDate(this[key])) { return { __type: "date", value: this[key].toISOString() }; } return value; } static decodeTypes(key, value) { if (isObject(this[key]) && "__type" in this[key] && this[key].__type === "date") { return new Date(this[key].value); } return value; } }; // packages/ag-charts-core/src/types/axisDirection.ts var ChartAxisDirection = /* @__PURE__ */ ((ChartAxisDirection3) => { ChartAxisDirection3["X"] = "x"; ChartAxisDirection3["Y"] = "y"; ChartAxisDirection3["Angle"] = "angle"; ChartAxisDirection3["Radius"] = "radius"; return ChartAxisDirection3; })(ChartAxisDirection || {}); // packages/ag-charts-core/src/types/updateType.ts var ChartUpdateType = /* @__PURE__ */ ((ChartUpdateType2) => { ChartUpdateType2[ChartUpdateType2["FULL"] = 0] = "FULL"; ChartUpdateType2[ChartUpdateType2["UPDATE_DATA"] = 1] = "UPDATE_DATA"; ChartUpdateType2[ChartUpdateType2["PROCESS_DATA"] = 2] = "PROCESS_DATA"; ChartUpdateType2[ChartUpdateType2["PROCESS_DOMAIN"] = 3] = "PROCESS_DOMAIN"; ChartUpdateType2[ChartUpdateType2["PROCESS_RANGE"] = 4] = "PROCESS_RANGE"; ChartUpdateType2[ChartUpdateType2["PERFORM_LAYOUT"] = 5] = "PERFORM_LAYOUT"; ChartUpdateType2[ChartUpdateType2["PRE_SERIES_UPDATE"] = 6] = "PRE_SERIES_UPDATE"; ChartUpdateType2[ChartUpdateType2["SERIES_UPDATE"] = 7] = "SERIES_UPDATE"; ChartUpdateType2[ChartUpdateType2["PRE_SCENE_RENDER"] = 8] = "PRE_SCENE_RENDER"; ChartUpdateType2[ChartUpdateType2["SCENE_RENDER"] = 9] = "SCENE_RENDER"; ChartUpdateType2[ChartUpdateType2["NONE"] = 10] = "NONE"; return ChartUpdateType2; })(ChartUpdateType || {}); // packages/ag-charts-core/src/types/zIndexMap.ts var ZIndexMap = /* @__PURE__ */ ((ZIndexMap2) => { ZIndexMap2[ZIndexMap2["CHART_BACKGROUND"] = 0] = "CHART_BACKGROUND"; ZIndexMap2[ZIndexMap2["AXIS_BAND_HIGHLIGHT"] = 1] = "AXIS_BAND_HIGHLIGHT"; ZIndexMap2[ZIndexMap2["AXIS_GRID"] = 2] = "AXIS_GRID"; ZIndexMap2[ZIndexMap2["AXIS"] = 3] = "AXIS"; ZIndexMap2[ZIndexMap2["SERIES_AREA_CONTAINER"] = 4] = "SERIES_AREA_CONTAINER"; ZIndexMap2[ZIndexMap2["ZOOM_SELECTION"] = 5] = "ZOOM_SELECTION"; ZIndexMap2[ZIndexMap2["SERIES_CROSSLINE_RANGE"] = 6] = "SERIES_CROSSLINE_RANGE"; ZIndexMap2[ZIndexMap2["SERIES_LAYER"] = 7] = "SERIES_LAYER"; ZIndexMap2[ZIndexMap2["AXIS_FOREGROUND"] = 8] = "AXIS_FOREGROUND"; ZIndexMap2[ZIndexMap2["SERIES_CROSSHAIR"] = 9] = "SERIES_CROSSHAIR"; ZIndexMap2[ZIndexMap2["SERIES_CROSSLINE_LINE"] = 10] = "SERIES_CROSSLINE_LINE"; ZIndexMap2[ZIndexMap2["SERIES_ANNOTATION"] = 11] = "SERIES_ANNOTATION"; ZIndexMap2[ZIndexMap2["CHART_ANNOTATION"] = 12] = "CHART_ANNOTATION"; ZIndexMap2[ZIndexMap2["CHART_ANNOTATION_FOCUSED"] = 13] = "CHART_ANNOTATION_FOCUSED"; ZIndexMap2[ZIndexMap2["STATUS_BAR"] = 14] = "STATUS_BAR"; ZIndexMap2[ZIndexMap2["SERIES_LABEL"] = 15] = "SERIES_LABEL"; ZIndexMap2[ZIndexMap2["LEGEND"] = 16] = "LEGEND"; ZIndexMap2[ZIndexMap2["NAVIGATOR"] = 17] = "NAVIGATOR"; ZIndexMap2[ZIndexMap2["FOREGROUND"] = 18] = "FOREGROUND"; return ZIndexMap2; })(ZIndexMap || {}); var SeriesZIndexMap = /* @__PURE__ */ ((SeriesZIndexMap2) => { SeriesZIndexMap2[SeriesZIndexMap2["BACKGROUND"] = 0] = "BACKGROUND"; SeriesZIndexMap2[SeriesZIndexMap2["ANY_CONTENT"] = 1] = "ANY_CONTENT"; return SeriesZIndexMap2; })(SeriesZIndexMap || {}); var SeriesContentZIndexMap = /* @__PURE__ */ ((SeriesContentZIndexMap2) => { SeriesContentZIndexMap2[SeriesContentZIndexMap2["FOREGROUND"] = 0] = "FOREGROUND"; SeriesContentZIndexMap2[SeriesContentZIndexMap2["HIGHLIGHT"] = 1] = "HIGHLIGHT"; SeriesContentZIndexMap2[SeriesContentZIndexMap2["LABEL"] = 2] = "LABEL"; return SeriesContentZIndexMap2; })(SeriesContentZIndexMap || {}); var PolarZIndexMap = /* @__PURE__ */ ((PolarZIndexMap2) => { PolarZIndexMap2[PolarZIndexMap2["BACKGROUND"] = 0] = "BACKGROUND"; PolarZIndexMap2[PolarZIndexMap2["FOREGROUND"] = 1] = "FOREGROUND"; PolarZIndexMap2[PolarZIndexMap2["HIGHLIGHT"] = 2] = "HIGHLIGHT"; PolarZIndexMap2[PolarZIndexMap2["LABEL"] = 3] = "LABEL"; return PolarZIndexMap2; })(PolarZIndexMap || {}); // packages/ag-charts-core/src/chart/legendUtil.ts function expandLegendPosition(position) { const { placement = "bottom", floating = false, xOffset = 0, yOffset = 0 } = typeof position === "string" ? { placement: position, floating: false } : position; return { placement, floating, xOffset, yOffset }; } // packages/ag-charts-core/src/state/properties.ts var BaseProperties = class { handleUnknownProperties(_unknownKeys, _properties) { } set(properties) { const { className = this.constructor.name } = this.constructor; if (properties == null) { this.clear(); return this; } if (typeof properties !== "object") { warn(`unable to set ${className} - expecting a properties object`); return this; } const keys = new Set(Object.keys(properties)); for (const propertyKey of listDecoratedProperties(this)) { if (keys.has(propertyKey)) { const value = properties[propertyKey]; const self = this; if (isProperties(self[propertyKey])) { if (self[propertyKey] instanceof PropertiesArray) { const array2 = self[propertyKey].reset(value); if (array2 == null) { warn(`unable to set [${String(propertyKey)}] - expecting a properties array`); } else { self[propertyKey] = array2; } } else { self[propertyKey].set(value); } } else if (isPlainObject(value)) { self[propertyKey] = merge(value, self[propertyKey] ?? {}); } else { self[propertyKey] = value; } keys.delete(propertyKey); } } this.handleUnknownProperties(keys, properties); for (const unknownKey of keys) { warn(`unable to set [${String(unknownKey)}] in ${className} - property is unknown`); } return this; } clear() { for (const propertyKey of listDecoratedProperties(this)) { const currentValue = this[propertyKey]; if (isProperties(currentValue)) { currentValue.clear(); } else { this[propertyKey] = void 0; } } return this; } toJson() { return listDecoratedProperties(this).reduce((object2, propertyKey) => { const propertyValue = this[propertyKey]; object2[String(propertyKey)] = isProperties(propertyValue) ? propertyValue.toJson() : propertyValue; return object2; }, {}); } }; var PropertiesArray = class _PropertiesArray extends Array { constructor(itemFactory, ...properties) { super(properties.length); const isConstructor = (value2) => Boolean(value2?.prototype?.constructor?.name); const value = isConstructor(itemFactory) ? (params) => new itemFactory().set(params) : itemFactory; Object.defineProperty(this, "itemFactory", { value, enumerable: false, configurable: false }); this.set(properties); } set(properties) { if (isArray(properties)) { this.length = properties.length; for (let i = 0; i < properties.length; i++) { this[i] = this.itemFactory(properties[i]); } } return this; } reset(properties) { if (Array.isArray(properties)) { return new _PropertiesArray(this.itemFactory, ...properties); } } toJson() { return this.map((value) => value?.toJson?.() ?? value); } }; function isProperties(value) { return value instanceof BaseProperties || value instanceof PropertiesArray; } // packages/ag-charts-core/src/chart/interpolationProperties.ts var InterpolationProperties = class extends BaseProperties { constructor() { super(...arguments); this.type = "linear"; this.tension = 1; this.position = "end"; } }; __decorateClass([ addFakeTransformToInstanceProperty ], InterpolationProperties.prototype, "type", 2); __decorateClass([ addFakeTransformToInstanceProperty ], InterpolationProperties.prototype, "tension", 2); __decorateClass([ addFakeTransformToInstanceProperty ], InterpolationProperties.prototype, "position", 2); // packages/ag-charts-core/src/utils/data/numberArray.ts function clampArray(value, array2) { const [min, max] = findMinMax(array2); return clamp(min, value, max); } function findMinMax(array2) { if (array2.length === 0) return []; const result = [Infinity, -Infinity]; for (const val of array2) { if (val < result[0]) result[0] = val; if (val > result[1]) result[1] = val; } return result; } function findRangeExtent(array2) { const [min, max] = findMinMax(array2); return max - min; } function nextPowerOf2(value) { value = Math.trunc(value); if (value <= 0) return 1; if (value === 1) return 2; return 1 << 32 - Math.clz32(value - 1); } function previousPowerOf2(value) { value = Math.trunc(value); if (value <= 0) return 0; if (value === 1) return 1; return 1 << 31 - Math.clz32(value); } // packages/ag-charts-core/src/utils/aggregation.ts var AGGREGATION_INDEX_X_MIN = 0; var AGGREGATION_INDEX_X_MAX = 1; var AGGREGATION_INDEX_Y_MIN = 2; var AGGREGATION_INDEX_Y_MAX = 3; var AGGREGATION_SPAN = 4; var AGGREGATION_THRESHOLD = 1e3; var AGGREGATION_MAX_POINTS = 10; var AGGREGATION_MIN_RANGE = 64; var AGGREGATION_INDEX_UNSET = 4294967295; var SMALLEST_INTERVAL_MIN_RECURSE = 3; var SMALLEST_INTERVAL_RECURSE_LIMIT = 20; var SMALLEST_INTERVAL_MAX_INDEX_ADJUSTMENTS = 100; function estimateSmallestPixelIntervalIter(xValues, d0, d1, startDatumIndex, endDatumIndex, currentSmallestInterval, depth, xNeedsValueOf) { let indexAdjustments = 0; while (indexAdjustments < SMALLEST_INTERVAL_MAX_INDEX_ADJUSTMENTS && xValues[startDatumIndex] == null && startDatumIndex < endDatumIndex) { startDatumIndex += 1; indexAdjustments += 1; } while (indexAdjustments < SMALLEST_INTERVAL_MAX_INDEX_ADJUSTMENTS && xValues[endDatumIndex] == null && endDatumIndex > startDatumIndex) { endDatumIndex -= 1; indexAdjustments += 1; } if (indexAdjustments >= SMALLEST_INTERVAL_MAX_INDEX_ADJUSTMENTS || startDatumIndex >= endDatumIndex) { return currentSmallestInterval; } const ratio2 = Number.isFinite(d0) ? aggregationXRatioForXValue(xValues[endDatumIndex], d0, d1, xNeedsValueOf) - aggregationXRatioForXValue(xValues[startDatumIndex], d0, d1, xNeedsValueOf) : aggregationXRatioForDatumIndex(endDatumIndex, xValues.length) - aggregationXRatioForDatumIndex(startDatumIndex, xValues.length); if (ratio2 === 0 || !Number.isFinite(ratio2)) return currentSmallestInterval; const currentInterval = Math.abs(ratio2) / (endDatumIndex - startDatumIndex); let recurse; if (depth < SMALLEST_INTERVAL_MIN_RECURSE) { recurse = true; } else if (depth > SMALLEST_INTERVAL_RECURSE_LIMIT) { recurse = false; } else { recurse = currentInterval <= currentSmallestInterval; } currentSmallestInterval = Math.min(currentSmallestInterval, currentInterval); if (!recurse) return currentSmallestInterval; const midIndex = Math.floor((startDatumIndex + endDatumIndex) / 2); const leadingInterval = estimateSmallestPixelIntervalIter( xValues, d0, d1, startDatumIndex, midIndex, currentSmallestInterval, depth + 1, xNeedsValueOf ); const trailingInterval = estimateSmallestPixelIntervalIter( xValues, d0, d1, midIndex + 1, endDatumIndex, currentSmallestInterval, depth + 1, xNeedsValueOf ); return Math.min(leadingInterval, trailingInterval, currentSmallestInterval); } function estimateSmallestPixelInterval(xValues, d0, d1, xNeedsValueOf) { return estimateSmallestPixelIntervalIter( xValues, d0, d1, 0, xValues.length - 1, 1 / (xValues.length - 1), 0, xNeedsValueOf ); } function aggregationRangeFittingPoints(xValues, d0, d1, opts) { if (Number.isFinite(d0)) { const smallestKeyInterval = opts?.smallestKeyInterval; const xNeedsValueOf = opts?.xNeedsValueOf ?? true; const smallestPixelInterval = smallestKeyInterval == null ? estimateSmallestPixelInterval(xValues, d0, d1, xNeedsValueOf) : smallestKeyInterval / (d1 - d0); return nextPowerOf2(Math.trunc(1 / smallestPixelInterval)) >> 3; } else { let power = Math.ceil(Math.log2(xValues.length)) - 1; power = Math.min(Math.max(power, 0), 24); return Math.trunc(2 ** power); } } function aggregationDomain(scale2, domainInput) { const { domain, sortMetadata } = domainInput; switch (scale2) { case "category": return [Number.NaN, Number.NaN]; case "number": case "time": case "ordinal-time": case "unit-time": { if (domain.length === 0) return [Infinity, -Infinity]; if (sortMetadata?.sortOrder === 1) { return [Number(domain[0]), Number(domain.at(-1))]; } if (sortMetadata?.sortOrder === -1) { return [Number(domain.at(-1)), Number(domain[0])]; } let min = Infinity; let max = -Infinity; for (const d of domain) { const value = Number(d); min = Math.min(min, value); max = Math.max(max, value); } return [min, max]; } case "color": case "log": case "mercator": return [0, 0]; } } function aggregationXRatioForDatumIndex(datumIndex, domainCount) { return datumIndex / domainCount; } function aggregationXRatioForXValue(xValue, d0, d1, xNeedsValueOf) { if (xNeedsValueOf) { return (xValue.valueOf() - d0) / (d1 - d0); } return (xValue - d0) / (d1 - d0); } function aggregationIndexForXRatio(xRatio, maxRange) { return Math.trunc(Math.min(Math.floor(xRatio * maxRange), maxRange - 1) * AGGREGATION_SPAN); } function aggregationBucketForDatum(xValues, d0, d1, maxRange, datumIndex, { xNeedsValueOf = true, xValuesLength } = {}) { const xValue = xValues[datumIndex]; if (xValue == null) return -1; const length2 = xValuesLength ?? xValues.length; const xRatio = Number.isFinite(d0) ? aggregationXRatioForXValue(xValue, d0, d1, xNeedsValueOf) : aggregationXRatioForDatumIndex(datumIndex, length2); return aggregationIndexForXRatio(xRatio, maxRange); } function aggregationDatumMatchesIndex(indexData, aggIndex, datumIndex, offsets) { for (const offset of offsets) { if (datumIndex === indexData[aggIndex + offset]) { return true; } } return false; } function createAggregationIndices(xValues, yMaxValues, yMinValues, d0, d1, maxRange, { positive, split = false, xNeedsValueOf = true, yNeedsValueOf = true, // Optional pre-allocated arrays to reuse (must be correct size: maxRange * AGGREGATION_SPAN) reuseIndexData, reuseValueData, reuseNegativeIndexData, reuseNegativeValueData } = {}) { const nan = Number.NaN; const requiredSize = maxRange * AGGREGATION_SPAN; const indexData = reuseIndexData?.length === requiredSize ? reuseIndexData : new Uint32Array(requiredSize); const valueData = reuseValueData?.length === requiredSize ? reuseValueData : new Float64Array(requiredSize); let negativeIndexData; let negativeValueData; if (split) { if (reuseNegativeIndexData?.length === requiredSize) { negativeIndexData = reuseNegativeIndexData; } else { negativeIndexData = new Uint32Array(requiredSize); } if (reuseNegativeValueData?.length === requiredSize) { negativeValueData = reuseNegativeValueData; } else { negativeValueData = new Float64Array(requiredSize); } } const continuous = Number.isFinite(d0) && Number.isFinite(d1); const domainCount = xValues.length; if (continuous) { valueData.fill(nan); indexData.fill(AGGREGATION_INDEX_UNSET); if (split) { negativeValueData.fill(nan); negativeIndexData.fill(AGGREGATION_INDEX_UNSET); } } const scaleFactor = continuous ? maxRange / (d1 - d0) : maxRange * (1 / domainCount); let lastAggIndex = -1; let cachedXMinIndex = -1; let cachedXMinValue = nan; let cachedXMaxIndex = -1; let cachedXMaxValue = nan; let cachedYMinIndex = -1; let cachedYMinValue = nan; let cachedYMaxIndex = -1; let cachedYMaxValue = nan; let negLastAggIndex = -1; let negCachedXMinIndex = -1; let negCachedXMinValue = nan; let negCachedXMaxIndex = -1; let negCachedXMaxValue = nan; let negCachedYMinIndex = -1; let negCachedYMinValue = nan; let negCachedYMaxIndex = -1; let negCachedYMaxValue = nan; const xValuesLength = xValues.length; const yArraysSame = yMaxValues === yMinValues; for (let datumIndex = 0; datumIndex < xValuesLength; datumIndex++) { const xValue = xValues[datumIndex]; if (xValue == null) continue; const yMaxValue = yMaxValues[datumIndex]; const yMinValue = yArraysSame ? yMaxValue : yMinValues[datumIndex]; let yMax; let yMin; if (yNeedsValueOf) { yMax = yMaxValue == null ? nan : yMaxValue.valueOf(); yMin = yMinValue == null ? nan : yMinValue.valueOf(); } else { yMax = yMaxValue ?? nan; yMin = yMinValue ?? nan; } let isPositiveDatum = true; if (split) { isPositiveDatum = yMax >= 0; } else if (positive != null && yMax >= 0 !== positive) { continue; } let scaledX; if (continuous) { if (xNeedsValueOf) { scaledX = (xValue.valueOf() - d0) * scaleFactor; } else { scaledX = (xValue - d0) * scaleFactor; } } else { scaledX = datumIndex * scaleFactor; } const bucketIndex = Math.floor(scaledX); const aggIndex = (bucketIndex < maxRange ? bucketIndex : maxRange - 1) << 2; if (isPositiveDatum) { if (aggIndex !== lastAggIndex) { if (lastAggIndex !== -1) { indexData[lastAggIndex] = cachedXMinIndex; indexData[lastAggIndex + 1] = cachedXMaxIndex; indexData[lastAggIndex + 2] = cachedYMinIndex; indexData[lastAggIndex + 3] = cachedYMaxIndex; valueData[lastAggIndex] = cachedXMinValue; valueData[lastAggIndex + 1] = cachedXMaxValue; valueData[lastAggIndex + 2] = cachedYMinValue; valueData[lastAggIndex + 3] = cachedYMaxValue; } lastAggIndex = aggIndex; cachedXMinIndex = -1; cachedXMinValue = nan; cachedXMaxIndex = -1; cachedXMaxValue = nan; cachedYMinIndex = -1; cachedYMinValue = nan; cachedYMaxIndex = -1; cachedYMaxValue = nan; } const yMinValid = yMin === yMin; const yMaxValid = yMax === yMax; if (cachedXMinIndex === -1) { cachedXMinIndex = datumIndex; cachedXMinValue = scaledX; cachedXMaxIndex = datumIndex; cachedXMaxValue = scaledX; if (yMinValid) { cachedYMinIndex = datumIndex; cachedYMinValue = yMin; } if (yMaxValid) { cachedYMaxIndex = datumIndex; cachedYMaxValue = yMax; } } else { if (scaledX < cachedXMinValue) { cachedXMinIndex = datumIndex; cachedXMinValue = scaledX; } if (scaledX > cachedXMaxValue) { cachedXMaxIndex = datumIndex; cachedXMaxValue = scaledX; } if (yMinValid && yMin < cachedYMinValue) { cachedYMinIndex = datumIndex; cachedYMinValue = yMin; } if (yMaxValid && yMax > cachedYMaxValue) { cachedYMaxIndex = datumIndex; cachedYMaxValue = yMax; } } } else { if (aggIndex !== negLastAggIndex) { if (negLastAggIndex !== -1) { negativeIndexData[negLastAggIndex] = negCachedXMinIndex; negativeIndexData[negLastAggIndex + 1] = negCachedXMaxIndex; negativeIndexData[negLastAggIndex + 2] = negCachedYMinIndex; negativeIndexData[negLastAggIndex + 3] = negCachedYMaxIndex; negativeValueData[negLastAggIndex] = negCachedXMinValue; negativeValueData[negLastAggIndex + 1] = negCachedXMaxValue; negativeValueData[negLastAggIndex + 2] = negCachedYMinValue; negativeValueData[negLastAggIndex + 3] = negCachedYMaxValue; } negLastAggIndex = aggIndex; negCachedXMinIndex = -1; negCachedXMinValue = nan; negCachedXMaxIndex = -1; negCachedXMaxValue = nan; negCachedYMinIndex = -1; negCachedYMinValue = nan; negCachedYMaxIndex = -1; negCachedYMaxValue = nan; } const yMinValid = yMin === yMin; const yMaxValid = yMax === yMax; if (negCachedXMinIndex === -1) { negCachedXMinIndex = datumIndex; negCachedXMinValue = scaledX; negCachedXMaxIndex = datumIndex; negCachedXMaxValue = scaledX; if (yMinValid) { negCachedYMinIndex = datumIndex; negCachedYMinValue = yMin; } if (yMaxValid) { negCachedYMaxIndex = datumIndex; negCachedYMaxValue = yMax; } } else { if (scaledX < negCachedXMinValue) { negCachedXMinIndex = datumIndex; negCachedXMinValue = scaledX; } if (scaledX > negCachedXMaxValue) { negCachedXMaxIndex = datumIndex; negCachedXMaxValue = scaledX; } if (yMinValid && yMin < negCachedYMinValue) { negCachedYMinIndex = datumIndex; negCachedYMinValue = yMin; } if (yMaxValid && yMax > negCachedYMaxValue) { negCachedYMaxIndex = datumIndex; negCachedYMaxValue = yMax; } } } } if (lastAggIndex !== -1) { indexData[lastAggIndex] = cachedXMinIndex; indexData[lastAggIndex + 1] = cachedXMaxIndex; indexData[lastAggIndex + 2] = cachedYMinIndex; indexData[lastAggIndex + 3] = cachedYMaxIndex; valueData[lastAggIndex] = cachedXMinValue; valueData[lastAggIndex + 1] = cachedXMaxValue; valueData[lastAggIndex + 2] = cachedYMinValue; valueData[lastAggIndex + 3] = cachedYMaxValue; } if (split && negLastAggIndex !== -1) { negativeIndexData[negLastAggIndex] = negCachedXMinIndex; negativeIndexData[negLastAggIndex + 1] = negCachedXMaxIndex; negativeIndexData[negLastAggIndex + 2] = negCachedYMinIndex; negativeIndexData[negLastAggIndex + 3] = negCachedYMaxIndex; negativeValueData[negLastAggIndex] = negCachedXMinValue; negativeValueData[negLastAggIndex + 1] = negCachedXMaxValue; negativeValueData[negLastAggIndex + 2] = negCachedYMinValue; negativeValueData[negLastAggIndex + 3] = negCachedYMaxValue; } return { indexData, valueData, negativeIndexData, negativeValueData }; } function compactAggregationIndices(indexData, valueData, maxRange, { inPlace = false, midpointData, reuseIndexData, reuseValueData } = {}) { const nextMaxRange = Math.trunc(maxRange / 2); const requiredSize = nextMaxRange * AGGREGATION_SPAN; let nextIndexData; if (inPlace) { nextIndexData = indexData; } else if (reuseIndexData?.length === requiredSize) { nextIndexData = reuseIndexData; } else { nextIndexData = new Uint32Array(requiredSize); } let nextValueData; if (inPlace) { nextValueData = valueData; } else if (reuseValueData?.length === requiredSize) { nextValueData = reuseValueData; } else { nextValueData = new Float64Array(requiredSize); } const nextMidpointData = midpointData ?? new Uint32Array(nextMaxRange); for (let i = 0; i < nextMaxRange; i += 1) { const aggIndex = Math.trunc(i * AGGREGATION_SPAN); const index0 = Math.trunc(aggIndex * 2); const index1 = Math.trunc(index0 + AGGREGATION_SPAN); const index1Unset = indexData[index1 + AGGREGATION_INDEX_X_MIN] === AGGREGATION_INDEX_UNSET; const xMinAggIndex = index1Unset || valueData[index0 + AGGREGATION_INDEX_X_MIN] < valueData[index1 + AGGREGATION_INDEX_X_MIN] ? index0 : index1; const xMinIndex = indexData[xMinAggIndex + AGGREGATION_INDEX_X_MIN]; nextIndexData[aggIndex + AGGREGATION_INDEX_X_MIN] = xMinIndex; nextValueData[aggIndex + AGGREGATION_INDEX_X_MIN] = valueData[xMinAggIndex + AGGREGATION_INDEX_X_MIN]; const xMaxAggIndex = index1Unset || valueData[index0 + AGGREGATION_INDEX_X_MAX] > valueData[index1 + AGGREGATION_INDEX_X_MAX] ? index0 : index1; const xMaxIndex = indexData[xMaxAggIndex + AGGREGATION_INDEX_X_MAX]; nextIndexData[aggIndex + AGGREGATION_INDEX_X_MAX] = xMaxIndex; nextValueData[aggIndex + AGGREGATION_INDEX_X_MAX] = valueData[xMaxAggIndex + AGGREGATION_INDEX_X_MAX]; nextMidpointData[i] = xMinIndex + xMaxIndex >> 1; const yMinAggIndex = index1Unset || valueData[index0 + AGGREGATION_INDEX_Y_MIN] < valueData[index1 + AGGREGATION_INDEX_Y_MIN] ? index0 : index1; nextIndexData[aggIndex + AGGREGATION_INDEX_Y_MIN] = indexData[yMinAggIndex + AGGREGATION_INDEX_Y_MIN]; nextValueData[aggIndex + AGGREGATION_INDEX_Y_MIN] = valueData[yMinAggIndex + AGGREGATION_INDEX_Y_MIN]; const yMaxAggIndex = index1Unset || valueData[index0 + AGGREGATION_INDEX_Y_MAX] > valueData[index1 + AGGREGATION_INDEX_Y_MAX] ? index0 : index1; nextIndexData[aggIndex + AGGREGATION_INDEX_Y_MAX] = indexData[yMaxAggIndex + AGGREGATION_INDEX_Y_MAX]; nextValueData[aggIndex + AGGREGATION_INDEX_Y_MAX] = valueData[yMaxAggIndex + AGGREGATION_INDEX_Y_MAX]; } return { maxRange: nextMaxRange, indexData: nextIndexData, valueData: nextValueData, midpointData: nextMidpointData }; } function getMidpointsForIndices(maxRange, indexData, reuseMidpointData, xMinOffset = AGGREGATION_INDEX_X_MIN, xMaxOffset = AGGREGATION_INDEX_X_MAX, invalidSentinel = -1) { const midpoints = reuseMidpointData?.length === maxRange ? reuseMidpointData : new Uint32Array(maxRange); for (let i = 0, offset = 0; i < maxRange; i += 1, offset += AGGREGATION_SPAN) { const xMin = indexData[offset + xMinOffset]; const xMax = indexData[offset + xMaxOffset]; midpoints[i] = xMin === invalidSentinel ? invalidSentinel : xMin + xMax >> 1; } return midpoints; } function collectAggregationLevels(state, { collectLevel, shouldContinue, minRange = AGGREGATION_MIN_RANGE, compactInPlace = false }) { let aggregationState = state; let level = collectLevel(aggregationState); const levels = [level]; while (aggregationState.maxRange > minRange && shouldContinue(level, aggregationState)) { const compacted = compactAggregationIndices( aggregationState.indexData, aggregationState.valueData, aggregationState.maxRange, { inPlace: compactInPlace } ); aggregationState = { maxRange: compacted.maxRange, indexData: compacted.indexData, valueData: compacted.valueData, midpointData: compacted.midpointData }; level = collectLevel(aggregationState); levels.push(level); } levels.reverse(); return levels; } function computeExtremesAggregation(domain, xValues, highValues, lowValues, options) { if (xValues.length < AGGREGATION_THRESHOLD) return; const [d0, d1] = domain; const { smallestKeyInterval, xNeedsValueOf, yNeedsValueOf, existingFilters } = options; let maxRange = aggregationRangeFittingPoints(xValues, d0, d1, { smallestKeyInterval, xNeedsValueOf }); const existingFilter = existingFilters?.find((f) => f.maxRange === maxRange); let { indexData, valueData } = createAggregationIndices(xValues, highValues, lowValues, d0, d1, maxRange, { xNeedsValueOf, yNeedsValueOf, reuseIndexData: existingFilter?.indexData, reuseValueData: existingFilter?.valueData }); let midpointIndices = getMidpointsForIndices(maxRange, indexData, existingFilter?.midpointIndices); const filters = [ { maxRange, indexData, valueData, midpointIndices } ]; while (maxRange > AGGREGATION_MIN_RANGE) { const currentMaxRange = maxRange; const nextMaxRange = Math.trunc(currentMaxRange / 2); const nextExistingFilter = existingFilters?.find((f) => f.maxRange === nextMaxRange); const compacted = compactAggregationIndices(indexData, valueData, currentMaxRange, { reuseIndexData: nextExistingFilter?.indexData, reuseValueData: nextExistingFilter?.valueData }); maxRange = compacted.maxRange; indexData = compacted.indexData; valueData = compacted.valueData; midpointIndices = compacted.midpointData ?? getMidpointsForIndices(maxRange, indexData, nextExistingFilter?.midpointIndices); filters.push({ maxRange, indexData, valueData, midpointIndices }); } filters.reverse(); return filters; } function computeExtremesAggregationPartial(domain, xValues, highValues, lowValues, options) { if (xValues.length < AGGREGATION_THRESHOLD) return; const [d0, d1] = domain; const { smallestKeyInterval, targetRange, xNeedsValueOf, yNeedsValueOf, existingFilters } = options; const finestMaxRange = aggregationRangeFittingPoints(xValues, d0, d1, { smallestKeyInterval, xNeedsValueOf }); const targetMaxRange = Math.min(finestMaxRange, nextPowerOf2(Math.max(targetRange, AGGREGATION_MIN_RANGE))); const existingFilter = existingFilters?.find((f) => f.maxRange === targetMaxRange); const { indexData, valueData } = createAggregationIndices(xValues, highValues, lowValues, d0, d1, targetMaxRange, { xNeedsValueOf, yNeedsValueOf, reuseIndexData: existingFilter?.indexData, reuseValueData: existingFilter?.valueData }); const midpointIndices = getMidpointsForIndices(targetMaxRange, indexData, existingFilter?.midpointIndices); const immediateLevel = { maxRange: targetMaxRange, indexData, valueData, midpointIndices }; function computeRemaining() { const allLevels = computeExtremesAggregation([d0, d1], xValues, highValues, lowValues, { smallestKeyInterval, xNeedsValueOf, yNeedsValueOf, existingFilters }); return allLevels?.filter((level) => level.maxRange !== targetMaxRange) ?? []; } return { immediate: [immediateLevel], computeRemaining }; } // packages/ag-charts-core/src/types/themeSymbols.ts var IS_DARK_THEME = Symbol("is-dark-theme"); var DEFAULT_SHADOW_COLOUR = Symbol("default-shadow-colour"); var DEFAULT_CAPTION_LAYOUT_STYLE = Symbol("default-caption-layout-style"); var DEFAULT_CAPTION_ALIGNMENT = Symbol("default-caption-alignment"); var PALETTE_UP_STROKE = Symbol("palette-up-stroke"); var PALETTE_DOWN_STROKE = Symbol("palette-down-stroke"); var PALETTE_UP_FILL = Symbol("palette-up-fill"); var PALETTE_DOWN_FILL = Symbol("palette-down-fill"); var PALETTE_NEUTRAL_STROKE = Symbol("palette-neutral-stroke"); var PALETTE_NEUTRAL_FILL = Symbol("palette-neutral-fill"); var PALETTE_ALT_UP_STROKE = Symbol("palette-alt-up-stroke"); var PALETTE_ALT_DOWN_STROKE = Symbol("palette-alt-down-stroke"); var PALETTE_ALT_UP_FILL = Symbol("palette-alt-up-fill"); var PALETTE_ALT_DOWN_FILL = Symbol("palette-alt-down-fill"); var PALETTE_ALT_NEUTRAL_FILL = Symbol("palette-gray-fill"); var PALETTE_ALT_NEUTRAL_STROKE = Symbol("palette-gray-stroke"); var DEFAULT_POLAR_SERIES_STROKE = Symbol("default-polar-series-stroke"); var DEFAULT_SPARKLINE_CROSSHAIR_STROKE = Symbol("default-sparkline-crosshair-stroke"); var DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR = Symbol( "default-financial-charts-annotation-stroke" ); var DEFAULT_FIBONACCI_STROKES = Symbol("default-hierarchy-strokes"); var DEFAULT_TEXT_ANNOTATION_COLOR = Symbol("default-text-annotation-color"); var DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL = Symbol( "default-financial-charts-annotation-background-fill" ); var DEFAULT_ANNOTATION_HANDLE_FILL = Symbol("default-annotation-handle-fill"); var DEFAULT_ANNOTATION_STATISTICS_FILL = Symbol("default-annotation-statistics-fill"); var DEFAULT_ANNOTATION_STATISTICS_STROKE = Symbol("default-annotation-statistics-stroke"); var DEFAULT_ANNOTATION_STATISTICS_COLOR = Symbol("default-annotation-statistics-color"); var DEFAULT_ANNOTATION_STATISTICS_DIVIDER_STROKE = Symbol( "default-annotation-statistics-divider-stroke" ); var DEFAULT_ANNOTATION_STATISTICS_DOWN_FILL = Symbol( "default-annotation-statistics-fill" ); var DEFAULT_ANNOTATION_STATISTICS_DOWN_STROKE = Symbol( "default-annotation-statistics-stroke" ); var DEFAULT_TEXTBOX_FILL = Symbol("default-textbox-fill"); var DEFAULT_TEXTBOX_STROKE = Symbol("default-textbox-stroke"); var DEFAULT_TEXTBOX_COLOR = Symbol("default-textbox-color"); var DEFAULT_TOOLBAR_POSITION = Symbol("default-toolbar-position"); // packages/ag-charts-core/src/state/callbackCache.ts function needsContext(caller, _params) { return "context" in caller; } function maybeSetContext(caller, params) { if (caller != null && needsContext(caller, params)) { if (params != null && typeof params === "object" && params.context === void 0) { params.context = caller.context; return true; } } return false; } function callWithContext(callers, fn, params) { if (Array.isArray(callers)) { for (const caller of callers) { if (maybeSetContext(caller, params)) { break; } } } else { maybeSetContext(callers, params); } return fn(params); } var CallbackCache = class { constructor() { this.cache = /* @__PURE__ */ new WeakMap(); } call(callers, fn, params) { let serialisedParams; let paramCache = this.cache.get(fn); try { serialisedParams = JSON.stringify(params); } catch { return this.invoke(callers, fn, paramCache, void 0, params); } if (paramCache == null) { paramCache = /* @__PURE__ */ new Map(); this.cache.set(fn, paramCache); } if (!paramCache.has(serialisedParams)) { return this.invoke(callers, fn, paramCache, serialisedParams, params); } return paramCache.get(serialisedParams); } invoke(callers, fn, paramCache, serialisedParams, params) { try { const result = callWithContext(callers, fn, params); if (paramCache && serialisedParams != null) { paramCache.set(serialisedParams, result); } return result; } catch (e) { warnOnce(`User callback errored, ignoring`, e); return; } } invalidateCache() { this.cache = /* @__PURE__ */ new WeakMap(); } }; // packages/ag-charts-core/src/utils/dom/domUtil.ts function setElementBBox(element2, bbox) { if (!element2) return; const { x, y, width: width2, height: height2 } = normalizeBounds(bbox); setPixelValue(element2.style, "width", width2); setPixelValue(element2.style, "height", height2); setPixelValue(element2.style, "left", x); setPixelValue(element2.style, "top", y); } function getElementBBox(element2) { const styleWidth = Number.parseFloat(element2.style.width); const styleHeight = Number.parseFloat(element2.style.height); const styleX = Number.parseFloat(element2.style.left); const styleY = Number.parseFloat(element2.style.top); const width2 = Number.isFinite(styleWidth) ? styleWidth : element2.offsetWidth; const height2 = Number.isFinite(styleHeight) ? styleHeight : element2.offsetHeight; const x = Number.isFinite(styleX) ? styleX : element2.offsetLeft; const y = Number.isFinite(styleY) ? styleY : element2.offsetTop; return { x, y, width: width2, height: height2 }; } function focusCursorAtEnd(element2) { element2.focus({ preventScroll: true }); if (element2.lastChild?.textContent == null) return; const range3 = getDocument().createRange(); range3.setStart(element2.lastChild, element2.lastChild.textContent.length); range3.setEnd(element2.lastChild, element2.lastChild.textContent.length); const selection = getWindow().getSelection(); selection?.removeAllRanges(); selection?.addRange(range3); } function isInputPending() { const navigator = getWindow("navigator"); if ("scheduling" in navigator) { const scheduling = navigator.scheduling; if ("isInputPending" in scheduling) { return scheduling.isInputPending({ includeContinuous: true }); } } return false; } function getIconClassNames(icon) { return `ag-charts-icon ag-charts-icon-${icon}`; } function normalizeBounds(bbox) { let { x, y, width: width2, height: height2 } = bbox; if ((width2 == null || width2 > 0) && (height2 == null || height2 > 0)) { return bbox; } if (x != null && width2 != null && width2 < 0) { width2 = -width2; x = x - width2; } if (y != null && height2 != null && height2 < 0) { height2 = -height2; y = y - height2; } return { x, y, width: width2, height: height2 }; } function setPixelValue(style2, key, value) { if (value == null) { style2.removeProperty(key); } else { style2.setProperty(key, `${value}px`); } } // packages/ag-charts-core/src/utils/geometry/math/shapeUtils.ts function getMaxInnerRectSize(rotationDeg, containerWidth, containerHeight = Infinity) { const W = containerWidth; const H = containerHeight; const angle2 = rotationDeg % 180 * (Math.PI / 180); const sin = Math.abs(Math.sin(angle2)); const cos = Math.abs(Math.cos(angle2)); if (sin === 0) return { width: W, height: H }; if (cos === 0) return { width: H, height: W }; if (!Number.isFinite(H)) { const r = cos / sin; const width2 = W / (cos + r * sin); return { width: width2, height: r * width2 }; } const denominator = cos * cos - sin * sin; if (denominator === 0) { const side = Math.min(W, H) / Math.SQRT2; return { width: side, height: side }; } return { width: Math.abs((W * cos - H * sin) / denominator), height: Math.abs((H * cos - W * sin) / denominator) }; } function getMinOuterRectSize(rotationDeg, innerWidth, innerHeight = Infinity) { const w = innerWidth; const h = innerHeight; const angle2 = rotationDeg % 180 * (Math.PI / 180); const sin = Math.abs(Math.sin(angle2)); const cos = Math.abs(Math.cos(angle2)); if (sin === 0) return { width: w, height: h }; if (cos === 0) return { width: h, height: w }; return { width: w * cos + h * sin, height: w * sin + h * cos }; } function rotatePoint(x, y, angle2, originX = 0, originY = 0) { const cos = Math.cos(angle2); const sin = Math.sin(angle2); const dx = x - originX; const dy = y - originY; return { x: originX + dx * cos - dy * sin, y: originY + dx * sin + dy * cos }; } // packages/ag-charts-core/src/utils/geometry/angle.ts var twoPi = Math.PI * 2; var halfPi = Math.PI / 2; function normalizeAngle360(radians) { radians %= twoPi; radians += twoPi; radians %= twoPi; return radians; } function normalizeAngle360Inclusive(radians) { radians %= twoPi; radians += twoPi; if (radians !== twoPi) { radians %= twoPi; } return radians; } function normalizeAngle180(radians) { radians %= twoPi; if (radians < -Math.PI) { radians += twoPi; } else if (radians >= Math.PI) { radians -= twoPi; } return radians; } function isBetweenAngles(targetAngle, startAngle, endAngle) { const t = normalizeAngle360(targetAngle); const a0 = normalizeAngle360(startAngle); const a1 = normalizeAngle360(endAngle); if (a0 < a1) { return a0 <= t && t <= a1; } else if (a0 > a1) { return a0 <= t || t <= a1; } else { return startAngle !== endAngle; } } function toRadians(degrees) { return degrees / 180 * Math.PI; } function toDegrees(radians) { return radians / Math.PI * 180; } function angleBetween(angle0, angle1) { angle0 = normalizeAngle360(angle0); angle1 = normalizeAngle360(angle1); return angle1 - angle0 + (angle0 > angle1 ? twoPi : 0); } function getAngleRatioRadians(angle2) { const normalizedAngle = normalizeAngle360(angle2); if (normalizedAngle <= halfPi) { return normalizedAngle / halfPi; } else if (normalizedAngle <= Math.PI) { return (Math.PI - normalizedAngle) / halfPi; } else if (normalizedAngle <= 1.5 * Math.PI) { return (normalizedAngle - Math.PI) / halfPi; } else { return (twoPi - normalizedAngle) / halfPi; } } function angularPadding(hPadding, vPadding, angle2) { const angleRatio = getAngleRatioRadians(angle2); return hPadding * angleRatio + vPadding * Math.abs(1 - angleRatio); } function normalizeAngle360FromDegrees(degrees) { return degrees ? normalizeAngle360(toRadians(degrees)) : 0; } // packages/ag-charts-core/src/utils/async.ts var AsyncAwaitQueue = class { constructor() { this.queue = []; } /** Await another async process to call notify(). */ waitForCompletion(timeout = 50) { const queue = this.queue; function createCompletionPromise(resolve) { function successFn() { clearTimeout(timeoutHandle); resolve(true); } function timeoutFn() { const queueIndex = queue.indexOf(successFn); if (queueIndex < 0) return; queue.splice(queueIndex, 1); resolve(false); } const timeoutHandle = setTimeout(timeoutFn, timeout); queue.push(successFn); } return new Promise(createCompletionPromise); } /** Trigger any await()ing async processes to continue. */ notify() { for (const cb of this.queue.splice(0)) { cb(); } } }; function pause(delayMilliseconds = 0) { function resolveAfterDelay(resolve) { setTimeout(resolve, delayMilliseconds); } return new Promise(resolveAfterDelay); } async function withTimeout(promise, timeoutMs, errorMessage = `Timeout after ${timeoutMs}ms`) { let timer; const timeoutPromise = new Promise((_, reject) => { timer = setTimeout(() => reject(new Error(errorMessage)), timeoutMs); }); try { return await Promise.race([promise, timeoutPromise]); } finally { clearTimeout(timer); } } // packages/ag-charts-core/src/utils/dom/attributeUtil.ts function booleanParser(value) { return value === "true"; } function numberParser(value) { return Number(value); } function stringParser(value) { return value; } var AttributeTypeParsers = { role: stringParser, "aria-checked": booleanParser, "aria-controls": stringParser, "aria-describedby": stringParser, "aria-disabled": booleanParser, "aria-expanded": booleanParser, "aria-haspopup": stringParser, "aria-hidden": booleanParser, "aria-label": stringParser, "aria-labelledby": stringParser, "aria-live": stringParser, "aria-orientation": stringParser, "aria-selected": booleanParser, "data-focus-override": booleanParser, "data-focus-visible-override": booleanParser, "data-preventdefault": booleanParser, class: stringParser, for: stringParser, id: stringParser, tabindex: numberParser, title: stringParser, placeholder: stringParser }; function setAttribute(e, qualifiedName, value) { if (value == null || value === "" || value === "") { e?.removeAttribute(qualifiedName); } else { e?.setAttribute(qualifiedName, value.toString()); } } function setAttributes(e, attrs) { if (attrs == null) return; for (const [key, value] of entries(attrs)) { if (key === "class") continue; setAttribute(e, key, value); } } function getAttribute(e, qualifiedName, defaultValue) { if (!isHTMLElement(e)) return void 0; const value = e.getAttribute(qualifiedName); if (value === null) return defaultValue; return AttributeTypeParsers[qualifiedName]?.(value) ?? void 0; } function setElementStyle(e, property, value) { if (e == null) return; if (value == null) { e.style.removeProperty(property); } else { e.style.setProperty(property, value.toString()); } } function setElementStyles(e, styles) { for (const [key, value] of entries(styles)) { setElementStyle(e, key, value); } } // packages/ag-charts-core/src/state/proxy.ts function ProxyProperty(proxyPath, configMetadata) { const pathArray = Array.isArray(proxyPath) ? proxyPath : proxyPath.split("."); if (pathArray.length === 1) { const [property] = pathArray; return addTransformToInstanceProperty( (target, _, value) => target[property] = value, (target) => target[property], configMetadata ); } return addTransformToInstanceProperty( (target, _, value) => setPath(target, pathArray, value), (target) => getPath(target, pathArray), configMetadata ); } function ProxyOnWrite(proxyProperty) { return addTransformToInstanceProperty((target, _, value) => target[proxyProperty] = value); } function ProxyPropertyOnWrite(childName, childProperty) { return addTransformToInstanceProperty((target, key, value) => target[childName][childProperty ?? key] = value); } function ActionOnSet(opts) { const { newValue: newValueFn, oldValue: oldValueFn, changeValue: changeValueFn } = opts; return addTransformToInstanceProperty((target, _, newValue, oldValue) => { if (newValue !== oldValue) { if (oldValue !== void 0) { oldValueFn?.call(target, oldValue); } if (newValue !== void 0) { newValueFn?.call(target, newValue); } changeValueFn?.call(target, newValue, oldValue); } return newValue; }); } function ObserveChanges(observerFn) { return addObserverToInstanceProperty(observerFn); } // packages/ag-charts-core/src/utils/geometry/border.ts var Border = class extends BaseProperties { constructor(node) { super(); this.node = node; this.enabled = false; this.stroke = "black"; this.strokeOpacity = 1; this.strokeWidth = 1; } }; __decorateClass([ ActionOnSet({ changeValue(newValue) { if (newValue) { this.node.strokeWidth = this.strokeWidth; } else { this.node.strokeWidth = 0; } } }), addFakeTransformToInstanceProperty ], Border.prototype, "enabled", 2); __decorateClass([ ProxyPropertyOnWrite("node", "stroke"), addFakeTransformToInstanceProperty ], Border.prototype, "stroke", 2); __decorateClass([ ProxyPropertyOnWrite("node", "strokeOpacity"), addFakeTransformToInstanceProperty ], Border.prototype, "strokeOpacity", 2); __decorateClass([ ActionOnSet({ changeValue(newValue) { if (this.enabled) { this.node.strokeWidth = newValue; } else { this.node.strokeWidth = 0; } } }), addFakeTransformToInstanceProperty ], Border.prototype, "strokeWidth", 2); // packages/ag-charts-core/src/utils/geometry/boxBounds.ts function boxCollides(b, x, y, w, h) { return x < b.x + b.width && x + w > b.x && y < b.y + b.height && y + h > b.y; } function boxContains(b, x, y, w = 0, h = 0) { return x >= b.x && x + w <= b.x + b.width && y >= b.y && y + h <= b.y + b.height; } function boxEmpty(b) { return b == null || b.height === 0 || b.width === 0 || Number.isNaN(b.height) || Number.isNaN(b.width); } function boxesEqual(a, b) { if (a === b) return true; if (a == null || b == null) return false; return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height; } // packages/ag-charts-core/src/utils/data/binarySearch.ts function findMaxIndex(min, max, iteratee) { if (min > max) return; let found; while (max >= min) { const index = Math.floor((max + min) / 2); const value = iteratee(index); if (value) { found = index; min = index + 1; } else { max = index - 1; } } return found; } function findMinIndex(min, max, iteratee) { if (min > max) return; let found; while (max >= min) { const index = Math.floor((max + min) / 2); const value = iteratee(index); if (value) { found = index; max = index - 1; } else { min = index + 1; } } return found; } function findMaxValue(min, max, iteratee) { if (min > max) return; let found; while (max >= min) { const index = Math.floor((max + min) / 2); const value = iteratee(index); if (value == null) { max = index - 1; } else { found = value; min = index + 1; } } return found; } function findMinValue(min, max, iteratee) { if (min > max) return; let found; while (max >= min) { const index = Math.floor((max + min) / 2); const value = iteratee(index); if (value == null) { min = index + 1; } else { found = value; max = index - 1; } } return found; } // packages/ag-charts-core/src/utils/canvas.ts function createCanvasContext(width2 = 0, height2 = 0) { const OffscreenCanvasCtor = getOffscreenCanvas(); return new OffscreenCanvasCtor(width2, height2).getContext("2d"); } // packages/ag-charts-core/src/utils/configuredCanvasMixin.ts var CANVAS_WIDTH = 800; var CANVAS_HEIGHT = 600; var CANVAS_TO_BUFFER_DEFAULTS = { quality: 1 }; function ConfiguredCanvasMixin(Base) { const ConfiguredCanvasClass = class ConfiguredCanvas extends Base { constructor(...args) { super(...args); this.gpu = false; } toBuffer(format, options) { return super.toBuffer(format, { ...options, msaa: false }); } transferToImageBitmap() { const { width: width2, height: height2 } = this; const bitmap = new ConfiguredCanvasClass(Math.max(1, width2), Math.max(1, height2)); if (width2 > 0 && height2 > 0) { bitmap.getContext("2d").drawCanvas(this, 0, 0, width2, height2); } Object.defineProperty(bitmap, "close", { value: () => { } }); return bitmap; } }; return ConfiguredCanvasClass; } var patchesApplied = false; function applySkiaPatches(CanvasRenderingContext2D, DOMMatrix) { if (patchesApplied) return; patchesApplied = true; const superCreateConicGradient = CanvasRenderingContext2D.prototype.createConicGradient; Object.defineProperty(CanvasRenderingContext2D.prototype, "createConicGradient", { value: function(angle2, x, y) { return superCreateConicGradient.call(this, angle2 + Math.PI / 2, x, y); }, writable: true, configurable: true }); Object.defineProperty(CanvasRenderingContext2D.prototype, "fillText", { value: function(text, x, y) { let path2d = this.outlineText(text); path2d = path2d.transform(new DOMMatrix([1, 0, 0, 1, x, y])); this.fill(path2d); }, writable: true, configurable: true }); } // packages/ag-charts-core/src/state/caching.ts var SimpleCache = class { constructor(getter) { this.getter = getter; } get() { this.result ?? (this.result = this.getter()); return this.result; } clear() { this.result = void 0; } }; var WeakCache = class { constructor(getter) { this.getter = getter; } get() { let result = this.result?.deref(); if (result) return result; result = this.getter(); this.result = new WeakRef(result); return result; } clear() { this.result = void 0; } }; // packages/ag-charts-core/src/utils/time/date.ts function compareDates(a, b) { return a.valueOf() - b.valueOf(); } function deduplicateSortedArray(values) { let v0 = Number.NaN; const out = []; for (const v of values) { const v1 = v.valueOf(); if (v0 !== v1) out.push(v); v0 = v1; } return out; } function sortAndUniqueDates(values) { const sortedValues = values.slice().sort(compareDates); return datesSortOrder(sortedValues) == null ? deduplicateSortedArray(sortedValues) : sortedValues; } function datesSortOrder(d) { if (d.length === 0) return 1; const sign = Number(d.at(-1)) > Number(d[0]) ? 1 : -1; let v0 = -Infinity * sign; for (const v of d) { const v1 = v.valueOf(); if (Math.sign(v1 - v0) !== sign) return; v0 = v1; } return sign; } // packages/ag-charts-core/src/utils/deprecation.ts function createDeprecationWarning() { return (key, message) => { const msg = [`Property [${key}] is deprecated.`, message].filter(Boolean).join(" "); warnOnce(msg); }; } function Deprecated(message, opts) { const warnDeprecated = createDeprecationWarning(); const def = opts?.default; return addTransformToInstanceProperty((_, key, value) => { if (value !== def) { warnDeprecated(key.toString(), message); } return value; }); } function DeprecatedAndRenamedTo(newPropName, mapValue) { const warnDeprecated = createDeprecationWarning(); return addTransformToInstanceProperty( (target, key, value) => { if (value !== target[newPropName]) { warnDeprecated(key.toString(), `Use [${newPropName}] instead.`); setPath(target, newPropName, mapValue ? mapValue(value) : value); } return BREAK_TRANSFORM_CHAIN; }, (target, key) => { warnDeprecated(key.toString(), `Use [${newPropName}] instead.`); return getPath(target, newPropName); } ); } // packages/ag-charts-core/src/utils/data/diff.ts function diffArrays(previous, current) { const size = Math.max(previous.length, current.length); const added = /* @__PURE__ */ new Set(); const removed = /* @__PURE__ */ new Set(); for (let i = 0; i < size; i++) { const prev = previous[i]; const curr = current[i]; if (prev === curr) continue; if (removed.has(curr)) { removed.delete(curr); } else if (curr) { added.add(curr); } if (added.has(prev)) { added.delete(prev); } else if (prev) { removed.add(prev); } } return { changed: added.size > 0 || removed.size > 0, added, removed }; } // packages/ag-charts-core/src/utils/geometry/distance.ts function pointsDistanceSquared(x1, y1, x2, y2) { const dx = x1 - x2; const dy = y1 - y2; return dx * dx + dy * dy; } function lineDistanceSquared(x, y, x1, y1, x2, y2, best) { if (x1 === x2 && y1 === y2) { return Math.min(best, pointsDistanceSquared(x, y, x1, y1)); } const dx = x2 - x1; const dy = y2 - y1; const t = Math.max(0, Math.min(1, ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy))); const ix = x1 + t * dx; const iy = y1 + t * dy; return Math.min(best, pointsDistanceSquared(x, y, ix, iy)); } function arcDistanceSquared(x, y, cx, cy, radius, startAngle, endAngle, counterClockwise, best) { if (counterClockwise) { [endAngle, startAngle] = [startAngle, endAngle]; } const angle2 = Math.atan2(y - cy, x - cx); if (!isBetweenAngles(angle2, startAngle, endAngle)) { const startX = cx + Math.cos(startAngle) * radius; const startY = cy + Math.sin(startAngle) * radius; const endX = cx + Math.cos(startAngle) * radius; const endY = cy + Math.sin(startAngle) * radius; return Math.min(best, pointsDistanceSquared(x, y, startX, startY), pointsDistanceSquared(x, y, endX, endY)); } const distToArc = radius - Math.sqrt(pointsDistanceSquared(x, y, cx, cy)); return Math.min(best, distToArc * distToArc); } // packages/ag-charts-core/src/utils/data/extent.ts function extent(values, sortOrder) { if (values.length === 0) { return null; } if (sortOrder !== void 0) { const first2 = values.at(0); const last = values.at(-1); const v0 = first2 instanceof Date ? first2.getTime() : first2; const v1 = last instanceof Date ? last.getTime() : last; if (typeof v0 === "number" && typeof v1 === "number") { return sortOrder === 1 ? [v0, v1] : [v1, v0]; } } let min = Infinity; let max = -Infinity; for (const n of values) { const v = n instanceof Date ? n.getTime() : n; if (typeof v !== "number") continue; if (v < min) { min = v; } if (v > max) { max = v; } } const result = [min, max]; return result.every(Number.isFinite) ? result : null; } function normalisedExtentWithMetadata(d, min, max, preferredMin, preferredMax, toValue, sortOrder) { let clipped = false; const domainExtentNumbers = extent(d, sortOrder); const domainExtent = domainExtentNumbers && toValue ? [toValue(domainExtentNumbers[0]), toValue(domainExtentNumbers[1])] : domainExtentNumbers; if (domainExtent == null) { let nullExtent; if (min != null && max != null && min <= max) { nullExtent = [min, max]; } else if (preferredMin != null && preferredMax != null && preferredMin <= preferredMax) { nullExtent = [preferredMin, preferredMax]; } return { extent: nullExtent ?? [], clipped: false }; } let [d0, d1] = domainExtent; if (min != null) { clipped || (clipped = min > d0); d0 = min; } else if (preferredMin != null && preferredMin < d0) { d0 = preferredMin; } if (max != null) { clipped || (clipped = max < d1); d1 = max; } else if (preferredMax != null && preferredMax > d1) { d1 = preferredMax; } if (d0 > d1) { return { extent: [], clipped: false }; } return { extent: [d0, d1], clipped }; } function normalisedTimeExtentWithMetadata(input, min, max, preferredMin, preferredMax) { const { extent: e, clipped } = normalisedExtentWithMetadata( input.domain, isNumber(min) ? new Date(min) : min, isNumber(max) ? new Date(max) : max, isNumber(preferredMin) ? new Date(preferredMin) : preferredMin, isNumber(preferredMax) ? new Date(preferredMax) : preferredMax, (x) => new Date(x), input.sortMetadata?.sortOrder ); return { extent: e.map((x) => new Date(x)), clipped }; } // packages/ag-charts-core/src/utils/format/format.util.ts var percentFormatter = new Intl.NumberFormat("en-US", { style: "percent" }); function formatValue(value, maximumFractionDigits = 2) { if (typeof value === "number") { return formatNumber(value, maximumFractionDigits); } return typeof value === "string" ? value : String(value ?? ""); } function formatPercent(value) { return percentFormatter.format(value); } var numberFormatters = (/* @__PURE__ */ new Map()).set( 2, new Intl.NumberFormat("en-US", { maximumFractionDigits: 2, useGrouping: false }) ); function formatNumber(value, maximumFractionDigits) { let formatter2 = numberFormatters.get(maximumFractionDigits); if (!formatter2) { formatter2 = new Intl.NumberFormat("en-US", { maximumFractionDigits, useGrouping: false }); numberFormatters.set(maximumFractionDigits, formatter2); } return formatter2.format(value); } // packages/ag-charts-core/src/utils/geojson.ts function isValidCoordinate(value) { return Array.isArray(value) && value.length >= 2 && value.every(isFiniteNumber); } function isValidCoordinates(value) { return Array.isArray(value) && value.length >= 2 && value.every(isValidCoordinate); } function hasSameStartEndPoint(c) { const start2 = c[0]; const end3 = c.at(-1); if (end3 === void 0) return false; return isNumberEqual(start2[0], end3[0], 1e-3) && isNumberEqual(start2[1], end3[1], 1e-3); } function isValidPolygon(value) { return Array.isArray(value) && value.every(isValidCoordinates) && value.every(hasSameStartEndPoint); } function isValidGeometry(value) { if (value === null) return true; if (!isObject(value) || value.type == null) return false; const { type, coordinates } = value; switch (type) { case "GeometryCollection": return Array.isArray(value.geometries) && value.geometries.every(isValidGeometry); case "MultiPolygon": return Array.isArray(coordinates) && coordinates.every(isValidPolygon); case "Polygon": return isValidPolygon(coordinates); case "MultiLineString": return Array.isArray(coordinates) && coordinates.every(isValidCoordinates); case "LineString": return isValidCoordinates(coordinates); case "MultiPoint": return isValidCoordinates(coordinates); case "Point": return isValidCoordinate(coordinates); default: return false; } } function isValidFeature(value) { return isObject(value) && value.type === "Feature" && isValidGeometry(value.geometry); } function isValidFeatureCollection(value) { return isObject(value) && value.type === "FeatureCollection" && Array.isArray(value.features) && value.features.every(isValidFeature); } var geoJson = attachDescription(isValidFeatureCollection, "a GeoJSON object"); // packages/ag-charts-core/src/structures/graph.ts var AdjacencyListGraph = class { constructor(cachedNeighboursEdge, processedEdge, singleValueEdges) { this._vertexCount = 0; this._edgeCount = 0; this.pendingProcessingEdgesFrom = []; this.pendingProcessingEdgesTo = []; this.cachedNeighboursEdge = cachedNeighboursEdge; this.processedEdge = processedEdge; this.singleValueEdges = singleValueEdges; } clear() { this._vertexCount = 0; this._edgeCount = 0; this.pendingProcessingEdgesFrom = []; this.pendingProcessingEdgesTo = []; this.singleValueEdges?.clear(); } getVertexCount() { return this._vertexCount; } getEdgeCount() { return this._edgeCount; } addVertex(value) { const vertex = new Vertex(value); this._vertexCount++; return vertex; } addEdge(from3, to, edge) { if (edge === this.cachedNeighboursEdge) { from3.updateCachedNeighbours().set(to.value, to); } else if (edge === this.processedEdge) { this.pendingProcessingEdgesFrom.push(from3); this.pendingProcessingEdgesTo.push(to); } const { edges } = from3; const vertices = edges.get(edge); if (!vertices) { edges.set(edge, [to]); this._edgeCount++; } else if (!vertices.includes(to)) { if (this.singleValueEdges?.has(edge)) { edges.set(edge, [to]); } else { vertices.push(to); this._edgeCount++; } } } removeVertex(vertex) { this._vertexCount--; const edges = vertex.edges; if (!edges) return; for (const [, adjacentVertices] of edges) { this._vertexCount -= adjacentVertices.length; } vertex.clear(); } removeEdge(from3, to, edge) { const neighbours = from3.edges.get(edge); if (!neighbours) return; const index = neighbours.indexOf(to); if (index === -1) return; neighbours.splice(index, 1); if (neighbours.length === 0) { from3.edges.delete(edge); } this._edgeCount--; if (edge === this.cachedNeighboursEdge) { from3.readCachedNeighbours()?.delete(to.value); } } removeEdges(from3, edgeValue) { from3.edges.delete(edgeValue); } getVertexValue(vertex) { return vertex.value; } // Iterate all the neighbours of a given vertex. *neighbours(from3) { for (const [, adjacentVertices] of from3.edges) { for (const adjacentVertex of adjacentVertices) { yield adjacentVertex; } } } // Iterate all the neighbours and their edges of a given vertex *neighboursAndEdges(from3) { for (const [edge, adjacentVertices] of from3.edges) { for (const adjacentVertex of adjacentVertices) { yield [adjacentVertex, edge]; } } } // Get the set of neighbours along a given edge. neighboursWithEdgeValue(from3, edgeValue) { return from3.edges.get(edgeValue); } // Find the first neighbour along the given edge. findNeighbour(from3, edgeValue) { return from3.edges.get(edgeValue)?.[0]; } // Find the value of the first neighbour along the given edge. findNeighbourValue(from3, edgeValue) { const neighbour = this.findNeighbour(from3, edgeValue); if (!neighbour) return; return this.getVertexValue(neighbour); } // Find the first neighbour with a given value, optionally along a given edge. findNeighbourWithValue(from3, value, edgeValue) { const neighbours = edgeValue == null ? this.neighbours(from3) : this.neighboursWithEdgeValue(from3, edgeValue); if (!neighbours) return; for (const neighbour of neighbours) { if (this.getVertexValue(neighbour) === value) { return neighbour; } } } // Find a vertex by iterating an array of vertex values along a given edge. findVertexAlongEdge(from3, findValues, edgeValue) { if (edgeValue === this.cachedNeighboursEdge) { let found2; for (const value of findValues) { found2 = (found2 ?? from3).readCachedNeighbours()?.get(value); if (!found2) return; } return found2; } if (findValues.length === 0) return; let found = from3; for (const value of findValues) { const neighbours = found ? this.neighboursWithEdgeValue(found, edgeValue) : void 0; if (!neighbours) return; found = neighbours.find((n) => n.value === value); } return found; } adjacent(from3, to) { for (const [, adjacentVertices] of from3.edges) { if (adjacentVertices.includes(to)) return true; } return false; } }; var Vertex = class { constructor(value) { this.value = value; this.edges = /* @__PURE__ */ new Map(); } readCachedNeighbours() { return this._cachedNeighbours; } updateCachedNeighbours() { this._cachedNeighbours ?? (this._cachedNeighbours = /* @__PURE__ */ new Map()); return this._cachedNeighbours; } clear() { this.edges.clear(); this._cachedNeighbours?.clear(); } }; // packages/ag-charts-core/src/utils/data/json.ts var CLASS_INSTANCE_TYPE = "class-instance"; function jsonDiff(source, target, shallow) { if (isArray(target)) { if (!isArray(source) || source.length !== target.length || target.some((v, i) => jsonDiff(source[i], v, shallow) != null)) { return target; } } else if (isPlainObject(target)) { if (!isPlainObject(source)) { return target; } const result = {}; const allKeys = /* @__PURE__ */ new Set([ ...Object.keys(source), ...Object.keys(target) ]); for (const key of allKeys) { if (source[key] === target[key]) { continue; } else if (shallow?.has(key)) { result[key] = target[key]; } else if (typeof source[key] === typeof target[key]) { const diff2 = jsonDiff(source[key], target[key], shallow); if (diff2 !== null) { result[key] = diff2; } } else { result[key] = target[key]; } } return Object.keys(result).length ? result : null; } else if (source !== target) { return target; } return null; } function jsonPropertyCompare(source, target) { for (const key of Object.keys(source)) { if (source[key] === target?.[key]) continue; return false; } return true; } function deepClone(source, opts) { if (isArray(source)) { return cloneArray(source, opts); } if (isPlainObject(source)) { return clonePlainObject(source, opts); } if (source instanceof Map) { return new Map(deepClone(Array.from(source))); } return shallowClone(source); } function cloneArray(source, opts) { const result = []; const seen = opts?.seen; for (const item of source) { if (typeof item === "object" && seen?.includes(item)) { warn("cycle detected in array", item); continue; } seen?.push(item); result.push(deepClone(item, opts)); seen?.pop(); } return result; } function clonePlainObject(source, opts) { const target = {}; for (const key of Object.keys(source)) { if (opts?.assign?.has(key)) { target[key] = source[key]; } else if (opts?.shallow?.has(key)) { target[key] = shallowClone(source[key]); } else { target[key] = deepClone(source[key], opts); } } return target; } function shallowClone(source) { if (isArray(source)) { return source.slice(0); } if (isPlainObject(source)) { return { ...source }; } if (isDate(source)) { return new Date(source); } if (isRegExp(source)) { return new RegExp(source.source, source.flags); } return source; } function jsonWalk(json, visit, skip, parallelJson, ctx, acc) { if (isArray(json)) { acc = visit(json, parallelJson, ctx, acc); let index = 0; for (const node of json) { acc = jsonWalk(node, visit, skip, parallelJson?.[index], ctx, acc); index++; } } else if (isPlainObject(json)) { acc = visit(json, parallelJson, ctx, acc); for (const key of Object.keys(json)) { if (skip?.has(key)) { continue; } const value = json[key]; acc = jsonWalk(value, visit, skip, parallelJson?.[key], ctx, acc); } } return acc; } function jsonApply(target, source, params = {}) { const { path, matcherPath = path?.replace(/(\[[0-9+]+])/i, "[]"), skip = [] } = params; if (target == null) { throw new Error(`AG Charts - target is uninitialised: ${path ?? ""}`); } if (source == null) { return target; } if (isProperties(target)) { return target.set(source); } const targetAny = target; const targetType = classify(target); for (const property of Object.keys(source)) { if (SKIP_JS_BUILTINS.has(property)) continue; const propertyMatcherPath = `${matcherPath ? matcherPath + "." : ""}${property}`; if (skip.includes(propertyMatcherPath)) continue; const newValue = source[property]; const propertyPath = `${path ? path + "." : ""}${property}`; const targetClass = targetAny.constructor; const currentValue = targetAny[property]; try { const currentValueType = classify(currentValue); const newValueType = classify(newValue); if (targetType === CLASS_INSTANCE_TYPE && !(property in target || property === "context")) { if (newValue === void 0) continue; warn(`unable to set [${propertyPath}] in ${targetClass?.name} - property is unknown`); continue; } if (currentValueType != null && newValueType != null && newValueType !== currentValueType && (currentValueType !== CLASS_INSTANCE_TYPE || newValueType !== "object")) { warn( `unable to set [${propertyPath}] in ${targetClass?.name} - can't apply type of [${newValueType}], allowed types are: [${currentValueType}]` ); continue; } if (isProperties(currentValue)) { if (newValue === void 0) { currentValue.clear(); } else { currentValue.set(newValue); } } else if (newValueType === "object" && property !== "context") { if (!(property in targetAny)) { warn(`unable to set [${propertyPath}] in ${targetClass?.name} - property is unknown`); continue; } if (currentValue == null) { targetAny[property] = newValue; } else { jsonApply(currentValue, newValue, { ...params, path: propertyPath, matcherPath: propertyMatcherPath }); } } else { targetAny[property] = newValue; } } catch (error2) { warn(`unable to set [${propertyPath}] in [${targetClass?.name}]; nested error is: ${error2.message}`); } } return target; } function classify(value) { if (value == null) { return null; } if (isHtmlElement(value) || isDate(value)) { return "primitive"; } if (isArray(value)) { return "array"; } if (isObject(value)) { return isPlainObject(value) ? "object" : CLASS_INSTANCE_TYPE; } if (isFunction(value)) { return "function"; } return "primitive"; } // packages/ag-charts-core/src/utils/dom/domEvents.ts function attachListener(element2, eventName, handler, options) { element2.addEventListener(eventName, handler, options); return () => element2.removeEventListener(eventName, handler, options); } // packages/ag-charts-core/src/utils/dom/keynavUtil.ts function addEscapeEventListener(elem, onEscape, keyCodes = ["Escape"]) { return attachListener(elem, "keydown", (event) => { if (matchesKey(event, ...keyCodes)) { onEscape(event); } }); } function addMouseCloseListener(menu, hideCallback) { const removeEvent = attachListener(getWindow(), "mousedown", (event) => { if ([0, 2].includes(event.button) && !containsEvent(menu, event)) { hideCallback(); removeEvent(); } }); return removeEvent; } function addTouchCloseListener(menu, hideCallback) { const removeEvent = attachListener(getWindow(), "touchstart", (event) => { const touches = Array.from(event.targetTouches); if (touches.some((touch) => !containsEvent(menu, touch))) { hideCallback(); removeEvent(); } }); return removeEvent; } function containsEvent(container, event) { if (isElement(event.target) && event.target.shadowRoot != null) { return true; } return isNode(event.target) && container.contains(event.target); } function addOverrideFocusVisibleEventListener(menu, buttons, overrideFocusVisible) { const setFocusVisible = (value) => { for (const btn of buttons) { setAttribute(btn, "data-focus-visible-override", value); } }; setFocusVisible(overrideFocusVisible); return attachListener(menu, "keydown", () => setFocusVisible(true), { once: true }); } function hasNoModifiers(event) { return !(event.shiftKey || event.altKey || event.ctrlKey || event.metaKey); } function matchesKey(event, ...keys) { return hasNoModifiers(event) && keys.includes(event.key); } function linkTwoButtons(src, dst, key) { return attachListener(src, "keydown", (event) => { if (matchesKey(event, key)) { dst.focus(); } }); } var PREV_NEXT_KEYS = { horizontal: { nextKey: "ArrowRight", prevKey: "ArrowLeft" }, vertical: { nextKey: "ArrowDown", prevKey: "ArrowUp" } }; function initRovingTabIndex(opts) { const { orientation, buttons, wrapAround = false, onEscape, onFocus, onBlur } = opts; const { nextKey, prevKey } = PREV_NEXT_KEYS[orientation]; const setTabIndices = (event) => { if (event.target && "tabIndex" in event.target) { for (const b of buttons) { b.tabIndex = -1; } event.target.tabIndex = 0; } }; const [c, m] = wrapAround ? [buttons.length, buttons.length] : [0, Infinity]; const cleanup = new CleanupRegistry(); for (let i = 0; i < buttons.length; i++) { const prev = buttons[(c + i - 1) % m]; const curr = buttons[i]; const next = buttons[(c + i + 1) % m]; cleanup.register( attachListener(curr, "focus", setTabIndices), onFocus && attachListener(curr, "focus", onFocus), onBlur && attachListener(curr, "blur", onBlur), onEscape && addEscapeEventListener(curr, onEscape), prev && linkTwoButtons(curr, prev, prevKey), next && linkTwoButtons(curr, next, nextKey), attachListener(curr, "keydown", (event) => { if (matchesKey(event, nextKey, prevKey)) { event.preventDefault(); } }) ); curr.tabIndex = i === 0 ? 0 : -1; } return cleanup; } function makeAccessibleClickListener(element2, onclick) { return (event) => { if (element2.ariaDisabled === "true") { return event.preventDefault(); } onclick(event); }; } function isButtonClickEvent(event) { if ("button" in event) { return event.button === 0; } return hasNoModifiers(event) && (event.code === "Space" || event.key === "Enter"); } function getLastFocus(sourceEvent) { const target = sourceEvent?.target; if (isElement(target) && "tabindex" in target.attributes) { return target; } return void 0; } function stopPageScrolling(element2) { return attachListener(element2, "keydown", (event) => { if (event.defaultPrevented) return; const shouldPrevent = getAttribute(event.target, "data-preventdefault", true); if (shouldPrevent && matchesKey(event, "ArrowRight", "ArrowLeft", "ArrowDown", "ArrowUp")) { event.preventDefault(); } }); } // packages/ag-charts-core/src/identity/id.ts var ID_MAP = /* @__PURE__ */ new Map(); var nextElementID = 1; function resetIds() { ID_MAP.clear(); nextElementID = 1; } function createId(instance) { const constructor = instance.constructor; let className = Object.hasOwn(constructor, "className") ? constructor.className : constructor.name; inDevelopmentMode(() => { if (!className) { throw new Error(`The ${String(constructor)} is missing the 'className' property.`); } }); className ?? (className = "Unknown"); const nextId = (ID_MAP.get(className) ?? 0) + 1; ID_MAP.set(className, nextId); return `${className}-${nextId}`; } function createElementId() { return `ag-charts-${nextElementID++}`; } function generateUUID() { return crypto.randomUUID?.() ?? generateUUIDv4(); } function generateUUIDv4() { const uuidArray = new Uint8Array(16); crypto.getRandomValues(uuidArray); uuidArray[6] = uuidArray[6] & 15 | 64; uuidArray[8] = uuidArray[8] & 63 | 128; let uuid = ""; for (let i = 0; i < uuidArray.length; i++) { if (i === 4 || i === 6 || i === 8 || i === 10) { uuid += "-"; } uuid += uuidArray[i].toString(16).padStart(2, "0"); } return uuid; } // packages/ag-charts-core/src/utils/data/linkedList.ts function insertListItemsSorted(list, items, cmp2) { let head = list; let current = head; for (const value of items) { if (head == null || cmp2(head.value, value) > 0) { head = { value, next: head }; current = head; } else { current = current; while (current.next != null && cmp2(current.next.value, value) <= 0) { current = current.next; } current.next = { value, next: current.next }; } } return head; } // packages/ag-charts-core/src/state/memo.ts var memorizedFns = /* @__PURE__ */ new WeakMap(); function memo(params, fnGenerator) { const serialisedParams = JSON.stringify(params, null, 0); if (!memorizedFns.has(fnGenerator)) { memorizedFns.set(fnGenerator, /* @__PURE__ */ new Map()); } if (!memorizedFns.get(fnGenerator)?.has(serialisedParams)) { memorizedFns.get(fnGenerator)?.set(serialisedParams, fnGenerator(params)); } return memorizedFns.get(fnGenerator)?.get(serialisedParams); } var MemoizeNode = class { constructor() { this.weak = /* @__PURE__ */ new WeakMap(); this.strong = /* @__PURE__ */ new Map(); this.set = false; this.value = void 0; } }; function simpleMemorize2(fn, cacheCallback2) { let root = new MemoizeNode(); const memoised = (...p) => { let current = root; for (const param of p) { const target = typeof param === "object" || typeof param === "symbol" ? current.weak : current.strong; let next = target.get(param); if (next == null) { next = new MemoizeNode(); target.set(param, next); } current = next; } if (current.set) { cacheCallback2?.("hit", fn, p); return current.value; } else { const out = fn(...p); current.set = true; current.value = out; cacheCallback2?.("miss", fn, p); return out; } }; memoised.reset = () => { root = new MemoizeNode(); }; return memoised; } function simpleMemorize(fn, cacheCallback2) { const primitiveCache = /* @__PURE__ */ new Map(); const paramsToKeys = (...params) => { return params.map((v) => { if (typeof v === "object") return v; if (typeof v === "symbol") return v; if (!primitiveCache.has(v)) { primitiveCache.set(v, { v }); } return primitiveCache.get(v); }); }; const empty = {}; const cache = /* @__PURE__ */ new WeakMap(); return (...p) => { const keys = p.length === 0 ? [empty] : paramsToKeys(...p); let currentCache = cache; for (const key of keys.slice(0, -1)) { if (!currentCache.has(key)) { currentCache.set(key, /* @__PURE__ */ new WeakMap()); } currentCache = currentCache.get(key); } const finalKey = keys.at(-1); let cachedValue = currentCache.get(finalKey); if (cachedValue) { cacheCallback2?.("hit", fn, p); } else { cachedValue = fn(...p); currentCache.set(finalKey, cachedValue); cacheCallback2?.("miss", fn, p); } return cachedValue; }; } // packages/ag-charts-core/src/utils/data/nearest.ts function nearestSquared(x, y, objects, maxDistanceSquared = Infinity) { const result = { nearest: void 0, distanceSquared: maxDistanceSquared }; for (const obj of objects) { const thisDistance = obj.distanceSquared(x, y); if (thisDistance === 0) { return { nearest: obj, distanceSquared: 0 }; } else if (thisDistance < result.distanceSquared) { result.nearest = obj; result.distanceSquared = thisDistance; } } return result; } function nearestSquaredInContainer(x, y, container, maxDistanceSquared = Infinity) { const { x: tx = x, y: ty = y } = container.transformPoint?.(x, y) ?? {}; const result = { nearest: void 0, distanceSquared: maxDistanceSquared }; for (const child of container.children) { const { nearest, distanceSquared: distanceSquared2 } = child.nearestSquared(tx, ty, result.distanceSquared); if (distanceSquared2 === 0) { return { nearest, distanceSquared: distanceSquared2 }; } else if (distanceSquared2 < result.distanceSquared) { result.nearest = nearest; result.distanceSquared = distanceSquared2; } } return result; } // packages/ag-charts-core/src/utils/geometry/padding.ts var Padding = class extends BaseProperties { constructor(top = 0, right = top, bottom = top, left = right) { super(); this.top = top; this.right = right; this.bottom = bottom; this.left = left; } }; __decorateClass([ addFakeTransformToInstanceProperty ], Padding.prototype, "top", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Padding.prototype, "right", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Padding.prototype, "bottom", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Padding.prototype, "left", 2); // packages/ag-charts-core/src/utils/geometry/placement.ts function calculatePlacement(naturalWidth, naturalHeight, container, bounds) { let { top, right, bottom, left, width: width2, height: height2 } = bounds; if (left != null) { if (width2 != null) { right = container.width - left + width2; } else if (right != null) { width2 = container.width - left - right; } } else if (right != null && width2 != null) { left = container.width - right - width2; } if (top != null) { if (height2 != null) { bottom = container.height - top - height2; } else if (bottom != null) { height2 = container.height - bottom - top; } } else if (bottom != null && height2 != null) { top = container.height - bottom - height2; } if (width2 == null) { if (height2 == null) { height2 = naturalHeight; width2 = naturalWidth; } else { width2 = Math.ceil(naturalWidth * height2 / naturalHeight); } } else { height2 ?? (height2 = Math.ceil(naturalHeight * width2 / naturalWidth)); } if (left == null) { if (right == null) { left = Math.floor((container.width - width2) / 2); } else { left = container.width - right - width2; } } if (top == null) { if (bottom == null) { top = Math.floor((container.height - height2) / 2); } else { top = container.height - height2 - bottom; } } return { x: left, y: top, width: width2, height: height2 }; } // packages/ag-charts-core/src/state/stateMachine.ts var debugColor = "color: green"; var debugQuietColor = "color: grey"; function StateMachineProperty() { return addObserverToInstanceProperty(() => { }); } function applyProperties(parentState, childState) { const childProperties = listDecoratedProperties(childState); if (childProperties.length === 0) return; const properties = extractDecoratedProperties(parentState); for (const property of childProperties) { if (property in properties) { childState[property] = properties[property]; } } } var AbstractStateMachine = class { transitionRoot(event, data) { if (this.parent) { this.parent.transitionRoot(event, data); } else { this.transition(event, data); } } }; var _StateMachine = class _StateMachine extends AbstractStateMachine { constructor(defaultState, states, enterEach) { super(); this.defaultState = defaultState; this.states = states; this.enterEach = enterEach; this.debug = create(true, "animation"); this.state = defaultState; this.debug(`%c${this.constructor.name} | init -> ${defaultState}`, debugColor); } // TODO: handle events which do not require data without requiring `undefined` to be passed as as parameter, while // also still requiring data to be passed to those events which do require it. transition(event, data) { const shouldTransitionSelf = this.transitionChild(event, data); if (!shouldTransitionSelf || this.state === _StateMachine.child || this.state === _StateMachine.parent) { return; } const currentState = this.state; const currentStateConfig = this.states[this.state]; let destination = currentStateConfig[event]; const debugPrefix = `%c${this.constructor.name} | ${this.state} -> ${event} ->`; if (Array.isArray(destination)) { destination = destination.find((transition) => { if (!transition.guard) return true; const valid = transition.guard(data); if (!valid) { this.debug(`${debugPrefix} (guarded)`, transition.target, debugQuietColor); } return valid; }); } else if (typeof destination === "object" && !(destination instanceof _StateMachine) && destination.guard && !destination.guard(data)) { this.debug(`${debugPrefix} (guarded)`, destination.target, debugQuietColor); return; } if (!destination) { this.debug(`${debugPrefix} ${this.state}`, debugQuietColor); return; } const destinationState = this.getDestinationState(destination); const exitFn = destinationState === this.state ? void 0 : currentStateConfig.onExit; this.debug(`${debugPrefix} ${destinationState}`, debugColor); this.state = destinationState; if (typeof destination === "function") { destination(data); } else if (typeof destination === "object" && !(destination instanceof _StateMachine)) { destination.action?.(data); } exitFn?.(); this.enterEach?.(currentState, destinationState); if (destinationState !== currentState && destinationState !== _StateMachine.child && destinationState !== _StateMachine.parent) { this.states[destinationState].onEnter?.(currentState, data); } } transitionAsync(event, data) { setTimeout(() => { this.transition(event, data); }, 0); } is(value) { if (this.state === _StateMachine.child && this.childState) { return this.childState.is(value); } return this.state === value; } resetHierarchy() { this.debug( `%c${this.constructor.name} | ${this.state} -> [resetHierarchy] -> ${this.defaultState}`, "color: green" ); this.state = this.defaultState; } transitionChild(event, data) { if (this.state !== _StateMachine.child || !this.childState) return true; applyProperties(this, this.childState); this.childState.transition(event, data); if (!this.childState.is(_StateMachine.parent)) return true; this.debug(`%c${this.constructor.name} | ${this.state} -> ${event} -> ${this.defaultState}`, debugColor); this.state = this.defaultState; this.states[this.state].onEnter?.(); this.childState.resetHierarchy(); return false; } getDestinationState(destination) { let state = this.state; if (typeof destination === "string") { state = destination; } else if (destination instanceof _StateMachine) { this.childState = destination; this.childState.parent = this; state = _StateMachine.child; } else if (typeof destination === "object") { if (destination.target instanceof _StateMachine) { this.childState = destination.target; this.childState.parent = this; state = _StateMachine.child; } else if (destination.target != null) { state = destination.target; } } return state; } }; _StateMachine.child = "__child"; _StateMachine.parent = "__parent"; var StateMachine = _StateMachine; var ParallelStateMachine = class extends AbstractStateMachine { constructor(...stateMachines) { super(); this.stateMachines = stateMachines; for (const stateMachine of stateMachines) { stateMachine.parent = this; } } transition(event, data) { for (const stateMachine of this.stateMachines) { applyProperties(this, stateMachine); stateMachine.transition(event, data); } } transitionAsync(event, data) { for (const stateMachine of this.stateMachines) { applyProperties(this, stateMachine); stateMachine.transitionAsync(event, data); } } }; // packages/ag-charts-core/src/rendering/textMeasurer.ts var TextMeasurer = class { constructor(ctx, measureTextCached) { this.ctx = ctx; this.measureTextCached = measureTextCached; this.baselineMap = /* @__PURE__ */ new Map(); this.charMap = /* @__PURE__ */ new Map(); this.lineHeightCache = null; } baselineDistance(textBaseline) { if (textBaseline === "alphabetic") return 0; if (this.baselineMap.has(textBaseline)) { return this.baselineMap.get(textBaseline); } this.ctx.textBaseline = textBaseline; const { alphabeticBaseline } = this.ctx.measureText(""); this.baselineMap.set(textBaseline, alphabeticBaseline); this.ctx.textBaseline = "alphabetic"; return alphabeticBaseline; } lineHeight() { this.lineHeightCache ?? (this.lineHeightCache = this.measureText("").height); return this.lineHeightCache; } measureText(text) { const m = this.measureTextCached?.(text) ?? this.ctx.measureText(text); const { width: width2, // Apply fallbacks for environments like `node-canvas` where some metrics may be missing. fontBoundingBoxAscent: ascent = m.emHeightAscent, fontBoundingBoxDescent: descent = m.emHeightDescent } = m; const height2 = ascent + descent; return { width: width2, height: height2, ascent, descent }; } measureLines(text) { const lines = typeof text === "string" ? text.split(LineSplitter) : text; let width2 = 0; let height2 = 0; const lineMetrics = lines.map((line) => { const b = this.measureText(line); if (width2 < b.width) { width2 = b.width; } height2 += b.height; return { text: line, ...b }; }); return { width: width2, height: height2, lineMetrics }; } textWidth(text, estimate) { if (estimate) { let estimatedWidth = 0; for (let i = 0; i < text.length; i++) { estimatedWidth += this.textWidth(text.charAt(i)); } return estimatedWidth; } if (text.length > 1) { return this.ctx.measureText(text).width; } return this.charMap.get(text) ?? this.charWidth(text); } charWidth(char) { const { width: width2 } = this.ctx.measureText(char); this.charMap.set(char, width2); return width2; } }; var instanceMap = new LRUCache(50); function cachedTextMeasurer(font) { if (typeof font === "object") { font = toFontString(font); } let cachedMeasurer = instanceMap.get(font); if (cachedMeasurer) return cachedMeasurer; const cachedTextMetrics = new LRUCache(1e4); const ctx = createCanvasContext(); ctx.font = font; cachedMeasurer = new TextMeasurer(ctx, (text) => { let textMetrics = cachedTextMetrics.get(text); if (textMetrics) return textMetrics; textMetrics = ctx.measureText(text); cachedTextMetrics.set(text, textMetrics); return textMetrics; }); instanceMap.set(font, cachedMeasurer); return cachedMeasurer; } cachedTextMeasurer.clear = () => instanceMap.clear(); function measureTextSegments(textSegments, defaultFont) { let currentLine = { segments: [], width: 0, height: 0, ascent: 0, descent: 0 }; const lineMetrics = [currentLine]; for (const segment of textSegments) { const { text, fontSize = defaultFont.fontSize, fontStyle = defaultFont.fontStyle, fontWeight: fontWeight2 = defaultFont.fontWeight, fontFamily = defaultFont.fontFamily, ...rest } = segment; const font = { fontSize, fontStyle, fontWeight: fontWeight2, fontFamily }; const measurer = cachedTextMeasurer(font); const textLines = toTextString(text).split(LineSplitter); for (let i = 0; i < textLines.length; i++) { const textLine = textLines[i]; const textMetrics = measurer.measureText(textLine); if (i > 0) { currentLine = { segments: [], width: 0, height: 0, ascent: 0, descent: 0 }; lineMetrics.push(currentLine); } if (textLine) { currentLine.width += textMetrics.width; currentLine.ascent = Math.max(currentLine.ascent, textMetrics.ascent); currentLine.descent = Math.max(currentLine.descent, textMetrics.descent); currentLine.height = Math.max(currentLine.height, currentLine.ascent + currentLine.descent); currentLine.segments.push({ ...font, ...rest, text: textLine, textMetrics }); } } } let maxWidth = 0; let totalHeight = 0; for (const line of lineMetrics) { maxWidth = Math.max(maxWidth, line.width); totalHeight += line.height; } return { width: maxWidth, height: totalHeight, lineMetrics }; } // packages/ag-charts-core/src/utils/dom/domElements.ts function createElement(tagName, className, style2) { const element2 = getDocument().createElement(tagName); if (typeof className === "object") { style2 = className; className = void 0; } if (className) { for (const name of className.split(" ")) { element2.classList.add(name); } } if (style2) { Object.assign(element2.style, style2); } return element2; } function createSvgElement(elementName) { return getDocument().createElementNS("http://www.w3.org/2000/svg", elementName); } // packages/ag-charts-core/src/utils/dom/domDownload.ts function downloadUrl(dataUrl, fileName) { const body = getDocument("body"); const element2 = createElement("a", { display: "none" }); element2.href = dataUrl; element2.download = fileName; body.appendChild(element2); element2.click(); setTimeout(() => element2.remove()); } // packages/ag-charts-core/src/utils/text/textWrapper.ts function shouldHideOverflow(clippedResult, options) { return options.overflow === "hide" && clippedResult.some(isTextTruncated); } function wrapTextOrSegments(input, options) { return isArray(input) ? wrapTextSegments(input, options) : wrapLines(toTextString(input), options).join("\n"); } function wrapText(text, options) { return wrapLines(text, options).join("\n"); } function wrapLines(text, options) { return textWrap(text, options); } function truncateLine(text, measurer, maxWidth, ellipsisForce) { const ellipsisWidth = measurer.textWidth(EllipsisChar); let estimatedWidth = 0; let i = 0; for (; i < text.length; i++) { const charWidth = measurer.textWidth(text.charAt(i)); if (estimatedWidth + charWidth > maxWidth) break; estimatedWidth += charWidth; } if (text.length === i && (!ellipsisForce || estimatedWidth + ellipsisWidth <= maxWidth)) { return ellipsisForce ? appendEllipsis(text) : text; } text = text.slice(0, i).trimEnd(); while (text.length && measurer.textWidth(text) + ellipsisWidth > maxWidth) { text = text.slice(0, -1).trimEnd(); } return appendEllipsis(text); } function textWrap(text, options, widthOffset = 0) { const lines = text.split(LineSplitter); const measurer = cachedTextMeasurer(options.font); const result = []; if (options.textWrap === "never") { for (const line of lines) { const truncatedLine = truncateLine(line.trimEnd(), measurer, Math.max(0, options.maxWidth - widthOffset)); if (!truncatedLine) break; result.push(truncatedLine); widthOffset = 0; } return shouldHideOverflow(result, options) ? [] : result; } const wrapHyphenate = options.textWrap === "hyphenate"; const wrapOnSpace = options.textWrap == null || options.textWrap === "on-space"; for (const untrimmedLine of lines) { let line = untrimmedLine.trimEnd(); if (line === "") { result.push(line); continue; } let i = 0; let estimatedWidth = 0; let lastSpaceIndex = 0; if (!result.length) { estimatedWidth = widthOffset; } while (i < line.length) { const char = line.charAt(i); if (char === " ") { lastSpaceIndex = i; } estimatedWidth += measurer.textWidth(char); if (estimatedWidth > options.maxWidth) { if (i === 0) { line = ""; break; } let actualWidth = measurer.textWidth(line.slice(0, i + 1)); if (!result.length) { actualWidth += widthOffset; } if (actualWidth <= options.maxWidth) { estimatedWidth = actualWidth; i++; continue; } if (lastSpaceIndex) { const nextWord = getWordAt(line, lastSpaceIndex + 1); const textWidth = measurer.textWidth(nextWord); if (textWidth <= options.maxWidth) { result.push(line.slice(0, lastSpaceIndex).trimEnd()); line = line.slice(lastSpaceIndex).trimStart(); i = 0; estimatedWidth = 0; lastSpaceIndex = 0; continue; } else if (wrapOnSpace && textWidth > options.maxWidth) { result.push( line.slice(0, lastSpaceIndex).trimEnd(), truncateLine(line.slice(lastSpaceIndex).trimStart(), measurer, options.maxWidth, true) ); } } else if (wrapOnSpace) { const newLine2 = truncateLine(line, measurer, options.maxWidth, true); if (newLine2) { result.push(newLine2); } } if (wrapOnSpace) { line = ""; break; } const postfix = wrapHyphenate ? "-" : ""; let newLine = line.slice(0, i).trim(); while (newLine.length && measurer.textWidth(newLine + postfix) > options.maxWidth) { newLine = newLine.slice(0, -1).trimEnd(); } if (newLine && newLine !== TrimEdgeGuard) { result.push(newLine + postfix); } else { line = ""; break; } line = line.slice(newLine.length).trimStart(); i = -1; estimatedWidth = 0; lastSpaceIndex = 0; } i++; } if (line) { result.push(line); } } avoidOrphans(result, measurer, options); const clippedResult = clipLines(result, measurer, options); return shouldHideOverflow(clippedResult, options) ? [] : clippedResult; } function getWordAt(text, position) { const nextSpaceIndex = text.indexOf(" ", position); return nextSpaceIndex === -1 ? text.slice(position) : text.slice(position, nextSpaceIndex); } function clipLines(lines, measurer, options) { if (!isFiniteNumber(options.maxHeight)) { return lines; } const { height: height2, lineMetrics } = measurer.measureLines(lines); if (height2 <= options.maxHeight) { return lines; } for (let i = 0, cumulativeHeight = 0; i < lineMetrics.length; i++) { cumulativeHeight += lineMetrics[i].height; if (cumulativeHeight > options.maxHeight) { if (options.overflow === "hide" || i === 0) return []; const clippedResults = lines.slice(0, i); const lastLine = clippedResults.pop(); return clippedResults.concat( isTextTruncated(lastLine) ? lastLine : truncateLine(lastLine, measurer, options.maxWidth, true) ); } } return lines; } function avoidOrphans(lines, measurer, options) { if (options.avoidOrphans === false || lines.length < 2) return; const { length: length2 } = lines; const lastLine = lines[length2 - 1]; const beforeLast = lines[length2 - 2]; if (beforeLast.length < lastLine.length) return; const lastSpaceIndex = beforeLast.lastIndexOf(" "); if (lastSpaceIndex === -1 || lastSpaceIndex === beforeLast.indexOf(" ") || lastLine.includes(" ")) return; const lastWord = beforeLast.slice(lastSpaceIndex + 1); if (measurer.textWidth(lastLine + lastWord) <= options.maxWidth) { lines[length2 - 2] = beforeLast.slice(0, lastSpaceIndex); lines[length2 - 1] = lastWord + " " + lastLine; } } function wrapTextSegments(textSegments, options) { const { maxHeight = Infinity } = options; const result = []; let lineWidth = 0; let totalHeight = 0; function truncateLastSegment() { const lastSegment = result.pop(); if (!lastSegment) return; const measurer = cachedTextMeasurer(lastSegment); const truncatedText = truncateLine(lastSegment.text, measurer, options.maxWidth, true); const textMetrics = measurer.measureText(truncatedText); result.push({ ...lastSegment, text: truncatedText, textMetrics }); } for (const { width: width2, height: height2, segments } of measureTextSegments(textSegments, options.font).lineMetrics) { if (totalHeight + height2 > maxHeight) { if (result.length) { truncateLastSegment(); } break; } if (lineWidth + width2 <= options.maxWidth) { lineWidth += width2; totalHeight += height2; result.push(...segments); continue; } for (const segment of segments) { if (lineWidth + segment.textMetrics.width <= options.maxWidth) { lineWidth += segment.textMetrics.width; result.push(segment); continue; } const measurer = cachedTextMeasurer(segment); const guardedText = guardTextEdges(segment.text); const wrapOptions = { ...options, font: segment, maxHeight: maxHeight - totalHeight }; let wrappedLines = textWrap(guardedText, { ...wrapOptions, overflow: "hide" }, lineWidth); if (wrappedLines.length === 0) { if (options.textWrap === "never") { wrappedLines = textWrap(guardedText, wrapOptions, lineWidth); } else { wrappedLines = textWrap(guardedText, wrapOptions); const lastSegment = result.at(-1); if (lastSegment) { lastSegment.text += "\n"; lineWidth = 0; } } } if (wrappedLines.length === 0) { truncateLastSegment(); break; } const truncationIndex = wrappedLines.findIndex(isTextTruncated); if (truncationIndex !== -1) { wrappedLines = wrappedLines.slice(0, truncationIndex + 1); } const lastLine = wrappedLines.at(-1); for (const wrappedLine of wrappedLines) { const cleanLine = unguardTextEdges(wrappedLine); const textMetrics = measurer.measureText(cleanLine); const subSegment = { ...segment, text: cleanLine, textMetrics }; if (wrappedLine === lastLine) { lineWidth += textMetrics.width; } else { subSegment.text += "\n"; lineWidth = 0; } totalHeight += textMetrics.height; result.push(subSegment); } if (truncationIndex !== -1) break; } } return result; } // packages/ag-charts-core/src/utils/data/visibleRange.ts function rescaleVisibleRange(visibleRange, [s0, s1], [d0, d1]) { const dr = d1 - d0; const vr = s1 - s0; const vd0 = s0 + vr * visibleRange[0]; const vd1 = s0 + vr * visibleRange[1]; return [(vd0 - d0) / dr, (vd1 - d0) / dr]; } // packages/ag-charts-core/src/utils/time/time/duration.ts var durationSecond = 1e3; var durationMinute = durationSecond * 60; var durationHour = durationMinute * 60; var durationDay = durationHour * 24; var durationWeek = durationDay * 7; var durationMonth = durationDay * 30; var durationYear = durationDay * 365; // packages/ag-charts-core/src/utils/time/time/encoding.ts var tzOffset = (/* @__PURE__ */ new Date()).getTimezoneOffset() * durationMinute; var unitEncoding = { millisecond: { milliseconds: 1, hierarchy: "day", encode(date2) { return date2.getTime(); }, decode(encoded) { return new Date(encoded); } }, second: { milliseconds: durationSecond, hierarchy: "day", encode(date2, utc) { const offset = utc ? 0 : tzOffset; return Math.floor((date2.getTime() - offset) / durationSecond); }, decode(encoded, utc) { const offset = utc ? 0 : tzOffset; return new Date(offset + encoded * durationSecond); } }, minute: { milliseconds: durationMinute, hierarchy: "day", encode(date2, utc) { const offset = utc ? 0 : tzOffset; return Math.floor((date2.getTime() - offset) / durationMinute); }, decode(encoded, utc) { const offset = utc ? 0 : tzOffset; return new Date(offset + encoded * durationMinute); } }, hour: { milliseconds: durationHour, hierarchy: "day", encode(date2, utc) { const offset = utc ? 0 : tzOffset; return Math.floor((date2.getTime() - offset) / durationHour); }, decode(encoded, utc) { const offset = utc ? 0 : tzOffset; return new Date(offset + encoded * durationHour); } }, day: { milliseconds: durationDay, hierarchy: "month", encode(date2, utc) { const tzOffsetMs2 = utc ? 0 : date2.getTimezoneOffset() * durationMinute; return Math.floor((date2.getTime() - tzOffsetMs2) / durationDay); }, decode(encoded, utc) { let d; if (utc) { d = /* @__PURE__ */ new Date(0); d.setUTCDate(d.getUTCDate() + encoded); d.setUTCHours(0, 0, 0, 0); } else { d = new Date(1970, 0, 1); d.setDate(d.getDate() + encoded); } return d; } }, month: { milliseconds: durationMonth, hierarchy: "year", encode(date2, utc) { if (utc) { return date2.getUTCFullYear() * 12 + date2.getUTCMonth(); } else { return date2.getFullYear() * 12 + date2.getMonth(); } }, decode(encoded, utc) { if (utc) { const year = Math.floor(encoded / 12); const m = encoded - year * 12; return new Date(Date.UTC(year, m, 1)); } else { const y = Math.floor(encoded / 12); const month = encoded - y * 12; return new Date(y, month, 1); } } }, year: { milliseconds: durationYear, hierarchy: void 0, encode(date2, utc) { if (utc) { return date2.getUTCFullYear(); } else { return date2.getFullYear(); } }, decode(encoded, utc) { let d; if (utc) { d = /* @__PURE__ */ new Date(); d.setUTCFullYear(encoded); d.setUTCMonth(0, 1); d.setUTCHours(0, 0, 0, 0); } else { d = new Date(encoded, 0, 1, 0, 0, 0, 0); } return d; } } }; // packages/ag-charts-core/src/utils/time/time/range.ts function timeInterval(interval) { return typeof interval === "string" ? { unit: interval, step: 1, epoch: void 0, utc: false } : { unit: interval.unit, step: interval.step ?? 1, epoch: interval.epoch, utc: interval.utc ?? false }; } function getOffset(unit, step, epoch, utc) { if (epoch == null) return 0; const encoding = unitEncoding[unit]; return Math.floor(encoding.encode(new Date(epoch), utc)) % step; } function encode(d, unit, step, utc, offset) { const encoding = unitEncoding[unit]; return Math.floor((encoding.encode(new Date(d), utc) - offset) / step); } function decode(encoded, unit, step, utc, offset) { const encoding = unitEncoding[unit]; return encoding.decode(encoded * step + offset, utc); } function encodingFloor(date2, unit, step, utc, offset) { const d = new Date(date2); const e = encode(d, unit, step, utc, offset); return decode(e, unit, step, utc, offset); } function encodingCeil(date2, unit, step, utc, offset) { const d = new Date(Number(date2) - 1); const e = encode(d, unit, step, utc, offset); return decode(e + 1, unit, step, utc, offset); } function intervalFloor(interval, date2) { const { unit, step, epoch, utc } = timeInterval(interval); const offset = getOffset(unit, step, epoch, utc); return encodingFloor(date2, unit, step, utc, offset); } function intervalCeil(interval, date2) { const { unit, step, epoch, utc } = timeInterval(interval); const offset = getOffset(unit, step, epoch, utc); return encodingCeil(date2, unit, step, utc, offset); } function intervalPrevious(interval, date2) { const { unit, step, epoch, utc } = timeInterval(interval); const offset = getOffset(unit, step, epoch, utc); return decode( encode(encodingCeil(date2, unit, step, utc, offset), unit, step, utc, offset) - 1, unit, step, utc, offset ); } function intervalNext(interval, date2) { const { unit, step, epoch, utc } = timeInterval(interval); const offset = getOffset(unit, step, epoch, utc); return decode( encode(encodingFloor(date2, unit, step, utc, offset), unit, step, utc, offset) + 1, unit, step, utc, offset ); } function intervalExtent(start2, stop, visibleRange) { if (start2.valueOf() > stop.valueOf()) { [start2, stop] = [stop, start2]; if (visibleRange != null) { visibleRange = [1 - visibleRange[1], 1 - visibleRange[0]]; } } if (visibleRange != null) { const delta3 = stop.valueOf() - start2.valueOf(); const t0 = start2.valueOf(); start2 = new Date(t0 + visibleRange[0] * delta3); stop = new Date(t0 + visibleRange[1] * delta3); } return [new Date(start2), new Date(stop)]; } function rangeData(interval, start2, stop, { extend = false, visibleRange = [0, 1], limit, defaultAlignment = "start" } = {}) { const params = timeInterval(interval); const { unit, step, utc } = params; let epoch; if (params.epoch != null) { epoch = params.epoch; } else if (defaultAlignment === "interval") { epoch = void 0; } else if (start2.valueOf() > stop.valueOf()) { epoch = stop; } else { epoch = start2; } const offset = getOffset(params.unit, params.step, epoch, params.utc); let [d0, d1] = intervalExtent(start2, stop, visibleRange); d0 = extend ? encodingFloor(d0, unit, step, utc, offset) : encodingCeil(d0, unit, step, utc, offset); d1 = extend ? encodingCeil(d1, unit, step, utc, offset) : encodingFloor(d1, unit, step, utc, offset); const e0 = encode(d0, unit, step, utc, offset); let e1 = encode(d1, unit, step, utc, offset); if (limit != null && e1 - e0 > limit) { e1 = e0 + limit; } return { range: [e0, e1], unit, step, utc, offset }; } function intervalRangeCount(interval, start2, stop, params) { const { range: [e0, e1] } = rangeData(interval, start2, stop, params); return Math.abs(e1 - e0); } function intervalRange(interval, start2, stop, params) { const { range: [e0, e1], unit, step, utc, offset } = rangeData(interval, start2, stop, params); const values = []; for (let e = e0; e <= e1; e += 1) { const d = decode(e, unit, step, utc, offset); values.push(d); } return values; } function intervalRangeNumeric(interval, start2, stop, params) { const { range: [e0, e1], unit, step, utc, offset } = rangeData(interval, start2, stop, params); const count = Math.max(0, e1 - e0 + 1); const encodedValues = new Array(count); for (let i = 0; i < count; i++) { encodedValues[i] = e0 + i; } return { encodedValues, encodingParams: { unit, step, utc, offset } }; } function decodeIntervalValue(encoded, encodingParams) { return decode(encoded, encodingParams.unit, encodingParams.step, encodingParams.utc, encodingParams.offset); } var tzOffsetMs = (/* @__PURE__ */ new Date()).getTimezoneOffset() * 6e4; var DURATION_SECOND = 1e3; var DURATION_MINUTE = 6e4; var DURATION_HOUR = 36e5; function encodedToTimestamp(encoded, encodingParams) { const { unit, step, utc, offset } = encodingParams; const rawEncoded = encoded * step + offset; switch (unit) { case "millisecond": return rawEncoded; case "second": { const tzOffset2 = utc ? 0 : tzOffsetMs; return tzOffset2 + rawEncoded * DURATION_SECOND; } case "minute": { const tzOffset2 = utc ? 0 : tzOffsetMs; return tzOffset2 + rawEncoded * DURATION_MINUTE; } case "hour": { const tzOffset2 = utc ? 0 : tzOffsetMs; return tzOffset2 + rawEncoded * DURATION_HOUR; } default: { const encoding = unitEncoding[unit]; return encoding.decode(rawEncoded, utc).valueOf(); } } } function intervalRangeStartIndex(interval, start2, stop, { extend, visibleRange, limit, defaultAlignment } = {}) { const { range: [s] } = rangeData(interval, start2, stop, { extend, visibleRange, limit, defaultAlignment }); const { range: [s0] } = rangeData(interval, start2, stop, { extend, limit, defaultAlignment }); return s - s0; } // packages/ag-charts-core/src/utils/time/time/index.ts function intervalUnit(interval) { return typeof interval === "string" ? interval : interval.unit; } function intervalStep(interval) { return typeof interval === "string" ? 1 : interval.step ?? 1; } function intervalEpoch(interval) { return typeof interval === "string" ? void 0 : interval.epoch; } function intervalHierarchy(interval) { return unitEncoding[intervalUnit(interval)].hierarchy; } function intervalMilliseconds(interval) { const step = intervalStep(interval); return step * unitEncoding[intervalUnit(interval)].milliseconds; } // packages/ag-charts-core/src/utils/time/ticks.ts var tInterval = (timeInterval3, step) => ({ duration: intervalMilliseconds(timeInterval3) * step, timeInterval: timeInterval3, step }); var TickIntervals = [ tInterval({ unit: "second" }, 1), tInterval({ unit: "second" }, 5), tInterval({ unit: "second" }, 15), tInterval({ unit: "second" }, 30), tInterval({ unit: "minute" }, 1), tInterval({ unit: "minute" }, 5), tInterval({ unit: "minute" }, 15), tInterval({ unit: "minute" }, 30), tInterval({ unit: "hour" }, 1), tInterval({ unit: "hour" }, 3), tInterval({ unit: "hour" }, 6), tInterval({ unit: "hour" }, 12), tInterval({ unit: "day" }, 1), tInterval({ unit: "day" }, 2), tInterval({ unit: "day", step: 7 }, 1), tInterval({ unit: "day", step: 7 }, 2), tInterval({ unit: "day", step: 7 }, 3), tInterval({ unit: "month" }, 1), tInterval({ unit: "month" }, 2), tInterval({ unit: "month" }, 3), tInterval({ unit: "month" }, 4), tInterval({ unit: "month" }, 6), tInterval({ unit: "year" }, 1) ]; var TickMultipliers = [1, 2, 5, 10]; function isCloseToInteger(n, delta3) { return Math.abs(Math.round(n) - n) < delta3; } function countTicks(d0, d1, step) { const extent2 = Math.abs(d1 - d0); return extent2 >= step ? Math.abs(d1 - d0) / step + 1 : 1; } function createTicks(start2, stop, count, minCount, maxCount, visibleRange) { if (start2 === stop) return { ticks: [start2], count: 1, firstTickIndex: 0 }; if (count < 2) return { ticks: [start2, stop], count: 2, firstTickIndex: 0 }; const step = tickStep(start2, stop, count, minCount, maxCount); if (!Number.isFinite(step)) return { ticks: [], count: 0, firstTickIndex: void 0 }; let d0 = start2; let d1 = stop; if (!isCloseToInteger(d0 / step, 1e-12)) { d0 = Math.ceil(d0 / step) * step; } if (!isCloseToInteger(d1 / step, 1e-12)) { d1 = Math.floor(d1 / step) * step; } if (visibleRange != null) { visibleRange = rescaleVisibleRange(visibleRange, [start2, stop], [d0, d1]); } const { ticks } = range(d0, d1, step, visibleRange); const firstTick = ticks.at(0); return { ticks, count: countTicks(d0, d1, step), firstTickIndex: firstTick == null ? void 0 : Math.round((firstTick - d0) / step) }; } var minPrimaryTickRatio = Math.floor(2 * durationWeek / durationMonth * 10) / 10; function isPrimaryTickInterval({ timeInterval: timeInterval3, step }) { const milliseconds = intervalMilliseconds(timeInterval3) * step; const hierarchy = intervalHierarchy(timeInterval3); const hierarchyMilliseconds = hierarchy ? intervalMilliseconds(hierarchy) : void 0; return milliseconds <= (hierarchyMilliseconds ?? Infinity) * minPrimaryTickRatio; } function defaultEpoch(timeInterval3, { weekStart }) { if (timeInterval3.unit === "day" && timeInterval3.step === 7) { return weekStart; } } function getTickTimeInterval(start2, stop, count, minCount, maxCount, { weekStart, primaryOnly = false, targetInterval }) { if (count <= 0) return; const target = targetInterval ?? Math.abs(stop - start2) / Math.max(count, 1); const i0 = TickIntervals.findLast((t) => (!primaryOnly || isPrimaryTickInterval(t)) && target > t.duration); const i1 = TickIntervals.find((t) => (!primaryOnly || isPrimaryTickInterval(t)) && target <= t.duration); if (i0 == null) { const step2 = Math.max(tickStep(start2, stop, count, minCount, maxCount), 1); return { unit: "millisecond", step: step2 }; } else if (i1 == null) { const step2 = targetInterval == null ? tickStep(start2 / durationYear, stop / durationYear, count, minCount, maxCount) : 1; return { unit: "year", step: step2 }; } const { timeInterval: timeInterval3, step } = target - i0.duration < i1.duration - target ? i0 : i1; return { unit: timeInterval3.unit, step: intervalStep(timeInterval3) * step, epoch: defaultEpoch(timeInterval3, { weekStart }) }; } function tickStep(start2, end3, count, minCount = 0, maxCount = Infinity) { if (start2 === end3) { return clamp(1, minCount, maxCount); } else if (count < 1) { return Number.NaN; } const extent2 = Math.abs(end3 - start2); const step = 10 ** Math.floor(Math.log10(extent2 / count)); let m = Number.NaN, minDiff = Infinity, isInBounds = false; for (const multiplier of TickMultipliers) { const c = Math.ceil(extent2 / (multiplier * step)); const validBounds = c >= minCount && c <= maxCount; if (isInBounds && !validBounds) continue; const diffCount = Math.abs(c - count); if (minDiff > diffCount || isInBounds !== validBounds) { isInBounds || (isInBounds = validBounds); minDiff = diffCount; m = multiplier; } } return m * step; } function decimalPlaces(decimal) { for (let i = decimal.length - 1; i >= 0; i -= 1) { if (decimal[i] !== "0") { return i + 1; } } return 0; } function tickFormat(ticks, format) { const options = parseNumberFormat(format ?? ",f"); if (options == null) return; if (options.precision == null || Number.isNaN(options.precision)) { if (!options.type || "eEFgGnprs".includes(options.type)) { options.precision = Math.max( ...ticks.map((x) => { if (!Number.isFinite(x)) return 0; const [integer, decimal] = x.toExponential((options.type ? 6 : 12) - 1).split(/[.e]/g); return (integer !== "1" && integer !== "-1" ? 1 : 0) + decimalPlaces(decimal) + 1; }) ); } else if ("f%".includes(options.type)) { options.precision = Math.max( ...ticks.map((x) => { if (!Number.isFinite(x) || x === 0) return 0; const l = Math.floor(Math.log10(Math.abs(x))); const digits = options.type ? 6 : 12; const decimal = x.toExponential(digits - 1).split(/[.e]/g)[1]; const decimalLength = decimalPlaces(decimal); return Math.max(0, decimalLength - l); }) ); } } const formatter2 = createNumberFormatter(options); return (n) => formatter2(Number(n)); } function range(start2, end3, step, visibleRange) { if (!Number.isFinite(step) || step <= 0) { return { ticks: [], count: 0, firstTickIndex: void 0 }; } else if (start2 === end3) { return { ticks: [start2], count: 1, firstTickIndex: 0 }; } const f = 10 ** countFractionDigits(step); const d0 = Math.min(start2, end3); const d1 = Math.max(start2, end3); let vd0; let vd1; if (visibleRange != null && (visibleRange[0] !== 0 || visibleRange[1] !== 1)) { const rangeExtent = end3 - start2; const adjustedStart = start2 + rangeExtent * visibleRange[0]; const adjustedEnd = end3 - rangeExtent * (1 - visibleRange[1]); vd0 = Math.min(adjustedStart, adjustedEnd); vd1 = Math.max(adjustedStart, adjustedEnd); } else { vd0 = d0; vd1 = d1; } vd0 = Math.floor(vd0 * f) / f; vd1 = Math.ceil(vd1 * f) / f; const ticks = []; for (let i = 0; ; i += 1) { const p = Math.round((d0 + step * i) * f) / f; if (p > d1) break; if (p >= vd0 && p <= vd1) { ticks.push(p); } } const firstTick = ticks.at(0); return { ticks, count: countTicks(d0, d1, step), firstTickIndex: firstTick == null ? void 0 : Math.round((firstTick - d0) / step) }; } function isDenseInterval(count, availableRange) { if (count >= availableRange) { warnOnce( `the configured interval results in more than 1 item per pixel, ignoring. Supply a larger interval or omit this configuration` ); return true; } return false; } function niceTicksDomain(start2, end3) { const extent2 = Math.abs(end3 - start2); const step = 10 ** Math.floor(Math.log10(extent2)); let minError = Infinity, ticks = [start2, end3]; for (const multiplier of TickMultipliers) { const m = multiplier * step; const d0 = Math.floor(start2 / m) * m; const d1 = Math.ceil(end3 / m) * m; const error2 = 1 - extent2 / Math.abs(d1 - d0); if (minError > error2) { minError = error2; ticks = [d0, d1]; } } return ticks; } function estimateTickCount(rangeExtent, zoomExtent, minSpacing, maxSpacing, defaultTickCount, defaultMinSpacing) { if (rangeExtent <= 0) { return { minTickCount: 0, maxTickCount: 0, tickCount: 0 }; } defaultMinSpacing = Math.max(defaultMinSpacing, rangeExtent / (defaultTickCount + 1)); minSpacing ?? (minSpacing = defaultMinSpacing); maxSpacing ?? (maxSpacing = rangeExtent); if (minSpacing > maxSpacing) { if (minSpacing === defaultMinSpacing) { minSpacing = maxSpacing; } else { maxSpacing = minSpacing; } } minSpacing = Math.max(minSpacing, 1); const maxTickCount = Math.max(1, Math.floor(rangeExtent / (zoomExtent * minSpacing))); const minTickCount = Math.min(maxTickCount, Math.ceil(rangeExtent / (zoomExtent * maxSpacing))); const tickCount = clamp(minTickCount, Math.floor(defaultTickCount / zoomExtent), maxTickCount); return { minTickCount, maxTickCount, tickCount }; } // packages/ag-charts-core/src/utils/time/timeFormatDefaults.ts function dateToNumber(value) { return value instanceof Date ? value.getTime() : value; } function lowestGranularityForInterval(interval) { if (interval < durationSecond) { return "millisecond"; } else if (interval < durationMinute) { return "second"; } else if (interval < durationHour) { return "minute"; } else if (interval < durationHour * 23) { return "hour"; } else if (interval < 28 * durationDay) { return "day"; } else if (interval < durationYear) { return "month"; } else { return "year"; } } function lowestGranularityUnitForTicks(ticks) { if (ticks.length === 0) { return "millisecond"; } else if (ticks.length === 1) { return lowestGranularityUnitForValue(ticks[0]); } let minInterval = Infinity; for (let i = 1; i < ticks.length; i++) { minInterval = Math.min(minInterval, Math.abs(ticks[i].valueOf() - ticks[i - 1].valueOf())); } return lowestGranularityForInterval(minInterval); } function lowestGranularityUnitForValue(value) { if (intervalFloor("second", value) < value) { return "millisecond"; } else if (intervalFloor("minute", value) < value) { return "second"; } else if (intervalFloor("hour", value) < value) { return "minute"; } else if (intervalFloor("day", value) < value) { return "hour"; } else if (intervalFloor("month", value) < value) { return "day"; } else if (intervalFloor("year", value) < value) { return "month"; } return "year"; } function dateTruncationForDomain(domain) { const [d0, d1] = domain.length === 0 ? [0, 0] : findMinMax([domain[0].valueOf(), domain.at(-1).valueOf()]); const startYear = new Date(d0).getFullYear(); const stopYear = new Date(d1).getFullYear(); if (startYear !== stopYear) return; const startMonth = new Date(d0).getMonth(); const stopMonth = new Date(d1).getMonth(); if (startMonth !== stopMonth) return "year"; const startDate = new Date(d0).getDate(); const stopDate = new Date(d1).getDate(); if (startDate !== stopDate) return "month"; return "day"; } // packages/ag-charts-core/src/identity/idGenerator.ts function createIdsGenerator() { const idsCounter = /* @__PURE__ */ new Map(); return (name) => { const counter = idsCounter.get(name); if (counter) { idsCounter.set(name, counter + 1); return `${name}_${counter}`; } idsCounter.set(name, 1); return name; }; } // packages/ag-charts-core/src/utils/data/value.ts function isStringObject(value) { return value != null && Object.hasOwn(value, "toString") && isString(value.toString()); } function isNumberObject(value) { return value != null && Object.hasOwn(value, "valueOf") && isFiniteNumber(value.valueOf()); } function isContinuous(value) { return isFiniteNumber(value) || isValidDate(value) || isNumberObject(value); } function checkDatum(value, isContinuousScale) { return value != null && (!isContinuousScale || isContinuous(value)); } function transformIntegratedCategoryValue(value) { if (isStringObject(value) && Object.hasOwn(value, "id")) { return value.id; } return value; } function readIntegratedWrappedValue(value) { if (isStringObject(value) && Object.hasOwn(value, "value")) { return value.value; } return value; } // packages/ag-charts-core/src/utils/geometry/vector.ts var vector_exports = {}; __export(vector_exports, { add: () => add, angle: () => angle, apply: () => apply, distance: () => distance, distanceSquared: () => distanceSquared, equal: () => equal, from: () => from, gradient: () => gradient, intercept: () => intercept, intersectAtX: () => intersectAtX, intersectAtY: () => intersectAtY, length: () => length, lengthSquared: () => lengthSquared, multiply: () => multiply, normalized: () => normalized, origin: () => origin, required: () => required2, rotate: () => rotate, round: () => round, sub: () => sub }); function add(a, b) { if (typeof b === "number") { return { x: a.x + b, y: a.y + b }; } return { x: a.x + b.x, y: a.y + b.y }; } function sub(a, b) { if (typeof b === "number") { return { x: a.x - b, y: a.y - b }; } return { x: a.x - b.x, y: a.y - b.y }; } function multiply(a, b) { if (typeof b === "number") { return { x: a.x * b, y: a.y * b }; } return { x: a.x * b.x, y: a.y * b.y }; } function length(a) { return Math.hypot(a.x, a.y); } function lengthSquared(a) { return a.x * a.x + a.y * a.y; } function distance(a, b) { return length(sub(a, b)); } function distanceSquared(a, b) { return lengthSquared(sub(a, b)); } function normalized(a) { const l = length(a); return { x: a.x / l, y: a.y / l }; } function angle(a, b) { if (b == null) return Math.atan2(a.y, a.x); return Math.atan2(a.y, a.x) - Math.atan2(b.y, b.x); } function rotate(a, theta, b = origin()) { const l = length(a); return { x: b.x + l * Math.cos(theta), y: b.y + l * Math.sin(theta) }; } function gradient(a, b, reflection) { const dx = b.x - a.x; const dy = reflection == null ? b.y - a.y : reflection - b.y - (reflection - a.y); return dy / dx; } function intercept(a, gradient2, reflection) { const y = reflection == null ? a.y : reflection - a.y; return y - gradient2 * a.x; } function intersectAtY(gradient2, coefficient, y = 0, reflection) { return { x: gradient2 === Infinity ? Infinity : (y - coefficient) / gradient2, y: reflection == null ? y : reflection - y }; } function intersectAtX(gradient2, coefficient, x = 0, reflection) { const y = gradient2 === Infinity ? Infinity : gradient2 * x + coefficient; return { x, y: reflection == null ? y : reflection - y }; } function round(a, decimals = 2) { return { x: roundTo(a.x, decimals), y: roundTo(a.y, decimals) }; } function equal(a, b) { return a.x === b.x && a.y === b.y; } function from(a, b) { if (typeof a === "number") { return { x: a, y: b }; } if ("currentX" in a) { return { x: a.currentX, y: a.currentY }; } if ("offsetWidth" in a) { return { x: a.offsetWidth, y: a.offsetHeight }; } if ("width" in a) { return [ { x: a.x, y: a.y }, { x: a.x + a.width, y: a.y + a.height } ]; } if ("x1" in a) { return [ { x: a.x1, y: a.y1 }, { x: a.x2, y: a.y2 } ]; } throw new Error(`Values can not be converted into a vector: [${JSON.stringify(a)}] [${b}]`); } function apply(a, b) { a.x = b.x; a.y = b.y; return a; } function required2(a) { return { x: a?.x ?? 0, y: a?.y ?? 0 }; } function origin() { return { x: 0, y: 0 }; } // packages/ag-charts-core/src/utils/geometry/vector4.ts var vector4_exports = {}; __export(vector4_exports, { bottomCenter: () => bottomCenter, center: () => center, clone: () => clone, collides: () => collides, end: () => end, from: () => from2, height: () => height, normalise: () => normalise, origin: () => origin2, round: () => round2, start: () => start, topCenter: () => topCenter, width: () => width }); function start(a) { return { x: a.x1, y: a.y1 }; } function end(a) { return { x: a.x2, y: a.y2 }; } function topCenter(a) { return { x: (a.x1 + a.x2) / 2, y: Math.min(a.y1, a.y2) }; } function center(a) { return { x: (a.x1 + a.x2) / 2, y: (a.y1 + a.y2) / 2 }; } function bottomCenter(a) { return { x: (a.x1 + a.x2) / 2, y: Math.max(a.y1, a.y2) }; } function width(a) { return Math.abs(a.x2 - a.x1); } function height(a) { return Math.abs(a.y2 - a.y1); } function round2(a) { return { x1: Math.round(a.x1), y1: Math.round(a.y1), x2: Math.round(a.x2), y2: Math.round(a.y2) }; } function clone(a) { return { x1: a.x1, y1: a.y1, x2: a.x2, y2: a.y2 }; } function collides(a, b) { const an = normalise(a); const bn = normalise(b); return an.x1 <= bn.x2 && an.x2 >= bn.x1 && an.y1 <= bn.y2 && an.y2 >= bn.y1; } function normalise(a) { return { x1: Math.min(a.x1, a.x2), x2: Math.max(a.x1, a.x2), y1: Math.min(a.y1, a.y2), y2: Math.max(a.y1, a.y2) }; } function from2(a, b, c, d) { if (typeof a === "number") { return { x1: a, y1: b, x2: c, y2: d }; } if ("width" in a) { return normalise({ x1: a.x, y1: a.y, x2: a.x + a.width, y2: a.y + a.height }); } throw new Error(`Values can not be converted into a vector4: [${JSON.stringify(a)}] [${b}] [${c}] [${d}]`); } function origin2() { return { x1: 0, y1: 0, x2: 0, y2: 0 }; } // packages/ag-charts-core/src/utils/geometry/fill.ts function isGradientFill(fill) { return isObject(fill) && fill.type == "gradient"; } function isGradientFillArray(fills) { return isArray(fills) && fills.every(isGradientFill); } function isStringFillArray(fills) { return isArray(fills) && fills.every((fill) => typeof fill === "string"); } function isPatternFill(fill) { return fill !== null && isObject(fill) && fill.type == "pattern"; } function isImageFill(fill) { return fill !== null && isObject(fill) && fill.type == "image"; } function isGradientOrPatternFill(fill) { return isGradientFill(fill) || isPatternFill(fill); } // packages/ag-charts-core/src/utils/geometry/bezier.ts function evaluateBezier(p0, p1, p2, p3, t) { return (1 - t) ** 3 * p0 + 3 * (1 - t) ** 2 * t * p1 + 3 * (1 - t) * t ** 2 * p2 + t ** 3 * p3; } function solveBezier(p0, p1, p2, p3, value) { if (value <= Math.min(p0, p3)) { return p0 < p3 ? 0 : 1; } else if (value >= Math.max(p0, p3)) { return p0 < p3 ? 1 : 0; } let t0 = 0; let t1 = 1; let t = Number.NaN; for (let i = 0; i < 12; i += 1) { t = (t0 + t1) / 2; const curveValue = evaluateBezier(p0, p1, p2, p3, t); if (curveValue < value) { t0 = t; } else { t1 = t; } } return t; } function splitBezier2D(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y, t) { const x01 = (1 - t) * p0x + t * p1x; const y01 = (1 - t) * p0y + t * p1y; const x12 = (1 - t) * p1x + t * p2x; const y12 = (1 - t) * p1y + t * p2y; const x23 = (1 - t) * p2x + t * p3x; const y23 = (1 - t) * p2y + t * p3y; const x012 = (1 - t) * x01 + t * x12; const y012 = (1 - t) * y01 + t * y12; const x123 = (1 - t) * x12 + t * x23; const y123 = (1 - t) * y12 + t * y23; const x0123 = (1 - t) * x012 + t * x123; const y0123 = (1 - t) * y012 + t * y123; return [ [ { x: p0x, y: p0y }, { x: x01, y: y01 }, { x: x012, y: y012 }, { x: x0123, y: y0123 } ], [ { x: x0123, y: y0123 }, { x: x123, y: y123 }, { x: x23, y: y23 }, { x: p3x, y: p3y } ] ]; } function calculateDerivativeExtrema(p0, p1, p2, p3) { const a = -p0 + 3 * p1 - 3 * p2 + p3; const b = 2 * (p0 - 2 * p1 + p2); const c = -p0 + p1; if (a === 0) { if (b !== 0) { const t = -c / b; if (t > 0 && t < 1) { return [t]; } } return []; } const discriminant = b * b - 4 * a * c; if (discriminant >= 0) { const sqrtDiscriminant = Math.sqrt(discriminant); const t1 = (-b + sqrtDiscriminant) / (2 * a); const t2 = (-b - sqrtDiscriminant) / (2 * a); return [t1, t2].filter((t) => t > 0 && t < 1); } return []; } function bezier2DExtrema(cp0x, cp0y, cp1x, cp1y, cp2x, cp2y, cp3x, cp3y) { const tx = calculateDerivativeExtrema(cp0x, cp1x, cp2x, cp3x); const ty = calculateDerivativeExtrema(cp0y, cp1y, cp2y, cp3y); return [...tx, ...ty]; } function bezierCandidate(points, x, y) { const midX = evaluateBezier(points[0].x, points[1].x, points[2].x, points[3].x, 0.5); const midY = evaluateBezier(points[0].y, points[1].y, points[2].y, points[3].y, 0.5); const distance2 = Math.hypot(midX - x, midY - y); const minDistance = Math.min( Math.hypot(points[0].x - x, points[0].y - y), Math.hypot(points[1].x - x, points[1].y - y), Math.hypot(points[2].x - x, points[2].y - y), Math.hypot(points[3].x - x, points[3].y - y) ); return { points, distance: distance2, minDistance }; } function bezier2DDistance(cp0x, cp0y, cp1x, cp1y, cp2x, cp2y, cp3x, cp3y, x, y, precision = 1) { const points0 = [ { x: cp0x, y: cp0y }, { x: cp1x, y: cp1y }, { x: cp2x, y: cp2y }, { x: cp3x, y: cp3y } ]; let queue = { value: bezierCandidate(points0, x, y), next: null }; let bestResult; while (queue != null) { const { points, distance: distance2, minDistance } = queue.value; queue = queue.next; if (bestResult == null || distance2 < bestResult.distance) { bestResult = { distance: distance2, minDistance }; } if (bestResult != null && bestResult.distance - minDistance <= precision) { continue; } const [leftPoints, rightPoints] = splitBezier2D( points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y, points[3].x, points[3].y, 0.5 ); const newCandidates = [bezierCandidate(leftPoints, x, y), bezierCandidate(rightPoints, x, y)].sort( bezierCandidateCmp ); queue = insertListItemsSorted(queue, newCandidates, bezierCandidateCmp); } return bestResult?.distance ?? Infinity; } var bezierCandidateCmp = (a, b) => b.minDistance - a.minDistance; // packages/ag-charts-core/src/utils/geometry/labelPlacement.ts function circleRectOverlap({ point: c, anchor: unitCenter }, x, y, w, h) { if (c.size === 0) { return false; } let cx = c.x; let cy = c.y; if (unitCenter != null) { cx -= (unitCenter.x - 0.5) * c.size; cy -= (unitCenter.y - 0.5) * c.size; } let edgeX = cx; if (cx < x) { edgeX = x; } else if (cx > x + w) { edgeX = x + w; } let edgeY = cy; if (cy < y) { edgeY = y; } else if (cy > y + h) { edgeY = y + h; } const dx = cx - edgeX; const dy = cy - edgeY; const d = Math.hypot(dx, dy); return d <= c.size / 2; } function isPointLabelDatum(x) { return x != null && typeof x.point === "object" && typeof x.label === "object"; } var labelPlacements = { top: { x: 0, y: -1 }, bottom: { x: 0, y: 1 }, left: { x: -1, y: 0 }, right: { x: 1, y: 0 }, "top-left": { x: -1, y: -1 }, "top-right": { x: 1, y: -1 }, "bottom-left": { x: -1, y: 1 }, "bottom-right": { x: 1, y: 1 } }; function placeLabels(data, bounds, padding2 = 5) { const result = /* @__PURE__ */ new Map(); const previousResults = []; const sortedDataClone = new Map( Array.from(data.entries(), ([k, d]) => [k, d.toSorted((a, b) => b.point.size - a.point.size)]) ); const dataValues = [...sortedDataClone.values()].flat(); for (const [seriesId, datums] of sortedDataClone.entries()) { const labels = []; if (!datums[0]?.label) continue; for (let index = 0, ln = datums.length; index < ln; index++) { const d = datums[index]; const { point, label, anchor } = d; const { text, width: width2, height: height2 } = label; const r = point.size / 2; let dx = 0; let dy = 0; if (r > 0 && d.placement != null) { const placement = labelPlacements[d.placement]; dx = (width2 / 2 + r + padding2) * placement.x; dy = (height2 / 2 + r + padding2) * placement.y; } let x = point.x - width2 / 2 + dx; let y = point.y - height2 / 2 + dy; if (anchor) { x -= (anchor.x - 0.5) * point.size; y -= (anchor.y - 0.5) * point.size; } if (boxContains(bounds, x, y, width2, height2) && !dataValues.some((dataDatum) => circleRectOverlap(dataDatum, x, y, width2, height2)) && !previousResults.some((pr) => boxCollides(pr, x, y, width2, height2))) { const resultDatum = { index, text, x, y, width: width2, height: height2, datum: d }; labels.push(resultDatum); previousResults.push(resultDatum); } } result.set(seriesId, labels); } return result; } // packages/ag-charts-core/src/utils/geometry/scaling.ts function isContinuousScaling(scaling) { return scaling.type === "continuous" || scaling.type === "log"; } function isCategoryScaling(scaling) { return scaling.type === "category"; } function isUnitTimeCategoryScaling(scaling) { return "variant" in scaling && scaling.variant === "unit-time"; } function isStandardCategoryScaling(scaling) { return !("variant" in scaling); } function areScalingEqual(a, b) { if (a === void 0 || b === void 0) { return a !== void 0 || b !== void 0; } if (isContinuousScaling(a) && isContinuousScaling(b)) { return a.type === b.type && arraysEqual(a.domain, b.domain) && arraysEqual(a.range, b.range); } if (isCategoryScaling(a) && isCategoryScaling(b)) { if (isUnitTimeCategoryScaling(a) && isUnitTimeCategoryScaling(b)) { return a.firstBandTime === b.firstBandTime && a.lastBandTime === b.lastBandTime && a.bandCount === b.bandCount && a.intervalMs === b.intervalMs && a.inset === b.inset && a.step === b.step; } if (isStandardCategoryScaling(a) && isStandardCategoryScaling(b)) { return a.inset === b.inset && a.step === b.step && arraysEqual(a.domain, b.domain); } return false; } return false; } function isScaleValid(scale2) { if (scale2 == null) return false; if (scale2.type === "category") { if (isUnitTimeCategoryScaling(scale2)) { return Number.isFinite(scale2.firstBandTime) && Number.isFinite(scale2.lastBandTime) && Number.isFinite(scale2.bandCount) && scale2.bandCount > 0; } return scale2.domain.every((v) => v != null); } return scale2.domain.every((v) => Number.isFinite(v) || v instanceof Date) && scale2.range.every((v) => Number.isFinite(v)); } // packages/ag-charts-core/src/utils/geometry/lineInterpolation.ts function spanRange(span) { switch (span.type) { case "linear": case "step": case "multi-line": return [ { x: span.x0, y: span.y0 }, { x: span.x1, y: span.y1 } ]; case "cubic": return [ { x: span.cp0x, y: span.cp0y }, { x: span.cp3x, y: span.cp3y } ]; } } function spanRangeNormalized(span) { const range3 = spanRange(span); if (range3[0].x > range3[1].x) { range3.reverse(); } return range3; } function collapseSpanToPoint(span, point) { const { x, y } = point; switch (span.type) { case "linear": return { type: "linear", moveTo: span.moveTo, x0: x, y0: y, x1: x, y1: y }; case "step": return { type: "step", moveTo: span.moveTo, x0: x, y0: y, x1: x, y1: y, stepX: x }; case "cubic": return { type: "cubic", moveTo: span.moveTo, cp0x: x, cp0y: y, cp1x: x, cp1y: y, cp2x: x, cp2y: y, cp3x: x, cp3y: y }; case "multi-line": return { type: "multi-line", moveTo: span.moveTo, x0: x, y0: y, x1: x, y1: y, midPoints: span.midPoints.map(() => ({ x, y })) }; } } function rescaleSpan(span, nextStart, nextEnd) { const [prevStart, prevEnd] = spanRange(span); const widthScale = prevEnd.x === prevStart.x ? 0 : (nextEnd.x - nextStart.x) / (prevEnd.x - prevStart.x); const heightScale = prevEnd.y === prevStart.y ? 0 : (nextEnd.y - nextStart.y) / (prevEnd.y - prevStart.y); switch (span.type) { case "linear": return { type: "linear", moveTo: span.moveTo, x0: nextStart.x, y0: nextStart.y, x1: nextEnd.x, y1: nextEnd.y }; case "cubic": return { type: "cubic", moveTo: span.moveTo, cp0x: nextStart.x, cp0y: nextStart.y, cp1x: nextEnd.x - (span.cp2x - prevStart.x) * widthScale, cp1y: nextEnd.y - (span.cp2y - prevStart.y) * heightScale, cp2x: nextEnd.x - (span.cp1x - prevStart.x) * widthScale, cp2y: nextEnd.y - (span.cp1y - prevStart.y) * heightScale, cp3x: nextEnd.x, cp3y: nextEnd.y }; case "step": return { type: "step", moveTo: span.moveTo, x0: nextStart.x, y0: nextStart.y, x1: nextEnd.x, y1: nextEnd.y, stepX: nextEnd.x - (span.stepX - prevStart.x) * widthScale }; case "multi-line": return { type: "multi-line", moveTo: span.moveTo, x0: nextStart.x, y0: nextStart.y, x1: nextEnd.x, y1: nextEnd.y, midPoints: span.midPoints.map((midPoint) => ({ x: nextStart.x + (midPoint.x - prevStart.x) * widthScale, y: nextStart.y + (midPoint.y - prevStart.y) * heightScale })) }; } } function clipSpanX(span, x0, x1) { const { moveTo } = span; const [start2, end3] = spanRangeNormalized(span); const { x: spanX0, y: spanY0 } = start2; const { x: spanX1, y: spanY1 } = end3; if (x1 < spanX0) { return rescaleSpan(span, start2, start2); } else if (x0 > spanX1) { return rescaleSpan(span, end3, end3); } switch (span.type) { case "linear": { const m = spanY0 === spanY1 ? void 0 : (spanY1 - spanY0) / (spanX1 - spanX0); const y0 = m == null ? spanY0 : m * (x0 - spanX0) + spanY0; const y1 = m == null ? spanY0 : m * (x1 - spanX0) + spanY0; return { type: "linear", moveTo, x0, y0, x1, y1 }; } case "step": if (x1 <= span.stepX) { const y = span.y0; return { type: "step", moveTo, x0, y0: y, x1, y1: y, stepX: x1 }; } else if (x0 >= span.stepX) { const y = span.y1; return { type: "step", moveTo, x0, y0: y, x1, y1: y, stepX: x0 }; } else { const { y0, y1, stepX } = span; return { type: "step", moveTo, x0, y0, x1, y1, stepX }; } case "cubic": { const t0 = solveBezier(span.cp0x, span.cp1x, span.cp2x, span.cp3x, x0); let [_unused, bezier] = splitBezier2D( span.cp0x, span.cp0y, span.cp1x, span.cp1y, span.cp2x, span.cp2y, span.cp3x, span.cp3y, t0 ); const t1 = solveBezier(bezier[0].x, bezier[1].x, bezier[2].x, bezier[3].x, x1); [bezier, _unused] = splitBezier2D( bezier[0].x, bezier[0].y, bezier[1].x, bezier[1].y, bezier[2].x, bezier[2].y, bezier[3].x, bezier[3].y, t1 ); return { type: "cubic", moveTo, cp0x: bezier[0].x, cp0y: bezier[0].y, cp1x: bezier[1].x, cp1y: bezier[1].y, cp2x: bezier[2].x, cp2y: bezier[2].y, cp3x: bezier[3].x, cp3y: bezier[3].y }; } case "multi-line": { const { midPoints } = span; const midPointStartIndex = midPoints.findLastIndex((midPoint) => midPoint.x <= x0); let midPointEndIndex = midPoints.findIndex((midPoint) => midPoint.x >= x1); if (midPointEndIndex === -1) midPointEndIndex = midPoints.length; const startPoint = midPointStartIndex >= 0 ? midPoints[midPointStartIndex] : void 0; const startX = startPoint?.x ?? spanX0; const startY = startPoint?.y ?? spanY0; const endPoint = midPointEndIndex < midPoints.length ? midPoints[midPointEndIndex] : void 0; const endX = endPoint?.x ?? spanX1; const endY = endPoint?.y ?? spanY1; const m = startY === endY ? void 0 : (endY - startY) / (endX - startX); const y0 = m == null ? startY : m * (startX - spanX0) + startY; const y1 = m == null ? startY : m * (endX - spanX0) + startY; return { type: "multi-line", moveTo, x0, y0, x1, y1, midPoints: midPoints.slice(Math.max(midPointStartIndex, 0), midPointEndIndex) }; } } } var SpanJoin = /* @__PURE__ */ ((SpanJoin2) => { SpanJoin2[SpanJoin2["MoveTo"] = 0] = "MoveTo"; SpanJoin2[SpanJoin2["LineTo"] = 1] = "LineTo"; SpanJoin2[SpanJoin2["Skip"] = 2] = "Skip"; return SpanJoin2; })(SpanJoin || {}); function linearPoints(points) { const spans = []; let i = 0; let x0 = Number.NaN; let y0 = Number.NaN; for (const { x: x1, y: y1 } of points) { if (i > 0) { const moveTo = i === 1; spans.push({ type: "linear", moveTo, x0, y0, x1, y1 }); } i += 1; x0 = x1; y0 = y1; } return spans; } var lineSteps = { start: 0, middle: 0.5, end: 1 }; function stepPoints(points, position) { const spans = []; let i = 0; let x0 = Number.NaN; let y0 = Number.NaN; const p0 = typeof position === "number" ? position : lineSteps[position]; for (const { x: x1, y: y1 } of points) { if (i > 0) { const moveTo = i === 1; const stepX = x0 + (x1 - x0) * p0; spans.push({ type: "step", moveTo, x0, y0, x1, y1, stepX }); } i += 1; x0 = x1; y0 = y1; } return spans; } function smoothPoints(iPoints, tension) { const points = Array.isArray(iPoints) ? iPoints : Array.from(iPoints); if (points.length <= 1) return []; const flatnessRatio = 0.05; const gradients = points.map((c, i) => { const p = i === 0 ? c : points[i - 1]; const n = i === points.length - 1 ? c : points[i + 1]; const isTerminalPoint = i === 0 || i === points.length - 1; if (Math.sign(p.y - c.y) === Math.sign(n.y - c.y)) { return 0; } if (!isTerminalPoint) { const range3 = Math.abs(p.y - n.y); const prevRatio = Math.abs(c.y - p.y) / range3; const nextRatio = Math.abs(c.y - n.y) / range3; if (prevRatio <= flatnessRatio || 1 - prevRatio <= flatnessRatio || nextRatio <= flatnessRatio || 1 - nextRatio <= flatnessRatio) { return 0; } } return (n.y - p.y) / (n.x - p.x); }); if (gradients[1] === 0) { gradients[0] *= 2; } if (gradients.at(-2) === 0) { gradients[gradients.length - 1] *= 2; } const spans = []; for (let i = 1; i < points.length; i += 1) { const prev = points[i - 1]; const prevM = gradients[i - 1]; const cur = points[i]; const curM = gradients[i]; const dx = cur.x - prev.x; const dy = cur.y - prev.y; let dcp1x = dx * tension / 3; let dcp1y = dx * prevM * tension / 3; let dcp2x = dx * tension / 3; let dcp2y = dx * curM * tension / 3; if (curM === 0 && Math.abs(dcp1y) > Math.abs(dy)) { dcp1x *= Math.abs(dy / dcp1y); dcp1y = Math.sign(dcp1y) * Math.abs(dy); } if (prevM === 0 && Math.abs(dcp2y) > Math.abs(dy)) { dcp2x *= Math.abs(dy / dcp2y); dcp2y = Math.sign(dcp2y) * Math.abs(dy); } spans.push({ type: "cubic", moveTo: i === 1, cp0x: prev.x, cp0y: prev.y, cp1x: prev.x + dcp1x, cp1y: prev.y + dcp1y, cp2x: cur.x - dcp2x, cp2y: cur.y - dcp2y, cp3x: cur.x, cp3y: cur.y }); } return spans; } // packages/ag-charts-core/src/utils/zoomUtils.ts var UNIT_MIN = 0; var UNIT_MAX = 1; function definedZoomState(zoom) { return { x: { min: zoom?.x?.min ?? UNIT_MIN, max: zoom?.x?.max ?? UNIT_MAX }, y: { min: zoom?.y?.min ?? UNIT_MIN, max: zoom?.y?.max ?? UNIT_MAX } }; } // packages/ag-charts-core/src/rendering/changeDetectableProperties.ts var ChangeDetectableProperties = class extends BaseProperties { constructor() { super(...arguments); this._dirty = true; } markDirty() { this._dirty = true; } markClean(_opts) { this._dirty = false; } isDirty() { return this._dirty; } onChangeDetection(_property) { this.markDirty(); } }; // packages/ag-charts-core/src/utils/format/timeFormat.ts var CONSTANTS = { periods: ["AM", "PM"], days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], shortDays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], months: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ], shortMonths: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] }; function dayOfYear(date2, startOfYear = new Date(date2.getFullYear(), 0, 1)) { const startOffset = date2.getTimezoneOffset() - startOfYear.getTimezoneOffset(); const timeDiff = date2.getTime() - startOfYear.getTime() + startOffset * 6e4; const timeOneDay = 36e5 * 24; return Math.floor(timeDiff / timeOneDay); } function weekOfYear(date2, startDay) { const startOfYear = new Date(date2.getFullYear(), 0, 1); const startOfYearDay = startOfYear.getDay(); const firstWeekStartOffset = (startDay - startOfYearDay + 7) % 7; const startOffset = new Date(date2.getFullYear(), 0, firstWeekStartOffset + 1); if (startOffset <= date2) { return Math.floor(dayOfYear(date2, startOffset) / 7) + 1; } return 0; } var SUNDAY = 0; var MONDAY = 1; var THURSDAY = 4; function isoWeekOfYear(date2, year = date2.getFullYear()) { const firstOfYear = new Date(year, 0, 1); const firstOfYearDay = firstOfYear.getDay(); const firstThursdayOffset = (THURSDAY - firstOfYearDay + 7) % 7; const startOffset = new Date(year, 0, firstThursdayOffset - (THURSDAY - MONDAY) + 1); if (startOffset <= date2) { return Math.floor(dayOfYear(date2, startOffset) / 7) + 1; } return isoWeekOfYear(date2, year - 1); } function timezone(date2) { const offset = date2.getTimezoneOffset(); const unsignedOffset = Math.abs(offset); const sign = offset > 0 ? "-" : "+"; return `${sign}${pad(Math.floor(unsignedOffset / 60), 2, "0")}${pad(Math.floor(unsignedOffset % 60), 2, "0")}`; } var FORMATTERS = { a: (d) => CONSTANTS.shortDays[d.getDay()], A: (d) => CONSTANTS.days[d.getDay()], b: (d) => CONSTANTS.shortMonths[d.getMonth()], B: (d) => CONSTANTS.months[d.getMonth()], c: "%x, %X", d: (d, p) => pad(d.getDate(), 2, p ?? "0"), e: "%_d", f: (d, p) => pad(d.getMilliseconds() * 1e3, 6, p ?? "0"), H: (d, p) => pad(d.getHours(), 2, p ?? "0"), I: (d, p) => { const hours = d.getHours() % 12; return hours === 0 ? "12" : pad(hours, 2, p ?? "0"); }, j: (d, p) => pad(dayOfYear(d) + 1, 3, p ?? "0"), m: (d, p) => pad(d.getMonth() + 1, 2, p ?? "0"), M: (d, p) => pad(d.getMinutes(), 2, p ?? "0"), L: (d, p) => pad(d.getMilliseconds(), 3, p ?? "0"), p: (d) => d.getHours() < 12 ? "AM" : "PM", Q: (d) => String(d.getTime()), s: (d) => String(Math.floor(d.getTime() / 1e3)), S: (d, p) => pad(d.getSeconds(), 2, p ?? "0"), u: (d) => { let day = d.getDay(); if (day < 1) day += 7; return String(day % 7); }, U: (d, p) => pad(weekOfYear(d, SUNDAY), 2, p ?? "0"), V: (d, p) => pad(isoWeekOfYear(d), 2, p ?? "0"), w: (d, p) => pad(d.getDay(), 2, p ?? "0"), W: (d, p) => pad(weekOfYear(d, MONDAY), 2, p ?? "0"), x: "%-m/%-d/%Y", X: "%-I:%M:%S %p", y: (d, p) => pad(d.getFullYear() % 100, 2, p ?? "0"), Y: (d, p) => pad(d.getFullYear(), 4, p ?? "0"), Z: (d) => timezone(d), "%": () => "%" }; var PADS = { _: " ", "0": "0", "-": "" }; function pad(value, size, padChar) { const output = String(Math.floor(value)); if (output.length >= size) { return output; } return `${padChar.repeat(size - output.length)}${output}`; } function buildDateFormatter(formatString) { const formatParts = []; while (formatString.length > 0) { let nextEscapeIdx = formatString.indexOf("%"); if (nextEscapeIdx !== 0) { const literalPart = nextEscapeIdx > 0 ? formatString.substring(0, nextEscapeIdx) : formatString; formatParts.push(literalPart); } if (nextEscapeIdx < 0) break; const maybePadSpecifier = formatString[nextEscapeIdx + 1]; const maybePad = PADS[maybePadSpecifier]; if (maybePad != null) { nextEscapeIdx++; } const maybeFormatterSpecifier = formatString[nextEscapeIdx + 1]; const maybeFormatter = FORMATTERS[maybeFormatterSpecifier]; if (typeof maybeFormatter === "function") { formatParts.push([maybeFormatter, maybePad]); } else if (typeof maybeFormatter === "string") { const formatter2 = buildDateFormatter(maybeFormatter); formatParts.push([formatter2, maybePad]); } else { formatParts.push(`${maybePad ?? ""}${maybeFormatterSpecifier}`); } formatString = formatString.substring(nextEscapeIdx + 2); } return (dateTime) => { const dateTimeAsDate = typeof dateTime === "number" ? new Date(dateTime) : dateTime; return formatParts.map((c) => typeof c === "string" ? c : c[0](dateTimeAsDate, c[1])).join(""); }; } // packages/ag-charts-core/src/rendering/domElements.ts function createButton(options, attrs) { const button = createElement("button", getClassName("ag-charts-input ag-charts-button", attrs)); if (options.label === void 0) { button.append(createIcon(options.icon)); button.ariaLabel = options.altText; } else { button.append(options.label); } button.addEventListener("click", options.onPress); setAttributes(button, attrs); return button; } function createCheckbox(options, attrs) { const checkbox = createElement("input", getClassName("ag-charts-input ag-charts-checkbox", attrs)); checkbox.type = "checkbox"; checkbox.checked = options.checked; checkbox.addEventListener("change", (event) => options.onChange(checkbox.checked, event)); checkbox.addEventListener("keydown", (event) => { if (isButtonClickEvent(event)) { event.preventDefault(); checkbox.click(); } }); setAttributes(checkbox, attrs); return checkbox; } function createSelect(options, attrs) { const select = createElement("select", getClassName("ag-charts-input ag-charts-select", attrs)); select.append( ...options.options.map((option) => { const optionEl = createElement("option"); optionEl.value = option.value; optionEl.textContent = option.label; return optionEl; }) ); setAttribute(select, "data-preventdefault", false); select.value = options.value; select.addEventListener("change", (event) => options.onChange(select.value, event)); setAttributes(select, attrs); return select; } function createTextArea(options, attrs) { const textArea = createElement("textarea", getClassName("ag-charts-input ag-charts-textarea", attrs)); textArea.value = options.value; textArea.addEventListener("input", (event) => options.onChange(textArea.value, event)); setAttributes(textArea, attrs); setAttribute(textArea, "data-preventdefault", false); return textArea; } function createIcon(icon) { const el = createElement("span", `ag-charts-icon ag-charts-icon-${icon}`); setAttribute(el, "aria-hidden", true); return el; } function getClassName(baseClass, attrs) { if (attrs == null) return baseClass; return `${baseClass} ${attrs.class}`; } // packages/ag-charts-core/src/rendering/easing.ts var linear = (n) => n; var easeIn = (n) => 1 - Math.cos(n * Math.PI / 2); var easeOut = (n) => Math.sin(n * Math.PI / 2); var easeInOut = (n) => -(Math.cos(n * Math.PI) - 1) / 2; var easeInQuad = (n) => n * n; var easeOutQuad = (n) => 1 - (1 - n) ** 2; var easeInOutQuad = (n) => n < 0.5 ? 2 * n * n : 1 - (-2 * n + 2) ** 2 / 2; var inverseEaseOut = (x) => 2 * Math.asin(x) / Math.PI; // packages/ag-charts-core/src/rendering/changeDetectable.ts var TRIPLE_EQ = (lhs, rhs) => lhs === rhs; function SceneChangeDetection(opts) { return function(target, key) { const privateKey = `__${key}`; if (target[key]) return; prepareGetSet(target, key, privateKey, opts); }; } function SceneRefChangeDetection(opts) { return SceneChangeDetection(opts); } function SceneObjectChangeDetection(opts) { return SceneChangeDetection(opts); } function SceneArrayChangeDetection(opts) { const baseOpts = opts ?? {}; baseOpts.equals = arraysEqual; return SceneChangeDetection(opts); } function DeclaredSceneChangeDetection(opts) { return function(target, key) { const privateKey = `__${key}`; if (target[key]) return; prepareGetSet(target, key, privateKey, opts); }; } function DeclaredSceneObjectChangeDetection(opts) { return function(target, key) { const privateKey = `__${key}`; if (target[key]) return; prepareGetSet(target, key, privateKey, opts); }; } function prepareGetSet(target, key, privateKey, opts) { const { changeCb, convertor, checkDirtyOnAssignment = false } = opts ?? {}; const requiredOpts = { changeCb, checkDirtyOnAssignment, convertor }; const setter = buildCheckDirtyChain( privateKey, buildChangeCallbackChain( buildConvertorChain(buildSetter(privateKey, requiredOpts), requiredOpts), requiredOpts ), requiredOpts ); function propertyGetter() { return this[privateKey]; } Object.defineProperty(target, key, { set: setter, get: propertyGetter, enumerable: true, configurable: true }); } function buildConvertorChain(setterFn, opts) { const { convertor } = opts; if (convertor) { let convertValueAndSet2 = function(value) { setterFn.call(this, convertValue(value)); }; var convertValueAndSet = convertValueAndSet2; const convertValue = convertor; return convertValueAndSet2; } return setterFn; } var NO_CHANGE = Symbol("no-change"); function buildChangeCallbackChain(setterFn, opts) { const { changeCb } = opts; if (changeCb) { let invokeChangeCallback2 = function(value) { const change = setterFn.call(this, value); if (change !== NO_CHANGE) { changeCallback.call(this, this); } return change; }; var invokeChangeCallback = invokeChangeCallback2; const changeCallback = changeCb; return invokeChangeCallback2; } return setterFn; } function buildCheckDirtyChain(privateKey, setterFn, opts) { const { checkDirtyOnAssignment } = opts; if (checkDirtyOnAssignment) { let checkDirtyOnAssignmentFn2 = function(value) { const change = setterFn.call(this, value); if (value?._dirty === true) { this.markDirty(privateKey); } return change; }; var checkDirtyOnAssignmentFn = checkDirtyOnAssignmentFn2; return checkDirtyOnAssignmentFn2; } return setterFn; } function buildSetter(privateKey, opts) { const { equals = TRIPLE_EQ } = opts; function setWithChangeDetection(value) { const oldValue = this[privateKey]; if (!equals(value, oldValue)) { this[privateKey] = value; this.onChangeDetection(privateKey); return value; } return NO_CHANGE; } return setWithChangeDetection; } // packages/ag-charts-community/src/chart/series/cartesian/areaSeriesOptionsDef.ts var highlight = multiSeriesHighlightOptionsDef(shapeHighlightOptionsDef, shapeHighlightOptionsDef); var areaStyler = callbackDefs({ ...strokeOptionsDef, ...fillOptionsDef, ...lineDashOptionsDef, marker: markerStyleOptionsDefs }); var areaSeriesThemeableOptionsDef = { showInMiniChart: boolean, connectMissingData: boolean, interpolation: interpolationOptionsDefs, label: seriesLabelOptionsDefs, styler: areaStyler, marker: markerOptionsDefs, tooltip: tooltipOptionsDefsWithArea, shadow: shadowOptionsDefs, ...commonSeriesThemeableOptionsDefs, ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef, highlight, segmentation: shapeSegmentation }; var areaSeriesOptionsDef = { ...areaSeriesThemeableOptionsDef, ...commonSeriesOptionsDefs, highlight, type: required(constant("area")), xKey: required(string), yKey: required(string), xKeyAxis: string, yKeyAxis: string, xName: string, yName: string, legendItemName: string, stacked: boolean, stackGroup: string, normalizedTo: number }; areaSeriesOptionsDef.yFilterKey = undocumented(string); // packages/ag-charts-community/src/chart/series/cartesian/barSeriesOptionsDef.ts var highlight2 = multiSeriesHighlightOptionsDef(barHighlightOptionsDef, barHighlightOptionsDef); var barStyler = callbackDefs({ ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef, cornerRadius: positiveNumber }); var barSeriesThemeableOptionsDef = { direction: union("horizontal", "vertical"), showInMiniChart: boolean, cornerRadius: positiveNumber, styler: barStyler, itemStyler: barStyler, crisp: boolean, label: { ...seriesLabelOptionsDefs, placement: union("inside-center", "inside-start", "inside-end", "outside-start", "outside-end"), spacing: positiveNumber }, errorBar: errorBarThemeableOptionsDefs, shadow: shadowOptionsDefs, tooltip: tooltipOptionsDefs, ...commonSeriesThemeableOptionsDefs, highlight: highlight2, ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef, segmentation: shapeSegmentation, width: positiveNumberNonZero, widthRatio: ratio }; barSeriesThemeableOptionsDef.sparklineMode = undocumented(boolean); var barSeriesOptionsDef = { ...barSeriesThemeableOptionsDef, ...commonSeriesOptionsDefs, highlight: highlight2, type: required(constant("bar")), xKey: required(string), yKey: required(string), xKeyAxis: string, yKeyAxis: string, xName: string, yName: string, direction: union("horizontal", "vertical"), grouped: boolean, stacked: boolean, stackGroup: string, normalizedTo: number, legendItemName: string, errorBar: errorBarOptionsDefs }; barSeriesOptionsDef.yFilterKey = undocumented(string); barSeriesOptionsDef.pickOutsideVisibleMinorAxis = undocumented(boolean); barSeriesOptionsDef.focusPriority = undocumented(number); barSeriesOptionsDef.simpleItemStyler = undocumented(callback); // packages/ag-charts-community/src/chart/series/cartesian/lineSeriesOptionsDef.ts var highlight3 = multiSeriesHighlightOptionsDef(shapeHighlightOptionsDef, lineHighlightOptionsDef); var lineStyler = callbackDefs({ ...strokeOptionsDef, ...lineDashOptionsDef, marker: markerStyleOptionsDefs }); var lineSeriesThemeableOptionsDef = { title: string, showInMiniChart: boolean, connectMissingData: boolean, interpolation: interpolationOptionsDefs, label: seriesLabelOptionsDefs, styler: lineStyler, marker: markerOptionsDefs, tooltip: tooltipOptionsDefs, errorBar: errorBarThemeableOptionsDefs, ...commonSeriesThemeableOptionsDefs, ...strokeOptionsDef, ...lineDashOptionsDef, highlight: highlight3, segmentation: lineSegmentation }; lineSeriesThemeableOptionsDef.sparklineMode = undocumented(boolean); var lineSeriesOptionsDef = { ...lineSeriesThemeableOptionsDef, ...commonSeriesOptionsDefs, highlight: highlight3, type: required(constant("line")), xKey: required(string), yKey: required(string), xKeyAxis: string, yKeyAxis: string, xName: string, yName: string, stacked: boolean, stackGroup: string, normalizedTo: number, legendItemName: string, errorBar: errorBarOptionsDefs }; lineSeriesOptionsDef.yFilterKey = undocumented(string); lineSeriesOptionsDef.pickOutsideVisibleMinorAxis = undocumented(boolean); lineSeriesOptionsDef.focusPriority = undocumented(number); // packages/ag-charts-community/src/version.ts var VERSION = "13.1.0"; // packages/ag-charts-community/src/api/preset/sparkline.ts var commonAxisProperties = { title: { enabled: false }, label: { enabled: false }, line: { enabled: false }, gridLine: { enabled: false }, crosshair: { enabled: false, stroke: DEFAULT_SPARKLINE_CROSSHAIR_STROKE, lineDash: [0], label: { enabled: false } } }; var numericAxisProperties = { ...commonAxisProperties, nice: false }; var chartTooltipDefaults = { mode: "compact", position: { anchorTo: "node", placement: ["right", "left"] }, showArrow: false }; var barGridLineDefaults = { style: [{ stroke: { $ref: "gridLineColor" } }], width: 2 }; var barAxisDefaults = { number: { gridLine: barGridLineDefaults }, time: { gridLine: barGridLineDefaults }, category: { gridLine: barGridLineDefaults } }; var SPARKLINE_THEME = { overrides: { common: { animation: { enabled: false }, contextMenu: { enabled: false }, keyboard: { enabled: false }, background: { visible: false }, navigator: { enabled: false }, padding: { top: 0, right: 0, bottom: 0, left: 0 }, axes: { number: { ...numericAxisProperties, interval: { values: [0] } }, log: { ...numericAxisProperties }, time: { ...numericAxisProperties }, category: { ...commonAxisProperties } } }, bar: { series: { crisp: false, label: { placement: "inside-end", padding: 4 }, // @ts-expect-error undocumented option sparklineMode: true }, tooltip: { ...chartTooltipDefaults, position: { ...chartTooltipDefaults.position, anchorTo: "pointer" }, range: "nearest" }, axes: barAxisDefaults }, line: { seriesArea: { padding: { top: 2, right: 2, bottom: 2, left: 2 } }, series: { // @ts-expect-error undocumented option sparklineMode: true, strokeWidth: 1, marker: { enabled: false, size: 3 } }, tooltip: chartTooltipDefaults }, area: { seriesArea: { padding: { top: 1, right: 0, bottom: 1, left: 0 } }, series: { strokeWidth: 1, fillOpacity: 0.4 }, tooltip: chartTooltipDefaults } } }; var setInitialBaseTheme = simpleMemorize(createInitialBaseTheme); function createInitialBaseTheme(baseTheme, initialBaseTheme) { if (typeof baseTheme === "string") { return { ...initialBaseTheme, baseTheme }; } if (baseTheme != null) { return { ...baseTheme, // @ts-expect-error internal implementation baseTheme: setInitialBaseTheme(baseTheme.baseTheme, initialBaseTheme) }; } return initialBaseTheme; } function sparklineDataPreset(data) { if (Array.isArray(data) && data.length !== 0) { const firstItem = data.find((v) => v != null); if (typeof firstItem === "number") { const mappedData = data.map((y, x) => ({ x, y })); return { data: mappedData, series: [{ xKey: "x", yKey: "y" }], datumKey: "y" }; } else if (Array.isArray(firstItem)) { const mappedData = data.map((datum) => ({ x: datum?.[0], y: datum?.[1], datum })); return { data: mappedData, series: [{ xKey: "x", yKey: "y" }], datumKey: "datum" }; } } else if (data?.length === 0) { return { data, series: [{ xKey: "x", yKey: "y" }], datumKey: "y" }; } return { data }; } function axisPreset(opts) { switch (opts?.type) { case "number": { const { reverse, min, max } = opts ?? {}; return { type: "number", reverse, min, max }; } case "time": { const { reverse, min, max } = opts ?? {}; return { type: "time", reverse, min, max }; } case "category": default: { if (opts == null) { return { type: "category" }; } const { reverse, paddingInner, paddingOuter } = opts; return { type: "category", reverse, paddingInner, paddingOuter }; } } } function gridLinePreset(opts, defaultEnabled, sparkOpts) { const gridLineOpts = {}; if (opts?.stroke != null) { gridLineOpts.style = [{ stroke: opts?.stroke }]; gridLineOpts.enabled ?? (gridLineOpts.enabled = true); } if (opts?.strokeWidth != null) { gridLineOpts.width = opts?.strokeWidth; gridLineOpts.enabled ?? (gridLineOpts.enabled = true); } if (sparkOpts.type === "bar" && sparkOpts.direction !== "horizontal") { gridLineOpts.enabled ?? (gridLineOpts.enabled = true); } if (opts?.visible != null) { gridLineOpts.enabled = opts.visible; } gridLineOpts.enabled ?? (gridLineOpts.enabled = defaultEnabled); return gridLineOpts; } var tooltipRendererFn = simpleMemorize((context, tooltip, datumKey) => { return (params) => { const xValue = params.datum[params.xKey]; const yValue = params.datum[params.yKey]; const datum = datumKey == null ? params.datum : params.datum[datumKey]; const userContent = tooltip?.renderer?.({ context, datum, xValue, yValue }); if (isString(userContent) || isNumber(userContent) || isDate(userContent)) { return toTextString(userContent); } const content = userContent?.content ?? yValue.toFixed(2); return userContent?.title ? { heading: void 0, title: void 0, data: [{ label: userContent.title, value: content }] } : { heading: void 0, title: content, data: [] }; }; }); function sparkline(opts) { const { background, container, foreground, height: height2, listeners, locale, minHeight, minWidth, overrideDevicePixelRatio, padding: padding2, width: width2, theme: baseTheme, data: baseData, crosshair, axis, min, max, tooltip, context, styleNonce, ...seriesOptions } = opts; const chartOpts = { background, container, foreground, height: height2, listeners, locale, minHeight, minWidth, overrideDevicePixelRatio, padding: padding2, width: width2, styleNonce }; const { data, series: [seriesOverrides] = [], datumKey } = sparklineDataPreset(baseData); const seriesConfig = seriesOptions; if (seriesOverrides != null) { Object.assign(seriesConfig, seriesOverrides); } seriesConfig.tooltip = { ...tooltip, renderer: tooltipRendererFn(context, tooltip, datumKey) }; chartOpts.theme = setInitialBaseTheme(baseTheme, SPARKLINE_THEME); chartOpts.data = data; chartOpts.series = [seriesConfig]; const swapAxes = seriesConfig.type === "bar" && seriesConfig.direction === "horizontal"; const [crossAxisPosition, numberAxisPosition] = swapAxes ? ["left", "bottom"] : ["bottom", "left"]; const crossAxis = { ...axisPreset(axis), position: crossAxisPosition, ...crosshair == null ? {} : { crosshair } }; const numberAxis = { type: "number", gridLine: gridLinePreset(axis, false, opts), position: numberAxisPosition, ...min == null ? {} : { min }, ...max == null ? {} : { max } }; chartOpts.axes = swapAxes ? { x: numberAxis, y: crossAxis } : { x: crossAxis, y: numberAxis }; return chartOpts; } // packages/ag-charts-community/src/api/preset/presetModules.ts var commonSparklineOmit = [ "showInLegend", "showInMiniChart", "grouped", "stacked", "stackGroup", "tooltip", "listeners", "errorBar", "xKey", "yKey", "type" ]; var commonSparklineAxisOptionsDef = { visible: boolean, reverse: boolean, stroke: color, strokeWidth: positiveNumber }; var commonSparklineOptionsDef = { context: () => true, tooltip: defined, theme: defined, background: defined, container: defined, width: defined, height: defined, minWidth: defined, minHeight: defined, padding: defined, listeners: defined, locale: defined, data: defined, styleNonce: string, axis: typeUnion( { number: { ...commonSparklineAxisOptionsDef, min: and(number, lessThan("max")), max: and(number, greaterThan("min")) }, category: { ...commonSparklineAxisOptionsDef, paddingInner: ratio, paddingOuter: ratio }, time: { ...commonSparklineAxisOptionsDef, min: and(or(number, date), lessThan("max")), max: and(or(number, date), greaterThan("min")) } }, "axis options", "category" ), min: and(number, lessThan("max")), max: and(number, greaterThan("min")), crosshair: { enabled: boolean, snap: boolean, ...strokeOptionsDef, ...lineDashOptionsDef }, xKey: string, yKey: string }; commonSparklineOptionsDef.overrideDevicePixelRatio = undocumented(number); commonSparklineOptionsDef.foreground = undocumented(defined); var SparklinePresetModule = { type: "preset", name: "sparkline", version: VERSION, options: typeUnion( { area: { ...commonSparklineOptionsDef, ...without(areaSeriesOptionsDef, commonSparklineOmit) }, bar: { ...commonSparklineOptionsDef, ...without(barSeriesOptionsDef, commonSparklineOmit) }, line: { ...commonSparklineOptionsDef, ...without(lineSeriesOptionsDef, commonSparklineOmit) } }, "sparkline options" ), create: sparkline, processData: sparklineDataPreset }; // packages/ag-charts-community/src/util/interpolating.ts var interpolate = Symbol("interpolate"); var isInterpolating = (x) => x[interpolate] != null; // packages/ag-charts-community/src/scene/bbox.ts var _BBox = class _BBox { constructor(x, y, width2, height2) { this.x = x; this.y = y; this.width = width2; this.height = height2; } static fromObject({ x, y, width: width2, height: height2 }) { return new _BBox(x, y, width2, height2); } static merge(boxes) { let left = Infinity; let top = Infinity; let right = -Infinity; let bottom = -Infinity; for (const box of boxes) { if (box.x < left) { left = box.x; } if (box.y < top) { top = box.y; } if (end2(box.x, box.width) > right) { right = end2(box.x, box.width); } if (end2(box.y, box.height) > bottom) { bottom = end2(box.y, box.height); } } return new _BBox(left, top, right - left, bottom - top); } static nearestBox(x, y, boxes) { return nearestSquared(x, y, boxes); } toDOMRect() { return { x: this.x, y: this.y, width: this.width, height: this.height, top: this.y, left: this.x, right: end2(this.x, this.width), bottom: end2(this.y, this.height), toJSON() { return {}; } }; } clone() { const { x, y, width: width2, height: height2 } = this; return new _BBox(x, y, width2, height2); } equals(other) { return boxesEqual(this, other); } containsPoint(x, y) { return boxContains(this, x, y); } intersection(other) { const x0 = Math.max(this.x, other.x); const y0 = Math.max(this.y, other.y); const x1 = Math.min(end2(this.x, this.width), end2(other.x, other.width)); const y1 = Math.min(end2(this.y, this.height), end2(other.y, other.height)); if (x0 > x1 || y0 > y1) return; return new _BBox(x0, y0, x1 - x0, y1 - y0); } collidesBBox(other) { return this.x < end2(other.x, other.width) && end2(this.x, this.width) > other.x && this.y < end2(other.y, other.height) && end2(this.y, this.height) > other.y; } computeCenter() { return { x: this.x + this.width / 2, y: this.y + this.height / 2 }; } isFinite() { return Number.isFinite(this.x) && Number.isFinite(this.y) && Number.isFinite(this.width) && Number.isFinite(this.height); } distanceSquared(x, y) { if (this.containsPoint(x, y)) { return 0; } const dx = x - clamp(this.x, x, end2(this.x, this.width)); const dy = y - clamp(this.y, y, end2(this.y, this.height)); return dx * dx + dy * dy; } shrink(amount, position) { if (typeof amount === "number") { this.applyMargin(amount, position); } else { for (const key of Object.keys(amount)) { const value = amount[key]; if (typeof value === "number") { this.applyMargin(value, key); } } } if (this.width < 0) { this.width = 0; } if (this.height < 0) { this.height = 0; } return this; } grow(amount, position) { if (typeof amount === "number") { this.applyMargin(-amount, position); } else { for (const key of Object.keys(amount)) { const value = amount[key]; if (typeof value === "number") { this.applyMargin(-value, key); } } } return this; } applyMargin(value, position) { switch (position) { case "top": this.y += value; case "bottom": this.height -= value; break; case "left": this.x += value; case "right": this.width -= value; break; case "vertical": this.y += value; this.height -= value * 2; break; case "horizontal": this.x += value; this.width -= value * 2; break; case void 0: this.x += value; this.y += value; this.width -= value * 2; this.height -= value * 2; break; } } translate(x, y) { this.x += x; this.y += y; return this; } [interpolate](other, d) { return new _BBox( this.x * (1 - d) + other.x * d, this.y * (1 - d) + other.y * d, this.width * (1 - d) + other.width * d, this.height * (1 - d) + other.height * d ); } }; _BBox.zero = Object.freeze(new _BBox(0, 0, 0, 0)); _BBox.NaN = Object.freeze(new _BBox(Number.NaN, Number.NaN, Number.NaN, Number.NaN)); var BBox = _BBox; function end2(x, width2) { if (x === -Infinity && width2 === Infinity) return Infinity; return x + width2; } // packages/ag-charts-community/src/scene/canvas/canvasUtil.ts function clearContext({ context, pixelRatio, width: width2, height: height2 }) { context.save(); try { context.resetTransform(); context.clearRect(0, 0, Math.ceil(width2 * pixelRatio), Math.ceil(height2 * pixelRatio)); } finally { context.restore(); } } function debugContext(ctx) { if (debugLogger_exports.check("canvas")) { const save = ctx.save.bind(ctx); const restore = ctx.restore.bind(ctx); let depth = 0; Object.assign(ctx, { save() { save(); depth++; }, restore() { if (depth === 0) { throw new Error("AG Charts - Unable to restore() past depth 0"); } restore(); depth--; }, verifyDepthZero() { if (depth !== 0) { throw new Error(`AG Charts - Save/restore depth is non-zero: ${depth}`); } } }); } } // packages/ag-charts-community/src/scene/canvas/hdpiOffscreenCanvas.ts function canvasDimensions(width2, height2, pixelRatio) { return [Math.floor(width2 * pixelRatio), Math.floor(height2 * pixelRatio)]; } var fallbackCanvas; function getFallbackCanvas() { const OffscreenCanvasCtor = getOffscreenCanvas(); fallbackCanvas ?? (fallbackCanvas = new OffscreenCanvasCtor(1, 1)); return fallbackCanvas; } var HdpiOffscreenCanvas = class { constructor(options) { const { width: width2, height: height2, pixelRatio, willReadFrequently = false } = options; this.width = width2; this.height = height2; this.pixelRatio = pixelRatio; const [canvasWidth, canvasHeight] = canvasDimensions(width2, height2, pixelRatio); const OffscreenCanvasCtor = getOffscreenCanvas(); this.canvas = new OffscreenCanvasCtor(canvasWidth, canvasHeight); this.context = this.canvas.getContext("2d", { willReadFrequently }); this.context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0); debugContext(this.context); } drawImage(context, dx = 0, dy = 0) { return context.drawImage(this.canvas, dx, dy); } transferToImageBitmap() { if (this.canvas.width < 1 || this.canvas.height < 1) { return getFallbackCanvas().transferToImageBitmap(); } return this.canvas.transferToImageBitmap(); } resize(width2, height2, pixelRatio) { if (!(width2 > 0 && height2 > 0)) return; const { canvas, context } = this; if (width2 !== this.width || height2 !== this.height || pixelRatio !== this.pixelRatio) { const [canvasWidth, canvasHeight] = canvasDimensions(width2, height2, pixelRatio); canvas.width = canvasWidth; canvas.height = canvasHeight; } context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0); this.width = width2; this.height = height2; this.pixelRatio = pixelRatio; } clear() { clearContext(this); } destroy() { this.canvas.width = 0; this.canvas.height = 0; this.context.clearRect(0, 0, 0, 0); this.canvas = null; this.context = null; Object.freeze(this); } }; // packages/ag-charts-community/src/scene/zIndex.ts var cmp = (a, b) => Math.sign(a - b); function compareZIndex(a, b) { if (typeof a === "number" && typeof b === "number") { return cmp(a, b); } const aArray = typeof a === "number" ? [a] : a; const bArray = typeof b === "number" ? [b] : b; const length2 = Math.min(aArray.length, bArray.length); for (let i = 0; i < length2; i += 1) { const diff2 = cmp(aArray[i], bArray[i]); if (diff2 !== 0) return diff2; } return cmp(aArray.length, bArray.length); } // packages/ag-charts-community/src/scene/node.ts var PointerEvents = /* @__PURE__ */ ((PointerEvents2) => { PointerEvents2[PointerEvents2["All"] = 0] = "All"; PointerEvents2[PointerEvents2["None"] = 1] = "None"; return PointerEvents2; })(PointerEvents || {}); var MAX_ERROR_COUNT = 5; var _Node = class _Node { constructor(options) { /** Unique number to allow creation order to be easily determined. */ this.serialNumber = _Node._nextSerialNumber++; this.childNodeCounts = { groups: 0, nonGroups: 0, thisComplexity: 0, complexity: 0 }; /** Unique node ID in the form `ClassName-NaturalNumber`. */ this.id = createId(this); this.name = void 0; this.transitionOut = void 0; this.pointerEvents = 0 /* All */; this._datum = void 0; this._previousDatum = void 0; this.scene = void 0; this._debugDirtyProperties = void 0; this.parentNode = void 0; this.cachedBBox = void 0; /** * To simplify the type system (especially in Selections) we don't have the `Parent` node * (one that has children). Instead, we mimic HTML DOM, where any node can have children. * But we still need to distinguish regular leaf nodes from container leafs somehow. */ this.isContainerNode = false; this.visible = true; this.zIndex = 0; this.batchLevel = 0; this.batchDirty = false; this.name = options?.name; this.tag = options?.tag ?? Number.NaN; this.zIndex = options?.zIndex ?? 0; this.scene = options?.scene; if (options?.debugDirty ?? _Node._debugEnabled) { this._debugDirtyProperties = /* @__PURE__ */ new Map([["__first__", []]]); } } static toSVG(node, width2, height2) { const svg = node?.toSVG(); if (svg == null || !svg.elements.length && !svg.defs?.length) return; const root = createSvgElement("svg"); root.setAttribute("width", String(width2)); root.setAttribute("height", String(height2)); root.setAttribute("viewBox", `0 0 ${width2} ${height2}`); root.setAttribute("overflow", "visible"); if (svg.defs?.length) { const defs = createSvgElement("defs"); defs.append(...svg.defs); root.append(defs); } root.append(...svg.elements); return root.outerHTML; } static *extractBBoxes(nodes, skipInvisible) { for (const n of nodes) { if (!skipInvisible || n.visible && !n.transitionOut) { const bbox = n.getBBox(); if (bbox) yield bbox; } } } /** * Some arbitrary data bound to the node. */ get datum() { return this._datum; } set datum(datum) { if (this._datum !== datum) { this._previousDatum = this._datum; this._datum = datum; } } get previousDatum() { return this._previousDatum; } get layerManager() { return this.scene?.layersManager; } get imageLoader() { return this.scene?.imageLoader; } closestDatum() { for (const { datum } of this.traverseUp(true)) { if (datum != null) { return datum; } } } /** Perform any pre-rendering initialization. */ preRender(_renderCtx, thisComplexity = 1) { this.childNodeCounts.groups = 0; this.childNodeCounts.nonGroups = 1; this.childNodeCounts.complexity = thisComplexity; this.childNodeCounts.thisComplexity = thisComplexity; if (this.batchLevel > 0 || this.batchDirty) { throw new Error("AG Charts - illegal rendering state; batched update in progress"); } return this.childNodeCounts; } /** Guaranteed isolated render - if there is any failure, the Canvas2D context is returned to its prior state. */ isolatedRender(renderCtx) { renderCtx.ctx.save(); try { this.render(renderCtx); } catch (e) { const errorCount = e.errorCount ?? 1; if (errorCount >= MAX_ERROR_COUNT) { e.errorCount = errorCount; throw e; } logger_exports.warnOnce("Error during rendering", e, e.stack); } finally { renderCtx.ctx.restore(); } } render(renderCtx) { const { stats } = renderCtx; this.debugDirtyProperties(); if (renderCtx.debugNodeSearch) { const idOrName = this.name ?? this.id; if (renderCtx.debugNodeSearch.some((v) => typeof v === "string" ? v === idOrName : v.test(idOrName))) { renderCtx.debugNodes[this.name ?? this.id] = this; } } if (stats) { stats.nodesRendered++; stats.opsPerformed += this.childNodeCounts.thisComplexity; } } setScene(scene) { this.scene = scene; } *traverseUp(includeSelf) { if (includeSelf) { yield this; } let node = this.parentNode; while (node) { yield node; node = node.parentNode; } } /** * Checks if the node is the root (has no parent). */ isRoot() { return !this.parentNode; } removeChild(node) { throw new Error( `AG Charts - internal error, unknown child node ${node.name ?? node.id} in $${this.name ?? this.id}` ); } remove() { this.parentNode?.removeChild(this); } destroy() { if (this.parentNode) { this.remove(); } } batchedUpdate(fn) { this.batchLevel++; try { fn(); } finally { this.batchLevel--; if (this.batchLevel === 0 && this.batchDirty) { this.markDirty(); this.batchDirty = false; } } } setProperties(styles) { this.batchLevel++; try { assignIfNotStrictlyEqual(this, styles); } finally { this.batchLevel--; if (this.batchLevel === 0 && this.batchDirty) { this.markDirty(); this.batchDirty = false; } } return this; } setPropertiesWithKeys(styles, keys) { this.batchLevel++; try { assignIfNotStrictlyEqual(this, styles, keys); } finally { this.batchLevel--; if (this.batchLevel === 0 && this.batchDirty) { this.markDirty(); this.batchDirty = false; } } return this; } containsPoint(_x, _y) { return false; } pickNode(x, y) { if (this.containsPoint(x, y)) { return this; } } pickNodes(x, y, into = []) { if (this.containsPoint(x, y)) { into.push(this); } return into; } getBBox() { this.cachedBBox ?? (this.cachedBBox = Object.freeze(this.computeBBox())); return this.cachedBBox; } computeBBox() { return; } onChangeDetection(property) { this.markDirty(property); } markDirtyChildrenOrder() { this.cachedBBox = void 0; } markDirty(property) { if (this.batchLevel > 0) { this.batchDirty = true; return; } if (property != null && this._debugDirtyProperties) { this.markDebugProperties(property); } this.cachedBBox = void 0; this.parentNode?.markDirty(); } markDebugProperties(property) { const sources = this._debugDirtyProperties?.get(property) ?? []; const caller = new Error("Stack trace for property change tracking").stack?.split("\n").filter((line) => { return line !== "Error" && !line.includes(".markDebugProperties") && !line.includes(".markDirty") && !line.includes("Object.assign ") && !line.includes(`${this.constructor.name}.`); }) ?? "unknown"; sources.push(caller[0].replace(" at ", "").trim()); this._debugDirtyProperties?.set(property, sources); } debugDirtyProperties() { if (this._debugDirtyProperties == null) return; if (!this._debugDirtyProperties.has("__first__")) { for (const [property, sources] of this._debugDirtyProperties.entries()) { if (sources.length > 1) { logger_exports.logGroup( `Property changed multiple times before render: ${this.constructor.name}.${property} (${sources.length}x)`, () => { for (const source of sources) { logger_exports.log(source); } } ); } } } this._debugDirtyProperties.clear(); } static handleNodeZIndexChange(target) { target.onZIndexChange(); } onZIndexChange() { this.parentNode?.markDirtyChildrenOrder(); } toSVG() { return; } }; _Node.className = "AbstractNode"; _Node._nextSerialNumber = 0; // eslint-disable-next-line sonarjs/public-static-readonly _Node._debugEnabled = false; __decorateClass([ DeclaredSceneChangeDetection() ], _Node.prototype, "visible", 2); __decorateClass([ DeclaredSceneChangeDetection({ equals: objectsEqual, changeCb: _Node.handleNodeZIndexChange }) ], _Node.prototype, "zIndex", 2); var Node = _Node; // packages/ag-charts-community/src/scale/abstractScale.ts var AbstractScale = class { ticks(_ticks, _domain, _visibleRange) { return void 0; } niceDomain(_ticks, domain = this.domain) { return domain; } get bandwidth() { return void 0; } get step() { return void 0; } get inset() { return void 0; } }; // packages/ag-charts-community/src/scale/invalidating.ts var Invalidating = (target, propertyKey) => { const mappedProperty = Symbol(String(propertyKey)); target[mappedProperty] = void 0; Object.defineProperty(target, propertyKey, { get() { return this[mappedProperty]; }, set(newValue) { const oldValue = this[mappedProperty]; if (oldValue !== newValue) { this[mappedProperty] = newValue; this.invalid = true; } }, enumerable: true, configurable: false }); }; // packages/ag-charts-community/src/scale/scaleUtil.ts function visibleTickRange(ticks, reversed, visibleRange) { if (visibleRange == null || visibleRange[0] === 0 && visibleRange[1] === 1) return; const vt0 = clamp(0, Math.floor(visibleRange[0] * ticks.length), ticks.length); const vt1 = clamp(0, Math.ceil(visibleRange[1] * ticks.length), ticks.length); const t0 = reversed ? ticks.length - vt1 : vt0; const t1 = reversed ? ticks.length - vt0 : vt1; return [t0, t1]; } function visibleTickSliceIndices(ticks, reversed, visibleRange) { return visibleTickRange(ticks, reversed, visibleRange) ?? [0, ticks.length]; } function filterVisibleTicks(ticks, reversed, visibleRange) { const tickRange = visibleTickRange(ticks, reversed, visibleRange); if (tickRange == null) return { ticks, count: ticks.length, firstTickIndex: 0 }; const [t0, t1] = tickRange; return { ticks: ticks.slice(t0, t1), count: ticks.length, firstTickIndex: t0 }; } function unpackDomainMinMax(domain) { const min = readIntegratedWrappedValue(domain.at(0)); const max = readIntegratedWrappedValue(domain.at(-1)); return min != void 0 && max != void 0 ? [min, max] : [void 0, void 0]; } // packages/ag-charts-community/src/scale/colorScale.ts var convertColorStringToOklcha = (v) => { const color2 = Color.fromString(v); const [l, c, h] = Color.RGBtoOKLCH(color2.r, color2.g, color2.b); return { l, c, h, a: color2.a }; }; var delta = 1e-6; var isAchromatic = (x) => x.c < delta || x.l < delta || x.l > 1 - delta; var interpolateOklch = (x, y, d) => { d = clamp(0, d, 1); let h; if (isAchromatic(x)) { h = y.h; } else if (isAchromatic(y)) { h = x.h; } else { const xH = x.h; let yH = y.h; const deltaH = y.h - x.h; if (deltaH > 180) { yH -= 360; } else if (deltaH < -180) { yH += 360; } h = xH * (1 - d) + yH * d; } const c = x.c * (1 - d) + y.c * d; const l = x.l * (1 - d) + y.l * d; const a = x.a * (1 - d) + y.a * d; return Color.fromOKLCH(l, c, h, a); }; var ColorScale = class extends AbstractScale { constructor() { super(...arguments); this.type = "color"; this.defaultTickCount = 0; this.invalid = true; this.domain = [0, 1]; this.range = ["red", "blue"]; this.parsedRange = this.range.map(convertColorStringToOklcha); } update() { const { domain, range: range3 } = this; if (domain.length < 2) { logger_exports.warnOnce("`colorDomain` should have at least 2 values."); if (domain.length === 0) { domain.push(0, 1); } else if (domain.length === 1) { domain.push(domain[0] + 1); } } for (let i = 1; i < domain.length; i++) { const a = domain[i - 1]; const b = domain[i]; if (a > b) { logger_exports.warnOnce("`colorDomain` values should be supplied in ascending order."); domain.sort((a2, b2) => a2 - b2); break; } } if (range3.length < domain.length) { for (let i = range3.length; i < domain.length; i++) { range3.push(range3.length > 0 ? range3[0] : "black"); } } this.parsedRange = this.range.map(convertColorStringToOklcha); } normalizeDomains(...domains) { return { domain: domains.map((d) => d.domain).flat(), animatable: true }; } toDomain() { return; } convert(x) { this.refresh(); const { domain, range: range3, parsedRange } = this; const d0 = domain[0]; const d1 = domain.at(-1); const r0 = range3[0]; const r1 = range3.at(-1); if (x <= d0) { return r0; } if (x >= d1) { return r1; } let index; let q; if (domain.length === 2) { const t = (x - d0) / (d1 - d0); const step = 1 / (range3.length - 1); index = range3.length <= 2 ? 0 : Math.min(Math.floor(t * (range3.length - 1)), range3.length - 2); q = (t - index * step) / step; } else { for (index = 0; index < domain.length - 2; index++) { if (x < domain[index + 1]) { break; } } const a = domain[index]; const b = domain[index + 1]; q = (x - a) / (b - a); } const c0 = parsedRange[index]; const c1 = parsedRange[index + 1]; return interpolateOklch(c0, c1, q).toRgbaString(); } invert() { return; } getDomainMinMax() { return unpackDomainMinMax(this.domain); } refresh() { if (!this.invalid) return; this.invalid = false; this.update(); if (this.invalid) { logger_exports.warnOnce("Expected update to not invalidate scale"); } } }; __decorateClass([ Invalidating ], ColorScale.prototype, "domain", 2); __decorateClass([ Invalidating ], ColorScale.prototype, "range", 2); // packages/ag-charts-community/src/scene/gradient/gradient.ts var Gradient = class { constructor(colorSpace, stops = [], bbox) { this.colorSpace = colorSpace; this.stops = stops; this.bbox = bbox; this._cache = void 0; } createGradient(ctx, shapeBbox, params) { const bbox = this.bbox ?? shapeBbox; if (!bbox.isFinite()) { return; } if (this._cache?.ctx === ctx && this._cache.bbox.equals(bbox)) { return this._cache.gradient; } const { stops, colorSpace } = this; if (stops.length === 0) return; if (stops.length === 1) return stops[0].color; let gradient2 = this.createCanvasGradient(ctx, bbox, params); if (gradient2 == null) return; const isOkLch = colorSpace === "oklch"; const step = 0.05; let c0 = stops[0]; gradient2.addColorStop(c0.stop, c0.color); for (let i = 1; i < stops.length; i += 1) { const c1 = stops[i]; if (isOkLch) { const scale2 = new ColorScale(); scale2.domain = [c0.stop, c1.stop]; scale2.range = [c0.color, c1.color]; for (let stop = c0.stop + step; stop < c1.stop; stop += step) { gradient2.addColorStop(stop, scale2.convert(stop) ?? "transparent"); } } gradient2.addColorStop(c1.stop, c1.color); c0 = c1; } if ("createPattern" in gradient2) { gradient2 = gradient2.createPattern(); } this._cache = { ctx, bbox, gradient: gradient2 }; return gradient2; } toSvg(shapeBbox) { const bbox = this.bbox ?? shapeBbox; const gradient2 = this.createSvgGradient(bbox); for (const { stop: offset, color: color2 } of this.stops) { const stop = createSvgElement("stop"); stop.setAttribute("offset", `${offset}`); stop.setAttribute("stop-color", `${color2}`); gradient2.appendChild(stop); } return gradient2; } }; // packages/ag-charts-community/src/scene/gradient/conicGradient.ts var ConicGradient = class extends Gradient { constructor(colorSpace, stops, angle2 = 0, bbox) { super(colorSpace, stops, bbox); this.angle = angle2; } createCanvasGradient(ctx, bbox, params) { const angleOffset = -90; const { angle: angle2 } = this; const radians = normalizeAngle360FromDegrees(angle2 + angleOffset); const cx = params?.centerX ?? bbox.x + bbox.width * 0.5; const cy = params?.centerY ?? bbox.y + bbox.height * 0.5; return ctx.createConicGradient(radians, cx, cy); } createSvgGradient(_bbox) { return createSvgElement("linearGradient"); } }; // packages/ag-charts-community/src/scene/gradient/linearGradient.ts var LinearGradient = class extends Gradient { constructor(colorSpace, stops, angle2 = 0, bbox) { super(colorSpace, stops, bbox); this.angle = angle2; } getGradientPoints(bbox) { const angleOffset = 90; const { angle: angle2 } = this; const radians = normalizeAngle360FromDegrees(angle2 + angleOffset); const cos = Math.cos(radians); const sin = Math.sin(radians); const w = bbox.width; const h = bbox.height; const cx = bbox.x + w * 0.5; const cy = bbox.y + h * 0.5; const diagonal = Math.hypot(h, w) / 2; const diagonalAngle = Math.atan2(h, w); let quarteredAngle; if (radians < Math.PI / 2) { quarteredAngle = radians; } else if (radians < Math.PI) { quarteredAngle = Math.PI - radians; } else if (radians < 1.5 * Math.PI) { quarteredAngle = radians - Math.PI; } else { quarteredAngle = 2 * Math.PI - radians; } const l = diagonal * Math.abs(Math.cos(quarteredAngle - diagonalAngle)); return { x0: cx + cos * l, y0: cy + sin * l, x1: cx - cos * l, y1: cy - sin * l }; } createCanvasGradient(ctx, bbox) { const { x0, y0, x1, y1 } = this.getGradientPoints(bbox); if (Number.isNaN(x0) || Number.isNaN(y0) || Number.isNaN(x1) || Number.isNaN(y1)) { return void 0; } return ctx.createLinearGradient(x0, y0, x1, y1); } createSvgGradient(bbox) { const { x0, y0, x1, y1 } = this.getGradientPoints(bbox); const gradient2 = createSvgElement("linearGradient"); gradient2.setAttribute("x1", String(x0)); gradient2.setAttribute("y1", String(y0)); gradient2.setAttribute("x2", String(x1)); gradient2.setAttribute("y2", String(y1)); gradient2.setAttribute("gradientUnits", "userSpaceOnUse"); return gradient2; } }; // packages/ag-charts-community/src/scene/gradient/radialGradient.ts var RadialGradient = class extends Gradient { constructor(colorSpace, stops, bbox) { super(colorSpace, stops, bbox); } createCanvasGradient(ctx, bbox, params) { const cx = params?.centerX ?? bbox.x + bbox.width * 0.5; const cy = params?.centerY ?? bbox.y + bbox.height * 0.5; const innerRadius = params?.innerRadius ?? 0; const outerRadius = params?.outerRadius ?? Math.hypot(bbox.width * 0.5, bbox.height * 0.5) / Math.SQRT2; return ctx.createRadialGradient(cx, cy, innerRadius, cx, cy, outerRadius); } createSvgGradient(bbox) { const cx = bbox.x + bbox.width * 0.5; const cy = bbox.y + bbox.height * 0.5; const gradient2 = createSvgElement("radialGradient"); gradient2.setAttribute("cx", String(cx)); gradient2.setAttribute("cy", String(cy)); gradient2.setAttribute("r", String(Math.hypot(bbox.width * 0.5, bbox.height * 0.5) / Math.SQRT2)); gradient2.setAttribute("gradientUnits", "userSpaceOnUse"); return gradient2; } }; // packages/ag-charts-community/src/scene/gradient/stops.ts var StopProperties = class extends BaseProperties { constructor() { super(...arguments); this.color = "black"; } }; __decorateClass([ addFakeTransformToInstanceProperty ], StopProperties.prototype, "stop", 2); __decorateClass([ addFakeTransformToInstanceProperty ], StopProperties.prototype, "color", 2); function stopsAreAscending(fills) { let currentStop; for (const fill of fills) { if (fill?.stop == null) continue; if (currentStop != null && fill.stop < currentStop) { return false; } currentStop = fill.stop; } return true; } function discreteColorStops(colorStops) { return colorStops.flatMap((colorStop2, i) => { const { stop } = colorStop2; const nextColor = colorStops.at(i + 1)?.color; return nextColor == null ? [colorStop2] : [colorStop2, { stop, color: nextColor }]; }); } function getDefaultColorStops(defaultColorStops, fillMode) { const stopOffset = fillMode === "discrete" ? 1 : 0; const colorStops = defaultColorStops.map( (color2, index, { length: length2 }) => ({ stop: (index + stopOffset) / (length2 - 1 + stopOffset), color: color2 }) ); return fillMode === "discrete" ? discreteColorStops(colorStops) : colorStops; } function getColorStops(baseFills, defaultColorStops, domain, fillMode = "continuous") { const fills = baseFills.map((fill) => typeof fill === "string" ? { color: fill } : fill); if (fills.length === 0) { return getDefaultColorStops(defaultColorStops, fillMode); } else if (!stopsAreAscending(fills)) { logger_exports.warnOnce(`[fills] must have the stops defined in ascending order`); return []; } const d0 = Math.min(...domain); const d1 = Math.max(...domain); const isDiscrete = fillMode === "discrete"; const stops = new Float64Array(fills.length); let previousDefinedStopIndex = 0; let nextDefinedStopIndex = -1; for (let i = 0; i < fills.length; i += 1) { const colorStop2 = fills[i]; if (i >= nextDefinedStopIndex) { nextDefinedStopIndex = fills.length - 1; for (let j = i + 1; j < fills.length; j += 1) { if (fills[j]?.stop != null) { nextDefinedStopIndex = j; break; } } } let stop = colorStop2?.stop; if (stop == null) { const stop0 = fills[previousDefinedStopIndex]?.stop; const stop1 = fills[nextDefinedStopIndex]?.stop; const value0 = stop0 ?? d0; const value1 = stop1 ?? d1; const stopOffset = isDiscrete && stop0 == null ? 1 : 0; stop = value0 + (value1 - value0) * (i - previousDefinedStopIndex + stopOffset) / (nextDefinedStopIndex - previousDefinedStopIndex + stopOffset); } else { previousDefinedStopIndex = i; } stops[i] = Math.max(0, Math.min(1, (stop - d0) / (d1 - d0))); } let lastDefinedColor = fills.find((c) => c.color != null)?.color; let colorScale; const colorStops = fills.map((fill, i) => { let color2 = fill?.color; const stop = stops[i]; if (color2 != null) { lastDefinedColor = color2; } else if (lastDefinedColor == null) { if (colorScale == null) { colorScale = new ColorScale(); colorScale.domain = [0, 1]; colorScale.range = defaultColorStops; } color2 = colorScale.convert(stop); } else { color2 = lastDefinedColor; } return { stop, color: color2 }; }); return fillMode === "discrete" ? discreteColorStops(colorStops) : colorStops; } // packages/ag-charts-community/src/scene/image/image.ts var Image = class { constructor(imageLoader, imageOptions) { this.imageLoader = imageLoader; this._cache = void 0; this.url = imageOptions.url; this.backgroundFill = imageOptions.backgroundFill ?? "black"; this.backgroundFillOpacity = imageOptions.backgroundFillOpacity ?? 1; this.repeat = imageOptions.repeat ?? "no-repeat"; this.width = imageOptions.width; this.height = imageOptions.height; this.fit = imageOptions.fit ?? "stretch"; this.rotation = imageOptions.rotation ?? 0; } createCanvasImage(ctx, image, width2, height2) { if (!image) return null; const [renderedWidth, renderedHeight] = this.getSize(image.width, image.height, width2, height2); if (renderedWidth < 1 || renderedHeight < 1) { logger_exports.warnOnce("Image fill is too small to render, ignoring."); return null; } return ctx.createPattern(image, this.repeat); } getSize(imageWidth, imageHeight, width2, height2) { const { fit } = this; let dw = imageWidth; let dh = imageHeight; let scale2 = 1; const shapeAspectRatio = width2 / height2; const imageAspectRatio = imageWidth / imageHeight; if (fit === "stretch" || imageWidth === 0 || imageHeight === 0) { dw = width2; dh = height2; } else if (fit === "contain") { scale2 = imageAspectRatio > shapeAspectRatio ? width2 / imageWidth : height2 / imageHeight; } else if (fit === "cover") { scale2 = imageAspectRatio > shapeAspectRatio ? height2 / imageHeight : width2 / imageWidth; } return [Math.max(1, dw * scale2), Math.max(1, dh * scale2)]; } setImageTransform(pattern, bbox) { if (typeof pattern === "string") return; const { url, rotation, width: width2, height: height2 } = this; const image = this.imageLoader?.loadImage(url); if (!image) { return; } const angle2 = normalizeAngle360FromDegrees(rotation); const cos = Math.cos(angle2); const sin = Math.sin(angle2); const [renderedWidth, renderedHeight] = this.getSize( image.width, image.height, width2 ?? bbox.width, height2 ?? bbox.height ); const widthScale = renderedWidth / image.width; const heightScale = renderedHeight / image.height; const bboxCenterX = bbox.x + bbox.width / 2; const bboxCenterY = bbox.y + bbox.height / 2; const rotatedW = cos * renderedWidth - sin * renderedHeight; const rotatedH = sin * renderedWidth + cos * renderedHeight; const shapeCenterX = rotatedW / 2; const shapeCenterY = rotatedH / 2; const DOMMatrixCtor = getDOMMatrix(); pattern?.setTransform( new DOMMatrixCtor([ cos * widthScale, sin * heightScale, -sin * widthScale, cos * heightScale, bboxCenterX - shapeCenterX, bboxCenterY - shapeCenterY ]) ); } createPattern(ctx, shapeWidth, shapeHeight, node) { const width2 = this.width ?? shapeWidth; const height2 = this.height ?? shapeHeight; const cache = this._cache; if (cache?.ctx === ctx && cache.width === width2 && cache.height === height2) { return cache.pattern; } const image = this.imageLoader?.loadImage(this.url, node); const pattern = this.createCanvasImage(ctx, image, width2, height2); if (pattern == null) return; this._cache = { ctx, pattern, width: width2, height: height2 }; return pattern; } toSvg(bbox, pixelRatio) { const { url, rotation, backgroundFill, backgroundFillOpacity } = this; const { x, y, width: width2, height: height2 } = bbox; const pattern = createSvgElement("pattern"); pattern.setAttribute("viewBox", `0 0 ${width2} ${height2}`); pattern.setAttribute("x", String(x)); pattern.setAttribute("y", String(y)); pattern.setAttribute("width", String(width2)); pattern.setAttribute("height", String(height2)); pattern.setAttribute("patternUnits", "userSpaceOnUse"); const rect2 = createSvgElement("rect"); rect2.setAttribute("x", "0"); rect2.setAttribute("y", "0"); rect2.setAttribute("width", String(width2)); rect2.setAttribute("height", String(height2)); rect2.setAttribute("fill", backgroundFill); rect2.setAttribute("fill-opacity", String(backgroundFillOpacity)); pattern.appendChild(rect2); const image = createSvgElement("image"); image.setAttribute("href", url); image.setAttribute("x", "0"); image.setAttribute("y", "0"); image.setAttribute("width", String(width2)); image.setAttribute("height", String(height2)); image.setAttribute("preserveAspectRatio", "none"); image.setAttribute("transform", `scale(${1 / pixelRatio}) rotate(${rotation}, ${width2 / 2}, ${height2 / 2})`); pattern.appendChild(image); return pattern; } }; // packages/ag-charts-community/src/util/svg.ts var commandEx = /^[\t\n\f\r ]*([achlmqstvz])[\t\n\f\r ]*/i; var coordinateEx = /^[+-]?((\d*\.\d+)|(\d+\.)|(\d+))(e[+-]?\d+)?/i; var commaEx = /[\t\n\f\r ]*,?[\t\n\f\r ]*/; var flagEx = /^[01]/; var pathParams = { z: [], h: [coordinateEx], v: [coordinateEx], m: [coordinateEx, coordinateEx], l: [coordinateEx, coordinateEx], t: [coordinateEx, coordinateEx], s: [coordinateEx, coordinateEx, coordinateEx, coordinateEx], q: [coordinateEx, coordinateEx, coordinateEx, coordinateEx], c: [coordinateEx, coordinateEx, coordinateEx, coordinateEx, coordinateEx, coordinateEx], a: [coordinateEx, coordinateEx, coordinateEx, flagEx, flagEx, coordinateEx, coordinateEx] }; function parseSvg(d) { if (!d) return; const segments = []; let i = 0; let currentCommand; while (i < d.length) { const commandMatch = commandEx.exec(d.slice(i)); let command; if (commandMatch == null) { if (!currentCommand) { logger_exports.warnOnce(`Invalid SVG path, error at index ${i}: Missing command.`); return; } command = currentCommand; } else { command = commandMatch[1]; i += commandMatch[0].length; } const segment = parseSegment(command, d, i); if (!segment) return; i = segment[0]; currentCommand = command; segments.push(segment[1]); } return segments; } function parseSegment(command, d, index) { const params = pathParams[command.toLocaleLowerCase()]; const pathSeg = { command, params: [] }; for (const regex of params) { const segment = d.slice(index); const match = regex.exec(segment); if (match != null) { pathSeg.params.push(Number.parseFloat(match[0])); index += match[0].length; const next = commaEx.exec(segment.slice(match[0].length)); if (next != null) { index += next[0].length; } } else if (pathSeg.params.length === 1) { return [index, pathSeg]; } else { logger_exports.warnOnce( `Invalid SVG path, error at index ${index}: No path segment parameters for command [${command}]` ); return; } } return [index, pathSeg]; } // packages/ag-charts-community/src/scene/polyRoots.ts function linearRoot(a, b) { const t = -b / a; return a !== 0 && t >= 0 && t <= 1 ? [t] : []; } function quadraticRoots(a, b, c, delta3 = 1e-6) { if (Math.abs(a) < delta3) { return linearRoot(b, c); } const D = b * b - 4 * a * c; const roots = []; if (Math.abs(D) < delta3) { const t = -b / (2 * a); if (t >= 0 && t <= 1) { roots.push(t); } } else if (D > 0) { const rD = Math.sqrt(D); const t1 = (-b - rD) / (2 * a); const t2 = (-b + rD) / (2 * a); if (t1 >= 0 && t1 <= 1) { roots.push(t1); } if (t2 >= 0 && t2 <= 1) { roots.push(t2); } } return roots; } function cubicRoots(a, b, c, d, delta3 = 1e-6) { if (Math.abs(a) < delta3) { return quadraticRoots(b, c, d, delta3); } const A = b / a; const B = c / a; const C2 = d / a; const Q = (3 * B - A * A) / 9; const R = (9 * A * B - 27 * C2 - 2 * A * A * A) / 54; const D = Q * Q * Q + R * R; const third = 1 / 3; const roots = []; if (D >= 0) { const rD = Math.sqrt(D); const S = Math.sign(R + rD) * Math.pow(Math.abs(R + rD), third); const T = Math.sign(R - rD) * Math.pow(Math.abs(R - rD), third); const Im = Math.abs(Math.sqrt(3) * (S - T) / 2); const t = -third * A + (S + T); if (t >= 0 && t <= 1) { roots.push(t); } if (Math.abs(Im) < delta3) { const t2 = -third * A - (S + T) / 2; if (t2 >= 0 && t2 <= 1) { roots.push(t2); } } } else { const theta = Math.acos(R / Math.sqrt(-Q * Q * Q)); const thirdA = third * A; const twoSqrtQ = 2 * Math.sqrt(-Q); const t1 = twoSqrtQ * Math.cos(third * theta) - thirdA; const t2 = twoSqrtQ * Math.cos(third * (theta + 2 * Math.PI)) - thirdA; const t3 = twoSqrtQ * Math.cos(third * (theta + 4 * Math.PI)) - thirdA; if (t1 >= 0 && t1 <= 1) { roots.push(t1); } if (t2 >= 0 && t2 <= 1) { roots.push(t2); } if (t3 >= 0 && t3 <= 1) { roots.push(t3); } } return roots; } // packages/ag-charts-community/src/scene/intersection.ts function segmentIntersection(ax1, ay1, ax2, ay2, bx1, by1, bx2, by2) { const d = (ax2 - ax1) * (by2 - by1) - (ay2 - ay1) * (bx2 - bx1); if (d === 0) { return 0; } const ua = ((bx2 - bx1) * (ay1 - by1) - (ax1 - bx1) * (by2 - by1)) / d; const ub = ((ax2 - ax1) * (ay1 - by1) - (ay2 - ay1) * (ax1 - bx1)) / d; if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1) { return 1; } return 0; } function cubicSegmentIntersections(px1, py1, px2, py2, px3, py3, px4, py4, x1, y1, x2, y2) { let intersections = 0; const A = y1 - y2; const B = x2 - x1; const C2 = x1 * (y2 - y1) - y1 * (x2 - x1); const bx = bezierCoefficients(px1, px2, px3, px4); const by = bezierCoefficients(py1, py2, py3, py4); const a = A * bx[0] + B * by[0]; const b = A * bx[1] + B * by[1]; const c = A * bx[2] + B * by[2]; const d = A * bx[3] + B * by[3] + C2; const roots = cubicRoots(a, b, c, d); for (const t of roots) { const tt = t * t; const ttt = t * tt; const x = bx[0] * ttt + bx[1] * tt + bx[2] * t + bx[3]; const y = by[0] * ttt + by[1] * tt + by[2] * t + by[3]; let s; if (x1 === x2) { s = (y - y1) / (y2 - y1); } else { s = (x - x1) / (x2 - x1); } if (s >= 0 && s <= 1) { intersections++; } } return intersections; } function bezierCoefficients(P1, P2, P3, P4) { return [ // Bézier expressed as matrix operations: // |-1 3 -3 1| |P1| // [t^3 t^2 t 1] | 3 -6 3 0| |P2| // |-3 3 0 0| |P3| // | 1 0 0 0| |P4| -P1 + 3 * P2 - 3 * P3 + P4, 3 * P1 - 6 * P2 + 3 * P3, -3 * P1 + 3 * P2, P1 ]; } // packages/ag-charts-community/src/scene/extendedPath2D.ts var ExtendedPath2D = class { constructor() { this.previousCommands = []; this.previousParams = []; this.previousClosedPath = false; this.commands = []; this.params = []; this.commandsLength = 0; this.paramsLength = 0; this.cx = Number.NaN; this.cy = Number.NaN; this.sx = Number.NaN; this.sy = Number.NaN; this.openedPath = false; this.closedPath = false; const Path2DCtor = getPath2D(); this.path2d = new Path2DCtor(); } isEmpty() { return this.commandsLength === 0; } isDirty() { return this.closedPath !== this.previousClosedPath || this.previousCommands.length !== this.commandsLength || this.previousParams.length !== this.paramsLength || this.previousCommands.toString() !== this.commands.slice(0, this.commandsLength).toString() || this.previousParams.toString() !== this.params.slice(0, this.paramsLength).toString(); } getPath2D() { return this.path2d; } moveTo(x, y) { this.openedPath = true; this.sx = x; this.sy = y; this.cx = x; this.cy = y; this.path2d.moveTo(x, y); this.commands[this.commandsLength++] = 0 /* Move */; this.params[this.paramsLength++] = x; this.params[this.paramsLength++] = y; } lineTo(x, y) { if (this.openedPath) { this.cx = x; this.cy = y; this.path2d.lineTo(x, y); this.commands[this.commandsLength++] = 1 /* Line */; this.params[this.paramsLength++] = x; this.params[this.paramsLength++] = y; } else { this.moveTo(x, y); } } cubicCurveTo(cx1, cy1, cx2, cy2, x, y) { if (!this.openedPath) { this.moveTo(cx1, cy1); } this.path2d.bezierCurveTo(cx1, cy1, cx2, cy2, x, y); this.commands[this.commandsLength++] = 2 /* Curve */; this.params[this.paramsLength++] = cx1; this.params[this.paramsLength++] = cy1; this.params[this.paramsLength++] = cx2; this.params[this.paramsLength++] = cy2; this.params[this.paramsLength++] = x; this.params[this.paramsLength++] = y; } closePath() { if (this.openedPath) { this.cx = this.sx; this.cy = this.sy; this.sx = Number.NaN; this.sy = Number.NaN; this.path2d.closePath(); this.commands[this.commandsLength++] = 3 /* ClosePath */; this.openedPath = false; this.closedPath = true; } } rect(x, y, width2, height2) { this.moveTo(x, y); this.lineTo(x + width2, y); this.lineTo(x + width2, y + height2); this.lineTo(x, y + height2); this.closePath(); } roundRect(x, y, width2, height2, radii) { radii = Math.min(radii, width2 / 2, height2 / 2); this.moveTo(x, y + radii); this.arc(x + radii, y + radii, radii, Math.PI, 1.5 * Math.PI); this.lineTo(x + radii, y); this.lineTo(x + width2 - radii, y); this.arc(x + width2 - radii, y + radii, radii, 1.5 * Math.PI, 2 * Math.PI); this.lineTo(x + width2, y + radii); this.lineTo(x + width2, y + height2 - radii); this.arc(x + width2 - radii, y + height2 - radii, radii, 0, Math.PI / 2); this.lineTo(x + width2 - radii, y + height2); this.lineTo(x + radii, y + height2); this.arc(x + +radii, y + height2 - radii, radii, Math.PI / 2, Math.PI); this.lineTo(x, y + height2 - radii); this.closePath(); } ellipse(cx, cy, rx, ry, rotation, sAngle, eAngle, counterClockwise = false) { const r = rx; const scaleY = ry / rx; const mxx = Math.cos(rotation); const myx = Math.sin(rotation); const mxy = -scaleY * myx; const myy = scaleY * mxx; const x0 = r * Math.cos(sAngle); const y0 = r * Math.sin(sAngle); const sx = cx + mxx * x0 + mxy * y0; const sy = cy + myx * x0 + myy * y0; const distanceSquared2 = (sx - this.cx) ** 2 + (sy - this.cy) ** 2; if (!this.openedPath) { this.moveTo(sx, sy); } else if (distanceSquared2 > 1e-6) { this.lineTo(sx, sy); } let sweep = counterClockwise ? -normalizeAngle360(sAngle - eAngle) : normalizeAngle360(eAngle - sAngle); if (Math.abs(Math.abs(eAngle - sAngle) - 2 * Math.PI) < 1e-6 && sweep < 2 * Math.PI) { sweep += 2 * Math.PI * (counterClockwise ? -1 : 1); } const arcSections = Math.max(Math.ceil(Math.abs(sweep) / (Math.PI / 2)), 1); const step = sweep / arcSections; const h = 4 / 3 * Math.tan(step / 4); for (let i = 0; i < arcSections; i += 1) { const a0 = sAngle + step * (i + 0); const a1 = sAngle + step * (i + 1); const rSinStart = r * Math.sin(a0); const rCosStart = r * Math.cos(a0); const rSinEnd = r * Math.sin(a1); const rCosEnd = r * Math.cos(a1); const cp1x = rCosStart - h * rSinStart; const cp1y = rSinStart + h * rCosStart; const cp2x = rCosEnd + h * rSinEnd; const cp2y = rSinEnd - h * rCosEnd; const cp3x = rCosEnd; const cp3y = rSinEnd; this.cubicCurveTo( cx + mxx * cp1x + mxy * cp1y, cy + myx * cp1x + myy * cp1y, cx + mxx * cp2x + mxy * cp2y, cy + myx * cp2x + myy * cp2y, cx + mxx * cp3x + mxy * cp3y, cy + myx * cp3x + myy * cp3y ); } } arc(x, y, r, sAngle, eAngle, counterClockwise) { this.ellipse(x, y, r, r, 0, sAngle, eAngle, counterClockwise); } appendSvg(svg) { const parts = parseSvg(svg); if (parts == null) return false; let sx = 0; let sy = 0; let cx; let cy; let cpx = 0; let cpy = 0; for (const { command, params } of parts) { cx ?? (cx = params[0]); cy ?? (cy = params[1]); const relative = command === command.toLowerCase(); const dx = relative ? cx : 0; const dy = relative ? cy : 0; switch (command.toLowerCase()) { case "m": this.moveTo(dx + params[0], dy + params[1]); cx = dx + params[0]; cy = dy + params[1]; sx = cx; sy = cy; break; case "c": this.cubicCurveTo( dx + params[0], dy + params[1], dx + params[2], dy + params[3], dx + params[4], dy + params[5] ); cpx = dx + params[2]; cpy = dy + params[3]; cx = dx + params[4]; cy = dy + params[5]; break; case "s": this.cubicCurveTo( cx + cx - cpx, cy + cy - cpy, dx + params[0], dy + params[1], dx + params[2], dy + params[3] ); cpx = dx + params[0]; cpy = dy + params[1]; cx = dx + params[2]; cy = dy + params[3]; break; case "q": this.cubicCurveTo( (dx + 2 * params[0]) / 3, (dy + 2 * params[1]) / 3, (2 * params[0] + params[2]) / 3, (2 * params[1] + params[3]) / 3, params[2], params[3] ); cpx = params[0]; cpy = params[1]; cx = params[2]; cy = params[3]; break; case "t": this.cubicCurveTo( (cx + 2 * (cx + cx - cpx)) / 3, (cy + 2 * (cy + cy - cpy)) / 3, (2 * (cx + cx - cpx) + params[0]) / 3, (2 * (cy + cy - cpy) + params[1]) / 3, params[0], params[1] ); cpx = cx + cx - cpx; cpy = cy + cy - cpy; cx = params[0]; cy = params[1]; break; case "a": this.svgEllipse( cx, cy, params[0], params[1], params[2] * Math.PI / 180, params[3], params[4], dx + params[5], dy + params[6] ); cx = dx + params[5]; cy = dy + params[6]; break; case "h": this.lineTo(dx + params[0], cy); cx = dx + params[0]; break; case "l": this.lineTo(dx + params[0], dy + params[1]); cx = dx + params[0]; cy = dy + params[1]; break; case "v": this.lineTo(cx, dy + params[0]); cy = dy + params[0]; break; case "z": this.closePath(); cx = sx; cy = sy; break; default: throw new Error(`Could not translate command '${command}' with '${params.join(" ")}'`); } } return true; } svgEllipse(x1, y1, rx, ry, rotation, fA, fS, x2, y2) { rx = Math.abs(rx); ry = Math.abs(ry); const dx = (x1 - x2) / 2; const dy = (y1 - y2) / 2; const sin = Math.sin(rotation); const cos = Math.cos(rotation); const rotX = cos * dx + sin * dy; const rotY = -sin * dx + cos * dy; const normX = rotX / rx; const normY = rotY / ry; let scale2 = normX * normX + normY * normY; let cx = (x1 + x2) / 2; let cy = (y1 + y2) / 2; let cpx = 0; let cpy = 0; if (scale2 >= 1) { scale2 = Math.sqrt(scale2); rx *= scale2; ry *= scale2; } else { scale2 = Math.sqrt(1 / scale2 - 1); if (fA === fS) scale2 = -scale2; cpx = scale2 * rx * normY; cpy = -scale2 * ry * normX; cx += cos * cpx - sin * cpy; cy += sin * cpx + cos * cpy; } const sAngle = Math.atan2((rotY - cpy) / ry, (rotX - cpx) / rx); const deltaTheta = Math.atan2((-rotY - cpy) / ry, (-rotX - cpx) / rx) - sAngle; const eAngle = sAngle + deltaTheta; const counterClockwise = !!(1 - fS); this.ellipse(cx, cy, rx, ry, rotation, sAngle, eAngle, counterClockwise); } clear(trackChanges) { if (trackChanges) { this.previousCommands = this.commands.slice(0, this.commandsLength); this.previousParams = this.params.slice(0, this.paramsLength); this.previousClosedPath = this.closedPath; this.commands = []; this.params = []; this.commandsLength = 0; this.paramsLength = 0; } else { this.commandsLength = 0; this.paramsLength = 0; } const Path2DCtor = getPath2D(); this.path2d = new Path2DCtor(); this.openedPath = false; this.closedPath = false; } isPointInPath(x, y) { const commands = this.commands; const params = this.params; const cn = this.commandsLength; const ox = -1e4; const oy = -1e4; let sx = Number.NaN; let sy = Number.NaN; let px = 0; let py = 0; let intersectionCount = 0; for (let ci = 0, pi = 0; ci < cn; ci++) { switch (commands[ci]) { case 0 /* Move */: intersectionCount += segmentIntersection(sx, sy, px, py, ox, oy, x, y); px = params[pi++]; sx = px; py = params[pi++]; sy = py; break; case 1 /* Line */: intersectionCount += segmentIntersection(px, py, params[pi++], params[pi++], ox, oy, x, y); px = params[pi - 2]; py = params[pi - 1]; break; case 2 /* Curve */: intersectionCount += cubicSegmentIntersections( px, py, params[pi++], params[pi++], params[pi++], params[pi++], params[pi++], params[pi++], ox, oy, x, y ); px = params[pi - 2]; py = params[pi - 1]; break; case 3 /* ClosePath */: intersectionCount += segmentIntersection(sx, sy, px, py, ox, oy, x, y); break; } } return intersectionCount % 2 === 1; } distanceSquared(x, y) { let best = Infinity; const commands = this.commands; const params = this.params; const cn = this.commandsLength; let sx = Number.NaN; let sy = Number.NaN; let cx = 0; let cy = 0; for (let ci = 0, pi = 0; ci < cn; ci++) { switch (commands[ci]) { case 0 /* Move */: cx = sx = params[pi++]; cy = sy = params[pi++]; break; case 1 /* Line */: { const x0 = cx; const y0 = cy; cx = params[pi++]; cy = params[pi++]; best = lineDistanceSquared(x, y, x0, y0, cx, cy, best); break; } case 2 /* Curve */: { const cp0x = cx; const cp0y = cy; const cp1x = params[pi++]; const cp1y = params[pi++]; const cp2x = params[pi++]; const cp2y = params[pi++]; cx = params[pi++]; cy = params[pi++]; best = bezier2DDistance(cp0x, cp0y, cp1x, cp1y, cp2x, cp2y, cx, cy, x, y) ** 2; break; } case 3 /* ClosePath */: best = lineDistanceSquared(x, y, cx, cy, sx, sy, best); break; } } return best; } // https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d toSVG(transform = (x, y) => ({ x, y })) { const buffer = []; const { commands, params } = this; const addCommand = (command, count) => { buffer.push(command); for (let i = 0; i < count; i += 2) { const { x, y } = transform(params[pi++], params[pi++]); buffer.push(x, y); } }; let pi = 0; for (let ci = 0; ci < this.commandsLength; ci++) { const command = commands[ci]; switch (command) { case 0 /* Move */: addCommand("M", 2); break; case 1 /* Line */: addCommand("L", 2); break; case 2 /* Curve */: addCommand("C", 6); break; case 3 /* ClosePath */: addCommand("Z", 0); break; } } return buffer.join(" "); } computeBBox() { const { commands, params } = this; let [top, left, right, bot] = [Infinity, Infinity, -Infinity, -Infinity]; let [cx, cy] = [Number.NaN, Number.NaN]; let [sx, sy] = [Number.NaN, Number.NaN]; const joinPoint = (x, y) => { top = Math.min(y, top); left = Math.min(x, left); right = Math.max(x, right); bot = Math.max(y, bot); cx = x; cy = y; }; let pi = 0; for (let ci = 0; ci < this.commandsLength; ci++) { const command = commands[ci]; switch (command) { case 0 /* Move */: joinPoint(params[pi++], params[pi++]); sx = cx; sy = cy; break; case 1 /* Line */: joinPoint(params[pi++], params[pi++]); break; case 2 /* Curve */: { const cp0x = cx; const cp0y = cy; const cp1x = params[pi++]; const cp1y = params[pi++]; const cp2x = params[pi++]; const cp2y = params[pi++]; const cp3x = params[pi++]; const cp3y = params[pi++]; const ts = bezier2DExtrema(cp0x, cp0y, cp1x, cp1y, cp2x, cp2y, cp3x, cp3y); for (const t of ts) { const px = evaluateBezier(cp0x, cp1x, cp2x, cp3x, t); const py = evaluateBezier(cp0y, cp1y, cp2y, cp3y, t); joinPoint(px, py); } joinPoint(cp3x, cp3y); break; } case 3 /* ClosePath */: joinPoint(sx, sy); sx = Number.NaN; sy = Number.NaN; break; } } return new BBox(left, top, right - left, bot - top); } }; // packages/ag-charts-community/src/scene/util/pixel.ts function align(pixelRatio, start2, length2) { const alignedStart = Math.round(start2 * pixelRatio) / pixelRatio; if (length2 == null) { return alignedStart; } else if (length2 === 0) { return 0; } else if (length2 < 1) { return Math.ceil(length2 * pixelRatio) / pixelRatio; } return Math.round((length2 + start2) * pixelRatio) / pixelRatio - alignedStart; } function alignBefore(pixelRatio, start2) { return Math.floor(start2 * pixelRatio) / pixelRatio; } // packages/ag-charts-community/src/scene/pattern/patterns.ts function drawPatternUnitPolygon(path, params, moves) { const { width: width2, height: height2, padding: padding2, strokeWidth } = params; const x0 = width2 / 2; const y0 = height2 / 2; const w = Math.max(1, width2 - padding2 - strokeWidth / 2); const h = Math.max(1, height2 - padding2 - strokeWidth / 2); let didMove = false; for (const [dx, dy] of moves) { const x = x0 + (dx - 0.5) * w; const y = y0 + (dy - 0.5) * h; if (didMove) { path.lineTo(x, y); } else { path.moveTo(x, y); } didMove = true; } path.closePath(); } var PATTERNS = { circles(path, { width: width2, strokeWidth, padding: padding2 }) { const c = width2 / 2; const r = Math.max(1, c - padding2 - strokeWidth / 2); path.arc(c, c, r, 0, Math.PI * 2); }, squares(path, { width: width2, height: height2, pixelRatio, padding: padding2, strokeWidth }) { const offset = padding2 + strokeWidth / 2; path.moveTo(align(pixelRatio, offset), align(pixelRatio, offset)); path.lineTo(align(pixelRatio, width2 - offset), align(pixelRatio, offset)); path.lineTo(align(pixelRatio, width2 - offset), align(pixelRatio, height2 - offset)); path.lineTo(align(pixelRatio, offset), align(pixelRatio, height2 - offset)); path.closePath(); }, triangles(path, params) { drawPatternUnitPolygon(path, params, [ [0.5, 0], [1, 1], [0, 1] ]); }, diamonds(path, params) { drawPatternUnitPolygon(path, params, [ [0.5, 0], [1, 0.5], [0.5, 1], [0, 0.5] ]); }, stars(path, { width: width2, height: height2, padding: padding2 }) { const spikes = 5; const outerRadius = Math.max(1, (width2 - padding2) / 2); const innerRadius = outerRadius / 2; const rotation = Math.PI / 2; for (let i = 0; i < spikes * 2; i++) { const radius = i % 2 === 0 ? outerRadius : innerRadius; const angle2 = i * Math.PI / spikes - rotation; const xCoordinate = width2 / 2 + Math.cos(angle2) * radius; const yCoordinate = height2 / 2 + Math.sin(angle2) * radius; path.lineTo(xCoordinate, yCoordinate); } path.closePath(); }, hearts(path, { width: width2, height: height2, padding: padding2 }) { const r = Math.max(1, width2 / 4 - padding2 / 2); const x = width2 / 2; const y = height2 / 2 + r / 2; path.arc(x - r, y - r, r, toRadians(130), toRadians(330)); path.arc(x + r, y - r, r, toRadians(220), toRadians(50)); path.lineTo(x, y + r); path.closePath(); }, crosses(path, params) { drawPatternUnitPolygon(path, params, [ [0.25, 0], [0.5, 0.25], [0.75, 0], [1, 0.25], [0.75, 0.5], [1, 0.75], [0.75, 1], [0.5, 0.75], [0.25, 1], [0, 0.75], [0.25, 0.5], [0, 0.25] ]); }, "vertical-lines"(path, { width: width2, height: height2, pixelRatio, strokeWidth }) { const x = align(pixelRatio, width2 / 2) - strokeWidth % 2 / 2; path.moveTo(x, 0); path.lineTo(x, height2); }, "horizontal-lines"(path, { width: width2, height: height2, pixelRatio, strokeWidth }) { const y = align(pixelRatio, height2 / 2) - strokeWidth % 2 / 2; path.moveTo(0, y); path.lineTo(width2, y); }, "forward-slanted-lines"(path, { width: width2, height: height2, strokeWidth }) { const angle2 = Math.atan2(height2, width2); const insetX = strokeWidth * Math.cos(angle2); const insetY = strokeWidth * Math.sin(angle2); path.moveTo(-insetX, insetY); path.lineTo(insetX, -insetY); path.moveTo(-insetX, height2 + insetY); path.lineTo(width2 + insetX, -insetY); path.moveTo(width2 - insetX, height2 + insetY); path.lineTo(width2 + insetX, height2 - insetY); }, "backward-slanted-lines"(path, { width: width2, height: height2, strokeWidth }) { const angle2 = Math.atan2(height2, width2); const insetX = strokeWidth * Math.cos(angle2); const insetY = strokeWidth * Math.sin(angle2); path.moveTo(width2 - insetX, -insetY); path.lineTo(width2 + insetX, insetY); path.moveTo(-insetX, -insetY); path.lineTo(width2 + insetX, height2 + insetY); path.moveTo(-insetX, height2 - insetY); path.lineTo(insetX, height2 + insetY); } }; // packages/ag-charts-community/src/scene/pattern/pattern.ts var Pattern = class { constructor(patternOptions) { this._cache = void 0; this.width = Math.max(patternOptions?.width ?? 10, 1); this.height = Math.max(patternOptions?.height ?? 10, 1); this.fill = patternOptions.fill ?? "none"; this.fillOpacity = patternOptions.fillOpacity ?? 1; this.backgroundFill = patternOptions.backgroundFill ?? "none"; this.backgroundFillOpacity = patternOptions.backgroundFillOpacity ?? 1; this.stroke = patternOptions.stroke ?? "black"; this.strokeOpacity = patternOptions.strokeOpacity ?? 1; this.strokeWidth = patternOptions.strokeWidth ?? 1; this.padding = patternOptions.padding ?? 1; this.pattern = patternOptions.pattern ?? "forward-slanted-lines"; this.rotation = patternOptions.rotation ?? 0; this.scale = patternOptions.scale ?? 1; this.path = patternOptions.path; } getPath(pixelRatio) { const { pattern, width: width2, height: height2, padding: padding2, strokeWidth, path: svgPath } = this; const path = new ExtendedPath2D(); let renderPattern = PATTERNS[pattern] != null; if (svgPath) { renderPattern && (renderPattern = !path.appendSvg(svgPath)); } if (renderPattern) { PATTERNS[pattern](path, { width: width2, height: height2, pixelRatio, strokeWidth, padding: padding2 }); } return path; } renderStroke(path2d, ctx) { const { stroke, strokeWidth, strokeOpacity } = this; if (!strokeWidth) return; ctx.strokeStyle = stroke; ctx.lineWidth = strokeWidth; ctx.globalAlpha = strokeOpacity; ctx.stroke(path2d); } renderFill(path2d, ctx) { const { fill, fillOpacity } = this; if (fill === "none") { return; } ctx.fillStyle = fill; ctx.globalAlpha = fillOpacity; ctx.fill(path2d); } createCanvasPattern(ctx, pixelRatio) { const { width: width2, height: height2, scale: scale2, backgroundFill, backgroundFillOpacity } = this; if (width2 * scale2 < 1 || height2 * scale2 < 1) { logger_exports.warnOnce("Pattern fill is too small to render, ignoring."); return null; } const offscreenPattern = new HdpiOffscreenCanvas({ width: width2, height: height2, pixelRatio: pixelRatio * scale2 }); const offscreenPatternCtx = offscreenPattern.context; if (backgroundFill !== "none") { offscreenPatternCtx.fillStyle = backgroundFill; offscreenPatternCtx.globalAlpha = backgroundFillOpacity; offscreenPatternCtx.fillRect(0, 0, width2, height2); } const path2d = this.getPath(pixelRatio).getPath2D(); this.renderFill(path2d, offscreenPatternCtx); this.renderStroke(path2d, offscreenPatternCtx); const pattern = ctx.createPattern(offscreenPattern.canvas, "repeat"); this.setPatternTransform(pattern, pixelRatio); offscreenPattern.destroy(); return pattern; } setPatternTransform(pattern, pixelRatio, tx = 0, ty = 0) { if (pattern == null) return; const angle2 = normalizeAngle360FromDegrees(this.rotation); const scale2 = 1 / pixelRatio; const cos = Math.cos(angle2) * scale2; const sin = Math.sin(angle2) * scale2; const DOMMatrixCtor = getDOMMatrix(); pattern.setTransform(new DOMMatrixCtor([cos, sin, -sin, cos, tx, ty])); } createPattern(ctx, pixelRatio) { if (this._cache?.ctx === ctx && this._cache.pixelRatio === pixelRatio) { return this._cache.pattern; } const pattern = this.createCanvasPattern(ctx, pixelRatio); if (pattern == null) return; this._cache = { ctx, pattern, pixelRatio }; return pattern; } toSvg() { const { width: width2, height: height2, fill, fillOpacity, backgroundFill, backgroundFillOpacity, stroke, strokeWidth, strokeOpacity, rotation, scale: scale2 } = this; const pattern = createSvgElement("pattern"); pattern.setAttribute("viewBox", `0 0 ${width2} ${height2}`); pattern.setAttribute("width", String(width2)); pattern.setAttribute("height", String(height2)); pattern.setAttribute("patternUnits", "userSpaceOnUse"); const rect2 = createSvgElement("rect"); rect2.setAttribute("x", "0"); rect2.setAttribute("y", "0"); rect2.setAttribute("width", String(width2)); rect2.setAttribute("height", String(height2)); rect2.setAttribute("fill", backgroundFill); rect2.setAttribute("fill-opacity", String(backgroundFillOpacity)); pattern.appendChild(rect2); const path = createSvgElement("path"); path.setAttribute("fill", fill); path.setAttribute("fill-opacity", String(fillOpacity)); path.setAttribute("stroke-opacity", String(strokeOpacity)); path.setAttribute("stroke", stroke); path.setAttribute("stroke-width", String(strokeWidth)); path.setAttribute("transform", `rotate(${rotation}) scale(${scale2})`); path.setAttribute("d", this.getPath(1).toSVG()); pattern.appendChild(path); return pattern; } }; // packages/ag-charts-community/src/scene/shape/svgUtils.ts function setSvgFontAttributes(element2, options) { const { fontStyle, fontWeight: fontWeight2, fontSize, fontFamily } = options; if (fontStyle) element2.setAttribute("font-style", fontStyle); if (fontWeight2) element2.setAttribute("font-weight", String(fontWeight2)); if (fontSize != null) element2.setAttribute("font-size", String(fontSize)); if (fontFamily) element2.setAttribute("font-family", fontFamily); } function setSvgStrokeAttributes(element2, options) { const { stroke, strokeWidth, strokeOpacity } = options; if (stroke) element2.setAttribute("stroke", stroke); if (strokeWidth != null) element2.setAttribute("stroke-width", String(strokeWidth)); if (strokeOpacity != null) element2.setAttribute("stroke-opacity", String(strokeOpacity)); } function setSvgLineDashAttributes(element2, options) { const { lineDash, lineDashOffset } = options; if (lineDash?.some((d) => d !== 0)) { const lineDashArray = lineDash.length % 2 === 1 ? [...lineDash, ...lineDash] : lineDash; element2.setAttribute("stroke-dasharray", lineDashArray.join(" ")); if (lineDashOffset != null) element2.setAttribute("stroke-dashoffset", String(lineDashOffset)); } } // packages/ag-charts-community/src/scene/shape/shape.ts var _Shape = class _Shape extends Node { constructor() { super(...arguments); this.drawingMode = "overlay"; this.fillOpacity = 1; this.strokeOpacity = 1; this.fill = "black"; this.strokeWidth = 0; this.lineDashOffset = 0; this.opacity = 1; } // optimised field accessor getGradient(fill) { if (isGradientFill(fill)) return this.createGradient(fill); } createGradient(fill) { const { colorSpace = "rgb", gradient: gradient2 = "linear", colorStops, rotation = 0, reverse = false } = fill; if (colorStops == null) return; let stops = getColorStops(colorStops, ["black"], [0, 1]); if (reverse) { stops = stops.map((s) => ({ color: s.color, stop: 1 - s.stop })).reverse(); } switch (gradient2) { case "linear": return new LinearGradient(colorSpace, stops, rotation); case "radial": return new RadialGradient(colorSpace, stops); case "conic": return new ConicGradient(colorSpace, stops, rotation); } } getPattern(fill) { if (isPatternFill(fill)) return this.createPattern(fill); } createPattern(fill) { return new Pattern(fill); } getImage(fill) { if (isImageFill(fill)) return this.createImage(fill); } createImage(fill) { return new Image(this.imageLoader, fill); } onFillChange() { if (typeof this.fill === "object") { if (objectsEqual(this._cachedFill ?? {}, this.fill)) { return; } } this.fillGradient = this.getGradient(this.fill); this.fillPattern = this.getPattern(this.fill); this.fillImage = this.getImage(this.fill); this._cachedFill = this.fill; } // optimised field accessor onStrokeChange() { this.strokeGradient = this.getGradient(this.stroke); } // optimised field accessor /** * Returns a device-pixel aligned coordinate (or length if length is supplied). * * NOTE: Not suitable for strokes, since the stroke needs to be offset to the middle * of a device pixel. */ align(start2, length2) { return align(this.layerManager?.canvas?.pixelRatio ?? 1, start2, length2); } markDirty(property) { super.markDirty(property); this.cachedDefaultGradientFillBBox = void 0; } fillStroke(ctx, path) { if (this.__drawingMode === "cutout") { ctx.globalCompositeOperation = "destination-out"; this.executeFill(ctx, path); ctx.globalCompositeOperation = "source-over"; } this.renderFill(ctx, path); this.renderStroke(ctx, path); } renderFill(ctx, path) { const { __fill: fill, __fillOpacity: fillOpacity = 1, fillImage } = this; if (fill != null && fill !== "none" && fillOpacity > 0) { const globalAlpha = ctx.globalAlpha; if (fillImage) { ctx.globalAlpha = fillImage.backgroundFillOpacity; ctx.fillStyle = fillImage.backgroundFill; this.executeFill(ctx, path); ctx.globalAlpha = globalAlpha; } this.applyFillAndAlpha(ctx); this.applyShadow(ctx); this.executeFill(ctx, path); ctx.globalAlpha = globalAlpha; if (this.fillShadow?.enabled) { ctx.shadowColor = "rgba(0, 0, 0, 0)"; } } } executeFill(ctx, path) { if (path) { ctx.fill(path); } else { ctx.fill(); } } applyFillAndAlpha(ctx) { const { __fill: fill, fillGradient, fillPattern, fillImage, __fillOpacity: fillOpacity = 1, __opacity: opacity = 1 } = this; const combinedOpacity = opacity * fillOpacity; if (combinedOpacity !== 1) { ctx.globalAlpha *= combinedOpacity; } if (fillGradient) { const { fillBBox = this.getDefaultGradientFillBBox() ?? this.getBBox(), fillParams } = this; ctx.fillStyle = fillGradient.createGradient(ctx, fillBBox, fillParams) ?? "black"; } else if (fillPattern) { const { x, y } = this.getBBox(); const pixelRatio = this.layerManager?.canvas?.pixelRatio ?? 1; const pattern = fillPattern.createPattern(ctx, pixelRatio); fillPattern.setPatternTransform(pattern, pixelRatio, x, y); if (pattern) { ctx.fillStyle = pattern; } else { ctx.fillStyle = fillPattern.fill; ctx.globalAlpha *= fillPattern.fillOpacity; } } else if (fillImage) { const bbox = this.getBBox(); const image = fillImage.createPattern(ctx, bbox.width, bbox.height, this); fillImage.setImageTransform(image, bbox); ctx.fillStyle = image ?? "transparent"; } else { ctx.fillStyle = typeof fill === "string" ? fill : "black"; } } applyStrokeAndAlpha(ctx) { const { __stroke: stroke, __strokeOpacity: strokeOpacity = 1, strokeGradient, __opacity: opacity = 1 } = this; ctx.strokeStyle = strokeGradient?.createGradient(ctx, this.getBBox()) ?? (typeof stroke === "string" ? stroke : void 0) ?? "black"; const combinedOpacity = opacity * strokeOpacity; if (combinedOpacity !== 1) { ctx.globalAlpha *= combinedOpacity; } } applyShadow(ctx) { const pixelRatio = this.layerManager?.canvas.pixelRatio ?? 1; const { __fillShadow: fillShadow } = this; if (fillShadow?.enabled) { ctx.shadowColor = fillShadow.color; ctx.shadowOffsetX = fillShadow.xOffset * pixelRatio; ctx.shadowOffsetY = fillShadow.yOffset * pixelRatio; ctx.shadowBlur = fillShadow.blur * pixelRatio; } } renderStroke(ctx, path) { const { __stroke: stroke, __strokeWidth: strokeWidth = 0, __strokeOpacity: strokeOpacity = 1, __lineDash: lineDash, __lineDashOffset: lineDashOffset, __lineCap: lineCap, __lineJoin: lineJoin, __miterLimit: miterLimit } = this; if (stroke != null && stroke !== "none" && strokeWidth > 0 && strokeOpacity > 0) { const { globalAlpha } = ctx; this.applyStrokeAndAlpha(ctx); ctx.lineWidth = strokeWidth; if (lineDash) { ctx.setLineDash(lineDash); } if (lineDashOffset) { ctx.lineDashOffset = lineDashOffset; } if (lineCap) { ctx.lineCap = lineCap; } if (lineJoin) { ctx.lineJoin = lineJoin; } if (miterLimit != null) { ctx.miterLimit = miterLimit; } this.executeStroke(ctx, path); ctx.globalAlpha = globalAlpha; } } executeStroke(ctx, path) { if (path) { ctx.stroke(path); } else { ctx.stroke(); } } getDefaultGradientFillBBox() { this.cachedDefaultGradientFillBBox ?? (this.cachedDefaultGradientFillBBox = Object.freeze(this.computeDefaultGradientFillBBox())); return this.cachedDefaultGradientFillBBox; } computeDefaultGradientFillBBox() { return; } containsPoint(x, y) { return this.isPointInPath(x, y); } applySvgFillAttributes(element2, defs) { const { fill, fillOpacity } = this; if (typeof fill === "string") { element2.setAttribute("fill", fill); } else if (isGradientFill(fill) && this.fillGradient) { defs ?? (defs = []); const gradient2 = this.fillGradient.toSvg(this.fillBBox ?? this.getBBox()); const id = generateUUID(); gradient2.setAttribute("id", id); defs.push(gradient2); element2.setAttribute("fill", `url(#${id})`); } else if (isPatternFill(fill) && this.fillPattern) { defs ?? (defs = []); const pattern = this.fillPattern.toSvg(); const id = generateUUID(); pattern.setAttribute("id", id); defs.push(pattern); element2.setAttribute("fill", `url(#${id})`); } else if (isImageFill(fill) && this.fillImage) { defs ?? (defs = []); const pixelRatio = this.layerManager?.canvas?.pixelRatio ?? 1; const pattern = this.fillImage.toSvg(this.getBBox(), pixelRatio); const id = generateUUID(); pattern.setAttribute("id", id); defs.push(pattern); element2.setAttribute("fill", `url(#${id})`); } else { element2.setAttribute("fill", "none"); } element2.setAttribute("fill-opacity", String(fillOpacity)); return defs; } applySvgStrokeAttributes(element2) { const { stroke, strokeOpacity, strokeWidth, lineDash, lineDashOffset } = this; setSvgStrokeAttributes(element2, { stroke: isString(stroke) ? stroke : void 0, strokeOpacity, strokeWidth }); setSvgLineDashAttributes(element2, { lineDash, lineDashOffset }); } static handleFillChange(shape) { shape.onFillChange(); } static handleStrokeChange(shape) { shape.onStrokeChange(); } /** * Sets style properties on the shape, optimizing by writing directly to __ prefix fields * where possible to avoid setter overhead. */ setStyleProperties(style2, fillBBox, fillParams) { const opacity = style2?.opacity ?? 1; const fill = style2?.fill; const computedFillOpacity = (style2?.fillOpacity ?? 1) * opacity; const computedStrokeOpacity = (style2?.strokeOpacity ?? 1) * opacity; const computedStrokeWidth = style2?.strokeWidth ?? 0; const computedLineDashOffset = style2?.lineDashOffset ?? 0; let hasDirectChanges = false; if (this.__fillOpacity !== computedFillOpacity) { this.__fillOpacity = computedFillOpacity; hasDirectChanges = true; } if (this.__strokeOpacity !== computedStrokeOpacity) { this.__strokeOpacity = computedStrokeOpacity; hasDirectChanges = true; } if (this.__strokeWidth !== computedStrokeWidth) { this.__strokeWidth = computedStrokeWidth; hasDirectChanges = true; } if (this.__lineDashOffset !== computedLineDashOffset) { this.__lineDashOffset = computedLineDashOffset; hasDirectChanges = true; } if (this.__lineDash !== style2?.lineDash) { this.__lineDash = style2?.lineDash; hasDirectChanges = true; } this.setFillProperties(fill, fillBBox, fillParams); if (fill !== this.fill) { this.fill = fill; } if (style2?.stroke !== this.stroke) { this.stroke = style2?.stroke; } if (hasDirectChanges) { this.markDirty(); } } /** * Sets fill-related properties (fillBBox and fillParams) on the shape. * Used for gradient fills that need bounding box information. */ setFillProperties(fill, fillBBox, fillParams) { const computedFillBBox = fillBBox == null || !isGradientFill(fill) || fill.bounds == null || fill.bounds === "item" ? void 0 : fillBBox[fill.bounds]; let hasDirectChanges = false; if (this.__fillBBox !== computedFillBBox) { this.__fillBBox = computedFillBBox; hasDirectChanges = true; } if (this.__fillParams !== fillParams) { this.__fillParams = fillParams; hasDirectChanges = true; } if (hasDirectChanges) { this.onFillChange(); this.markDirty(); } } }; __decorateClass([ DeclaredSceneChangeDetection() ], _Shape.prototype, "drawingMode", 2); __decorateClass([ DeclaredSceneChangeDetection() ], _Shape.prototype, "fillOpacity", 2); __decorateClass([ DeclaredSceneChangeDetection() ], _Shape.prototype, "strokeOpacity", 2); __decorateClass([ DeclaredSceneObjectChangeDetection({ equals: objectsEqual, changeCb: _Shape.handleFillChange }) ], _Shape.prototype, "fill", 2); __decorateClass([ SceneObjectChangeDetection({ equals: objectsEqual, changeCb: _Shape.handleStrokeChange }) ], _Shape.prototype, "stroke", 2); __decorateClass([ DeclaredSceneChangeDetection() ], _Shape.prototype, "strokeWidth", 2); __decorateClass([ SceneArrayChangeDetection() ], _Shape.prototype, "lineDash", 2); __decorateClass([ DeclaredSceneChangeDetection() ], _Shape.prototype, "lineDashOffset", 2); __decorateClass([ DeclaredSceneChangeDetection() ], _Shape.prototype, "lineCap", 2); __decorateClass([ DeclaredSceneChangeDetection() ], _Shape.prototype, "lineJoin", 2); __decorateClass([ DeclaredSceneChangeDetection() ], _Shape.prototype, "miterLimit", 2); __decorateClass([ DeclaredSceneChangeDetection({ convertor: (v) => clamp(0, v ?? 1, 1) }) ], _Shape.prototype, "opacity", 2); __decorateClass([ SceneObjectChangeDetection({ equals: TRIPLE_EQ, checkDirtyOnAssignment: true }) ], _Shape.prototype, "fillShadow", 2); __decorateClass([ DeclaredSceneObjectChangeDetection({ equals: boxesEqual, changeCb: (s) => s.onFillChange() }) ], _Shape.prototype, "fillBBox", 2); __decorateClass([ DeclaredSceneObjectChangeDetection({ equals: objectsEqual, changeCb: (s) => s.onFillChange() }) ], _Shape.prototype, "fillParams", 2); var Shape = _Shape; // packages/ag-charts-community/src/scene/matrix.ts var IDENTITY_MATRIX_ELEMENTS = [1, 0, 0, 1, 0, 0]; var Matrix = class _Matrix { get e() { return [...this.elements]; } constructor(elements = IDENTITY_MATRIX_ELEMENTS) { this.elements = [...elements]; } setElements(elements) { const e = this.elements; e[0] = elements[0]; e[1] = elements[1]; e[2] = elements[2]; e[3] = elements[3]; e[4] = elements[4]; e[5] = elements[5]; return this; } get identity() { const e = this.elements; return isNumberEqual(e[0], 1) && isNumberEqual(e[1], 0) && isNumberEqual(e[2], 0) && isNumberEqual(e[3], 1) && isNumberEqual(e[4], 0) && isNumberEqual(e[5], 0); } /** * Performs the AxB matrix multiplication and saves the result * to `C`, if given, or to `A` otherwise. */ AxB(A, B, C2) { const a = A[0] * B[0] + A[2] * B[1], b = A[1] * B[0] + A[3] * B[1], c = A[0] * B[2] + A[2] * B[3], d = A[1] * B[2] + A[3] * B[3], e = A[0] * B[4] + A[2] * B[5] + A[4], f = A[1] * B[4] + A[3] * B[5] + A[5]; C2 = C2 ?? A; C2[0] = a; C2[1] = b; C2[2] = c; C2[3] = d; C2[4] = e; C2[5] = f; } /** * The `other` matrix gets post-multiplied to the current matrix. * Returns the current matrix. * @param other */ multiplySelf(other) { this.AxB(this.elements, other.elements); return this; } /** * The `other` matrix gets post-multiplied to the current matrix. * Returns a new matrix. * @param other */ multiply(other) { const elements = [Number.NaN, Number.NaN, Number.NaN, Number.NaN, Number.NaN, Number.NaN]; if (other instanceof _Matrix) { this.AxB(this.elements, other.elements, elements); } else { this.AxB(this.elements, [other.a, other.b, other.c, other.d, other.e, other.f], elements); } return new _Matrix(elements); } preMultiplySelf(other) { this.AxB(other.elements, this.elements, this.elements); return this; } /** * Returns the inverse of this matrix as a new matrix. */ inverse() { const el = this.elements; let a = el[0], b = el[1], c = el[2], d = el[3]; const e = el[4], f = el[5]; const rD = 1 / (a * d - b * c); a *= rD; b *= rD; c *= rD; d *= rD; return new _Matrix([d, -b, -c, a, c * f - d * e, b * e - a * f]); } invertSelf() { const el = this.elements; let a = el[0], b = el[1], c = el[2], d = el[3]; const e = el[4], f = el[5]; const rD = 1 / (a * d - b * c); a *= rD; b *= rD; c *= rD; d *= rD; el[0] = d; el[1] = -b; el[2] = -c; el[3] = a; el[4] = c * f - d * e; el[5] = b * e - a * f; return this; } transformPoint(x, y) { const e = this.elements; return { x: x * e[0] + y * e[2] + e[4], y: x * e[1] + y * e[3] + e[5] }; } transformBBox(bbox, target) { const el = this.elements; const xx = el[0]; const xy = el[1]; const yx = el[2]; const yy = el[3]; const h_w = bbox.width * 0.5; const h_h = bbox.height * 0.5; const cx = bbox.x + h_w; const cy = bbox.y + h_h; const w = Math.abs(h_w * xx) + Math.abs(h_h * yx); const h = Math.abs(h_w * xy) + Math.abs(h_h * yy); target ?? (target = new BBox(0, 0, 0, 0)); target.x = cx * xx + cy * yx + el[4] - w; target.y = cx * xy + cy * yy + el[5] - h; target.width = w + w; target.height = h + h; return target; } toContext(ctx) { if (this.identity) { return; } const e = this.elements; ctx.transform(e[0], e[1], e[2], e[3], e[4], e[5]); } static updateTransformMatrix(matrix, scalingX, scalingY, rotation, translationX, translationY, opts) { const sx = scalingX; const sy = scalingY; let scx; let scy; if (sx === 1 && sy === 1) { scx = 0; scy = 0; } else { scx = opts?.scalingCenterX ?? 0; scy = opts?.scalingCenterY ?? 0; } const r = rotation; const cos = Math.cos(r); const sin = Math.sin(r); let rcx; let rcy; if (r === 0) { rcx = 0; rcy = 0; } else { rcx = opts?.rotationCenterX ?? 0; rcy = opts?.rotationCenterY ?? 0; } const tx = translationX; const ty = translationY; const tx4 = scx * (1 - sx) - rcx; const ty4 = scy * (1 - sy) - rcy; matrix.setElements([ cos * sx, sin * sx, -sin * sy, cos * sy, cos * tx4 - sin * ty4 + rcx + tx, sin * tx4 + cos * ty4 + rcy + ty ]); return matrix; } }; // packages/ag-charts-community/src/scene/transformable.ts function isMatrixTransform(node) { return isMatrixTransformType(node.constructor); } var MATRIX_TRANSFORM_TYPE = Symbol("isMatrixTransform"); function isMatrixTransformType(cstr) { return cstr[MATRIX_TRANSFORM_TYPE] === true; } function MatrixTransform(Parent) { var _a, _b; const ParentNode = Parent; if (isMatrixTransformType(Parent)) { return Parent; } const TRANSFORM_MATRIX = Symbol("matrix_combined_transform"); class MatrixTransformInternal extends ParentNode { constructor() { super(...arguments); this[_b] = new Matrix(); this._dirtyTransform = true; } onChangeDetection(property) { super.onChangeDetection(property); this._dirtyTransform = true; if (this.batchLevel > 0) { return; } this.markDirty("transform"); } updateMatrix(_matrix) { } computeTransformMatrix() { if (!this._dirtyTransform) return; this[TRANSFORM_MATRIX].setElements(IDENTITY_MATRIX_ELEMENTS); this.updateMatrix(this[TRANSFORM_MATRIX]); this._dirtyTransform = false; } toParent(bbox) { this.computeTransformMatrix(); if (this[TRANSFORM_MATRIX].identity) return bbox.clone(); return this[TRANSFORM_MATRIX].transformBBox(bbox); } toParentPoint(x, y) { this.computeTransformMatrix(); if (this[TRANSFORM_MATRIX].identity) return { x, y }; return this[TRANSFORM_MATRIX].transformPoint(x, y); } fromParent(bbox) { this.computeTransformMatrix(); if (this[TRANSFORM_MATRIX].identity) return bbox.clone(); return this[TRANSFORM_MATRIX].inverse().transformBBox(bbox); } fromParentPoint(x, y) { this.computeTransformMatrix(); if (this[TRANSFORM_MATRIX].identity) return { x, y }; return this[TRANSFORM_MATRIX].inverse().transformPoint(x, y); } computeBBox() { const bbox = super.computeBBox(); if (!bbox) return bbox; return this.toParent(bbox); } computeBBoxWithoutTransforms() { return super.computeBBox(); } pickNode(x, y) { ({ x, y } = this.fromParentPoint(x, y)); return super.pickNode(x, y); } pickNodes(x, y, into) { ({ x, y } = this.fromParentPoint(x, y)); return super.pickNodes(x, y, into); } render(renderCtx) { this.computeTransformMatrix(); const { ctx } = renderCtx; const matrix = this[TRANSFORM_MATRIX]; let performRestore = false; try { if (!matrix.identity) { ctx.save(); performRestore = true; matrix.toContext(ctx); } super.render(renderCtx); } finally { if (performRestore) { ctx.restore(); } } } toSVG() { this.computeTransformMatrix(); const svg = super.toSVG(); const matrix = this[TRANSFORM_MATRIX]; if (matrix.identity || svg == null) return svg; const g = createSvgElement("g"); g.append(...svg.elements); const [a, b, c, d, e, f] = matrix.e; g.setAttribute("transform", `matrix(${a} ${b} ${c} ${d} ${e} ${f})`); return { elements: [g], defs: svg.defs }; } } _a = MATRIX_TRANSFORM_TYPE, _b = TRANSFORM_MATRIX; MatrixTransformInternal[_a] = true; return MatrixTransformInternal; } function Rotatable(Parent) { var _a; const ParentNode = Parent; const ROTATABLE_MATRIX = Symbol("matrix_rotation"); class RotatableInternal extends MatrixTransform(ParentNode) { constructor() { super(...arguments); this[_a] = new Matrix(); this.rotationCenterX = 0; this.rotationCenterY = 0; this.rotation = 0; } updateMatrix(matrix) { super.updateMatrix(matrix); const { rotation, rotationCenterX, rotationCenterY } = this; if (rotation === 0) return; Matrix.updateTransformMatrix(this[ROTATABLE_MATRIX], 1, 1, rotation, 0, 0, { rotationCenterX, rotationCenterY }); matrix.multiplySelf(this[ROTATABLE_MATRIX]); } } _a = ROTATABLE_MATRIX; __decorateClass([ SceneChangeDetection() ], RotatableInternal.prototype, "rotationCenterX", 2); __decorateClass([ SceneChangeDetection() ], RotatableInternal.prototype, "rotationCenterY", 2); __decorateClass([ SceneChangeDetection() ], RotatableInternal.prototype, "rotation", 2); return RotatableInternal; } function isScalable(node) { return "scalingX" in node && "scalingY" in node && "scalingCenterX" in node && "scalingCenterY" in node; } function Scalable(Parent) { var _a; const ParentNode = Parent; const SCALABLE_MATRIX = Symbol("matrix_scale"); class ScalableInternal extends MatrixTransform(ParentNode) { constructor() { super(...arguments); this[_a] = new Matrix(); this.scalingX = 1; this.scalingY = 1; this.scalingCenterX = 0; this.scalingCenterY = 0; } // optimised field accessor updateMatrix(matrix) { super.updateMatrix(matrix); const { scalingX, scalingY, scalingCenterX, scalingCenterY } = this; if (scalingX === 1 && scalingY === 1) return; Matrix.updateTransformMatrix(this[SCALABLE_MATRIX], scalingX, scalingY, 0, 0, 0, { scalingCenterX, scalingCenterY }); matrix.multiplySelf(this[SCALABLE_MATRIX]); } /** * Optimised reset for animation hot paths. * Bypasses SceneChangeDetection decorators by writing directly to backing fields. */ resetScalingProperties(scalingX, scalingY, scalingCenterX, scalingCenterY) { this.__scalingX = scalingX; this.__scalingY = scalingY; this.__scalingCenterX = scalingCenterX; this.__scalingCenterY = scalingCenterY; this.onChangeDetection("scaling"); } } _a = SCALABLE_MATRIX; __decorateClass([ SceneChangeDetection() ], ScalableInternal.prototype, "scalingX", 2); __decorateClass([ SceneChangeDetection() ], ScalableInternal.prototype, "scalingY", 2); __decorateClass([ SceneChangeDetection() ], ScalableInternal.prototype, "scalingCenterX", 2); __decorateClass([ SceneChangeDetection() ], ScalableInternal.prototype, "scalingCenterY", 2); return ScalableInternal; } function Translatable(Parent) { var _a; const ParentNode = Parent; const TRANSLATABLE_MATRIX = Symbol("matrix_translation"); class TranslatableInternal extends MatrixTransform(ParentNode) { constructor() { super(...arguments); this[_a] = new Matrix(); this.translationX = 0; this.translationY = 0; } updateMatrix(matrix) { super.updateMatrix(matrix); const { translationX, translationY } = this; if (translationX === 0 && translationY === 0) return; Matrix.updateTransformMatrix(this[TRANSLATABLE_MATRIX], 1, 1, 0, translationX, translationY); matrix.multiplySelf(this[TRANSLATABLE_MATRIX]); } } _a = TRANSLATABLE_MATRIX; __decorateClass([ SceneChangeDetection() ], TranslatableInternal.prototype, "translationX", 2); __decorateClass([ SceneChangeDetection() ], TranslatableInternal.prototype, "translationY", 2); return TranslatableInternal; } var Transformable = class { /** * Converts a BBox from canvas coordinate space into the coordinate space of the given Node. */ static fromCanvas(node, bbox) { const parents = []; for (const parent of node.traverseUp()) { if (isMatrixTransform(parent)) { parents.unshift(parent); } } for (const parent of parents) { bbox = parent.fromParent(bbox); } if (isMatrixTransform(node)) { bbox = node.fromParent(bbox); } return bbox; } /** * Converts a Nodes BBox (or an arbitrary BBox if supplied) from local Node coordinate space * into the Canvas coordinate space. */ static toCanvas(node, bbox) { if (bbox == null) { bbox = node.getBBox(); } else if (isMatrixTransform(node)) { bbox = node.toParent(bbox); } for (const parent of node.traverseUp()) { if (isMatrixTransform(parent)) { bbox = parent.toParent(bbox); } } return bbox; } /** * Converts a point from canvas coordinate space into the coordinate space of the given Node. */ static fromCanvasPoint(node, x, y) { const parents = []; for (const parent of node.traverseUp()) { if (isMatrixTransform(parent)) { parents.unshift(parent); } } for (const parent of parents) { ({ x, y } = parent.fromParentPoint(x, y)); } if (isMatrixTransform(node)) { ({ x, y } = node.fromParentPoint(x, y)); } return { x, y }; } /** * Converts a point from a Nodes local coordinate space into the Canvas coordinate space. */ static toCanvasPoint(node, x, y) { if (isMatrixTransform(node)) { ({ x, y } = node.toParentPoint(x, y)); } for (const parent of node.traverseUp()) { if (isMatrixTransform(parent)) { ({ x, y } = parent.toParentPoint(x, y)); } } return { x, y }; } }; // packages/ag-charts-community/src/scene/group.ts var sharedOffscreenCanvas; var _Group = class _Group extends Node { // optimizeForInfrequentRedraws: true constructor(opts) { super(opts); this.childNodes = /* @__PURE__ */ new Set(); this.dirty = false; this.dirtyZIndex = false; this.clipRect = void 0; this.opacity = 1; // Used when renderToOffscreenCanvas: true this.layer = void 0; // optimizeForInfrequentRedraws: false this.image = void 0; this._lastWidth = Number.NaN; this._lastHeight = Number.NaN; this._lastDevicePixelRatio = Number.NaN; this.isContainerNode = true; this.renderToOffscreenCanvas = opts?.renderToOffscreenCanvas === true; this.optimizeForInfrequentRedraws = opts?.optimizeForInfrequentRedraws === true; } static is(value) { return value instanceof _Group; } static computeChildrenBBox(nodes, skipInvisible = true) { return BBox.merge(Node.extractBBoxes(nodes, skipInvisible)); } static compareChildren(a, b) { return compareZIndex(a.__zIndex, b.__zIndex) || a.serialNumber - b.serialNumber; } // We consider a group to be boundless, thus any point belongs to it. containsPoint(_x, _y) { return true; } computeBBox() { return _Group.computeChildrenBBox(this.children()); } computeSafeClippingBBox(pixelRatio) { const bbox = this.computeBBox(); if (bbox?.isFinite() !== true) return; let strokeWidth = 0; const strokeMiterAmount = 4; for (const child of this.descendants()) { if (child instanceof Shape) { strokeWidth = Math.max(strokeWidth, child.strokeWidth); } } const padding2 = Math.max( // Account for anti-aliasing artefacts 1, // Account for strokes (incl. miters) - this may not be the best place to include this strokeWidth / 2 * strokeMiterAmount ); const { x: originX, y: originY } = Transformable.toCanvasPoint(this, 0, 0); const x = alignBefore(pixelRatio, originX + bbox.x - padding2) - originX; const y = alignBefore(pixelRatio, originY + bbox.y - padding2) - originY; const width2 = Math.ceil(bbox.x + bbox.width - x + padding2); const height2 = Math.ceil(bbox.y + bbox.height - y + padding2); return new BBox(x, y, width2, height2); } prepareSharedCanvas(width2, height2, pixelRatio) { if (sharedOffscreenCanvas?.pixelRatio === pixelRatio) { sharedOffscreenCanvas.resize(width2, height2, pixelRatio); } else { sharedOffscreenCanvas = new HdpiOffscreenCanvas({ width: width2, height: height2, pixelRatio }); } return sharedOffscreenCanvas; } setScene(scene) { const previousScene = this.scene; super.setScene(scene); if (this.layer && previousScene && previousScene !== scene) { previousScene.layersManager.removeLayer(this.layer); this.layer = void 0; } for (const child of this.children()) { child.setScene(scene); } } markDirty(property) { this.dirty = true; super.markDirty(property); } markDirtyChildrenOrder() { super.markDirtyChildrenOrder(); this.dirtyZIndex = true; this.markDirty(); } /** * Appends one or more new node instances to this parent. * If one needs to: * - move a child to the end of the list of children * - move a child from one parent to another (including parents in other scenes) * one should use the {@link insertBefore} method instead. * @param nodes A node or nodes to append. */ append(nodes) { for (const node of toIterable(nodes)) { node.remove(); this.childNodes.add(node); node.parentNode = this; node.setScene(this.scene); } this.markDirtyChildrenOrder(); this.markDirty(); } appendChild(node) { this.append(node); return node; } removeChild(node) { if (!this.childNodes?.delete(node)) { throw new Error( `AG Charts - internal error, unknown child node ${node.name ?? node.id} in $${this.name ?? this.id}` ); } node.parentNode = void 0; node.setScene(); this.markDirtyChildrenOrder(); this.markDirty(); } clear() { for (const child of this.children()) { delete child.parentNode; child.setScene(); } this.childNodes?.clear(); this.markDirty(); } /** * Hit testing method. * Recursively checks if the given point is inside this node or any of its children. * Returns the first matching node or `undefined`. * Nodes that render later (show on top) are hit tested first. */ pickNode(x, y) { if (!this.visible || this.pointerEvents === 1 /* None */ || !this.containsPoint(x, y)) { return; } if (this.childNodes != null && this.childNodes.size !== 0) { const children = [...this.children()]; for (let i = children.length - 1; i >= 0; i--) { const child = children[i]; const hit = child.pickNode(x, y); if (hit != null) { return hit; } } } else if (!this.isContainerNode) { return this; } } pickNodes(x, y, into = []) { if (!this.visible || this.pointerEvents === 1 /* None */ || !this.containsPoint(x, y)) { return into; } if (!this.isContainerNode) { into.push(this); } for (const child of this.children()) { child.pickNodes(x, y, into); } return into; } isDirty(renderCtx) { const { width: width2, height: height2, devicePixelRatio } = renderCtx; const { dirty, layer } = this; const layerResized = layer != null && (this._lastWidth !== width2 || this._lastHeight !== height2); const pixelRatioChanged = this._lastDevicePixelRatio !== devicePixelRatio; this._lastWidth = width2; this._lastHeight = height2; this._lastDevicePixelRatio = devicePixelRatio; return dirty || layerResized || pixelRatioChanged; } preRender(renderCtx) { let counts; if (this.dirty) { counts = super.preRender(renderCtx, 0); for (const child of this.children()) { const childCounts = child.preRender(renderCtx); counts.groups += childCounts.groups; counts.nonGroups += childCounts.nonGroups; counts.complexity += childCounts.complexity; } counts.groups += 1; counts.nonGroups -= 1; } else { counts = this.childNodeCounts; } if (this.renderToOffscreenCanvas && !this.optimizeForInfrequentRedraws && counts.nonGroups > 0 && this.getVisibility()) { this.layer ?? (this.layer = this.layerManager?.addLayer({ name: this.name })); } else if (this.layer != null) { this.layerManager?.removeLayer(this.layer); this.layer = void 0; } return counts; } render(renderCtx) { const { layer, renderToOffscreenCanvas } = this; const childRenderCtx = { ...renderCtx }; const dirty = this.isDirty(renderCtx); this.dirty = false; if (!renderToOffscreenCanvas) { this.renderInContext(childRenderCtx); super.render(childRenderCtx); return; } const { ctx, stats, devicePixelRatio: pixelRatio } = renderCtx; let { image } = this; if (dirty) { image?.bitmap.close(); image = void 0; const bbox = layer ? void 0 : this.computeSafeClippingBBox(pixelRatio); const renderOffscreen = (offscreenCanvas, ...transform) => { const offscreenCtx = offscreenCanvas.context; childRenderCtx.ctx = offscreenCtx; offscreenCanvas.clear(); offscreenCtx.save(); try { offscreenCtx.setTransform(...transform); offscreenCtx.globalAlpha = 1; this.renderInContext(childRenderCtx); } finally { offscreenCtx.restore(); offscreenCtx.verifyDepthZero?.(); } }; if (layer) { renderOffscreen(layer, ctx.getTransform()); } else if (bbox) { const { x, y, width: width2, height: height2 } = bbox; const scaledWidth = Math.floor(width2 * pixelRatio); const scaledHeight = Math.floor(height2 * pixelRatio); if (scaledWidth > 0 && scaledHeight > 0) { const canvas = this.prepareSharedCanvas(width2, height2, pixelRatio); renderOffscreen(canvas, pixelRatio, 0, 0, pixelRatio, -x * pixelRatio, -y * pixelRatio); image = { bitmap: canvas.transferToImageBitmap(), x, y, width: width2, height: height2 }; } } this.image = image; if (stats) stats.layersRendered++; } else if (stats) { stats.layersSkipped++; } const { globalAlpha } = ctx; ctx.globalAlpha = globalAlpha * this.opacity; if (layer) { ctx.save(); try { ctx.resetTransform(); layer.drawImage(ctx); } finally { ctx.restore(); } } else if (image) { const { bitmap, x, y, width: width2, height: height2 } = image; ctx.drawImage(bitmap, 0, 0, width2 * pixelRatio, height2 * pixelRatio, x, y, width2, height2); } ctx.globalAlpha = globalAlpha; super.render(childRenderCtx); } applyClip(ctx, clipRect) { const { x, y, width: width2, height: height2 } = clipRect; ctx.beginPath(); ctx.rect(x, y, width2, height2); ctx.clip(); } renderInContext(childRenderCtx) { const { ctx, stats } = childRenderCtx; if (this.dirtyZIndex) { this.sortChildren(_Group.compareChildren); this.dirtyZIndex = false; } ctx.save(); try { ctx.globalAlpha *= this.opacity; if (this.clipRect != null) { this.applyClip(ctx, this.clipRect); childRenderCtx.clipBBox = Transformable.toCanvas(this, this.clipRect); } for (const child of this.children()) { if (!child.visible) { if (stats) { stats.nodesSkipped += child.childNodeCounts.nonGroups + child.childNodeCounts.groups; stats.opsSkipped += child.childNodeCounts.complexity; } continue; } child.isolatedRender(childRenderCtx); } } finally { ctx.restore(); } } sortChildren(compareFn) { if (!this.childNodes) return; const sortedChildren = [...this.childNodes].sort(compareFn); this.childNodes.clear(); for (const child of sortedChildren) { this.childNodes.add(child); } } *children() { yield* this.childNodes; } *excludeChildren(exclude) { for (const child of this.children()) { if (exclude.instance && !(child instanceof exclude.instance) || exclude.name && child.name !== exclude.name) { yield child; } } } *descendants() { for (const child of this.children()) { yield child; if (child instanceof _Group) { yield* child.descendants(); } } } /** * Transforms bbox given in the canvas coordinate space to bbox in this group's coordinate space and * sets this group's clipRect to the transformed bbox. * @param bbox clipRect bbox in the canvas coordinate space. */ setClipRect(bbox) { this.clipRect = bbox ? Transformable.fromCanvas(this, bbox) : void 0; } /** * Set the clip rect within the canvas coordinate space. * @param bbox clipRect bbox in the canvas coordinate space. */ setClipRectCanvasSpace(bbox) { this.clipRect = bbox; } getVisibility() { for (const node of this.traverseUp(true)) { if (!node.visible) { return false; } } return true; } toSVG() { if (!this.visible) return; const defs = []; const elements = []; for (const child of this.children()) { const svg = child.toSVG(); if (svg != null) { elements.push(...svg.elements); if (svg.defs != null) { defs.push(...svg.defs); } } } return { elements, defs }; } }; _Group.className = "Group"; __decorateClass([ SceneChangeDetection({ convertor: (v) => clamp(0, v, 1) }) ], _Group.prototype, "opacity", 2); var Group = _Group; var ScalableGroup = class extends Scalable(Group) { }; var RotatableGroup = class extends Rotatable(Group) { }; var TranslatableGroup = class extends Translatable(Group) { }; var TransformableGroup = class extends Rotatable(Translatable(Group)) { }; // packages/ag-charts-community/src/scene/selection.ts var Selection = class _Selection { constructor(parentNode, classOrFactory, autoCleanup = true) { this.parentNode = parentNode; this.autoCleanup = autoCleanup; this.garbageBin = /* @__PURE__ */ new Set(); this._nodesMap = /* @__PURE__ */ new Map(); this._nodes = []; this.data = []; this.debug = debugLogger_exports.create(true, "scene", "scene:selections"); this.nodeFactory = Object.prototype.isPrototypeOf.call(Node, classOrFactory) ? () => new classOrFactory() : classOrFactory; } static select(parent, classOrFactory, garbageCollection = true) { return new _Selection(parent, classOrFactory, garbageCollection); } static selectAll(parent, predicate) { const results = []; const traverse = (node) => { if (predicate(node)) { results.push(node); } if (node instanceof Group) { for (const child of node.children()) { traverse(child); } } }; traverse(parent); return results; } static selectByClass(node, ...Classes) { return _Selection.selectAll(node, (n) => Classes.some((C2) => n instanceof C2)); } static selectByTag(node, tag) { return _Selection.selectAll(node, (n) => n.tag === tag); } createNode(datum, initializer, idx) { const node = this.nodeFactory(datum); node.datum = datum; initializer?.(node); if (idx == null) { this._nodes.push(node); } else { this._nodes.splice(idx, 0, node); } this.parentNode.appendChild(node); return node; } /** * Update the data in a selection. If an `getDatumId()` function is provided, maintain a list of ids related to * the nodes. Otherwise, take the more efficient route of simply creating and destroying nodes at the end * of the array. */ update(data, initializer, getDatumId) { if (this.garbageBin.size > 0) { this.debug(`Selection - update() called with pending garbage`, data); } if (getDatumId && this._nodesMap.size === 0 && this._nodes.length > 0) { for (const node of this._nodes) { this.garbageBin.add(node); } } if (!getDatumId && this._nodesMap.size > 0) { this._nodesMap.clear(); } if (getDatumId) { const dataMap = /* @__PURE__ */ new Map(); const duplicateMap = /* @__PURE__ */ new Map(); for (let idx = 0; idx < data.length; idx++) { const datum = data[idx]; let id = getDatumId(datum); if (dataMap.has(id)) { const index = (duplicateMap.get(id) ?? 0) + 1; duplicateMap.set(id, index); id = `${id}:${index}`; } dataMap.set(id, idx); } for (const [node, datumId] of this._nodesMap.entries()) { const idx = dataMap.get(datumId); if (idx == null) { this.garbageBin.add(node); } else { node.datum = data[idx]; this.garbageBin.delete(node); dataMap.delete(datumId); } } for (const [datumId, idx] of dataMap.entries()) { const datum = data[idx]; this._nodesMap.set(this.createNode(datum, initializer, idx), datumId); } } else { const maxLength = Math.max(data.length, this.data.length); for (let i = 0; i < maxLength; i++) { if (i >= data.length) { this.garbageBin.add(this._nodes[i]); } else if (i >= this._nodes.length) { this.createNode(data[i], initializer); } else { this._nodes[i].datum = data[i]; this.garbageBin.delete(this._nodes[i]); } } } this.data = data.slice(); if (this.autoCleanup) { this.cleanup(); } return this; } cleanup() { if (this.garbageBin.size === 0) { return this; } const selection = this; function removeGarbage(node) { if (selection.garbageBin.has(node)) { selection._nodesMap.delete(node); selection.garbageBin.delete(node); node.destroy(); return false; } return true; } this._nodes = this._nodes.filter(removeGarbage); return this; } clear() { this.update([]); for (const node of this._nodesMap.keys()) { this.garbageBin.add(node); } this._nodesMap.clear(); return this; } isGarbage(node) { return this.garbageBin.has(node); } each(iterate2) { const nodes = this._nodes; this.parentNode.batchedUpdate(function selectionEach() { for (const entry of nodes.entries()) { iterate2(entry[1], entry[1].datum, entry[0]); } }); return this; } *[Symbol.iterator]() { for (let index = 0; index < this._nodes.length; index++) { const node = this._nodes[index]; yield { node, datum: node.datum, index }; } } select(predicate) { return _Selection.selectAll(this.parentNode, predicate); } selectByClass(Class) { return _Selection.selectByClass(this.parentNode, Class); } selectByTag(tag) { return _Selection.selectByTag(this.parentNode, tag); } nodes() { return this._nodes; } at(index) { return this._nodes.at(index); } get length() { return this._nodes.length; } batchedUpdate(fn) { this.parentNode.batchedUpdate(fn); } }; // packages/ag-charts-community/src/scene/util/corner.ts var drawCorner = (path, { x0, y0, x1, y1, cx, cy }, cornerRadius, move) => { if (move) { path.moveTo(x0, y0); } if (x0 !== x1 || y0 !== y1) { const r0 = Math.atan2(y0 - cy, x0 - cx); const r1 = Math.atan2(y1 - cy, x1 - cx); path.arc(cx, cy, cornerRadius, r0, r1); } else { path.lineTo(x0, y0); } }; // packages/ag-charts-community/src/scene/shape/path.ts var Path = class extends Shape { constructor() { super(...arguments); /** * Declare a path to retain for later rendering and hit testing * using custom Path2D class. Think of it as a TypeScript version * of the native Path2D (with some differences) that works in all browsers. */ this.path = new ExtendedPath2D(); this._clipX = Number.NaN; this._clipY = Number.NaN; this.clip = false; /** * The path only has to be updated when certain attributes change. * For example, if transform attributes (such as `translationX`) * are changed, we don't have to update the path. The `dirtyPath` flag * is how we keep track if the path has to be updated or not. */ this._dirtyPath = true; this.lastPixelRatio = Number.NaN; } set clipX(value) { this._clipX = value; this.dirtyPath = true; } set clipY(value) { this._clipY = value; this.dirtyPath = true; } set dirtyPath(value) { if (this._dirtyPath !== value) { this._dirtyPath = value; if (value) { this.markDirty("path"); } } } get dirtyPath() { return this._dirtyPath; } checkPathDirty() { if (this._dirtyPath) { return; } this.dirtyPath = this.path.isDirty() || (this.fillShadow?.isDirty() ?? false) || (this._clipPath?.isDirty() ?? false); } resetPathDirty() { this.path.clear(true); this._dirtyPath = false; } isPathDirty() { return this.path.isDirty(); } onChangeDetection(property) { if (!this._dirtyPath) { this._dirtyPath = true; super.onChangeDetection(property); } } computeBBox() { this.updatePathIfDirty(); return this.path.computeBBox(); } isPointInPath(x, y) { this.updatePathIfDirty(); return this.path.closedPath && this.path.isPointInPath(x, y); } distanceSquared(x, y) { return this.distanceSquaredTransformedPoint(x, y); } svgPathData(transform) { this.updatePathIfDirty(); return this.path.toSVG(transform); } distanceSquaredTransformedPoint(x, y) { this.updatePathIfDirty(); if (this.path.closedPath && this.path.isPointInPath(x, y)) { return 0; } return this.path.distanceSquared(x, y); } isDirtyPath() { return false; } updatePath() { } updatePathIfDirty() { if (this.dirtyPath || this.isDirtyPath()) { this.updatePath(); this.dirtyPath = false; } } preRender(renderCtx) { if (renderCtx.devicePixelRatio !== this.lastPixelRatio) { this.dirtyPath = true; } this.lastPixelRatio = renderCtx.devicePixelRatio; this.updatePathIfDirty(); return super.preRender(renderCtx, this.path.commands.length); } render(renderCtx) { const { ctx } = renderCtx; if (this.clip && !Number.isNaN(this._clipX) && !Number.isNaN(this._clipY)) { ctx.save(); try { const margin = this.strokeWidth / 2; this._clipPath ?? (this._clipPath = new ExtendedPath2D()); this._clipPath.clear(); this._clipPath.rect(-margin, -margin, this._clipX + margin, this._clipY + margin + margin); ctx.clip(this._clipPath?.getPath2D()); if (this._clipX > 0 && this._clipY > 0) { this.drawPath(ctx); } } finally { ctx.restore(); } } else { this._clipPath = void 0; this.drawPath(ctx); } this.fillShadow?.markClean(); super.render(renderCtx); } drawPath(ctx) { this.fillStroke(ctx, this.path.getPath2D()); } toSVG() { if (!this.visible) return; const element2 = createSvgElement("path"); element2.setAttribute("d", this.svgPathData()); const defs = this.applySvgFillAttributes(element2, []); this.applySvgStrokeAttributes(element2); return { elements: [element2], defs }; } }; Path.className = "Path"; __decorateClass([ SceneChangeDetection() ], Path.prototype, "clip", 2); __decorateClass([ SceneChangeDetection() ], Path.prototype, "clipX", 1); __decorateClass([ SceneChangeDetection() ], Path.prototype, "clipY", 1); // packages/ag-charts-community/src/scene/shape/rect.ts function cornerEdges(leadingEdge, trailingEdge, leadingInset, trailingInset, cornerRadius) { let leadingClipped = false; let trailingClipped = false; let leading0 = trailingInset - Math.sqrt(Math.max(cornerRadius ** 2 - leadingInset ** 2, 0)); let leading1 = 0; let trailing0 = 0; let trailing1 = leadingInset - Math.sqrt(Math.max(cornerRadius ** 2 - trailingInset ** 2, 0)); if (leading0 > leadingEdge) { leadingClipped = true; leading0 = leadingEdge; leading1 = leadingInset - Math.sqrt(Math.max(cornerRadius ** 2 - (trailingInset - leadingEdge) ** 2)); } else if (isNumberEqual(leading0, 0)) { leading0 = 0; } if (trailing1 > trailingEdge) { trailingClipped = true; trailing0 = trailingInset - Math.sqrt(Math.max(cornerRadius ** 2 - (leadingInset - trailingEdge) ** 2)); trailing1 = trailingEdge; } else if (isNumberEqual(trailing1, 0)) { trailing1 = 0; } return { leading0, leading1, trailing0, trailing1, leadingClipped, trailingClipped }; } function clippedRoundRect(path, x, y, width2, height2, cornerRadii, clipBBox) { let { topLeft: topLeftCornerRadius, topRight: topRightCornerRadius, bottomRight: bottomRightCornerRadius, bottomLeft: bottomLeftCornerRadius } = cornerRadii; const maxVerticalCornerRadius = Math.max( topLeftCornerRadius + bottomLeftCornerRadius, topRightCornerRadius + bottomRightCornerRadius ); const maxHorizontalCornerRadius = Math.max( topLeftCornerRadius + topRightCornerRadius, bottomLeftCornerRadius + bottomRightCornerRadius ); if (maxVerticalCornerRadius <= 0 && maxHorizontalCornerRadius <= 0) { if (clipBBox == null) { path.rect(x, y, width2, height2); } else { const x0 = Math.max(x, clipBBox.x); const x1 = Math.min(x + width2, clipBBox.x + clipBBox.width); const y0 = Math.max(y, clipBBox.y); const y1 = Math.min(y + height2, clipBBox.y + clipBBox.height); path.rect(x0, y0, x1 - x0, y1 - y0); } return; } else if (clipBBox == null && topLeftCornerRadius === topRightCornerRadius && topLeftCornerRadius === bottomRightCornerRadius && topLeftCornerRadius === bottomLeftCornerRadius) { path.roundRect(x, y, width2, height2, topLeftCornerRadius); return; } if (width2 < 0) { x += width2; width2 = Math.abs(width2); } if (height2 < 0) { y += height2; height2 = Math.abs(height2); } if (width2 <= 0 || height2 <= 0) return; if (clipBBox == null) { clipBBox = new BBox(x, y, width2, height2); } else { const x0 = Math.max(x, clipBBox.x); const x1 = Math.min(x + width2, clipBBox.x + clipBBox.width); const y0 = Math.max(y, clipBBox.y); const y1 = Math.min(y + height2, clipBBox.y + clipBBox.height); clipBBox = new BBox(x0, y0, x1 - x0, y1 - y0); } const borderScale = Math.max(maxVerticalCornerRadius / height2, maxHorizontalCornerRadius / width2, 1); if (borderScale > 1) { topLeftCornerRadius /= borderScale; topRightCornerRadius /= borderScale; bottomRightCornerRadius /= borderScale; bottomLeftCornerRadius /= borderScale; } let drawTopLeftCorner = true; let drawTopRightCorner = true; let drawBottomRightCorner = true; let drawBottomLeftCorner = true; let topLeftCorner; let topRightCorner; let bottomRightCorner; let bottomLeftCorner; if (drawTopLeftCorner) { const nodes = cornerEdges( clipBBox.height, clipBBox.width, Math.max(x + topLeftCornerRadius - clipBBox.x, 0), Math.max(y + topLeftCornerRadius - clipBBox.y, 0), topLeftCornerRadius ); if (nodes.leadingClipped) drawBottomLeftCorner = false; if (nodes.trailingClipped) drawTopRightCorner = false; const x0 = Math.max(clipBBox.x + nodes.leading1, clipBBox.x); const y0 = Math.max(clipBBox.y + nodes.leading0, clipBBox.y); const x1 = Math.max(clipBBox.x + nodes.trailing1, clipBBox.x); const y1 = Math.max(clipBBox.y + nodes.trailing0, clipBBox.y); const cx = x + topLeftCornerRadius; const cy = y + topLeftCornerRadius; topLeftCorner = { x0, y0, x1, y1, cx, cy }; } if (drawTopRightCorner) { const nodes = cornerEdges( clipBBox.width, clipBBox.height, Math.max(y + topRightCornerRadius - clipBBox.y, 0), Math.max(clipBBox.x + clipBBox.width - (x + width2 - topRightCornerRadius), 0), topRightCornerRadius ); if (nodes.leadingClipped) drawTopLeftCorner = false; if (nodes.trailingClipped) drawBottomRightCorner = false; const x0 = Math.min(clipBBox.x + clipBBox.width - nodes.leading0, clipBBox.x + clipBBox.width); const y0 = Math.max(clipBBox.y + nodes.leading1, clipBBox.y); const x1 = Math.min(clipBBox.x + clipBBox.width - nodes.trailing0, clipBBox.x + clipBBox.width); const y1 = Math.max(clipBBox.y + nodes.trailing1, clipBBox.y); const cx = x + width2 - topRightCornerRadius; const cy = y + topRightCornerRadius; topRightCorner = { x0, y0, x1, y1, cx, cy }; } if (drawBottomRightCorner) { const nodes = cornerEdges( clipBBox.height, clipBBox.width, Math.max(clipBBox.x + clipBBox.width - (x + width2 - bottomRightCornerRadius), 0), Math.max(clipBBox.y + clipBBox.height - (y + height2 - bottomRightCornerRadius), 0), bottomRightCornerRadius ); if (nodes.leadingClipped) drawTopRightCorner = false; if (nodes.trailingClipped) drawBottomLeftCorner = false; const x0 = Math.min(clipBBox.x + clipBBox.width - nodes.leading1, clipBBox.x + clipBBox.width); const y0 = Math.min(clipBBox.y + clipBBox.height - nodes.leading0, clipBBox.y + clipBBox.height); const x1 = Math.min(clipBBox.x + clipBBox.width - nodes.trailing1, clipBBox.x + clipBBox.width); const y1 = Math.min(clipBBox.y + clipBBox.height - nodes.trailing0, clipBBox.y + clipBBox.height); const cx = x + width2 - bottomRightCornerRadius; const cy = y + height2 - bottomRightCornerRadius; bottomRightCorner = { x0, y0, x1, y1, cx, cy }; } if (drawBottomLeftCorner) { const nodes = cornerEdges( clipBBox.width, clipBBox.height, Math.max(clipBBox.y + clipBBox.height - (y + height2 - bottomLeftCornerRadius), 0), Math.max(x + bottomLeftCornerRadius - clipBBox.x, 0), bottomLeftCornerRadius ); if (nodes.leadingClipped) drawBottomRightCorner = false; if (nodes.trailingClipped) drawTopLeftCorner = false; const x0 = Math.max(clipBBox.x + nodes.leading0, clipBBox.x); const y0 = Math.min(clipBBox.y + clipBBox.height - nodes.leading1, clipBBox.y + clipBBox.height); const x1 = Math.max(clipBBox.x + nodes.trailing0, clipBBox.x); const y1 = Math.min(clipBBox.y + clipBBox.height - nodes.trailing1, clipBBox.y + clipBBox.height); const cx = x + bottomLeftCornerRadius; const cy = y + height2 - bottomLeftCornerRadius; bottomLeftCorner = { x0, y0, x1, y1, cx, cy }; } let didMove = false; if (drawTopLeftCorner && topLeftCorner != null) { drawCorner(path, topLeftCorner, topLeftCornerRadius, !didMove); didMove || (didMove = true); } if (drawTopRightCorner && topRightCorner != null) { drawCorner(path, topRightCorner, topRightCornerRadius, !didMove); didMove || (didMove = true); } if (drawBottomRightCorner && bottomRightCorner != null) { drawCorner(path, bottomRightCorner, bottomRightCornerRadius, !didMove); didMove || (didMove = true); } if (drawBottomLeftCorner && bottomLeftCorner != null) { drawCorner(path, bottomLeftCorner, bottomLeftCornerRadius, !didMove); } path.closePath(); } var Rect = class extends Path { constructor() { super(...arguments); this.borderPath = new ExtendedPath2D(); this.x = 0; this.y = 0; this.width = 10; this.height = 10; this.topLeftCornerRadius = 0; this.topRightCornerRadius = 0; this.bottomRightCornerRadius = 0; this.bottomLeftCornerRadius = 0; this.clipBBox = void 0; this.crisp = false; this.lastUpdatePathStrokeWidth = this.__strokeWidth; this.effectiveStrokeWidth = this.__strokeWidth; this.hittester = super.isPointInPath.bind(this); this.distanceCalculator = super.distanceSquaredTransformedPoint.bind(this); /** * When the rectangle's width or height is less than a pixel * and crisp mode is on, the rectangle will still fit into the pixel, * but will be less opaque to make an effect of holding less space. */ this.microPixelEffectOpacity = 1; } // optimised field accessor set cornerRadius(cornerRadius) { this.topLeftCornerRadius = cornerRadius; this.topRightCornerRadius = cornerRadius; this.bottomRightCornerRadius = cornerRadius; this.bottomLeftCornerRadius = cornerRadius; } isDirtyPath() { return this.lastUpdatePathStrokeWidth !== this.__strokeWidth || Boolean(this.path.isDirty() || this.borderPath.isDirty()); } updatePath() { const { path, borderPath, __crisp: crisp, __topLeftCornerRadius: topLeft, __topRightCornerRadius: topRight, __bottomRightCornerRadius: bottomRight, __bottomLeftCornerRadius: bottomLeft } = this; let { __x: x, __y: y, __width: w, __height: h, __strokeWidth: strokeWidth, __clipBBox: clipBBox } = this; const pixelRatio = this.layerManager?.canvas.pixelRatio ?? 1; const pixelSize = 1 / pixelRatio; let microPixelEffectOpacity = 1; path.clear(); borderPath.clear(); if (w === 0 || h === 0) { this.effectiveStrokeWidth = 0; this.lastUpdatePathStrokeWidth = 0; this.microPixelEffectOpacity = 0; return; } if (crisp) { if (w <= pixelSize) { microPixelEffectOpacity *= w / pixelSize; } if (h <= pixelSize) { microPixelEffectOpacity *= h / pixelSize; } w = this.align(x, w); h = this.align(y, h); x = this.align(x); y = this.align(y); clipBBox = clipBBox == null ? void 0 : new BBox( this.align(clipBBox.x), this.align(clipBBox.y), this.align(clipBBox.x, clipBBox.width), this.align(clipBBox.y, clipBBox.height) ); } if (strokeWidth) { if (w < pixelSize) { const lx = x + pixelSize / 2; borderPath.moveTo(lx, y); borderPath.lineTo(lx, y + h); strokeWidth = pixelSize; this.borderClipPath = void 0; } else if (h < pixelSize) { const ly = y + pixelSize / 2; borderPath.moveTo(x, ly); borderPath.lineTo(x + w, ly); strokeWidth = pixelSize; this.borderClipPath = void 0; } else if (strokeWidth < w && strokeWidth < h) { const halfStrokeWidth = strokeWidth / 2; x += halfStrokeWidth; y += halfStrokeWidth; w -= strokeWidth; h -= strokeWidth; const adjustedClipBBox = clipBBox?.clone().shrink(halfStrokeWidth); const cornerRadii = { topLeft: topLeft > 0 ? topLeft - strokeWidth : 0, topRight: topRight > 0 ? topRight - strokeWidth : 0, bottomRight: bottomRight > 0 ? bottomRight - strokeWidth : 0, bottomLeft: bottomLeft > 0 ? bottomLeft - strokeWidth : 0 }; this.borderClipPath = void 0; if (w > 0 && h > 0 && (adjustedClipBBox == null || adjustedClipBBox?.width > 0 && adjustedClipBBox?.height > 0)) { clippedRoundRect(path, x, y, w, h, cornerRadii, adjustedClipBBox); clippedRoundRect(borderPath, x, y, w, h, cornerRadii, adjustedClipBBox); } } else { this.borderClipPath = this.borderClipPath ?? new ExtendedPath2D(); this.borderClipPath.clear(); this.borderClipPath.rect(x, y, w, h); borderPath.rect(x, y, w, h); } } else { const cornerRadii = { topLeft, topRight, bottomRight, bottomLeft }; this.borderClipPath = void 0; clippedRoundRect(path, x, y, w, h, cornerRadii, clipBBox); } if ([topLeft, topRight, bottomRight, bottomLeft].every(areCornersZero)) { let distanceSquaredFromRect2 = function(hitX, hitY) { return rectInstance.getBBox().distanceSquared(hitX, hitY); }; var distanceSquaredFromRect = distanceSquaredFromRect2; const bbox = this.getBBox(); this.hittester = bbox.containsPoint.bind(bbox); const rectInstance = this; this.distanceSquared = distanceSquaredFromRect2; } else { this.hittester = super.isPointInPath; this.distanceCalculator = super.distanceSquaredTransformedPoint; } this.effectiveStrokeWidth = strokeWidth; this.lastUpdatePathStrokeWidth = strokeWidth; this.microPixelEffectOpacity = microPixelEffectOpacity; } computeBBox() { const { __x: x, __y: y, __width: width2, __height: height2, __clipBBox: clipBBox } = this; return clipBBox?.clone() ?? new BBox(x, y, width2, height2); } isPointInPath(x, y) { return this.hittester(x, y); } get midPoint() { return { x: this.__x + this.__width / 2, y: this.__y + this.__height / 2 }; } /** * High-performance static property setter that bypasses the decorator system entirely. * Writes directly to backing fields (__propertyName) to avoid: * - Decorator setter chains and equality checks * - Multiple onChangeDetection calls per property * - Object.keys() iteration in assignIfNotStrictlyEqual * - Object allocation overhead * * A single markDirty() call at the end ensures the scene graph is properly invalidated. * WARNING: Only use for hot paths where performance is critical and properties don't need * individual change detection (e.g., when updating many nodes in a loop). */ setStaticProperties(drawingMode, topLeftCornerRadius, topRightCornerRadius, bottomRightCornerRadius, bottomLeftCornerRadius, visible, crisp, fillShadow) { this.__drawingMode = drawingMode; this.__topLeftCornerRadius = topLeftCornerRadius; this.__topRightCornerRadius = topRightCornerRadius; this.__bottomRightCornerRadius = bottomRightCornerRadius; this.__bottomLeftCornerRadius = bottomLeftCornerRadius; this.__visible = visible; this.__crisp = crisp; this.__fillShadow = fillShadow; this.dirtyPath = true; this.markDirty(); } /** * High-performance animation reset that bypasses the decorator system entirely. * Writes directly to backing fields (__x, __y, etc.) to avoid: * - Decorator setter chains and equality checks * - Multiple onChangeDetection calls * - Object.keys() iteration * * A single markDirty() call at the end ensures the scene graph is properly invalidated. * WARNING: Only use for animation hot paths where performance is critical. */ resetAnimationProperties(x, y, width2, height2, opacity, clipBBox) { this.__x = x; this.__y = y; this.__width = width2; this.__height = height2; this.__opacity = opacity; this.__clipBBox = clipBBox; this.dirtyPath = true; this.markDirty(); } distanceSquared(x, y) { return this.distanceCalculator(x, y); } applyFillAndAlpha(ctx) { super.applyFillAndAlpha(ctx); ctx.globalAlpha *= this.microPixelEffectOpacity; } applyStrokeAndAlpha(ctx) { super.applyStrokeAndAlpha(ctx); ctx.globalAlpha *= this.microPixelEffectOpacity; } renderStroke(ctx) { const { stroke, effectiveStrokeWidth } = this; if (stroke && effectiveStrokeWidth) { const { globalAlpha } = ctx; const { lineDash, lineDashOffset, lineCap, lineJoin, borderPath, borderClipPath } = this; if (borderClipPath) { ctx.clip(borderClipPath.getPath2D()); } this.applyStrokeAndAlpha(ctx); ctx.lineWidth = effectiveStrokeWidth; if (lineDash) { ctx.setLineDash(lineDash); } if (lineDashOffset) { ctx.lineDashOffset = lineDashOffset; } if (lineCap) { ctx.lineCap = lineCap; } if (lineJoin) { ctx.lineJoin = lineJoin; } ctx.stroke(borderPath.getPath2D()); ctx.globalAlpha = globalAlpha; } } }; Rect.className = "Rect"; __decorateClass([ DeclaredSceneChangeDetection() ], Rect.prototype, "x", 2); __decorateClass([ DeclaredSceneChangeDetection() ], Rect.prototype, "y", 2); __decorateClass([ DeclaredSceneChangeDetection() ], Rect.prototype, "width", 2); __decorateClass([ DeclaredSceneChangeDetection() ], Rect.prototype, "height", 2); __decorateClass([ DeclaredSceneChangeDetection() ], Rect.prototype, "topLeftCornerRadius", 2); __decorateClass([ DeclaredSceneChangeDetection() ], Rect.prototype, "topRightCornerRadius", 2); __decorateClass([ DeclaredSceneChangeDetection() ], Rect.prototype, "bottomRightCornerRadius", 2); __decorateClass([ DeclaredSceneChangeDetection() ], Rect.prototype, "bottomLeftCornerRadius", 2); __decorateClass([ DeclaredSceneChangeDetection({ equals: boxesEqual }) ], Rect.prototype, "clipBBox", 2); __decorateClass([ DeclaredSceneChangeDetection() ], Rect.prototype, "crisp", 2); function areCornersZero(cornerRadius) { return cornerRadius === 0; } // packages/ag-charts-community/src/chart/gridLayout.ts function gridLayout({ orientation, bboxes, maxHeight, maxWidth, itemPaddingY = 0, itemPaddingX = 0, forceResult = false }) { const horizontal = orientation === "horizontal"; const primary = { max: horizontal ? maxWidth : maxHeight, fn: horizontal ? (b) => b.width : (b) => b.height, padding: horizontal ? itemPaddingX : itemPaddingY }; const secondary = { max: horizontal ? maxHeight : maxWidth, fn: horizontal ? (b) => b.height : (b) => b.width, padding: horizontal ? itemPaddingY : itemPaddingX }; let processedBBoxCount = 0; const rawPages = []; while (processedBBoxCount < bboxes.length) { const unprocessedBBoxes = bboxes.slice(processedBBoxCount); const result = processBBoxes(unprocessedBBoxes, processedBBoxCount, primary, secondary, forceResult); if (!result) { return; } processedBBoxCount += result.processedBBoxCount; rawPages.push(result.pageIndices); } return buildPages(rawPages, orientation, bboxes, itemPaddingY, itemPaddingX); } function processBBoxes(bboxes, indexOffset, primary, secondary, forceResult) { const minGuess = 1; let startingGuess = estimateStartingGuess(bboxes, primary); if (startingGuess < minGuess) { if (!forceResult) { return; } startingGuess = minGuess; } let guess = startingGuess; while (guess >= minGuess) { const pageIndices = calculatePage(bboxes, indexOffset, guess, primary, secondary, forceResult); if (pageIndices == null && guess <= minGuess) { return; } if (pageIndices == null) { guess--; continue; } if (typeof pageIndices === "number") { if (pageIndices <= minGuess) { return; } guess = pageIndices < guess && pageIndices > minGuess ? pageIndices : guess; guess--; continue; } const processedBBoxCount = pageIndices.length * pageIndices[0].length; return { processedBBoxCount, pageIndices }; } } function calculatePage(bboxes, indexOffset, primaryCount, primary, secondary, forceResult) { const result = []; let sumSecondary = 0; let currentMaxSecondary = 0; let currentPrimaryIndices = []; const maxPrimaryValues = []; for (let bboxIndex = 0; bboxIndex < bboxes.length; bboxIndex++) { const primaryValueIdx = (bboxIndex + primaryCount) % primaryCount; if (primaryValueIdx === 0) { sumSecondary += currentMaxSecondary; currentMaxSecondary = 0; if (currentPrimaryIndices.length > 0) { result.push(currentPrimaryIndices); } currentPrimaryIndices = []; } const primaryValue = primary.fn(bboxes[bboxIndex]) + primary.padding; maxPrimaryValues[primaryValueIdx] = Math.max(maxPrimaryValues[primaryValueIdx] ?? 0, primaryValue); currentMaxSecondary = Math.max(currentMaxSecondary, secondary.fn(bboxes[bboxIndex]) + secondary.padding); const currentSecondaryDimension = sumSecondary + currentMaxSecondary; const returnResult = !forceResult || result.length > 0; if (currentSecondaryDimension > secondary.max && returnResult) { currentPrimaryIndices = []; break; } const sumPrimary = maxPrimaryValues.reduce((sum, next) => sum + next, 0); if (sumPrimary > primary.max && !forceResult) { if (maxPrimaryValues.length < primaryCount) { return maxPrimaryValues.length; } return; } currentPrimaryIndices.push(bboxIndex + indexOffset); } if (currentPrimaryIndices.length > 0) { result.push(currentPrimaryIndices); } return result.length > 0 ? result : void 0; } function buildPages(rawPages, orientation, bboxes, itemPaddingY, itemPaddingX) { let maxPageWidth = 0; let maxPageHeight = 0; const pages = rawPages.map((indices) => { if (orientation === "horizontal") { indices = transpose(indices); } let endIndex = 0; const columns = indices.map((colIndices) => { const colBBoxes = colIndices.map((bboxIndex) => { endIndex = Math.max(bboxIndex, endIndex); return bboxes[bboxIndex]; }); let columnHeight = 0; let columnWidth = 0; for (const bbox of colBBoxes) { columnHeight += bbox.height + itemPaddingY; columnWidth = Math.max(columnWidth, bbox.width + itemPaddingX); } return { indices: colIndices, bboxes: colBBoxes, columnHeight: Math.ceil(columnHeight), columnWidth: Math.ceil(columnWidth) }; }); let pageWidth = 0; let pageHeight = 0; for (const column of columns) { pageWidth += column.columnWidth; pageHeight = Math.max(pageHeight, column.columnHeight); } maxPageWidth = Math.max(pageWidth, maxPageWidth); maxPageHeight = Math.max(pageHeight, maxPageHeight); return { columns, startIndex: indices[0][0], endIndex, pageWidth, pageHeight }; }); return { pages, maxPageWidth, maxPageHeight }; } function transpose(data) { const result = []; for (const _ of data[0]) { result.push([]); } for (const [dataIdx, innerData] of data.entries()) { for (const [itemIdx, item] of innerData.entries()) { result[itemIdx][dataIdx] = item; } } return result; } function estimateStartingGuess(bboxes, primary) { const n = bboxes.length; let primarySum = 0; for (let bboxIndex = 0; bboxIndex < n; bboxIndex++) { primarySum += primary.fn(bboxes[bboxIndex]) + primary.padding; if (primarySum > primary.max) { const ratio2 = n / bboxIndex; if (ratio2 < 2) { return Math.ceil(n / 2); } return bboxIndex; } } return n; } // packages/ag-charts-community/src/chart/interaction/interactionManager.ts var InteractionState = /* @__PURE__ */ ((InteractionState2) => { InteractionState2[InteractionState2["Default"] = 64] = "Default"; InteractionState2[InteractionState2["ZoomDrag"] = 32] = "ZoomDrag"; InteractionState2[InteractionState2["Annotations"] = 16] = "Annotations"; InteractionState2[InteractionState2["ContextMenu"] = 8] = "ContextMenu"; InteractionState2[InteractionState2["Animation"] = 4] = "Animation"; InteractionState2[InteractionState2["AnnotationsSelected"] = 2] = "AnnotationsSelected"; InteractionState2[InteractionState2["Frozen"] = 1] = "Frozen"; InteractionState2[InteractionState2["Clickable"] = 82] = "Clickable"; InteractionState2[InteractionState2["Focusable"] = 68] = "Focusable"; InteractionState2[InteractionState2["Keyable"] = 86] = "Keyable"; InteractionState2[InteractionState2["ContextMenuable"] = 72] = "ContextMenuable"; InteractionState2[InteractionState2["AnnotationsMoveable"] = 18] = "AnnotationsMoveable"; InteractionState2[InteractionState2["AnnotationsDraggable"] = 114] = "AnnotationsDraggable"; InteractionState2[InteractionState2["ZoomDraggable"] = 100] = "ZoomDraggable"; InteractionState2[InteractionState2["ZoomClickable"] = 68] = "ZoomClickable"; InteractionState2[InteractionState2["ZoomWheelable"] = 118] = "ZoomWheelable"; InteractionState2[InteractionState2["All"] = 126] = "All"; return InteractionState2; })(InteractionState || {}); var InteractionManager = class { constructor() { this.stateQueue = 64 /* Default */ | 4 /* Animation */; } pushState(state) { this.stateQueue |= state; } popState(state) { this.stateQueue &= ~state; } isState(allowedStates) { return !!(this.stateQueue & -this.stateQueue & allowedStates); } }; // packages/ag-charts-community/src/chart/layout/layoutManager.ts var LayoutElement = /* @__PURE__ */ ((LayoutElement2) => { LayoutElement2[LayoutElement2["Caption"] = 0] = "Caption"; LayoutElement2[LayoutElement2["Legend"] = 1] = "Legend"; LayoutElement2[LayoutElement2["ToolbarLeft"] = 2] = "ToolbarLeft"; LayoutElement2[LayoutElement2["ToolbarBottom"] = 3] = "ToolbarBottom"; LayoutElement2[LayoutElement2["Scrollbar"] = 4] = "Scrollbar"; LayoutElement2[LayoutElement2["Navigator"] = 5] = "Navigator"; LayoutElement2[LayoutElement2["Overlay"] = 6] = "Overlay"; return LayoutElement2; })(LayoutElement || {}); var LayoutManager = class { constructor(eventsHub) { this.eventsHub = eventsHub; this.elements = /* @__PURE__ */ new Map(); } registerElement(element2, listener) { if (this.elements.has(element2)) { this.elements.get(element2).add(listener); } else { this.elements.set(element2, /* @__PURE__ */ new Set([listener])); } return () => this.elements.get(element2)?.delete(listener); } createContext(width2, height2) { const context = { width: width2, height: height2, layoutBox: new BBox(0, 0, width2, height2), scrollbars: {} }; for (const element2 of Object.values(LayoutElement)) { if (typeof element2 !== "number") continue; const listeners = this.elements.get(element2); if (listeners) { for (const listener of listeners) { listener(context); } } } return context; } emitLayoutComplete({ width: width2, height: height2 }, options) { this.eventsHub.emit("layout:complete", { axes: options.axes ?? {}, chart: { width: width2, height: height2 }, clipSeries: options.clipSeries ?? false, series: options.series }); } }; // packages/ag-charts-community/src/chart/marker/shapes.ts function drawMarkerUnitPolygon(params, moves) { const { path, size } = params; const { x: x0, y: y0 } = params; path.clear(); let didMove = false; for (const [dx, dy] of moves) { const x = x0 + (dx - 0.5) * size; const y = y0 + (dy - 0.5) * size; if (didMove) { path.lineTo(x, y); } else { path.moveTo(x, y); } didMove = true; } path.closePath(); } var MARKER_SHAPES = { circle({ path, x, y, size }) { const r = size / 2; path.arc(x, y, r, 0, Math.PI * 2); path.closePath(); }, cross(params) { drawMarkerUnitPolygon(params, [ [0.25, 0], [0.5, 0.25], [0.75, 0], [1, 0.25], [0.75, 0.5], [1, 0.75], [0.75, 1], [0.5, 0.75], [0.25, 1], [0, 0.75], [0.25, 0.5], [0, 0.25] ]); }, diamond(params) { drawMarkerUnitPolygon(params, [ [0.5, 0], [1, 0.5], [0.5, 1], [0, 0.5] ]); }, heart({ path, x, y, size }) { const r = size / 4; y = y + r / 2; path.arc(x - r, y - r, r, toRadians(130), toRadians(330)); path.arc(x + r, y - r, r, toRadians(220), toRadians(50)); path.lineTo(x, y + r); path.closePath(); }, pin({ path, x, y, size: s }) { const cx = 0.5; const cy = 0.5; path.moveTo(x + (0.891 - cx) * s, y + (0.391 - cy) * s); path.cubicCurveTo( x + (0.891 - cx) * s, y + (0.606 - cy) * s, x + (0.5 - cx) * s, y + (1 - cy) * s, x + (0.5 - cx) * s, y + (1 - cy) * s ); path.cubicCurveTo( x + (0.5 - cx) * s, y + (1 - cy) * s, x + (0.109 - cx) * s, y + (0.606 - cy) * s, x + (0.109 - cx) * s, y + (0.391 - cy) * s ); path.cubicCurveTo( x + (0.109 - cx) * s, y + (0.175 - cy) * s, x + (0.284 - cx) * s, y + (0 - cy) * s, x + (0.5 - cx) * s, y + (0 - cy) * s ); path.cubicCurveTo( x + (0.716 - cx) * s, y + (0 - cy) * s, x + (0.891 - cx) * s, y + (0.175 - cy) * s, x + (0.891 - cx) * s, y + (0.391 - cy) * s ); path.closePath(); }, plus(params) { drawMarkerUnitPolygon(params, [ [1 / 3, 0], [2 / 3, 0], [2 / 3, 1 / 3], [1, 1 / 3], [1, 2 / 3], [2 / 3, 2 / 3], [2 / 3, 1], [1 / 3, 1], [1 / 3, 2 / 3], [0, 2 / 3], [0, 1 / 3], [1 / 3, 1 / 3] ]); }, square({ path, x, y, size, pixelRatio }) { const hs = size / 2; path.moveTo(align(pixelRatio, x - hs), align(pixelRatio, y - hs)); path.lineTo(align(pixelRatio, x + hs), align(pixelRatio, y - hs)); path.lineTo(align(pixelRatio, x + hs), align(pixelRatio, y + hs)); path.lineTo(align(pixelRatio, x - hs), align(pixelRatio, y + hs)); path.closePath(); }, star({ path, x, y, size }) { const spikes = 5; const outerRadius = size / 2; const innerRadius = outerRadius / 2; const rotation = Math.PI / 2; for (let i = 0; i < spikes * 2; i++) { const radius = i % 2 === 0 ? outerRadius : innerRadius; const angle2 = i * Math.PI / spikes - rotation; const xCoordinate = x + Math.cos(angle2) * radius; const yCoordinate = y + Math.sin(angle2) * radius; path.lineTo(xCoordinate, yCoordinate); } path.closePath(); }, triangle(params) { drawMarkerUnitPolygon(params, [ [0.5, 0], [1, 0.87], [0, 0.87] ]); } }; // packages/ag-charts-community/src/chart/marker/marker.ts var InternalMarker = class extends Path { constructor() { super(...arguments); this.shape = "square"; this.x = 0; this.y = 0; this.size = 12; } // optimised field accessor isPointInPath(x, y) { return this.distanceSquared(x, y) <= 0; } get midPoint() { return { x: this.x, y: this.y }; } distanceSquared(x, y) { const anchor = Marker.anchor(this.shape); const dx = x - this.x + (anchor.x - 0.5) * this.size; const dy = y - this.y + (anchor.y - 0.5) * this.size; const radius = this.size / 2; return Math.max(dx * dx + dy * dy - radius * radius, 0); } updatePath() { const { path, shape, x, y, size } = this; const pixelRatio = this.layerManager?.canvas?.pixelRatio ?? 1; const anchor = Marker.anchor(shape); const drawParams = { path, x: x - (anchor.x - 0.5) * size, y: y - (anchor.y - 0.5) * size, size, pixelRatio }; path.clear(); if (typeof shape === "string") { MARKER_SHAPES[shape](drawParams); } else if (typeof shape === "function") { shape(drawParams); } } computeBBox() { const { x, y, size } = this; const anchor = Marker.anchor(this.shape); return new BBox(x - size * anchor.x, y - size * anchor.y, size, size); } executeFill(ctx, path) { if (!path) return; return super.executeFill(ctx, path); } executeStroke(ctx, path) { if (!path) return; return super.executeStroke(ctx, path); } }; __decorateClass([ DeclaredSceneObjectChangeDetection({ equals: TRIPLE_EQ }) ], InternalMarker.prototype, "shape", 2); __decorateClass([ DeclaredSceneChangeDetection() ], InternalMarker.prototype, "x", 2); __decorateClass([ DeclaredSceneChangeDetection() ], InternalMarker.prototype, "y", 2); __decorateClass([ DeclaredSceneChangeDetection({ convertor: Math.abs }) ], InternalMarker.prototype, "size", 2); var Marker = class extends Rotatable(Scalable(Translatable(InternalMarker))) { static anchor(shape) { if (shape === "pin") { return { x: 0.5, y: 1 }; } else if (typeof shape === "function" && "anchor" in shape) { return shape.anchor; } return { x: 0.5, y: 0.5 }; } constructor(options) { super(options); if (options?.shape != null) { this.shape = options.shape; } } /** * Optimised reset for animation hot paths. * Bypasses SceneChangeDetection decorators by writing directly to backing fields. * * This avoids per-property overhead from: * - Equality checks (comparing old vs new values) * - Change callbacks (triggering downstream updates) * - Object.keys() iteration * * A single markDirty() call at the end ensures the scene graph is properly invalidated. * WARNING: Only use for animation hot paths where performance is critical. */ resetAnimationProperties(x, y, size, opacity, scalingX, scalingY) { this.__x = x; this.__y = y; this.__size = size; this.__opacity = opacity; this.resetScalingProperties(scalingX, scalingY, x, y); this.dirtyPath = true; this.markDirty(); } }; // packages/ag-charts-community/src/scene/sceneDebug.ts var StatsAccumulator = class { // Log every 10 seconds constructor() { this.stats = /* @__PURE__ */ new Map(); this.lastLogTime = Date.now(); this.LOG_INTERVAL_MS = 1e4; this.startPeriodicLogging(); } startPeriodicLogging() { if (!debugLogger_exports.check("scene:stats" /* SCENE_STATS */, "scene:stats:verbose" /* SCENE_STATS_VERBOSE */)) { return; } this.stopPeriodicLogging(); this.intervalId = setInterval(() => { this.logAccumulatedStats(); }, this.LOG_INTERVAL_MS); } stopPeriodicLogging() { if (this.intervalId) { clearInterval(this.intervalId); this.intervalId = void 0; } } recordTiming(category, duration) { const existing = this.stats.get(category); if (existing) { existing.min = Math.min(existing.min, duration); existing.max = Math.max(existing.max, duration); existing.sum += duration; existing.count += 1; } else { this.stats.set(category, { min: duration, max: duration, sum: duration, count: 1 }); } } recordTimings(durations) { for (const [category, duration] of Object.entries(durations)) { if (category !== "start" && typeof duration === "number") { this.recordTiming(category, duration); } } } logAccumulatedStats() { if (this.stats.size === 0) return; const timeSinceLastLog = (Date.now() - this.lastLogTime) / 1e3; const categories = Array.from(this.stats.keys()).sort((a, b) => { if (a === "\u23F1\uFE0F") return -1; if (b === "\u23F1\uFE0F") return 1; return a.localeCompare(b); }); const parts = []; for (const category of categories) { const stats = this.stats.get(category); const avg = stats.sum / stats.count; parts.push(`${category}[${stats.min.toFixed(1)}/${avg.toFixed(1)}/${stats.max.toFixed(1)}]ms`); } const totalStats = this.stats.get("\u23F1\uFE0F"); const count = totalStats?.count ?? 0; logger_exports.log(`\u{1F4CA} Stats (${timeSinceLastLog.toFixed(0)}s, ${count} renders): ${parts.join(" ")}`); this.stats.clear(); this.lastLogTime = Date.now(); } destroy() { this.stopPeriodicLogging(); this.stats.clear(); } }; var globalStatsAccumulator; var statsAccumulatorConsumers = 0; function getStatsAccumulator() { globalStatsAccumulator ?? (globalStatsAccumulator = new StatsAccumulator()); return globalStatsAccumulator; } function registerDebugStatsConsumer() { statsAccumulatorConsumers++; let released = false; return () => { if (released || statsAccumulatorConsumers === 0) return; released = true; statsAccumulatorConsumers--; if (statsAccumulatorConsumers === 0) { cleanupDebugStats(); } }; } function formatBytes(value) { for (const unit of ["B", "KB", "MB", "GB"]) { if (value < 1536) { return `${value.toFixed(1)}${unit}`; } value /= 1024; } return `${value.toFixed(1)}TB}`; } function memoryUsage() { if (!("memory" in performance)) return; const { totalJSHeapSize, usedJSHeapSize, jsHeapSizeLimit } = performance.memory; const result = []; for (const amount of [usedJSHeapSize, totalJSHeapSize, jsHeapSizeLimit]) { if (typeof amount !== "number") continue; result.push(formatBytes(amount)); } return `Heap ${result.join(" / ")}`; } function debugStats(layersManager, debugSplitTimes, ctx, renderCtxStats, extraDebugStats = {}, seriesRect = BBox.zero, colors) { if (!debugLogger_exports.check("scene:stats" /* SCENE_STATS */, "scene:stats:verbose" /* SCENE_STATS_VERBOSE */)) return; const { layersRendered = 0, layersSkipped = 0, nodesRendered = 0, nodesSkipped = 0, opsPerformed = 0, opsSkipped = 0 } = renderCtxStats ?? {}; const end3 = performance.now(); const { start: start2, ...durations } = debugSplitTimes; const totalTime = end3 - start2; const statsAccumulator = getStatsAccumulator(); statsAccumulator.recordTimings(durations); statsAccumulator.recordTiming("\u23F1\uFE0F", totalTime); const splits = Object.entries(durations).map(([n, t]) => time(n, t)).filter((v) => v != null).join(" + "); const extras = Object.entries(extraDebugStats).map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join(" ; "); const detailedStats = debugLogger_exports.check("scene:stats:verbose" /* SCENE_STATS_VERBOSE */); const memUsage = detailedStats ? memoryUsage() : null; const metrics2 = detailedStats ? debugMetrics_exports.flush() : {}; const metricsEntries = Object.entries(metrics2); const aggregationMetrics = []; const nodeDataMetrics = []; for (const [k, v] of metricsEntries) { if (k.endsWith(":aggregation") && Array.isArray(v)) { aggregationMetrics.push(`${k.replace(":aggregation", "")}(${v.join(",")})`); } else if (k.endsWith(":nodeData") && typeof v === "number") { nodeDataMetrics.push(`${k.replace(":nodeData", "")}(${v})`); } } const aggregationText = aggregationMetrics.length > 0 ? `Aggregation: ${aggregationMetrics.join(", ")}` : null; const nodeDataText = nodeDataMetrics.length > 0 ? `NodeData: ${nodeDataMetrics.join(", ")}` : null; const stats = [ `${time("\u23F1\uFE0F", start2, end3)} (${splits})`, `${extras}`, aggregationText, nodeDataText, `Layers: ${detailedStats ? pct(layersRendered, layersSkipped) : layersManager.size}`, detailedStats ? `Nodes: ${pct(nodesRendered, nodesSkipped)}` : null, detailedStats ? `Ops: ${pct(opsPerformed, opsSkipped)}` : null, memUsage ].filter(isString); const measurer = new TextMeasurer(ctx); const statsSize = new Map(stats.map((t) => [t, measurer.measureText(t)])); const width2 = Math.max(...Array.from(statsSize.values(), (s) => s.width)); const height2 = accumulate(statsSize.values(), (s) => s.height); const x = 2 + seriesRect.x; ctx.save(); try { ctx.fillStyle = colors?.background ?? "white"; ctx.fillRect(x, 0, width2, height2); ctx.fillStyle = colors?.foreground ?? "black"; let y = 0; for (const [stat, size] of statsSize.entries()) { y += size.height; ctx.fillText(stat, x, y); } } catch (e) { logger_exports.warnOnce("Error during debug stats rendering", e); } finally { ctx.restore(); } } function prepareSceneNodeHighlight(ctx) { const config = toArray(getWindow("agChartsSceneDebug")); const result = []; for (const name of config) { if (name === "layout") { result.push("seriesRoot", "legend", "root", /.*Axis-\d+-axis.*/); } else { result.push(name); } } ctx.debugNodeSearch = result; } function debugSceneNodeHighlight(ctx, debugNodes) { ctx.save(); try { for (const [name, node] of Object.entries(debugNodes)) { const bbox = Transformable.toCanvas(node); if (!bbox) { logger_exports.log(`Scene.render() - no bbox for debugged node [${name}].`); continue; } ctx.globalAlpha = 0.8; ctx.strokeStyle = "red"; ctx.lineWidth = 1; ctx.strokeRect(bbox.x, bbox.y, bbox.width, bbox.height); ctx.fillStyle = "red"; ctx.strokeStyle = "white"; ctx.font = "16px sans-serif"; ctx.textBaseline = "top"; ctx.textAlign = "left"; ctx.lineWidth = 2; ctx.strokeText(name, bbox.x, bbox.y, bbox.width); ctx.fillText(name, bbox.x, bbox.y, bbox.width); } } catch (e) { logger_exports.warnOnce("Error during debug rendering", e); } finally { ctx.restore(); } } var skippedProperties = /* @__PURE__ */ new Set(); var allowedProperties = /* @__PURE__ */ new Set([ "gradient", // '_datum', "zIndex", "clipRect", "cachedBBox", "childNodeCounts", "path", "__zIndex", "name", "__scalingCenterX", "__scalingCenterY", "__rotationCenterX", "__rotationCenterY", "_previousDatum", "__fill", "__lineDash", "borderPath", "borderClipPath", "_clipPath" ]); function nodeProps(node) { const { ...allProps } = node; for (const prop of Object.keys(allProps)) { if (allowedProperties.has(prop)) continue; if (typeof allProps[prop] === "number") continue; if (typeof allProps[prop] === "string") continue; if (typeof allProps[prop] === "boolean") continue; skippedProperties.add(prop); delete allProps[prop]; } return allProps; } function buildTree(node, mode) { if (!debugLogger_exports.check(true, "scene" /* SCENE */)) { return {}; } let order = 0; return { node: mode === "json" ? nodeProps(node) : node, name: node.name ?? node.id, dirty: node instanceof Group ? node.dirty : void 0, ...Array.from(node instanceof Group ? node.children() : [], (c) => buildTree(c, mode)).reduce((result, childTree) => { let { name: treeNodeName } = childTree; const { node: { visible, opacity, zIndex, translationX, translationY, rotation, scalingX, scalingY }, node: childNode } = childTree; if (!visible || opacity <= 0) { treeNodeName = `(${treeNodeName})`; } if (Group.is(childNode) && childNode.renderToOffscreenCanvas) { treeNodeName = `*${treeNodeName}*`; } const zIndexString = Array.isArray(zIndex) ? `(${zIndex.join(", ")})` : zIndex; const key = [ `${(order++).toString().padStart(3, "0")}|`, `${treeNodeName ?? ""}`, `z: ${zIndexString}`, translationX && `x: ${translationX}`, translationY && `y: ${translationY}`, rotation && `r: ${rotation}`, scalingX != null && scalingX !== 1 && `sx: ${scalingX}`, scalingY != null && scalingY !== 1 && `sy: ${scalingY}` ].filter((v) => !!v).join(" "); let selectedKey = key; let index = 1; while (result[selectedKey] != null && index < 100) { selectedKey = `${key} (${index++})`; } result[selectedKey] = childTree; return result; }, {}) }; } function buildDirtyTree(node) { const nodeDirty = node instanceof Group ? node.dirty : void 0; if (!nodeDirty) { return { dirtyTree: {}, paths: [] }; } const childrenDirtyTree = Array.from(node instanceof Group ? node.children() : [], (c) => buildDirtyTree(c)).filter( (c) => c.paths.length > 0 ); const name = Group.is(node) ? node.name ?? node.id : node.id; const paths = childrenDirtyTree.length ? childrenDirtyTree.flatMap((c) => c.paths).map((p) => `${name}.${p}`) : [name]; return { dirtyTree: { name, node, dirty: nodeDirty, ...childrenDirtyTree.map((c) => c.dirtyTree).filter((t) => t.dirty != null).reduce((result, childTree) => { result[childTree.name ?? ""] = childTree; return result; }, {}) }, paths }; } function pct(rendered, skipped) { const total = rendered + skipped; return `${rendered} / ${total} (${Math.round(100 * rendered / total)}%)`; } function time(name, start2, end3) { const duration = end3 == null ? start2 : end3 - start2; return `${name}: ${Math.round(duration * 100) / 100}ms`; } function accumulate(iterator, mapper) { let sum = 0; for (const item of iterator) { sum += mapper(item); } return sum; } function cleanupDebugStats(force = false) { if (!globalStatsAccumulator) { if (force) { statsAccumulatorConsumers = 0; } return; } if (!force && statsAccumulatorConsumers > 0) { return; } globalStatsAccumulator.destroy(); globalStatsAccumulator = void 0; if (force) { statsAccumulatorConsumers = 0; } } // packages/ag-charts-community/src/scene/shape/text.ts var _Text = class _Text extends Shape { constructor(options) { super(options); this.x = 0; this.y = 0; this.lines = []; this.text = void 0; this.fontCache = void 0; this.fontSize = _Text.defaultFontSize; this.fontFamily = "sans-serif"; this.textAlign = "start"; this.textBaseline = "alphabetic"; this.boxPadding = 0; this.trimText = options?.trimText ?? true; } onTextChange() { this.richText?.clear(); this.textMap?.clear(); if (isArray(this.text)) { this.lines = []; this.richText ?? (this.richText = new Group()); this.richText.setScene(this.scene); this.richText.append( this.text.flatMap((s) => toTextString(s.text).split(LineSplitter)).filter(Boolean).map(() => new _Text({ trimText: false })) ); } else { const lines = toTextString(this.text).split(LineSplitter); this.lines = this.trimText ? lines.map((line) => line.trim()) : lines; } } get font() { this.fontCache ?? (this.fontCache = toFontString(this)); return this.fontCache; } static measureBBox(text, x, y, options) { if (isArray(text)) { const { font, lineHeight, textAlign, textBaseline } = options; const { width: width2, height: height2, lineMetrics } = measureTextSegments(text, font); const totalHeight = lineHeight ? lineHeight * lineMetrics.length : height2; const offsetTop = _Text.calcTopOffset(totalHeight, lineMetrics[0], textBaseline); const offsetLeft = _Text.calcLeftOffset(width2, textAlign); return new BBox(x - offsetLeft, y - offsetTop, width2, totalHeight); } else { return _Text.computeBBox(toTextString(text).split(LineSplitter), x, y, options); } } static computeBBox(lines, x, y, opts) { const { font, lineHeight, textAlign, textBaseline } = opts; const { width: width2, height: height2, lineMetrics } = cachedTextMeasurer(font).measureLines(lines); const totalHeight = lineHeight ? lineHeight * lineMetrics.length : height2; const offsetTop = _Text.calcTopOffset(totalHeight, lineMetrics[0], textBaseline); const offsetLeft = _Text.calcLeftOffset(width2, textAlign); return new BBox(x - offsetLeft, y - offsetTop, width2, totalHeight); } static calcTopOffset(height2, textMetrics, textBaseline) { switch (textBaseline) { case "alphabetic": return textMetrics?.ascent ?? 0; case "middle": return height2 / 2; case "bottom": return height2; default: return 0; } } static calcSegmentedTopOffset(height2, lineMetrics, textBaseline) { switch (textBaseline) { case "alphabetic": return lineMetrics[0]?.ascent ?? 0; case "middle": return lineMetrics.length === 1 ? lineMetrics[0].ascent + lineMetrics[0].segments.reduce( (offsetY, segment) => Math.min(offsetY, cachedTextMeasurer(segment).baselineDistance("middle")), 0 ) : height2 / 2; case "bottom": return height2; default: return 0; } } static calcLeftOffset(width2, textAlign) { let offset = 0; switch (textAlign) { case "center": offset = 0.5; break; case "right": case "end": offset = 1; } return width2 * offset; } getBBox() { const bbox = super.getBBox(); if (!this.textMap?.size || !isArray(this.text)) return bbox; const { height: height2, lineMetrics } = measureTextSegments(this.text, this); const offsetTop = _Text.calcSegmentedTopOffset(height2, lineMetrics, this.textBaseline); const y = this.y - offsetTop; if (bbox.y === y) return bbox; return new BBox(bbox.x, y, bbox.width, bbox.height); } computeBBox() { this.generateTextMap(); if (this.textMap?.size) { const bbox = BBox.merge(this.textMap.values()); bbox.x = this.x - _Text.calcLeftOffset(bbox.width, this.textAlign); bbox.y = this.y; return bbox; } const { x, y, lines, textBaseline, textAlign } = this; const measuredTextBounds = _Text.computeBBox(lines, x, y, { font: this, textBaseline, textAlign }); if (this.boxing != null) measuredTextBounds.grow(this.boxPadding); return measuredTextBounds; } getTextMeasureBBox() { return this.computeBBox(); } getPlainText() { return toPlainText(this.text); } isPointInPath(x, y) { return this.getBBox()?.containsPoint(x, y) ?? false; } setScene(scene) { this.richText?.setScene(scene); super.setScene(scene); } generateTextMap() { if (!isArray(this.text) || this.textMap?.size) return; this.textMap ?? (this.textMap = /* @__PURE__ */ new Map()); let offsetY = 0; const textNodes = this.richText.children(); for (const { width: width2, height: height2, ascent, segments } of measureTextSegments(this.text, this).lineMetrics) { let offsetX = 0; for (const { color: color2, textMetrics, ...segment } of segments) { const textNode = textNodes.next().value; textNode.x = this.x - width2 / 2 + offsetX; textNode.y = ascent + offsetY; textNode.setProperties({ ...segment, fill: color2 ?? this.fill }); const textBBox = textNode.getBBox(); this.textMap.set(textNode, textBBox); offsetX += textMetrics.width; } offsetY += height2; } } render(renderCtx) { const { ctx, stats } = renderCtx; if (!this.layerManager || !this.hasRenderableText()) { if (stats) stats.nodesSkipped += 1; return super.render(renderCtx); } if (isArray(this.text) && this.richText) { this.generateTextMap(); const richTextBBox = this.richText.getBBox(); const { width: width2, height: height2, lineMetrics } = measureTextSegments(this.text, this); let translateX = 0; switch (this.textAlign) { case "left": case "start": translateX = width2 / 2; break; case "right": case "end": translateX = width2 / -2; } const translateY = this.y - _Text.calcSegmentedTopOffset(height2, lineMetrics, this.textBaseline); this.renderBoxing(renderCtx, richTextBBox.clone().translate(translateX, translateY)); ctx.save(); ctx.translate(translateX, translateY); this.richText.opacity = this.opacity; this.richText.render(renderCtx); ctx.restore(); } else { this.renderText(renderCtx); } if (_Text.debug.check()) { const bbox = this.getBBox(); ctx.lineWidth = this.textMap?.size ? 2 : 1; ctx.strokeStyle = this.textMap?.size ? "blue" : "red"; ctx.strokeRect(bbox.x, bbox.y, bbox.width, bbox.height); } super.render(renderCtx); } markDirty(property) { this.textMap?.clear(); return super.markDirty(property); } renderText(renderCtx) { const { fill, stroke, strokeWidth, font, textAlign } = this; if (!fill && !(stroke && strokeWidth) || !this.layerManager) { return super.render(renderCtx); } const { ctx } = renderCtx; if (ctx.font !== font) { ctx.font = font; } ctx.textAlign = textAlign; this.renderBoxing(renderCtx); this.fillStroke(ctx); } renderBoxing(renderCtx, bbox) { if (!this.boxing) return; const textBBox = bbox ?? _Text.computeBBox(this.lines, this.x, this.y, this); if (textBBox.width === 0 || textBBox.height === 0) return; const { x, y, width: width2, height: height2 } = textBBox.grow(this.boxPadding); this.boxing.opacity = this.opacity; this.boxing.x = x; this.boxing.y = y; this.boxing.width = width2; this.boxing.height = height2; this.boxing.preRender(renderCtx); this.boxing.render(renderCtx); } executeFill(ctx) { this.renderLines((line, x, y) => ctx.fillText(line, x, y)); } executeStroke(ctx) { this.renderLines((line, x, y) => ctx.strokeText(line, x, y)); } renderLines(renderCallback) { const { x, y, lines } = this; if (!Number.isFinite(x) || !Number.isFinite(y)) return; const measurer = cachedTextMeasurer(this); const { lineMetrics } = measurer.measureLines(lines); const { textBaseline, lineHeight = measurer.lineHeight() } = this; let offsetY = 0; if (textBaseline === "top") { offsetY = lineMetrics[0].ascent; } else if (textBaseline === "middle" || textBaseline === "bottom") { offsetY = lineHeight * (1 - lines.length); if (textBaseline === "middle") { offsetY /= 2; offsetY -= measurer.baselineDistance(textBaseline); } else { offsetY -= lineMetrics[0].descent; } } for (const line of lineMetrics) { renderCallback(line.text, x, y + offsetY); offsetY += lineHeight; } } setFont(props) { this.fontFamily = props.fontFamily; this.fontSize = props.fontSize; this.fontStyle = props.fontStyle; this.fontWeight = props.fontWeight; } setAlign(props) { this.textAlign = props.textAlign; this.textBaseline = props.textBaseline; } setBoxing(props) { const stroke = props.border?.enabled ? props.border?.stroke : void 0; if (props.fill != null || stroke != null) { this.boxing ?? (this.boxing = new Rect({ scene: this.scene })); this.boxing.fill = props.fill; this.boxing.fillOpacity = props.fillOpacity ?? 1; this.boxing.cornerRadius = props.cornerRadius ?? 0; this.boxing.stroke = stroke; this.boxing.strokeWidth = props.border?.strokeWidth ?? 0; this.boxing.strokeOpacity = props.border?.strokeOpacity ?? 1; this.boxPadding = props.padding ?? 0; } else if (this.boxing) { this.boxing.destroy(); this.boxing = void 0; } } getBoxingProperties() { const { fill, fillOpacity, cornerRadius, stroke, strokeWidth, strokeOpacity } = this.boxing ?? {}; return { border: { enabled: stroke != null, stroke, strokeWidth, strokeOpacity }, cornerRadius, fill, fillOpacity, padding: this.boxPadding }; } toSVG() { if (!this.visible || !this.hasRenderableText()) return; const text = this.text; if (text == null) return; const element2 = createSvgElement("text"); if (isArray(text)) { for (const segment of text) { const segmentElement = createSvgElement("tspan"); setSvgFontAttributes(segmentElement, { fontSize: segment.fontSize ?? this.fontSize, fontFamily: segment.fontFamily ?? this.fontFamily, fontWeight: segment.fontWeight ?? this.fontWeight, fontStyle: segment.fontStyle ?? this.fontStyle }); this.applySvgFillAttributes(segmentElement); segmentElement.textContent = toTextString(segment.text); element2.append(segmentElement); } } else { this.applySvgFillAttributes(element2); setSvgFontAttributes(element2, this); element2.setAttribute( "text-anchor", { center: "middle", left: "start", right: "end", start: "start", end: "end" }[this.textAlign ?? "start"] ); element2.setAttribute("alignment-baseline", this.textBaseline); element2.setAttribute("x", String(this.x)); element2.setAttribute("y", String(this.y)); element2.textContent = toTextString(text); } return { elements: [element2] }; } hasRenderableText() { const { text } = this; if (text == null) { return false; } return isArray(text) ? true : toTextString(text) !== ""; } }; _Text.className = "Text"; _Text.debug = debugLogger_exports.create(true, "scene:text" /* SCENE_TEXT */); _Text.defaultFontSize = 10; __decorateClass([ SceneChangeDetection() ], _Text.prototype, "x", 2); __decorateClass([ SceneChangeDetection() ], _Text.prototype, "y", 2); __decorateClass([ SceneRefChangeDetection({ changeCb: (o) => o.onTextChange() }) ], _Text.prototype, "text", 2); __decorateClass([ SceneChangeDetection({ changeCb: (o) => { o.fontCache = void 0; } }) ], _Text.prototype, "fontStyle", 2); __decorateClass([ SceneChangeDetection({ changeCb: (o) => { o.fontCache = void 0; } }) ], _Text.prototype, "fontWeight", 2); __decorateClass([ SceneChangeDetection({ changeCb: (o) => { o.fontCache = void 0; } }) ], _Text.prototype, "fontSize", 2); __decorateClass([ SceneChangeDetection({ changeCb: (o) => { o.fontCache = void 0; } }) ], _Text.prototype, "fontFamily", 2); __decorateClass([ SceneChangeDetection() ], _Text.prototype, "textAlign", 2); __decorateClass([ SceneChangeDetection() ], _Text.prototype, "textBaseline", 2); __decorateClass([ SceneChangeDetection() ], _Text.prototype, "lineHeight", 2); var Text = _Text; var RotatableText = class extends Rotatable(Text) { }; var TransformableText = class extends Rotatable(Translatable(Text)) { }; // packages/ag-charts-community/src/chart/pagination/pagination.ts var PaginationLabel = class extends BaseProperties { constructor() { super(...arguments); this.color = "black"; this.fontStyle = void 0; this.fontWeight = void 0; this.fontSize = 12 /* SMALL */; this.fontFamily = "Verdana, sans-serif"; } }; __decorateClass([ addFakeTransformToInstanceProperty ], PaginationLabel.prototype, "color", 2); __decorateClass([ addFakeTransformToInstanceProperty ], PaginationLabel.prototype, "fontStyle", 2); __decorateClass([ addFakeTransformToInstanceProperty ], PaginationLabel.prototype, "fontWeight", 2); __decorateClass([ addFakeTransformToInstanceProperty ], PaginationLabel.prototype, "fontSize", 2); __decorateClass([ addFakeTransformToInstanceProperty ], PaginationLabel.prototype, "fontFamily", 2); var PaginationMarkerStyle = class extends BaseProperties { constructor() { super(...arguments); this.size = 15; this.fill = void 0; this.fillOpacity = void 0; this.stroke = void 0; this.strokeWidth = 1; this.strokeOpacity = 1; } }; __decorateClass([ addFakeTransformToInstanceProperty ], PaginationMarkerStyle.prototype, "size", 2); __decorateClass([ addFakeTransformToInstanceProperty ], PaginationMarkerStyle.prototype, "fill", 2); __decorateClass([ addFakeTransformToInstanceProperty ], PaginationMarkerStyle.prototype, "fillOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty ], PaginationMarkerStyle.prototype, "stroke", 2); __decorateClass([ addFakeTransformToInstanceProperty ], PaginationMarkerStyle.prototype, "strokeWidth", 2); __decorateClass([ addFakeTransformToInstanceProperty ], PaginationMarkerStyle.prototype, "strokeOpacity", 2); var PaginationMarker = class extends BaseProperties { constructor(parent) { super(); this.parent = parent; this.shape = "triangle"; this.size = 15; this.padding = 8; } }; __decorateClass([ ActionOnSet({ changeValue() { if (this.parent.marker === this) { this.parent.onMarkerShapeChange(); } } }) ], PaginationMarker.prototype, "shape", 2); __decorateClass([ addFakeTransformToInstanceProperty ], PaginationMarker.prototype, "size", 2); __decorateClass([ addFakeTransformToInstanceProperty ], PaginationMarker.prototype, "padding", 2); var Pagination = class extends BaseProperties { constructor(chartUpdateCallback, pageUpdateCallback) { super(); this.chartUpdateCallback = chartUpdateCallback; this.pageUpdateCallback = pageUpdateCallback; this.id = createId(this); this.marker = new PaginationMarker(this); this.activeStyle = new PaginationMarkerStyle(); this.inactiveStyle = new PaginationMarkerStyle(); this.highlightStyle = new PaginationMarkerStyle(); this.label = new PaginationLabel(); this.group = new TranslatableGroup({ name: "pagination" }); this.labelNode = new Text(); this.totalPages = 0; this.currentPage = 0; this.translationX = 0; this.translationY = 0; this.nextButtonDisabled = false; this.previousButtonDisabled = false; this._visible = true; this._enabled = true; this._orientation = "vertical"; this.nextButton = new Marker(); this.previousButton = new Marker(); this.labelNode.setProperties({ textBaseline: "middle", fontSize: 12 /* SMALL */, fontFamily: "Verdana, sans-serif", fill: "black", y: 1 }); this.group.append([this.nextButton, this.previousButton, this.labelNode]); this.update(); this.updateMarkers(); } set visible(value) { this._visible = value; this.updateGroupVisibility(); } get visible() { return this._visible; } set enabled(value) { this._enabled = value; this.updateGroupVisibility(); } get enabled() { return this._enabled; } updateGroupVisibility() { this.group.visible = this.enabled && this.visible; } set orientation(value) { this._orientation = value; switch (value) { case "horizontal": { this.previousButton.rotation = -Math.PI / 2; this.nextButton.rotation = Math.PI / 2; break; } case "vertical": default: { this.previousButton.rotation = 0; this.nextButton.rotation = Math.PI; } } } get orientation() { return this._orientation; } update() { this.updateLabel(); this.updatePositions(); this.enableOrDisableButtons(); } updatePositions() { this.group.translationX = this.translationX; this.group.translationY = this.translationY; this.updateLabelPosition(); this.updateNextButtonPosition(); } updateLabelPosition() { const { size: markerSize, padding: markerPadding } = this.marker; this.nextButton.size = markerSize; this.previousButton.size = markerSize; this.labelNode.x = markerSize / 2 + markerPadding; } updateNextButtonPosition() { const labelBBox = this.labelNode.getBBox(); this.nextButton.translationX = labelBBox.width + (this.marker.size / 2 + this.marker.padding) * 2; } updateLabel() { const { currentPage, totalPages: pages, labelNode, label: { color: color2, fontStyle, fontWeight: fontWeight2, fontSize, fontFamily } } = this; labelNode.text = `${currentPage + 1} / ${pages}`; labelNode.fill = color2; labelNode.fontStyle = fontStyle; labelNode.fontWeight = fontWeight2; labelNode.fontSize = fontSize; labelNode.fontFamily = fontFamily; } updateMarkers() { const { nextButton, previousButton, nextButtonDisabled, previousButtonDisabled, activeStyle, inactiveStyle, highlightStyle, highlightActive } = this; const buttonStyle = (button, disabled) => { if (disabled) { return inactiveStyle; } else if (button === highlightActive) { return highlightStyle; } return activeStyle; }; this.updateMarker(nextButton, buttonStyle("next", nextButtonDisabled)); this.updateMarker(previousButton, buttonStyle("previous", previousButtonDisabled)); } updateMarker(marker, style2) { const { shape, size } = this.marker; marker.shape = shape; marker.size = size; marker.fill = style2.fill; marker.fillOpacity = style2.fillOpacity ?? 1; marker.stroke = style2.stroke; marker.strokeWidth = style2.strokeWidth; marker.strokeOpacity = style2.strokeOpacity; } enableOrDisableButtons() { const { currentPage, totalPages } = this; const zeroPagesToDisplay = totalPages === 0; const onLastPage = currentPage === totalPages - 1; const onFirstPage = currentPage === 0; this.nextButtonDisabled = onLastPage || zeroPagesToDisplay; this.previousButtonDisabled = onFirstPage || zeroPagesToDisplay; } setPage(pageNumber) { pageNumber = clamp(0, pageNumber, Math.max(0, this.totalPages - 1)); if (this.currentPage !== pageNumber) { this.currentPage = pageNumber; this.onPaginationChanged(); } } getCursor(node) { return { previous: this.previousButtonDisabled, next: this.nextButtonDisabled }[node] ? void 0 : "pointer"; } onClick(event, node) { event.preventDefault(); if (node === "next" && !this.nextButtonDisabled) { this.incrementPage(); this.onPaginationChanged(); } else if (node === "previous" && !this.previousButtonDisabled) { this.decrementPage(); this.onPaginationChanged(); } } onMouseHover(node) { this.highlightActive = node; this.updateMarkers(); this.chartUpdateCallback(9 /* SCENE_RENDER */); } onPaginationChanged() { this.pageUpdateCallback(this.currentPage); } incrementPage() { this.currentPage = Math.min(this.currentPage + 1, this.totalPages - 1); } decrementPage() { this.currentPage = Math.max(this.currentPage - 1, 0); } onMarkerShapeChange() { this.updatePositions(); this.updateMarkers(); this.chartUpdateCallback(9 /* SCENE_RENDER */); } attachPagination(node) { node.append(this.group); } getBBox() { return this.group.getBBox(); } computeCSSBounds() { const prev = Transformable.toCanvas(this.previousButton); const next = Transformable.toCanvas(this.nextButton); return { prev, next }; } }; Pagination.className = "Pagination"; __decorateClass([ addFakeTransformToInstanceProperty ], Pagination.prototype, "marker", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Pagination.prototype, "activeStyle", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Pagination.prototype, "inactiveStyle", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Pagination.prototype, "highlightStyle", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Pagination.prototype, "label", 2); // packages/ag-charts-community/src/chart/series/shapeUtil.ts function getShapeFill(fill, defaultGradient, defaultPattern, defaultImage) { if (isGradientFill(fill)) { return { type: "gradient", gradient: fill.gradient ?? defaultGradient.gradient, colorStops: fill.colorStops ?? defaultGradient.colorStops, bounds: fill.bounds ?? defaultGradient.bounds, rotation: fill.rotation ?? defaultGradient.rotation, reverse: fill.reverse ?? defaultGradient.reverse, colorSpace: fill.colorSpace ?? defaultGradient.colorSpace }; } if (isPatternFill(fill)) { const pattern = fill.pattern ?? defaultPattern.pattern; let strokeWidth = fill.strokeWidth; if (pattern === "backward-slanted-lines" || pattern === "forward-slanted-lines" || pattern === "horizontal-lines" || pattern === "vertical-lines") { strokeWidth ?? (strokeWidth = defaultPattern.strokeWidth); } else { strokeWidth ?? (strokeWidth = 0); } const width2 = fill.width ?? fill.height ?? defaultPattern.width; const height2 = fill.height ?? fill.width ?? defaultPattern.height; return { type: "pattern", pattern, width: width2, height: height2, path: fill.path, padding: fill.padding ?? defaultPattern.padding, fill: fill.fill ?? defaultPattern.fill, fillOpacity: fill.fillOpacity ?? defaultPattern.fillOpacity, backgroundFill: fill.backgroundFill ?? defaultPattern.backgroundFill, backgroundFillOpacity: fill.backgroundFillOpacity ?? defaultPattern.backgroundFillOpacity, stroke: fill.stroke ?? defaultPattern.stroke, strokeOpacity: fill.strokeOpacity ?? defaultPattern.strokeOpacity, strokeWidth, rotation: fill.rotation ?? defaultPattern.rotation, scale: fill.scale ?? defaultPattern.scale }; } if (isImageFill(fill)) { return { type: "image", url: fill.url, width: fill.width, height: fill.height, backgroundFill: fill.backgroundFill ?? defaultImage.backgroundFill, backgroundFillOpacity: fill.backgroundFillOpacity ?? defaultImage.backgroundFillOpacity, rotation: fill.rotation ?? defaultImage.rotation, repeat: fill.repeat ?? defaultImage.repeat, fit: fill.fit ?? defaultImage.fit }; } return fill; } function getShapeStyle(style2, defaultGradient, defaultPattern, defaultImage) { if (!isGradientFill(style2?.fill) && !isPatternFill(style2?.fill) && !isImageFill(style2?.fill)) return style2; return { ...style2, fill: getShapeFill(style2.fill, defaultGradient, defaultPattern, defaultImage) }; } // packages/ag-charts-community/src/util/pixelRatioObserver.ts var PixelRatioObserver = class { constructor(callback2) { this.callback = callback2; this.devicePixelRatio = getWindow("devicePixelRatio") ?? 1; this.devicePixelRatioMediaQuery = void 0; this.devicePixelRatioListener = (e) => { if (e.matches) return; this.devicePixelRatio = getWindow("devicePixelRatio") ?? 1; this.unregisterDevicePixelRatioListener(); this.registerDevicePixelRatioListener(); this.callback(this.pixelRatio); }; } get pixelRatio() { return this.devicePixelRatio; } observe() { this.registerDevicePixelRatioListener(); } disconnect() { this.unregisterDevicePixelRatioListener(); } unregisterDevicePixelRatioListener() { this.devicePixelRatioMediaQuery?.removeEventListener("change", this.devicePixelRatioListener); this.devicePixelRatioMediaQuery = void 0; } registerDevicePixelRatioListener() { const devicePixelRatioMediaQuery = getWindow("matchMedia")?.(`(resolution: ${this.pixelRatio}dppx)`); devicePixelRatioMediaQuery?.addEventListener("change", this.devicePixelRatioListener); this.devicePixelRatioMediaQuery = devicePixelRatioMediaQuery; } }; // packages/ag-charts-community/src/util/sizeMonitor.ts var SizeMonitor = class { constructor() { this.elements = /* @__PURE__ */ new Map(); this.documentReady = false; this.queuedObserveRequests = []; this.onLoad = () => { this.documentReady = true; for (const [el, cb] of this.queuedObserveRequests) { this.observe(el, cb); } this.queuedObserveRequests = []; this.observeWindow(); }; const ResizeObserverCtor = getResizeObserver(); if (ResizeObserverCtor !== void 0) { this.resizeObserver = new ResizeObserverCtor((entries2) => { for (const { target, contentRect: { width: width2, height: height2 } } of entries2) { const entry = this.elements.get(target); this.checkSize(entry, target, width2, height2); } }); } let animationFrame; this.pixelRatioObserver = new PixelRatioObserver(() => { clearTimeout(animationFrame); animationFrame = setTimeout(() => this.checkPixelRatio(), 0); }); this.documentReady = getDocument("readyState") === "complete"; if (this.documentReady) { this.observeWindow(); } else { getWindow()?.addEventListener("load", this.onLoad); } } destroy() { getWindow()?.removeEventListener("load", this.onLoad); this.resizeObserver?.disconnect(); this.resizeObserver = void 0; this.pixelRatioObserver?.disconnect(); this.pixelRatioObserver = void 0; } observeWindow() { this.pixelRatioObserver?.observe(); } checkPixelRatio() { const pixelRatio = this.pixelRatioObserver?.pixelRatio ?? 1; for (const [element2, entry] of this.elements) { if (entry.size != null && entry.size.pixelRatio !== pixelRatio) { const { width: width2, height: height2 } = entry.size; entry.size = { width: width2, height: height2, pixelRatio }; entry.cb(entry.size, element2); } } } checkSize(entry, element2, width2, height2) { if (!entry) return; if (width2 !== entry.size?.width || height2 !== entry.size?.height) { const pixelRatio = this.pixelRatioObserver?.pixelRatio ?? 1; entry.size = { width: width2, height: height2, pixelRatio }; entry.cb(entry.size, element2); } } // Only a single callback is supported. observe(element2, cb) { if (!this.documentReady) { this.queuedObserveRequests.push([element2, cb]); return; } if (this.elements.has(element2)) { this.removeFromQueue(element2); } else { this.resizeObserver?.observe(element2); } const entry = { cb }; this.elements.set(element2, entry); } unobserve(element2) { this.resizeObserver?.unobserve(element2); this.elements.delete(element2); this.removeFromQueue(element2); if (!this.elements.size) { this.destroy(); } } removeFromQueue(element2) { this.queuedObserveRequests = this.queuedObserveRequests.filter(([el]) => el !== element2); } }; // packages/ag-charts-community/src/chart/tooltip/springAnimation.ts var M = 0.1; var K = 200; var C = 12; var DELTA = 0.5; var SpringAnimation = class { constructor() { this.events = new EventEmitter(); this.x1 = Number.NaN; this.y1 = Number.NaN; this.x = Number.NaN; this.y = Number.NaN; this.vx = 0; this.vy = 0; this.t0 = Number.NaN; this.animationFrameHandle = void 0; } reset() { this.x = Number.NaN; this.y = Number.NaN; if (this.animationFrameHandle != null) { cancelAnimationFrame(this.animationFrameHandle); this.animationFrameHandle = void 0; } } update(x, y) { if (Number.isNaN(this.x) || Number.isNaN(this.y)) { this.x = x; this.y = y; this.vx = 0; this.vy = 0; this.emitUpdate(); if (this.animationFrameHandle != null) { cancelAnimationFrame(this.animationFrameHandle); this.animationFrameHandle = void 0; } return; } this.x1 = x; this.y1 = y; this.t0 = Date.now(); this.animationFrameHandle ?? (this.animationFrameHandle = requestAnimationFrame(this.onFrame.bind(this))); } onFrame() { this.animationFrameHandle = void 0; const { x1, y1, t0 } = this; const t1 = Date.now(); const dt = t1 - t0; this.t0 = t1; const stepT = 1e-3; const iterations = Math.trunc(Math.ceil(dt / (stepT * 1e3))); let { x, y, vx, vy } = this; for (let i = 0; i < iterations; i += 1) { const dx = x - x1; const dy = y - y1; const ax = -(K * dx + C * vx) / M; const ay = -(K * dy + C * vy) / M; vx += ax * stepT; vy += ay * stepT; x += vx * stepT; y += vy * stepT; } if (Math.hypot(x - x1, y - y1) < DELTA) { this.x = this.x1; this.y = this.y1; this.vx = 0; this.vy = 0; } else { this.x = x; this.y = y; this.vx = vx; this.vy = vy; this.animationFrameHandle = requestAnimationFrame(this.onFrame.bind(this)); } this.emitUpdate(); } emitUpdate() { this.events.emit("update", { x: this.x, y: this.y }); } }; // packages/ag-charts-community/src/util/sanitize.ts var element = null; function sanitizeHtml(text) { const plainText = toPlainText(text); if (plainText === "") return ""; element ?? (element = createElement("div")); element.textContent = plainText; return element.innerHTML.replaceAll("\n", "
"); } // packages/ag-charts-community/src/scene/shape/line.ts var Line = class extends Shape { constructor(opts = {}) { super(opts); this.x1 = 0; this.y1 = 0; this.x2 = 0; this.y2 = 0; this.fill = void 0; this.strokeWidth = 1; } set x(value) { this.x1 = value; this.x2 = value; } set y(value) { this.y1 = value; this.y2 = value; } get midPoint() { return { x: (this.x1 + this.x2) / 2, y: (this.y1 + this.y2) / 2 }; } computeBBox() { return new BBox( Math.min(this.x1, this.x2), Math.min(this.y1, this.y2), Math.abs(this.x2 - this.x1), Math.abs(this.y2 - this.y1) ); } isPointInPath(x, y) { if (this.x1 === this.x2 || this.y1 === this.y2) { return this.getBBox().clone().grow(this.strokeWidth / 2).containsPoint(x, y); } return false; } distanceSquared(px, py) { const { x1, y1, x2, y2 } = this; return lineDistanceSquared(px, py, x1, y1, x2, y2, Infinity); } render(renderCtx) { const { ctx, devicePixelRatio } = renderCtx; let { x1, y1, x2, y2 } = this; if (x1 === x2) { const { strokeWidth } = this; const x = Math.round(x1 * devicePixelRatio) / devicePixelRatio + Math.trunc(strokeWidth * devicePixelRatio) % 2 / (devicePixelRatio * 2); x1 = x; x2 = x; } else if (y1 === y2) { const { strokeWidth } = this; const y = Math.round(y1 * devicePixelRatio) / devicePixelRatio + Math.trunc(strokeWidth * devicePixelRatio) % 2 / (devicePixelRatio * 2); y1 = y; y2 = y; } ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); this.fillStroke(ctx); this.fillShadow?.markClean(); super.render(renderCtx); } toSVG() { if (!this.visible) return; const element2 = createSvgElement("line"); element2.setAttribute("x1", String(this.x1)); element2.setAttribute("y1", String(this.y1)); element2.setAttribute("x2", String(this.x2)); element2.setAttribute("y2", String(this.y2)); this.applySvgStrokeAttributes(element2); return { elements: [element2] }; } }; Line.className = "Line"; __decorateClass([ SceneChangeDetection() ], Line.prototype, "x1", 2); __decorateClass([ SceneChangeDetection() ], Line.prototype, "y1", 2); __decorateClass([ SceneChangeDetection() ], Line.prototype, "x2", 2); __decorateClass([ SceneChangeDetection() ], Line.prototype, "y2", 2); // packages/ag-charts-community/src/chart/legend/legendSymbol.ts function legendSymbolSvg(symbol, size, lineSize = size * (5 / 3)) { const group = new Group(); const markerStrokeWidth = Math.min(symbol.marker.strokeWidth ?? 1, 2); const lineStrokeWidth = symbol.line?.enabled ? Math.min(symbol.line.strokeWidth, 2) : 0; const width2 = Math.max(symbol.marker.enabled === false ? 0 : size, symbol.line == null ? 0 : lineSize); const height2 = Math.max(symbol.marker.enabled === false ? 0 : size, lineStrokeWidth); if (symbol.line?.enabled) { const { stroke, strokeOpacity, lineDash } = symbol.line; const line = new Line(); line.x1 = 0; line.y1 = height2 / 2; line.x2 = width2; line.y2 = height2 / 2; line.stroke = stroke; line.strokeOpacity = strokeOpacity; line.strokeWidth = lineStrokeWidth; line.lineDash = lineDash; group.append(line); } if (symbol.marker.enabled !== false) { const { shape, fill, fillOpacity, stroke, strokeOpacity, lineDash, lineDashOffset } = symbol.marker; const marker = new Marker(); marker.shape = shape ?? "square"; marker.size = size; marker.fill = fill; marker.fillOpacity = fillOpacity ?? 1; marker.stroke = stroke; marker.strokeOpacity = strokeOpacity ?? 1; marker.strokeWidth = markerStrokeWidth; marker.lineDash = lineDash; marker.lineDashOffset = lineDashOffset ?? 0; const anchor = Marker.anchor(shape); const x = width2 / 2 + (anchor.x - 0.5) * size; const y = height2 / 2 + (anchor.y - 0.5) * size; const scale2 = size / (size + markerStrokeWidth); marker.x = x; marker.y = y; marker.scalingCenterX = x; marker.scalingCenterY = y; marker.scalingX = scale2; marker.scalingY = scale2; group.append(marker); } return Group.toSVG(group, width2, height2); } // packages/ag-charts-community/src/chart/tooltip/tooltipContent.ts var DEFAULT_TOOLTIP_CLASS = "ag-charts-tooltip"; var DEFAULT_TOOLTIP_DARK_CLASS = "ag-charts-tooltip--dark"; function textOrSegmentsIsDefined(value) { if (value == null) { return false; } else if (Array.isArray(value)) { return value.some((segment) => textOrSegmentsIsDefined(segment.text)); } else { return toTextString(value).trim() !== ""; } } function isTooltipValueMissing(value, allowNull = false) { if (value == null) return !allowNull; return typeof value === "number" && !Number.isFinite(value); } function hasAllMissingData(content) { if (content.type === "raw") return false; if (!content.data || content.data.length === 0) return false; return content.data.every((datum) => datum.missing === true); } function aggregateTooltipContent(content) { const out = []; const groupedContents = /* @__PURE__ */ new Map(); for (const item of content) { if (hasAllMissingData(item)) continue; if (item.type === "structured") { const { heading } = item; const insertionTarget = textOrSegmentsIsDefined(heading) ? groupedContents.get(heading) : void 0; const groupedItem = { type: "structured", heading, items: [item] }; if (insertionTarget == null) { groupedContents.set(heading, groupedItem); out.push(groupedItem); } else { insertionTarget.items.push(item); } } else { out.push(item); } } return out; } function tooltipContentAriaLabel(ungroupedContent) { const content = aggregateTooltipContent(ungroupedContent); const ariaLabel = []; for (const c of content) { if (c.type === "raw") { continue; } if (textOrSegmentsIsDefined(c.heading)) { ariaLabel.push(toPlainText(c.heading)); } for (const i of c.items) { if (textOrSegmentsIsDefined(i.title)) { ariaLabel.push(toPlainText(i.title)); } if (i.data) { for (const datum of i.data) { if (datum.missing === true) continue; ariaLabel.push(datum.label ?? datum.fallbackLabel, toPlainText(datum.value)); } } } } return ariaLabel.filter((s) => s !== "").join("; "); } function dataHtml(label, value, inline) { let rowHtml = ""; if (textOrSegmentsIsDefined(label)) { rowHtml += `${sanitizeHtml(label)}`; rowHtml += " "; rowHtml += `${sanitizeHtml(value)}`; } else { rowHtml += `${sanitizeHtml(value)}`; } const rowClassNames = [`${DEFAULT_TOOLTIP_CLASS}-row`]; if (inline) rowClassNames.push(`${DEFAULT_TOOLTIP_CLASS}-row--inline`); rowHtml = `
${rowHtml}
`; return rowHtml; } function tooltipRowContentHtml(content) { let html = ""; if (content.data?.length && content.data.every((datum) => datum.missing === true)) { return html; } const titleDefined = textOrSegmentsIsDefined(content.title); const dataInline = !titleDefined && content.data?.length === 1; const symbol = content.symbol == null ? void 0 : legendSymbolSvg(content.symbol, 12); if (symbol != null && (titleDefined || content.data?.length)) { html += `${symbol}`; } if (titleDefined) { html += `${sanitizeHtml(content.title)}`; html += " "; } if (content.data) { for (const datum of content.data) { if (datum.missing === true) continue; html += dataHtml(datum.label ?? datum.fallbackLabel, toPlainText(datum.value), dataInline); html += " "; } } return html; } function tooltipPaginationContentHtml(localeManager, pagination) { if (localeManager == null || pagination.length === 1) return; const text = localeManager?.t("tooltipPaginationStatus", { index: pagination.index + 1, count: pagination.length }); return ``; } function tooltipContentHtml(localeManager, content, mode, pagination) { const singleItem = content.items.length === 1 ? content.items[0] : void 0; let compact; let compactTitle; let compactFallbackLabel; switch (mode) { case "compact": compact = true; compactTitle = toPlainText(singleItem?.title); break; case "single": const headingDefined = textOrSegmentsIsDefined(content.heading); compact = singleItem != null && (!headingDefined || singleItem.title == null) && singleItem.data?.length === 1 && singleItem.data[0].label == null && singleItem.data[0].value != null; compactFallbackLabel = toPlainText(headingDefined ? content.heading : singleItem?.title); break; case "shared": compact = false; } let html = ""; if (compact && singleItem != null) { if (textOrSegmentsIsDefined(compactTitle)) { html += `${sanitizeHtml(compactTitle)}`; } if (singleItem.data) { for (const datum of singleItem.data) { if (datum.missing === true) continue; html += dataHtml(datum.label ?? compactFallbackLabel, toPlainText(datum.value), false); html += " "; } } } else { if (textOrSegmentsIsDefined(content.heading)) { html += `${sanitizeHtml(toPlainText(content.heading))}`; html += " "; } for (const item of content.items) { html += tooltipRowContentHtml(item); } } if (html.length === 0) return; const paginationContent = mode !== "compact" && pagination != null ? tooltipPaginationContentHtml(localeManager, pagination) : void 0; if (paginationContent + null) { html += paginationContent; } html = `
${html.trimEnd()}
`; return html; } function tooltipPaginationHtml(localeManager, pagination) { const paginationContent = pagination == null ? void 0 : tooltipPaginationContentHtml(localeManager, pagination); if (paginationContent == null) return ""; return `
${paginationContent}
`; } function tooltipHtml(localeManager, content, mode, pagination) { const aggregatedContent = aggregateTooltipContent(content); if (aggregatedContent.length === 0) return; if (aggregatedContent.length === 1 && aggregatedContent[0].type === "structured") { return tooltipContentHtml(localeManager, aggregatedContent[0], mode, pagination); } else { const htmlRows = aggregatedContent.map((c) => { return c.type === "structured" ? tooltipContentHtml(localeManager, c, mode) : c.rawHtmlString; }); if (pagination != null) { htmlRows.push(tooltipPaginationHtml(localeManager, pagination) ?? ""); } return htmlRows.join(""); } } // packages/ag-charts-community/src/chart/tooltip/tooltip.ts var horizontalAlignments = { left: -1, "top-left": -1, "bottom-left": -1, top: 0, center: 0, bottom: 0, right: 1, "top-right": 1, "bottom-right": 1 }; var verticalAlignments = { "top-left": -1, top: -1, "top-right": -1, left: 0, center: 0, right: 0, "bottom-left": 1, bottom: 1, "bottom-right": 1 }; var arrowPositions = { left: 3 /* Right */, "top-left": void 0, "bottom-left": void 0, top: 2 /* Bottom */, center: void 0, bottom: 1 /* Top */, right: 0 /* Left */, "top-right": void 0, "bottom-right": void 0 }; var directionChecks = { top: 2 /* Vertical */, bottom: 2 /* Vertical */, left: 1 /* Horizontal */, right: 1 /* Horizontal */, "top-right": 3 /* Both */, "top-left": 3 /* Both */, "bottom-right": 3 /* Both */, "bottom-left": 3 /* Both */, center: 0 /* None */ }; var defaultPlacements = { pointer: "top", node: "top", chart: "top-left" }; var TooltipPosition = class extends BaseProperties { constructor() { super(...arguments); /** The horizontal offset in pixels for the position of the tooltip. */ this.xOffset = 0; /** The vertical offset in pixels for the position of the tooltip. */ this.yOffset = 0; } }; __decorateClass([ addFakeTransformToInstanceProperty ], TooltipPosition.prototype, "xOffset", 2); __decorateClass([ addFakeTransformToInstanceProperty ], TooltipPosition.prototype, "yOffset", 2); __decorateClass([ addFakeTransformToInstanceProperty ], TooltipPosition.prototype, "anchorTo", 2); __decorateClass([ addFakeTransformToInstanceProperty ], TooltipPosition.prototype, "placement", 2); var Tooltip = class extends BaseProperties { constructor() { super(); this.enabled = true; this.mode = "single"; this.delay = 0; this.range = void 0; this.wrapping = "hyphenate"; this.position = new TooltipPosition(); this.pagination = false; this.darkTheme = false; this.bounds = "extended"; this.cleanup = new CleanupRegistry(); this.springAnimation = new SpringAnimation(); this.enableInteraction = false; this.wrapTypes = ["always", "hyphenate", "on-space", "never"]; this.sizeMonitor = new SizeMonitor(); // Reading the element size is expensive, so cache the result this._elementSize = void 0; this._showTimeout = void 0; this.arrowPosition = void 0; this._visible = false; this.positionParams = void 0; this.localeManager = void 0; this.cleanup.register(this.springAnimation.events.on("update", this.updateTooltipPosition.bind(this))); } get interactive() { return this.enableInteraction; } setup(localeManager, domManager) { if ("togglePopover" in getWindow().HTMLElement.prototype) { this.element = domManager.addChild("tooltip-container", DEFAULT_TOOLTIP_CLASS); this.element.setAttribute("popover", "manual"); this.element.className = DEFAULT_TOOLTIP_CLASS; this.element.style.positionAnchor = domManager.anchorName; this.sizeMonitor.observe(this.element, (size) => { this._elementSize = size; this.updateTooltipPosition(); }); } this.localeManager = localeManager; return () => { domManager.removeChild("tooltip-container", DEFAULT_TOOLTIP_CLASS); this.cleanup.flush(); if (this.element) { this.sizeMonitor.unobserve(this.element); } }; } isVisible() { return this._visible; } contains(node) { return this.element?.contains(node) ?? false; } updateTooltipPosition() { const { element: element2, _elementSize: elementSize, positionParams } = this; if (element2 == null || elementSize == null || positionParams == null) return; const { canvasRect, relativeRect, meta } = positionParams; const { x: canvasX, y: canvasY } = this.springAnimation; const anchorTo = meta.position?.anchorTo ?? "pointer"; let placements2 = meta.position?.placement ?? defaultPlacements[anchorTo]; if (!Array.isArray(placements2)) { placements2 = [placements2]; } const xOffset = meta.position?.xOffset ?? 0; const yOffset = meta.position?.yOffset ?? 0; const minX = relativeRect.x; const minY = relativeRect.y; const maxX = relativeRect.width - elementSize.width - 1 + minX; const maxY = relativeRect.height - elementSize.height + minY; let i = 0; let placement; let position; let constrained = false; do { placement = placements2[i]; i += 1; const tooltipBounds = this.getTooltipBounds({ elementSize, placement, anchorTo, canvasX, canvasY, yOffset, xOffset, canvasRect }); position = calculatePlacement(elementSize.width, elementSize.height, relativeRect, tooltipBounds); constrained = false; if (directionChecks[placement] & 1 /* Horizontal */) { constrained || (constrained = position.x < minX || position.x > maxX); } if (directionChecks[placement] & 2 /* Vertical */) { constrained || (constrained = position.y < minY || position.y > maxY); } } while (i < placements2.length && constrained); const left = clamp(minX, position.x, maxX); const top = clamp(minY, position.y, maxY); constrained || (constrained = left !== position.x || top !== position.y); const defaultShowArrow = anchorTo !== "chart" && !constrained && !xOffset && !yOffset; const showArrow = meta.showArrow ?? this.showArrow ?? defaultShowArrow; this.arrowPosition = showArrow ? arrowPositions[placement] : void 0; this.updateClassModifiers(); element2.style.translate = `${left}px ${top}px`; } /** * Shows tooltip at the given event's coordinates. * If the `html` parameter is missing, moves the existing tooltip to the new position. */ show(boundingRect, canvasRect, meta, content, pagination, instantly = false) { const { element: element2 } = this; if (element2 != null && content != null && content.length !== 0) { const html = tooltipHtml(this.localeManager, content, this.mode, this.pagination ? pagination : void 0); if (html == null) { element2.innerHTML = ""; this.toggle(false); return; } element2.innerHTML = html; } else if (element2 == null || element2.innerHTML === "") { this.toggle(false); return; } const relativeRect = { x: boundingRect.x - canvasRect.x, y: boundingRect.y - canvasRect.y, width: boundingRect.width, height: boundingRect.height }; this.positionParams = { canvasRect, relativeRect, meta }; const anchorTo = meta.position?.anchorTo ?? "pointer"; switch (anchorTo) { case "node": this.springAnimation.update(meta.nodeCanvasX ?? meta.canvasX, meta.nodeCanvasY ?? meta.canvasY); break; case "pointer": this.springAnimation.update(meta.canvasX, meta.canvasY); break; case "chart": this.springAnimation.reset(); } if (meta.enableInteraction) { this.enableInteraction = true; element2.style.pointerEvents = "auto"; element2.removeAttribute("aria-hidden"); element2.tabIndex = -1; } else { this.enableInteraction = false; element2.style.pointerEvents = "none"; element2.setAttribute("aria-hidden", "true"); element2.removeAttribute("tabindex"); } element2.style.setProperty("--top", `${canvasRect.top}px`); element2.style.setProperty("--left", `${canvasRect.left}px`); this.updateClassModifiers(); this.toggle(true, instantly); } hide() { this.toggle(false); } maybeEnterInteractiveTooltip({ relatedTarget }, callback2) { const { interactive, interactiveLeave, enabled, element: element2 } = this; if (element2 == null) return false; if (interactiveLeave) return true; const isEntering = interactive && enabled && this.isVisible() && isNode(relatedTarget) && this.contains(relatedTarget); if (isEntering) { this.interactiveLeave = { callback: callback2, listener: (popoverEvent) => { const isLeaving = popoverEvent.relatedTarget == null || isNode(popoverEvent.relatedTarget) && !this.contains(popoverEvent.relatedTarget); if (isLeaving) { this.popInteractiveLeaveCallback(); } } }; element2.addEventListener("focusout", this.interactiveLeave.listener); element2.addEventListener("mouseout", this.interactiveLeave.listener); } return isEntering; } popInteractiveLeaveCallback() { const { interactiveLeave, element: element2 } = this; this.interactiveLeave = void 0; if (interactiveLeave) { if (element2) { element2.removeEventListener("focusout", interactiveLeave.listener); element2.removeEventListener("mouseout", interactiveLeave.listener); } interactiveLeave.callback(); } } toggle(visible, instantly = false) { const { delay } = this; if (visible && delay > 0 && !instantly) { this._showTimeout ?? (this._showTimeout = setTimeout(() => { this._showTimeout = void 0; this.toggleCallback(true); }, delay)); } else { clearTimeout(this._showTimeout); this._showTimeout = void 0; this.toggleCallback(visible); } } toggleCallback(visible) { if (!this.element?.isConnected) return; if (this._visible === visible) return; this._visible = visible; this.element.togglePopover(visible); if (visible) { this.updateTooltipPosition(); } else { this.springAnimation.reset(); this.popInteractiveLeaveCallback(); } } updateClassModifiers() { if (!this.element?.isConnected) return; const { classList } = this.element; const toggleClass = (name, include) => classList.toggle(`${DEFAULT_TOOLTIP_CLASS}--${name}`, include); toggleClass("no-interaction", !this.enableInteraction); toggleClass("arrow-top", this.arrowPosition === 1 /* Top */); toggleClass("arrow-right", this.arrowPosition === 3 /* Right */); toggleClass("arrow-bottom", this.arrowPosition === 2 /* Bottom */); toggleClass("arrow-left", this.arrowPosition === 0 /* Left */); toggleClass("compact", this.mode === "compact"); classList.toggle(DEFAULT_TOOLTIP_DARK_CLASS, this.darkTheme); for (const wrapType of this.wrapTypes) { classList.toggle(`${DEFAULT_TOOLTIP_CLASS}--wrap-${wrapType}`, wrapType === this.wrapping); } } getTooltipBounds(opts) { const { elementSize, anchorTo, placement, canvasX, canvasY, yOffset, xOffset, canvasRect } = opts; const { width: tooltipWidth, height: tooltipHeight } = elementSize; const bounds = { width: tooltipWidth, height: tooltipHeight }; if (anchorTo === "node" || anchorTo === "pointer") { const horizontalAlignment = horizontalAlignments[placement]; const verticalAlignment = verticalAlignments[placement]; bounds.top = canvasY + yOffset + tooltipHeight * (verticalAlignment - 1) / 2 + 8 * verticalAlignment; bounds.left = canvasX + xOffset + tooltipWidth * (horizontalAlignment - 1) / 2 + 8 * horizontalAlignment; return bounds; } switch (placement) { case "top": { bounds.top = yOffset; bounds.left = canvasRect.width / 2 - tooltipWidth / 2 + xOffset; return bounds; } case "right": { bounds.top = canvasRect.height / 2 - tooltipHeight / 2 + yOffset; bounds.left = canvasRect.width - tooltipWidth + xOffset; return bounds; } case "left": { bounds.top = canvasRect.height / 2 - tooltipHeight / 2 + yOffset; bounds.left = xOffset; return bounds; } case "bottom": { bounds.top = canvasRect.height - tooltipHeight + yOffset; bounds.left = canvasRect.width / 2 - tooltipWidth / 2 + xOffset; return bounds; } case "top-left": { bounds.top = yOffset; bounds.left = xOffset; return bounds; } case "top-right": { bounds.top = yOffset; bounds.left = canvasRect.width - tooltipWidth + xOffset; return bounds; } case "bottom-right": { bounds.top = canvasRect.height - tooltipHeight + yOffset; bounds.left = canvasRect.width - tooltipWidth + xOffset; return bounds; } case "bottom-left": { bounds.top = canvasRect.height - tooltipHeight + yOffset; bounds.left = xOffset; return bounds; } } return bounds; } }; __decorateClass([ addFakeTransformToInstanceProperty ], Tooltip.prototype, "enabled", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Tooltip.prototype, "mode", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Tooltip.prototype, "showArrow", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Tooltip.prototype, "delay", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Tooltip.prototype, "range", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Tooltip.prototype, "wrapping", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Tooltip.prototype, "position", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Tooltip.prototype, "pagination", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Tooltip.prototype, "darkTheme", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Tooltip.prototype, "bounds", 2); // packages/ag-charts-community/src/chart/legend/legendDOMProxy.ts var LegendDOMProxy = class { constructor(ctx, idPrefix) { this.dirty = true; this.itemList = ctx.proxyInteractionService.createProxyContainer({ type: "list", domManagerId: `${idPrefix}-toolbar`, classList: ["ag-charts-proxy-legend-toolbar"], ariaLabel: { id: "ariaLabelLegend" } }); this.paginationGroup = ctx.proxyInteractionService.createProxyContainer({ type: "group", domManagerId: `${idPrefix}-pagination`, classList: ["ag-charts-proxy-legend-pagination"], ariaLabel: { id: "ariaLabelLegendPagination" } }); this.itemDescription = createElement("p"); this.itemDescription.style.display = "none"; this.itemDescription.id = createElementId(); this.itemDescription.textContent = this.getItemAriaDescription(ctx.localeManager); this.itemList.getElement().append(this.itemDescription); } initLegendList(params) { if (!this.dirty) return; const { ctx, itemSelection, datumReader, itemListener } = params; const lm = ctx.localeManager; const count = itemSelection.length; itemSelection.each((markerLabel, datum, index) => { markerLabel.proxyButton?.destroy(); markerLabel.proxyButton = ctx.proxyInteractionService.createProxyElement({ type: "listswitch", textContent: this.getItemAriaText(lm, toPlainText(datumReader.getItemLabel(datum)), index, count), ariaChecked: !!markerLabel.datum.enabled, ariaDescribedBy: this.itemDescription.id, parent: this.itemList }); const button = markerLabel.proxyButton; button.addListener("click", (ev) => itemListener.onClick(ev.sourceEvent, markerLabel.datum, button)); button.addListener("dblclick", (ev) => itemListener.onDoubleClick(ev.sourceEvent, markerLabel.datum)); button.addListener("mouseenter", (ev) => itemListener.onHover(ev.sourceEvent, markerLabel)); button.addListener("mouseleave", () => itemListener.onLeave()); button.addListener("contextmenu", (ev) => itemListener.onContextClick(ev, markerLabel)); button.addListener("blur", () => itemListener.onLeave()); button.addListener("focus", (ev) => itemListener.onHover(ev.sourceEvent, markerLabel)); button.addListener("drag-start", () => { }); }); this.dirty = false; } update(params) { if (params.visible) { this.initLegendList(params); this.updateItemProxyButtons(params); this.updatePaginationProxyButtons(params, true); } this.updateVisibility(params.visible); } updateVisibility(visible) { this.itemList.setHidden(!visible); this.paginationGroup.setHidden(!visible); } updateItemProxyButtons({ itemSelection, group, pagination, interactive }) { const groupBBox = Transformable.toCanvas(group); this.itemList.setBounds(groupBBox); const maxHeight = Math.max(...itemSelection.nodes().map((l) => l.getTextMeasureBBox().height)); itemSelection.each((l, _datum) => { if (l.proxyButton) { const visible = l.pageIndex === pagination.currentPage; const { x, y, height: height2, width: width2 } = Transformable.toCanvas(l, l.getTextMeasureBBox()); const margin = (maxHeight - height2) / 2; const bbox = { x: x - groupBBox.x, y: y - margin - groupBBox.y, height: maxHeight, width: width2 }; const enabled = interactive && visible; l.proxyButton.setCursor("pointer"); l.proxyButton.setEnabled(enabled); l.proxyButton.setPointerEvents(enabled ? void 0 : "none"); l.proxyButton.setBounds(bbox); } }); } updatePaginationProxyButtons(params, init) { const { pagination } = params; this.paginationGroup.setHidden(!pagination.visible); if (init && "ctx" in params) { const { oldPages, newPages } = params; const oldNeedsButtons = (oldPages?.length ?? newPages.length) > 1; const newNeedsButtons = newPages.length > 1; if (oldNeedsButtons !== newNeedsButtons) { if (newNeedsButtons) { this.createPaginationButtons(params); } else { this.destroyPaginationButtons(); } } this.paginationGroup.setAriaHidden(newNeedsButtons ? void 0 : true); } if (this.prevButton && this.nextButton) { const { prev, next } = pagination.computeCSSBounds(); const group = BBox.merge([prev, next]); prev.x -= group.x; prev.y -= group.y; next.x -= group.x; next.y -= group.y; this.paginationGroup.setBounds(group); this.prevButton.setBounds(prev); this.nextButton.setBounds(next); this.prevButton.setEnabled(pagination.currentPage !== 0); this.nextButton.setEnabled(pagination.currentPage !== pagination.totalPages - 1); this.nextButton.setCursor(pagination.getCursor("next")); this.prevButton.setCursor(pagination.getCursor("previous")); } } createPaginationButtons(params) { const { ctx, pagination } = params; if (!this.prevButton) { this.prevButton = ctx.proxyInteractionService.createProxyElement({ type: "button", textContent: { id: "ariaLabelLegendPagePrevious" }, tabIndex: 0, parent: this.paginationGroup }); this.prevButton.addListener("click", (ev) => this.onPageButton(params, ev, "previous")); this.prevButton.addListener("mouseenter", () => pagination.onMouseHover("previous")); this.prevButton.addListener("mouseleave", () => pagination.onMouseHover(void 0)); } if (!this.nextButton) { this.nextButton = ctx.proxyInteractionService.createProxyElement({ type: "button", textContent: { id: "ariaLabelLegendPageNext" }, tabIndex: 0, parent: this.paginationGroup }); this.nextButton.addListener("click", (ev) => this.onPageButton(params, ev, "next")); this.nextButton.addListener("mouseenter", () => pagination.onMouseHover("next")); this.nextButton.addListener("mouseleave", () => pagination.onMouseHover(void 0)); } } destroyPaginationButtons() { this.nextButton?.destroy(); this.prevButton?.destroy(); this.nextButton = void 0; this.prevButton = void 0; } onPageButton(params, ev, node) { params.pagination.onClick(ev.sourceEvent, node); this.updatePaginationProxyButtons(params, false); } onDataUpdate(oldData, newData) { this.dirty = oldData.length !== newData.length || oldData.some((_v, index, _a) => { const [newValue, oldValue] = [newData[index], oldData[index]]; return newValue.id !== oldValue.id; }); } onLocaleChanged(localeManager, itemSelection, datumReader) { const count = itemSelection.length; itemSelection.each(({ proxyButton }, datum, index) => { const button = proxyButton?.getElement(); if (button != null) { const label = toPlainText(datumReader.getItemLabel(datum)); button.textContent = this.getItemAriaText(localeManager, label, index, count); } }); this.itemDescription.textContent = this.getItemAriaDescription(localeManager); } onPageChange(params) { this.updateItemProxyButtons(params); this.updatePaginationProxyButtons(params, false); } getItemAriaText(localeManager, label, index, count) { if (index >= 0 && label) { index++; return localeManager.t("ariaLabelLegendItem", { label, index, count }); } return localeManager.t("ariaLabelLegendItemUnknown"); } getItemAriaDescription(localeManager) { return localeManager.t("ariaDescriptionLegendItem"); } }; // packages/ag-charts-community/src/chart/legend/legendEvent.ts function makeLegendItemEvent(type, { itemId, seriesId, label: { text } }, event) { const result = { defaultPrevented: false, apiEvent: { type, itemId, // FIXME: AG-16068 seriesId, event, text: toPlainText(text), preventDefault: () => result.defaultPrevented = true } }; return result; } // packages/ag-charts-community/src/chart/legend/legendMarkerLabel.ts var LegendMarkerLabel = class extends TranslatableGroup { constructor() { super({ name: "markerLabelGroup" }); this.symbolsGroup = this.appendChild( new Group({ name: "legend-markerLabel-symbols", renderToOffscreenCanvas: true, optimizeForInfrequentRedraws: true }) ); this.label = this.appendChild(new Text()); this.enabled = true; this.pageIndex = Number.NaN; this.spacing = 0; this.length = 0; this.isCustomMarker = false; this.marker = this.symbolsGroup.appendChild(new Marker({ zIndex: 1 })); this.line = this.symbolsGroup.appendChild(new Line({ zIndex: 0 })); this.line.visible = false; this.label.textBaseline = "middle"; this.label.y = 1; } destroy() { super.destroy(); this.proxyButton?.destroy(); } setEnabled(enabled) { this.enabled = enabled; this.refreshVisibilities(); } getTextMeasureBBox() { this.layout(); return BBox.merge([this.symbolsGroup.getBBox(), this.label.getTextMeasureBBox()]); } refreshVisibilities() { const opacity = this.enabled ? 1 : 0.5; this.label.opacity = opacity; this.opacity = opacity; } layout() { const { marker, line, length: length2, isCustomMarker } = this; let centerTranslateX = 0; let centerTranslateY = 0; if (marker.visible) { const { size } = marker; const anchor = Marker.anchor(marker.shape); centerTranslateX = (anchor.x - 0.5) * size + length2 / 2; centerTranslateY = (anchor.y - 0.5) * size; if (isCustomMarker) { marker.x = 0; marker.y = 0; marker.translationX = centerTranslateX; marker.translationY = centerTranslateY; } else { marker.x = centerTranslateX; marker.y = centerTranslateY; marker.translationX = 0; marker.translationY = 0; } } if (line.visible) { line.x1 = 0; line.x2 = length2; line.y1 = 0; line.y2 = 0; } } preRender(renderCtx) { const out = super.preRender(renderCtx); this.layout(); return out; } layoutLabel() { const { length: length2, spacing } = this; this.label.x = length2 + spacing; } computeBBox() { this.layout(); return super.computeBBox(); } }; LegendMarkerLabel.className = "MarkerLabel"; __decorateClass([ ProxyPropertyOnWrite("label") ], LegendMarkerLabel.prototype, "text", 2); __decorateClass([ ProxyPropertyOnWrite("label") ], LegendMarkerLabel.prototype, "fontStyle", 2); __decorateClass([ ProxyPropertyOnWrite("label") ], LegendMarkerLabel.prototype, "fontWeight", 2); __decorateClass([ ProxyPropertyOnWrite("label") ], LegendMarkerLabel.prototype, "fontSize", 2); __decorateClass([ ProxyPropertyOnWrite("label") ], LegendMarkerLabel.prototype, "fontFamily", 2); __decorateClass([ ProxyPropertyOnWrite("label", "fill") ], LegendMarkerLabel.prototype, "color", 2); __decorateClass([ ObserveChanges((target) => target.layoutLabel()) ], LegendMarkerLabel.prototype, "spacing", 2); __decorateClass([ ObserveChanges((target) => target.layoutLabel()) ], LegendMarkerLabel.prototype, "length", 2); __decorateClass([ SceneChangeDetection() ], LegendMarkerLabel.prototype, "isCustomMarker", 2); // packages/ag-charts-community/src/chart/legend/legend.ts function toHighlightNodeDatum(series, legendDatum) { switch (typeof legendDatum.itemId) { case "number": return { series, itemId: void 0, datum: void 0, datumIndex: legendDatum.itemId, legendItemName: legendDatum.legendItemName }; case "string": return { series, itemId: legendDatum.itemId, datum: void 0, datumIndex: void 0, legendItemName: legendDatum.legendItemName }; default: return legendDatum.itemId; } } var LegendLabel = class extends BaseProperties { constructor() { super(...arguments); this.maxLength = void 0; this.color = "black"; this.fontStyle = void 0; this.fontWeight = void 0; this.fontSize = 12 /* SMALL */; this.fontFamily = "Verdana, sans-serif"; } }; __decorateClass([ addFakeTransformToInstanceProperty ], LegendLabel.prototype, "maxLength", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LegendLabel.prototype, "color", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LegendLabel.prototype, "fontStyle", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LegendLabel.prototype, "fontWeight", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LegendLabel.prototype, "fontSize", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LegendLabel.prototype, "fontFamily", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LegendLabel.prototype, "formatter", 2); var LegendMarker = class extends BaseProperties { constructor() { super(...arguments); this.shape = void 0; this.size = 15; this.padding = 8; } }; __decorateClass([ addFakeTransformToInstanceProperty ], LegendMarker.prototype, "shape", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LegendMarker.prototype, "size", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LegendMarker.prototype, "padding", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LegendMarker.prototype, "strokeWidth", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LegendMarker.prototype, "enabled", 2); var LegendLine = class extends BaseProperties { }; __decorateClass([ addFakeTransformToInstanceProperty ], LegendLine.prototype, "strokeWidth", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LegendLine.prototype, "length", 2); var LegendItem = class extends BaseProperties { constructor() { super(...arguments); this.paddingX = 16; this.paddingY = 8; this.showSeriesStroke = false; this.marker = new LegendMarker(); this.label = new LegendLabel(); this.line = new LegendLine(); } }; __decorateClass([ addFakeTransformToInstanceProperty ], LegendItem.prototype, "maxWidth", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LegendItem.prototype, "paddingX", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LegendItem.prototype, "paddingY", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LegendItem.prototype, "showSeriesStroke", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LegendItem.prototype, "marker", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LegendItem.prototype, "label", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LegendItem.prototype, "line", 2); var LegendListeners = class extends BaseProperties { }; __decorateClass([ addFakeTransformToInstanceProperty ], LegendListeners.prototype, "legendItemClick", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LegendListeners.prototype, "legendItemDoubleClick", 2); var fillGradientDefaults2 = { type: "gradient", bounds: "item", gradient: "linear", colorStops: [{ color: "black" }], rotation: 0, reverse: false, colorSpace: "rgb" }; var fillPatternDefaults2 = { type: "pattern", pattern: "forward-slanted-lines", width: 8, height: 8, padding: 1, fill: "black", fillOpacity: 1, backgroundFill: "white", backgroundFillOpacity: 1, stroke: "black", strokeOpacity: 1, strokeWidth: 1, rotation: 0, scale: 1 }; var fillImageDefaults2 = { type: "image", backgroundFill: "black", backgroundFillOpacity: 1, rotation: 0, repeat: "no-repeat", fit: "contain", width: 8, height: 8 }; var Legend = class extends BaseProperties { constructor(ctx) { super(); this.ctx = ctx; this.id = createId(this); this.group = new TranslatableGroup({ name: "legend", zIndex: 16 /* LEGEND */ }); this.itemSelection = Selection.select( this.group, LegendMarkerLabel ); this.containerNode = this.group.appendChild(new Rect({ name: "legend-container" })); this.oldSize = [0, 0]; this.pages = []; this.maxPageSize = [0, 0]; /** Item index to track on re-pagination, so current page updates appropriately. */ this.paginationTrackingIndex = 0; this.truncatedItems = /* @__PURE__ */ new Set(); this._data = []; this.toggleSeries = true; this.item = new LegendItem(); this.listeners = new LegendListeners(); this.enabled = false; this.position = "bottom"; this.border = new Border(this.containerNode); this.cornerRadius = 0; this.fillOpacity = 1; this.padding = 4; this.spacing = 0; this.cleanup = new CleanupRegistry(); this.size = [0, 0]; this._visible = true; this.pagination = new Pagination( (type) => ctx.updateService.update(type), (page) => this.updatePageNumber(page) ); this.pagination.attachPagination(this.group); const { items } = ctx.contextMenuRegistry.builtins; items["toggle-series-visibility"].action = (params) => this.contextToggleVisibility(params); items["toggle-other-series"].action = (params) => this.contextToggleOtherSeries(params); this.cleanup.register( ctx.eventsHub.on("active:load-memento", (event) => this.onActiveLoadMemento(event)), ctx.eventsHub.on("active:update", (event) => this.onActiveUpdate(event)), ctx.eventsHub.on("legend:change", this.onLegendDataChange.bind(this)), ctx.eventsHub.on("legend:change-partial", this.onLegendDataChangePartial.bind(this)), ctx.layoutManager.registerElement(1 /* Legend */, (e) => this.positionLegend(e)), ctx.eventsHub.on("locale:change", () => this.onLocaleChanged()), () => delete items["toggle-series-visibility"].action, () => delete items["toggle-other-series"].action, () => this.group.remove() ); this.domProxy = new LegendDOMProxy(this.ctx, this.id); this.ctx.historyManager.addMementoOriginator(ctx.legendManager); } set data(value) { if (objectsEqual(value, this._data)) return; this.domProxy.onDataUpdate(this._data, value); this._data = value; this.updateGroupVisibility(); } get data() { return this._data; } onLegendDataChange({ legendData = [] }) { if (!this.enabled) return; this.data = legendData.filter((datum) => !datum.hideInLegend); } onLegendDataChangePartial(event) { this.itemSelection.each(({ proxyButton }, { itemId }) => { if (proxyButton == null) return; for (const eventElem of event.legendData) { if (eventElem.itemId === itemId) { proxyButton.setChecked(eventElem.enabled); } } }); } destroy() { this.ctx.domManager.removeChild("canvas-overlay", `${this.id}-toolbar`); this.ctx.domManager.removeChild("canvas-overlay", `${this.id}-pagination`); this.cleanup.flush(); this.itemSelection.clear(); } getOrientation() { return this.orientation ?? "horizontal"; } set visible(value) { this._visible = value; this.updateGroupVisibility(); } get visible() { return this._visible; } updateGroupVisibility() { this.group.visible = this.enabled && this.visible && this.data.length > 0; } updateItemSelection() { const data = [...this.data]; if (this.reverseOrder) { data.reverse(); } this.itemSelection.update(data); } isInteractive() { const { toggleSeries, listeners: { legendItemClick, legendItemDoubleClick } } = this; return toggleSeries || legendItemDoubleClick != null || legendItemClick != null; } checkInteractionState() { return this.ctx.interactionManager.isState(1 /* Frozen */); } attachLegend(scene) { scene.appendChild(this.group); } getItemLabel(datum) { const { formatter: formatter2 } = this.item.label; if (formatter2) { const seriesDatum = datum.datum; return this.cachedCallWithContext(formatter2, { itemId: datum.itemId, value: datum.label.text, seriesId: datum.seriesId, ...seriesDatum && { datum: seriesDatum } }); } return datum.label.text; } /** * The method is given the desired size of the legend, which only serves as a hint. * The vertically oriented legend will take as much horizontal space as needed, but will * respect the height constraints, and the horizontal legend will take as much vertical * space as needed in an attempt not to exceed the given width. * After the layout is done, the {@link size} will contain the actual size of the legend. * If the actual size is not the same as the previous actual size, the legend will fire * the 'layoutChange' event to communicate that another layout is needed, and the above * process should be repeated. * @param width * @param height */ calcLayout(width2, height2) { const { paddingX, paddingY, label, maxWidth, label: { maxLength = Infinity, fontStyle, fontWeight: fontWeight2, fontSize, fontFamily } } = this.item; this.updateItemSelection(); const bboxes = []; const measurer = cachedTextMeasurer(label); const itemMaxWidthPercentage = 0.8; const maxItemWidth = maxWidth ?? width2 * itemMaxWidthPercentage; const { markerWidth, anyLineEnabled } = this.calculateMarkerWidth(); this.itemSelection.each((markerLabel, datum) => { markerLabel.fontStyle = fontStyle; markerLabel.fontWeight = fontWeight2; markerLabel.fontSize = fontSize; markerLabel.fontFamily = fontFamily; const paddedSymbolWidth = this.updateMarkerLabel(markerLabel, datum, markerWidth, anyLineEnabled); const id = datum.itemId ?? datum.id; const labelText = this.getItemLabel(datum); const text = toPlainText(labelText, "").replace(LineSplitter, " "); markerLabel.text = this.truncate(text, maxLength, maxItemWidth, paddedSymbolWidth, measurer, id); bboxes.push(markerLabel.getTextMeasureBBox()); }); width2 = Math.max(1, width2); height2 = Math.max(1, height2); if (!Number.isFinite(width2)) { return {}; } [width2, height2] = this.updateContainer(width2, height2); const size = this.size; const oldSize = this.oldSize; size[0] = width2; size[1] = height2; if (size[0] !== oldSize[0] || size[1] !== oldSize[1]) { oldSize[0] = size[0]; oldSize[1] = size[1]; } const { pages, maxPageHeight, maxPageWidth } = this.updatePagination(bboxes, width2, height2); const oldPages = this.pages; this.pages = pages; this.maxPageSize = [maxPageWidth - paddingX, maxPageHeight - paddingY]; const pageNumber = this.pagination.currentPage; const page = this.pages[pageNumber]; if (this.pages.length < 1 || !page) { this.visible = false; return { oldPages }; } this.visible = true; this.updatePositions(pageNumber); this.update(); return { oldPages }; } isCustomMarker(markerEnabled2, shape) { return markerEnabled2 && shape !== void 0 && typeof shape !== "string"; } calcSymbolsEnabled(symbol) { const { showSeriesStroke, marker } = this.item; const markerEnabled2 = !!marker.enabled || !showSeriesStroke || (symbol.marker.enabled ?? true); const lineEnabled = !!(symbol.line && showSeriesStroke); const isCustomMarker = this.isCustomMarker(markerEnabled2, symbol.marker.shape); return { markerEnabled: markerEnabled2, lineEnabled, isCustomMarker }; } calcSymbolsLengths(symbol, markerEnabled2, lineEnabled) { const { marker, line } = this.item; let customMarkerSize; const { shape } = symbol.marker; if (this.isCustomMarker(markerEnabled2, shape)) { const tmpShape = new Marker(); tmpShape.shape = shape; tmpShape.updatePath(); const bbox = tmpShape.getBBox(); customMarkerSize = Math.max(bbox.width, bbox.height); } const markerLength = markerEnabled2 ? marker.size : 0; const lineLength = lineEnabled ? line.length ?? 25 : 0; return { markerLength, lineLength, customMarkerSize }; } calculateMarkerWidth() { let markerWidth = 0; let anyLineEnabled = false; this.itemSelection.each((_, datum) => { const { symbol } = datum; const { lineEnabled, markerEnabled: markerEnabled2 } = this.calcSymbolsEnabled(symbol); const { markerLength, lineLength, customMarkerSize = -Infinity } = this.calcSymbolsLengths(symbol, markerEnabled2, lineEnabled); markerWidth = Math.max(markerWidth, lineLength, customMarkerSize, markerLength); anyLineEnabled || (anyLineEnabled = lineEnabled); }); return { markerWidth, anyLineEnabled }; } updateMarkerLabel(markerLabel, datum, markerWidth, anyLineEnabled) { const { marker: itemMarker, paddingX } = this.item; const { symbol } = datum; let paddedSymbolWidth = paddingX; const { markerEnabled: markerEnabled2, isCustomMarker } = this.calcSymbolsEnabled(symbol); const spacing = itemMarker.padding; if (markerEnabled2 || anyLineEnabled) { paddedSymbolWidth += spacing + markerWidth; } const { marker, line } = markerLabel; marker.visible = markerEnabled2; if (marker.visible) { marker.shape = itemMarker.shape ?? symbol.marker.shape ?? "square"; marker.size = itemMarker.size; marker.setStyleProperties(this.getMarkerStyles(deepClone(symbol))); } line.visible = anyLineEnabled; if (line.visible) { line.setStyleProperties(this.getLineStyles(symbol)); } markerLabel.length = markerWidth; markerLabel.spacing = spacing; markerLabel.isCustomMarker = isCustomMarker; return paddedSymbolWidth; } updateContainer(width2, height2) { const containerStyles = this.getContainerStyles(); this.containerNode.width = 0; this.containerNode.height = 0; this.containerNode.setStyleProperties(containerStyles); this.containerNode.cornerRadius = containerStyles.cornerRadius; width2 -= containerStyles.strokeWidth * 2 + containerStyles.padding.left + containerStyles.padding.right; height2 -= containerStyles.strokeWidth * 2 + containerStyles.padding.top + containerStyles.padding.bottom; return [width2, height2]; } truncate(text, maxCharLength, maxItemWidth, paddedMarkerWidth, measurer, id) { let addEllipsis = false; if (text.length > maxCharLength) { text = text.substring(0, maxCharLength); addEllipsis = true; } const result = truncateLine(text, measurer, maxItemWidth - paddedMarkerWidth, addEllipsis); if (isTextTruncated(result)) { this.truncatedItems.add(id); } else { this.truncatedItems.delete(id); } return result; } updatePagination(bboxes, width2, height2) { const orientation = this.getOrientation(); const trackingIndex = Math.min(this.paginationTrackingIndex, bboxes.length); this.pagination.orientation = orientation; this.pagination.translationX = 0; this.pagination.translationY = 0; const { pages, maxPageHeight, maxPageWidth, paginationBBox, paginationVertical } = this.calculatePagination( bboxes, width2, height2 ); const newCurrentPage = pages.findIndex((p) => p.endIndex >= trackingIndex); this.pagination.currentPage = clamp(0, newCurrentPage, pages.length - 1); const { paddingX: itemPaddingX, paddingY: itemPaddingY } = this.item; const paginationComponentPadding = 8; const legendItemsWidth = maxPageWidth - itemPaddingX; const legendItemsHeight = maxPageHeight - itemPaddingY; let paginationX = 0; let paginationY = -paginationBBox.y - this.item.marker.size / 2; if (paginationVertical) { paginationY += legendItemsHeight + paginationComponentPadding; } else { paginationX += -paginationBBox.x + legendItemsWidth + paginationComponentPadding; paginationY += (legendItemsHeight - paginationBBox.height) / 2; } this.pagination.translationX = paginationX; this.pagination.translationY = paginationY; this.pagination.update(); this.pagination.updateMarkers(); let pageIndex = 0; this.itemSelection.each((markerLabel, _, nodeIndex) => { if (nodeIndex > (pages[pageIndex]?.endIndex ?? Infinity)) { pageIndex++; } markerLabel.pageIndex = pageIndex; }); return { maxPageHeight, maxPageWidth, pages }; } calculatePagination(bboxes, width2, height2) { const { paddingX: itemPaddingX, paddingY: itemPaddingY } = this.item; const vertPositions = [ "left", "left-top", "left-bottom", "right", "right-top", "right-bottom" ]; const { placement } = expandLegendPosition(this.position); const orientation = this.getOrientation(); const paginationVertical = vertPositions.includes(placement); let paginationBBox = this.pagination.getBBox(); let lastPassPaginationBBox = new BBox(0, 0, 0, 0); let pages = []; let maxPageWidth = 0; let maxPageHeight = 0; let count = 0; const stableOutput = (bbox) => { return bbox.width === paginationBBox.width && bbox.height === paginationBBox.height; }; const forceResult = this.maxWidth !== void 0 && this.maxHeight !== void 0; do { if (count++ > 10) { logger_exports.warn("unable to find stable legend layout."); break; } paginationBBox = lastPassPaginationBBox; const maxWidth = width2 - (paginationVertical ? 0 : paginationBBox.width); const maxHeight = height2 - (paginationVertical ? paginationBBox.height : 0); const layout = gridLayout({ orientation, bboxes, maxHeight, maxWidth, itemPaddingY, itemPaddingX, forceResult }); pages = layout?.pages ?? []; maxPageWidth = layout?.maxPageWidth ?? 0; maxPageHeight = layout?.maxPageHeight ?? 0; const totalPages = pages.length; this.pagination.visible = totalPages > 1; this.pagination.totalPages = totalPages; this.pagination.update(); this.pagination.updateMarkers(); lastPassPaginationBBox = this.pagination.getBBox(); if (!this.pagination.visible) { break; } } while (!stableOutput(lastPassPaginationBBox)); return { maxPageWidth, maxPageHeight, pages, paginationBBox: lastPassPaginationBBox, paginationVertical }; } updatePositions(pageNumber = 0) { const { item: { paddingY }, itemSelection, pages } = this; if (pages.length < 1 || !pages[pageNumber]) { return; } const { columns, startIndex: visibleStart, endIndex: visibleEnd } = pages[pageNumber]; let x = 0; let y = 0; const columnCount = columns.length; const rowCount = columns[0].indices.length; const horizontal = this.getOrientation() === "horizontal"; const itemHeight = columns[0].bboxes[0].height + paddingY; const rowSumColumnWidths = []; itemSelection.each((markerLabel, _, i) => { if (i < visibleStart || i > visibleEnd) { markerLabel.visible = false; return; } const pageIndex = i - visibleStart; let columnIndex; let rowIndex; if (horizontal) { columnIndex = pageIndex % columnCount; rowIndex = Math.floor(pageIndex / columnCount); } else { columnIndex = Math.floor(pageIndex / rowCount); rowIndex = pageIndex % rowCount; } markerLabel.visible = true; const column = columns[columnIndex]; if (!column) { return; } y = Math.floor(itemHeight * rowIndex); x = Math.floor(rowSumColumnWidths[rowIndex] ?? 0); rowSumColumnWidths[rowIndex] = (rowSumColumnWidths[rowIndex] ?? 0) + column.columnWidth; markerLabel.translationX = x; markerLabel.translationY = y; }); } updatePageNumber(pageNumber) { const { itemSelection, group, pagination, pages } = this; const { startIndex, endIndex } = pages[pageNumber]; if (startIndex === 0) { this.paginationTrackingIndex = 0; } else if (pageNumber === pages.length - 1) { this.paginationTrackingIndex = endIndex; } else { this.paginationTrackingIndex = Math.floor((startIndex + endIndex) / 2); } this.pagination.update(); this.pagination.updateMarkers(); this.updatePositions(pageNumber); this.domProxy.onPageChange({ itemSelection, group, pagination, interactive: this.isInteractive() }); this.ctx.updateService.update(9 /* SCENE_RENDER */); } update() { const { label: { color: color2 } } = this.item; this.itemSelection.each((markerLabel, datum) => { markerLabel.setEnabled(datum.enabled); markerLabel.color = color2; }); this.updateContextMenu(); } updateContextMenu() { const action = this.toggleSeries ? "show" : "hide"; this.ctx.contextMenuRegistry.toggle("toggle-series-visibility", action); this.ctx.contextMenuRegistry.toggle("toggle-other-series", action); } getLineStyles(datum) { const { stroke, strokeOpacity = 1, strokeWidth, lineDash } = datum.line ?? {}; const defaultLineStrokeWidth = Math.min(2, strokeWidth ?? 1); return { stroke, strokeOpacity, strokeWidth: this.item.line.strokeWidth ?? defaultLineStrokeWidth, lineDash }; } getMarkerStyles({ marker }) { const { fill, stroke, strokeOpacity = 1, fillOpacity = 1, strokeWidth, lineDash, lineDashOffset } = marker; const defaultLineStrokeWidth = Math.min(2, strokeWidth ?? 1); if (isPatternFill(fill)) { fill.width = 8; fill.height = 8; fill.padding = 1; fill.strokeWidth = Math.min(2, fill.strokeWidth ?? 2); } if (isImageFill(fill)) { fill.fit = "contain"; fill.width = void 0; fill.height = void 0; fill.repeat = "no-repeat"; } return getShapeStyle( { fill, stroke, strokeOpacity, fillOpacity, strokeWidth: this.item.marker.strokeWidth ?? defaultLineStrokeWidth, lineDash, lineDashOffset }, fillGradientDefaults2, fillPatternDefaults2, fillImageDefaults2 ); } getContainerStyles() { const { stroke, strokeOpacity, strokeWidth } = this.border; const { cornerRadius, fill, fillOpacity, padding: padding2 } = this; const isPaddingNumber = typeof padding2 === "number"; return getShapeStyle( { cornerRadius, fill, fillOpacity, padding: { top: isPaddingNumber ? padding2 : padding2.top ?? 0, right: isPaddingNumber ? padding2 : padding2.right ?? 0, bottom: isPaddingNumber ? padding2 : padding2.bottom ?? 0, left: isPaddingNumber ? padding2 : padding2.left ?? 0 }, stroke, strokeOpacity, strokeWidth: this.border.enabled ? strokeWidth : 0 }, fillGradientDefaults2, fillPatternDefaults2, fillImageDefaults2 ); } computePagedBBox() { const actualBBox = Group.computeChildrenBBox(this.group.excludeChildren({ name: "legend-container" })); if (this.pages.length > 1) { const [maxPageWidth, maxPageHeight] = this.maxPageSize; actualBBox.height = Math.max(maxPageHeight, actualBBox.height); actualBBox.width = Math.max(maxPageWidth, actualBBox.width); } const { strokeWidth, padding: padding2 } = this.getContainerStyles(); actualBBox.grow(padding2).grow(strokeWidth); return actualBBox; } findNode(params) { const { datum, proxyButton } = this.itemSelection.select((ml) => ml.datum?.itemId === params.itemId)[0] ?? {}; if (datum === void 0 || proxyButton === void 0) { throw new Error( `AG Charts - Missing required properties { datum: ${datum}, proxyButton: ${JSON.stringify(proxyButton)} }` ); } return { datum, proxyButton }; } contextToggleVisibility(params) { const { datum, proxyButton } = this.findNode(params); this.doClick(params.event, datum, proxyButton); this.clearHighlight(); } contextToggleOtherSeries(params) { this.doDoubleClick(params.event, this.findNode(params).datum); this.clearHighlight(); } onContextClick(widgetEvent, node) { if (this.checkInteractionState()) return; const { sourceEvent } = widgetEvent; const legendItem = node.datum; this.clearHighlight(); if (this.preventHidingAll && this.contextMenuDatum?.enabled && this.getVisibleItemCount() <= 1) { this.ctx.contextMenuRegistry.builtins.items["toggle-series-visibility"].enabled = false; } else { this.ctx.contextMenuRegistry.builtins.items["toggle-series-visibility"].enabled = true; } const toggleOtherSeriesVisible = this.ctx.chartService.series.length > 1 && this.ctx.chartService.series[0]?.getLegendData("category")[0]?.hideToggleOtherSeries !== true; const action = toggleOtherSeriesVisible ? "show" : "hide"; this.ctx.contextMenuRegistry.toggle("toggle-other-series", action); const { offsetX, offsetY } = sourceEvent; const { x: canvasX, y: canvasY } = Transformable.toCanvasPoint(node, offsetX, offsetY); this.ctx.contextMenuRegistry.dispatchContext("legend-item", { widgetEvent, canvasX, canvasY }, { legendItem }); } onClick(event, datum, proxyButton) { if (this.checkInteractionState()) return; if (this.doClick(event, datum, proxyButton)) { event.preventDefault(); } } getVisibleItemCount() { return this.ctx.chartService.series.flatMap((s) => s.getLegendData("category")).filter((d) => d.enabled).length; } doClick(event, datum, proxyButton) { const { listeners: { legendItemClick }, ctx: { chartService }, preventHidingAll, toggleSeries } = this; if (!datum) { return false; } const { legendType, seriesId, itemId, enabled, legendItemName } = datum; const series = chartService.series.find((s) => s.id === seriesId); if (!series) { return false; } let newEnabled = enabled; const clickEvent = makeLegendItemEvent("click", datum, event); if (legendItemClick) { callWithContext([series.properties, this.ctx.chartService], legendItemClick, clickEvent.apiEvent); } if (clickEvent.defaultPrevented) return true; if (toggleSeries) { newEnabled = !enabled; if (preventHidingAll && !newEnabled) { const numVisibleItems = this.getVisibleItemCount(); if (numVisibleItems < 2) { newEnabled = true; } } proxyButton.setChecked(newEnabled); this.ctx.eventsHub.emit("legend:item-click", { legendType, series, itemId, enabled: newEnabled, legendItemName }); } this.updateHighlight(newEnabled, datum, series); this.ctx.legendManager.update(); this.ctx.updateService.update(2 /* PROCESS_DATA */, { forceNodeDataRefresh: true, skipAnimations: datum.skipAnimations ?? false }); return true; } onDoubleClick(event, datum) { if (this.checkInteractionState()) return; if (this.doDoubleClick(event, datum)) { event.preventDefault(); } } doDoubleClick(event, datum) { const { listeners: { legendItemDoubleClick }, ctx: { chartService }, toggleSeries } = this; if (!datum) { return false; } const { legendType, id, itemId, seriesId } = datum; const series = chartService.series.find((s) => s.id === id); if (!series) { return false; } const doubleClickEvent = makeLegendItemEvent("dblclick", datum, event); if (legendItemDoubleClick) { callWithContext( [series.properties, this.ctx.chartService], legendItemDoubleClick, doubleClickEvent.apiEvent ); } if (doubleClickEvent.defaultPrevented) return true; if (toggleSeries) { const legendData = chartService.series.flatMap((s) => s.getLegendData("category")); let numVisibleItems = 0; const visibleLegendItemNames = /* @__PURE__ */ new Set(); for (const d of legendData) { if (!d.enabled) continue; numVisibleItems += 1; if (d.legendItemName != null) { visibleLegendItemNames.add(d.legendItemName); } } if (visibleLegendItemNames.size > 0) { numVisibleItems = visibleLegendItemNames.size; } const clickedItem = legendData.find((d) => d.itemId === itemId && d.seriesId === seriesId); this.ctx.eventsHub.emit("legend:item-double-click", { legendType, series, itemId, numVisibleItems, enabled: clickedItem?.enabled ?? false, legendItemName: clickedItem?.legendItemName }); } this.ctx.legendManager.update(); this.ctx.updateService.update(2 /* PROCESS_DATA */, { forceNodeDataRefresh: true }); return true; } toTooltipMeta(event, node) { let point; if (event instanceof FocusEvent) { point = Transformable.toCanvas(node).computeCenter(); } else { event.preventDefault(); point = Transformable.toCanvasPoint(node, event.offsetX, event.offsetY); } return { canvasX: point.x, canvasY: point.y, showArrow: false }; } onHover(event, node) { if (this.checkInteractionState()) return; if (!this.enabled) throw new Error("AG Charts - onHover handler called on disabled legend"); this.pagination.setPage(node.pageIndex); const datum = node.datum; const series = datum ? this.ctx.chartService.series.find((s) => s.id === datum?.id) : void 0; if (datum && this.truncatedItems.has(datum.itemId ?? datum.id)) { const meta = this.toTooltipMeta(event, node); this.ctx.tooltipManager.updateTooltip(this.id, meta, [ { type: "structured", title: this.getItemLabel(datum) } ]); } else { this.ctx.tooltipManager.removeTooltip(this.id, void 0, true); } this.updateHighlight(datum?.enabled, datum, series); } onLeave() { if (this.checkInteractionState()) return; this.ctx.tooltipManager.removeTooltip(this.id, void 0, true); this.clearHighlight(); } clearHighlight() { this.updateHighlight(void 0, void 0, void 0); } updateHighlight(enabled, legendDatum, series, event) { const updateManagers = (opts) => { if (opts === void 0) { this.ctx.activeManager.clear(); } else { const seriesId = opts.nodeDatum.series.id; const itemId = opts.itemId; this.ctx.activeManager.update({ type: "legend", seriesId, itemId }, void 0); } this.ctx.highlightManager.updateHighlight(this.id, opts?.nodeDatum); }; const highlightNodeDatum = (opts) => { if (this.ctx.interactionManager.isState(64 /* Default */) || event?.initialState) { updateManagers(opts); } else if (this.ctx.interactionManager.isState(4 /* Animation */)) { this.ctx.animationManager.onBatchStop(() => { updateManagers(opts); }); } else if (opts === void 0) { updateManagers(opts); } }; if (enabled === true && series !== void 0 && legendDatum !== void 0) { const itemId = legendDatum.itemId; const nodeDatum = toHighlightNodeDatum(series, legendDatum); highlightNodeDatum({ itemId, nodeDatum }); } else { highlightNodeDatum(void 0); } } onActiveUpdate(activeItem) { if (activeItem?.type === "series-node") { this.ctx.highlightManager.updateHighlight(this.id, void 0); } } onActiveLoadMemento(event) { const { activeItem } = event; if (activeItem?.type !== "legend") { return this.ctx.highlightManager.updateHighlight(this.id, void 0); } const datum = this.data.find((d) => d.seriesId === activeItem.seriesId && d.itemId === activeItem.itemId); const series = this.ctx.chartService.series.find((s) => s.id === activeItem.seriesId); if (series === void 0) { logger_exports.warn(`Cannot find seriesId: "${activeItem.seriesId}"`); event.reject(); } else if (datum === void 0) { const json = JSON.stringify({ seriesId: activeItem.seriesId, itemId: activeItem.itemId }); logger_exports.warn(`cannot find legend item: ${json}`); event.reject(); } else { this.updateHighlight(datum.enabled, datum, series, event); } } onLocaleChanged() { this.updateItemSelection(); this.domProxy.onLocaleChanged(this.ctx.localeManager, this.itemSelection, this); } positionLegend(ctx) { const oldPages = this.positionLegendScene(ctx); this.positionLegendDOM(oldPages); } positionLegendScene(ctx) { if (!this.enabled || !this.data.length) return; const { placement, floating, xOffset, yOffset } = expandLegendPosition(this.position); const layoutBox = floating ? new BBox(0, 0, ctx.width, ctx.height) : ctx.layoutBox; const { x, y, width: width2, height: height2 } = layoutBox; const [legendWidth, legendHeight] = this.calculateLegendDimensions(layoutBox); const { oldPages } = this.calcLayout(legendWidth, legendHeight); const legendBBox = this.computePagedBBox(); if (this.visible) { let unreachable2 = function(_a) { return void 0; }; var unreachable = unreachable2; const legendSpacing = this.spacing; let translationX; let translationY; switch (placement) { case "top": translationX = (width2 - legendBBox.width) / 2; translationY = 0; break; case "bottom": translationX = (width2 - legendBBox.width) / 2; translationY = height2 - legendBBox.height; break; case "right": translationX = width2 - legendBBox.width; translationY = (height2 - legendBBox.height) / 2; break; case "left": translationX = 0; translationY = (height2 - legendBBox.height) / 2; break; case "top-right": case "right-top": translationX = width2 - legendBBox.width; translationY = 0; break; case "top-left": case "left-top": translationX = 0; translationY = 0; break; case "bottom-right": case "right-bottom": translationX = width2 - legendBBox.width; translationY = height2 - legendBBox.height; break; case "bottom-left": case "left-bottom": translationX = 0; translationY = height2 - legendBBox.height; break; default: unreachable2(placement); } if (!floating) { let shrinkAmount; let shrinkDirection; switch (placement) { case "top": case "top-right": case "top-left": shrinkAmount = legendBBox.height + legendSpacing; shrinkDirection = "top"; break; case "bottom": case "bottom-right": case "bottom-left": shrinkAmount = legendBBox.height + legendSpacing; shrinkDirection = "bottom"; break; case "left": case "left-top": case "left-bottom": shrinkAmount = legendBBox.width + legendSpacing; shrinkDirection = "left"; break; case "right": case "right-top": case "right-bottom": shrinkAmount = legendBBox.width + legendSpacing; shrinkDirection = "right"; break; default: unreachable2(placement); } layoutBox.shrink(shrinkAmount, shrinkDirection); } translationX += xOffset; translationY += yOffset; this.group.translationX = Math.floor(x + translationX - legendBBox.x); this.group.translationY = Math.floor(y + translationY - legendBBox.y); this.containerNode.x = legendBBox.x; this.containerNode.y = legendBBox.y; this.containerNode.width = legendBBox.width; this.containerNode.height = legendBBox.height; } return oldPages; } positionLegendDOM(oldPages) { const { ctx, itemSelection, pagination, pages: newPages, group } = this; const visible = this.visible && this.enabled; const interactive = this.isInteractive(); this.domProxy.update({ visible, interactive, ctx, itemSelection, group, pagination, oldPages, newPages, datumReader: this, itemListener: this }); } calculateLegendDimensions(shrinkRect) { const { width: width2, height: height2 } = shrinkRect; const { placement } = expandLegendPosition(this.position); const aspectRatio = width2 / height2; const maxCoefficient = 0.5; const minHeightCoefficient = 0.2; const minWidthCoefficient = 0.25; let legendWidth, legendHeight; function unreachable(_a) { return void 0; } switch (placement) { case "top": case "top-left": case "top-right": case "bottom": case "bottom-left": case "bottom-right": { const heightCoefficient = aspectRatio < 1 ? Math.min(maxCoefficient, minHeightCoefficient * (1 / aspectRatio)) : minHeightCoefficient; legendWidth = this.maxWidth ? Math.min(this.maxWidth, width2) : width2; legendHeight = this.maxHeight ? Math.min(this.maxHeight, height2) : Math.round(height2 * heightCoefficient); break; } case "left": case "left-top": case "left-bottom": case "right": case "right-top": case "right-bottom": { const widthCoefficient = aspectRatio > 1 ? Math.min(maxCoefficient, minWidthCoefficient * aspectRatio) : minWidthCoefficient; legendWidth = this.maxWidth ? Math.min(this.maxWidth, width2) : Math.round(width2 * widthCoefficient); legendHeight = this.maxHeight ? Math.min(this.maxHeight, height2) : height2; break; } default: unreachable(placement); } return [legendWidth, legendHeight]; } cachedCallWithContext(fn, params) { const { callbackCache, chartService } = this.ctx; return callbackCache.call([this, chartService], fn, params); } }; Legend.className = "Legend"; __decorateClass([ addFakeTransformToInstanceProperty ], Legend.prototype, "toggleSeries", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Legend.prototype, "pagination", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Legend.prototype, "item", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Legend.prototype, "listeners", 2); __decorateClass([ ObserveChanges((target, newValue, oldValue) => { target.updateGroupVisibility(); if (newValue === oldValue) { return; } const { ctx: { legendManager, stateManager } } = target; if (oldValue === false && newValue === true) { stateManager.restoreState(legendManager); } }), addFakeTransformToInstanceProperty ], Legend.prototype, "enabled", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Legend.prototype, "position", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Legend.prototype, "maxWidth", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Legend.prototype, "maxHeight", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Legend.prototype, "reverseOrder", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Legend.prototype, "orientation", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Legend.prototype, "preventHidingAll", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Legend.prototype, "border", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Legend.prototype, "cornerRadius", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Legend.prototype, "fill", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Legend.prototype, "fillOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Legend.prototype, "padding", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Legend.prototype, "spacing", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Legend.prototype, "xOffset", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Legend.prototype, "yOffset", 2); // packages/ag-charts-community/src/chart/legend/legendModule.ts var LegendModule = { type: "plugin", name: "legend", version: VERSION, // TODO fix missing behaviour // removable: 'standalone-only', options: { enabled: boolean, position: legendPositionValidator, orientation: union("horizontal", "vertical"), maxWidth: positiveNumber, maxHeight: positiveNumber, spacing: positiveNumber, border: borderOptionsDef, cornerRadius: number, padding, fill: colorUnion, fillOpacity: ratio, preventHidingAll: boolean, reverseOrder: boolean, toggleSeries: boolean, item: { marker: { size: positiveNumber, shape: shapeValidator, padding: positiveNumber, strokeWidth: positiveNumber }, line: { length: positiveNumber, strokeWidth: positiveNumber }, label: { maxLength: positiveNumber, formatter: callback, ...fontOptionsDef }, maxWidth: positiveNumber, paddingX: positiveNumber, paddingY: positiveNumber, showSeriesStroke: boolean }, pagination: { marker: { size: positiveNumber, shape: shapeValidator, padding: positiveNumber }, activeStyle: { ...fillOptionsDef, ...strokeOptionsDef }, inactiveStyle: { ...fillOptionsDef, ...strokeOptionsDef }, highlightStyle: { ...fillOptionsDef, ...strokeOptionsDef }, label: fontOptionsDef }, listeners: { legendItemClick: callback, legendItemDoubleClick: callback } }, themeTemplate: { ...LEGEND_CONTAINER_THEME, enabled: { $and: [ { $greaterThan: [{ $size: { $path: "/series" } }, 1] }, { $or: [ { $isChartType: "cartesian" }, { $isChartType: "standalone" }, { $and: [ { $isChartType: "polar" }, { $not: { $isSeriesType: "pie" } }, { $not: { $isSeriesType: "donut" } } ] } ] } ] }, position: "bottom" /* BOTTOM */, orientation: { $if: [ { $or: [ { $eq: [{ $path: "./position" }, "left" /* LEFT */] }, { $eq: [{ $path: "./position" }, "left-top" /* LEFT_TOP */] }, { $eq: [{ $path: "./position" }, "left-bottom" /* LEFT_BOTTOM */] }, { $eq: [{ $path: "./position" }, "right" /* RIGHT */] }, { $eq: [{ $path: "./position" }, "right-top" /* RIGHT_TOP */] }, { $eq: [{ $path: "./position" }, "right-bottom" /* RIGHT_BOTTOM */] }, { $eq: [{ $path: "./position/placement" }, "left" /* LEFT */] }, { $eq: [{ $path: "./position/placement" }, "left-top" /* LEFT_TOP */] }, { $eq: [{ $path: "./position/placement" }, "left-bottom" /* LEFT_BOTTOM */] }, { $eq: [{ $path: "./position/placement" }, "right" /* RIGHT */] }, { $eq: [{ $path: "./position/placement" }, "right-top" /* RIGHT_TOP */] }, { $eq: [{ $path: "./position/placement" }, "right-bottom" /* RIGHT_BOTTOM */] } ] }, "vertical", "horizontal" ] }, spacing: 30, listeners: {}, toggleSeries: true, item: { paddingX: 16, paddingY: 8, marker: { size: 15, padding: 8 }, showSeriesStroke: true, label: { color: { $ref: "textColor" }, fontSize: { $rem: FONT_SIZE_RATIO.SMALL }, fontFamily: { $ref: "fontFamily" }, fontWeight: { $ref: "fontWeight" } } }, reverseOrder: false, pagination: { marker: { size: 12 }, activeStyle: { fill: { $ref: "foregroundColor" } }, inactiveStyle: { fill: { $ref: "subtleTextColor" } }, highlightStyle: { fill: { $ref: "foregroundColor" } }, label: { color: { $ref: "textColor" } } }, fill: { $if: [{ $path: ["./position/floating", false] }, { $ref: "chartBackgroundColor" }, "transparent"] } }, create: (ctx) => { const moduleInstance = new Legend(ctx); moduleInstance.attachLegend(ctx.scene); return moduleInstance; } }; // packages/ag-charts-community/src/locale/locale.ts var Locale = class extends AbstractModuleInstance { constructor(ctx) { super(); this.ctx = ctx; this.localeText = void 0; } }; __decorateClass([ ObserveChanges((target) => { target.ctx.localeManager.setLocaleText(target.localeText); }), addFakeTransformToInstanceProperty ], Locale.prototype, "localeText", 2); __decorateClass([ ObserveChanges((target) => { target.ctx.localeManager.setLocaleTextFormatter(target.getLocaleText); }), addFakeTransformToInstanceProperty ], Locale.prototype, "getLocaleText", 2); // packages/ag-charts-community/src/locale/localeModule.ts var LocaleModule = { type: "plugin", name: "locale", version: VERSION, options: { localeText: object, getLocaleText: callbackOf(string) }, create: (ctx) => new Locale(ctx) }; // packages/ag-charts-community/src/chart/axesOptionsDefs.ts var timeIntervalUnit = union("millisecond", "second", "minute", "hour", "day", "month", "year"); var timeIntervalDefs = { unit: required(timeIntervalUnit), step: positiveNumberNonZero, epoch: date, utc: boolean }; timeIntervalDefs.every = callback; var timeInterval2 = optionsDefs(timeIntervalDefs, "a time interval object"); var commonCrossLineLabelOptionsDefs = { enabled: boolean, text: string, padding: number, border: borderOptionsDef, cornerRadius: number, ...fontOptionsDef, ...fillOptionsDef }; var commonCrossLineOptionsDefs = attachDescription( { enabled: boolean, type: required(union("line", "range")), range: and( attachDescription((_, { options }) => options.type === "range", "crossLine type to be 'range'"), arrayOf(defined), arrayLength(2, 2) ), value: and( attachDescription((_, { options }) => options.type === "line", "crossLine type to be 'line'"), defined ), label: commonCrossLineLabelOptionsDefs, fill: color, fillOpacity: ratio, ...strokeOptionsDef, ...lineDashOptionsDef }, "cross-line options" ); var cartesianCrossLineOptionsDefs = { ...commonCrossLineOptionsDefs, label: { ...commonCrossLineLabelOptionsDefs, position: union( "top", "left", "right", "bottom", "top-left", "top-right", "bottom-left", "bottom-right", "inside", "inside-left", "inside-right", "inside-top", "inside-bottom", "inside-top-left", "inside-bottom-left", "inside-top-right", "inside-bottom-right" ), rotation: number } }; var commonAxisLabelOptionsDefs = { enabled: boolean, rotation: number, avoidCollisions: boolean, minSpacing: positiveNumber, spacing: positiveNumber, formatter: callbackOf(textOrSegments), itemStyler: callbackDefs({ ...fontOptionsDef, ...labelBoxOptionsDef, spacing: number }), ...fontOptionsDef, ...labelBoxOptionsDef }; var cartesianAxisLabelOptionsDefs = { autoRotate: boolean, autoRotateAngle: number, wrapping: union("never", "always", "hyphenate", "on-space"), truncate: boolean, ...commonAxisLabelOptionsDefs }; var cartesianNumericAxisLabel = { format: numberFormatValidator, ...cartesianAxisLabelOptionsDefs }; var cartesianTimeAxisLabel = { format: or(string, object), ...cartesianAxisLabelOptionsDefs }; var cartesianAxisTick = { enabled: boolean, width: positiveNumber, size: positiveNumber, stroke: color }; var cartesianTimeAxisParentLevel = { enabled: boolean, label: cartesianTimeAxisLabel, tick: cartesianAxisTick }; var commonAxisIntervalOptionsDefs = { values: arrayOf(defined), minSpacing: positiveNumber }; var commonAxisOptionsDefs = { reverse: boolean, gridLine: { enabled: boolean, width: positiveNumber, style: arrayOfDefs( { fill: color, fillOpacity: positiveNumber, stroke: or(color, themeOperator), strokeWidth: positiveNumber, lineDash: arrayOf(positiveNumber) }, "a grid-line style object array" ) }, interval: commonAxisIntervalOptionsDefs, label: commonAxisLabelOptionsDefs, line: { enabled: boolean, width: positiveNumber, stroke: color }, tick: cartesianAxisTick, context: () => true }; commonAxisOptionsDefs.layoutConstraints = undocumented({ stacked: required(boolean), align: required(union("start", "end")), unit: required(union("percent", "px")), width: required(positiveNumber) }); var cartesianAxisOptionsDefs = { ...commonAxisOptionsDefs, crossAt: { value: required(or(number, date, string, arrayOf(string))), sticky: boolean }, crossLines: arrayOfDefs(cartesianCrossLineOptionsDefs, "a cross-line options array"), position: union("top", "right", "bottom", "left"), thickness: positiveNumber, maxThicknessRatio: ratio, title: { enabled: boolean, text: textOrSegments, spacing: positiveNumber, formatter: callbackOf(textOrSegments), ...fontOptionsDef } }; cartesianAxisOptionsDefs.title._enabledFromTheme = undocumented(boolean); var cartesianAxisBandHighlightOptions = { enabled: boolean, ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }; function cartesianAxisCrosshairOptions(canFormat, timeFormat) { const baseCrosshairLabel = { enabled: boolean, xOffset: number, yOffset: number, formatter: callbackOf(string), renderer: callbackOf( or( string, optionsDefs( { text: string, color, backgroundColor: color, opacity: ratio }, "crosshair label renderer result object" ) ) ) }; let crosshairLabel; if (canFormat) { crosshairLabel = { ...baseCrosshairLabel, format: timeFormat ? or( string, optionsDefs({ millisecond: string, second: string, hour: string, day: string, month: string, year: string }) ) : string }; } return { enabled: boolean, snap: boolean, label: crosshairLabel ?? baseCrosshairLabel, ...strokeOptionsDef, ...lineDashOptionsDef }; } function continuousAxisOptions(validDatum, supportTimeInterval) { return { min: and(validDatum, lessThan("max")), max: and(validDatum, greaterThan("min")), preferredMin: and(validDatum, lessThan("preferredMax"), lessThan("max")), preferredMax: and(validDatum, greaterThan("preferredMin"), greaterThan("min")), nice: boolean, interval: { step: supportTimeInterval ? or(positiveNumberNonZero, timeIntervalUnit, timeInterval2) : positiveNumberNonZero, values: arrayOf(validDatum), minSpacing: and(positiveNumber, lessThan("maxSpacing")), maxSpacing: and(positiveNumber, greaterThan("minSpacing")) } }; } var discreteTimeAxisIntervalOptionsDefs = { step: or(positiveNumberNonZero, timeIntervalUnit, timeInterval2), values: arrayOf(or(number, date)), minSpacing: and(positiveNumber, lessThan("maxSpacing")), maxSpacing: and(positiveNumber, greaterThan("minSpacing")), placement: union("on", "between") }; var categoryAxisOptionsDefs = { ...cartesianAxisOptionsDefs, type: constant("category"), label: cartesianAxisLabelOptionsDefs, paddingInner: ratio, paddingOuter: ratio, groupPaddingInner: ratio, crosshair: cartesianAxisCrosshairOptions(), bandAlignment: union("justify", "start", "center", "end"), bandHighlight: cartesianAxisBandHighlightOptions, interval: { ...commonAxisIntervalOptionsDefs, placement: union("on", "between") } }; var groupedCategoryAxisOptionsDefs = { ...cartesianAxisOptionsDefs, type: constant("grouped-category"), label: cartesianAxisLabelOptionsDefs, crosshair: cartesianAxisCrosshairOptions(), bandHighlight: cartesianAxisBandHighlightOptions, paddingInner: ratio, groupPaddingInner: ratio, depthOptions: arrayOfDefs( { label: { enabled: boolean, avoidCollisions: boolean, wrapping: union("never", "always", "hyphenate", "on-space"), truncate: boolean, rotation: number, spacing: number, ...fontOptionsDef, ...labelBoxOptionsDef }, tick: { enabled: boolean, stroke: color, width: positiveNumber } }, "depth options objects array" ) }; var numberAxisOptionsDefs = { ...cartesianAxisOptionsDefs, ...continuousAxisOptions(number), type: constant("number"), label: cartesianNumericAxisLabel, crosshair: cartesianAxisCrosshairOptions(true) }; var logAxisOptionsDefs = { ...cartesianAxisOptionsDefs, ...continuousAxisOptions(number), type: constant("log"), base: and( positiveNumberNonZero, attachDescription((value) => value !== 1, "not equal to 1") ), label: cartesianNumericAxisLabel, crosshair: cartesianAxisCrosshairOptions(true) }; var timeAxisOptionsDefs = { ...cartesianAxisOptionsDefs, ...continuousAxisOptions(or(number, date), true), type: constant("time"), label: cartesianTimeAxisLabel, parentLevel: cartesianTimeAxisParentLevel, crosshair: cartesianAxisCrosshairOptions(true, true) }; var unitTimeAxisOptionsDefs = { ...cartesianAxisOptionsDefs, type: constant("unit-time"), unit: or(timeInterval2, timeIntervalUnit), label: cartesianTimeAxisLabel, parentLevel: cartesianTimeAxisParentLevel, paddingInner: ratio, paddingOuter: ratio, groupPaddingInner: ratio, crosshair: cartesianAxisCrosshairOptions(true, true), bandAlignment: union("justify", "start", "center", "end"), bandHighlight: cartesianAxisBandHighlightOptions, min: and(or(number, date), lessThan("max")), max: and(or(number, date), greaterThan("min")), preferredMin: and(or(number, date), lessThan("preferredMax"), lessThan("max")), preferredMax: and(or(number, date), greaterThan("preferredMin"), greaterThan("min")), interval: discreteTimeAxisIntervalOptionsDefs }; // packages/ag-charts-community/src/scale/bandScale.ts var _BandScale = class _BandScale extends AbstractScale { constructor() { super(...arguments); this.invalid = true; this.range = [0, 1]; this.round = false; this._bandwidth = 1; this._step = 1; this._inset = 1; this._rawBandwidth = 1; /** * The ratio of the range that is reserved for space between bands. */ this._paddingInner = 0; /** * The ratio of the range that is reserved for space before the first * and after the last band. */ this._paddingOuter = 0; } static is(value) { return value instanceof _BandScale; } get bandwidth() { this.refresh(); return this._bandwidth; } get step() { this.refresh(); return this._step; } get inset() { this.refresh(); return this._inset; } get rawBandwidth() { this.refresh(); return this._rawBandwidth; } set padding(value) { value = clamp(0, value, 1); this._paddingInner = value; this._paddingOuter = value; } get padding() { return this._paddingInner; } set paddingInner(value) { this.invalid = true; this._paddingInner = clamp(0, value, 1); } get paddingInner() { return this._paddingInner; } set paddingOuter(value) { this.invalid = true; this._paddingOuter = clamp(0, value, 1); } get paddingOuter() { return this._paddingOuter; } /** Override in subclass to provide band count without triggering full band materialization */ getBandCountForUpdate() { return this.bands.length; } refresh() { if (!this.invalid) return; this.invalid = false; this.update(); if (this.invalid) { logger_exports.warnOnce("Expected update to not invalidate scale"); } } convert(d, options) { this.refresh(); const i = this.findIndex(d, options?.alignment); if (i == null || i < 0 || i >= this.getBandCountForUpdate()) { return Number.NaN; } return this.ordinalRange(i); } getDomainMinMax() { return unpackDomainMinMax(this.domain); } invertNearestIndex(position) { this.refresh(); const bandCount = this.getBandCountForUpdate(); if (bandCount === 0) return -1; let low = 0; let high = bandCount - 1; let closestDistance = Infinity; let closestIndex = 0; while (low <= high) { const mid = Math.trunc((high + low) / 2); const p = this.ordinalRange(mid); const distance2 = Math.abs(p - position); if (distance2 === 0) return mid; if (distance2 < closestDistance) { closestDistance = distance2; closestIndex = mid; } if (p < position) { low = mid + 1; } else { high = mid - 1; } } return closestIndex; } update() { const [r0, r1] = this.range; let { _paddingInner: paddingInner } = this; const { _paddingOuter: paddingOuter } = this; const bandCount = this.getBandCountForUpdate(); if (bandCount === 0) return; const rangeDistance = r1 - r0; let rawStep; if (bandCount === 1) { paddingInner = 0; rawStep = rangeDistance * (1 - paddingOuter * 2); } else { rawStep = rangeDistance / Math.max(1, bandCount - paddingInner + paddingOuter * 2); } const round3 = this.round && Math.floor(rawStep) > 0; const step = round3 ? Math.floor(rawStep) : rawStep; let inset = r0 + (rangeDistance - step * (bandCount - paddingInner)) / 2; let bandwidth = step * (1 - paddingInner); if (round3) { inset = Math.round(inset); bandwidth = Math.round(bandwidth); } this._step = step; this._inset = inset; this._bandwidth = bandwidth; this._rawBandwidth = rawStep * (1 - paddingInner); } ordinalRange(i) { const { _inset: inset, _step: step, range: range3 } = this; const min = Math.min(range3[0], range3[1]); const max = Math.max(range3[0], range3[1]); return clamp(min, inset + step * i, max); } }; __decorateClass([ Invalidating ], _BandScale.prototype, "range", 2); __decorateClass([ Invalidating ], _BandScale.prototype, "round", 2); var BandScale = _BandScale; // packages/ag-charts-community/src/scale/categoryScale.ts var CategoryScale = class _CategoryScale extends BandScale { constructor() { super(...arguments); this.type = "category"; this.defaultTickCount = 0; /** * Maps datum to its index in the {@link domain} array. * Used to check for duplicate data (not allowed). */ this.index = /* @__PURE__ */ new Map(); this.indexInitialized = false; /** * Contains unique data only. */ this._domain = []; } static is(value) { return value instanceof _CategoryScale; } set domain(values) { if (this._domain === values) return; this.invalid = true; this._domain = values; this.index.clear(); this.indexInitialized = false; } get domain() { return this._domain; } get bands() { return this._domain; } normalizeDomains(...domains) { let normalizedDomain = void 0; const seenDomains = /* @__PURE__ */ new Set(); let animatable = true; for (const input of domains) { const domain = input.domain; if (seenDomains.has(domain)) continue; seenDomains.add(domain); if (normalizedDomain == null) { normalizedDomain = deduplicateCategories(domain); } else { animatable && (animatable = domainOrderedToNormalizedDomain(domain, normalizedDomain)); normalizedDomain = deduplicateCategories([...normalizedDomain, ...domain]); } } normalizedDomain ?? (normalizedDomain = []); return { domain: normalizedDomain, animatable }; } toDomain(_value) { return void 0; } invert(position, nearest = false) { this.refresh(); const offset = nearest ? this.bandwidth / 2 : 0; const index = this.invertNearestIndex(Math.max(0, position - offset)); const matches = nearest || position === this.ordinalRange(index); return matches ? this.domain[index] : void 0; } ticks(params, domain = this.domain, visibleRange) { const { bands } = this; let { tickCount } = params; if (tickCount === 0) { const firstTickIndex2 = bands.length > 1 ? 1 : 0; const ticks2 = bands[firstTickIndex2] ? [bands[firstTickIndex2]] : []; return { ticks: ticks2, count: void 0, firstTickIndex: firstTickIndex2 }; } let step = tickCount != null && tickCount !== 0 ? Math.trunc(bands.length / tickCount) : 1; step = previousPowerOf2(step); if (step <= 1) { return filterVisibleTicks(domain, false, visibleRange); } tickCount = Math.trunc(bands.length / step); const span = step * tickCount; const inset = previousPowerOf2(Math.trunc((bands.length - span) / 2)); const vt0 = clamp(0, Math.floor((visibleRange?.[0] ?? 0) * bands.length), bands.length); const vt1 = clamp(0, Math.ceil((visibleRange?.[1] ?? 1) * bands.length), bands.length); const i0 = Math.floor((vt0 - inset) / step) * step + inset; const i1 = Math.ceil((vt1 - inset) / step) * step + inset; const ticks = []; for (let i = i0; i < i1; i += step) { if (i >= 0 && i < bands.length) { ticks.push(bands[i]); } } let firstTickIndex = ticks.length > 0 ? this.findIndex(ticks[0]) : void 0; if (firstTickIndex != null) { firstTickIndex = Math.floor((firstTickIndex - inset) / step); } return { ticks, count: void 0, firstTickIndex }; } findIndex(value) { const { index, indexInitialized } = this; if (!indexInitialized) { const { domain } = this; for (let i = 0; i < domain.length; i++) { index.set(dateToNumber(domain[i]), i); } this.indexInitialized = true; } return index.get(dateToNumber(value)); } }; function deduplicateCategories(d) { let domain; const uniqueValues = /* @__PURE__ */ new Set(); for (const value of d) { const key = dateToNumber(value); const lastSize = uniqueValues.size; uniqueValues.add(key); const isUniqueValue = uniqueValues.size !== lastSize; if (isUniqueValue) { domain?.push(value); } else { domain ?? (domain = d.slice(0, uniqueValues.size)); } } return domain ?? d; } function domainOrderedToNormalizedDomain(domain, normalizedDomain) { let normalizedIndex = -1; for (const value of domain) { const normalizedNextIndex = normalizedDomain.indexOf(value); if (normalizedNextIndex === -1) { normalizedIndex = Infinity; } else if (normalizedNextIndex <= normalizedIndex) { return false; } else { normalizedIndex = normalizedNextIndex; } } return true; } // packages/ag-charts-community/src/motion/fromToMotion.ts var fromToMotion_exports = {}; __export(fromToMotion_exports, { NODE_UPDATE_STATE_TO_PHASE_MAPPING: () => NODE_UPDATE_STATE_TO_PHASE_MAPPING, fromToMotion: () => fromToMotion, staticFromToMotion: () => staticFromToMotion }); // packages/ag-charts-community/src/util/interpolate.ts function interpolateNumber(a, b) { return (d) => Number(a) * (1 - d) + Number(b) * d; } function interpolateColor(a, b) { if (typeof a === "string") { try { a = Color.fromString(a); } catch { a = Color.fromArray([0, 0, 0]); } } if (typeof b === "string") { try { b = Color.fromString(b); } catch { b = Color.fromArray([0, 0, 0]); } } return (d) => Color.mix(a, b, d).toRgbaString(); } // packages/ag-charts-community/src/motion/animation.ts var QUICK_TRANSITION = 0.2; var PHASE_ORDER = ["initial", "remove", "update", "add", "trailing", "end", "none"]; var PHASE_METADATA = { initial: { animationDuration: 1, animationDelay: 0 }, add: { animationDuration: 0.25, animationDelay: 0.75 }, remove: { animationDuration: 0.25, animationDelay: 0 }, update: { animationDuration: 0.5, animationDelay: 0.25 }, trailing: { animationDuration: QUICK_TRANSITION, animationDelay: 1, skipIfNoEarlierAnimations: true }, end: { animationDelay: 1 + QUICK_TRANSITION, animationDuration: 0, skipIfNoEarlierAnimations: true }, none: { animationDuration: 0, animationDelay: 0 } }; function isNodeArray(array2) { return array2.every((n) => n instanceof Node); } function deconstructSelectionsOrNodes(selectionsOrNodes) { return isNodeArray(selectionsOrNodes) ? { nodes: selectionsOrNodes, selections: [] } : { nodes: [], selections: selectionsOrNodes }; } function animationValuesEqual(a, b) { if (a === b) { return true; } else if (Array.isArray(a) && Array.isArray(b)) { return a.length === b.length && a.every((v, i) => animationValuesEqual(v, b[i])); } else if (isInterpolating(a) && isInterpolating(b)) { return a.equals(b); } else if (isPlainObject(a) && isPlainObject(b)) { return objectsEqualWith(a, b, animationValuesEqual); } return false; } var Animation = class { constructor(opts) { this.isComplete = false; this.elapsed = 0; this.iteration = 0; this.isPlaying = false; this.isReverse = false; this.id = opts.id; this.groupId = opts.groupId; this.autoplay = opts.autoplay ?? true; this.ease = opts.ease ?? linear; this.phase = opts.phase; const durationProportion = opts.duration ?? PHASE_METADATA[this.phase].animationDuration; this.duration = durationProportion * opts.defaultDuration; this.delay = (opts.delay ?? 0) * opts.defaultDuration; this.onComplete = opts.onComplete; this.onPlay = opts.onPlay; this.onStop = opts.onStop; this.onUpdate = opts.onUpdate; this.interpolate = this.createInterpolator(opts.from, opts.to); this.from = opts.from; if (opts.skip === true) { this.onUpdate?.(opts.to, false, this); this.onStop?.(this); this.onComplete?.(this); this.isComplete = true; } if (opts.collapsable !== false) { this.duration = this.checkCollapse(opts, this.duration); } } checkCollapse(opts, calculatedDuration) { return animationValuesEqual(opts.from, opts.to) ? 0 : calculatedDuration; } play(initialUpdate = false) { if (this.isPlaying || this.isComplete) return; this.isPlaying = true; this.onPlay?.(this); if (!this.autoplay) return; this.autoplay = false; if (!initialUpdate) return; this.onUpdate?.(this.from, true, this); } stop() { this.isPlaying = false; if (!this.isComplete) { this.isComplete = true; this.onStop?.(this); } } update(time3) { if (this.isComplete) return time3; if (!this.isPlaying && this.autoplay) { this.play(true); } const previousElapsed = this.elapsed; this.elapsed += time3; if (this.delay > this.elapsed) return 0; const value = this.interpolate(this.isReverse ? 1 - this.delta : this.delta); this.onUpdate?.(value, false, this); const totalDuration = this.delay + this.duration; if (this.elapsed >= totalDuration) { this.stop(); this.isComplete = true; this.onComplete?.(this); return time3 - (totalDuration - previousElapsed); } return 0; } get delta() { return this.ease(clamp(0, (this.elapsed - this.delay) / this.duration, 1)); } createInterpolator(from3, to) { if (typeof to !== "object" || isInterpolating(to)) { return this.interpolateValue(from3, to); } else if (Array.isArray(to)) { const interpolatorValues = []; for (let i = 0; i < to.length; i++) { const interpolator = this.createInterpolator(from3[i], to[i]); if (interpolator != null) { interpolatorValues.push(interpolator); } } return (d) => { const out = []; for (const interpolator of interpolatorValues) { out.push(interpolator(d)); } return out; }; } const interpolatorEntries = []; for (const key of Object.keys(to)) { const interpolator = this.createInterpolator(from3[key], to[key]); if (interpolator != null) { interpolatorEntries.push([key, interpolator]); } } return (d) => { const result = {}; for (const [key, interpolator] of interpolatorEntries) { result[key] = interpolator(d); } return result; }; } interpolateValue(a, b) { if (a == null || b == null) { return; } else if (isInterpolating(a)) { return (d) => a[interpolate](b, d); } try { switch (typeof a) { case "number": return interpolateNumber(a, b); case "string": return interpolateColor(a, b); case "boolean": if (a === b) { return () => a; } break; case "object": return () => a; default: throw new Error(`Unable to interpolate values: ${a}, ${b}`); } } catch { } throw new Error(`Unable to interpolate values: ${a}, ${b}`); } }; // packages/ag-charts-community/src/motion/fromToMotion.ts var NODE_UPDATE_STATE_TO_PHASE_MAPPING = { added: "add", updated: "update", removed: "remove", unknown: "initial", "no-op": "none" }; function fromToMotion(groupId, subId, animationManager, selectionsOrNodes, fns, getDatumId, diff2) { const { fromFn, toFn, applyFn = (node, props) => node.setProperties(props) } = fns; const { nodes, selections } = deconstructSelectionsOrNodes(selectionsOrNodes); const processNodes = (liveNodes, subNodes) => { let prevFromProps; let liveNodeIndex = 0; let nodeIndex = 0; for (const node of subNodes) { const isLive = liveNodes[liveNodeIndex] === node; const ctx = { last: nodeIndex >= subNodes.length - 1, lastLive: liveNodeIndex >= liveNodes.length - 1, prev: subNodes[nodeIndex - 1], prevFromProps, prevLive: liveNodes[liveNodeIndex - 1], next: subNodes[nodeIndex + 1], nextLive: liveNodes[liveNodeIndex + (isLive ? 1 : 0)] }; const animationId = `${groupId}_${subId}_${node.id}`; animationManager.stopByAnimationId(animationId); let status = "unknown"; if (!isLive) { status = "removed"; } else if (getDatumId && diff2) { status = calculateStatus(node, node.datum, getDatumId, diff2); } node.transitionOut = status === "removed"; const { phase, start: start2, finish, delay, duration, ...from3 } = fromFn(node, node.datum, status, ctx); const { phase: toPhase, start: toStart, finish: toFinish, delay: toDelay, duration: toDuration, ...to } = toFn(node, node.datum, status, ctx); const collapsable = finish == null; animationManager.animate({ id: animationId, groupId, phase: phase ?? toPhase ?? "update", duration: duration ?? toDuration, delay: delay ?? toDelay, from: from3, to, ease: easeOut, collapsable, onPlay: () => { const startProps = { ...start2, ...toStart, ...from3 }; applyFn(node, startProps, "start"); }, onUpdate(props) { applyFn(node, props, "update"); }, onStop: () => { const endProps = { ...start2, ...toStart, ...from3, ...to, ...finish, ...toFinish }; applyFn(node, endProps, "end"); } }); if (isLive) { liveNodeIndex++; } nodeIndex++; prevFromProps = from3; } }; let selectionIndex = 0; for (const selection of selections) { const selectionNodes = selection.nodes(); const liveNodes = selectionNodes.filter((n) => !selection.isGarbage(n)); processNodes(liveNodes, selectionNodes); animationManager.animate({ id: `${groupId}_${subId}_selection_${selectionIndex}`, groupId, phase: "end", from: 0, to: 1, ease: easeOut, onStop() { selection.cleanup(); } }); selectionIndex++; } processNodes(nodes, nodes); } function staticFromToMotion(groupId, subId, animationManager, selectionsOrNodes, from3, to, extraOpts) { const { nodes, selections } = deconstructSelectionsOrNodes(selectionsOrNodes); const { start: start2, finish, phase } = extraOpts; animationManager.animate({ id: `${groupId}_${subId}`, groupId, phase: phase ?? "update", from: from3, to, ease: easeOut, onPlay: () => { if (!start2) return; for (const node of nodes) { node.setProperties(start2); } for (const selection of selections) { const selectionNodes = selection.nodes(); selection.batchedUpdate(function staticMotionPlay() { for (const node of selectionNodes) { node.setProperties(start2); } }); } }, onUpdate(props) { for (const node of nodes) { node.setProperties(props); } for (const selection of selections) { const selectionNodes = selection.nodes(); selection.batchedUpdate(function staticMotionUpdate() { for (const node of selectionNodes) { node.setProperties(props); } }); } }, onStop: () => { for (const node of nodes) { node.setProperties({ ...to, ...finish }); } for (const selection of selections) { const selectionNodes = selection.nodes(); selection.batchedUpdate(function staticMotionStop() { for (const node of selectionNodes) { node.setProperties({ ...to, ...finish }); } selection.cleanup(); }); } } }); } function calculateStatus(node, datum, getDatumId, diff2) { const id = getDatumId(node, datum); if (diff2.added.has(id)) { return "added"; } if (diff2.removed.has(id)) { return "removed"; } if (node.previousDatum == null && node.datum != null) { return "added"; } if (node.previousDatum != null && node.datum == null) { return "removed"; } return "updated"; } // packages/ag-charts-community/src/motion/resetMotion.ts var resetMotion_exports = {}; __export(resetMotion_exports, { resetMotion: () => resetMotion }); function resetMotion(selectionsOrNodes, propsFn) { const { nodes, selections } = deconstructSelectionsOrNodes(selectionsOrNodes); for (const selection of selections) { const selectionNodes = selection.nodes(); selection.batchedUpdate(function resetMotionNodes() { for (const node of selectionNodes) { const from3 = propsFn(node, node.datum); node.setProperties(from3); } selection.cleanup(); }); } for (const node of nodes) { const from3 = propsFn(node, node.datum); node.setProperties(from3); } } // packages/ag-charts-community/src/scale/continuousScale.ts var _ContinuousScale = class _ContinuousScale extends AbstractScale { constructor(domain = [], range3 = []) { super(); this.range = range3; this.defaultTickCount = _ContinuousScale.defaultTickCount; this.defaultClamp = false; // Domain caching to avoid repeated valueOf() calls in hot paths this._domain = []; this.domainNeedsValueOf = true; // Safe default this.d0Cache = Number.NaN; this.d1Cache = Number.NaN; this.domain = domain; } static is(value) { return value instanceof _ContinuousScale; } get domain() { return this._domain; } set domain(values) { this._domain = values; if (values && values.length >= 2) { const sample = values[0]; this.domainNeedsValueOf = sample != null && typeof sample === "object"; if (this.domainNeedsValueOf) { this.d0Cache = values[0].valueOf(); this.d1Cache = values[1].valueOf(); } else { this.d0Cache = values[0]; this.d1Cache = values[1]; } } else { this.d0Cache = Number.NaN; this.d1Cache = Number.NaN; } } normalizeDomains(...domains) { return normalizeContinuousDomains(...domains); } calcBandwidth(smallestInterval = 1, minWidth = 1) { const { domain } = this; const rangeDistance = this.getPixelRange(); if (domain.length === 0) return rangeDistance; const intervals = Math.abs(this.d1Cache - this.d0Cache) / smallestInterval + 1; let bands = intervals; if (minWidth !== 0) { const maxBands = Math.floor(rangeDistance); bands = Math.min(bands, maxBands); } return rangeDistance / Math.max(1, bands); } convert(value, options) { const { domain } = this; if (!domain || domain.length < 2 || value == null) { return Number.NaN; } const { range: range3 } = this; const clamp2 = options?.clamp ?? this.defaultClamp; let d0 = this.d0Cache; let d1 = this.d1Cache; let x = typeof value === "number" ? value : value.valueOf(); if (this.transform) { d0 = this.transform(d0); d1 = this.transform(d1); x = this.transform(x); } if (clamp2) { const [start2, stop] = findMinMax([d0, d1]); if (x < start2) { return range3[0]; } else if (x > stop) { return range3[1]; } } if (d0 === d1) { return (range3[0] + range3[1]) / 2; } else if (x === d0) { return range3[0]; } else if (x === d1) { return range3[1]; } const r0 = range3[0]; return r0 + (x - d0) / (d1 - d0) * (range3[1] - r0); } invert(x, _nearest) { const { domain } = this; if (domain.length < 2) return; let d0 = this.d0Cache; let d1 = this.d1Cache; if (this.transform) { d0 = this.transform(d0); d1 = this.transform(d1); } const { range: range3 } = this; const [r0, r1] = range3; let d; if (r0 === r1) { d = this.toDomain((d0 + d1) / 2); } else { d = this.toDomain(d0 + (x - r0) / (r1 - r0) * (d1 - d0)); } return this.transformInvert ? this.transformInvert(d) : d; } getDomainMinMax() { return unpackDomainMinMax(this.domain); } getPixelRange() { const [a, b] = this.range; return Math.abs(b - a); } }; _ContinuousScale.defaultTickCount = 5; var ContinuousScale = _ContinuousScale; function normalizeContinuousDomains(...domains) { let min; let minValue = Infinity; let max; let maxValue = -Infinity; for (const input of domains) { const domain = input.domain; for (const d of domain) { const value = d.valueOf(); if (value < minValue) { minValue = value; min = d; } if (value > maxValue) { maxValue = value; max = d; } } } if (min != null && max != null) { const domain = [min, max]; return { domain, animatable: true }; } else { return { domain: [], animatable: false }; } } // packages/ag-charts-community/src/scale/discreteTimeScale.ts var APPROXIMATE_THRESHOLD = 1e3; var SAMPLE_POINTS = 20; function checkUniformityBySampling(bands, startIdx = 0, endIdx = bands.length - 1) { const n = endIdx - startIdx + 1; if (n < 2) return { isUniform: false }; const indices = Array.from( { length: SAMPLE_POINTS }, (_, i) => startIdx + Math.floor(i * (n - 1) / (SAMPLE_POINTS - 1)) ); const samples = indices.map((i) => bands[i].valueOf()); const expectedInterval = (samples.at(-1) - samples[0]) / (n - 1); if (!Number.isFinite(expectedInterval) || expectedInterval === 0) { return { isUniform: false }; } const tolerance = Math.abs(expectedInterval * 0.01); for (let i = 1; i < samples.length; i++) { const indexGap = indices[i] - indices[i - 1]; const actualInterval = (samples[i] - samples[i - 1]) / indexGap; if (Math.abs(actualInterval - expectedInterval) > tolerance) { return { isUniform: false }; } } return { isUniform: true, interval: expectedInterval }; } var DiscreteTimeScale = class _DiscreteTimeScale extends BandScale { static is(value) { return value instanceof _DiscreteTimeScale; } toDomain(value) { return new Date(value); } get reversed() { const { domain } = this; return domain.length > 0 && domain[0].valueOf() > domain.at(-1).valueOf(); } /** Cached numeric band values for efficient binary search. Subclasses should override with a cached version. */ get numericBands() { return this.bands.map((d) => d.valueOf()); } convert(value, options) { this.refresh(); if (!(value instanceof Date)) value = new Date(value); const { domain, reversed } = this; const numericBands = this.numericBands; const bandCount = numericBands.length; if (domain.length <= 0) return Number.NaN; const r0 = this.ordinalRange(0); const r1 = this.ordinalRange(bandCount - 1); if (bandCount === 0) return r0; if (options?.clamp === true) { const { range: range3 } = this; if (value.valueOf() < numericBands[0]) return range3[0]; if (value.valueOf() > numericBands.at(-1)) return range3[1]; } const alignment = options?.alignment ?? 0 /* Leading */; if (alignment !== 2 /* Interpolate */) { const r2 = super.convert(value, options); return reversed ? r1 - (r2 - r0) : r2; } const v = value.valueOf(); let bandIndex = this.findIndex(value) ?? 0; let dIndex; if (reversed) { bandIndex = Math.min(Math.max(bandIndex, 1), bandCount - 1); dIndex = -1; } else { bandIndex = Math.min(Math.max(bandIndex, 0), bandCount - 2); dIndex = 1; } const v0 = numericBands[bandIndex]; const v1 = numericBands[bandIndex + dIndex]; const vr0 = this.ordinalRange(bandIndex); const vr1 = this.ordinalRange(bandIndex + dIndex); const ratio2 = (v - v0) / (v1 - v0); const r = ratio2 * (vr1 - vr0) + vr0; return reversed ? r1 - (r - r0) : r; } invert(position, nearest = false) { this.refresh(); const { domain } = this; if (domain.length <= 0) return; const bands = this.bands; const bandCount = this.getBandCountForUpdate(); const reversed = domain[0].valueOf() > domain.at(-1).valueOf(); let index; if (nearest) { index = this.invertNearestIndex(position - this.bandwidth / 2); } else { const closestIndex = findMinIndex(0, bandCount - 1, (i) => { const p = this.ordinalRange(i); return p >= position; }); index = closestIndex ?? bandCount - 1; } return bands[reversed ? bandCount - 1 - index : index]; } /** Override in subclass to provide cached uniformity check result */ getUniformityCache(_visibleRange) { return void 0; } findIndex(value, alignment = 0 /* Leading */) { if (value == null) return void 0; const numericBands = this.numericBands; const n = numericBands.length; if (n === 0) return void 0; if (n === 1) return 0; const target = value.valueOf(); if (alignment === 1 /* Trailing */) { return findMinIndex(0, n - 1, (index) => numericBands[index] >= target); } return findMaxIndex(0, n - 1, (index) => numericBands[index] <= target); } }; // packages/ag-charts-community/src/chart/caption.ts var Caption = class extends BaseProperties { constructor() { super(...arguments); this.id = createId(this); this.node = new RotatableText({ zIndex: 1 }).setProperties({ textAlign: "center", pointerEvents: 1 /* None */ }); this.enabled = false; this.textAlign = "center"; this.fontSize = 10 /* SMALLER */; this.fontFamily = "sans-serif"; this.wrapping = "always"; this.padding = 0; this.layoutStyle = "block"; this.truncated = false; } registerInteraction(moduleCtx, where) { return moduleCtx.eventsHub.on("layout:complete", () => this.updateA11yText(moduleCtx, where)); } computeTextWrap(containerWidth, containerHeight) { const { text, padding: padding2, wrapping } = this; const maxWidth = Math.min(this.maxWidth ?? Infinity, containerWidth) - padding2 * 2; const maxHeight = this.maxHeight ?? containerHeight - padding2 * 2; const options = { maxWidth, maxHeight, font: this, textWrap: wrapping }; if (!Number.isFinite(maxWidth) && !Number.isFinite(maxHeight)) { this.node.text = text; return; } let wrappedText; if (isArray(text)) { wrappedText = wrapTextSegments(text, options); this.truncated = wrappedText.some(isSegmentTruncated); } else { wrappedText = wrapText(toTextString(text), options); this.truncated = isTextTruncated(wrappedText); } this.node.text = wrappedText; } updateA11yText(moduleCtx, where) { const { proxyInteractionService } = moduleCtx; if (!this.enabled || !this.text) { this.destroyProxyText(); return; } const bbox = Transformable.toCanvas(this.node); if (!bbox) return; const { id: domManagerId } = this; if (this.proxyText == null) { this.proxyText = proxyInteractionService.createProxyElement({ type: "text", domManagerId, where }); this.proxyTextListeners = [ this.proxyText.addListener("mousemove", (ev) => this.handleMouseMove(moduleCtx, ev)), this.proxyText.addListener("mouseleave", (ev) => this.handleMouseLeave(moduleCtx, ev)) ]; } const textContent = toPlainText(this.text); if (textContent !== this.lastProxyTextContent) { this.proxyText.textContent = textContent; this.lastProxyTextContent = textContent; } const { lastProxyBBox } = this; if (lastProxyBBox == null || bbox.x !== lastProxyBBox.x || bbox.y !== lastProxyBBox.y || bbox.width !== lastProxyBBox.width || bbox.height !== lastProxyBBox.height) { this.proxyText.setBounds(bbox); this.lastProxyBBox = { x: bbox.x, y: bbox.y, width: bbox.width, height: bbox.height }; } } handleMouseMove(moduleCtx, event) { if (event != null && this.enabled && this.truncated) { const { x, y } = Transformable.toCanvas(this.node); const canvasX = event.sourceEvent.offsetX + x; const canvasY = event.sourceEvent.offsetY + y; moduleCtx.tooltipManager.updateTooltip(this.id, { canvasX, canvasY, showArrow: false }, [ { type: "structured", title: toPlainText(this.text) } ]); } } handleMouseLeave(moduleCtx, _event) { moduleCtx.tooltipManager.removeTooltip(this.id, void 0, true); } destroy() { this.destroyProxyText(); } destroyProxyText() { if (this.proxyText == null) return; for (const cleanup of this.proxyTextListeners ?? []) { cleanup(); } this.proxyTextListeners = void 0; this.proxyText.destroy(); this.proxyText = void 0; this.lastProxyTextContent = void 0; this.lastProxyBBox = void 0; } }; Caption.className = "Caption"; Caption.SMALL_PADDING = 10; __decorateClass([ addFakeTransformToInstanceProperty, ProxyPropertyOnWrite("node", "visible") ], Caption.prototype, "enabled", 2); __decorateClass([ addFakeTransformToInstanceProperty, ProxyPropertyOnWrite("node") ], Caption.prototype, "text", 2); __decorateClass([ addFakeTransformToInstanceProperty, ProxyPropertyOnWrite("node") ], Caption.prototype, "textAlign", 2); __decorateClass([ addFakeTransformToInstanceProperty, ProxyPropertyOnWrite("node") ], Caption.prototype, "fontStyle", 2); __decorateClass([ addFakeTransformToInstanceProperty, ProxyPropertyOnWrite("node") ], Caption.prototype, "fontWeight", 2); __decorateClass([ addFakeTransformToInstanceProperty, ProxyPropertyOnWrite("node") ], Caption.prototype, "fontSize", 2); __decorateClass([ addFakeTransformToInstanceProperty, ProxyPropertyOnWrite("node") ], Caption.prototype, "fontFamily", 2); __decorateClass([ addFakeTransformToInstanceProperty, ProxyPropertyOnWrite("node", "fill") ], Caption.prototype, "color", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Caption.prototype, "spacing", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Caption.prototype, "maxWidth", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Caption.prototype, "maxHeight", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Caption.prototype, "wrapping", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Caption.prototype, "padding", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Caption.prototype, "layoutStyle", 2); // packages/ag-charts-community/src/util/listeners.ts var Listeners = class { constructor() { this.registeredListeners = /* @__PURE__ */ new Map(); } addListener(eventType, handler) { const record2 = { symbol: Symbol(eventType), handler }; if (this.registeredListeners.has(eventType)) { this.registeredListeners.get(eventType).push(record2); } else { this.registeredListeners.set(eventType, [record2]); } return () => this.removeListener(record2.symbol); } removeListener(eventSymbol) { for (const [type, listeners] of this.registeredListeners.entries()) { const matchIndex = listeners.findIndex((listener) => listener.symbol === eventSymbol); if (matchIndex >= 0) { listeners.splice(matchIndex, 1); if (listeners.length === 0) { this.registeredListeners.delete(type); } break; } } } dispatch(eventType, ...params) { for (const listener of this.getListenersByType(eventType)) { try { listener.handler(...params); } catch (e) { logger_exports.errorOnce(e); } } } getListenersByType(eventType) { return this.registeredListeners.get(eventType) ?? []; } destroy() { this.registeredListeners.clear(); } }; // packages/ag-charts-community/src/chart/axis/timeFormatUtil.ts var defaultTimeFormats = { millisecond: "%H:%M:%S.%L", second: "%H:%M:%S", minute: "%H:%M", hour: "%H:%M", day: "%e", month: "%b", year: "%Y" }; var hardCodedTimeFormats = { millisecond: "%Y %b %e %H:%M:%S.%L", second: "%Y %b %e %H:%M:%S", minute: "%Y %b %e %H:%M", hour: "%Y %b %e %H:%M", day: "%Y %b %e", month: "%Y %b", year: "%Y" }; var FORMAT_ORDERS = { year: 0, month: 1, day: 2, hour: 3, minute: 4, second: 5, millisecond: 6 }; var MILLISECOND_FORMAT = /%[-_0]?L/; var SECOND_FORMAT = /%[-_0]?S/; var MINUTE_FORMAT = /%[-_0]?M/; var HOUR_FORMAT = /%[-_0]?[HI]/; var DAY_FORMAT = /^%[-_0]?[de]$/; var MONTH_FORMAT = /^%[-_0]?[Bbm]$/; var YEAR_FORMAT = /^%[-_0]?[Yy]$/; function deriveTimeSpecifier(format, unit, truncateDate) { if (typeof format === "string") return format; format ?? (format = defaultTimeFormats); const { millisecond = defaultTimeFormats.millisecond, second = defaultTimeFormats.second, minute = defaultTimeFormats.minute, hour = defaultTimeFormats.hour, day = defaultTimeFormats.day, month = defaultTimeFormats.month, year = defaultTimeFormats.year } = format; const formatOrder = FORMAT_ORDERS[unit]; const hardcodedTimeFormat = hardCodedTimeFormats[unit]; const truncationOrder = truncateDate ? FORMAT_ORDERS[truncateDate] : -1; if (truncationOrder < FORMAT_ORDERS.year && formatOrder >= FORMAT_ORDERS.year && !YEAR_FORMAT.test(year) || truncationOrder < FORMAT_ORDERS.month && formatOrder >= FORMAT_ORDERS.month && !MONTH_FORMAT.test(month) || truncationOrder < FORMAT_ORDERS.day && formatOrder >= FORMAT_ORDERS.day && !DAY_FORMAT.test(day)) { return hardcodedTimeFormat; } let timeFormat; switch (unit) { case "year": return year; case "month": return truncationOrder < FORMAT_ORDERS.year ? `${month} ${year}` : month; case "day": return truncationOrder < FORMAT_ORDERS.year ? `${month} ${day} ${year}` : `${month} ${day}`; case "hour": timeFormat = hour; break; case "minute": timeFormat = minute; break; case "second": timeFormat = second; break; case "millisecond": timeFormat = millisecond; break; default: return hardcodedTimeFormat; } if (formatOrder >= FORMAT_ORDERS.hour && !HOUR_FORMAT.test(timeFormat) || formatOrder >= FORMAT_ORDERS.minute && !MINUTE_FORMAT.test(timeFormat) || formatOrder >= FORMAT_ORDERS.second && !SECOND_FORMAT.test(timeFormat) || formatOrder >= FORMAT_ORDERS.millisecond && !MILLISECOND_FORMAT.test(timeFormat)) { return hardcodedTimeFormat; } let dateFormat; if (truncationOrder < FORMAT_ORDERS.year) { dateFormat = `${month} ${day} ${year}`; } else if (truncationOrder < FORMAT_ORDERS.month) { dateFormat = `${month} ${day}`; } return dateFormat ? `${timeFormat} ${dateFormat}` : timeFormat; } // packages/ag-charts-community/src/chart/formatter/formatManager.ts var FormatManager = class _FormatManager extends Listeners { constructor() { super(...arguments); this.formats = /* @__PURE__ */ new Map(); this.dateFormatter = simpleMemorize2( (propertyFormatter, specifier, unit, style2, truncateDate) => { const mergedFormatter = _FormatManager.mergeSpecifiers(propertyFormatter, specifier) ?? defaultTimeFormats; return _FormatManager.getFormatter("date", mergedFormatter, unit, style2, { truncateDate }); } ); this.formatter = void 0; } static mergeSpecifiers(...specifiers) { let out; for (const specifier of specifiers) { if (isPlainObject(specifier) && isPlainObject(out)) { out = { ...out, ...specifier }; } else { out = specifier; } } return out; } static getFormatter(type, specifier, unit, style2 = "long", { truncateDate } = {}) { if (isPlainObject(specifier)) { if (type !== "date") { logger_exports.warn("Date formatter configuration is not supported for non-date types."); return; } unit ?? (unit = "millisecond"); const fullFormat = style2 === "component" ? specifier?.[unit] ?? defaultTimeFormats[unit] : deriveTimeSpecifier(specifier, unit, truncateDate); return buildDateFormatter(fullFormat); } switch (type) { case "number": { const options = parseNumberFormat(specifier); if (options == null) return; return createNumberFormatter(options); } case "date": return buildDateFormatter(specifier); case "category": return (value) => specifier.replace("%s", String(value)); } } setFormatter(formatter2) { if (this.formatter !== formatter2) { this.formatter = formatter2; this.formats.clear(); this.dateFormatter.reset(); this.dispatch("format-changed"); } } format(formatInContext, params, { specifier, truncateDate, allowNull } = {}) { if (params.value == null && !allowNull) return; const { formatter: formatter2 } = this; if (formatter2 == null) return; if (typeof formatter2 === "function") { const value = formatInContext(formatter2, params); return value == null ? void 0 : String(value); } const propertyFormatter = formatter2[params.property]; if (propertyFormatter == null) return; if (typeof propertyFormatter === "function") { const value = formatInContext(propertyFormatter, params); return value == null ? value : toTextString(value); } else if (params.type === "date") { const { unit, style: style2 } = params; const dateFormatter = this.dateFormatter(propertyFormatter, specifier, unit, style2, truncateDate); return dateFormatter?.(params.value); } const valueSpecifier = specifier ?? propertyFormatter; if (typeof valueSpecifier !== "string") return; let valueFormatter = this.formats.get(valueSpecifier); if (valueFormatter == null) { valueFormatter = _FormatManager.getFormatter(params.type, valueSpecifier); this.formats.set(valueSpecifier, valueFormatter); } return valueFormatter?.(params.value, params.type === "number" ? params.fractionDigits : void 0); } defaultFormat(params, { specifier, truncateDate } = {}) { const { formatter: formatter2 } = this; const propertyFormatter = typeof formatter2 === "function" ? void 0 : formatter2?.[params.property]; switch (params.type) { case "date": { const { unit, style: style2 } = params; const propertySpecifier = propertyFormatter != null && typeof propertyFormatter !== "function" ? propertyFormatter : void 0; const dateFormatter = this.dateFormatter(propertySpecifier, specifier, unit, style2, truncateDate); return dateFormatter?.(params.value) ?? String(params.value); } case "number": return formatValue(params.value, params.fractionDigits); case "category": if (params.value == null) { return ""; } else if (Array.isArray(params.value)) { return params.value.join(" - "); } else if (typeof params.value === "string") { return params.value; } else if (typeof params.value === "number") { return formatValue(params.value); } else { return String(params.value); } } } }; // packages/ag-charts-community/src/chart/label.ts var LabelBorder = class { constructor() { this.enabled = true; } }; __decorateClass([ addFakeTransformToInstanceProperty ], LabelBorder.prototype, "enabled", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LabelBorder.prototype, "stroke", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LabelBorder.prototype, "strokeWidth", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LabelBorder.prototype, "strokeOpacity", 2); var LabelStyle = class extends BaseProperties { constructor() { super(...arguments); this.border = new LabelBorder(); } }; __decorateClass([ addFakeTransformToInstanceProperty ], LabelStyle.prototype, "border", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LabelStyle.prototype, "color", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LabelStyle.prototype, "cornerRadius", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LabelStyle.prototype, "fill", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LabelStyle.prototype, "fillOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LabelStyle.prototype, "fontStyle", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LabelStyle.prototype, "fontWeight", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LabelStyle.prototype, "fontSize", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LabelStyle.prototype, "fontFamily", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LabelStyle.prototype, "padding", 2); var Label = class extends LabelStyle { constructor() { super(...arguments); this.enabled = false; this._cachedFormatter = void 0; } formatValue(formatWithContext, type, value, params) { const { formatter: formatter2, format } = this; let result; if (formatter2 != null) { result ?? (result = formatWithContext(formatter2, params)); } if (format != null) { let cachedFormatter = this._cachedFormatter; if (cachedFormatter?.type !== type || cachedFormatter?.format !== format) { cachedFormatter = { type, format, formatter: FormatManager.getFormatter(type, format) }; this._cachedFormatter = cachedFormatter; } result ?? (result = cachedFormatter.formatter?.(value)); } return result == null || isArray(result) ? result : String(result); } }; __decorateClass([ addFakeTransformToInstanceProperty ], Label.prototype, "enabled", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Label.prototype, "formatter", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Label.prototype, "format", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Label.prototype, "itemStyler", 2); function expandLabelPadding(label) { const { enabled: borderEnabled = false, stroke: borderStroke } = label?.border ?? {}; const hasBoxing = label?.fill != null || borderEnabled && borderStroke != null; const padding2 = hasBoxing ? label?.padding : null; if (padding2 == null) { return { bottom: 0, left: 0, right: 0, top: 0 }; } else if (typeof padding2 === "number") { return { bottom: padding2, left: padding2, right: padding2, top: padding2 }; } else { const { bottom = 0, left = 0, right = 0, top = 0 } = padding2; return { bottom, left, right, top }; } } // packages/ag-charts-community/src/module/moduleMap.ts var ModuleMap = class { constructor() { this.moduleMap = /* @__PURE__ */ new Map(); } modules() { return this.moduleMap.values(); } addModule(moduleName, moduleInstance) { if (this.moduleMap.has(moduleName)) { throw new Error(`AG Charts - module already initialised: ${moduleName}`); } this.moduleMap.set(moduleName, moduleInstance); } removeModule(moduleName) { this.moduleMap.get(moduleName)?.destroy?.(); this.moduleMap.delete(moduleName); } getModule(moduleName) { return this.moduleMap.get(moduleName); } isEnabled(moduleName) { return this.moduleMap.has(moduleName); } mapModules(callback2) { return Array.from(this.moduleMap.values(), callback2); } destroy() { for (const moduleInstance of this.moduleMap.values()) { moduleInstance?.destroy?.(); } this.moduleMap.clear(); } }; // packages/ag-charts-community/src/scene/shape/range.ts var Range = class extends Shape { constructor(opts = {}) { super(opts); this.x1 = 0; this.y1 = 0; this.x2 = 0; this.y2 = 0; this.startLine = false; this.endLine = false; this.horizontal = false; this.strokeWidth = 1; } computeBBox() { return new BBox(this.x1, this.y1, this.x2 - this.x1, this.y2 - this.y1); } isPointInPath(_x, _y) { return false; } render(renderCtx) { const { ctx } = renderCtx; let { x1, y1, x2, y2 } = this; x1 = this.align(x1); y1 = this.align(y1); x2 = this.align(x2); y2 = this.align(y2); const { fill, horizontal } = this; const { globalAlpha } = ctx; if (fill != null) { this.applyFillAndAlpha(ctx); ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y1); ctx.lineTo(x2, y2); ctx.lineTo(x1, y2); ctx.closePath(); ctx.fill(); ctx.globalAlpha = globalAlpha; } const { stroke, strokeWidth, startLine, endLine } = this; const strokeActive = !!((startLine || endLine) && stroke && strokeWidth); if (strokeActive) { const { lineDash, lineDashOffset, lineCap, lineJoin } = this; this.applyStrokeAndAlpha(ctx); ctx.lineWidth = strokeWidth; if (lineDash) { ctx.setLineDash([...lineDash]); } if (lineDashOffset) { ctx.lineDashOffset = lineDashOffset; } if (lineCap) { ctx.lineCap = lineCap; } if (lineJoin) { ctx.lineJoin = lineJoin; } ctx.beginPath(); if (startLine) { ctx.moveTo(x1, y1); if (horizontal) { ctx.lineTo(x1, y2); } else { ctx.lineTo(x2, y1); } } if (endLine) { ctx.moveTo(x2, y2); if (horizontal) { ctx.lineTo(x2, y1); } else { ctx.lineTo(x1, y2); } } ctx.stroke(); ctx.globalAlpha = globalAlpha; } this.fillShadow?.markClean(); super.render(renderCtx); } }; Range.className = "Range"; __decorateClass([ SceneChangeDetection() ], Range.prototype, "x1", 2); __decorateClass([ SceneChangeDetection() ], Range.prototype, "y1", 2); __decorateClass([ SceneChangeDetection() ], Range.prototype, "x2", 2); __decorateClass([ SceneChangeDetection() ], Range.prototype, "y2", 2); __decorateClass([ SceneChangeDetection() ], Range.prototype, "startLine", 2); __decorateClass([ SceneChangeDetection() ], Range.prototype, "endLine", 2); __decorateClass([ SceneChangeDetection() ], Range.prototype, "horizontal", 2); // packages/ag-charts-community/src/chart/rangeAlignment.ts function rangeAlignment(start2, end3) { const startValue = start2?.valueOf(); const endValue = end3?.valueOf(); if (typeof startValue !== "number" || typeof endValue !== "number") return [void 0, void 0]; return startValue < endValue ? [0 /* Leading */, 1 /* Trailing */] : [1 /* Trailing */, 0 /* Leading */]; } // packages/ag-charts-community/src/chart/crossline/crossLine.ts function getCrossLineValue(crossLine) { switch (crossLine.type) { case "line": return crossLine.value; case "range": return crossLine.range; } } function validateCrossLineValue(crossLine, scale2) { const value = getCrossLineValue(crossLine); if (value == null) { return false; } const isContinuous2 = ContinuousScale.is(scale2) || DiscreteTimeScale.is(scale2); const validValue = (val) => checkDatum(val, isContinuous2) && !Number.isNaN(scale2.convert(val, { clamp: true })); if (crossLine.type === "range") { const [start2, end3] = value; return validValue(start2) && validValue(end3); } else { return validValue(value); } } // packages/ag-charts-community/src/chart/crossline/cartesianCrossLine.ts var horizontalLineAnchors = { top: { rangeH: 0, rangeV: -1, labelH: 0, labelV: 1 }, "inside-top": { rangeH: 0, rangeV: -1, labelH: 0, labelV: 1 }, "top-left": { rangeH: -1, rangeV: -1, labelH: -1, labelV: 1 }, "inside-top-left": { rangeH: -1, rangeV: -1, labelH: -1, labelV: 1 }, left: { rangeH: -1, rangeV: 0, labelH: 1, labelV: 0 }, "inside-left": { rangeH: -1, rangeV: 0, labelH: -1, labelV: 0 }, "bottom-left": { rangeH: -1, rangeV: 1, labelH: -1, labelV: -1 }, "inside-bottom-left": { rangeH: -1, rangeV: 1, labelH: -1, labelV: -1 }, bottom: { rangeH: 0, rangeV: 1, labelH: 0, labelV: -1 }, "inside-bottom": { rangeH: 0, rangeV: 1, labelH: 0, labelV: -1 }, "bottom-right": { rangeH: 1, rangeV: 1, labelH: 1, labelV: -1 }, "inside-bottom-right": { rangeH: 1, rangeV: 1, labelH: 1, labelV: -1 }, right: { rangeH: 1, rangeV: 0, labelH: -1, labelV: 0 }, "inside-right": { rangeH: 1, rangeV: 0, labelH: 1, labelV: 0 }, "top-right": { rangeH: 1, rangeV: -1, labelH: 1, labelV: 1 }, "inside-top-right": { rangeH: 1, rangeV: -1, labelH: 1, labelV: 1 }, inside: { rangeH: 0, rangeV: 0, labelH: 0, labelV: 0 } }; var verticalLineAnchors = { top: { rangeH: 0, rangeV: -1, labelH: 0, labelV: 1 }, "inside-top": { rangeH: 0, rangeV: -1, labelH: 0, labelV: -1 }, "top-left": { rangeH: -1, rangeV: -1, labelH: 1, labelV: -1 }, "inside-top-left": { rangeH: -1, rangeV: -1, labelH: 1, labelV: -1 }, left: { rangeH: -1, rangeV: 0, labelH: 1, labelV: 0 }, "inside-left": { rangeH: -1, rangeV: 0, labelH: 1, labelV: 0 }, "bottom-left": { rangeH: -1, rangeV: 1, labelH: 1, labelV: 1 }, "inside-bottom-left": { rangeH: -1, rangeV: 1, labelH: 1, labelV: 1 }, bottom: { rangeH: 0, rangeV: 1, labelH: 0, labelV: -1 }, "inside-bottom": { rangeH: 0, rangeV: 1, labelH: 0, labelV: 1 }, "bottom-right": { rangeH: 1, rangeV: 1, labelH: -1, labelV: 1 }, "inside-bottom-right": { rangeH: 1, rangeV: 1, labelH: -1, labelV: 1 }, right: { rangeH: 1, rangeV: 0, labelH: -1, labelV: 0 }, "inside-right": { rangeH: 1, rangeV: 0, labelH: -1, labelV: 0 }, "top-right": { rangeH: 1, rangeV: -1, labelH: -1, labelV: -1 }, "inside-top-right": { rangeH: -1, rangeV: -1, labelH: -1, labelV: -1 }, inside: { rangeH: 0, rangeV: 0, labelH: 0, labelV: 0 } }; var horizontalRangeAnchors = { top: { rangeH: 0, rangeV: -1, labelH: 0, labelV: 1 }, "inside-top": { rangeH: 0, rangeV: -1, labelH: 0, labelV: -1 }, "top-left": { rangeH: -1, rangeV: -1, labelH: -1, labelV: 1 }, "inside-top-left": { rangeH: -1, rangeV: -1, labelH: -1, labelV: -1 }, left: { rangeH: -1, rangeV: 0, labelH: 1, labelV: 0 }, "inside-left": { rangeH: -1, rangeV: 0, labelH: -1, labelV: 0 }, "bottom-left": { rangeH: -1, rangeV: 1, labelH: -1, labelV: -1 }, "inside-bottom-left": { rangeH: -1, rangeV: 1, labelH: -1, labelV: 1 }, bottom: { rangeH: 0, rangeV: 1, labelH: 0, labelV: -1 }, "inside-bottom": { rangeH: 0, rangeV: 1, labelH: 0, labelV: 1 }, "bottom-right": { rangeH: 1, rangeV: 1, labelH: 1, labelV: -1 }, "inside-bottom-right": { rangeH: 1, rangeV: 1, labelH: 1, labelV: 1 }, right: { rangeH: 1, rangeV: 0, labelH: -1, labelV: 0 }, "inside-right": { rangeH: 1, rangeV: 0, labelH: 1, labelV: 0 }, "top-right": { rangeH: 1, rangeV: -1, labelH: 1, labelV: 1 }, "inside-top-right": { rangeH: 1, rangeV: -1, labelH: 1, labelV: -1 }, inside: { rangeH: 0, rangeV: 0, labelH: 0, labelV: 0 } }; var verticalRangeAnchors = { top: { rangeH: 0, rangeV: -1, labelH: 0, labelV: 1 }, "inside-top": { rangeH: 0, rangeV: -1, labelH: 0, labelV: -1 }, "top-left": { rangeH: -1, rangeV: -1, labelH: 1, labelV: -1 }, "inside-top-left": { rangeH: -1, rangeV: -1, labelH: -1, labelV: -1 }, left: { rangeH: -1, rangeV: 0, labelH: 1, labelV: 0 }, "inside-left": { rangeH: -1, rangeV: 0, labelH: -1, labelV: 0 }, "bottom-left": { rangeH: -1, rangeV: 1, labelH: 1, labelV: 1 }, "inside-bottom-left": { rangeH: -1, rangeV: 1, labelH: -1, labelV: 1 }, bottom: { rangeH: 0, rangeV: 1, labelH: 0, labelV: -1 }, "inside-bottom": { rangeH: 0, rangeV: 1, labelH: 0, labelV: 1 }, "bottom-right": { rangeH: 1, rangeV: 1, labelH: -1, labelV: 1 }, "inside-bottom-right": { rangeH: 1, rangeV: 1, labelH: 1, labelV: 1 }, right: { rangeH: 1, rangeV: 0, labelH: -1, labelV: 0 }, "inside-right": { rangeH: 1, rangeV: 0, labelH: 1, labelV: 0 }, "top-right": { rangeH: 1, rangeV: -1, labelH: -1, labelV: -1 }, "inside-top-right": { rangeH: 1, rangeV: -1, labelH: 1, labelV: -1 }, inside: { rangeH: 0, rangeV: 0, labelH: 0, labelV: 0 } }; var CartesianCrossLineLabel = class extends LabelStyle { constructor() { super(...arguments); this.enabled = void 0; this.padding = 5; } }; __decorateClass([ addFakeTransformToInstanceProperty ], CartesianCrossLineLabel.prototype, "enabled", 2); __decorateClass([ addFakeTransformToInstanceProperty ], CartesianCrossLineLabel.prototype, "padding", 2); __decorateClass([ addFakeTransformToInstanceProperty ], CartesianCrossLineLabel.prototype, "text", 2); __decorateClass([ addFakeTransformToInstanceProperty ], CartesianCrossLineLabel.prototype, "position", 2); __decorateClass([ addFakeTransformToInstanceProperty ], CartesianCrossLineLabel.prototype, "rotation", 2); __decorateClass([ addFakeTransformToInstanceProperty ], CartesianCrossLineLabel.prototype, "parallel", 2); var CartesianCrossLine = class extends BaseProperties { constructor() { super(); this.id = createId(this); this.defaultColorRange = []; this.fill = "#c16068"; this.label = new CartesianCrossLineLabel(); this.scale = void 0; this.clippedRange = [-Infinity, Infinity]; this.gridLength = 0; this.position = "top"; this.rangeGroup = new Group({ name: this.id }); this.lineGroup = new Group({ name: this.id }); this.labelGroup = new Group({ name: this.id }); this.crossLineRange = this.lineGroup.appendChild(new Range()); this.crossLineLabel = this.labelGroup.appendChild(new TransformableText()); this.data = void 0; this.startLine = false; this.endLine = false; this._isRange = void 0; this.crossLineRange.pointerEvents = 1 /* None */; } get defaultLabelPosition() { return "top"; } update(visible) { const { enabled, type, data, scale: scale2 } = this; if (!scale2 || !enabled || !visible || !validateCrossLineValue(this, scale2) || data == null) { this.rangeGroup.visible = false; this.lineGroup.visible = false; this.labelGroup.visible = false; return; } this.rangeGroup.visible = visible; this.lineGroup.visible = visible; this.labelGroup.visible = visible; this.updateNodes(); const isRange = type === "range"; if (isRange !== this._isRange) { if (isRange) { this.rangeGroup.appendChild(this.crossLineRange); } else { this.lineGroup.appendChild(this.crossLineRange); } } this._isRange = isRange; } calculateLayout(visible) { this.data = void 0; if (!visible) return; const { type, range: range3, value, scale: scale2, clippedRange, strokeWidth = 0 } = this; if (!scale2) return; const bandwidth = scale2.bandwidth ?? 0; const step = scale2.step ?? 0; const rangePadding = scale2 instanceof BandScale ? (step - bandwidth) / 2 : 0; let [clippedRange0, clippedRange1] = findMinMax(clippedRange); clippedRange0 -= bandwidth; clippedRange1 += bandwidth; let yStart; let yEnd; let clampedYStart; let clampedYEnd; if (type === "line") { const offset = bandwidth / 2; yStart = scale2.convert(value) + offset; yEnd = Number.NaN; clampedYStart = scale2.convert(value, { clamp: true }) + offset; clampedYEnd = Number.NaN; if (clampedYStart >= clippedRange1 || clampedYStart <= clippedRange0) { return; } } else if (range3) { const [r0, r1] = range3; const [startAlignment, endAlignment] = rangeAlignment(r0, r1); yStart = scale2.convert(r0, { alignment: startAlignment }); yEnd = scale2.convert(r1, { alignment: endAlignment }); clampedYStart = scale2.convert(r0, { clamp: true, alignment: startAlignment }); clampedYEnd = scale2.convert(r1, { clamp: true, alignment: endAlignment }); if (clampedYStart > clampedYEnd) { [clampedYStart, clampedYEnd] = [clampedYEnd, clampedYStart]; [yStart, yEnd] = [yEnd, yStart]; } if (clampedYStart >= clippedRange1 || clampedYEnd <= clippedRange0) { return; } if (Number.isFinite(yStart)) { clampedYStart -= rangePadding; } if (Number.isFinite(yEnd)) { yEnd += bandwidth; clampedYEnd += bandwidth + rangePadding; } } else { return; } clampedYStart = clampArray(clampedYStart, clippedRange); clampedYEnd = clampArray(clampedYEnd, clippedRange); if (yStart - rangePadding >= clampedYStart) yStart -= rangePadding; if (yEnd + rangePadding <= clampedYEnd) yEnd += rangePadding; this.startLine = strokeWidth > 0 && yStart >= clampedYStart && yStart <= clampedYStart + rangePadding; this.endLine = strokeWidth > 0 && yEnd >= clampedYEnd - bandwidth - rangePadding && yEnd <= clampedYEnd; this.data = [clampedYStart, clampedYEnd]; if (this.label.enabled === false || !this.label.text) return; } updateNodes() { const { position, data: [r0, r1] = [0, 0], gridLength } = this; const dr = Number.isFinite(r1) ? r1 - r0 : 0; let bounds; switch (position) { case "top": case "bottom": bounds = new BBox(r0, position === "top" ? 0 : -gridLength, dr, gridLength); break; case "left": case "right": bounds = new BBox(position === "left" ? 0 : -gridLength, r0, gridLength, dr); } this.updateRangeNode(bounds); const { label } = this; if (label.enabled !== false && label.text) { this.updateLabel(); this.positionLabel(bounds); } } updateRangeNode(bounds) { const { type, position, crossLineRange, startLine, endLine, fill, fillOpacity, stroke, strokeWidth, strokeOpacity, lineDash } = this; crossLineRange.x1 = bounds.x; crossLineRange.x2 = bounds.x + bounds.width; crossLineRange.y1 = bounds.y; crossLineRange.y2 = bounds.y + bounds.height; crossLineRange.horizontal = position === "top" || position === "bottom"; crossLineRange.startLine = startLine; crossLineRange.endLine = endLine; crossLineRange.fill = type === "range" ? fill : void 0; crossLineRange.fillOpacity = fillOpacity ?? 1; crossLineRange.stroke = stroke; crossLineRange.strokeWidth = strokeWidth ?? 1; crossLineRange.strokeOpacity = strokeOpacity ?? 1; crossLineRange.lineDash = lineDash; } updateLabel() { const { crossLineLabel, label } = this; if (!label.text) return; crossLineLabel.fill = label.color; crossLineLabel.text = label.text; crossLineLabel.textAlign = "center"; crossLineLabel.textBaseline = "middle"; crossLineLabel.setFont(label); crossLineLabel.setBoxing(label); } get anchor() { const horizontal = this.position === "left" || this.position === "right"; const range3 = this.type === "range"; const { position = this.defaultLabelPosition } = this.label; if (range3) { const anchors = horizontal ? horizontalRangeAnchors : verticalRangeAnchors; return anchors[position]; } else { const anchors = horizontal ? horizontalLineAnchors : verticalLineAnchors; return anchors[position]; } } positionLabel(bounds) { const { crossLineLabel, label, anchor } = this; crossLineLabel.rotation = toRadians(label.rotation ?? 0); const bbox = crossLineLabel.getBBox(); if (!bbox) return; const { width: width2, height: height2 } = bbox; const xOffset = label.padding + width2 / 2; const yOffset = label.padding + height2 / 2; const x = bounds.x + bounds.width * (anchor.rangeH + 1) / 2 - xOffset * anchor.labelH; const y = bounds.y + bounds.height * (anchor.rangeV + 1) / 2 - yOffset * anchor.labelV; crossLineLabel.x = x; crossLineLabel.y = y; crossLineLabel.rotationCenterX = x; crossLineLabel.rotationCenterY = y; } computeLabelSize() { const { label } = this; if (label.enabled === false || !label.text) return; const tempText = new TransformableText(); tempText.fontFamily = label.fontFamily; tempText.fontSize = label.fontSize; tempText.fontStyle = label.fontStyle; tempText.fontWeight = label.fontWeight; tempText.text = label.text; tempText.rotation = toRadians(label.rotation ?? 0); tempText.textBaseline = "middle"; tempText.textAlign = "center"; const bbox = tempText.getBBox(); if (!bbox) return; const { width: width2, height: height2 } = bbox; return { width: width2, height: height2 }; } calculatePadding(into) { const { label, anchor } = this; const size = this.computeLabelSize(); if (!size) return; const { width: width2, height: height2 } = size; const xOffset = label.padding + width2; const yOffset = label.padding + height2; const horizontal = this.position === "left" || this.position === "right"; if (horizontal) { if (anchor.rangeH === -1 && anchor.labelH === 1) { into.left = Math.max(into.left ?? 0, xOffset); } else if (anchor.rangeH === 1 && anchor.labelH === -1) { into.right = Math.max(into.right ?? 0, xOffset); } } if (!horizontal) { if (anchor.rangeV === -1 && anchor.labelV === 1) { into.top = Math.max(into.top ?? 0, yOffset); } else if (anchor.rangeV === 1 && anchor.labelV === -1) { into.bottom = Math.max(into.bottom ?? 0, yOffset); } } } }; CartesianCrossLine.className = "CrossLine"; __decorateClass([ addFakeTransformToInstanceProperty ], CartesianCrossLine.prototype, "enabled", 2); __decorateClass([ addFakeTransformToInstanceProperty ], CartesianCrossLine.prototype, "type", 2); __decorateClass([ addFakeTransformToInstanceProperty ], CartesianCrossLine.prototype, "range", 2); __decorateClass([ addFakeTransformToInstanceProperty ], CartesianCrossLine.prototype, "value", 2); __decorateClass([ addFakeTransformToInstanceProperty ], CartesianCrossLine.prototype, "defaultColorRange", 2); __decorateClass([ addFakeTransformToInstanceProperty ], CartesianCrossLine.prototype, "fill", 2); __decorateClass([ addFakeTransformToInstanceProperty ], CartesianCrossLine.prototype, "fillOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty ], CartesianCrossLine.prototype, "stroke", 2); __decorateClass([ addFakeTransformToInstanceProperty ], CartesianCrossLine.prototype, "strokeWidth", 2); __decorateClass([ addFakeTransformToInstanceProperty ], CartesianCrossLine.prototype, "strokeOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty ], CartesianCrossLine.prototype, "lineDash", 2); __decorateClass([ addFakeTransformToInstanceProperty ], CartesianCrossLine.prototype, "label", 2); // packages/ag-charts-community/src/chart/axis/axisGridLine.ts var AxisGridLine = class { constructor() { this.enabled = true; this.width = 1; this.style = [ { fill: void 0, fillOpacity: 1, stroke: void 0, strokeWidth: void 0, lineDash: [] } ]; } }; __decorateClass([ addFakeTransformToInstanceProperty ], AxisGridLine.prototype, "enabled", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisGridLine.prototype, "width", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisGridLine.prototype, "style", 2); // packages/ag-charts-community/src/chart/axis/axisInterval.ts var AxisInterval = class extends BaseProperties { }; __decorateClass([ addFakeTransformToInstanceProperty ], AxisInterval.prototype, "placement", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisInterval.prototype, "step", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisInterval.prototype, "values", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisInterval.prototype, "minSpacing", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisInterval.prototype, "maxSpacing", 2); // packages/ag-charts-community/src/chart/axis/axisLabel.ts var AxisLabel = class extends BaseProperties { constructor() { super(...arguments); this.enabled = true; this.border = new LabelBorder(); this.wrapping = "never"; this.truncate = false; this.spacing = 5; this.color = "#575757"; this.avoidCollisions = true; this.mirrored = false; this.parallel = false; this._formatters = { "component:year": void 0, "component:month": void 0, "component:day": void 0, "component:none": void 0, "long:year": void 0, "long:month": void 0, "long:day": void 0, "long:none": void 0 }; } /** * The side of the axis line to position the labels on. * -1 = left (default) * 1 = right */ getSideFlag() { return this.mirrored ? 1 : -1; } formatValue(callWithContext2, params, index, options) { const { formatter: formatter2, format } = this; const { type, value, domain, boundSeries } = params; const fractionDigits = params.type === "number" ? params.fractionDigits : void 0; const unit = params.type === "date" ? params.unit : void 0; let result; if (formatter2 != null) { const step = params.type === "date" ? params.step : void 0; const visibleDomain = params.type === "number" ? params.visibleDomain : void 0; result = callWithContext2(formatter2, { value, index, domain, fractionDigits, unit, step, boundSeries, visibleDomain }); } if (format != null && result == null) { const { specifier, dateStyle = "long", truncateDate } = options ?? {}; const cacheKey = `${dateStyle}:${truncateDate ?? "none"}`; let valueFormatter = this._formatters[cacheKey]; const mergedFormat = FormatManager.mergeSpecifiers(specifier, format); if (valueFormatter?.type !== type || valueFormatter?.unit !== unit || !objectsEqual(valueFormatter?.mergedFormat, mergedFormat)) { valueFormatter = { type, mergedFormat, unit, formatter: FormatManager.getFormatter(type, mergedFormat, unit, dateStyle, { truncateDate }) }; this._formatters[cacheKey] = valueFormatter; } result = valueFormatter.formatter?.(value, fractionDigits); } return result == null || isArray(result) ? result : String(result); } }; __decorateClass([ addFakeTransformToInstanceProperty ], AxisLabel.prototype, "enabled", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisLabel.prototype, "border", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisLabel.prototype, "cornerRadius", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisLabel.prototype, "fill", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisLabel.prototype, "fillOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisLabel.prototype, "fontStyle", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisLabel.prototype, "fontWeight", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisLabel.prototype, "fontSize", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisLabel.prototype, "fontFamily", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisLabel.prototype, "wrapping", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisLabel.prototype, "truncate", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisLabel.prototype, "spacing", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisLabel.prototype, "minSpacing", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisLabel.prototype, "color", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisLabel.prototype, "rotation", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisLabel.prototype, "avoidCollisions", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisLabel.prototype, "mirrored", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisLabel.prototype, "padding", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisLabel.prototype, "parallel", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisLabel.prototype, "itemStyler", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisLabel.prototype, "formatter", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisLabel.prototype, "format", 2); // packages/ag-charts-community/src/chart/axis/axisLine.ts var AxisLine = class { constructor() { this.enabled = true; this.width = 1; this.stroke = void 0; } }; __decorateClass([ addFakeTransformToInstanceProperty ], AxisLine.prototype, "enabled", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisLine.prototype, "width", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisLine.prototype, "stroke", 2); // packages/ag-charts-community/src/chart/axis/axisTick.ts var AxisTick = class extends BaseProperties { constructor() { super(...arguments); this.enabled = true; this.width = 1; this.size = 6; } }; __decorateClass([ addFakeTransformToInstanceProperty ], AxisTick.prototype, "enabled", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisTick.prototype, "width", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisTick.prototype, "size", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisTick.prototype, "stroke", 2); // packages/ag-charts-community/src/chart/axis/axisTitle.ts var AxisTitle = class extends BaseProperties { constructor() { super(...arguments); this.caption = new Caption(); this.enabled = false; this.spacing = Caption.SMALL_PADDING; this.fontSize = 10 /* SMALLER */; this.fontFamily = "sans-serif"; this.wrapping = "always"; } }; __decorateClass([ addFakeTransformToInstanceProperty ], AxisTitle.prototype, "enabled", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisTitle.prototype, "text", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisTitle.prototype, "spacing", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisTitle.prototype, "fontStyle", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisTitle.prototype, "fontWeight", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisTitle.prototype, "fontSize", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisTitle.prototype, "fontFamily", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisTitle.prototype, "color", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisTitle.prototype, "wrapping", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AxisTitle.prototype, "formatter", 2); // packages/ag-charts-community/src/chart/axis/axisUtil.ts var NiceMode = /* @__PURE__ */ ((NiceMode2) => { NiceMode2[NiceMode2["TickAndDomain"] = 0] = "TickAndDomain"; NiceMode2[NiceMode2["TicksOnly"] = 1] = "TicksOnly"; NiceMode2[NiceMode2["Off"] = 2] = "Off"; return NiceMode2; })(NiceMode || {}); function prepareAxisAnimationContext(axis) { const [requestedRangeMin, requestedRangeMax] = findMinMax(axis.range); const min = Math.floor(requestedRangeMin); const max = Math.ceil(requestedRangeMax); return { min, max, visible: min !== max }; } var fullCircle = Math.PI * 2; var halfCircle = fullCircle / 2; function normaliseEndRotation(start2, end3) { const directDistance = Math.abs(end3 - start2); if (directDistance < halfCircle) { return end3; } else if (start2 > end3) { return end3 + fullCircle; } return end3 - fullCircle; } function prepareAxisAnimationFunctions(ctx) { const { min, max } = ctx; const outOfBounds = (y) => { return y < min || y > max; }; const tick = { fromFn(node, datum, status) { let { x1, x2, y1, y2 } = node; let opacity = node.opacity; if (status === "added" || outOfBounds(datum.offset)) { ({ x1, x2, y1, y2 } = datum); opacity = 0; } return { x1, x2, y1, y2, opacity, phase: NODE_UPDATE_STATE_TO_PHASE_MAPPING[status] }; }, toFn(_node, datum, status) { const { x1, x2, y1, y2 } = datum; let opacity = 1; if (status === "removed") { opacity = 0; } return { x1, x2, y1, y2, opacity }; }, applyFn(node, props) { node.setProperties(props); node.visible = !outOfBounds(node.y); } }; const label = { fromFn(node, newDatum, status) { const datum = node.previousDatum ?? newDatum; let { x, y, rotationCenterX, rotationCenterY, rotation } = datum; let opacity = node.opacity; if (status === "removed" || outOfBounds(datum.y)) { rotation = newDatum.rotation; } else if (status === "added" || outOfBounds(node.datum.y)) { ({ x, y, rotationCenterX, rotationCenterY, rotation } = newDatum); opacity = 0; } return { x, y, rotationCenterX, rotationCenterY, rotation, opacity, phase: NODE_UPDATE_STATE_TO_PHASE_MAPPING[status] }; }, toFn(node, datum, status) { const { x, y, rotationCenterX, rotationCenterY } = datum; let rotation = 0; let opacity = 1; if (status === "added") { rotation = datum.rotation; } else if (status === "removed") { opacity = 0; rotation = datum.rotation; } else { rotation = normaliseEndRotation(node.previousDatum?.rotation ?? datum.rotation, datum.rotation); } return { x, y, rotationCenterX, rotationCenterY, rotation, opacity, finish: { rotation: datum.rotation } }; } }; const line = { fromFn(node, datum) { const { x1, x2, y1, y2 } = node.previousDatum ?? datum; return { x1, x2, y1, y2, phase: NODE_UPDATE_STATE_TO_PHASE_MAPPING["updated"] }; }, toFn(_node, datum) { const { x1, x2, y1, y2 } = datum; return { x1, x2, y1, y2 }; } }; const group = { fromFn(node, _datum) { const { translationX, translationY } = node; return { translationX, translationY, phase: NODE_UPDATE_STATE_TO_PHASE_MAPPING["updated"] }; }, toFn(_node, datum) { const { translationX, translationY } = datum; return { translationX, translationY }; } }; return { tick, line, label, group }; } function resetAxisGroupFn() { return (_node, datum) => { return { translationX: datum.translationX, translationY: datum.translationY }; }; } function resetAxisLabelSelectionFn() { return (_node, datum) => { return { x: datum.x, y: datum.y, rotationCenterX: datum.rotationCenterX, rotationCenterY: datum.rotationCenterY, rotation: datum.rotation }; }; } function resetAxisLineSelectionFn() { return (_node, datum) => { const { x1, x2, y1, y2 } = datum; return { x1, x2, y1, y2 }; }; } function resetAxisFillSelectionFn() { return (_node, datum) => { const { x1, x2, y1, y2 } = datum; return { x: x1, y: y1, width: x2 - x1, height: y2 - y1 }; }; } // packages/ag-charts-community/src/chart/axis/axis.ts var AxisGroupZIndexMap = /* @__PURE__ */ ((AxisGroupZIndexMap2) => { AxisGroupZIndexMap2[AxisGroupZIndexMap2["TickLines"] = 0] = "TickLines"; AxisGroupZIndexMap2[AxisGroupZIndexMap2["AxisLine"] = 1] = "AxisLine"; AxisGroupZIndexMap2[AxisGroupZIndexMap2["TickLabels"] = 2] = "TickLabels"; return AxisGroupZIndexMap2; })(AxisGroupZIndexMap || {}); function tickLayoutCacheValid(a, b) { return a.domain === b.domain && a.rangeExtent === b.rangeExtent && a.nice[0] === b.nice[0] && a.nice[1] === b.nice[1] && a.gridLength === b.gridLength && a.visibleRange[0] === b.visibleRange[0] && a.visibleRange[1] === b.visibleRange[1] && a.scrollbarKey === b.scrollbarKey && a.initialPrimaryTickCount?.unzoomed === b.initialPrimaryTickCount?.unzoomed && a.initialPrimaryTickCount?.zoomed === b.initialPrimaryTickCount?.zoomed; } function computeBand(scale2, range3, value) { const bandwidth = scale2.bandwidth ?? 0; const step = scale2.step ?? 0; const offset = (step - bandwidth) / 2; const position = scale2.convert(value); const start2 = position - offset; const end3 = position + bandwidth + offset; return [position, clampArray(start2, range3), clampArray(end3, range3)]; } var _Axis = class _Axis { constructor(moduleCtx, scale2) { this.moduleCtx = moduleCtx; this.scale = scale2; this.id = "unknown"; this._crossLines = []; this.nice = true; this.reverse = false; this.interval = new AxisInterval(); this.dataDomain = { domain: [], clipped: false }; this.allowNull = false; this.title = new AxisTitle(); this.gridLength = 0; /** * The distance between the grid ticks and the axis ticks. */ this.gridPadding = 0; /** * Is used to avoid collisions between axis labels and series. */ this.seriesAreaPadding = 0; this.layoutConstraints = { stacked: true, align: "justify", width: 100, unit: "percent" }; this.boundSeries = []; this.includeInvisibleDomains = false; this.interactionEnabled = true; this.axisGroup = new Group({ name: `${this.id}-axis` }); // Order is important to apply the correct z-index. this.tickLineGroup = this.axisGroup.appendChild( new TransformableGroup({ name: `${this.id}-Axis-tick-lines`, zIndex: 0 /* TickLines */ }) ); this.tickLabelGroup = this.axisGroup.appendChild( new TransformableGroup({ name: `${this.id}-Axis-tick-labels`, zIndex: 2 /* TickLabels */ }) ); this.labelGroup = new Group({ name: `${this.id}-Labels`, zIndex: 11 /* SERIES_ANNOTATION */ }); this.gridGroup = new TranslatableGroup({ name: `${this.id}-Axis-grid`, zIndex: 2 /* AXIS_GRID */ }); this.gridFillGroup = this.gridGroup.appendChild(new Group({ name: `${this.id}-gridFills` })); this.gridLineGroup = this.gridGroup.appendChild(new Group({ name: `${this.id}-gridLines` })); this.crossLineRangeGroup = new TransformableGroup({ name: `${this.id}-CrossLines-Range`, zIndex: 6 /* SERIES_CROSSLINE_RANGE */ }); this.crossLineLineGroup = new TransformableGroup({ name: `${this.id}-CrossLines-Line`, zIndex: 10 /* SERIES_CROSSLINE_LINE */ }); this.crossLineLabelGroup = new TransformableGroup({ name: `${this.id}-CrossLines-Label`, zIndex: 15 /* SERIES_LABEL */ }); this.tickLabelGroupSelection = Selection.select( this.tickLabelGroup, TransformableText, false ); this.line = new AxisLine(); this.tick = new AxisTick(); this.gridLine = new AxisGridLine(); this.label = this.createLabel(); this.defaultTickMinSpacing = _Axis.defaultTickMinSpacing; this.translation = { x: 0, y: 0 }; this.layout = { label: { fractionDigits: 0, spacing: this.label.spacing, format: this.label.format }, labelThickness: 0 }; this.axisContext = void 0; this.cleanup = new CleanupRegistry(); // AG-15360 Avoid calling removeTooltip() if no tooltip is shown. This avoid a laggy tooltips caused by interference // with SeriesAreaManager's tooltip updates. this.isHovering = false; this.range = [0, 1]; this.visibleRange = [0, 1]; this.animatable = true; this.tickLayout = void 0; this.formatterBoundSeries = new WeakCache(() => { const { direction, boundSeries } = this; return deepFreeze(boundSeries.flatMap((series) => series.getFormatterContext(direction))); }); this.moduleMap = new ModuleMap(); this.range = this.scale.range.slice(); for (const crossLine of this.crossLines) { this.initCrossLine(crossLine); } this.cleanup.register( this.moduleCtx.widgets.containerWidget.addListener("mousemove", (e) => this.onMouseMove(e)), this.moduleCtx.widgets.containerWidget.addListener("mouseleave", () => this.endHovering()) ); } set crossLines(value) { const { CrossLineConstructor } = this.constructor; for (const crossLine of this._crossLines) { this.detachCrossLine(crossLine); } this._crossLines = value.map((crossLine) => { const instance = new CrossLineConstructor(); instance.set(crossLine); return instance; }); for (const crossLine of this._crossLines) { this.attachCrossLine(crossLine); this.initCrossLine(crossLine); } } get crossLines() { return this._crossLines; } get type() { return this.constructor.type ?? ""; } get primaryLabel() { return void 0; } get primaryTick() { return void 0; } isCategoryLike() { return false; } resetAnimation(_phase) { } onMouseMove(event) { const node = this.tickLabelGroup.pickNode(event.currentX, event.currentY); const datum = node?.datum; const { textUntruncated: title = void 0 } = datum ?? {}; if (title) { this.moduleCtx.tooltipManager.updateTooltip( this.id, { canvasX: event.currentX, canvasY: event.currentY, showArrow: false }, [{ type: "structured", title }] ); this.isHovering = true; } else { this.endHovering(); } } endHovering() { if (this.isHovering) { this.moduleCtx.tooltipManager.removeTooltip(this.id, void 0, true); this.isHovering = false; } } attachCrossLine(crossLine) { this.crossLineRangeGroup.appendChild(crossLine.rangeGroup); this.crossLineLineGroup.appendChild(crossLine.lineGroup); this.crossLineLabelGroup.appendChild(crossLine.labelGroup); } detachCrossLine(crossLine) { crossLine.rangeGroup.remove(); crossLine.lineGroup.remove(); crossLine.labelGroup.remove(); } destroy() { this.moduleMap.destroy(); this.cleanup.flush(); } setScaleRange(visibleRange) { const { range: rr, scale: scale2 } = this; const span = (rr[1] - rr[0]) / (visibleRange[1] - visibleRange[0]); const shift = span * visibleRange[0]; const start2 = rr[0] - shift; scale2.range = [start2, start2 + span]; } updateScale() { const { range: [r0, r1] } = this; this.setScaleRange(this.visibleRange); for (const crossLine of this.crossLines) { crossLine.clippedRange = [r0, r1]; } } setCrossLinesVisible(visible) { this.crossLineRangeGroup.visible = visible; this.crossLineLineGroup.visible = visible; this.crossLineLabelGroup.visible = visible; } attachAxis(groups) { groups.gridNode.appendChild(this.gridGroup); groups.axisNode.appendChild(this.axisGroup); groups.labelNode.appendChild(this.labelGroup); groups.crossLineRangeNode.appendChild(this.crossLineRangeGroup); groups.crossLineLineNode.appendChild(this.crossLineLineGroup); groups.crossLineLabelNode.appendChild(this.crossLineLabelGroup); } detachAxis() { this.gridGroup.remove(); this.axisGroup.remove(); this.labelGroup.remove(); this.crossLineRangeGroup.remove(); this.crossLineLineGroup.remove(); this.crossLineLabelGroup.remove(); } attachLabel(axisLabelNode) { this.labelGroup.append(axisLabelNode); } /** * Checks if a point or an object is in range. * @param value A point (or object's starting point). * @param tolerance Expands the range on both ends by this amount. */ inRange(value, tolerance = 0) { const [min, max] = findMinMax(this.range); return value >= min - tolerance && value <= max + tolerance; } /** * Get a point's overflow on the range, expanded to include the non-visible range. * @param value Point * @returns Overflow */ getRangeOverflow(value) { const { range: rr, visibleRange: vr } = this; const size = (rr[1] - rr[0]) / (vr[1] - vr[0]); const [min, max] = findMinMax([rr[0] - size * vr[0], rr[0] - size * vr[0] + size]); if (value < min) return value - min; if (value > max) return value - max; return 0; } onGridLengthChange(value, prevValue) { if (prevValue ^ value) { this.onGridVisibilityChange(); } for (const crossLine of this.crossLines) { this.initCrossLine(crossLine); } } onGridVisibilityChange() { } createLabel() { return new AxisLabel(); } /** * Creates/removes/updates the scene graph nodes that constitute the axis. */ update() { this.formatterBoundSeries.clear(); this.updatePosition(); this.updateSelections(); this.gridLineGroup.visible = this.gridLine.enabled; this.updateLabels(); this.updateCrossLines(); } getLabelStyles(params, additionalStyles, label = this.label) { const defaultStyle = { border: label.border, color: label.color, cornerRadius: label.cornerRadius, fill: label.fill, fillOpacity: label.fillOpacity, fontFamily: label.fontFamily, fontSize: label.fontSize, fontStyle: label.fontStyle, fontWeight: label.fontWeight, padding: label.padding, spacing: label.spacing }; let stylerOutput; if (label.itemStyler) { stylerOutput = this.cachedCallWithContext(label.itemStyler, { ...params, ...defaultStyle }); } const merged = mergeDefaults(stylerOutput, additionalStyles, defaultStyle); return { border: merged.border, color: merged.color, cornerRadius: merged.cornerRadius, fill: merged.fill, fillOpacity: merged.fillOpacity, fontFamily: merged.fontFamily, fontSize: merged.fontSize, fontStyle: merged.fontStyle, fontWeight: merged.fontWeight, padding: merged.padding, spacing: merged.spacing }; } getTickSize(tick = this.tick) { return tick.enabled ? tick.size : 0; } getTickSpacing(tick = this.tick) { if (!tick.enabled) return 0; const scrollbar = this.chartLayout?.scrollbars?.[this.id]; if (!scrollbar?.enabled || scrollbar.placement !== "inner") return 0; return scrollbar.tickSpacing ?? 0; } processData() { this.invalidateLayoutCache(); const { includeInvisibleDomains, boundSeries, direction } = this; const visibleSeries = includeInvisibleDomains ? boundSeries : boundSeries.filter((s) => s.isEnabled()); const domains = visibleSeries.map((series) => series.getDomain(direction)); this.setDomains(...domains); } getDomainExtentsNice() { return [this.nice, this.nice]; } setDomains(...domains) { let normalizedDomain; let animatable; if (domains.length > 0) { const result = this.scale.normalizeDomains(...domains); normalizedDomain = { domain: result.domain, sortMetadata: { sortOrder: 1 } }; animatable = result.animatable; } else { normalizedDomain = { domain: [] }; animatable = true; } this.dataDomain = this.normaliseDataDomain(normalizedDomain); this.allowNull = this.dataDomain.domain.some(function(v) { return v == null; }); if (this.reverse) { this.dataDomain.domain.reverse(); } this.animatable = animatable; } calculateDomain(initialPrimaryTickCount, scrollbarKey = "none") { const { dataDomain: { domain }, range: range3, scale: scale2, gridLength } = this; const rangeExtent = findRangeExtent(range3); const visibleRange = [0, 1]; const nice = this.getDomainExtentsNice(); this.updateScale(); const { unzoomedTickLayoutCache } = this; let unzoomedTickLayout; if (unzoomedTickLayoutCache == null || !tickLayoutCacheValid(unzoomedTickLayoutCache, { domain, rangeExtent, nice, gridLength, visibleRange, initialPrimaryTickCount, scrollbarKey })) { const scaleRange = scale2.range; this.setScaleRange([0, 1]); const niceMode = nice.map((n) => n ? 0 /* TickAndDomain */ : 2 /* Off */); unzoomedTickLayout = this.calculateTickLayout(domain, niceMode, [0, 1], initialPrimaryTickCount); scale2.range = scaleRange; this.unzoomedTickLayoutCache = { domain, rangeExtent, nice, gridLength, visibleRange, initialPrimaryTickCount, scrollbarKey, tickLayout: unzoomedTickLayout }; } else { unzoomedTickLayout = unzoomedTickLayoutCache.tickLayout; } this.updateScale(); scale2.domain = unzoomedTickLayout.niceDomain; return { unzoomedTickLayout, domain: scale2.domain }; } calculateLayout(initialPrimaryTickCount, chartLayout) { this.chartLayout = chartLayout; const scrollbarKey = this.getScrollbarLayoutCacheKey(chartLayout); const { visibleRange } = this; const unzoomed = visibleRange[0] === 0 && visibleRange[1] === 1; const { unzoomedTickLayout, domain } = this.calculateDomain(initialPrimaryTickCount, scrollbarKey); const nice = this.getDomainExtentsNice(); let tickLayout; if (unzoomed) { tickLayout = unzoomedTickLayout; } else { const { range: range3, gridLength } = this; const rangeExtent = findRangeExtent(range3); const niceMode = nice.map((n) => n ? 1 /* TicksOnly */ : 2 /* Off */); const { tickLayoutCache } = this; if (tickLayoutCache == null || !tickLayoutCacheValid(tickLayoutCache, { domain, rangeExtent, nice, gridLength, visibleRange, initialPrimaryTickCount, scrollbarKey })) { tickLayout = this.calculateTickLayout(domain, niceMode, visibleRange, initialPrimaryTickCount); this.tickLayoutCache = { domain, rangeExtent, nice, gridLength, visibleRange, initialPrimaryTickCount, scrollbarKey, tickLayout }; } else { tickLayout = tickLayoutCache.tickLayout; } } const { rawTickCount: zoomedTickCount = 0, fractionDigits, bbox } = tickLayout; const unzoomedTickCount = unzoomedTickLayout.rawTickCount ?? 0; const primaryTickCount = zoomedTickCount !== 0 && unzoomedTickCount !== 0 ? { zoomed: zoomedTickCount, unzoomed: unzoomedTickCount } : void 0; this.tickLayout = tickLayout.layout; this.layout.label = { fractionDigits, spacing: this.label.spacing, format: this.label.format }; this.layoutCrossLines(); return { primaryTickCount, bbox }; } invalidateLayoutCache() { this.unzoomedTickLayoutCache = void 0; this.tickLayoutCache = void 0; this.tickLayout = void 0; } getScrollbarLayoutCacheKey(chartLayout) { const scrollbar = chartLayout?.scrollbars?.[this.id]; if (!scrollbar?.enabled) return "none"; return `${scrollbar.placement}:${scrollbar.spacing}:${scrollbar.thickness}:${scrollbar.tickSpacing}`; } updateCrossLines() { const crosslinesVisible = this.hasDefinedDomain() || this.hasVisibleSeries(); for (const crossLine of this.crossLines) { crossLine.update(crosslinesVisible); } } updatePosition() { const { crossLineRangeGroup, crossLineLineGroup, crossLineLabelGroup, gridGroup, translation } = this; const translationX = Math.floor(translation.x); const translationY = Math.floor(translation.y); gridGroup.setProperties({ translationX, translationY }); crossLineRangeGroup.setProperties({ translationX, translationY }); crossLineLineGroup.setProperties({ translationX, translationY }); crossLineLabelGroup.setProperties({ translationX, translationY }); } // For formatting (nice rounded) tick values. tickFormatter(domain, ticks, primary, inputFractionDigits, inputTimeInterval, dateStyle = "long") { const { moduleCtx, label } = this; const { formatManager } = moduleCtx; const primaryLabel = primary ? this.primaryLabel : void 0; const tickFormatParams = this.tickFormatParams(domain, ticks, inputFractionDigits, inputTimeInterval); const boundSeries = this.formatterBoundSeries.get(); let fractionDigits; let timeInterval3; let truncateDate; if (tickFormatParams.type === "number") { fractionDigits = tickFormatParams.fractionDigits; } else if (tickFormatParams.type === "date") { const { unit, step, epoch } = tickFormatParams; timeInterval3 = { unit, step, epoch }; truncateDate = tickFormatParams.truncateDate; } const f = this.uncachedCallWithContext.bind(this); const params = { datum: void 0, seriesId: void 0, legendItemName: void 0, key: void 0, source: "axis-label", property: this.getFormatterProperty(), domain, boundSeries }; const currentLabel = primaryLabel ?? label; const specifier = primary ? label.format : void 0; const { allowNull } = this; const options = { specifier: FormatManager.mergeSpecifiers(primaryLabel?.format, label.format), truncateDate, allowNull }; return (value, index) => { const formatParams = this.datumFormatParams(value, params, fractionDigits, timeInterval3, dateStyle); formatParams.value = value; return currentLabel.formatValue(f, formatParams, index, { specifier, dateStyle, truncateDate }) ?? formatManager.format(f, formatParams, options) ?? formatManager.defaultFormat(formatParams, options); }; } formatDatum(contextProvider, input, source, seriesId, legendItemName, datum, key, domain, label, params, allowNull) { if (input == null && !allowNull) return ""; const { moduleCtx, dataDomain } = this; domain ?? (domain = dataDomain.domain); const { formatManager } = moduleCtx; const boundSeries = this.formatterBoundSeries.get(); let inputFractionDigits; switch (source) { case "crosshair": case "annotation-label": inputFractionDigits = this.layout.label.fractionDigits + 1; break; case "series-label": inputFractionDigits = 2; break; case "tooltip": inputFractionDigits = 3; break; case "legend-label": inputFractionDigits = void 0; break; } const formatParams = this.datumFormatParams( input, { source, datum, seriesId, legendItemName, key, property: this.getFormatterProperty(), domain, boundSeries }, inputFractionDigits, void 0, "long" ); const { type, value } = formatParams; const f = this.createCallWithContext(contextProvider); const result = label?.formatValue(f, type, value, params ?? formatParams) ?? formatManager.format(f, formatParams, { allowNull }) ?? this.label.formatValue(f, formatParams, Number.NaN) ?? formatManager.defaultFormat(formatParams); return isArray(result) ? result : String(result); } getBBox() { return this.axisGroup.getBBox(); } initCrossLine(crossLine) { crossLine.scale = this.scale; crossLine.gridLength = this.gridLength; } hasVisibleSeries() { return this.boundSeries.some((s) => s.isEnabled()); } clipTickLines(x, y, width2, height2) { this.tickLineGroup.setClipRect(new BBox(x, y, width2, height2)); } clipGrid(x, y, width2, height2) { this.gridGroup.setClipRect(new BBox(x, y, width2, height2)); } getFormatterProperty() { const { direction, boundSeries } = this; let resolvedDirection = direction; for (const series of boundSeries) { const seriesResolvedDirection = series.resolveKeyDirection(direction); if (seriesResolvedDirection !== direction) { resolvedDirection = seriesResolvedDirection; break; } } return resolvedDirection; } getTitleFormatterParams(domain) { const { direction } = this; const boundSeries = this.formatterBoundSeries.get(); return { domain, direction, boundSeries, defaultValue: this.title?.text }; } normaliseDataDomain(d) { return { domain: [...d.domain], clipped: false }; } getLayoutTranslation() { return this.translation; } getLayoutState() { return { id: this.id, rect: this.getBBox(), translation: this.getLayoutTranslation(), gridPadding: this.gridPadding, seriesAreaPadding: this.seriesAreaPadding, tickSize: this.getTickSize(), direction: this.direction, domain: this.dataDomain.domain, scale: this.scale, ...this.layout }; } getModuleMap() { return this.moduleMap; } getUpdateTypeOnResize() { return 5 /* PERFORM_LAYOUT */; } createModuleContext() { this.axisContext ?? (this.axisContext = this.createAxisContext()); return { ...this.moduleCtx, parent: this.axisContext }; } createAxisContext() { const { scale: scale2 } = this; return { axisId: this.id, scale: this.scale, direction: this.direction, continuous: ContinuousScale.is(scale2) || DiscreteTimeScale.is(scale2), getCanvasBounds: () => { return Transformable.toCanvas(this.axisGroup); }, seriesKeyProperties: () => this.boundSeries.reduce((keys, series) => { const seriesKeys = series.getKeyProperties(this.direction); for (const key of seriesKeys) { keys.add(key); } return keys; }, /* @__PURE__ */ new Set()), seriesIds: () => this.boundSeries.map((series) => series.id), scaleInvert: (val) => scale2.invert(val, true), scaleInvertNearest: (val) => scale2.invert(val, true), formatScaleValue: (value, source, label) => { const { allowNull } = this; return this.formatDatum( void 0, value, source, void 0, void 0, void 0, void 0, void 0, label, void 0, allowNull ); }, attachLabel: (node) => this.attachLabel(node), inRange: (value, tolerance) => this.inRange(value, tolerance), getRangeOverflow: (value) => this.getRangeOverflow(value), pickBand: (point) => this.pickBand(point), measureBand: (value) => this.measureBand(value) }; } pickBand(point) { if (!BandScale.is(this.scale)) { return; } const { scale: scale2, range: range3, id } = this; const value = scale2.invert(this.isVertical() ? point.y : point.x, true); const [position, start2, end3] = computeBand(scale2, range3, value); return { id, value, band: [start2, end3], position }; } measureBand(value) { if (!BandScale.is(this.scale)) { return; } const [, start2, end3] = computeBand(this.scale, this.range, value); return { band: [start2, end3] }; } isVertical() { return this.direction === "y" /* Y */; } isReversed() { return this.reverse; } cachedCallWithContext(fn, params) { const { callbackCache, chartService } = this.moduleCtx; return callbackCache.call([this, chartService], fn, params); } uncachedCallWithContext(fn, params) { const { chartService } = this.moduleCtx; return callWithContext([this, chartService], fn, params); } createCallWithContext(contextProvider) { const { chartService } = this.moduleCtx; return (fn, params) => callWithContext([contextProvider, this, chartService], fn, params); } }; _Axis.defaultTickMinSpacing = 50; _Axis.CrossLineConstructor = CartesianCrossLine; __decorateClass([ addFakeTransformToInstanceProperty ], _Axis.prototype, "nice", 2); __decorateClass([ addFakeTransformToInstanceProperty ], _Axis.prototype, "reverse", 2); __decorateClass([ addFakeTransformToInstanceProperty ], _Axis.prototype, "interval", 2); __decorateClass([ addFakeTransformToInstanceProperty ], _Axis.prototype, "title", 2); __decorateClass([ ObserveChanges((target, value, oldValue) => target.onGridLengthChange(value, oldValue)) ], _Axis.prototype, "gridLength", 2); var Axis = _Axis; // packages/ag-charts-community/src/chart/axis/cartesianAxisLabel.ts var CartesianAxisLabel = class extends AxisLabel { constructor() { super(...arguments); this.autoRotateAngle = 335; } }; __decorateClass([ addFakeTransformToInstanceProperty ], CartesianAxisLabel.prototype, "autoRotate", 2); __decorateClass([ addFakeTransformToInstanceProperty ], CartesianAxisLabel.prototype, "autoRotateAngle", 2); // packages/ag-charts-community/src/scale/timeScale.ts var sunday = new Date(1970, 0, 4); var TimeScale = class _TimeScale extends ContinuousScale { constructor() { super([], [0, 1]); this.type = "time"; } static is(value) { return value instanceof _TimeScale; } toDomain(d) { return new Date(d); } convert(value, options) { return super.convert(typeof value === "number" ? value : value?.valueOf() ?? Number.NaN, options); } invert(value) { return new Date(super.invert(value)); } niceDomain(ticks, domain = this.domain) { if (domain.length < 2) return []; let [d0, d1] = domain; const maxAttempts = 4; const availableRange = this.getPixelRange(); for (let i = 0; i < maxAttempts; i++) { const [n0, n1] = updateNiceDomainIteration(d0, d1, ticks, availableRange); if (dateToNumber(d0) === dateToNumber(n0) && dateToNumber(d1) === dateToNumber(n1)) { break; } d0 = n0; d1 = n1; } return [d0, d1]; } /** * Returns uniformly-spaced dates that represent the scale's domain. */ ticks(params, domain = this.domain, visibleRange = [0, 1], { extend = false } = {}) { const { nice, interval, tickCount = ContinuousScale.defaultTickCount, minTickCount, maxTickCount } = params; if (domain.length < 2) return; const timestamps = domain.map(dateToNumber); const start2 = timestamps[0]; const stop = timestamps.at(-1); if (interval != null) { const availableRange = this.getPixelRange(); return { ticks: getDateTicksForInterval({ start: start2, stop, interval, availableRange, visibleRange, extend }) ?? getDefaultDateTicks({ start: start2, stop, tickCount, minTickCount, maxTickCount, visibleRange, extend }), count: void 0 }; } else if (nice.every(Boolean) && tickCount === 2) { return { ticks: domain, count: void 0 }; } else if (nice.every(Boolean) && tickCount === 1) { return { ticks: domain.slice(0, 1), count: void 0 }; } const timeInterval3 = getTickTimeInterval(start2, stop, tickCount, minTickCount, maxTickCount, { weekStart: sunday }); if (timeInterval3 == null) return; const ticks = intervalRange(timeInterval3, new Date(start2), new Date(stop), { visibleRange, extend }); const firstTickIndex = intervalRangeStartIndex(timeInterval3, new Date(start2), new Date(stop), { visibleRange, extend }); return { ticks, count: void 0, firstTickIndex, timeInterval: timeInterval3 }; } }; function getDefaultDateTicks({ start: start2, stop, tickCount, minTickCount, maxTickCount, visibleRange, extend }) { const t = getTickTimeInterval(start2, stop, tickCount, minTickCount, maxTickCount, { weekStart: sunday }); return t ? intervalRange(t, new Date(start2), new Date(stop), { visibleRange, extend }) : []; } function getDateTicksForInterval({ start: start2, stop, interval, availableRange, visibleRange, extend }) { if (!interval) { return []; } if (isPlainObject(interval) || typeof interval === "string") { const ticks2 = intervalRange(interval, new Date(start2), new Date(stop), { visibleRange, extend }); if (isDenseInterval(ticks2.length, availableRange)) { return; } return ticks2; } const absInterval = Math.abs(interval); if (isDenseInterval(Math.abs(stop - start2) / absInterval, availableRange)) return; const tickInterval = TickIntervals.findLast((t) => absInterval % t.duration === 0); if (tickInterval) { const { timeInterval: timeInterval3, step, duration } = tickInterval; const alignedInterval = { ...timeInterval3, step: step * intervalStep(timeInterval3) * Math.round(absInterval / duration), epoch: defaultEpoch(timeInterval3, { weekStart: sunday }) }; return intervalRange(alignedInterval, new Date(start2), new Date(stop), { visibleRange, extend }); } let date2 = new Date(Math.min(start2, stop)); const stopDate = new Date(Math.max(start2, stop)); const ticks = []; while (date2 <= stopDate) { ticks.push(date2); date2 = new Date(date2); date2.setMilliseconds(date2.getMilliseconds() + absInterval); } return ticks; } function updateNiceDomainIteration(d0, d1, ticks, availableRange) { const { interval } = ticks; const start2 = Math.min(dateToNumber(d0), dateToNumber(d1)); const stop = Math.max(dateToNumber(d0), dateToNumber(d1)); let i; if (isPlainObject(interval) || typeof interval === "string") { i = interval; } else { let tickCount; if (typeof interval === "number") { tickCount = (stop - start2) / Math.max(interval, 1); if (isDenseInterval(tickCount, availableRange)) { tickCount = void 0; } } tickCount ?? (tickCount = ticks.tickCount ?? ContinuousScale.defaultTickCount); i = getTickTimeInterval(start2, stop, tickCount, ticks.minTickCount, ticks.maxTickCount, { weekStart: sunday }); } if (i == null) return [d0, d1]; const domain = intervalRange(i, new Date(start2), new Date(stop), { extend: true }); if (domain == null || domain.length < 2) return [d0, d1]; const r0 = domain[0]; const r1 = domain.at(-1); return d0 <= d1 ? [r0, r1] : [r1, r0]; } // packages/ag-charts-community/src/scale/ordinalTimeScale.ts var APPROXIMATE_THRESHOLD2 = 1e3; var OrdinalTimeScale = class _OrdinalTimeScale extends DiscreteTimeScale { constructor() { super(...arguments); this.type = "ordinal-time"; this.defaultTickCount = ContinuousScale.defaultTickCount; this._domain = []; this.isReversed = false; } static is(value) { return value instanceof _OrdinalTimeScale; } set domain(domain) { if (domain === this._domain) return; this.invalid = true; this._domain = domain; this._bands = void 0; this._numericBands = void 0; this._uniformityCache = void 0; this.isReversed = domainReversed(domain); } get domain() { return this._domain; } get bands() { this._bands ?? (this._bands = this.isReversed ? this.domain.slice().reverse() : this.domain); return this._bands; } get numericBands() { this._numericBands ?? (this._numericBands = this.bands.map((d) => d.valueOf())); return this._numericBands; } getUniformityCache(visibleRange) { const { bands } = this; const n = bands.length; if (!visibleRange || visibleRange[0] === 0 && visibleRange[1] === 1) { if (n > APPROXIMATE_THRESHOLD2 && this._uniformityCache === void 0) { this._uniformityCache = checkUniformityBySampling(bands); } return this._uniformityCache; } const startIdx = Math.floor(visibleRange[0] * n); const endIdx = Math.min(Math.ceil(visibleRange[1] * n), n - 1); return checkUniformityBySampling(bands, startIdx, endIdx); } normalizeDomains(...domains) { const nonEmptyDomains = domains.filter((d) => d.domain.length > 0); if (nonEmptyDomains.length === 0) { return { domain: [], animatable: false }; } const firstDomain = nonEmptyDomains[0].domain; const allSame = nonEmptyDomains.every((d) => d.domain === firstDomain); if (nonEmptyDomains.length === 1 || allSame) { const input = nonEmptyDomains[0]; let domain = input.domain; let sortOrder; let isUnique = false; if (input.sortMetadata?.sortOrder === void 0) { sortOrder = datesSortOrder(domain); } else { sortOrder = input.sortMetadata.sortOrder; isUnique = input.sortMetadata.isUnique ?? false; } if (sortOrder === -1) { domain = domain.slice().reverse(); } else if (sortOrder == null) { domain = isUnique ? domain.slice().sort((a, b) => a.valueOf() - b.valueOf()) : sortAndUniqueDates(domain.slice()); } return { domain, animatable: true }; } return { domain: sortAndUniqueDates(nonEmptyDomains.map((d) => d.domain).flat()), animatable: true }; } ticks(params, domain, visibleRange = [0, 1], { extend = false, dropInitial = false } = {}) { const { interval, maxTickCount, tickCount = maxTickCount } = params; const { bands, reversed } = this; if (!bands.length) return; if (reversed) { visibleRange = [1 - visibleRange[1], 1 - visibleRange[0]]; } this.refresh(); if (interval == null) { const { ticks: ticks2, tickOffset, tickEvery } = this.getDefaultTicks(domain, tickCount, visibleRange, extend); let firstTickIndex = ticks2.length > 0 ? this.findIndex(ticks2[0]) : void 0; firstTickIndex = firstTickIndex == null ? void 0 : Math.floor((firstTickIndex - tickOffset) / tickEvery); return { ticks: ticks2, count: void 0, firstTickIndex }; } let start2; let stop; if (domain && domain.length >= 2) { start2 = domain[0].valueOf(); stop = domain.at(-1).valueOf(); } else { start2 = bands[0].valueOf(); stop = bands.at(-1).valueOf(); } const [r0, r1] = this.range; const availableRange = Math.abs(r1 - r0); const dateTicks = getDateTicksForInterval({ start: start2, stop, interval, availableRange, visibleRange, extend }) ?? this.getDefaultTicks(domain, tickCount, visibleRange, extend).ticks; const ticks = []; let lastIndex = -1; for (const dateTick of dateTicks) { const index = this.findIndex(dateTick, 1 /* Trailing */) ?? -1; const duplicated = index === lastIndex; lastIndex = index; if (!(dropInitial && index === 0) && index !== -1 && !duplicated) { ticks.push(bands[index]); } } return { ticks, count: void 0, firstTickIndex: void 0 }; } stepTicks(bandStep, domain, visibleRange = [0, 1], dropLast = true) { const bandIndices = domain ? this.bandDomainIndices(domain) : void 0; const ticks = this.ticksEvery(bandIndices, visibleRange, bandStep, 0, false); const lastTick = ticks.at(-1); const lastBandIndex = dropLast && bandStep > 1 ? bandIndices?.[1] : void 0; const lastTickIndex = lastBandIndex != null && lastTick != null ? this.findIndex(lastTick) : void 0; if (lastTickIndex != null && lastBandIndex != null && lastBandIndex - lastTickIndex < bandStep) { ticks.pop(); } return ticks; } bandCount(visibleRange = [0, 1]) { const { domain } = this; const startIndex = Math.floor(visibleRange[0] * domain.length); const endIndex = Math.ceil(visibleRange[1] * domain.length); return endIndex - startIndex; } getDefaultTicks(domain, maxTickCount, visibleRange, extend) { const { bands } = this; const tickEvery = Math.ceil(bands.length / maxTickCount); const tickOffset = Math.floor(tickEvery / 2); const bandIndices = domain ? this.bandDomainIndices(domain) : void 0; return { ticks: this.ticksEvery(bandIndices, visibleRange, tickEvery, tickOffset, extend), tickOffset, tickEvery }; } bandDomainIndices(domain) { const isReversed = domainReversed(domain); const i0 = this.findIndex(domain[isReversed ? domain.length - 1 : 0], 1 /* Trailing */) ?? 0; const i1 = this.findIndex(domain[isReversed ? 0 : domain.length - 1], 1 /* Trailing */) ?? this.bands.length - 1; return [i0, i1]; } ticksEvery([i0, i1] = [0, this.bands.length], visibleRange, tickEvery, tickOffset, extend) { const { bands } = this; const offset = i0; const span = i1 - i0 + 1; let startIndex = offset + Math.floor(visibleRange[0] * span); let endIndex = offset + Math.ceil(visibleRange[1] * span); if (extend) { startIndex -= tickEvery; endIndex += tickEvery; } startIndex = Math.max(startIndex, 0); endIndex = Math.min(endIndex, bands.length); let ticks; if (tickEvery <= 1) { ticks = bands.slice(startIndex, endIndex); } else { ticks = []; for (let index = startIndex; index < endIndex; index += 1) { if ((index - offset + tickOffset) % tickEvery === 0) { ticks.push(bands[index]); } } } return ticks; } }; function domainReversed(domain) { return domain.length > 0 && domain[0] > domain.at(-1); } // packages/ag-charts-community/src/scale/unitTimeScale.ts var APPROXIMATE_THRESHOLD3 = 1e3; var MAX_BANDS = 5e7; var UnitTimeScale = class _UnitTimeScale extends DiscreteTimeScale { constructor() { super(...arguments); this.type = "unit-time"; this.defaultTickCount = 12; this._domain = []; this._bands = void 0; } static is(value) { return value instanceof _UnitTimeScale; } static supportsInterval(domain, interval) { return supportsInterval(domain, interval); } set domain(domain) { if (domain === this._domain) return; this._domain = domain; this._bands = void 0; this._numericBands = void 0; this._uniformityCache = void 0; this._domainBoundaries = void 0; this._bandRangeCache = void 0; this._encodedBands = void 0; this._encodingParams = void 0; this._linearParams = void 0; } get domain() { return this._domain; } get interval() { return this._interval; } set interval(interval) { if (this._interval === interval) return; this._interval = interval; this._bands = void 0; this._numericBands = void 0; this._uniformityCache = void 0; this._domainBoundaries = void 0; this._bandRangeCache = void 0; this._encodedBands = void 0; this._encodingParams = void 0; this._linearParams = void 0; } get bands() { if (this._bands === void 0) { this.ensureEncodedBands(); if (this._encodedBands != null && this._encodingParams != null) { const params = this._encodingParams; this._bands = this._encodedBands.map((e) => decodeIntervalValue(e, params)); } else { this._bands = []; } } return this._bands; } get numericBands() { if (this._numericBands === void 0) { this.ensureEncodedBands(); if (this._encodedBands != null && this._encodingParams != null) { const params = this._encodingParams; this._numericBands = this._encodedBands.map((e) => encodedToTimestamp(e, params)); } else { this._numericBands = []; } } return this._numericBands; } /** * Ensure encoded bands are computed. This is the numeric-first optimization: * we compute just the encoded values (cheap numbers) and defer Date creation. */ ensureEncodedBands() { if (this._encodedBands !== void 0) return; const { domain, interval } = this; if (domain.length < 2 || interval == null) { this._encodedBands = []; return; } const bandRange = this.getCachedBandRange(); if (bandRange == null) { this._encodedBands = []; return; } const [start2, stop] = bandRange; const rangeParams = { visibleRange: [0, 1], extend: false }; if (intervalRangeCount(interval, start2, stop, rangeParams) > MAX_BANDS) { logger_exports.warnOnce(`the configured unit results in too many bands, ignoring. Supply a larger unit.`); this._encodedBands = []; return; } const { encodedValues, encodingParams } = intervalRangeNumeric(interval, start2, stop, rangeParams); this._encodedBands = encodedValues; this._encodingParams = encodingParams; } /** Override to return band count without triggering Date materialization */ getBandCountForUpdate() { this.ensureEncodedBands(); return this._encodedBands?.length ?? 0; } getUniformityCache(visibleRange) { const n = this.getBandCountForUpdate(); if (!visibleRange || visibleRange[0] === 0 && visibleRange[1] === 1) { if (n > APPROXIMATE_THRESHOLD3 && this._uniformityCache === void 0) { this.ensureEncodedBands(); if (this._encodingParams != null && this._encodedBands != null && this._encodedBands.length >= 2) { const t0 = encodedToTimestamp(this._encodedBands[0], this._encodingParams); const t1 = encodedToTimestamp(this._encodedBands[1], this._encodingParams); this._uniformityCache = { isUniform: true, interval: t1 - t0 }; } else { this._uniformityCache = { isUniform: false }; } } return this._uniformityCache; } this.ensureEncodedBands(); if (this._encodingParams != null && this._encodedBands != null && this._encodedBands.length >= 2) { const t0 = encodedToTimestamp(this._encodedBands[0], this._encodingParams); const t1 = encodedToTimestamp(this._encodedBands[1], this._encodingParams); return { isUniform: true, interval: t1 - t0 }; } return { isUniform: false }; } normalizeDomains(...domains) { return normalizeContinuousDomains(...domains); } getCachedBandRange() { const { domain, interval } = this; if (domain.length < 2 || interval == null) return void 0; this._bandRangeCache ?? (this._bandRangeCache = { start: intervalFloor(interval, domain[0]), stop: intervalFloor(interval, domain[1]) }); return [this._bandRangeCache.start, this._bandRangeCache.stop]; } getDomainBoundaries() { const { interval } = this; if (interval == null) return void 0; if (this._domainBoundaries === void 0) { const bandRange = this.getCachedBandRange(); if (bandRange == null) return void 0; const [start2, stop] = bandRange; const d0 = Math.min(start2.valueOf(), stop.valueOf()); const d1 = Math.max(start2.valueOf(), stop.valueOf()); const dNext = intervalNext(interval, new Date(d1)).valueOf(); this._domainBoundaries = { d0, dNext }; } return this._domainBoundaries; } /** Get linear params for O(1) index calculation and scaling metadata */ getLinearParams() { if (this._linearParams === void 0) { this.ensureEncodedBands(); if (this._encodedBands != null && this._encodingParams != null && this._encodedBands.length >= 2) { const firstBandTime = encodedToTimestamp(this._encodedBands[0], this._encodingParams); const secondBandTime = encodedToTimestamp(this._encodedBands[1], this._encodingParams); this._linearParams = { firstBandTime, intervalMs: secondBandTime - firstBandTime }; } } return this._linearParams; } /** Check if current encoding uses a linear unit (exact arithmetic, no DST issues) */ isLinearUnit() { const unit = this._encodingParams?.unit; return unit === "millisecond" || unit === "second" || unit === "minute" || unit === "hour"; } /** * O(1) findIndex for uniform bands. * For linear units (ms/sec/min/hour), uses pure arithmetic without verification. * For non-linear units (day/month/year), verifies against actual band values. */ findIndex(value, alignment = 0 /* Leading */) { if (value == null) return void 0; const n = this.getBandCountForUpdate(); if (n === 0) return void 0; if (n === 1) return 0; const linearParams = this.getLinearParams(); if (linearParams == null || linearParams.intervalMs === 0) { return super.findIndex(value, alignment); } const { firstBandTime, intervalMs } = linearParams; const target = value.valueOf(); const rawIndex = (target - firstBandTime) / intervalMs; let index = alignment === 1 /* Trailing */ ? Math.ceil(rawIndex) : Math.floor(rawIndex); index = Math.max(0, Math.min(index, n - 1)); if (this.isLinearUnit()) { if (alignment === 1 /* Trailing */) { const bandTime = firstBandTime + index * intervalMs; if (bandTime < target && index === n - 1) return void 0; } else { const bandTime = firstBandTime + index * intervalMs; if (bandTime > target && index === 0) return void 0; } return index; } const numericBands = this.numericBands; if (alignment === 1 /* Trailing */) { while (index > 0 && numericBands[index - 1] >= target) index--; while (index < n - 1 && numericBands[index] < target) index++; if (numericBands[index] < target) return void 0; } else { while (index < n - 1 && numericBands[index + 1] <= target) index++; while (index > 0 && numericBands[index] > target) index--; if (numericBands[index] > target) return void 0; } return index; } /** * Optimized convert for UnitTimeScale with O(1) boundary checks. * Uses linear params for fast bounds checking while delegating actual * conversion to parent for accuracy in edge cases. */ convert(value, options) { this.refresh(); if (!(value instanceof Date)) value = new Date(value); const { domain, interval } = this; if (domain.length < 2) return Number.NaN; if (options?.clamp !== true && interval != null) { const boundaries = this.getDomainBoundaries(); if (boundaries != null) { const t = value.valueOf(); if (t < boundaries.d0 || t >= boundaries.dNext) return Number.NaN; } } return super.convert(value, options); } calculateBands(domain, visibleRange, extend = false) { if (domain === this.domain && visibleRange[0] === 0 && visibleRange[1] === 1 && !extend && this._bands != null) { return { bands: this._bands, firstBandIndex: 0 }; } if (domain.length < 2) return { bands: [], firstBandIndex: void 0 }; const { interval } = this; if (interval == null) return { bands: [], firstBandIndex: void 0 }; const rangeParams = { visibleRange, extend }; if (!supportsInterval(domain, interval, rangeParams)) return { bands: [], firstBandIndex: void 0 }; const bandRange = domain === this.domain ? this.getCachedBandRange() : calculateBandRange(domain, interval); if (bandRange == null) return { bands: [], firstBandIndex: void 0 }; const [start2, stop] = bandRange; if (intervalRangeCount(interval, start2, stop, rangeParams) > MAX_BANDS) { logger_exports.warnOnce(`the configured unit results in too many bands, ignoring. Supply a larger unit.`); return { bands: [], firstBandIndex: void 0 }; } const bands = intervalRange(interval, start2, stop, rangeParams); const firstBandIndex = intervalRangeStartIndex(interval, start2, stop, rangeParams); return { bands, firstBandIndex }; } ticks({ interval }, domain = this.domain, visibleRange = [0, 1], { extend = false } = {}) { if (domain.length < 2) return; let bands; let firstBandIndex; let bandsSliceIndices; if (domain === this.domain && !extend) { ({ bands } = this.calculateBands(domain, [0, 1], false)); bandsSliceIndices = visibleTickSliceIndices(bands, false, visibleRange); firstBandIndex = bandsSliceIndices[0]; } else { ({ bands, firstBandIndex } = this.calculateBands(domain, visibleRange, extend)); } if (interval == null) { return { ticks: bands, count: void 0, firstTickIndex: firstBandIndex }; } const milliseconds = this.interval ? intervalMilliseconds(this.interval) : Infinity; const d0 = Math.min(domain[0].valueOf(), domain[1].valueOf()); const d1 = Math.max(domain[0].valueOf(), domain[1].valueOf()); let intervalTicks; let intervalStartIndex; let intervalEndIndex; if (isPlainObject(interval) || typeof interval === "string") { intervalTicks = intervalRange(interval, domain[0], domain[1], { extend: true, visibleRange }); intervalStartIndex = 0; intervalEndIndex = intervalTicks.length - 1; } else { const i0 = bandsSliceIndices ? bandsSliceIndices[0] : 0; const i1 = bandsSliceIndices ? bandsSliceIndices[1] : bands.length - 1; intervalTicks = bands; intervalStartIndex = findMaxIndex(i0, i1, (index) => bands[index].valueOf() <= d0) ?? i0; intervalEndIndex = findMaxIndex(i0, i1, (index) => bands[index].valueOf() <= d1) ?? i1; } const ticks = []; let lastIndex; for (let i = intervalStartIndex; i <= intervalEndIndex; i++) { const intervalTickValue = intervalTicks[i].valueOf(); const bandIndex = findMaxIndex(0, bands.length - 1, (index) => bands[index].valueOf() <= intervalTickValue); const tick = bandIndex != null && bandIndex != lastIndex ? bands[bandIndex] : void 0; lastIndex = bandIndex; if (tick != null && intervalTickValue - tick.getTime() <= milliseconds) ticks.push(tick); } let bandStart; let bandEnd; if (this.interval) { const bandRange = calculateBandRange([new Date(d0), new Date(d1)], this.interval); bandStart = bandRange[0].valueOf(); bandEnd = bandRange[1].valueOf(); } else { bandStart = d0; bandEnd = d1; } let firstTickIndex = findMinIndex(0, ticks.length - 1, (i) => ticks[i].valueOf() >= bandStart) ?? 0; let lastTickIndex = findMaxIndex(0, ticks.length - 1, (i) => ticks[i].valueOf() <= bandEnd) ?? ticks.length - 1; if (extend) { firstTickIndex = Math.max(firstTickIndex - 1, 0); lastTickIndex = Math.min(lastTickIndex + 1, ticks.length - 1); } return { ticks: ticks.slice(firstTickIndex, lastTickIndex + 1), count: ticks.length, firstTickIndex: firstBandIndex }; } }; function supportsInterval(domain, interval, rangeParams) { const [start2, stop] = calculateBandRange(domain, interval); return intervalRangeCount(interval, start2, stop, rangeParams) <= MAX_BANDS; } function calculateBandRange(domain, interval) { const start2 = intervalFloor(interval, domain[0]); const stop = intervalFloor(interval, domain[1]); return [start2, stop]; } // packages/ag-charts-community/src/util/secondaryAxisTicks.ts function calculateNiceSecondaryAxis(scale2, domain, primaryTickCount, reverse, visibleRange) { let [d0, d1] = findMinMax(domain.map(Number)); const unzoomedTickCount = Math.floor(primaryTickCount.unzoomed); if (unzoomedTickCount <= 1) { const [start3, stop2] = domainWithOddTickCount(d0, d1); const tickCount = 5 * Math.pow(2, -Math.ceil(Math.log2(visibleRange[1] - visibleRange[0]))); const { ticks: ticks2 } = createTicks(start3, stop2, tickCount, void 0, void 0, visibleRange); const d2 = [scale2.toDomain(start3), scale2.toDomain(stop2)]; if (reverse) d2.reverse(); return { domain: d2, ticks: ticks2 }; } if (d0 === d1) { const order = Math.floor(Math.log10(d0)); const magnitude = Math.pow(10, order); const rangeOffsetStep = Math.min(magnitude, 1); const rangeOffset = unzoomedTickCount - 1; d0 -= rangeOffsetStep * Math.floor(rangeOffset / 2); d1 = d0 + rangeOffsetStep * rangeOffset; } let start2 = d0; let stop = d1; start2 = calculateNiceStart(start2, stop, unzoomedTickCount); const baseStep = getTickStep(start2, stop, unzoomedTickCount); const segments = unzoomedTickCount - 1; stop = start2 + segments * baseStep; const stepAlignedStart = Math.floor(start2 / baseStep) * baseStep; const stepAlignedStop = Math.floor(stop / baseStep) * baseStep; if (stepAlignedStart <= d0 && stepAlignedStop >= d1) { start2 = stepAlignedStart; stop = stepAlignedStop; } const d = [scale2.toDomain(start2), scale2.toDomain(stop)]; if (reverse) d.reverse(); const step = baseStep * ((primaryTickCount.unzoomed - 1) / (primaryTickCount.zoomed - 1)); const ticks = getTicks(start2, step, Math.floor(primaryTickCount.zoomed)); return { domain: d, ticks }; } function domainWithOddTickCount(d0, d1) { let start2 = d0; let stop = d1; let iterations = 0; do { [start2, stop] = niceTicksDomain(start2, stop); const { ticks } = createTicks(start2, stop, 5); if (ticks.length % 2 === 1) return [start2, stop]; start2 -= 1; stop += 1; } while (iterations++ < 10); return [d0, d1]; } function calculateNiceStart(a, b, count) { a = Math.floor(a); const rawStep = Math.abs(b - a) / (count - 1); const order = Math.floor(Math.log10(rawStep)); const magnitude = Math.pow(10, order); return Math.floor(a / magnitude) * magnitude; } function getTicks(start2, step, count) { const fractionDigits = countFractionDigits(step); const f = Math.pow(10, fractionDigits); const ticks = []; for (let i = 0; i < count; i++) { const tick = start2 + step * i; ticks.push(Math.round(tick * f) / f); } return ticks; } function getTickStep(start2, stop, count) { const segments = count - 1; const rawStep = (stop - start2) / segments; return calculateNextNiceStep(rawStep); } function calculateNextNiceStep(rawStep) { const order = Math.floor(Math.log10(rawStep)); const magnitude = Math.pow(10, order); const step = rawStep / magnitude; if (step > 0 && step <= 1) return magnitude; if (step > 1 && step <= 2) return 2 * magnitude; if (step > 2 && step <= 5) return 5 * magnitude; if (step > 5 && step <= 10) return 10 * magnitude; return rawStep; } // packages/ag-charts-community/src/chart/axis/generateTicksUtils.ts var DENSE_TICK_COUNT = 18; var TICK_STEP_VALUES = [1, 2, 3, 4, 6, 8, 9, 10, 12]; function axisLabelsOverlap(data, padding2 = 0) { const result = []; for (const datum of data) { const { x, y, width: width2, height: height2 } = datum; if (result.some((l) => boxCollides(l, x, y, width2 + padding2, height2 + padding2))) { return true; } result.push(datum); } return false; } function createTimeScaleTicks(interval, domain, visibleRange, extend) { if (interval == null) { return domain; } if (typeof interval !== "number") { const epoch = domain[0]; const alignedInterval = typeof interval === "string" ? { unit: interval, epoch } : { ...interval, epoch }; return intervalRange(alignedInterval, domain[0], domain[1], { visibleRange, extend }); } const ticks = []; const d0 = domain[0].valueOf(); const d1 = domain[1].valueOf(); for (let intervalTickTime = d0; intervalTickTime <= d1; intervalTickTime += interval) { ticks.push(new Date(intervalTickTime)); } return ticks; } function ticksEqual(a, b) { if (a.length !== b.length) { return false; } for (let i = 0; i < a.length; i += 1) { if (a[i]?.valueOf() !== b[i]?.valueOf()) { return false; } } return true; } function ticksSpacing(ticks) { if (ticks.length < 2) return Infinity; let spacing = 0; let y0 = ticks[0].translation; for (let i = 1; i < ticks.length; i++) { const y1 = ticks[i].translation; const delta3 = Math.abs(y1 - y0); spacing = Math.max(spacing, delta3); y0 = y1; } return spacing; } function formatTicks(options, { niceDomain, rawTicks, rawFirstTickIndex = 0, generatePrimaryTicks, primaryTicksIndices, alignment, fractionDigits, timeInterval: timeInterval3 }) { const { scale: scale2, label, tickFormatter, inRange: inRange2, isVertical, sizeLimit = Infinity } = options; const isContinuous2 = TimeScale.is(scale2) || DiscreteTimeScale.is(scale2); const measurer = cachedTextMeasurer(label); const idGenerator = createIdsGenerator(); const ticks = []; withTemporaryDomain(scale2, niceDomain, () => { const maxBandwidth = BandScale.is(scale2) ? scale2.bandwidth ?? Infinity : Infinity; const halfBandwidth = (scale2.bandwidth ?? 0) / 2; const axisFormatter = axisTickFormatter( label.enabled, generatePrimaryTicks, niceDomain, rawTicks, fractionDigits, timeInterval3, tickFormatter ); let maxWidth = isVertical ? sizeLimit : maxBandwidth; let maxHeight = isVertical ? maxBandwidth : sizeLimit; if (label.rotation) { const innerRect = getMaxInnerRectSize(label.rotation, maxWidth, maxHeight); maxWidth = innerRect.width; maxHeight = innerRect.height; } const wrapOptions = { font: label, maxWidth, maxHeight, overflow: label.truncate ? "ellipsis" : "hide", textWrap: label.wrapping }; for (let i = 0; i < rawTicks.length; i++) { const tick = rawTicks[i]; const translation = scale2.convert(tick, { alignment }) + halfBandwidth; if (inRange2 && !inRange2(translation)) continue; const isPrimary = primaryTicksIndices?.has(i) ?? false; const inputText = axisFormatter(isPrimary, tick, i); let wrappedLabel = null; if (label.avoidCollisions) { wrappedLabel = wrapTextOrSegments(inputText, wrapOptions) || null; } const tickLabel = wrappedLabel ?? inputText; const isSegmented = isArray(tickLabel); const isTruncated2 = tickLabel !== inputText && (isSegmented ? isSegmentTruncated(tickLabel.at(-1)) : isTextTruncated(toTextString(tickLabel))); let tickId; if (isContinuous2) { const tickValue = tick?.valueOf(); if (Number.isFinite(tickValue)) { tickId = idGenerator(`v:${tickValue}`); } } tickId ?? (tickId = idGenerator(`l:${isSegmented ? toPlainText(tickLabel.flat()) : tickLabel}`)); ticks.push({ tick, tickId, tickLabel, isPrimary, index: i + rawFirstTickIndex, textUntruncated: isTruncated2 ? toPlainText(inputText) : void 0, textMetrics: isSegmented ? measureTextSegments(tickLabel, label) : measurer.measureLines(toTextString(tickLabel)), translation: Math.floor(translation) }); } }); return ticks; } function withTemporaryDomain(scale2, temporaryDomain, callback2) { const originalDomain = scale2.domain; try { scale2.domain = temporaryDomain; callback2(); } finally { scale2.domain = originalDomain; } } function axisTickFormatter(labelEnabled, generatePrimaryTicks, niceDomain, rawTicks, fractionDigits, timeInterval3, tickFormatter) { const dateStyle = generatePrimaryTicks ? "component" : "long"; const parentInterval = generatePrimaryTicks && timeInterval3 ? intervalHierarchy(timeInterval3) : void 0; const primaryFormatter = generatePrimaryTicks ? tickFormatter(niceDomain, rawTicks, true, fractionDigits, parentInterval, dateStyle) : null; const defaultFormatter = labelEnabled ? tickFormatter(niceDomain, rawTicks, false, fractionDigits, timeInterval3, dateStyle) : null; return (isPrimary, tick, index) => { const formatter2 = isPrimary ? primaryFormatter : defaultFormatter; return formatter2?.(tick, index) ?? String(tick); }; } function getTimeIntervalTicks(scale2, visibleRange, tickCount, maxTickCount, tickParams, timeInterval3, reverse, minimumTimeGranularity) { if (!TimeScale.is(scale2) && !DiscreteTimeScale.is(scale2)) return; const parentInterval = intervalHierarchy(timeInterval3); if (parentInterval == null) return; if (reverse) { visibleRange = [1 - visibleRange[1], 1 - visibleRange[0]]; } const dv0 = Math.min(scale2.domain[0].valueOf(), scale2.domain.at(-1).valueOf()); const dv1 = Math.max(scale2.domain[0].valueOf(), scale2.domain.at(-1).valueOf()); let [dp0, dp1] = intervalExtent(new Date(dv0), new Date(dv1), visibleRange); dp0 = intervalFloor(parentInterval, dp0); if (dp0.valueOf() >= dv0) { dp0 = intervalPrevious(parentInterval, dp0); } dp1 = intervalCeil(parentInterval, dp1); if (dp1.valueOf() <= dv1) { dp1 = intervalNext(parentInterval, dp1); } const primaryTicks = intervalRange(parentInterval, dp0, dp1); const milliseconds = intervalMilliseconds(timeInterval3); const skipFirstPrimaryTick = OrdinalTimeScale.is(scale2); const intervalTickParams = { ...tickParams, interval: timeInterval3 }; const ticks = []; let primaryTicksIndices; let parentLevelMode; let alignment; let ordinalTickStep = 0; if (OrdinalTimeScale.is(scale2)) { const timeIntervalGranularity = intervalUnit(timeInterval3); parentLevelMode = minimumTimeGranularity != null && intervalMilliseconds(minimumTimeGranularity) >= intervalMilliseconds(timeIntervalGranularity) ? 2 /* OrdinalTimeStepTicks */ : 3 /* OrdinalTimeScaleTicks */; alignment = 1 /* Trailing */; const tickDensity = tickCount / maxTickCount; const baseTickStep = scale2.bandCount(visibleRange) / (tickDensity * DENSE_TICK_COUNT); ordinalTickStep = TICK_STEP_VALUES.findLast((t) => baseTickStep >= t) ?? 1; } else if (UnitTimeScale.is(scale2) && (scale2.interval == null || intervalMilliseconds(scale2.interval) >= milliseconds)) { parentLevelMode = 1 /* UnitTimeScaleTicks */; } else { parentLevelMode = 0 /* ContinuousTimeScaleTicks */; alignment = 2 /* Interpolate */; } for (let i = 0; i < primaryTicks.length - 1; i++) { const p0 = primaryTicks[i]; const p1 = primaryTicks[i + 1]; const first2 = i === 0; const last = i === primaryTicks.length - 2; const dp = p1.valueOf() - p0.valueOf(); const pVisibleRange = [ Math.max((dv0 - p0.valueOf()) / dp, 0), Math.min((dv1 - p0.valueOf()) / dp, 1) ]; let intervalTicks; switch (parentLevelMode) { case 0 /* ContinuousTimeScaleTicks */: intervalTicks = createTimeScaleTicks(intervalTickParams.interval, [p0, p1], pVisibleRange, true); break; case 1 /* UnitTimeScaleTicks */: case 3 /* OrdinalTimeScaleTicks */: { const scaleTicks = scale2.ticks(intervalTickParams, [p0, p1], pVisibleRange, { extend: true, dropInitial: true }); intervalTicks = scaleTicks?.ticks ?? []; break; } case 2 /* OrdinalTimeStepTicks */: intervalTicks = scale2.stepTicks( ordinalTickStep, [p0, p1], void 0, !last ); break; } dropFirstWhile(intervalTicks, (firstTick2) => firstTick2.valueOf() < p0.valueOf()); if (!last) { dropLastWhile(intervalTicks, (lastTick) => { switch (parentLevelMode) { case 0 /* ContinuousTimeScaleTicks */: case 3 /* OrdinalTimeScaleTicks */: return lastTick.valueOf() + milliseconds > p1.valueOf(); case 1 /* UnitTimeScaleTicks */: case 2 /* OrdinalTimeStepTicks */: return lastTick.valueOf() >= p1.valueOf(); } }); } if (intervalTicks.length === 0) continue; const firstTick = intervalTicks[0]; const firstTickDiff = compareDates(firstTick, p0); const firstPrimary = parentLevelMode === 0 /* ContinuousTimeScaleTicks */ ? firstTickDiff === 0 : firstTickDiff <= milliseconds; if (firstPrimary && (!skipFirstPrimaryTick || !first2)) { primaryTicksIndices ?? (primaryTicksIndices = /* @__PURE__ */ new Set()); primaryTicksIndices.add(ticks.length); } ticks.push(...intervalTicks); } if (primaryTicksIndices?.size === 1 && primaryTicksIndices.has(0)) { primaryTicksIndices = void 0; } return { ticks, primaryTicksIndices, alignment }; } function timeIntervalMaxLabelSize(label, primaryLabel, domain, timeInterval3, textMeasurer) { const specifier = labelSpecifier(label.format, timeInterval3); if (specifier == null) { return { width: 0, height: 0 }; } const labelFormatter = buildDateFormatter(specifier); const hierarchy = timeInterval3 ? intervalHierarchy(timeInterval3) : void 0; const primarySpecifier = labelSpecifier(primaryLabel?.format, hierarchy); const primaryLabelFormatter = primarySpecifier ? buildDateFormatter(primarySpecifier) : labelFormatter; const d0 = new Date(domain[0]); const d1 = new Date(domain.at(-1)); const hierarchyRange = hierarchy ? intervalRange(hierarchy, new Date(domain[0]), new Date(domain.at(-1)), { extend: true }) : void 0; let maxWidth = 0; let maxHeight = 0; if (labelFormatter != null) { const padding2 = expandLabelPadding(label); const xPadding = padding2.left + padding2.right; const yPadding = padding2.top + padding2.bottom; let l0; let l1; if (hierarchyRange != null && hierarchyRange.length > 1) { l0 = hierarchyRange[0]; l1 = hierarchyRange[1]; } else { l0 = d0; l1 = d1; } const labelRange = intervalRange(timeInterval3, l0, l1, { limit: 50 }); for (const date2 of labelRange) { const text = labelFormatter(date2); const { width: width2, height: height2 } = textMeasurer.measureLines(text); maxWidth = Math.max(maxWidth, width2 + xPadding); maxHeight = Math.max(maxHeight, height2 + yPadding); } } if (primaryLabelFormatter != null && hierarchyRange != null) { const padding2 = expandLabelPadding(primaryLabel); const xPadding = padding2.left + padding2.right; const yPadding = padding2.top + padding2.bottom; for (const date2 of hierarchyRange) { const text = primaryLabelFormatter(date2); const { width: width2, height: height2 } = textMeasurer.measureLines(text); maxWidth = Math.max(maxWidth, width2 + xPadding); maxHeight = Math.max(maxHeight, height2 + yPadding); } } return { width: Math.ceil(maxWidth), height: Math.ceil(maxHeight) }; } function getTextBaseline(parallel, labelRotation, sideFlag, parallelFlipFlag) { if (parallel && !labelRotation) { return sideFlag * parallelFlipFlag === -1 ? "top" : "bottom"; } return "middle"; } function getTextAlign(parallel, labelRotation, labelAutoRotation, sideFlag, regularFlipFlag) { const labelRotated = labelRotation > 0 && labelRotation <= Math.PI; const labelAutoRotated = labelAutoRotation > 0 && labelAutoRotation <= Math.PI; const alignFlag = labelRotated || labelAutoRotated ? -1 : 1; if (parallel) { if (labelRotation || labelAutoRotation) { if (sideFlag * alignFlag === -1) { return "end"; } } else { return "center"; } } else if (sideFlag * regularFlipFlag === -1) { return "end"; } return "start"; } function labelSpecifier(format, timeInterval3) { if (format == null) return; if (typeof format === "string") { return format; } else if (isPlainObject(format) && timeInterval3 != null) { return format[intervalUnit(timeInterval3)]; } } function calculateLabelRotation(rotation, parallel, axisRotation = 0) { const configuredRotation = normalizeAngle360FromDegrees(rotation); const parallelFlipFlag = !configuredRotation && axisRotation >= 0 && axisRotation <= Math.PI ? -1 : 1; const regularFlipFlag = !configuredRotation && axisRotation - Math.PI / 2 >= 0 && axisRotation - Math.PI / 2 <= Math.PI ? -1 : 1; const defaultRotation = parallel ? parallelFlipFlag * (Math.PI / 2) : 0; return { configuredRotation, defaultRotation, parallelFlipFlag, regularFlipFlag }; } // packages/ag-charts-community/src/chart/axis/generateTicks.ts var sunday2 = new Date(1970, 0, 4); function generateTicks(options) { const { label, domain, axisRotation, labelOffset, sideFlag } = options; const { defaultRotation, configuredRotation, parallelFlipFlag, regularFlipFlag } = calculateLabelRotation( label.rotation, label.parallel, axisRotation ); const initialRotation = configuredRotation + defaultRotation; const checkLabelOverlap = (tickData2, rotation2 = 0) => { const labelSpacing = label.minSpacing ?? (configuredRotation === 0 && rotation2 === 0 ? 10 : 0); const labelRotation = initialRotation + rotation2; const labelPadding = expandLabelPadding(label); return axisLabelsOverlap(createTimeLabelData(options, tickData2, labelRotation), labelSpacing) || axisLabelsOverlap(createLabelData(tickData2.ticks, labelOffset, labelRotation, labelPadding), labelSpacing); }; const { maxTickCount } = estimateScaleTickCount(options); const tickGenerationType = getTickGenerationType(options); const avoidCollisions = label.enabled && label.avoidCollisions; const maxIterations = Number.isFinite(maxTickCount) ? maxTickCount : 10; const tryAutoRotate = avoidCollisions && label.autoRotate && label.rotation == null; let index = 0; let autoRotation = 0; let labelOverlap = true; let tickData = { tickDomain: [], niceDomain: domain, ticks: [], rawTicks: [], rawTickCount: void 0, timeInterval: void 0, fractionDigits: 0 }; while (labelOverlap && index <= maxIterations) { ({ tickData, index } = buildTickData(options, tickGenerationType, tickData, index)); autoRotation = tryAutoRotate && checkLabelOverlap(tickData, 0) ? normalizeAngle360FromDegrees(label.autoRotateAngle) : 0; labelOverlap = avoidCollisions && checkLabelOverlap(tickData, autoRotation); } const textAlign = getTextAlign(label.parallel, configuredRotation, autoRotation, sideFlag, regularFlipFlag); const textBaseline = getTextBaseline(label.parallel, configuredRotation, sideFlag, parallelFlipFlag); const rotation = configuredRotation + autoRotation; return { tickData, textAlign, textBaseline, rotation }; } function getTickGenerationType(options) { if (options.interval?.values) { return 2 /* VALUES */; } else if (options.primaryTickCount != null) { return 1 /* CREATE_SECONDARY */; } return 0 /* CREATE */; } function estimateScaleTickCount({ scale: scale2, domain, range: range3, visibleRange, label, defaultTickMinSpacing, interval: { minSpacing, maxSpacing } }) { const { defaultTickCount } = scale2; const rangeExtent = findRangeExtent(range3); const zoomExtent = findRangeExtent(visibleRange); if (CategoryScale.is(scale2) || OrdinalTimeScale.is(scale2) && domain.length < 1e3) { const maxTickCount = CategoryScale.is(scale2) ? domain.length : Math.min(domain.length, Math.max(1, Math.floor(rangeExtent / (zoomExtent * defaultTickMinSpacing)))); const estimatedTickCount = Math.ceil(rangeExtent / (zoomExtent * label.fontSize)); return { tickCount: Math.min(estimatedTickCount, maxTickCount), minTickCount: 0, maxTickCount }; } return estimateTickCount(rangeExtent, zoomExtent, minSpacing, maxSpacing, defaultTickCount, defaultTickMinSpacing); } function buildTickData(options, tickGenerationType, previousTickData, index) { const { step, values } = options.interval; const { maxTickCount, minTickCount, tickCount } = estimateScaleTickCount(options); const countTicks2 = (i) => Math.max(tickCount - i, minTickCount); const regenerateTicks = step == null && values == null && countTicks2(index) > minTickCount; const previousTicks = previousTickData.rawTicks; const maxIterations = tickCount - minTickCount; const countParams = { minTickCount, maxTickCount, tickCount: countTicks2(index) }; let nextTicks = calculateRawTicks(options, tickGenerationType, countParams); if (regenerateTicks && ticksEqual(nextTicks.rawTicks, previousTicks)) { let lowerBound = index; let upperBound = maxIterations; while (lowerBound <= upperBound) { index = Math.trunc((lowerBound + upperBound) / 2); countParams.tickCount = countTicks2(index); const nextTicksCandidate = calculateRawTicks(options, tickGenerationType, countParams); if (ticksEqual(nextTicksCandidate.rawTicks, previousTicks)) { lowerBound = index + 1; } else { nextTicks = nextTicksCandidate; upperBound = index - 1; } } } const { tickDomain, niceDomain, rawTicks, rawTickCount, rawFirstTickIndex, generatePrimaryTicks, primaryTicksIndices, alignment, fractionDigits, timeInterval: timeInterval3 } = nextTicks; return { tickData: { tickDomain, niceDomain, rawTicks, rawTickCount, fractionDigits, timeInterval: timeInterval3, ticks: formatTicks(options, { niceDomain, rawTicks, rawFirstTickIndex, generatePrimaryTicks, primaryTicksIndices, alignment, fractionDigits, timeInterval: timeInterval3 }) }, index: index + 1 }; } function calculateRawTicks(options, tickGenerationType, countParams) { const { domain, reverse, visibleRange, scale: scale2, interval, primaryLabel, niceMode, primaryTickCount, minimumTimeGranularity } = options; const domainParams = { nice: niceMode.map((n) => n === 0 /* TickAndDomain */), interval: interval.step, ...countParams }; const tickParams = { ...domainParams, nice: niceMode.map((n) => n === 0 /* TickAndDomain */ || n === 1 /* TicksOnly */) }; let secondaryAxisTicks; if (tickGenerationType === 1 /* CREATE_SECONDARY */ && primaryTickCount != null && ContinuousScale.is(scale2)) { secondaryAxisTicks = calculateNiceSecondaryAxis(scale2, domain, primaryTickCount, reverse, visibleRange); } const niceDomain = niceMode.includes(0 /* TickAndDomain */) ? secondaryAxisTicks?.domain ?? scale2.niceDomain(domainParams, domain) : domain; let tickDomain = niceDomain; let rawTicks; let rawTickCount; let rawFirstTickIndex; let timeInterval3; let primaryTicksIndices; let alignment; const generatePrimaryTicks = primaryLabel?.enabled === true && tickParams.interval == null; withTemporaryDomain(scale2, niceDomain, () => { switch (tickGenerationType) { case 2 /* VALUES */: tickDomain = interval.values; rawTicks = interval.values; rawTickCount = rawTicks.length; if (OrdinalTimeScale.is(scale2)) { alignment = 1 /* Trailing */; } else if (UnitTimeScale.is(scale2)) { alignment = 2 /* Interpolate */; } if (ContinuousScale.is(scale2)) { const [d0, d1] = findMinMax(niceDomain.map(Number)); rawTicks = rawTicks.filter((value) => Number(value) >= d0 && Number(value) <= d1).sort((a, b) => Number(a) - Number(b)); } break; case 1 /* CREATE_SECONDARY */: if (secondaryAxisTicks) { rawTicks = secondaryAxisTicks.ticks; rawTickCount = secondaryAxisTicks.ticks.length; } else { const tickGeneration = scale2.ticks(tickParams, niceDomain, visibleRange); rawTicks = tickGeneration?.ticks; rawTickCount = tickGeneration?.count; } break; default: { const { tickCount, minTickCount, maxTickCount } = countParams; if (niceDomain.length > 0 && tickParams.interval == null && (UnitTimeScale.is(scale2) || generatePrimaryTicks && (TimeScale.is(scale2) || OrdinalTimeScale.is(scale2)))) { const dates = niceDomain; const start2 = Math.min(dates[0].valueOf(), dates.at(-1).valueOf()); const end3 = Math.max(dates[0].valueOf(), dates.at(-1).valueOf()); timeInterval3 = getTickTimeInterval(start2, end3, tickCount, minTickCount, maxTickCount, { weekStart: primaryLabel == null ? sunday2 : void 0, primaryOnly: true }); } let minTimeInterval; if (OrdinalTimeScale.is(scale2)) { minTimeInterval = minimumTimeGranularity; } else if (UnitTimeScale.is(scale2)) { minTimeInterval = scale2.interval; } if (minTimeInterval != null && timeInterval3 != null && // Prefer UnitTimeAxis.unit over this interval, because the user may have defined an epoch intervalMilliseconds(minTimeInterval) >= intervalMilliseconds(timeInterval3)) { timeInterval3 = minTimeInterval; } const intervalTicks = timeInterval3 ? getTimeIntervalTicks( scale2, visibleRange, tickCount, maxTickCount, tickParams, timeInterval3, reverse, minimumTimeGranularity ) : void 0; if (intervalTicks) { ({ ticks: rawTicks, primaryTicksIndices, alignment } = intervalTicks); } else { const intervalTickParams = UnitTimeScale.is(scale2) && tickParams.interval == null && timeInterval3 != null ? { ...tickParams, interval: timeInterval3 } : tickParams; const tickGeneration = scale2.ticks(intervalTickParams, niceDomain, visibleRange); rawTicks = tickGeneration?.ticks; rawTickCount = tickGeneration?.count; rawFirstTickIndex = tickGeneration?.firstTickIndex; if (TimeScale.is(scale2) || DiscreteTimeScale.is(scale2)) { const paramsInterval = typeof tickParams.interval === "number" ? lowestGranularityForInterval(tickParams.interval) : tickParams.interval; timeInterval3 ?? (timeInterval3 = paramsInterval ?? tickGeneration?.timeInterval); } } } } }); rawTicks ?? (rawTicks = []); let fractionDigits = 0; for (const tick of rawTicks) { if (typeof tick !== "number") continue; const value = countFractionDigits(tick); if (value > fractionDigits) { fractionDigits = value; } } if (!generatePrimaryTicks) { primaryTicksIndices = void 0; } return { tickDomain, niceDomain, rawTicks, rawTickCount, rawFirstTickIndex, generatePrimaryTicks, primaryTicksIndices, alignment, fractionDigits, timeInterval: timeInterval3 }; } function createTimeLabelData(options, tickData, labelRotation) { const { niceDomain, ticks, timeInterval: timeInterval3 } = tickData; if (timeInterval3 == null) return []; const spacing = ticksSpacing(ticks); const { label, labelOffset, primaryLabel, domain } = options; const { width: width2, height: height2 } = timeIntervalMaxLabelSize( label, primaryLabel, niceDomain ?? domain, timeInterval3, cachedTextMeasurer(label) ); const labelData = []; for (const translation of [0, spacing]) { const { x, y } = rotatePoint(labelOffset, translation, labelRotation); labelData.push({ x, y, width: width2, height: height2 }); } return labelData; } function createLabelData(tickData, labelOffset, labelRotation, labelPadding) { const labelData = []; const xPadding = labelPadding.left + labelPadding.right; const yPadding = labelPadding.top + labelPadding.bottom; for (const { tickLabel, textMetrics, translation } of tickData) { if (!tickLabel) continue; const { x, y } = rotatePoint(labelOffset, translation, labelRotation); const width2 = textMetrics.width + xPadding; const height2 = textMetrics.height + yPadding; labelData.push({ x, y, width: width2, height: height2 }); } return labelData; } // packages/ag-charts-community/src/chart/axis/cartesianAxis.ts var _CartesianAxis = class _CartesianAxis extends Axis { constructor(moduleCtx, scale2) { super(moduleCtx, scale2); this.maxThicknessRatio = 0.3; this.crossAxisTranslation = { x: 0, y: 0 }; this.minimumTimeGranularity = void 0; this.headingLabelGroup = this.axisGroup.appendChild( new TranslatableGroup({ name: `${this.id}-Axis-heading` }) ); this.lineNodeGroup = this.axisGroup.appendChild( new TranslatableGroup({ name: `${this.id}-Axis-line` }) ); this.lineNode = this.lineNodeGroup.appendChild(new Line({ zIndex: 1 /* AxisLine */ })); this.tickLineGroupSelection = Selection.select(this.tickLineGroup, Line, false); this.gridLineGroupSelection = Selection.select(this.gridLineGroup, Line, false); this.gridFillGroupSelection = Selection.select(this.gridFillGroup, Rect, false); this.tempText = new TransformableText({ debugDirty: false }); this.tempCaption = new Caption(); this.animationManager = moduleCtx.animationManager; this.animationState = new StateMachine("empty", { empty: { update: { target: "ready", action: () => this.resetSelectionNodes() }, reset: "empty" }, ready: { update: (data) => this.animateReadyUpdate(data), resize: () => this.resetSelectionNodes(), reset: "empty" } }); this.headingLabelGroup.appendChild(this.title.caption.node); let previousSize = void 0; this.cleanup.register( moduleCtx.eventsHub.on("layout:complete", (e) => { const size = [e.chart.width, e.chart.height]; if (previousSize != null && !arraysEqual(size, previousSize)) { this.animationState.transition("resize"); } previousSize = size; }), this.title.caption.registerInteraction(this.moduleCtx, "afterend") ); } static is(value) { return value instanceof _CartesianAxis; } get horizontal() { return this.position === "top" || this.position === "bottom"; } onGridVisibilityChange() { } resetAnimation(phase) { if (phase === "initial") { this.animationState.transition("reset"); } } get direction() { return this.position === "top" || this.position === "bottom" ? "x" /* X */ : "y" /* Y */; } createAxisContext() { return { ...super.createAxisContext(), position: this.position }; } createLabel() { return new CartesianAxisLabel(); } updateDirection() { switch (this.position) { case "top": this.label.mirrored = true; this.label.parallel = true; break; case "right": this.label.mirrored = true; this.label.parallel = false; break; case "bottom": this.label.mirrored = false; this.label.parallel = true; break; case "left": this.label.mirrored = false; this.label.parallel = false; break; } if (this.axisContext) { this.axisContext.position = this.position; this.axisContext.direction = this.direction; } } calculateLayout(primaryTickCount, chartLayout) { this.updateDirection(); return super.calculateLayout(primaryTickCount, chartLayout); } layoutCrossLines() { const crosslinesVisible = this.hasDefinedDomain() || this.hasVisibleSeries(); for (const crossLine of this.crossLines) { crossLine.calculateLayout?.(crosslinesVisible); } } calculateTickLayout(domain, niceMode, visibleRange, initialPrimaryTickCount) { const sideFlag = this.label.getSideFlag(); const labelX = sideFlag * (this.getTickSize() + this.getTickSpacing() + this.label.spacing + this.seriesAreaPadding); const scrollbar = this.chartLayout?.scrollbars?.[this.id]; const scrollbarThickness = this.getScrollbarThickness(scrollbar); if (niceMode[0] === 2 /* Off */ && niceMode[1] === 2 /* Off */ && this.label.enabled === false && this.tick.enabled === false && this.gridLine.enabled === false) { const { bbox: bbox2, spacing: spacing2 } = this.measureAxisLayout(domain, [], [], scrollbar, scrollbarThickness); const layout2 = { ticks: [], tickLines: [], gridLines: [], gridFills: [], labels: [], spacing: spacing2 }; return { ticks: [], rawTickCount: 0, tickDomain: domain, niceDomain: domain, fractionDigits: 0, timeInterval: void 0, bbox: bbox2, layout: layout2 }; } const { label, primaryLabel, scale: scale2, range: range3, interval, reverse, defaultTickMinSpacing, minimumTimeGranularity } = this; const tickGenerationResult = generateTicks({ label, scale: scale2, interval, primaryLabel, domain, range: range3, reverse, niceMode, visibleRange, defaultTickMinSpacing, minimumTimeGranularity, sideFlag, labelOffset: labelX, primaryTickCount: initialPrimaryTickCount, axisRotation: this.horizontal ? Math.PI / -2 : 0, isVertical: this.direction === "y" /* Y */, sizeLimit: this.chartLayout?.sizeLimit, inRange: (translation) => this.inRange(translation, 1e-3), tickFormatter: (...args) => this.tickFormatter(...args) }); const { tickData } = tickGenerationResult; const removeOverflowLabels = this.label.avoidCollisions && this.horizontal && tickData.ticks.length > 2 && (ContinuousScale.is(this.scale) || DiscreteTimeScale.is(this.scale)); if (removeOverflowLabels) { const removeOverflowThreshold = this.chartLayout?.padding.right ?? 0; const lastTick = tickData.ticks.at(-1); if (lastTick?.tickLabel != null && lastTick.translation + lastTick.textMetrics.width / 2 > range3[1] + removeOverflowThreshold) { lastTick.tickLabel = void 0; if (visibleRange[0] === 0 && visibleRange[1] === 1) { tickData.ticks[0].tickLabel = void 0; } } } const { ticks, tickDomain, rawTicks, rawTickCount, fractionDigits, timeInterval: timeInterval3, niceDomain } = tickData; const labels = ticks.map((d) => this.getTickLabelProps(d, tickGenerationResult, scrollbarThickness)); const { position, gridPadding, gridLength } = this; const direction = position === "bottom" || position === "right" ? -1 : 1; const p1 = direction * gridPadding; const p2 = direction * (gridLength + gridPadding); const gridLines = this.calculateGridLines(ticks, p1, p2); const gridFills = this.calculateGridFills(ticks, p1, p2); const tickLines = this.calculateTickLines(ticks, direction, scrollbarThickness); const { bbox, spacing } = this.measureAxisLayout(tickDomain, ticks, labels, scrollbar, scrollbarThickness); const layout = { ticks, gridLines, gridFills, tickLines, labels, spacing }; return { ticks: rawTicks, rawTickCount, tickDomain, niceDomain, fractionDigits, timeInterval: timeInterval3, bbox, layout }; } calculateGridLines(ticks, p1, p2) { return ticks.map((tick, index) => this.calculateGridLine(tick, index, p1, p2, ticks)); } calculateGridLine({ index: tickIndex, tickId, translation: offset }, _index, p1, p2, _ticks) { const { gridLine, horizontal } = this; const [x1, y1, x2, y2] = horizontal ? [offset, p1, offset, p2] : [p1, offset, p2, offset]; const { style: style2 } = gridLine; const { stroke, strokeWidth = 0, lineDash } = style2[tickIndex % style2.length] ?? {}; return { tickId, offset, x1, y1, x2, y2, stroke, strokeWidth, lineDash }; } calculateGridFills(ticks, p1, p2) { const { horizontal, range: range3, type } = this; const gridFills = []; if (ticks.length == 0) return gridFills; let gridFillIndexOffset = 0; const isVerticalUnitTime = !horizontal && type === "unit-time"; const firstFillOffCanvas = isVerticalUnitTime && ticks[0].translation < range3[0] || !isVerticalUnitTime && ticks[0].translation > range3[0]; if (firstFillOffCanvas) { const injectedTick = { tickId: `before:${ticks[0].tickId}`, translation: range3[0] }; gridFills.push(this.calculateGridFill(injectedTick, -1, ticks[0].index, p1, p2, ticks)); gridFillIndexOffset = 1; } gridFills.push( ...ticks.map( (tick, index) => this.calculateGridFill(tick, index, tick.index + gridFillIndexOffset, p1, p2, ticks) ) ); return gridFills; } calculateGridFill({ tickId, translation }, index, gridFillIndex, p1, p2, ticks) { const { gridLine, horizontal, range: range3 } = this; const nextTick = ticks[index + 1]; const startOffset = translation; const endOffset = nextTick ? nextTick.translation : range3[1]; const [x1, y1, x2, y2] = horizontal ? [startOffset, Math.max(p1, p2), endOffset, Math.min(p1, p2)] : [Math.min(p1, p2), Math.min(startOffset, endOffset), Math.max(p1, p2), Math.max(startOffset, endOffset)]; const { fill, fillOpacity } = gridLine.style[gridFillIndex % gridLine.style.length] ?? {}; return { tickId, x1, y1, x2, y2, fill, fillOpacity }; } calculateTickLines(ticks, direction, scrollbarThickness = 0) { return ticks.map((tick) => this.calculateTickLine(tick, tick.index, direction, ticks, scrollbarThickness)); } calculateTickLine({ isPrimary, tickId, translation: offset }, _index, direction, _ticks, scrollbarThickness = 0) { const { horizontal, tick, primaryTick } = this; const datumTick = isPrimary && primaryTick ? primaryTick : tick; const tickSize = this.getTickSize(datumTick); const tickSpacing = this.getTickSpacing(datumTick); const tickOffset = -direction * (scrollbarThickness + tickSpacing); const h = -direction * tickSize; const [x1, y1, x2, y2] = horizontal ? [offset, tickOffset, offset, tickOffset + h] : [tickOffset, offset, tickOffset + h, offset]; const { stroke, width: strokeWidth } = datumTick; const lineDash = void 0; return { tickId, offset, x1, y1, x2, y2, stroke, strokeWidth, lineDash }; } update() { this.updateDirection(); const previousTicksIds = Array.from(this.tickLabelGroupSelection.nodes(), (node) => node.datum.tickId); super.update(); const { tickLayout } = this; this.updateTitle(this.scale.domain, tickLayout?.spacing ?? 0); if (!this.animatable) { this.moduleCtx.animationManager.skipCurrentBatch(); } if (tickLayout) { const { ticks } = tickLayout; if (this.animationManager.isSkipped()) { this.resetSelectionNodes(); } else { const tickIds = ticks.map((datum) => datum.tickId); const diff2 = diffArrays(previousTicksIds, tickIds); this.animationState.transition("update", diff2); } } const { enabled, stroke, width: width2 } = this.line; this.lineNode.setProperties({ stroke, strokeWidth: enabled ? width2 : 0 }); this.updateTickLines(); this.updateGridLines(); this.updateGridFills(); } getAxisTransform() { return { translationX: Math.floor(this.translation.x + this.crossAxisTranslation.x), translationY: Math.floor(this.translation.y + this.crossAxisTranslation.y) }; } getLayoutTranslation() { const { translationX, translationY } = this.getAxisTransform(); return { x: translationX, y: translationY }; } getLayoutState() { const layout = super.getLayoutState(); return { ...layout, position: this.position }; } updatePosition() { super.updatePosition(); const axisTransform = this.getAxisTransform(); this.tickLineGroup.datum = axisTransform; this.tickLabelGroup.datum = axisTransform; this.lineNodeGroup.datum = axisTransform; this.headingLabelGroup.datum = axisTransform; } setAxisVisible(visible) { this.tickLineGroup.visible = visible && (this.tick.enabled || (this.primaryTick?.enabled ?? false)); this.tickLabelGroup.visible = visible && (this.label.enabled || (this.primaryTick?.enabled ?? false)); this.lineNodeGroup.visible = visible; this.headingLabelGroup.visible = visible; } getAxisLineCoordinates() { const { horizontal } = this; const [c1, c2] = findMinMax(this.lineRange ?? this.range); return horizontal ? { x1: c1, x2: c2, y1: 0, y2: 0 } : { x1: 0, x2: 0, y1: c1, y2: c2 }; } getTickLineBBox(datum, scrollbarThickness) { const { translation } = datum; const { position, primaryTick } = this; let tickSize = this.getTickSize(); if (primaryTick?.enabled) { tickSize = Math.max(tickSize, this.getTickSize(primaryTick)); } const direction = position === "bottom" || position === "right" ? -1 : 1; const tickSpacing = this.getTickSpacing(this.tick); const tickOffset = -direction * (scrollbarThickness + tickSpacing); const start2 = tickOffset; const end3 = tickOffset - direction * (tickSize + tickSpacing); const min = Math.min(start2, end3); const max = Math.max(start2, end3); switch (position) { case "top": return new BBox(translation, min, 0, max - min); case "bottom": return new BBox(translation, min, 0, max - min); case "left": return new BBox(min, translation, max - min, 0); case "right": return new BBox(min, translation, max - min, 0); } } lineNodeBBox() { const { position, seriesAreaPadding } = this; const { y1, y2 } = this.getAxisLineCoordinates(); const dy = y2 - y1; switch (position) { case "top": return new BBox(y1, -seriesAreaPadding, dy, seriesAreaPadding); case "bottom": return new BBox(y1, 0, dy, seriesAreaPadding); case "left": return new BBox(-seriesAreaPadding, y1, seriesAreaPadding, dy); case "right": return new BBox(0, y1, seriesAreaPadding, dy); } } titleBBox(domain, spacing) { const { tempCaption } = this; tempCaption.node.setProperties(this.titleProps(tempCaption, domain, spacing)); return tempCaption.node.getBBox(); } getScrollbarThickness(scrollbar) { if (!scrollbar?.enabled) return 0; return scrollbar.placement === "inner" ? scrollbar.spacing + scrollbar.thickness : 0; } resolveScrollbarLayout(scrollbar, labelThickness) { if (!scrollbar) return void 0; const { position } = this; const direction = position === "top" || position === "left" ? -1 : 1; if (scrollbar.placement === "inner") { const offset2 = direction === 1 ? scrollbar.spacing : -scrollbar.spacing - scrollbar.thickness; return { ...scrollbar, offset: offset2 }; } const offset = direction === 1 ? labelThickness + scrollbar.spacing : -labelThickness - scrollbar.spacing - scrollbar.thickness; return { ...scrollbar, offset }; } applyScrollbarLayout(boxes, labelThickness, scrollbar) { const scrollbarLayout = this.resolveScrollbarLayout(scrollbar, labelThickness); let spacing = labelThickness; if (scrollbarLayout) { const { offset, thickness, placement } = scrollbarLayout; if (placement === "outer") { spacing += scrollbarLayout.spacing + thickness; } if (this.horizontal) { boxes.push(new BBox(0, offset, 0, thickness)); } else { boxes.push(new BBox(offset, 0, thickness, 0)); } } return { spacing, scrollbarLayout }; } measureAxisLayout(domain, ticks, labels, scrollbar, scrollbarThickness) { const { tick, primaryTick, label, primaryLabel, title, position, horizontal, seriesAreaPadding } = this; const boxes = []; boxes.push(this.lineNodeBBox()); if (tick.enabled || primaryTick?.enabled) { for (const datum of ticks) { boxes.push(this.getTickLineBBox(datum, scrollbarThickness)); } } const { tempText } = this; if (label.enabled) { for (const datum of labels) { if (!datum.visible) continue; tempText.setProperties(datum); const box = tempText.getBBox(); if (box) { boxes.push(box); } } } if (primaryLabel?.enabled && position === "bottom") { const inexactMeasurementPadding = 2; boxes.push( new BBox( 0, calcLineHeight(label.fontSize) + inexactMeasurementPadding, 1, this.getTickSize(tick) + this.getTickSpacing(tick) + label.spacing + seriesAreaPadding ) ); if (primaryLabel.format != null) { const { format } = primaryLabel; const formats = isPlainObject(format) ? Object.values(format) : [format]; const maxLines = formats.reduce((m, f) => Math.max(m, countLines(f)), 0); boxes.push( new BBox( 0, this.getTickSize(primaryTick ?? tick) + this.getTickSpacing(primaryTick ?? tick) + primaryLabel.spacing + seriesAreaPadding, 1, maxLines * calcLineHeight(primaryLabel.fontSize) + inexactMeasurementPadding ) ); } } const combined = BBox.merge(boxes); const labelThickness = horizontal ? combined.height : combined.width; const { spacing, scrollbarLayout } = this.applyScrollbarLayout(boxes, labelThickness, scrollbar); this.layout.labelThickness = labelThickness; this.layout.scrollbar = scrollbarLayout; if (title.enabled) { boxes.push(this.titleBBox(domain, spacing)); } const bbox = BBox.merge(boxes); return { bbox, spacing }; } titleProps(caption, domain, spacing) { const { title } = this; if (!title.enabled) { caption.enabled = false; return { visible: false, text: "", textBaseline: "bottom", x: 0, y: 0, rotationCenterX: 0, rotationCenterY: 0, rotation: 0 }; } caption.enabled = true; caption.color = title.color; caption.fontFamily = title.fontFamily; caption.fontSize = title.fontSize; caption.fontStyle = title.fontStyle; caption.fontWeight = title.fontWeight; caption.wrapping = title.wrapping; const padding2 = (title.spacing ?? 0) + spacing; const { range: range3 } = this; const midOffset = (range3[0] + range3[1]) / 2; let x; let y; let rotation; let textBaseline; switch (this.position) { case "top": x = midOffset; y = -padding2; rotation = 0; textBaseline = "bottom"; break; case "bottom": x = midOffset; y = padding2; rotation = 0; textBaseline = "top"; break; case "left": x = -padding2; y = midOffset; rotation = Math.PI / -2; textBaseline = "bottom"; break; case "right": x = padding2; y = midOffset; rotation = Math.PI / 2; textBaseline = "bottom"; break; } const { formatter: formatter2 = (p) => p.defaultValue } = title; const text = this.cachedCallWithContext(formatter2, this.getTitleFormatterParams(domain)); caption.text = text; return { visible: true, text, textBaseline, x, y, rotationCenterX: x, rotationCenterY: y, rotation }; } getTickLabelProps(datum, tickGenerationResult, scrollbarThickness) { const { horizontal, primaryLabel, primaryTick, seriesAreaPadding, scale: scale2 } = this; const { tickId, tickLabel: text = "", translation, isPrimary, textUntruncated } = datum; const label = isPrimary && primaryLabel?.enabled ? primaryLabel : this.label; const tick = isPrimary && primaryTick?.enabled ? primaryTick : this.tick; const { rotation, textBaseline, textAlign } = tickGenerationResult; const { range: range3 } = scale2; const sideFlag = this.label.getSideFlag(); const borderOffset = expandLabelPadding(label)[this.position]; let labelOffset = sideFlag * (this.getTickSize(tick) + this.getTickSpacing(tick) + label.spacing + seriesAreaPadding) - borderOffset; if (scrollbarThickness) { labelOffset += sideFlag * scrollbarThickness; } const visible = text !== ""; const x = horizontal ? translation : labelOffset; const y = horizontal ? -labelOffset : translation; return { ...this.getLabelStyles({ value: datum.tick, formattedValue: text }, void 0, label), tickId, rotation, text, textAlign, textBaseline, textUntruncated, visible, x, y, rotationCenterX: x, rotationCenterY: y, range: range3 }; } updateSelections() { if (!this.tickLayout) return; const lineData = this.getAxisLineCoordinates(); const { tickLines, gridLines, gridFills, labels } = this.tickLayout; const getDatumId = (datum) => datum.tickId; this.lineNode.datum = lineData; this.gridLineGroupSelection.update(this.gridLine.enabled ? gridLines : [], void 0, getDatumId); this.gridFillGroupSelection.update(this.gridLine.enabled ? gridFills : [], void 0, getDatumId); this.tickLineGroupSelection.update(tickLines, void 0, getDatumId); this.tickLabelGroupSelection.update(labels, void 0, getDatumId); } updateGridLines() { this.gridLineGroupSelection.each((line, datum) => { line.stroke = datum.stroke; line.strokeWidth = datum.strokeWidth; line.lineDash = datum.lineDash; }); } updateGridFills() { this.gridFillGroupSelection.each((rect2, datum) => { rect2.fill = datum.fill; rect2.fillOpacity = datum.fillOpacity ?? 1; }); } updateTickLines() { this.tickLineGroupSelection.each((line, datum) => { line.stroke = datum.stroke; line.strokeWidth = datum.strokeWidth; line.lineDash = datum.lineDash; }); } updateTitle(domain, spacing) { const { caption } = this.title; const titleProps = this.titleProps(caption, domain, spacing); caption.node.visible = titleProps.visible; caption.node.text = titleProps.text; caption.node.textBaseline = titleProps.textBaseline; caption.node.datum = titleProps; } updateLabels() { if (!this.label.enabled) return; this.tickLabelGroupSelection.each((node, datum) => { node.fill = datum.color; node.text = datum.text; node.textBaseline = datum.textBaseline; node.textAlign = datum.textAlign ?? "center"; node.pointerEvents = datum.textUntruncated == null ? 1 /* None */ : 0 /* All */; node.setFont(datum); node.setBoxing(datum); }); } animateReadyUpdate(diff2) { const { animationManager } = this.moduleCtx; const selectionCtx = prepareAxisAnimationContext(this); const fns = prepareAxisAnimationFunctions(selectionCtx); fromToMotion( this.id, "axis-group", animationManager, [this.lineNodeGroup, this.tickLabelGroup, this.tickLineGroup, this.headingLabelGroup], fns.group ); fromToMotion(this.id, "line", animationManager, [this.lineNode], fns.line); fromToMotion( this.id, "line-paths", animationManager, [this.gridLineGroupSelection, this.tickLineGroupSelection], fns.tick, (_, d) => d.tickId, diff2 ); fromToMotion( this.id, "tick-labels", animationManager, [this.tickLabelGroupSelection], fns.label, (_, d) => d.tickId, diff2 ); fromToMotion( this.id, "title", animationManager, [this.title.caption.node], fns.label, (_, d) => d.tickId, diff2 ); } resetSelectionNodes() { resetMotion( [this.lineNodeGroup, this.tickLabelGroup, this.tickLineGroup, this.headingLabelGroup], resetAxisGroupFn() ); resetMotion([this.gridLineGroupSelection, this.tickLineGroupSelection], resetAxisLineSelectionFn()); resetMotion([this.gridFillGroupSelection], resetAxisFillSelectionFn()); resetMotion([this.tickLabelGroupSelection], resetAxisLabelSelectionFn()); resetMotion([this.title.caption.node], resetAxisLabelSelectionFn()); resetMotion([this.lineNode], resetAxisLineSelectionFn()); } }; __decorateClass([ addFakeTransformToInstanceProperty ], _CartesianAxis.prototype, "thickness", 2); __decorateClass([ addFakeTransformToInstanceProperty ], _CartesianAxis.prototype, "maxThicknessRatio", 2); __decorateClass([ addFakeTransformToInstanceProperty ], _CartesianAxis.prototype, "position", 2); __decorateClass([ addFakeTransformToInstanceProperty ], _CartesianAxis.prototype, "crossAt", 2); var CartesianAxis = _CartesianAxis; // packages/ag-charts-community/src/chart/axis/categoryAxis.ts var _CategoryAxis = class _CategoryAxis extends CartesianAxis { constructor(moduleCtx, scale2 = new CategoryScale(), includeInvisibleDomains = true) { super(moduleCtx, scale2); this.groupPaddingInner = 0.1; this.includeInvisibleDomains = includeInvisibleDomains; this.nice = false; } static is(value) { return value instanceof _CategoryAxis; } isCategoryLike() { return true; } hasDefinedDomain() { return false; } normaliseDataDomain(d) { return { domain: d.domain, clipped: false }; } getUpdateTypeOnResize() { if (this.bandAlignment == null || this.bandAlignment === "justify") { return super.getUpdateTypeOnResize(); } return 3 /* PROCESS_DOMAIN */; } updateScale() { super.updateScale(); let { paddingInner, paddingOuter } = this; if (!isFiniteNumber(paddingInner) || !isFiniteNumber(paddingOuter)) { const padding2 = this.reduceBandScalePadding(); paddingInner ?? (paddingInner = padding2.inner); paddingOuter ?? (paddingOuter = padding2.outer); } this.scale.paddingInner = paddingInner ?? 0; this.scale.paddingOuter = paddingOuter ?? 0; } calculateGridLines(ticks, p1, p2) { const gridLines = super.calculateGridLines(ticks, p1, p2); if (this.interval.placement === "between" && ticks.length > 0) { gridLines.push( super.calculateGridLine( { index: ticks.at(-1).index + 1, tickId: `after:${ticks.at(-1).tickId}`, translation: this.range[1] }, ticks.length, p1, p2, ticks ) ); } return gridLines; } calculateGridLine({ index: tickIndex, tickId, translation }, index, p1, p2, ticks) { const { gridLine, horizontal, interval, scale: scale2 } = this; if (interval.placement !== "between") { return super.calculateGridLine({ index: tickIndex, tickId, translation }, index, p1, p2, ticks); } const halfStep = translation < scale2.step ? Math.floor(scale2.step / 2) : scale2.step / 2; const offset = translation - halfStep; const [x1, y1, x2, y2] = horizontal ? [offset, Math.max(p1, p2), offset, Math.min(p1, p2)] : [Math.min(p1, p2), offset, Math.max(p1, p2), offset]; const { style: style2 } = gridLine; const { stroke, strokeWidth = 0, lineDash } = style2[tickIndex % style2.length] ?? {}; return { tickId, offset, x1, y1, x2, y2, stroke, strokeWidth, lineDash }; } calculateGridFills(ticks, p1, p2) { const { horizontal, range: range3, scale: scale2 } = this; if (this.interval.placement !== "between") { return super.calculateGridFills(ticks, p1, p2); } const gridFills = []; if (ticks.length == 0) return gridFills; const firstTick = ticks[0]; const firstFillOffCanvas = firstTick.translation > range3[0] + scale2.step / 2; const lastTick = ticks.at(-1); const lastFillOffCanvas = horizontal && lastTick.translation < range3[1] - scale2.step / 2; if (firstFillOffCanvas) { const tick = { tickId: `before:${firstTick.tickId}`, translation: firstTick.translation - scale2.step }; gridFills.push(this.calculateGridFill(tick, -1, firstTick.index - 1, p1, p2, ticks)); } gridFills.push(...ticks.map((tick, index) => this.calculateGridFill(tick, index, tick.index, p1, p2, ticks))); if (lastFillOffCanvas) { const tick = { tickId: `after:${lastTick.tickId}`, translation: lastTick.translation + scale2.step }; gridFills.push(this.calculateGridFill(tick, ticks.length, lastTick.index + 1, p1, p2, ticks)); } return gridFills; } calculateGridFill({ tickId, translation }, index, gridFillIndex, p1, p2, ticks) { const { gridLine, horizontal, interval, scale: scale2 } = this; if (interval.placement !== "between") { return super.calculateGridFill({ tickId, translation }, index, gridFillIndex, p1, p2, ticks); } const startOffset = translation - scale2.step / 2; const endOffset = translation + scale2.step / 2; const [x1, y1, x2, y2] = horizontal ? [startOffset, Math.max(p1, p2), endOffset, Math.min(p1, p2)] : [Math.min(p1, p2), startOffset, Math.max(p1, p2), endOffset]; const { fill, fillOpacity } = gridLine.style[gridFillIndex % gridLine.style.length] ?? {}; return { tickId, x1, y1, x2, y2, fill, fillOpacity }; } calculateTickLines(ticks, direction, scrollbarThickness = 0) { const tickLines = super.calculateTickLines(ticks, direction, scrollbarThickness); if (this.interval.placement === "between" && ticks.length > 0) { tickLines.push( super.calculateTickLine( { isPrimary: false, tickId: `after:${ticks.at(-1)?.tickId}`, translation: this.range[1] }, ticks.length, direction, ticks, scrollbarThickness ) ); } return tickLines; } calculateTickLine({ isPrimary, tickId, translation }, index, direction, ticks, scrollbarThickness = 0) { const { horizontal, interval, primaryTick, scale: scale2, tick } = this; if (interval.placement !== "between") { return super.calculateTickLine( { isPrimary, tickId, translation }, index, direction, ticks, scrollbarThickness ); } const datumTick = isPrimary && primaryTick?.enabled ? primaryTick : tick; const h = -direction * this.getTickSize(datumTick); const halfStep = translation < scale2.step ? Math.floor(scale2.step / 2) : scale2.step / 2; const offset = translation - halfStep; const tickOffset = -direction * (scrollbarThickness + this.getTickSpacing(datumTick)); const [x1, y1, x2, y2] = horizontal ? [offset, tickOffset, offset, tickOffset + h] : [tickOffset, offset, tickOffset + h, offset]; const { stroke, width: strokeWidth } = datumTick; const lineDash = void 0; return { tickId, offset, x1, y1, x2, y2, stroke, strokeWidth, lineDash }; } reduceBandScalePadding() { return this.boundSeries.reduce( (result, series) => { const padding2 = series.getBandScalePadding?.(); if (padding2) { if (result.inner > padding2.inner) { result.inner = padding2.inner; } if (result.outer < padding2.outer) { result.outer = padding2.outer; } } return result; }, { inner: Infinity, outer: -Infinity } ); } tickFormatParams(_domain, _ticks, _fractionDigits, _timeInterval) { return { type: "category" }; } datumFormatParams(value, params, _fractionDigits, _timeInterval, _style) { const { datum, seriesId, legendItemName, key, source, property, domain, boundSeries } = params; if (Array.isArray(value) && value.some((v) => typeof v !== "string")) { value = value.map(String); } return { type: "category", value, datum, seriesId, legendItemName, key, source, property, domain, boundSeries }; } }; _CategoryAxis.className = "CategoryAxis"; _CategoryAxis.type = "category"; __decorateClass([ addFakeTransformToInstanceProperty ], _CategoryAxis.prototype, "groupPaddingInner", 2); __decorateClass([ addFakeTransformToInstanceProperty ], _CategoryAxis.prototype, "paddingInner", 2); __decorateClass([ addFakeTransformToInstanceProperty ], _CategoryAxis.prototype, "paddingOuter", 2); __decorateClass([ ProxyPropertyOnWrite("layoutConstraints", "align") ], _CategoryAxis.prototype, "bandAlignment", 2); __decorateClass([ ActionOnSet({ newValue(value) { if (value == null || value <= 0) { this.layoutConstraints.width = 100; this.layoutConstraints.unit = "percent"; } else { this.layoutConstraints.width = value; this.layoutConstraints.unit = "px"; this.animationManager.skipCurrentBatch(); } } }) ], _CategoryAxis.prototype, "requiredRange", 2); var CategoryAxis = _CategoryAxis; // packages/ag-charts-community/src/scale/linearScale.ts var LinearScale = class _LinearScale extends ContinuousScale { constructor() { super([0, 1], [0, 1]); this.type = "number"; } static is(value) { return value instanceof _LinearScale; } static getTickStep(start2, stop, ticks) { const { interval, tickCount = ContinuousScale.defaultTickCount, minTickCount, maxTickCount } = ticks; return interval ?? tickStep(start2, stop, tickCount, minTickCount, maxTickCount); } toDomain(d) { return d; } ticks({ interval, tickCount = ContinuousScale.defaultTickCount, minTickCount, maxTickCount }, domain = this.domain, visibleRange) { if (!domain || domain.length < 2 || tickCount < 1 || !domain.every(Number.isFinite)) { return { ticks: [], count: 0, firstTickIndex: 0 }; } const [d0, d1] = domain; if (interval) { const step = Math.abs(interval); if (!isDenseInterval((d1 - d0) / step, this.getPixelRange())) { return range(d0, d1, step, visibleRange); } } return createTicks(d0, d1, tickCount, minTickCount, maxTickCount, visibleRange); } niceDomain(ticks, domain = this.domain) { if (domain.length < 2) return []; const { tickCount = ContinuousScale.defaultTickCount } = ticks; let [start2, stop] = domain; if (tickCount === 1) { [start2, stop] = niceTicksDomain(start2, stop); } else if (tickCount > 1) { const roundStart = start2 > stop ? Math.ceil : Math.floor; const roundStop = start2 > stop ? Math.floor : Math.ceil; const maxAttempts = 4; for (let i = 0; i < maxAttempts; i++) { const prev0 = start2; const prev1 = stop; const step = _LinearScale.getTickStep(start2, stop, ticks); const [d0, d1] = domain; start2 = roundStart(d0 / step) * step; stop = roundStop(d1 / step) * step; if (start2 === prev0 && stop === prev1) break; } } return [ticks.nice[0] ? start2 : domain[0], ticks.nice[1] ? stop : domain[1]]; } }; // packages/ag-charts-community/src/chart/axis/numberAxis.ts var NumberAxis = class extends CartesianAxis { constructor(moduleCtx, scale2 = new LinearScale()) { super(moduleCtx, scale2); } hasDefinedDomain() { const { min, max } = this; return min != null && max != null && min < max; } normaliseDataDomain(d) { const { min, max, preferredMin, preferredMax } = this; const { extent: extent2, clipped } = normalisedExtentWithMetadata( d.domain, min, max, preferredMin, preferredMax, void 0, d.sortMetadata?.sortOrder ); return { domain: extent2, clipped }; } getDomainExtentsNice() { return [this.min == null && this.nice, this.max == null && this.nice]; } getVisibleDomain(domain) { const [d0, d1] = domain; const [r0, r1] = this.visibleRange; const length2 = d1 - d0; return [d0 + r0 * length2, d1 - (1 - r1) * length2]; } tickFormatParams(domain, _ticks, fractionDigits) { return { type: "number", visibleDomain: this.getVisibleDomain(domain), fractionDigits }; } datumFormatParams(value, params, fractionDigits) { const { datum, seriesId, legendItemName, key, source, property, domain, boundSeries } = params; const visibleDomain = this.getVisibleDomain(domain); return { type: "number", value, datum, seriesId, legendItemName, key, source, property, domain, visibleDomain, boundSeries, fractionDigits }; } }; NumberAxis.className = "NumberAxis"; NumberAxis.type = "number"; __decorateClass([ addFakeTransformToInstanceProperty ], NumberAxis.prototype, "min", 2); __decorateClass([ addFakeTransformToInstanceProperty ], NumberAxis.prototype, "max", 2); __decorateClass([ addFakeTransformToInstanceProperty ], NumberAxis.prototype, "preferredMin", 2); __decorateClass([ addFakeTransformToInstanceProperty ], NumberAxis.prototype, "preferredMax", 2); // packages/ag-charts-community/src/scale/logScale.ts var logFunctions = { 2: (_base, x) => Math.log2(x), [Math.E]: (_base, x) => Math.log(x), 10: (_base, x) => Math.log10(x) }; var DEFAULT_LOG = (base, x) => Math.log(x) / Math.log(base); function log2(base, domain, x) { const start2 = Math.min(...domain); const fn = logFunctions[base] ?? DEFAULT_LOG; return start2 >= 0 ? fn(base, x) : -fn(base, -x); } var powFunctions = { [Math.E]: (_base, x) => Math.exp(x), 10: (_base, x) => x >= 0 ? 10 ** x : 1 / 10 ** -x }; var DEFAULT_POW = (base, x) => base ** x; function pow(base, domain, x) { const start2 = Math.min(...domain); const fn = powFunctions[base] ?? DEFAULT_POW; return start2 >= 0 ? fn(base, x) : -fn(base, -x); } var LogScale = class _LogScale extends ContinuousScale { constructor(d = [1, 10], r = [0, 1]) { super(d, r); this.type = "log"; // Handling <1 and crossing 0 cases is tricky, easiest solution is to default to clamping. this.defaultClamp = true; this.base = 10; this.log = (x) => log2(this.base, this.domain, x); this.pow = (x) => pow(this.base, this.domain, x); } static is(value) { return value instanceof _LogScale; } transform(x) { const [min, max] = findMinMax(this.domain); if (min >= 0 !== max >= 0) return Number.NaN; return min >= 0 ? Math.log(x) : -Math.log(-x); } transformInvert(x) { const [min, max] = findMinMax(this.domain); if (min >= 0 !== max >= 0) return Number.NaN; return min >= 0 ? Math.exp(x) : -Math.exp(-x); } toDomain(d) { return d; } niceDomain(ticks, domain = this.domain) { if (domain.length < 2) return []; const { base } = this; const [d0, d1] = domain; const roundStart = d0 > d1 ? Math.ceil : Math.floor; const roundStop = d0 > d1 ? Math.floor : Math.ceil; const n0 = pow(base, domain, roundStart(log2(base, domain, d0))); const n1 = pow(base, domain, roundStop(log2(base, domain, d1))); return [ticks.nice[0] ? n0 : domain[0], ticks.nice[1] ? n1 : domain[1]]; } ticks({ interval, tickCount = ContinuousScale.defaultTickCount }, domain = this.domain, visibleRange) { if (!domain || domain.length < 2 || tickCount < 1) { return; } const base = this.base; const [d0, d1] = domain; const start2 = Math.min(d0, d1); const stop = Math.max(d0, d1); let p0 = this.log(start2); let p1 = this.log(stop); if (interval) { const inBounds = (tick) => tick >= start2 && tick <= stop; const step = Math.min(Math.abs(interval), Math.abs(p1 - p0)); const { ticks: rangeTicks, count, firstTickIndex } = range(p0, p1, step, visibleRange); const ticks2 = rangeTicks.map(this.pow).filter(inBounds); if (!isDenseInterval(ticks2.length, this.getPixelRange())) { return { ticks: ticks2, count, firstTickIndex }; } } if (!isInteger(base) || p1 - p0 >= tickCount) { const step = Math.min(p1 - p0, tickCount); const { ticks: ticks2, count, firstTickIndex } = createTicks(p0, p1, step, void 0, void 0, visibleRange); return { ticks: ticks2.map(this.pow), count, firstTickIndex }; } const ticks = []; const isPositive = start2 > 0; p0 = Math.floor(p0) - 1; p1 = Math.round(p1) + 1; const availableSpacing = findRangeExtent(this.range) / tickCount; let lastTickPosition = Infinity; for (let p = p0; p <= p1; p++) { const nextMagnitudeTickPosition = this.convert(this.pow(p + 1)); for (let k = 1; k < base; k++) { const q = isPositive ? k : base - k + 1; const t = this.pow(p) * q; const tickPosition = this.convert(t); const prevSpacing = Math.abs(lastTickPosition - tickPosition); const nextSpacing = Math.abs(tickPosition - nextMagnitudeTickPosition); const fits = prevSpacing >= availableSpacing && nextSpacing >= availableSpacing; if (t >= start2 && t <= stop && (k === 1 || fits || ticks.length === 0)) { ticks.push(t); lastTickPosition = tickPosition; } } } return filterVisibleTicks(ticks, isPositive, visibleRange); } }; // packages/ag-charts-community/src/scene/shape/segmentedPath.ts var SegmentedPath = class extends Path { constructor() { super(...arguments); this.segmentPath = new Path(); } drawPath(ctx) { if (!this.segments || this.segments.length === 0) { super.drawPath(ctx); return; } ctx.save(); const Path2DCtor = getPath2D(); const inverse = new Path2DCtor(); rect(inverse, { x0: 0, y0: 0, x1: ctx.canvas.width, y1: ctx.canvas.height }, false); for (const s of this.segments) { rect(inverse, s.clipRect); } ctx.clip(inverse); super.drawPath(ctx); ctx.restore(); const { segmentPath } = this; segmentPath.setProperties({ opacity: this.opacity, visible: this.visible, lineCap: this.lineCap, lineJoin: this.lineJoin, pointerEvents: this.pointerEvents }); for (const { clipRect, fill, stroke, ...styles } of this.segments) { ctx.save(); segmentPath.path = this.path; segmentPath.setProperties(styles); segmentPath.fill = this.fill == null ? "none" : fill; segmentPath.stroke = this.stroke == null ? "none" : stroke; const clipPath = new Path2DCtor(); rect(clipPath, clipRect); ctx.clip(clipPath); segmentPath.drawPath(ctx); ctx.restore(); } } }; __decorateClass([ SceneRefChangeDetection() ], SegmentedPath.prototype, "segments", 2); function rect(path, { x0, y0, x1, y1 }, clockwise = true) { const minX = Math.min(x0, x1); const minY = Math.min(y0, y1); const maxX = Math.max(x0, x1); const maxY = Math.max(y0, y1); path.moveTo(minX, minY); if (clockwise) { path.lineTo(maxX, minY); path.lineTo(maxX, maxY); path.lineTo(minX, maxY); } else { path.lineTo(minX, maxY); path.lineTo(maxX, maxY); path.lineTo(maxX, minY); } path.closePath(); } // packages/ag-charts-community/src/scene/segmentedGroup.ts var SegmentedGroup = class extends TranslatableGroup { constructor() { super(...arguments); this.segments = []; this.scalablePath = new (Scalable(Path))(); } renderInContext(childRenderCtx) { if (!this.visible) return; const { ctx } = childRenderCtx; if (!this.segments || this.segments?.length === 0) { return super.renderInContext(childRenderCtx); } ctx.save(); const Path2DCtor = getPath2D(); const inverse = new Path2DCtor(); rect(inverse, { x0: 0, y0: 0, x1: ctx.canvas.width, y1: ctx.canvas.height }, false); for (const s of this.segments) { rect(inverse, s.clipRect); } ctx.clip(inverse); for (const child of this.children()) { if (!child.visible) continue; child.render(childRenderCtx); } ctx.restore(); const { scalablePath } = this; for (const { clipRect, ...styles } of this.segments) { ctx.save(); const clipPath = new Path2DCtor(); rect(clipPath, clipRect); ctx.clip(clipPath); scalablePath.setProperties(styles); for (const child of this.children()) { if (!child.visible || !(child instanceof Path)) continue; scalablePath.path = child.path; scalablePath.setProperties({ opacity: child.opacity, lineCap: child.lineCap, lineJoin: child.lineJoin, ...isScalable(child) && { scalingX: child.scalingX, scalingY: child.scalingY, scalingCenterX: child.scalingCenterX, scalingCenterY: child.scalingCenterY } }); scalablePath.render(childRenderCtx); } ctx.restore(); } } }; __decorateClass([ SceneRefChangeDetection() ], SegmentedGroup.prototype, "segments", 2); // packages/ag-charts-community/src/scene/util/quadtree.ts var QuadtreeNearest = class { constructor(capacity, maxdepth, boundary) { this.root = new QuadtreeNodeNearest(capacity, maxdepth, boundary); } clear(boundary) { this.root.clear(boundary); } addValue(hitTester, value) { const elem = { hitTester, value, distanceSquared: (x, y) => { return hitTester.distanceSquared(x, y); } }; this.root.addElem(elem); } find(x, y) { const arg = { best: { nearest: void 0, distanceSquared: Infinity } }; this.root.find(x, y, arg); return arg.best; } }; var QuadtreeSubdivisions = class { constructor(nw, ne, sw, se) { this.nw = nw; this.ne = ne; this.sw = sw; this.se = se; } addElem(elem) { this.nw.addElem(elem); this.ne.addElem(elem); this.sw.addElem(elem); this.se.addElem(elem); } find(x, y, arg) { this.nw.find(x, y, arg); this.ne.find(x, y, arg); this.sw.find(x, y, arg); this.se.find(x, y, arg); } }; var QuadtreeNode = class { constructor(capacity, maxdepth, boundary) { this.capacity = capacity; this.maxdepth = maxdepth; this.boundary = boundary ?? BBox.NaN; this.elems = []; this.subdivisions = void 0; } clear(boundary) { this.elems.length = 0; this.boundary = boundary; this.subdivisions = void 0; } addElem(e) { if (this.addCondition(e)) { if (this.subdivisions === void 0) { if (this.maxdepth === 0 || this.elems.length < this.capacity) { this.elems.push(e); } else { this.subdivide(e); } } else { this.subdivisions.addElem(e); } } } find(x, y, arg) { if (this.findCondition(x, y, arg)) { if (this.subdivisions === void 0) { this.findAction(x, y, arg); } else { this.subdivisions.find(x, y, arg); } } } subdivide(newElem) { this.subdivisions = this.makeSubdivisions(); for (const e of this.elems) { this.subdivisions.addElem(e); } this.subdivisions.addElem(newElem); this.elems.length = 0; } makeSubdivisions() { const { x, y, width: width2, height: height2 } = this.boundary; const { capacity } = this; const depth = this.maxdepth - 1; const halfWidth = width2 / 2; const halfHeight = height2 / 2; const nwBoundary = new BBox(x, y, halfWidth, halfHeight); const neBoundary = new BBox(x + halfWidth, y, halfWidth, halfHeight); const swBoundary = new BBox(x, y + halfHeight, halfWidth, halfHeight); const seBoundary = new BBox(x + halfWidth, y + halfHeight, halfWidth, halfHeight); return new QuadtreeSubdivisions( this.child(capacity, depth, nwBoundary), this.child(capacity, depth, neBoundary), this.child(capacity, depth, swBoundary), this.child(capacity, depth, seBoundary) ); } }; var QuadtreeNodeNearest = class _QuadtreeNodeNearest extends QuadtreeNode { addCondition(e) { const { x, y } = e.hitTester.midPoint; return this.boundary.containsPoint(x, y); } findCondition(x, y, arg) { const { best } = arg; return best.distanceSquared !== 0 && this.boundary.distanceSquared(x, y) < best.distanceSquared; } findAction(x, y, arg) { const other = nearestSquared(x, y, this.elems, arg.best.distanceSquared); if (other.nearest !== void 0 && other.distanceSquared < arg.best.distanceSquared) { arg.best = other; } } child(capacity, depth, boundary) { return new _QuadtreeNodeNearest(capacity, depth, boundary); } }; // packages/ag-charts-community/src/chart/axis/timeAxis.ts var TimeAxisParentLevel = class extends BaseProperties { constructor() { super(...arguments); this.enabled = false; this.label = new AxisLabel(); this.tick = new AxisTick(); } }; __decorateClass([ addFakeTransformToInstanceProperty ], TimeAxisParentLevel.prototype, "enabled", 2); __decorateClass([ addFakeTransformToInstanceProperty ], TimeAxisParentLevel.prototype, "label", 2); __decorateClass([ addFakeTransformToInstanceProperty ], TimeAxisParentLevel.prototype, "tick", 2); var TimeAxis = class extends CartesianAxis { constructor(moduleCtx) { super(moduleCtx, new TimeScale()); this.parentLevel = new TimeAxisParentLevel(); this.min = void 0; this.max = void 0; this.preferredMin = void 0; this.preferredMax = void 0; } // eslint-disable-next-line sonarjs/use-type-alias get _unit() { return void 0; } set _unit(_unit) { logger_exports.warnOnce(`To use 'unit', use an axis with type 'unit-time' instead of 'time'.`); } hasDefinedDomain() { const { min, max } = this; return min != null && max != null && min < max; } isCategoryLike() { return false; } get primaryLabel() { return this.parentLevel.enabled ? this.parentLevel.label : void 0; } get primaryTick() { return this.parentLevel.enabled ? this.parentLevel.tick : void 0; } normaliseDataDomain(d) { const { extent: extent2, clipped } = normalisedTimeExtentWithMetadata( d, this.min, this.max, this.preferredMin, this.preferredMax ); return { domain: extent2, clipped }; } processData() { super.processData(); const { boundSeries, direction, min, max } = this; this.minimumTimeGranularity = minimumTimeAxisDatumGranularity(boundSeries, direction, min, max); } tickFormatParams(domain, ticks, _fractionDigits, timeInterval3) { timeInterval3 ?? (timeInterval3 = lowestGranularityUnitForTicks(ticks)); const truncateDate = dateTruncationForDomain(domain); const unit = intervalUnit(timeInterval3); const step = intervalStep(timeInterval3); const epoch = intervalEpoch(timeInterval3); return { type: "date", unit, step, epoch, truncateDate }; } datumFormatParams(value, params, _fractionDigits, timeInterval3, style2) { if (typeof value === "number") { value = new Date(value); } if (timeInterval3 == null) { const { minimumTimeGranularity } = this; const datumGranularity = lowestGranularityUnitForValue(value); if (minimumTimeGranularity != null && intervalMilliseconds(minimumTimeGranularity) < intervalMilliseconds(datumGranularity)) { timeInterval3 = minimumTimeGranularity; } else { timeInterval3 = datumGranularity; } } const { datum, seriesId, legendItemName, key, source, property, domain, boundSeries } = params; const unit = intervalUnit(timeInterval3); const step = intervalStep(timeInterval3); const epoch = intervalEpoch(timeInterval3); return { type: "date", value, datum, seriesId, legendItemName, key, source, property, domain, boundSeries, unit, step, epoch, style: style2 }; } }; TimeAxis.className = "TimeAxis"; TimeAxis.type = "time"; __decorateClass([ addFakeTransformToInstanceProperty ], TimeAxis.prototype, "parentLevel", 2); __decorateClass([ addFakeTransformToInstanceProperty ], TimeAxis.prototype, "min", 2); __decorateClass([ addFakeTransformToInstanceProperty ], TimeAxis.prototype, "max", 2); __decorateClass([ addFakeTransformToInstanceProperty ], TimeAxis.prototype, "preferredMin", 2); __decorateClass([ addFakeTransformToInstanceProperty ], TimeAxis.prototype, "preferredMax", 2); __decorateClass([ addFakeTransformToInstanceProperty, ProxyPropertyOnWrite("_unit") ], TimeAxis.prototype, "unit", 2); function minimumTimeAxisDatumGranularity(boundSeries, direction, min, max) { const minTimeInterval = boundSeries.reduce((t, series) => { return Math.min(series.minTimeInterval() ?? Infinity, t); }, Infinity); if (Number.isFinite(minTimeInterval)) { return lowestGranularityForInterval(minTimeInterval); } else { return calculateDefaultUnit(boundSeries, direction, min, max)?.unit; } } function calculateDefaultUnit(boundSeries, direction, min, max) { let start2 = Infinity; let end3 = -Infinity; let interval; let maxDataCount = 0; const domainValues = []; for (const series of boundSeries) { if (!series.visible) continue; const { extent: domain } = normalisedTimeExtentWithMetadata(series.getDomain(direction)); if (domain.length === 0) continue; const d0 = domain[0].valueOf(); const d1 = domain.at(-1).valueOf(); domainValues.push(d0, d1); start2 = Math.min(start2 ?? Infinity, d0, d1); end3 = Math.max(end3 ?? -Infinity, d0, d1); const domainExtent = Math.abs(d1 - d0); if (domainExtent === 0) continue; const dataCount = series.dataCount(); maxDataCount = Math.max(maxDataCount, dataCount); if (dataCount <= 1) continue; const i = domainExtent / (dataCount - 1); interval = Math.min(interval ?? Infinity, i); } start2 = Math.min(start2, min?.valueOf() ?? Infinity, max?.valueOf() ?? Infinity); end3 = Math.max(end3, min?.valueOf() ?? -Infinity, max?.valueOf() ?? -Infinity); if (!Number.isFinite(start2) || !Number.isFinite(end3)) return; interval ?? (interval = Math.abs(end3 - start2)); interval = Math.min(interval, minNonZeroDifference(domainValues)); const unit = lowestGranularityForInterval(interval); let step = interval / intervalMilliseconds(unit); if (maxDataCount <= 2) { step = Math.floor(step); } else { step = Math.round(step); } step = Math.max(step, 1); const epoch = step === 1 ? void 0 : intervalFloor(unit, start2); return { unit, step, epoch }; } function minNonZeroDifference(values) { values.sort((a, b) => a - b); let minDiff = Infinity; for (let i = 1; i < values.length; i++) { const d0 = values[i - 1]; const d1 = values[i]; const delta3 = d1 - d0; if (delta3 > 0) { minDiff = Math.min(minDiff, Math.abs(d1 - d0)); } } return minDiff; } // packages/ag-charts-community/src/chart/data/data-model/utils/bandedStructure.ts function adjustBandForInsertion(band, insertIndex, insertCount, isLastBand) { if (insertIndex < band.startIndex) { band.startIndex += insertCount; band.endIndex += insertCount; return false; } else if (insertIndex < band.endIndex || insertIndex === band.endIndex && isLastBand) { band.endIndex += insertCount; return true; } return false; } function adjustBandForRemoval(band, removeIndex, removeCount) { const removeEnd = removeIndex + removeCount; if (removeEnd <= band.startIndex) { band.startIndex = Math.max(0, band.startIndex - removeCount); band.endIndex = Math.max(band.startIndex, band.endIndex - removeCount); return false; } else if (removeIndex >= band.endIndex) { return false; } else { if (removeIndex <= band.startIndex && removeEnd >= band.endIndex) { band.startIndex = removeIndex; band.endIndex = removeIndex; } else if (removeIndex <= band.startIndex) { const deletedFromBand = removeEnd - band.startIndex; const oldBandSize = band.endIndex - band.startIndex; band.startIndex = removeIndex; band.endIndex = band.startIndex + Math.max(0, oldBandSize - deletedFromBand); } else if (removeEnd >= band.endIndex) { band.endIndex = Math.max(band.startIndex, removeIndex); } else { band.endIndex = Math.max(band.startIndex, band.endIndex - removeCount); } return true; } } function calculateTargetBandCount(dataSize, minBandCount) { const derivedCount = Math.ceil(dataSize / 1e3); return Math.max(minBandCount, derivedCount); } function calculateIdealBandSize(dataSize, targetBandCount) { return Math.max(1, Math.ceil(dataSize / targetBandCount)); } function filterEmptyBands(bands) { return bands.filter((band) => band.endIndex > band.startIndex); } function initializeBandArray(dataSize, config, bandFactory) { if (!config.enableBanding || dataSize < config.minDataSizeForBanding) { return [bandFactory(0, dataSize)]; } const targetBandCount = calculateTargetBandCount(dataSize, config.targetBandCount); const bandSize = calculateIdealBandSize(dataSize, targetBandCount); const bands = []; for (let startIndex = 0; startIndex < dataSize; startIndex += bandSize) { const endIndex = Math.min(startIndex + bandSize, dataSize); bands.push(bandFactory(startIndex, endIndex)); } return bands; } function applySpliceOperations(bandHandler, spliceOps) { for (const op of spliceOps) { if (op.insertCount > 0) { bandHandler.handleInsertion(op.index, op.insertCount); } if (op.deleteCount > 0) { bandHandler.handleRemoval(op.index, op.deleteCount); } } } function markUpdatedIndices(bandHandler, updatedIndices) { for (const index of updatedIndices) { bandHandler.handleInsertion(index, 0); } } function applyIndexMapToBandHandler(bandHandler, indexMap) { applySpliceOperations(bandHandler, indexMap.spliceOps); if (indexMap.updatedIndices.size > 0) { markUpdatedIndices(bandHandler, indexMap.updatedIndices); } } var DEFAULT_MIN_DATA_SIZE_FOR_BANDING = 1e3; var DEFAULT_TARGET_BAND_COUNT = 10; var BandedStructure = class { constructor(config = {}) { this.bands = []; this.dataSize = 0; this.config = { minDataSizeForBanding: config.minDataSizeForBanding ?? DEFAULT_MIN_DATA_SIZE_FOR_BANDING, targetBandCount: config.targetBandCount ?? DEFAULT_TARGET_BAND_COUNT, maxBandSize: config.maxBandSize ?? Infinity, enableBanding: config.enableBanding ?? true }; } applyIndexMap(indexMap) { applyIndexMapToBandHandler(this, indexMap); } /** * Initializes or rebalances bands based on current data size. */ initializeBands(dataSize) { this.dataSize = Math.max(0, dataSize); this.bands = initializeBandArray( this.dataSize, this.config, (startIndex, endIndex) => this.createBand(startIndex, endIndex) ); } /** * Returns the number of bands currently in this structure. * Useful for checking if bands need initialization. */ getBandCount() { return this.bands.length; } /** * Handles insertion of new data by adjusting band indices. * Uses proactive band splitting to maintain optimal band sizes. */ handleInsertion(insertIndex, insertCount) { this.dataSize += insertCount; if (this.bands.length === 0) { this.initializeBands(this.dataSize); return; } const targetBandCount = calculateTargetBandCount(this.dataSize, this.config.targetBandCount); const idealBandSize = calculateIdealBandSize(this.dataSize, targetBandCount); const maxBandSize = Math.ceil(idealBandSize * 1.1); for (let i = 0; i < this.bands.length; i++) { const band = this.bands[i]; const isLastBand = i === this.bands.length - 1; if (insertIndex === band.endIndex && isLastBand && insertCount > 0) { const currentBandSize = band.endIndex - band.startIndex; if (currentBandSize >= idealBandSize) { this.bands.push(this.createBand(insertIndex, insertIndex + insertCount)); } else { band.endIndex += insertCount; band.isDirty = true; } break; } const wasDirty = adjustBandForInsertion(band, insertIndex, insertCount, isLastBand); if (wasDirty) { band.isDirty = true; if (insertCount > 0 && insertIndex < band.endIndex) { const bandSize = band.endIndex - band.startIndex; if (bandSize > maxBandSize) { this.splitBand(i, idealBandSize); } } } } } /** * Handles removal of data by adjusting band indices. * Uses shared utilities for consistent band manipulation. */ handleRemoval(removeIndex, removeCount) { if (removeCount <= 0 || this.bands.length === 0) return; const effectiveRemoveCount = Math.min(removeCount, Math.max(0, this.dataSize - removeIndex)); if (effectiveRemoveCount <= 0) return; this.dataSize = Math.max(0, this.dataSize - effectiveRemoveCount); for (const band of this.bands) { const wasDirty = adjustBandForRemoval(band, removeIndex, effectiveRemoveCount); if (wasDirty) { band.isDirty = true; } } this.bands = filterEmptyBands(this.bands); } /** * Split an oversized band into two smaller bands. * Called when a band exceeds maxBandSize during insertion. * * Strategy: * - Split the band as evenly as possible * - Both halves marked as dirty (need recalculation) * - No cache preservation (splitting indicates data changed) */ splitBand(bandIndex, idealSize) { const band = this.bands[bandIndex]; const bandSize = band.endIndex - band.startIndex; const firstHalfSize = Math.min(idealSize, Math.floor(bandSize / 2)); const splitPoint = band.startIndex + firstHalfSize; const band1 = this.createBand(band.startIndex, splitPoint); const band2 = this.createBand(splitPoint, band.endIndex); this.bands.splice(bandIndex, 1, band1, band2); } /** * Returns statistics about the banded structure for debugging. * Subclasses can override to add domain-specific stats. */ getStats() { const dirtyBands = this.bands.filter((band) => band.isDirty); return { totalBands: this.bands.length, dirtyBands: dirtyBands.length, dataSize: this.dataSize }; } markRangeDirty(startIndex, endIndex) { for (const band of this.bands) { if (startIndex < band.endIndex && endIndex > band.startIndex) { band.isDirty = true; } } } }; // packages/ag-charts-community/src/chart/data/dataDomain.ts var DiscreteDomain = class _DiscreteDomain { constructor() { // Set-based storage (default mode) this.domain = /* @__PURE__ */ new Set(); this.dateTimestamps = /* @__PURE__ */ new Set(); this.hasDateValues = false; // Sorted array storage (optimized mode for sorted unique data) this.sortedValues = null; this.sortOrder = void 0; this.isSortedUnique = false; } static is(value) { return value instanceof _DiscreteDomain; } /** * Configure domain for sorted unique mode. * When enabled, uses a single array for O(1) append. * Call this before extending with data. */ setSortedUniqueMode(sortOrder, isUnique) { if (isUnique) { this.isSortedUnique = true; this.sortOrder = sortOrder; this.sortedValues = []; } } extend(val) { if (this.isSortedUnique && this.sortedValues) { this.sortedValues.push(val); if (val instanceof Date) { this.hasDateValues = true; } } else if (val instanceof Date) { this.hasDateValues = true; this.dateTimestamps.add(val.valueOf()); } else { this.domain.add(val); } } getDomain() { if (this.isSortedUnique && this.sortedValues) { let hasSeenInvalid = false; return this.sortedValues.filter((v) => { if (v == null) { if (hasSeenInvalid) return false; hasSeenInvalid = true; return true; } if (v instanceof Date && Number.isNaN(v.valueOf())) { if (hasSeenInvalid) return false; hasSeenInvalid = true; } return true; }); } if (this.hasDateValues) { const dates = Array.from(this.dateTimestamps, (ts) => new Date(ts)); if (this.domain.size > 0) { return [...dates, ...Array.from(this.domain)]; } return dates; } return Array.from(this.domain); } /** Returns true if this domain contains Date values stored as timestamps */ isDateDomain() { return this.hasDateValues; } /** Returns true if this domain is in sorted unique mode */ isSortedUniqueMode() { return this.isSortedUnique; } /** Returns the sort order if in sorted mode, undefined otherwise */ getSortOrder() { return this.sortOrder; } /** Merges another DiscreteDomain's values into this one */ mergeFrom(other) { if (this.isSortedUnique && other.isSortedUnique && this.sortOrder === other.sortOrder && this.sortOrder !== void 0 && other.sortedValues) { if (other.hasDateValues) { this.hasDateValues = true; } this.sortedValues ?? (this.sortedValues = []); this.sortedValues.push(...other.sortedValues); return; } this.convertToSetMode(); if (other.hasDateValues) { this.hasDateValues = true; } if (other.isSortedUnique && other.sortedValues) { for (const val of other.sortedValues) { if (val instanceof Date) { this.dateTimestamps.add(val.valueOf()); } else { this.domain.add(val); } } } else { for (const ts of other.dateTimestamps) { this.dateTimestamps.add(ts); } for (const val of other.domain) { this.domain.add(val); } } } /** Converts from sorted array mode to Set mode (one-way transition) */ convertToSetMode() { if (!this.isSortedUnique) return; if (this.sortedValues) { for (const val of this.sortedValues) { if (val instanceof Date) { this.dateTimestamps.add(val.valueOf()); } else { this.domain.add(val); } } this.sortedValues = null; } this.isSortedUnique = false; this.sortOrder = void 0; } }; var ContinuousDomain = class _ContinuousDomain { constructor() { this.domain = [Infinity, -Infinity]; } static is(value) { return value instanceof _ContinuousDomain; } static extendDomain(values, domain = [Infinity, -Infinity]) { for (const value of values) { if (typeof value !== "number") { continue; } if (domain[0] > value) { domain[0] = value; } if (domain[1] < value) { domain[1] = value; } } return domain; } extend(value) { if (typeof value !== "number" && !(value instanceof Date)) { return; } if (this.domain[0] > value) { this.domain[0] = value; } if (this.domain[1] < value) { this.domain[1] = value; } } getDomain() { return [...this.domain]; } }; var BandedDomain = class extends BandedStructure { constructor(domainFactory, config = {}, isDiscrete = false) { super(config); this.fullDomainCache = null; // Sort order metadata for optimization (set from KEY_SORT_ORDERS) this.sortOrder = void 0; this.isUnique = false; this.domainFactory = domainFactory; this.isDiscrete = isDiscrete; } /** * Set sort order metadata from KEY_SORT_ORDERS. * When data is sorted and unique, enables fast array concatenation in getDomain(). */ setSortOrderMetadata(sortOrder, isUnique) { this.sortOrder = sortOrder; this.isUnique = isUnique; } /** * Creates a new domain band with its own sub-domain instance. * Configures sub-domain for sorted mode if applicable. */ createBand(startIndex, endIndex) { const subDomain = this.domainFactory(); if (this.isDiscrete && this.sortOrder !== void 0 && this.isUnique) { if (DiscreteDomain.is(subDomain)) { subDomain.setSortedUniqueMode(this.sortOrder, this.isUnique); } } return { startIndex, endIndex, subDomain, isDirty: true }; } /** * Initializes bands and clears the full domain cache. */ initializeBands(dataSize) { super.initializeBands(dataSize); this.fullDomainCache = null; } /** * Handles insertion and clears the full domain cache. */ handleInsertion(insertIndex, insertCount) { super.handleInsertion(insertIndex, insertCount); this.fullDomainCache = null; } /** * Handles removal and clears the full domain cache. */ handleRemoval(removeIndex, removeCount) { super.handleRemoval(removeIndex, removeCount); this.fullDomainCache = null; } /** * Split an oversized band into two smaller bands. * Override to handle band splitting for large datasets where banding is beneficial. */ splitBand(bandIndex, idealSize) { if (this.bands.length > 1) { super.splitBand(bandIndex, idealSize); } } /** * Marks bands as dirty that need rescanning. */ markBandsDirty(startIndex, endIndex) { this.markRangeDirty(startIndex, endIndex); this.fullDomainCache = null; } /** * Marks all bands as dirty, forcing a full rescan. */ markAllBandsDirty() { for (const band of this.bands) { band.isDirty = true; } this.fullDomainCache = null; } /** * Extends the domain with values from specified bands. * This is called after dirty bands have been rescanned. */ extendBandsFromData(data, invalidData) { const dataLength = data.length; for (const band of this.bands) { if (!band.isDirty) continue; const subDomain = this.domainFactory(); if (this.isDiscrete && this.sortOrder !== void 0 && this.isUnique) { if (DiscreteDomain.is(subDomain)) { subDomain.setSortedUniqueMode(this.sortOrder, this.isUnique); } } band.subDomain = subDomain; const { startIndex, endIndex } = band; for (let i = startIndex; i < endIndex && i < dataLength; i++) { if (invalidData?.[i]) continue; band.subDomain.extend(data[i]); } band.isDirty = false; } this.fullDomainCache = null; } /** * Gets the bands that need rescanning. */ getDirtyBands() { return this.bands.filter((band) => band.isDirty); } /** * Standard IDataDomain interface - extends domain with a single value. * Note: This is less efficient than batch operations with bands. */ extend(_value) { this.markAllBandsDirty(); this.fullDomainCache = null; } /** * Check if all sub-domains support fast sorted concatenation. */ canUseSortedConcatenation() { if (!this.sortOrder || !this.isUnique || !this.isDiscrete) return false; for (const band of this.bands) { if (!DiscreteDomain.is(band.subDomain)) return false; if (!band.subDomain.isSortedUniqueMode()) return false; if (band.subDomain.getSortOrder() !== this.sortOrder) return false; } return true; } /** * Concatenate sorted domains efficiently. * Only valid when canUseSortedConcatenation() returns true. */ concatenateSortedDomains() { const combined = new DiscreteDomain(); combined.setSortedUniqueMode(this.sortOrder, this.isUnique); for (const band of this.bands) { if (DiscreteDomain.is(band.subDomain)) { combined.mergeFrom(band.subDomain); } } return combined.getDomain(); } /** * Deduplicate nulls and Invalid Dates in a domain result array. * These represent invalid data and may appear from multiple bands. */ deduplicateNulls(result) { let hasSeenInvalid = false; return result.filter((v) => { if (v == null) { if (hasSeenInvalid) return false; hasSeenInvalid = true; return true; } if (v instanceof Date && Number.isNaN(v.valueOf())) { if (hasSeenInvalid) return false; hasSeenInvalid = true; } return true; }); } /** * Combines all band sub-domains to get the overall domain. */ getDomain() { if (this.fullDomainCache !== null) { return this.fullDomainCache; } if (this.bands.length === 0) { this.fullDomainCache = []; return []; } if (this.bands.length === 1) { const result = this.bands[0].subDomain.getDomain(); this.fullDomainCache = this.isDiscrete ? this.deduplicateNulls(result) : result; return this.fullDomainCache; } if (this.isDiscrete) { const firstBand = this.bands[0].subDomain; if (DiscreteDomain.is(firstBand)) { if (this.canUseSortedConcatenation()) { this.fullDomainCache = this.deduplicateNulls(this.concatenateSortedDomains()); } else { const combined = new DiscreteDomain(); for (const band of this.bands) { if (DiscreteDomain.is(band.subDomain)) { combined.mergeFrom(band.subDomain); } } this.fullDomainCache = this.deduplicateNulls(combined.getDomain()); } } else { const combined = /* @__PURE__ */ new Set(); for (const band of this.bands) { for (const value of band.subDomain.getDomain()) { combined.add(value); } } this.fullDomainCache = Array.from(combined); } } else { let min; let max; for (const band of this.bands) { const bandDomain = band.subDomain.getDomain(); if (bandDomain.length === 2) { const [bandMin, bandMax] = bandDomain; if (min === void 0 || bandMin != null && min != null && bandMin < min) { min = bandMin; } if (max === void 0 || bandMax != null && max != null && bandMax > max) { max = bandMax; } } } if (min !== void 0 && max !== void 0) { this.fullDomainCache = [min, max]; } else { this.fullDomainCache = []; } } return this.fullDomainCache; } /** * Returns statistics about the banded domain for debugging. */ getStats() { const dirtyCount = this.bands.filter((b) => b.isDirty).length; const totalSize = this.bands.reduce((sum, b) => sum + (b.endIndex - b.startIndex), 0); return { bandCount: this.bands.length, dirtyBandCount: dirtyCount, averageBandSize: this.bands.length > 0 ? totalSize / this.bands.length : 0, dataSize: this.dataSize }; } }; // packages/ag-charts-community/src/chart/data/aggregateFunctions.ts function sumValues(values, accumulator = [0, 0]) { for (const value of values) { if (typeof value !== "number") { continue; } if (value < 0) { accumulator[0] += value; } if (value > 0) { accumulator[1] += value; } } return accumulator; } function groupSum(id, opts) { const visible = opts?.visible ?? true; return { id, type: "aggregate", matchGroupIds: opts?.matchGroupId ? [opts?.matchGroupId] : void 0, aggregateFunction: (values) => sumValues(values), groupAggregateFunction: (next, acc = [0, 0]) => { if (visible) { acc[0] += next?.[0] ?? 0; acc[1] += next?.[1] ?? 0; } return acc; } }; } function range2(id, matchGroupId) { const result = { id, matchGroupIds: [matchGroupId], type: "aggregate", aggregateFunction: (values) => ContinuousDomain.extendDomain(values) }; return result; } function groupCount(id, opts) { const visible = opts?.visible ?? true; return { id, type: "aggregate", aggregateFunction: () => [0, 1], groupAggregateFunction: (next, acc = [0, 0]) => { if (visible) { acc[0] += next?.[0] ?? 0; acc[1] += next?.[1] ?? 0; } return acc; } }; } function groupAverage(id, opts) { const visible = opts?.visible ?? true; const def = { id, matchGroupIds: opts?.matchGroupId ? [opts?.matchGroupId] : void 0, type: "aggregate", aggregateFunction: (values) => sumValues(values), groupAggregateFunction: (next, acc = [0, 0, -1]) => { if (visible) { acc[0] += next?.[0] ?? 0; acc[2]++; acc[1] += next?.[1] ?? 0; } return acc; }, finalFunction: (acc = [0, 0, 0]) => { const result = acc[0] + acc[1]; if (result >= 0) { return [0, result / acc[2]]; } return [result / acc[2], 0]; } }; return def; } function area(id, aggFn, matchGroupId) { const result = { id, matchGroupIds: matchGroupId ? [matchGroupId] : void 0, type: "aggregate", aggregateFunction: (values, keyRange = []) => { const keyWidth = keyRange[1] - keyRange[0]; return aggFn.aggregateFunction(values).map((v) => v / keyWidth); } }; if (aggFn.groupAggregateFunction) { result.groupAggregateFunction = aggFn.groupAggregateFunction; } return result; } function accumulatedValue(onlyPositive) { return () => { let value = 0; return (datum) => { if (!isFiniteNumber(datum)) { return datum; } value += onlyPositive ? Math.max(0, datum) : datum; return value; }; }; } function trailingAccumulatedValue() { return () => { let value = 0; return (datum) => { if (!isFiniteNumber(datum)) { return datum; } const trailingValue = value; value += datum; return trailingValue; }; }; } // packages/ag-charts-community/src/chart/data/data-model/utils/helpers.ts var NULL_KEY_STRING = "\0__AG_NULL__\0"; var UNDEFINED_KEY_STRING = "\0__AG_UNDEFINED__\0"; function keyToString(key) { if (key === null) return NULL_KEY_STRING; if (key === void 0) return UNDEFINED_KEY_STRING; if (Array.isArray(key)) { return "[" + key.map(keyToString).join(",") + "]"; } return isObject(key) ? JSON.stringify(key) : String(key); } function toKeyString(keys) { return keys.map(keyToString).join("-"); } function fixNumericExtent(extent2) { const numberExtent = extent2?.map(Number); return numberExtent?.every(Number.isFinite) ? numberExtent : []; } function getMissCount(scopeProvider, missMap) { return missMap?.get(scopeProvider.id) ?? 0; } function isScoped(obj) { return "scopes" in obj && Array.isArray(obj.scopes); } function createArray(length2, value) { const out = []; for (let i = 0; i < length2; i += 1) { out[i] = value; } return out; } function uniqueChangeDescriptions(scopeChanges) { const deduped = /* @__PURE__ */ new Set(); for (const changeDesc of scopeChanges.values()) { if (changeDesc) { deduped.add(changeDesc); } } return deduped; } function datumKeys(keys, datumIndex, allowNull = false) { const out = []; for (const k of keys) { const key = k?.[datumIndex]; if (key == null && !allowNull) return; out.push(key); } return out; } function getPathComponents(path) { const components = []; let matchIndex = 0; let matchGroup; const regExp = /((?:(?:^|\.)\s*\w+|\[\s*(?:'(?:[^']|(? { let current = datum; for (const component of components) { current = current[component]; } return current; }; } // packages/ag-charts-community/src/chart/data/data-model/aggregation/aggregator.ts var Aggregator = class { constructor(ctx, scopeCacheManager, resolvers) { this.ctx = ctx; this.scopeCacheManager = scopeCacheManager; this.resolvers = resolvers; } /** * Aggregates data for ungrouped datasets. * Each datum gets its own aggregation result. */ aggregateUngroupedData(processedData) { const domainAggValues = this.ctx.aggregates.map(() => [Infinity, -Infinity]); processedData.domain.aggValues = domainAggValues; const { columns, dataSources } = processedData; const onlyScope = first(dataSources.keys()); const keys = processedData.keys.map((k) => k.get(onlyScope)); const rawData = dataSources.get(onlyScope)?.data ?? []; const allowNull = this.ctx.keys.some((keyDef) => keyDef.allowNullKey === true); processedData.aggregation = rawData?.map((_, datumIndex) => { const aggregation = []; for (const [index, def] of this.ctx.aggregates.entries()) { const indices = this.valueGroupIdxLookup(def); let groupAggValues = def.groupAggregateFunction?.() ?? [Infinity, -Infinity]; const valuesToAgg = indices.map((columnIndex) => columns[columnIndex][datumIndex]); const k = datumKeys(keys, datumIndex, allowNull); const valuesAgg = k == null ? void 0 : def.aggregateFunction(valuesToAgg, k); if (valuesAgg) { groupAggValues = def.groupAggregateFunction?.(valuesAgg, groupAggValues) ?? ContinuousDomain.extendDomain(valuesAgg, groupAggValues); } const finalValues = def.finalFunction?.(groupAggValues) ?? groupAggValues; aggregation[index] = finalValues; ContinuousDomain.extendDomain(finalValues, domainAggValues[index]); } return aggregation; }); } /** * Aggregates data for grouped datasets. * Multiple datums in a group share aggregation results. */ aggregateGroupedData(processedData) { const domainAggValues = this.ctx.aggregates.map(() => [Infinity, -Infinity]); processedData.domain.aggValues = domainAggValues; const { columns } = processedData; for (const [index, def] of this.ctx.aggregates.entries()) { const indices = this.valueGroupIdxLookup(def); for (let groupIndex = 0; groupIndex < processedData.groups.length; groupIndex++) { const group = processedData.groups[groupIndex]; group.aggregation ?? (group.aggregation = []); const groupKeys = group.keys; let groupAggValues = def.groupAggregateFunction?.() ?? [Infinity, -Infinity]; const maxDatumIndex = Math.max( ...indices.map((columnIndex) => group.datumIndices[columnIndex]?.length ?? 0) ); for (let datumIndex = 0; datumIndex < maxDatumIndex; datumIndex++) { const valuesToAgg = indices.map((columnIndex) => { const relativeDatumIndex = group.datumIndices[columnIndex]?.[datumIndex]; if (relativeDatumIndex == null) { return void 0; } const absoluteDatumIndex = this.resolvers.resolveAbsoluteIndex(groupIndex, relativeDatumIndex); return columns[columnIndex][absoluteDatumIndex]; }); const valuesAgg = def.aggregateFunction(valuesToAgg, groupKeys); if (valuesAgg) { groupAggValues = def.groupAggregateFunction?.(valuesAgg, groupAggValues) ?? ContinuousDomain.extendDomain(valuesAgg, groupAggValues); } } const finalValues = def.finalFunction?.(groupAggValues) ?? groupAggValues; group.aggregation[index] = finalValues; ContinuousDomain.extendDomain(finalValues, domainAggValues[index]); } } } /** * Post-processes groups after grouping is complete. * Applies group value processors to adjust group values and recompute domains. */ postProcessGroups(processedData) { const { groupProcessors } = this.ctx; const { columnScopes, columns, invalidData } = processedData; for (const processor of groupProcessors) { const valueIndexes = this.valueGroupIdxLookup(processor); const adjustFn = processor.adjust()(); for (let groupIndex = 0; groupIndex < processedData.groups.length; groupIndex++) { const dataGroup = processedData.groups[groupIndex]; adjustFn(columns, valueIndexes, dataGroup, groupIndex); } for (const valueIndex of valueIndexes) { const valueDef = this.ctx.values[valueIndex]; const isDiscrete = valueDef.valueType === "category"; const column = columns[valueIndex]; const columnScope = first(columnScopes[valueIndex]); const invalidDatums = invalidData?.get(columnScope); const domain = isDiscrete ? new DiscreteDomain() : new ContinuousDomain(); for (let datumIndex = 0; datumIndex < column.length; datumIndex += 1) { if (invalidDatums?.[datumIndex] === true) continue; domain.extend(column[datumIndex]); } processedData.domain.values[valueIndex] = domain.getDomain(); } } } valueGroupIdxLookup(selector) { return this.scopeCacheManager.valueGroupIdxLookup(selector); } }; // packages/ag-charts-community/src/chart/data/data-model/domain/domainInitializer.ts var DomainInitializer = class { constructor(ctx) { this.ctx = ctx; } /** * Sets up the appropriate domain type for a property definition. * Returns a BandedDomain wrapping DiscreteDomain for category values, * or a BandedDomain wrapping ContinuousDomain for continuous values. * Falls back to non-banded domains when banding is disabled. * * @param sortOrderEntry Optional sort order metadata from KEY_SORT_ORDERS. * When data is sorted and unique, enables fast array concatenation optimization. */ setupDomainForDefinition(def, bandedDomains, sortOrderEntry) { const isDiscrete = def.valueType === "category"; let domain = bandedDomains.get(def); if (!domain && this.ctx.bandingConfig?.enableBanding !== false) { domain = new BandedDomain( isDiscrete ? () => new DiscreteDomain() : () => new ContinuousDomain(), this.ctx.bandingConfig, isDiscrete ); bandedDomains.set(def, domain); } if (domain && isDiscrete) { domain.setSortOrderMetadata( sortOrderEntry?.sortOrder, sortOrderEntry?.isUnique ?? false ); } if (domain) { return domain; } return isDiscrete ? new DiscreteDomain() : new ContinuousDomain(); } /** * Extends a domain from data array, using banded optimization if available. * Note: For BandedDomain, bands should already be initialized before calling this method. */ extendDomainFromData(domain, data, invalidData) { if (domain instanceof BandedDomain) { domain.extendBandsFromData(data, invalidData); } else { for (let i = 0; i < data.length; i++) { if (invalidData?.[i] === true) continue; domain.extend(data[i]); } } } /** * Initializes a banded domain if needed based on data size and state. * This is a memory optimization that divides large datasets into bands. */ initializeBandedDomain(domain, dataSize, propertyName) { if (!(domain instanceof BandedDomain)) return; const stats = domain.getStats(); const shouldReinit = stats.bandCount === 0 || stats.dataSize !== dataSize; if (this.ctx.debug.check() && shouldReinit && propertyName) { this.ctx.debug( `Reinitializing bands for ${propertyName}: bandCount=${stats.bandCount}, dataSize=${stats.dataSize}, dataLength=${dataSize}` ); } if (shouldReinit) { domain.initializeBands(dataSize); } } }; // packages/ag-charts-community/src/chart/data/dataModelTypes.ts var KEY_SORT_ORDERS = Symbol("key-sort-orders"); var COLUMN_SORT_ORDERS = Symbol("column-sort-orders"); var DOMAIN_RANGES = Symbol("domain-ranges"); var DOMAIN_BANDS = Symbol("domain-bands"); var REDUCER_BANDS = Symbol("reducer-bands"); var SHARED_ZERO_INDICES = Object.freeze([0]); // packages/ag-charts-community/src/chart/data/data-model/domain/processValueFactory.ts function trackMissingValue(missing, valueScopes) { if (typeof valueScopes === "string") { missing.set(valueScopes, (missing.get(valueScopes) ?? 0) + 1); } else { for (const scope of valueScopes) { missing.set(scope, (missing.get(scope) ?? 0) + 1); } } } function handleInvalidValue(meta, value) { meta.reusableResult.valid = false; if (meta.hasInvalidValue) { meta.reusableResult.value = meta.invalidValue; meta.domain.extend(meta.invalidValue); return; } if (meta.mode !== "integrated") { logger_exports.warnOnce( `invalid value of type [${typeof value}] for [${meta.def.scopes} / ${meta.def.id}] ignored:`, `[${value}]` ); } meta.reusableResult.value = void 0; } function processValidationCheck(validation, valueInDatum, value, datum, idx, meta) { if (validation && valueInDatum && validation(value, datum, idx) === false) { meta.reusableResult.missing = false; handleInvalidValue(meta, value); return meta.reusableResult; } return null; } function handleMissingTracking(valueInDatum, hasMissingValue, missing, valueScopes, reusableResult) { reusableResult.missing = !valueInDatum; if (!valueInDatum && !hasMissingValue) { trackMissingValue(missing, valueScopes); } } function setValidResult(value, reusableResult, domain) { reusableResult.valid = true; reusableResult.value = value; domain.extend(value); return reusableResult; } var ProcessValueFactory = class { constructor(ctx) { this.ctx = ctx; } createProcessValueFn(def, accessor, domain, reusableResult, processorFns, domainMode) { const context = { def, accessor, domain, reusableResult, processorFns, mode: this.ctx.mode }; const specializedFn = domainMode === "extend" ? this.createSpecializedProcessValue(context, def.validation) : null; return specializedFn ?? this.createGenericProcessValue(context, domainMode); } createSpecializedProcessValue(context, validation) { if (context.def.forceValue != null) { return this.createSpecializedProcessValueForceValue(context); } if (context.def.processor) { return this.createSpecializedProcessValueProcessor(context, validation); } if (validation) { return context.def.type === "key" ? this.createSpecializedProcessValueKeyValidation(context, validation) : this.createSpecializedProcessValueValueValidation(context, validation); } return null; } createValidationMeta(context) { const { def, domain, reusableResult, mode } = context; return { reusableResult, hasInvalidValue: "invalidValue" in def, invalidValue: def.invalidValue, domain, def, mode }; } /** * Creates a specialized processValue function optimized for key properties with validation. * Eliminates all branching for the most common key property case (~30% of calls). */ createSpecializedProcessValueKeyValidation(context, validation) { const { def, accessor, domain, reusableResult } = context; const property = def.property; const hasMissingValue = "missingValue" in def; const missingValue = def.missingValue; const missing = def.missing; const allowNullKey = def.allowNullKey ?? false; const validationMeta = this.createValidationMeta(context); if (accessor) { const accessorFn = accessor; return function processValueKeyValidationAccessor(datum, idx, valueScopes) { let value; try { value = accessorFn(datum); } catch { } const valueInDatum = value != null || allowNullKey && value == null; const nullInvalid = !allowNullKey && value == null; if (!valueInDatum || nullInvalid || validation(value, datum, idx) === false) { reusableResult.missing = !valueInDatum; if (!valueInDatum && !hasMissingValue) { trackMissingValue(missing, valueScopes); } handleInvalidValue(validationMeta, value); return reusableResult; } reusableResult.missing = false; reusableResult.valid = true; reusableResult.value = value; domain.extend(value); return reusableResult; }; } return function processValueKeyValidationDirect(datum, idx, valueScopes) { const valueInDatum = property in datum; const value = valueInDatum ? datum[property] : missingValue; const nullInvalid = !allowNullKey && value == null; if (!valueInDatum || nullInvalid || validation(value, datum, idx) === false) { reusableResult.missing = !valueInDatum; if (!valueInDatum && !hasMissingValue) { trackMissingValue(missing, valueScopes); } handleInvalidValue(validationMeta, value); return reusableResult; } reusableResult.missing = false; reusableResult.valid = true; reusableResult.value = value; domain.extend(value); return reusableResult; }; } /** * Creates a specialized processValue function optimized for value properties with validation. * Eliminates branching for the most common value property case (~50% of calls). */ createSpecializedProcessValueValueValidation(context, validation) { const { def, accessor, domain, reusableResult } = context; const property = def.property; const hasMissingValue = "missingValue" in def; const missingValue = def.missingValue; const missing = def.missing; const validationMeta = this.createValidationMeta(context); if (accessor) { const accessorFn = accessor; return function processValueValueValidationAccessor(datum, idx, valueScopes) { let value; try { value = accessorFn(datum); } catch { } const valueInDatum = value != null; const validationFailed = processValidationCheck( validation, valueInDatum, value, datum, idx, validationMeta ); if (validationFailed !== null) return validationFailed; handleMissingTracking(valueInDatum, hasMissingValue, missing, valueScopes, reusableResult); return setValidResult(value, reusableResult, domain); }; } return function processValueValueValidationDirect(datum, idx, valueScopes) { const valueInDatum = property in datum; const value = valueInDatum ? datum[property] : missingValue; const validationFailed = processValidationCheck( validation, valueInDatum, value, datum, idx, validationMeta ); if (validationFailed !== null) return validationFailed; handleMissingTracking(valueInDatum, hasMissingValue, missing, valueScopes, reusableResult); return setValidResult(value, reusableResult, domain); }; } /** * Creates a specialized processValue function for properties with forceValue. * Optimized for invisible series (~5-10% of calls). */ createSpecializedProcessValueForceValue(context) { const { def, accessor, domain, reusableResult } = context; const property = def.property; const forceValue = def.forceValue; if (accessor) { const accessorFn = accessor; return function processValueForceValueAccessor(datum, _idx, _valueScopes) { let value; try { value = accessorFn(datum); } catch { } const valueInDatum = value != null; const valueNegative = valueInDatum && isNegative(value); const forcedValue = valueNegative ? -1 * forceValue : forceValue; reusableResult.missing = false; reusableResult.valid = true; reusableResult.value = forcedValue; domain.extend(forcedValue); return reusableResult; }; } return function processValueForceValueDirect(datum, _idx, _valueScopes) { const valueInDatum = property in datum; const value = valueInDatum ? datum[property] : void 0; const valueNegative = valueInDatum && isNegative(value); const forcedValue = valueNegative ? -1 * forceValue : forceValue; reusableResult.missing = false; reusableResult.valid = true; reusableResult.value = forcedValue; domain.extend(forcedValue); return reusableResult; }; } /** * Creates a specialized processValue function for properties with processors. * Optimized for data transformations (~5-10% of calls). */ createSpecializedProcessValueProcessor(context, validation) { const { def, accessor, domain, reusableResult, processorFns } = context; const property = def.property; const hasMissingValue = "missingValue" in def; const missingValue = def.missingValue; const missing = def.missing; const processor = def.processor; const validationMeta = this.createValidationMeta(context); if (accessor) { const accessorFn = accessor; return function processValueProcessorAccessor(datum, idx, valueScopes) { let value; try { value = accessorFn(datum); } catch { } const valueInDatum = value != null; const validationFailed = processValidationCheck( validation, valueInDatum, value, datum, idx, validationMeta ); if (validationFailed !== null) return validationFailed; handleMissingTracking(valueInDatum, hasMissingValue, missing, valueScopes, reusableResult); let processorFn = processorFns.get(def); if (processorFn == null) { processorFn = processor(); processorFns.set(def, processorFn); } value = processorFn(value, idx); return setValidResult(value, reusableResult, domain); }; } return function processValueProcessorDirect(datum, idx, valueScopes) { const valueInDatum = property in datum; let value = valueInDatum ? datum[property] : missingValue; const validationFailed = processValidationCheck( validation, valueInDatum, value, datum, idx, validationMeta ); if (validationFailed !== null) return validationFailed; handleMissingTracking(valueInDatum, hasMissingValue, missing, valueScopes, reusableResult); let processorFn = processorFns.get(def); if (processorFn == null) { processorFn = processor(); processorFns.set(def, processorFn); } value = processorFn(value, idx); return setValidResult(value, reusableResult, domain); }; } /** * Creates the generic fallback processValue implementation used for edge cases * and for skip mode (no domain extension). Generates a per-definition function * so callers can resolve it once and reuse it without additional branching. */ createGenericProcessValue(context, domainMode) { const { def, accessor, domain, processorFns } = context; const property = def.property; const hasMissingValue = "missingValue" in def; const missingValue = def.missingValue; const missing = def.missing; const shouldExtendDomain = domainMode === "extend"; const reusableResult = context.reusableResult; const validationMeta = this.createValidationMeta(context); return function processValueGeneric(datum, idx, valueScopes) { let value; let valueInDatum; if (accessor) { try { value = accessor(datum); } catch { } valueInDatum = value != null; } else { valueInDatum = property in datum; value = valueInDatum ? datum[property] : missingValue; } if (def.forceValue != null) { const valueNegative = valueInDatum && isNegative(value); value = valueNegative ? -1 * def.forceValue : def.forceValue; valueInDatum = true; } handleMissingTracking(valueInDatum, hasMissingValue, missing, valueScopes, reusableResult); const allowNullKey = def.allowNullKey ?? false; const isKeyWithNullValue = def.type === "key" && value == null && !allowNullKey; if (isKeyWithNullValue) { handleInvalidValue(validationMeta, value); return reusableResult; } const validationFailed = processValidationCheck( def.validation, valueInDatum, value, datum, idx, validationMeta ); if (validationFailed !== null) return validationFailed; reusableResult.valid = true; if (def.processor) { let processor = processorFns.get(def); if (processor == null) { processor = def.processor(); processorFns.set(def, processor); } value = processor(value, idx); } if (shouldExtendDomain) { domain.extend(value); } reusableResult.value = value; return reusableResult; }; } }; // packages/ag-charts-community/src/chart/data/data-model/domain/domainManager.ts function scopesOverlap(scopes1, scopes2) { if (!scopes1 || !scopes2 || scopes1.length === 0 || scopes2.length === 0) { return true; } return scopes1.some((s) => scopes2.includes(s)); } function findMatchingKeyDef(valueDef, keyDefs) { if (valueDef.valueType !== "category") return void 0; for (const keyDef of keyDefs) { if (keyDef.property !== valueDef.property) continue; if (keyDef.valueType !== valueDef.valueType) continue; if (!scopesOverlap(keyDef.scopes, valueDef.scopes)) continue; if (keyDef.validation !== valueDef.validation) continue; return keyDef; } return void 0; } var DomainManager = class { constructor(ctx, initializer, scopeCacheManager) { this.ctx = ctx; this.initializer = initializer; this.scopeCacheManager = scopeCacheManager; this.processValueFactory = new ProcessValueFactory(ctx); } /** * Recomputes all domains from processed data. * Uses BandedDomain optimization for continuous domains to avoid full rescans. * Shares domains between keys and values when they reference the same property. */ recomputeDomains(processedData) { const startTime = this.ctx.debug.check() ? performance.now() : 0; const bandedDomains = processedData[DOMAIN_BANDS]; let bandStats; const keySortOrders = processedData[KEY_SORT_ORDERS]; const keyDomains = this.setupDefinitionDomains(this.ctx.keys, bandedDomains, keySortOrders); const valueToKeyDef = /* @__PURE__ */ new Map(); for (const valueDef of this.ctx.values) { const matchingKeyDef = findMatchingKeyDef(valueDef, this.ctx.keys); if (matchingKeyDef) { valueToKeyDef.set(valueDef, matchingKeyDef); } } const valueDomains = this.setupValueDomainsWithSharing( this.ctx.values, bandedDomains, keyDomains, valueToKeyDef ); const sharedDomains = /* @__PURE__ */ new Set(); for (const [, keyDef] of valueToKeyDef) { const sharedDomain = keyDomains.get(keyDef); if (sharedDomain) { sharedDomains.add(sharedDomain); } } this.initializeDomainBands( this.ctx.keys, keyDomains, (defIndex) => { const keysMap = processedData.keys[defIndex]; return Math.max(...Array.from(keysMap.values()).map((keys) => keys.length)); }, (def) => String(def.property) ); this.initializeDomainBands( this.ctx.values, valueDomains, (defIndex) => processedData.columns[defIndex].length, (def) => String(def.property) ); const preScanDomainStats = /* @__PURE__ */ new Map(); if (bandedDomains.size > 0) { bandStats = { totalBands: 0, dirtyBands: 0, totalData: 0 }; for (const domain of bandedDomains.values()) { if (domain instanceof BandedDomain) { const stats = domain.getStats(); preScanDomainStats.set(domain, stats); bandStats.totalBands += stats.bandCount; bandStats.dirtyBands += stats.dirtyBandCount; bandStats.totalData = Math.max(bandStats.totalData, stats.dataSize); } } } this.extendDomainsFromData( this.ctx.keys, keyDomains, (defIndex, scope) => processedData.keys[defIndex]?.get(scope), (def) => def.scopes ?? [], (scope) => processedData.invalidKeys?.get(scope) ); this.extendDomainsFromData( this.ctx.values, valueDomains, (defIndex, _scope) => processedData.columns[defIndex], (def) => [first(def.scopes)], (scope) => processedData.invalidKeys?.get(scope), sharedDomains ); processedData.domain.keys = this.ctx.keys.map(function mapDomainKeys(keyDef) { const domain = keyDomains.get(keyDef); const result = domain.getDomain(); if (ContinuousDomain.is(domain) && result[0] > result[1]) { return []; } return result; }); processedData.domain.values = this.ctx.values.map(function mapDomainValues(valueDef) { const domain = valueDomains.get(valueDef); const result = domain.getDomain(); if (ContinuousDomain.is(domain) && result[0] > result[1]) { return []; } return result; }); if (processedData.type === "grouped") { processedData.domain.groups = processedData.groups.map((group) => group.keys); } this.collectDomainBandingMetadata(processedData, keyDomains, valueDomains, bandedDomains, preScanDomainStats); if (this.ctx.debug.check() && startTime > 0) { const endTime = performance.now(); const duration = endTime - startTime; if (bandStats && bandStats.totalBands > 0) { const scanRatio = bandStats.dirtyBands / bandStats.totalBands; const dataScanned = Math.round(scanRatio * bandStats.totalData); this.ctx.debug( `recomputeDomains with banding: ${duration.toFixed(2)}ms, bands: ${bandStats.dirtyBands}/${bandStats.totalBands} dirty, data scanned: ~${dataScanned}/${bandStats.totalData} (${(scanRatio * 100).toFixed(1)}%)` ); } else { this.ctx.debug(`recomputeDomains: ${duration.toFixed(2)}ms (no banding)`); } } } /** * Creates domain instances for the given definitions, reusing banded domains when available. * For key definitions, passes KEY_SORT_ORDERS metadata to enable fast array concatenation. */ setupDefinitionDomains(defs, bandedDomains, keySortOrders) { const domains = /* @__PURE__ */ new Map(); for (const [defIndex, def] of defs.entries()) { const sortOrderEntry = keySortOrders?.get(defIndex); domains.set(def, this.initializer.setupDomainForDefinition(def, bandedDomains, sortOrderEntry)); } return domains; } /** * Initializes banded domains for each definition using the provided data length accessor. */ initializeDomainBands(defs, domains, getDataLength, getPropertyName) { for (const [defIndex, def] of defs.entries()) { const domain = domains.get(def); if (!domain) continue; const dataLength = getDataLength(defIndex); this.initializer.initializeBandedDomain(domain, dataLength, getPropertyName(def)); } } /** * Extends domains from data sources using a shared traversal. * @param skipDomains Optional set of domains to skip (already extended via shared key processing) */ extendDomainsFromData(defs, domains, getData, getScopes, getInvalid, skipDomains) { for (const [defIndex, def] of defs.entries()) { const domain = domains.get(def); if (!domain) continue; if (skipDomains?.has(domain)) continue; for (const scope of getScopes(def)) { if (!scope) continue; const data = getData(defIndex, scope); if (!data) continue; const invalid = getInvalid(scope); this.initializer.extendDomainFromData(domain, data, invalid); } } } /** * Sets up value domains, reusing key domains where properties match. * This avoids duplicate domain computation for properties that appear as both key and value. */ setupValueDomainsWithSharing(defs, bandedDomains, keyDomains, valueToKeyDef) { const domains = /* @__PURE__ */ new Map(); for (const def of defs) { const matchingKeyDef = valueToKeyDef.get(def); if (matchingKeyDef) { const keyDomain = keyDomains.get(matchingKeyDef); if (keyDomain) { domains.set(def, keyDomain); continue; } } domains.set(def, this.initializer.setupDomainForDefinition(def, bandedDomains)); } return domains; } /** * Initializes domain processor for value processing during data transformation. * Returns domain maps and processing functions used during data extraction. * Uses specialized functions per property definition to eliminate branching in hot paths. * Shares domains between keys and values when they reference the same property. */ initDataDomainProcessor(domainMode) { const { keys: keyDefs, values: valueDefs } = this.ctx; const scopes = /* @__PURE__ */ new Set(); for (const valueDef of valueDefs) { if (!valueDef.scopes) continue; for (const scope of valueDef.scopes) { scopes.add(scope); } } const dataDomain = /* @__PURE__ */ new Map(); const processorFns = /* @__PURE__ */ new Map(); let allScopesHaveSameDefs = true; const initDataDomain = () => { for (const def of keyDefs) { if (def.valueType === "category") { dataDomain.set(def, new DiscreteDomain()); } else { dataDomain.set(def, new ContinuousDomain()); } } for (const def of valueDefs) { const matchingKeyDef = findMatchingKeyDef(def, keyDefs); if (matchingKeyDef) { const keyDomain = dataDomain.get(matchingKeyDef); if (keyDomain) { dataDomain.set(def, keyDomain); allScopesHaveSameDefs && (allScopesHaveSameDefs = (def.scopes?.length ?? 0) === scopes.size); continue; } } if (def.valueType === "category") { dataDomain.set(def, new DiscreteDomain()); } else { dataDomain.set(def, new ContinuousDomain()); allScopesHaveSameDefs && (allScopesHaveSameDefs = (def.scopes?.length ?? 0) === scopes.size); } } }; initDataDomain(); const accessors = this.scopeCacheManager.buildAccessors(iterate(keyDefs, valueDefs)); const processValueFns = /* @__PURE__ */ new WeakMap(); for (const def of iterate(keyDefs, valueDefs)) { const accessor = accessors.get(def.property); const domain = dataDomain.get(def); const reusableResult = { value: void 0, missing: false, valid: false }; const processFn = this.processValueFactory.createProcessValueFn( def, accessor, domain, reusableResult, processorFns, domainMode ); processValueFns.set(def, processFn); } function getProcessValue(def) { const processFn = processValueFns.get(def); if (!processFn) { throw new Error("AG Charts - missing processValue function for definition"); } return processFn; } function processValue(def, datum, idx, valueScopes) { return getProcessValue(def)(datum, idx, valueScopes); } return { dataDomain, processValue, getProcessValue, initDataDomain, scopes, allScopesHaveSameDefs }; } /** * Collects metadata about banded domain optimization for debugging and testing. * Stores statistics about domain banding per key and value definition. */ collectDomainBandingMetadata(processedData, keyDomains, valueDomains, bandedDomains, preScanDomainStats) { processedData.optimizations ?? (processedData.optimizations = {}); const collectDefs = (defs, domains) => { return defs.map((def) => { const domain = domains.get(def); const bandedDomain = bandedDomains.get(def); const isBanded = domain instanceof BandedDomain; let reason; if (!isBanded) { reason = def.valueType === "category" ? "discrete domain" : "not configured"; } let stats; if (isBanded && bandedDomain) { const domainStats = preScanDomainStats.get(bandedDomain) ?? bandedDomain.getStats(); const scanRatio = domainStats.bandCount > 0 ? domainStats.dirtyBandCount / domainStats.bandCount : 0; stats = { totalBands: domainStats.bandCount, dirtyBands: domainStats.dirtyBandCount, dataSize: domainStats.dataSize, scanRatio }; } return { property: String(def.property), applied: isBanded, reason, stats }; }); }; const keyDefs = collectDefs(this.ctx.keys, keyDomains); const valueDefs = collectDefs(this.ctx.values, valueDomains); processedData.optimizations.domainBanding = { keyDefs, valueDefs }; } }; // packages/ag-charts-community/src/chart/data/data-model/extraction/dataExtractor.ts function createKeyTracker() { return { lastValue: void 0, sortOrder: 0, isUnique: true, isOrdered: true }; } function updateKeyTracker(tracker, value) { const numericValue = typeof value === "number" ? value : value?.valueOf?.(); if (typeof numericValue !== "number" || !Number.isFinite(numericValue)) return; if (tracker.lastValue === void 0) { tracker.lastValue = numericValue; return; } const diff2 = numericValue - tracker.lastValue; if (diff2 === 0) { tracker.isUnique = false; } else if (tracker.isOrdered) { const direction = diff2 > 0 ? 1 : -1; if (tracker.sortOrder === 0) { tracker.sortOrder = direction; } else if (tracker.sortOrder !== direction) { tracker.isOrdered = false; } } tracker.lastValue = numericValue; } function trackerToSortOrderEntry(tracker) { return { sortOrder: tracker.isOrdered && tracker.sortOrder !== 0 ? tracker.sortOrder : void 0, isUnique: tracker.isUnique, isDirty: false }; } var DataExtractor = class { constructor(ctx, domainManager) { this.ctx = ctx; this.domainManager = domainManager; this.markScopeDatumInvalid = function(scopes, data, datumIndex, invalidData, invalidDataCount) { for (const scope of scopes) { if (!invalidData.has(scope)) { invalidData.set(scope, createArray(data.length, false)); invalidDataCount.set(scope, 0); } const scopeInvalidData = invalidData.get(scope); if (!scopeInvalidData[datumIndex]) { scopeInvalidData[datumIndex] = true; invalidDataCount.set(scope, invalidDataCount.get(scope) + 1); } } }; } extractData(sources) { const { dataDomain, getProcessValue, allScopesHaveSameDefs } = this.domainManager.initDataDomainProcessor("extend"); const { keys: keyDefs, values: valueDefs } = this.ctx; const { invalidData, invalidKeys, invalidKeyCount, invalidDataCount, allKeyMappings, keySortOrders } = this.extractKeys(keyDefs, sources, getProcessValue); const { columns, columnScopes, columnNeedValueOf, partialValidDataCount, maxDataLength } = this.extractValues( invalidData, invalidDataCount, valueDefs, sources, invalidKeys, getProcessValue ); const propertyDomain = (def) => { const defDomain = dataDomain.get(def); const result = defDomain.getDomain(); if (ContinuousDomain.is(defDomain) && result[0] > result[1]) { return []; } return result; }; return { type: "ungrouped", input: { count: maxDataLength }, scopes: new Set(sources.keys()), dataSources: sources, aggregation: void 0, keys: [...allKeyMappings.values()], columns, columnScopes, columnNeedValueOf, invalidKeys, invalidKeyCount, invalidData, invalidDataCount, domain: { keys: keyDefs.map(propertyDomain), values: valueDefs.map(propertyDomain) }, defs: { allScopesHaveSameDefs, keys: keyDefs, values: valueDefs }, partialValidDataCount, time: 0, version: 0, [DOMAIN_RANGES]: /* @__PURE__ */ new Map(), [KEY_SORT_ORDERS]: keySortOrders, [COLUMN_SORT_ORDERS]: /* @__PURE__ */ new Map(), [DOMAIN_BANDS]: /* @__PURE__ */ new Map(), [REDUCER_BANDS]: /* @__PURE__ */ new Map() }; } extractKeys(keyDefs, sources, getProcessValue) { const invalidKeys = /* @__PURE__ */ new Map(); const invalidData = /* @__PURE__ */ new Map(); const invalidKeyCount = /* @__PURE__ */ new Map(); const invalidDataCount = /* @__PURE__ */ new Map(); const allKeys = /* @__PURE__ */ new Map(); const keySortOrders = /* @__PURE__ */ new Map(); let keyDefKeys; let scopeDataProcessed; const keyProcessors = keyDefs.map((def) => getProcessValue(def)); const cloneScope = (source, target) => { const sourceScope = scopeDataProcessed.get(source); keyDefKeys.set(target, keyDefKeys.get(sourceScope)); if (invalidKeys.has(sourceScope)) { invalidKeys.set(target, invalidKeys.get(sourceScope)); invalidData.set(target, invalidData.get(sourceScope)); invalidDataCount.set(target, invalidDataCount.get(sourceScope)); } }; for (const [keyDefIndex, keyDef] of keyDefs.entries()) { const { invalidValue, scopes: keyScopes } = keyDef; const processKeyValue = keyProcessors[keyDefIndex]; keyDefKeys = /* @__PURE__ */ new Map(); scopeDataProcessed = /* @__PURE__ */ new Map(); allKeys.set(keyDef, keyDefKeys); const tracker = createKeyTracker(); for (const scope of keyScopes ?? []) { const data = sources.get(scope)?.data ?? []; if (scopeDataProcessed.has(data)) { cloneScope(data, scope); continue; } const keys = []; keyDefKeys.set(scope, keys); scopeDataProcessed.set(data, scope); let invalidScopeKeys; let invalidScopeData; let missingKeys = 0; for (let datumIndex = 0; datumIndex < data.length; datumIndex++) { if (data[datumIndex] == null || typeof data[datumIndex] !== "object") { invalidScopeKeys ?? (invalidScopeKeys = createArray(data.length, false)); invalidScopeData ?? (invalidScopeData = createArray(data.length, false)); missingKeys += 1; invalidScopeKeys[datumIndex] = true; invalidScopeData[datumIndex] = true; keys.push(invalidValue); continue; } const result = processKeyValue(data[datumIndex], datumIndex, scope); if (result.valid) { keys.push(result.value); updateKeyTracker(tracker, result.value); continue; } keys.push(invalidValue); invalidScopeKeys ?? (invalidScopeKeys = createArray(data.length, false)); invalidScopeData ?? (invalidScopeData = createArray(data.length, false)); missingKeys += 1; invalidScopeKeys[datumIndex] = true; invalidScopeData[datumIndex] = true; } if (invalidScopeKeys && invalidScopeData) { invalidKeys.set(scope, invalidScopeKeys); invalidData.set(scope, invalidScopeData); invalidKeyCount.set(scope, missingKeys); invalidDataCount.set(scope, missingKeys); } } keySortOrders.set(keyDefIndex, trackerToSortOrderEntry(tracker)); } return { invalidData, invalidKeys, invalidKeyCount, invalidDataCount, allKeyMappings: allKeys, keySortOrders }; } extractValues(invalidData, invalidDataCount, valueDefs, sources, scopeInvalidKeys, getProcessValue) { let partialValidDataCount = 0; const columns = []; const allColumnScopes = []; const columnNeedValueOf = []; let maxDataLength = 0; const valueProcessors = valueDefs.map((def) => getProcessValue(def)); for (const [valueDefIndex, def] of valueDefs.entries()) { const { invalidValue } = def; const processValueForDef = valueProcessors[valueDefIndex]; const valueSources = new Set(def.scopes.map((s) => sources.get(s))); if (valueSources.size > 1) { throw new Error(`AG Charts - more than one data source for: ${JSON.stringify(def)}`); } const columnScopes = new Set(def.scopes); const columnScope = first(def.scopes); const columnSource = sources.get(columnScope)?.data ?? []; const column = new Array(); const invalidKeys = scopeInvalidKeys.get(columnScope); let needsValueOf = false; for (let datumIndex = 0; datumIndex < columnSource.length; datumIndex++) { if (columnSource[datumIndex] == null || typeof columnSource[datumIndex] !== "object") { this.markScopeDatumInvalid(def.scopes, columnSource, datumIndex, invalidData, invalidDataCount); column[datumIndex] = invalidValue; continue; } const valueDatum = columnSource[datumIndex]; const invalidKey = invalidKeys == null ? false : invalidKeys[datumIndex]; const result = processValueForDef(valueDatum, datumIndex, def.scopes); let value = result.value; if (invalidKey || !result.valid) { this.markScopeDatumInvalid(def.scopes, columnSource, datumIndex, invalidData, invalidDataCount); } if (invalidKey) { value = invalidValue; } else if (!result.valid) { partialValidDataCount += 1; value = invalidValue; } if (!needsValueOf && value != null && typeof value === "object") { needsValueOf = true; } column[datumIndex] = value; } columns.push(column); allColumnScopes.push(columnScopes); columnNeedValueOf.push(needsValueOf); maxDataLength = Math.max(maxDataLength, column.length); } return { columns, columnScopes: allColumnScopes, columnNeedValueOf, partialValidDataCount, maxDataLength }; } warnDataMissingProperties(sources) { if (sources.size === 0) return; for (const def of iterate(this.ctx.keys, this.ctx.values)) { for (const [scope, missCount] of def.missing) { if (missCount < (sources.get(scope)?.data.length ?? Infinity)) continue; const scopeHint = scope == null ? "" : ` for ${scope}`; logger_exports.warnOnce(`the key '${def.property}' was not found in any data element${scopeHint}.`); } } } }; // packages/ag-charts-community/src/chart/data/data-model/grouping/dataGrouper.ts var DataGrouper = class { constructor(ctx) { this.ctx = ctx; } /** * Groups data by keys or custom grouping function. * * GROUPED DATA STRUCTURE AND INVARIANTS: * * When groupsUnique=true (each datum has distinct keys): * - groups.length === columns[i].length for all columns * - groups[i] corresponds to datum at columns[j][i] * - All datumIndices arrays contain [0] (shared memory optimization) * - Relative indexing: datumIndices contains offsets from group start * - Absolute indexing: groupIndex + relativeDatumIndex gives column position * * When groupsUnique=false (data is aggregated): * - groups.length <= columns[i].length * - Multiple datums may map to same group * - datumIndices contain actual relative offsets * * This design optimizes memory usage for high-frequency data updates * where each datum typically has unique keys (e.g., time series data). */ groupData(data, customGroupingFn) { var _a; const { keys: dataKeys, columns: allColumns, columnScopes, invalidKeys, invalidData } = data; const allScopes = data.scopes; const resultGroups = []; const resultData = []; const groups = allScopes.size !== 1 || customGroupingFn != null ? /* @__PURE__ */ new Map() : void 0; let groupsUnique = true; let groupIndex = 0; const rawBatchCount = allScopes.size; const columnBatches = this.groupBatches( allScopes, allColumns, columnScopes, dataKeys, invalidData, invalidKeys ); const mergedBatchCount = columnBatches.length; if (this.ctx.debug?.check() && !data.optimizations) { data.optimizations = {}; } if (this.ctx.debug?.check()) { const mergeRatio = rawBatchCount > 0 ? 1 - mergedBatchCount / rawBatchCount : 0; data.optimizations.batchMerging = { originalBatchCount: rawBatchCount, mergedBatchCount, mergeRatio }; } const singleBatch = columnBatches.length === 1; const allZeroDatumIndices = Object.freeze(createArray(columnBatches[0][1].length, SHARED_ZERO_INDICES)); for (const [ scopes, scopeColumnIndexes, scopeKeys, siblingScopes, scopeInvalidData, scopeInvalidKeys ] of columnBatches) { const firstColumn = allColumns[first(scopeColumnIndexes)]; for (let datumIndex = 0; datumIndex < firstColumn.length; datumIndex++) { if (scopeInvalidKeys?.[datumIndex] === true) continue; const keys = scopeKeys.map((k) => k[datumIndex]); if (keys == null || keys.length === 0) { throw new Error("AG Charts - no keys found for scope(s): " + scopes.join(", ")); } const group = customGroupingFn?.(keys) ?? keys; const groupStr = groups == null ? void 0 : toKeyString(group); let outputGroup = groups?.get(groupStr); let currentGroup; let currentGroupIndex; let isNewGroup = false; if (outputGroup == null) { currentGroup = { keys: group, datumIndices: [], aggregation: [], validScopes: allScopes }; currentGroupIndex = groupIndex++; outputGroup = [currentGroupIndex, currentGroup]; isNewGroup = true; groups?.set(groupStr, outputGroup); resultGroups.push(currentGroup.keys); resultData.push(currentGroup); } else { [currentGroupIndex, currentGroup] = outputGroup; groupsUnique = false; } if (scopeInvalidData?.[datumIndex] === true) { if (currentGroup.validScopes === allScopes) { currentGroup.validScopes = new Set(allScopes.values()); } for (const invalidScope of siblingScopes) { currentGroup.validScopes.delete(invalidScope); } } if (isNewGroup && datumIndex === currentGroupIndex && singleBatch) { currentGroup.datumIndices = allZeroDatumIndices; } else { if (!isNewGroup && currentGroup.datumIndices === allZeroDatumIndices) { currentGroup.datumIndices = allZeroDatumIndices.map((arr) => [...arr]); } for (const columnIdx of scopeColumnIndexes) { (_a = currentGroup.datumIndices)[columnIdx] ?? (_a[columnIdx] = []); currentGroup.datumIndices[columnIdx].push(datumIndex - currentGroupIndex); } } } } return { ...data, type: "grouped", domain: { ...data.domain, groups: resultGroups }, groups: resultData, groupsUnique, optimizations: data.optimizations, [DOMAIN_BANDS]: data[DOMAIN_BANDS], [REDUCER_BANDS]: data[REDUCER_BANDS] }; } /** * Groups and merges column batches for efficient processing. * * BATCH MERGING OPTIMIZATION: * - Identifies columns that share the same data characteristics * - Merges compatible batches to reduce iteration overhead * - Can reduce processing iterations by 30-50% for multi-scope datasets * * Compatibility criteria: * - Same keys arrays (by reference) * - Same invalidity arrays (by reference) * - Scopes can be safely processed together */ groupBatches(allScopes, allColumns, columnScopes, dataKeys, invalidData, invalidKeys) { const columnBatches = []; const processedColumnIndexes = /* @__PURE__ */ new Set(); for (const scope of allScopes) { const scopeColumnIndexes = allColumns.map((_, idx) => idx).filter((idx) => !processedColumnIndexes.has(idx) && columnScopes[idx].has(scope)); if (scopeColumnIndexes.length === 0) continue; for (const idx of scopeColumnIndexes) { processedColumnIndexes.add(idx); } const siblingScopes = /* @__PURE__ */ new Set(); for (const columnIdx of scopeColumnIndexes) { for (const columnScope of columnScopes[columnIdx]) { siblingScopes.add(columnScope); } } const scopeKeys = dataKeys.map((k) => k.get(scope)).filter((k) => k != null); const scopeInvalidData = invalidData?.get(scope); const scopeInvalidKeys = invalidKeys?.get(scope); columnBatches.push([ scope, scopeColumnIndexes, scopeKeys, siblingScopes, scopeInvalidData, scopeInvalidKeys ]); } return this.mergeCompatibleBatches(columnBatches); } /** * Checks if two column batches can be merged based on shared data characteristics. */ areBatchesCompatible(batch1, batch2) { const [, , keys1, , invalidData1, invalidKeys1] = batch1; const [, , keys2, , invalidData2, invalidKeys2] = batch2; return keys1.every((k, i) => k === keys2[i]) && invalidKeys1 === invalidKeys2 && invalidData1 === invalidData2; } mergeCompatibleBatches(columnBatches) { const merged = []; const processed = /* @__PURE__ */ new Set(); for (let i = 0; i < columnBatches.length; i++) { if (processed.has(i)) continue; const [scope, columnIndexes, keys, siblingScopes, invalidData, invalidKeys] = columnBatches[i]; const mergedBatch = [ [scope], [...columnIndexes], keys, new Set(siblingScopes), invalidData, invalidKeys ]; this.findAndMergeCompatibleBatches(columnBatches, i, mergedBatch, processed); merged.push(mergedBatch); processed.add(i); } return merged; } findAndMergeCompatibleBatches(columnBatches, startIndex, mergedBatch, processed) { const firstBatch = columnBatches[startIndex]; for (let j = startIndex + 1; j < columnBatches.length; j++) { if (processed.has(j)) continue; const otherBatch = columnBatches[j]; const [scope, otherColumnIndexes, , otherSiblingScopes] = otherBatch; if (!this.areBatchesCompatible(firstBatch, otherBatch)) continue; mergedBatch[0].push(scope); mergedBatch[1].push(...otherColumnIndexes); for (const siblingScope of otherSiblingScopes) { mergedBatch[3].add(siblingScope); } processed.add(j); } } }; // packages/ag-charts-community/src/chart/data/dataChangeDescription.ts function isAppendOnly(indexMap) { return indexMap.removedIndices.size === 0 && indexMap.totalPrependCount === 0 && indexMap.totalAppendCount > 0; } function isPrependOnly(indexMap) { return indexMap.removedIndices.size === 0 && indexMap.totalAppendCount === 0 && indexMap.totalPrependCount > 0; } function hasNoRemovals(indexMap) { return indexMap.removedIndices.size === 0; } function isUpdateOnly(indexMap) { return indexMap.removedIndices.size === 0 && indexMap.totalPrependCount === 0 && indexMap.totalAppendCount === 0 && indexMap.spliceOps.every((op) => op.insertCount === 0 && op.deleteCount === 0); } function hasOnlyRemovals(indexMap) { return indexMap.removedIndices.size > 0 && indexMap.totalPrependCount === 0 && indexMap.totalAppendCount === 0 && indexMap.spliceOps.every((op) => op.insertCount === 0); } function hasContiguousRemovalsAtStart(indexMap) { const { removedIndices } = indexMap; if (removedIndices.size === 0) return false; const sorted = Array.from(removedIndices).sort((a, b) => a - b); if (sorted[0] !== 0) return false; for (let i = 0; i < sorted.length; i++) { if (sorted[i] !== i) return false; } return true; } function isRollingWindow(indexMap) { return hasContiguousRemovalsAtStart(indexMap) && indexMap.totalAppendCount > 0 && indexMap.totalPrependCount === 0; } var DataChangeDescription = class { constructor(indexMap, insertions) { this.indexMap = indexMap; this.prependValues = insertions.prependValues; this.appendValues = insertions.appendValues; this.insertionValues = insertions.insertionValues; } /** * Get all indices that were removed from the original array, sorted ascending. * * @returns Array of removed indices (e.g., [2, 5, 8]) * * @example * ```typescript * const removed = changeDesc.getRemovedIndices(); * console.log(`Removed ${removed.length} items at indices: ${removed}`); * ``` */ getRemovedIndices() { return Array.from(this.indexMap.removedIndices).sort((a, b) => a - b); } /** * Get all indices that were updated in the final array, sorted ascending. * * @returns Array of updated indices (e.g., [1, 3, 7]) * * @example * ```typescript * const updated = changeDesc.getUpdatedIndices(); * console.log(`Updated ${updated.length} items at indices: ${updated}`); * ``` */ getUpdatedIndices() { return Array.from(this.indexMap.updatedIndices).sort((a, b) => a - b); } /** * Iterate over preserved elements, mapping source index to destination index. * Only calls callback for elements that were NOT removed. * * **Use this for:** * - Tracking which elements moved (when sourceIndex !== destIndex) * - Generating diff metadata (added/removed/moved items) * - Understanding index shifts caused by prepends/removes * * @param callback - Called for each preserved element with (sourceIndex, destIndex) * * @example Detecting moved items * ```typescript * const movedItems = new Set(); * changeDesc.forEachPreservedIndex((srcIdx, destIdx) => { * if (srcIdx !== destIdx) { * movedItems.add(destIdx); * } * }); * ``` */ forEachPreservedIndex(callback2) { const { originalLength, removedIndices, totalPrependCount } = this.indexMap; let removalsBeforeCount = 0; const sortedRemovals = Array.from(removedIndices).sort((a, b) => a - b); let removalIdx = 0; for (let srcIdx = 0; srcIdx < originalLength; srcIdx++) { while (removalIdx < sortedRemovals.length && sortedRemovals[removalIdx] < srcIdx) { removalsBeforeCount++; removalIdx++; } if (!removedIndices.has(srcIdx)) { const destIdx = srcIdx + totalPrependCount - removalsBeforeCount; callback2(srcIdx, destIdx); } } } /** * Get the values that were prepended to the beginning of the array. * * These values are stored during change description construction and can be used * to avoid reprocessing prepended data. * * @returns Array of prepended values in order * * @example Processing prepended data * ```typescript * const prependedData = changeDesc.getPrependedValues(); * for (const row of prependedData) { * processRow(row); * } * ``` */ getPrependedValues() { return this.prependValues; } /** * Get the values that were appended to the end of the array. * * These values are stored during change description construction and can be used * to avoid reprocessing appended data. * * @returns Array of appended values in order * * @example Processing appended data * ```typescript * const appendedData = changeDesc.getAppendedValues(); * for (const row of appendedData) { * processRow(row); * } * ``` */ getAppendedValues() { return this.appendValues; } /** * Get the values that were inserted at arbitrary indices. * * These values are stored during change description construction and can be used * to avoid reprocessing inserted data. * * @returns Array of insertion values in the order they appear in splice operations * * @example Processing inserted data * ```typescript * const insertedData = changeDesc.getInsertionValues(); * for (const row of insertedData) { * processRow(row); * } * ``` */ getInsertionValues() { return this.insertionValues; } /** * Applies the transformation to an array in-place using native Array operations. * This is a zero-copy operation that mutates the array directly. * * **Use this for:** * - Transforming processed data arrays (keys, columns, invalidity) * - Applying prepends, removals, and appends in a single pass * - Maintaining synchronization between data and processed arrays * * **How it works:** * 1. Applies splice operations in order (prepends, removals, appends) * 2. Calls processInsertion callback for each inserted element * 3. Mutates the array in-place for zero-copy efficiency * * @param array - The array to transform in-place (will be mutated) * @param processInsertion - Callback to generate values for inserted indices * * @example Transforming a column array * ```typescript * // Transform processed column to match new data layout * const insertionCache = new Map(); // Pre-computed processed values * changeDesc.applyToArray(columnArray, (destIndex) => { * return insertionCache.get(destIndex) ?? defaultValue; * }); * ``` * * @example Transforming an invalidity array * ```typescript * // Transform invalidity flags to match new data * changeDesc.applyToArray(invalidityArray, (destIndex) => { * const cached = insertionCache.get(destIndex); * return cached?.hasInvalidKey ?? false; * }); * ``` */ applyToArray(array2, processInsertion, onRemove) { const { spliceOps, finalLength, originalLength } = this.indexMap; if (originalLength === finalLength && spliceOps.length === 0) { return; } for (const op of spliceOps) { const insertElements = op.insertCount > 0 ? Array.from({ length: op.insertCount }, function processOpInsertion(_, j) { return processInsertion(op.index + j); }) : []; const removed = array2.splice(op.index, op.deleteCount, ...insertElements); if (onRemove && removed.length > 0) { onRemove(removed, op); } } if (array2.length !== finalLength) { array2.length = finalLength; } } }; // packages/ag-charts-community/src/chart/data/data-model/reducers/bandedReducer.ts var BandedReducer = class extends BandedStructure { constructor(config = {}) { super(config); this.lastDirtyBandCount = 0; this.lastScanRatio = 0; this.statsCaptured = false; } /** * Creates a new reducer band with undefined cached result. */ createBand(startIndex, endIndex) { return { startIndex, endIndex, cachedResult: void 0, isDirty: true }; } /** * Initializes bands and resets stats capture flag. */ initializeBands(dataSize) { super.initializeBands(dataSize); this.statsCaptured = false; } /** * Gets the array of bands for direct access. * For production code, prefer using evaluateFromData() and getResult() for symmetry with BandedDomain. * This method is primarily for testing and debugging band structure. */ getBands() { return this.bands; } /** * Evaluates a reducer across all bands, reusing cached results for clean bands. * Symmetrical to BandedDomain.extendBandsFromData(). * * @param def Reducer definition with reducer function, initial value, and overlap settings * @param context Reducer context containing raw data and key columns * @param reuseCleanBands Whether to reuse cached results for clean bands (default: false) */ evaluateFromData(def, context, reuseCleanBands = false) { const reducerFn = def.reducer(); for (const band of this.bands) { if (reuseCleanBands && !band.isDirty) { continue; } const startIndex = def.needsOverlap && band.startIndex > 0 ? Math.max(0, band.startIndex - 1) : band.startIndex; const result = this.evaluateRange(def, reducerFn, context, startIndex, band.endIndex); band.cachedResult = result; band.isDirty = false; } } /** * Combines all band results to get the final aggregated value. * Symmetrical to BandedDomain.getDomain(). * * @param def Reducer definition with combineResults function * @returns Combined result from all bands */ getResult(def) { const bandResults = this.bands.map((band) => band.cachedResult); return def.combineResults(bandResults); } /** * Evaluates a reducer over a specific range of data indices. * Symmetrical to BandedDomain's band scanning loop in extendBandsFromData(). * * @param def Reducer definition with initial value * @param reducer Reducer function to apply * @param context Reducer context with data and keys * @param startIndex Starting index (inclusive) * @param endIndex Ending index (exclusive) * @returns Accumulated reducer result for the range */ evaluateRange(def, reducer, context, startIndex, endIndex) { let accValue = def.initialValue; const { keyColumns, keysParam, rawData } = context; const clampedEnd = Math.min(endIndex, rawData.length); for (let datumIndex = startIndex; datumIndex < clampedEnd; datumIndex += 1) { for (let keyIdx = 0; keyIdx < keysParam.length; keyIdx++) { keysParam[keyIdx] = keyColumns[keyIdx]?.[datumIndex]; } accValue = reducer(accValue, keysParam); } return accValue; } /** * Capture the current dirty state before processing. * Call this before marking bands as clean to preserve stats for reporting. */ captureStatsBeforeProcessing() { const dirtyBands = this.bands.filter((band) => band.isDirty); const dirtySpan = dirtyBands.reduce((sum, band) => sum + (band.endIndex - band.startIndex), 0); this.lastDirtyBandCount = dirtyBands.length; this.lastScanRatio = this.dataSize > 0 ? dirtySpan / this.dataSize : 0; this.statsCaptured = true; } /** * Returns reducer-specific statistics including cache hits and scan ratio. */ getStats() { const cleanBands = this.bands.filter((band) => !band.isDirty && band.cachedResult !== void 0); let dirtyBands; let scanRatio; if (this.statsCaptured) { dirtyBands = this.lastDirtyBandCount; scanRatio = this.lastScanRatio; } else { const currentDirtyBands = this.bands.filter((band) => band.isDirty); const dirtySpan = currentDirtyBands.reduce((sum, band) => sum + (band.endIndex - band.startIndex), 0); dirtyBands = currentDirtyBands.length; scanRatio = this.dataSize > 0 ? dirtySpan / this.dataSize : 0; } return { totalBands: this.bands.length, dirtyBands, dataSize: this.dataSize, scanRatio, cacheHits: cleanBands.length }; } }; // packages/ag-charts-community/src/chart/data/data-model/reducers/reducerManager.ts var ReducerManager = class { constructor(bandingConfig = {}) { this.bandingConfig = bandingConfig; } /** * Evaluates a reducer over a specific range of data indices. * Used for non-banded reducer evaluation (fallback path). * * This is a static utility method for cases where banding is not applicable * (e.g., grouped data, small datasets, reducers that don't support banding). * * @param def Reducer definition with initial value * @param reducer Reducer function to apply * @param context Reducer context with data and keys * @param startIndex Starting index (inclusive) * @param endIndex Ending index (exclusive) * @returns Accumulated reducer result for the range */ static evaluateRange(def, reducer, context, startIndex, endIndex) { let accValue = def.initialValue; const { keyColumns, keysParam, rawData } = context; const clampedEnd = Math.min(endIndex, rawData.length); for (let datumIndex = startIndex; datumIndex < clampedEnd; datumIndex += 1) { for (let keyIdx = 0; keyIdx < keysParam.length; keyIdx++) { keysParam[keyIdx] = keyColumns[keyIdx]?.[datumIndex]; } accValue = reducer(accValue, keysParam); } return accValue; } /** * Evaluates a banded reducer and returns the aggregated result. * Symmetrical to DomainManager.recomputeDomains(). * * @param def Reducer definition * @param processedData Processed data containing raw data and keys * @param options Evaluation options including band reuse settings * @returns Aggregated reducer result */ evaluate(def, processedData, options = {}) { var _a; const context = this.createContext(def, processedData); if (!context) { return void 0; } processedData[_a = REDUCER_BANDS] ?? (processedData[_a] = /* @__PURE__ */ new Map()); const reducerBands = processedData[REDUCER_BANDS]; const property = def.property; let bandManager = reducerBands.get(property); if (!bandManager) { bandManager = new BandedReducer(this.bandingConfig); reducerBands.set(property, bandManager); } if (bandManager.getBandCount() === 0) { bandManager.initializeBands(context.rawData.length); } options.beforeEvaluate?.(bandManager, context); bandManager.captureStatsBeforeProcessing(); bandManager.evaluateFromData(def, context, options.reuseCleanBands ?? false); return bandManager.getResult(def); } /** * Applies index map transformations to all reducer bands. * Symmetrical to DomainManager's band update logic. * * @param processedData Processed data containing reducer bands * @param indexMap Index map with splice operations and updated indices */ applyIndexMap(processedData, indexMap) { const reducerBands = processedData[REDUCER_BANDS]; if (!reducerBands) return; for (const bandManager of reducerBands.values()) { bandManager.applyIndexMap(indexMap); } } /** * Creates a reducer context from processed data. * Extracts raw data and key columns for the appropriate scope. * Symmetrical to domain context creation in DomainManager. * * @param def Reducer definition * @param processedData Processed data * @returns Reducer context with scope information, or undefined if not applicable */ createContext(def, processedData) { if (processedData.type !== "ungrouped") { return void 0; } const scopeId = isScoped(def) ? def.scopes[0] : first(processedData.dataSources.keys()); if (scopeId == null) { return void 0; } const rawData = processedData.dataSources.get(scopeId)?.data ?? []; const keyColumns = processedData.keys.map((column) => column.get(scopeId)).filter((column) => column != null); const keysParam = keyColumns.map(() => void 0); return { scopeId, rawData, keyColumns, keysParam }; } }; // packages/ag-charts-community/src/chart/data/data-model/incremental/incrementalProcessor.ts var IncrementalProcessor = class { constructor(ctx, reducerManager) { this.ctx = ctx; this.reducerManager = reducerManager; } /** * Checks if incremental reprocessing is supported for the given data configuration. */ isReprocessingSupported(processedData) { if (processedData.type === "grouped") { if (!processedData.groupsUnique) return false; const uniqueDataSets = this.getUniqueDataSets(processedData); if (uniqueDataSets.size !== 1) return false; const scope = first(processedData.scopes); const invalidKeys = processedData.invalidKeys?.get(scope); if (invalidKeys?.some((invalid) => invalid)) return false; } if (this.ctx.aggregates.length > 0) return false; const hasUnsupportedReducers = this.ctx.reducers.some( (reducer) => reducer.supportsBanding !== true || typeof reducer.combineResults !== "function" ); if (hasUnsupportedReducers) return false; const hasUnsupportedProcessors = this.ctx.processors.some( (processor) => processor.incrementalCalculate === void 0 ); if (hasUnsupportedProcessors) return false; if (this.ctx.propertyProcessors.length > 0) return false; return this.ctx.groupProcessors.every((p) => p.supportsReprocessing ?? false); } /** * Performs incremental reprocessing of data based on change descriptions. */ reprocessData(processedData, dataSets, getProcessValue, reprocessGroupProcessorsFn, recomputeDomainsFn, collectOptimizationMetadataFn) { const start2 = performance.now(); const scopeChanges = this.collectScopeChanges(processedData, dataSets); if (scopeChanges.size === 0) { return processedData; } this.commitPendingTransactions(processedData); const keyProcessors = this.buildDefinitionProcessors(this.ctx.keys, getProcessValue); const valueProcessors = this.buildDefinitionProcessors(this.ctx.values, getProcessValue); const insertionCaches = this.processAllInsertions(processedData, scopeChanges, keyProcessors, valueProcessors); this.processAllUpdates(processedData, scopeChanges, keyProcessors, valueProcessors, insertionCaches); this.updateBandsForChanges(processedData, scopeChanges); const removedKeys = this.transformKeysArrays(processedData, scopeChanges, insertionCaches); this.transformColumnsArrays(processedData, scopeChanges, insertionCaches); this.transformInvalidityArrays(processedData, scopeChanges, insertionCaches); this.reprocessBandedReducers(processedData, scopeChanges); if (processedData.type === "grouped") { this.transformGroupsArray(processedData, scopeChanges, insertionCaches); if (this.ctx.groupProcessors.length > 0) { reprocessGroupProcessorsFn(processedData, scopeChanges); } } this.invalidateSortOrdersForChanges(processedData, scopeChanges); recomputeDomainsFn(processedData); this.reprocessProcessors(processedData); if (processedData.reduced?.diff != null && scopeChanges.size > 0) { this.generateDiffMetadata(processedData, scopeChanges, removedKeys); } this.updateProcessedDataMetadata(processedData); const end3 = performance.now(); processedData.time = end3 - start2; processedData.version += 1; collectOptimizationMetadataFn(processedData, "reprocess"); const uniqueChanges = uniqueChangeDescriptions(scopeChanges); processedData.changeDescription = uniqueChanges.size === 1 ? uniqueChanges.values().next().value : void 0; return processedData; } /** * Updates banded domains based on pending changes. * * BANDING OPTIMIZATION: * - Divides large datasets into bands (default ~100 bands) * - Tracks which bands are "dirty" and need recalculation * - During updates, only dirty bands are reprocessed * - Significantly reduces domain calculation overhead for large datasets * * Example: 1M data points → 100 bands of 10K points each * Adding 1000 points only dirties 1-2 bands instead of scanning all 1M points * * This optimizes domain recalculation by only marking affected bands as dirty. * Deduplicates change descriptions to avoid processing the same changes multiple times * when multiple scopes share the same DataSet. */ updateBandsForChanges(processedData, scopeChanges) { const bandedDomains = processedData[DOMAIN_BANDS]; if (bandedDomains.size === 0) return; const processedChangeDescs = uniqueChangeDescriptions(scopeChanges); for (const changeDesc of processedChangeDescs) { const { indexMap } = changeDesc; for (const domain of bandedDomains.values()) { domain.applyIndexMap(indexMap); } } } reprocessBandedReducers(processedData, scopeChanges) { if (processedData.type !== "ungrouped") return; const bandedReducers = this.ctx.reducers.filter( (reducer) => reducer.supportsBanding && typeof reducer.combineResults === "function" ); if (bandedReducers.length === 0) return; processedData.reduced ?? (processedData.reduced = {}); for (const def of bandedReducers) { const result = this.reducerManager.evaluate(def, processedData, { reuseCleanBands: true, beforeEvaluate: (bandManager, context) => { if (!context.scopeId) return; const changeDesc = scopeChanges.get(context.scopeId); if (changeDesc) { bandManager.applyIndexMap(changeDesc.indexMap); } } }); if (result !== void 0) { processedData.reduced[def.property] = result; } } } /** * Collects change descriptions from all DataSets before committing. */ collectScopeChanges(processedData, dataSets) { const scopeChanges = /* @__PURE__ */ new Map(); for (const [scopeId, dataSet] of processedData.dataSources) { const changeDesc = dataSets?.get(dataSet) ?? dataSet.getChangeDescription(); if (changeDesc) { scopeChanges.set(scopeId, changeDesc); } } return scopeChanges; } /** * Commits all pending transactions to the data arrays. * Deduplicates DataSets to avoid committing the same DataSet multiple times * when multiple scopes share the same DataSet. */ commitPendingTransactions(processedData) { const uniqueDataSets = this.getUniqueDataSets(processedData); for (const dataSet of uniqueDataSets) { dataSet.commitPendingTransactions(); } } buildDefinitionProcessors(defs, getProcessValue) { return defs.map((def, index) => ({ def, index, processValue: getProcessValue(def) })); } /** * Pre-processes all insertions once per scope to avoid redundant computation. */ processAllInsertions(processedData, scopeChanges, keyProcessors, valueProcessors) { const insertionCaches = /* @__PURE__ */ new Map(); for (const [scope, changeDesc] of scopeChanges) { const dataSet = processedData.dataSources.get(scope); if (!dataSet) continue; const cache = this.processInsertionsOnce(scope, changeDesc, dataSet, keyProcessors, valueProcessors); insertionCaches.set(scope, cache); } return insertionCaches; } /** * Processes all updated items once per scope, adding them to the insertion cache. * This ensures updated values are available when transforming columns/keys arrays. */ processAllUpdates(processedData, scopeChanges, keyProcessors, valueProcessors, insertionCaches) { for (const [scope, changeDesc] of scopeChanges) { const dataSet = processedData.dataSources.get(scope); if (!dataSet) continue; const updatedIndices = changeDesc.getUpdatedIndices(); if (updatedIndices.length === 0) continue; let cache = insertionCaches.get(scope); if (!cache) { cache = /* @__PURE__ */ new Map(); insertionCaches.set(scope, cache); } for (const destIndex of updatedIndices) { if (destIndex < 0 || destIndex >= dataSet.data.length) { continue; } const processed = this.processDatum(dataSet, destIndex, scope, keyProcessors, valueProcessors); if (processed) { cache.set(destIndex, processed); } } } } /** * Processes all insertions for a given scope once, caching the results. * Returns a map from ADJUSTED destIndex to processed values for all keys and values. * The adjusted destIndex accounts for out-of-bounds insertions that need to be shifted. */ processInsertionsOnce(scope, changeDesc, dataSet, keyProcessors, valueProcessors) { const cache = /* @__PURE__ */ new Map(); const { finalLength } = changeDesc.indexMap; for (const op of changeDesc.indexMap.spliceOps) { if (op.insertCount <= 0) continue; for (let i = 0; i < op.insertCount; i++) { const destIndex = op.index + i; if (destIndex < 0 || destIndex >= finalLength) { continue; } const processed = this.processDatum(dataSet, destIndex, scope, keyProcessors, valueProcessors); if (processed) { cache.set(destIndex, processed); } } } return cache; } /** * Processes a single datum for the given scope, returning cached key/value results. * Shared between insert and update paths to keep behaviour consistent. */ processDatum(dataSet, destIndex, scope, keyProcessors, valueProcessors) { const datum = dataSet.data[destIndex]; const keys = /* @__PURE__ */ new Map(); const values = /* @__PURE__ */ new Map(); let hasInvalidKey = false; let hasInvalidValue = false; if (datum == null || typeof datum !== "object") { hasInvalidKey = true; hasInvalidValue = true; } else { for (const { index: keyDefIndex, def: keyDef, processValue: processKeyValue } of keyProcessors) { if (!keyDef.scopes?.includes(scope)) continue; const result = processKeyValue(datum, destIndex, scope); keys.set(keyDefIndex, { value: result.value, valid: result.valid }); if (!result.valid) { hasInvalidKey = true; } } for (const { index: valueDefIndex, def: valueDef, processValue: processValueForDef } of valueProcessors) { if (!valueDef.scopes?.includes(scope)) continue; const result = processValueForDef(datum, destIndex, valueDef.scopes); values.set(valueDefIndex, { value: result.value, valid: result.valid }); if (!result.valid) { hasInvalidValue = true; } } } return { keys, values, hasInvalidKey, hasInvalidValue }; } /** * Generic utility to transform arrays using cached insertion results. * This reduces duplication across transformKeysArrays, transformColumnsArrays, and transformInvalidityArrays. */ transformArraysWithCache(definitions, scopeChanges, insertionCaches, getArray, getScopes, extractValue) { for (const [defIndex, def] of definitions.entries()) { for (const scope of getScopes(def)) { const changeDesc = scopeChanges.get(scope); if (!changeDesc) continue; const array2 = getArray(defIndex, scope); if (!array2) continue; const insertionCache = insertionCaches.get(scope); this.applyChangeDescWithCache( changeDesc, array2, insertionCache, (cached, _destIndex) => extractValue(cached, def, defIndex) ); } } } /** * Transforms keys arrays using cached insertion results. */ transformKeysArrays(processedData, scopeChanges, insertionCaches) { const removedByScope = /* @__PURE__ */ new Map(); const ensureRemovedMetadata = (scope) => { let metadata = removedByScope.get(scope); if (!metadata) { metadata = { tuples: [] }; removedByScope.set(scope, metadata); } return metadata; }; const processedArrays = /* @__PURE__ */ new WeakSet(); for (const [defIndex, def] of this.ctx.keys.entries()) { for (const scope of def.scopes ?? []) { const changeDesc = scopeChanges.get(scope); if (!changeDesc) continue; const keysArray = processedData.keys[defIndex]?.get(scope); if (!keysArray) continue; if (processedArrays.has(keysArray)) { const sourceScope = Array.from(processedData.keys[defIndex].entries()).find( ([_, arr]) => arr === keysArray )?.[0]; if (sourceScope && sourceScope !== scope && removedByScope.has(sourceScope)) { removedByScope.set(scope, removedByScope.get(sourceScope)); } continue; } processedArrays.add(keysArray); const insertionCache = insertionCaches.get(scope); const removedMetadata = ensureRemovedMetadata(scope); let removalCursor = 0; this.applyChangeDescWithCache( changeDesc, keysArray, insertionCache, (cached) => { const keyResult = cached?.keys.get(defIndex); return keyResult?.valid ? keyResult.value : def.invalidValue; }, (removedValues) => { for (const value of removedValues) { if (!removedMetadata.tuples[removalCursor]) { removedMetadata.tuples[removalCursor] = new Array(this.ctx.keys.length); } removedMetadata.tuples[removalCursor][defIndex] = value; removalCursor += 1; } } ); } } const removedKeyStrings = /* @__PURE__ */ new Map(); for (const [scope, { tuples }] of removedByScope) { if (tuples.length === 0) continue; const scopeSet = /* @__PURE__ */ new Set(); for (const tuple of tuples) { const keyValues = []; for (const [defIndex, value] of tuple.entries()) { const keyDef = this.ctx.keys[defIndex]; if (!keyDef.scopes?.includes(scope)) continue; keyValues.push(value); } if (keyValues.length > 0) { scopeSet.add(toKeyString(keyValues)); } } removedKeyStrings.set(scope, scopeSet); } return removedKeyStrings; } /** * Transforms columns arrays using cached insertion results. */ transformColumnsArrays(processedData, scopeChanges, insertionCaches) { this.transformArraysWithCache( this.ctx.values, scopeChanges, insertionCaches, (defIndex) => processedData.columns[defIndex], (def) => [first(def.scopes)], (cached, def, defIndex) => { if (cached) { if (cached.hasInvalidKey) { return def.invalidValue; } const valueResult = cached.values.get(defIndex); return valueResult?.valid ? valueResult.value : def.invalidValue; } return def.invalidValue; } ); } /** * Helper to transform a scope-based invalidity map. */ transformInvalidityMap(invalidityMap, scopeChanges, insertionCaches, extractValue) { const processedArrays = /* @__PURE__ */ new Set(); for (const [scope, changeDesc] of scopeChanges) { let array2 = invalidityMap.get(scope); if (!array2) { const insertionCache2 = insertionCaches.get(scope); const hasAnyInvalid = insertionCache2 && Array.from(insertionCache2.values()).some(extractValue); if (hasAnyInvalid) { array2 = createArray(changeDesc.indexMap.originalLength, false); invalidityMap.set(scope, array2); } else { continue; } } if (processedArrays.has(array2)) continue; processedArrays.add(array2); const insertionCache = insertionCaches.get(scope); this.applyChangeDescWithCache( changeDesc, array2, insertionCache, (cached, _destIndex) => extractValue(cached) ); } } /** * Transforms invalidity arrays using cached insertion results. */ transformInvalidityArrays(processedData, scopeChanges, insertionCaches) { if (processedData.invalidKeys) { this.transformInvalidityMap( processedData.invalidKeys, scopeChanges, insertionCaches, (cached) => cached?.hasInvalidKey ?? false ); } if (processedData.invalidData) { this.transformInvalidityMap( processedData.invalidData, scopeChanges, insertionCaches, (cached) => cached ? cached.hasInvalidKey || cached.hasInvalidValue : false ); } } /** * Applies a change description to an array using the provided cache-aware extractor. * Shared by array transformation helpers to keep update logic consistent. */ applyChangeDescWithCache(changeDesc, target, insertionCache, extractValue, onRemove) { changeDesc.applyToArray( target, (destIndex) => { const cached = insertionCache?.get(destIndex); return extractValue(cached, destIndex); }, onRemove ); const updatedIndices = changeDesc.getUpdatedIndices(); if (updatedIndices.length === 0) return; for (const destIndex of updatedIndices) { if (destIndex < 0 || destIndex >= target.length) { continue; } const cached = insertionCache?.get(destIndex); target[destIndex] = extractValue(cached, destIndex); } } /** * Transforms the groups array for grouped data during reprocessing. * Only called when groupsUnique=true and no invalid keys exist. * * This maintains the invariant: groups[i] corresponds to datum at columns[i]. */ transformGroupsArray(processedData, scopeChanges, insertionCaches) { const scope = first(processedData.scopes); const changeDesc = scopeChanges.get(scope); if (!changeDesc) return; const insertionCache = insertionCaches.get(scope); for (const [, cached] of insertionCache ?? []) { if (cached.hasInvalidKey) { throw new Error( "AG Charts - reprocessing grouped data with invalid keys not supported. This typically indicates a data quality issue that requires full reprocessing." ); } } changeDesc.applyToArray(processedData.groups, (destIndex) => { return this.createDataGroupForInsertion(destIndex, processedData, scope, insertionCache); }); const updatedIndices = changeDesc.getUpdatedIndices(); if (updatedIndices.length > 0) { for (const destIndex of updatedIndices) { if (destIndex < 0 || destIndex >= processedData.groups.length) { continue; } processedData.groups[destIndex] = this.createDataGroupForInsertion( destIndex, processedData, scope, insertionCache ); } } } /** * Creates a new DataGroup for an inserted datum during reprocessing. * * When groupsUnique=true and no invalid keys exist, each datum has: * - A unique set of keys * - datumIndices[columnIdx] = [0] (relative offset is always 0) * - All scopes are valid initially (unless invalid value detected) */ createDataGroupForInsertion(datumIndex, processedData, scope, insertionCache) { const keys = []; for (const keysMap of processedData.keys) { const scopeKeys = keysMap.get(scope); if (scopeKeys) { keys.push(scopeKeys[datumIndex]); } } const firstGroup = processedData.groups[0]; const allZeroDatumIndices = () => Object.freeze(createArray(processedData.columnScopes.length, SHARED_ZERO_INDICES)); const datumIndices = firstGroup?.datumIndices ?? allZeroDatumIndices(); const cached = insertionCache?.get(datumIndex); const hasInvalidValue = cached?.hasInvalidValue ?? false; let validScopes; if (hasInvalidValue) { validScopes = new Set(processedData.scopes); validScopes.delete(scope); } else { validScopes = processedData.scopes; } return { keys, datumIndices, aggregation: [], // Empty - we don't support aggregates in reprocessing yet validScopes }; } /** * Generates diff metadata for animations and incremental rendering. * This is an opt-in feature - only runs if diff tracking is already initialized. */ generateDiffMetadata(processedData, scopeChanges, removedKeys) { const getKeyString = (scope, datumIndex) => { const keys = []; for (const keysMap of processedData.keys) { const scopeKeys = keysMap.get(scope); if (!scopeKeys) return void 0; keys.push(scopeKeys[datumIndex]); } return keys.length > 0 ? toKeyString(keys) : void 0; }; for (const [scope, changeDesc] of scopeChanges) { const diff2 = { changed: true, added: /* @__PURE__ */ new Set(), removed: removedKeys.get(scope) ?? /* @__PURE__ */ new Set(), updated: /* @__PURE__ */ new Set(), moved: /* @__PURE__ */ new Set() }; for (const op of changeDesc.indexMap.spliceOps) { if (op.insertCount > 0) { for (let i = 0; i < op.insertCount; i++) { const datumIndex = op.index + i; const keyStr = getKeyString(scope, datumIndex); if (keyStr) { diff2.added.add(keyStr); } } } } const { originalLength, totalPrependCount } = changeDesc.indexMap; if (isAppendOnly(changeDesc.indexMap)) { } else if (isPrependOnly(changeDesc.indexMap) && originalLength > 0) { for (let destIndex = totalPrependCount; destIndex < totalPrependCount + originalLength; destIndex++) { const keyStr = getKeyString(scope, destIndex); if (keyStr) diff2.moved.add(keyStr); } } else if (hasNoRemovals(changeDesc.indexMap) && totalPrependCount > 0) { for (let sourceIndex = 0; sourceIndex < originalLength; sourceIndex++) { const destIndex = sourceIndex + totalPrependCount; const keyStr = getKeyString(scope, destIndex); if (keyStr) diff2.moved.add(keyStr); } } else { changeDesc.forEachPreservedIndex((sourceIndex, destIndex) => { if (sourceIndex !== destIndex) { const keyStr = getKeyString(scope, destIndex); if (keyStr) diff2.moved.add(keyStr); } }); } processedData.reduced.diff[scope] = diff2; } } /** * Updates metadata after array transformations. * Uses intelligent cache management based on change patterns. */ updateProcessedDataMetadata(processedData) { let maxDataLength = 0; for (const dataSet of processedData.dataSources.values()) { maxDataLength = Math.max(maxDataLength, dataSet.data.length); } processedData.input.count = maxDataLength; let partialValidDataCount = 0; for (const [scope, invalidData] of processedData.invalidData ?? /* @__PURE__ */ new Map()) { const invalidKeys = processedData.invalidKeys?.get(scope); for (let i = 0; i < invalidData.length; i++) { if (invalidData[i] && !invalidKeys?.[i]) { partialValidDataCount += 1; } } } processedData.partialValidDataCount = partialValidDataCount; this.recountInvalid(processedData.invalidKeys, processedData.invalidKeyCount); this.recountInvalid(processedData.invalidData, processedData.invalidDataCount); this.invalidateCachesForChanges(processedData); } /** * Updates sort order entry incrementally for appended values. * Checks if new values maintain the existing ordering/uniqueness. */ updateSortOrderForAppend(entry, lastExistingValue, appendedValues) { if (appendedValues.length === 0) return; const toNumeric = (v) => { if (typeof v === "number") return v; if (v instanceof Date) return v.valueOf(); return void 0; }; let lastValue = toNumeric(lastExistingValue); const existingSortOrder = entry.sortOrder; for (const value of appendedValues) { const numericValue = toNumeric(value); if (numericValue === void 0) continue; if (lastValue === void 0) { lastValue = numericValue; continue; } const diff2 = numericValue - lastValue; if (diff2 === 0) { entry.isUnique = false; } if (entry.sortOrder !== void 0) { let direction = 0; if (diff2 > 0) { direction = 1; } else if (diff2 < 0) { direction = -1; } if (direction !== 0 && direction !== existingSortOrder) { entry.sortOrder = void 0; } } lastValue = numericValue; } } /** * Updates KEY_SORT_ORDERS incrementally after an append operation. */ updateKeySortOrdersForAppend(processedData, originalLength) { for (const [keyDefIndex, keysMap] of processedData.keys.entries()) { const sortOrderEntry = processedData[KEY_SORT_ORDERS].get(keyDefIndex); if (!sortOrderEntry) continue; const keysArray = first(keysMap.values()); if (!keysArray || keysArray.length <= originalLength) continue; const lastExistingValue = originalLength > 0 ? keysArray[originalLength - 1] : void 0; const appendedValues = keysArray.slice(originalLength); this.updateSortOrderForAppend(sortOrderEntry, lastExistingValue, appendedValues); } } /** * Invalidates sort order metadata BEFORE domain recomputation. * * This must be called BEFORE recomputeDomains() so that BandedDomain.setSortOrderMetadata() * receives the correct (possibly cleared) metadata. Without this, rolling window operations * would see stale sort order data and incorrectly configure sub-domains for sorted mode. * * @param anyKeyChanged - Whether any key values changed during update processing */ invalidateSortOrdersForChanges(processedData, scopeChanges) { const changeDescs = uniqueChangeDescriptions(scopeChanges); let preserveSortOrders = true; let hasAppendOnly = false; let hasRollingWindow = false; let appendOriginalLength; let rollingWindowInfo; for (const changeDesc of changeDescs) { const { indexMap } = changeDesc; if (isUpdateOnly(indexMap)) { } else if (isAppendOnly(indexMap)) { hasAppendOnly = true; appendOriginalLength = indexMap.originalLength; } else if (hasOnlyRemovals(indexMap)) { } else if (isRollingWindow(indexMap)) { hasRollingWindow = true; rollingWindowInfo = { originalLength: indexMap.originalLength, removedCount: indexMap.removedIndices.size }; } else { preserveSortOrders = false; } } if (!preserveSortOrders) { processedData[KEY_SORT_ORDERS].clear(); processedData[COLUMN_SORT_ORDERS].clear(); } else if (hasAppendOnly && appendOriginalLength !== void 0) { this.updateKeySortOrdersForAppend(processedData, appendOriginalLength); } else if (hasRollingWindow && rollingWindowInfo) { this.updateKeySortOrdersForRollingWindow(processedData, rollingWindowInfo); } } /** * Updates KEY_SORT_ORDERS incrementally after a rolling window operation. * Rolling window = contiguous removals at start + appends at end. */ updateKeySortOrdersForRollingWindow(processedData, info) { const { originalLength, removedCount } = info; for (const [keyDefIndex, keysMap] of processedData.keys.entries()) { const sortOrderEntry = processedData[KEY_SORT_ORDERS].get(keyDefIndex); if (!sortOrderEntry) continue; const keysArray = first(keysMap.values()); if (!keysArray || keysArray.length === 0) continue; const appendStartIndex = originalLength - removedCount; const lastRemainingValue = appendStartIndex > 0 ? keysArray[appendStartIndex - 1] : void 0; const appendedValues = keysArray.slice(appendStartIndex); this.updateSortOrderForAppend(sortOrderEntry, lastRemainingValue, appendedValues); } } /** * Invalidates domain range caches after domain recomputation. * * Called AFTER recomputeDomains() to mark domain ranges as dirty for lazy rebuild. * Sort order invalidation is handled separately by invalidateSortOrdersForChanges(). */ invalidateCachesForChanges(processedData) { this.markDomainRangesDirty(processedData[DOMAIN_RANGES]); } /** * Marks all RangeLookup entries as dirty for lazy rebuild. */ markDomainRangesDirty(domainRanges) { for (const rangeLookup of domainRanges.values()) { rangeLookup.isDirty = true; } } /** * Recounts invalid entries for the given map into the provided counts map. */ recountInvalid(invalidMap, counts) { if (!invalidMap || !counts) return; for (const [scope, invalidArray] of invalidMap) { const invalidCount = invalidArray.filter(Boolean).length; if (invalidCount === 0) { invalidMap.delete(scope); counts.delete(scope); } else { counts.set(scope, invalidCount); } } } /** * Recomputes processor outputs using their incrementalCalculate hook when available. * Falls back to calculate to avoid stale reducer outputs if a processor lacks the hook. */ reprocessProcessors(processedData) { if (this.ctx.processors.length === 0) return; processedData.reduced ?? (processedData.reduced = {}); for (const def of this.ctx.processors) { const previousValue = processedData.reduced[def.property]; const nextValue = def.incrementalCalculate?.(processedData, previousValue) ?? def.calculate(processedData, previousValue); processedData.reduced[def.property] = nextValue; } } /** * Helper to get unique DataSets from processed data. */ getUniqueDataSets(processedData) { return new Set(processedData.dataSources.values()); } }; // packages/ag-charts-community/src/chart/data/rangeLookup.ts var MIN = 0; var MAX = 1; var SPAN = 2; var RangeLookup = class _RangeLookup { constructor(allValues) { /** When true, the lookup needs to be rebuilt before use */ this.isDirty = false; const dataLength = allValues.reduce((acc, v) => Math.max(acc, v.length), 0); const { maxLevelSize, buffer } = _RangeLookup.createBuffer(dataLength); this.maxLevelSize = maxLevelSize; this.buffer = buffer; this.dataLength = dataLength; this.populateBuffer(allValues); } static computeMaxLevelSize(dataLength) { const sizePower = 32 - Math.clz32(dataLength); let maxLevelSize = 1 << sizePower; if (dataLength === maxLevelSize / 2) { maxLevelSize = maxLevelSize >>> 1; } return maxLevelSize; } static createBuffer(dataLength) { const maxLevelSize = _RangeLookup.computeMaxLevelSize(dataLength); const buffer = new Float64Array((maxLevelSize * 2 - 1) * 2).fill(Number.NaN); return { maxLevelSize, buffer }; } populateBuffer(allValues) { const { maxLevelSize, buffer } = this; const leafOffset = maxLevelSize - 1; for (const values of allValues) { const valuesLength = values.length; for (let i = 0; i < valuesLength; i += 1) { const value = Number(values[i]); if (value !== value) continue; const bufferOffset = leafOffset + i << 1; const prevMin = buffer[bufferOffset]; const prevMax = buffer[bufferOffset + 1]; if (prevMin !== prevMin || value < prevMin) { buffer[bufferOffset] = value; } if (prevMax !== prevMax || value > prevMax) { buffer[bufferOffset + 1] = value; } } } for (let size = maxLevelSize >>> 1; size >= 1; size >>>= 1) { const start2 = size - 1; const childStart = start2 + size << 1; let nodeOffset = start2 << 1; let leftOffset = childStart; for (let i = 0; i < size; i += 1) { const rightOffset = leftOffset + 2; const aMin = buffer[leftOffset]; const bMin = buffer[rightOffset]; buffer[nodeOffset] = bMin !== bMin || aMin < bMin ? aMin : bMin; const aMax = buffer[leftOffset + 1]; const bMax = buffer[rightOffset + 1]; buffer[nodeOffset + 1] = bMax !== bMax || aMax > bMax ? aMax : bMax; nodeOffset += 2; leftOffset += 4; } } } /** * Rebuild the segment tree with new values, reusing the buffer if possible. * Only allocates a new buffer if the data length requires a different maxLevelSize. */ rebuild(allValues) { const dataLength = allValues.reduce((acc, v) => Math.max(acc, v.length), 0); const requiredMaxLevelSize = _RangeLookup.computeMaxLevelSize(dataLength); if (requiredMaxLevelSize === this.maxLevelSize) { this.buffer.fill(Number.NaN); } else { const { maxLevelSize, buffer } = _RangeLookup.createBuffer(dataLength); this.maxLevelSize = maxLevelSize; this.buffer = buffer; } this.dataLength = dataLength; this.populateBuffer(allValues); } /** * Update values at a specific data index - O(k log n) where k is number of columns. * After updating the leaf, propagates changes up to the root. * * @param dataIndex - Index in the data array (0-based) * @param newValues - New values for this index from all columns */ updateValue(dataIndex, newValues) { const { maxLevelSize, buffer } = this; const bufferIndex = maxLevelSize + dataIndex - 1; const bufferMinIndex = Math.trunc(bufferIndex * SPAN) + MIN; const bufferMaxIndex = Math.trunc(bufferIndex * SPAN) + MAX; buffer[bufferMinIndex] = Number.NaN; buffer[bufferMaxIndex] = Number.NaN; for (const value of newValues) { const numValue = Number(value); const prevMin = buffer[bufferMinIndex]; const prevMax = buffer[bufferMaxIndex]; if (!Number.isFinite(prevMin) || numValue < prevMin) { buffer[bufferMinIndex] = numValue; } if (!Number.isFinite(prevMax) || numValue > prevMax) { buffer[bufferMaxIndex] = numValue; } } this.propagateUp(bufferIndex); } /** * Batch update multiple values - O(k log n) per update. * More efficient than individual updateValue calls when tracking dirty nodes. * * @param updates - Array of {index, values} pairs to update */ updateValues(updates) { for (const { index, values } of updates) { this.updateValue(index, values); } } /** * Propagate min/max changes from a leaf up to the root. * Each level recalculates its min/max from its children. */ propagateUp(bufferIndex) { const { buffer } = this; while (bufferIndex > 0) { const parentIndex = Math.trunc((bufferIndex - 1) / 2); const leftChild = 2 * parentIndex + 1; const rightChild = 2 * parentIndex + 2; const leftMin = buffer[Math.trunc(leftChild * SPAN) + MIN]; const leftMax = buffer[Math.trunc(leftChild * SPAN) + MAX]; const rightMin = buffer[Math.trunc(rightChild * SPAN) + MIN]; const rightMax = buffer[Math.trunc(rightChild * SPAN) + MAX]; buffer[Math.trunc(parentIndex * SPAN) + MIN] = !Number.isFinite(rightMin) || leftMin < rightMin ? leftMin : rightMin; buffer[Math.trunc(parentIndex * SPAN) + MAX] = !Number.isFinite(rightMax) || leftMax > rightMax ? leftMax : rightMax; bufferIndex = parentIndex; } } computeRangeInto(buffer, start2, end3, bufferIndex, currentStart, step, into) { const currentEnd = currentStart + step - 1; if (currentEnd < start2 || currentStart >= end3) return into; if (currentStart >= start2 && currentEnd < end3) { const min = buffer[Math.trunc(bufferIndex * SPAN) + MIN]; const max = buffer[Math.trunc(bufferIndex * SPAN) + MAX]; if (Number.isFinite(min)) into[0] = Math.min(into[0], min); if (Number.isFinite(max)) into[1] = Math.max(into[1], max); } else if (step > 1) { bufferIndex = Math.trunc(bufferIndex * 2); step = Math.trunc(step / 2); this.computeRangeInto(buffer, start2, end3, Math.trunc(bufferIndex + 1), currentStart, step, into); this.computeRangeInto(buffer, start2, end3, Math.trunc(bufferIndex + 2), currentStart + step, step, into); } return into; } rangeBetween(start2, end3, into) { const result = into ?? [0, 0]; if (start2 > end3) { result[0] = Number.NaN; result[1] = Number.NaN; return result; } const { maxLevelSize, buffer } = this; result[0] = Infinity; result[1] = -Infinity; this.computeRangeInto(buffer, start2, end3, 0, 0, maxLevelSize, result); return result; } getRange(into) { const { buffer } = this; const result = into ?? [0, 0]; result[0] = buffer[MIN]; result[1] = buffer[MAX]; return result; } get range() { const { buffer } = this; return [buffer[MIN], buffer[MAX]]; } /** The number of data elements in the segment tree */ get length() { return this.dataLength; } }; // packages/ag-charts-community/src/chart/data/sortOrder.ts function valuesSortOrder(values, needsValueOf) { const valuesLength = values.length; if (values.length <= 1) return 1; let order = 0; let v0 = values[0]; for (let i = 1; i < valuesLength; i++) { const v1 = values[i]; if (v1 == null) continue; const primitive = needsValueOf ? v1.valueOf() : v1; if (typeof primitive !== "number") return; const diff2 = Math.sign(v1 - v0); if (diff2 !== 0) { if (order !== 0 && order !== diff2) return; order = diff2; } v0 = v1; } return order === 0 ? 1 : order; } // packages/ag-charts-community/src/chart/data/data-model/utils/resolvers.ts var DataModelResolvers = class { constructor(ctx) { this.ctx = ctx; this.rangeBetweenBuffer = [0, 0]; } resolveMissingDataCount(scope) { let missingDataCount = 0; for (const value of this.ctx.values) { missingDataCount = Math.max(missingDataCount, value.missing.get(scope.id) ?? 0); } return missingDataCount; } resolveProcessedDataDefById(scope, searchId) { const def = this.ctx.scopeCache.get(scope.id)?.get(searchId); if (!def) { throw new Error(`AG Charts - didn't find property definition for [${searchId}, ${scope.id}]`); } return { index: def.index, def }; } resolveProcessedDataIndexById(scope, searchId) { return this.resolveProcessedDataDefById(scope, searchId).index; } resolveKeysById(scope, searchId, processedData) { const index = this.resolveProcessedDataIndexById(scope, searchId); const keys = processedData.keys[index]; if (keys == null) { throw new Error(`AG Charts - didn't find keys for [${searchId}, ${scope.id}]`); } return keys.get(scope.id); } hasColumnById(scope, searchId) { return this.ctx.scopeCache.get(scope.id)?.get(searchId) != null; } resolveColumnById(scope, searchId, processedData) { const index = this.resolveProcessedDataIndexById(scope, searchId); const column = processedData.columns?.[index]; if (column == null) { throw new Error(`AG Charts - didn't find column for [${searchId}, ${scope.id}]`); } return column; } resolveColumnNeedsValueOf(scope, searchId, processedData) { const index = this.resolveProcessedDataIndexById(scope, searchId); return processedData.columnNeedValueOf?.[index] ?? true; } /** * Converts a relative datum index to an absolute column index. * * INDEXING STRATEGY: * - Relative index: Offset from the start of a group (stored in datumIndices) * - Absolute index: Position in the full column array * - Conversion: absoluteIndex = groupIndex + relativeIndex * * When groupsUnique=true, relativeIndex is always 0, making this a simple * identity mapping. This optimization reduces memory usage significantly * for large datasets with unique keys. * * @param groupIndex index of the group in ProcessedData.groups * @param relativeDatumIndex relative index stored in group.datumIndices * @returns absolute index for accessing columns */ resolveAbsoluteIndex(groupIndex, relativeDatumIndex) { return groupIndex + relativeDatumIndex; } getDomain(scope, searchId, type, processedData) { const domains = this.getDomainsByType(type ?? "value", processedData); return domains?.[this.resolveProcessedDataIndexById(scope, searchId)] ?? []; } getDomainBetweenRange(scope, searchIds, [i0, i1], processedData) { const columnIndices = searchIds.map((searchId) => this.resolveProcessedDataIndexById(scope, searchId)); const dataLength = processedData.input.count; if (i0 <= 0 && i1 >= dataLength) { const domains = processedData.domain.values; let min = Infinity; let max = -Infinity; for (const columnIndex of columnIndices) { const domain = domains[columnIndex]; if (domain != null) { if (domain[0] < min) min = domain[0]; if (domain[1] > max) max = domain[1]; } } this.rangeBetweenBuffer[0] = min; this.rangeBetweenBuffer[1] = max; return this.rangeBetweenBuffer; } const cacheKey = columnIndices.join(":"); const domainRanges = processedData[DOMAIN_RANGES]; const values = columnIndices.map((columnIndex) => processedData.columns[columnIndex]); let rangeLookup = domainRanges.get(cacheKey); if (rangeLookup == null) { rangeLookup = new RangeLookup(values); domainRanges.set(cacheKey, rangeLookup); } else if (rangeLookup.isDirty) { rangeLookup.rebuild(values); rangeLookup.isDirty = false; } return rangeLookup.rangeBetween(i0, i1, this.rangeBetweenBuffer); } getSortOrder(values, index, sortOrders, needsValueOf) { const entry = sortOrders.get(index); if (entry == null || entry.isDirty) { const newEntry = { sortOrder: valuesSortOrder(values, needsValueOf) }; sortOrders.set(index, newEntry); return newEntry.sortOrder; } return entry.sortOrder; } getKeySortOrder(scope, searchId, processedData) { const columnIndex = this.resolveProcessedDataIndexById(scope, searchId); const keys = processedData.keys[columnIndex]?.get(scope.id); return keys ? this.getSortOrder(keys, columnIndex, processedData[KEY_SORT_ORDERS], true) : void 0; } getKeySortEntry(scope, searchId, processedData) { const columnIndex = this.resolveProcessedDataIndexById(scope, searchId); const entry = processedData[KEY_SORT_ORDERS].get(columnIndex); return entry?.isDirty ? void 0 : entry; } getColumnSortOrder(scope, searchId, processedData) { const columnIndex = this.resolveProcessedDataIndexById(scope, searchId); const needsValueOf = processedData.columnNeedValueOf?.[columnIndex] ?? true; return this.getSortOrder( processedData.columns[columnIndex], columnIndex, processedData[COLUMN_SORT_ORDERS], needsValueOf ); } getDomainsByType(type, processedData) { switch (type) { case "key": return processedData.domain.keys; case "value": return processedData.domain.values; case "aggregate": return processedData.domain.aggValues; case "group-value-processor": return processedData.domain.groups; default: return null; } } }; // packages/ag-charts-community/src/chart/data/data-model/utils/scopeCache.ts var ScopeCacheManager = class { constructor(ctx) { this.ctx = ctx; } processScopeCache() { this.ctx.scopeCache.clear(); for (const def of iterate(this.ctx.keys, this.ctx.values, this.ctx.aggregates)) { if (!def.idsMap) continue; for (const [scope, ids] of def.idsMap) { for (const id of ids) { if (!this.ctx.scopeCache.has(scope)) { this.ctx.scopeCache.set(scope, /* @__PURE__ */ new Map([[id, def]])); } else if (this.ctx.scopeCache.get(scope)?.has(id)) { throw new Error("duplicate definition ids on the same scope are not allowed."); } else { this.ctx.scopeCache.get(scope).set(id, def); } } } } } valueGroupIdxLookup({ matchGroupIds }) { const result = []; for (const [index, def] of this.ctx.values.entries()) { if (!matchGroupIds || def.groupId && matchGroupIds.includes(def.groupId)) { result.push(index); } } return result; } valueIdxLookup(scopes, prop) { const noScopesToMatch = scopes == null || scopes.length === 0; const propId = typeof prop === "string" ? prop : prop.id; const hasMatchingScopeId = (def) => { if (def.idsMap) { for (const [scope, ids] of def.idsMap) { if (scopes?.includes(scope) && ids.has(propId)) { return true; } } } return false; }; const result = this.ctx.values.reduce((res, def, index) => { const validDefScopes = def.scopes == null || noScopesToMatch && !def.scopes.length || def.scopes.some((s) => scopes?.includes(s)); if (validDefScopes && (def.property === propId || def.id === propId || hasMatchingScopeId(def))) { res.push(index); } return res; }, []); if (result.length === 0) { throw new Error( `AG Charts - configuration error, unknown property ${JSON.stringify(prop)} in scope(s) ${JSON.stringify( scopes )}` ); } return result; } buildAccessors(defs) { const result = /* @__PURE__ */ new Map(); if (this.ctx.suppressFieldDotNotation) { return result; } for (const def of defs) { const isPath = def.property.includes(".") || def.property.includes("["); if (!isPath) continue; const components = getPathComponents(def.property); if (components == null) { logger_exports.warnOnce("Invalid property path [%s]", def.property); continue; } const accessor = createPathAccessor(components); result.set(def.property, accessor); } return result; } }; // packages/ag-charts-community/src/chart/data/dataModel.ts var DataModel = class { constructor(opts, mode = "standalone", suppressFieldDotNotation = false, eventsHub) { this.opts = opts; this.mode = mode; this.suppressFieldDotNotation = suppressFieldDotNotation; this.eventsHub = eventsHub; this.debug = debugLogger_exports.create(true, "data-model"); this.scopeCache = /* @__PURE__ */ new Map(); this.keys = []; this.values = []; this.aggregates = []; this.groupProcessors = []; this.propertyProcessors = []; this.reducers = []; this.processors = []; let keys = true; for (const next of opts.props) { if (next.type === "key" && !keys) { throw new Error("AG Charts - internal config error: keys must come before values."); } if (next.type === "value" && keys) { keys = false; } } const verifyMatchGroupId = ({ matchGroupIds = [] }) => { for (const matchGroupId of matchGroupIds) { if (this.values.every((def) => def.groupId !== matchGroupId)) { throw new Error( `AG Charts - internal config error: matchGroupIds properties must match defined groups (${matchGroupId}).` ); } } }; const keyScopes = /* @__PURE__ */ new Set(); const valueScopes = /* @__PURE__ */ new Set(); for (const def of opts.props) { const scopes = def.type === "key" ? keyScopes : valueScopes; if (isScoped(def)) { if (def.scopes) { for (const s of def.scopes) { scopes.add(s); } } } switch (def.type) { case "key": this.keys.push({ ...def, index: this.keys.length, missing: /* @__PURE__ */ new Map() }); break; case "value": if (def.property == null) { throw new Error( `AG Charts - internal config error: no properties specified for value definitions: ${JSON.stringify( def )}` ); } this.values.push({ ...def, index: this.values.length, missing: /* @__PURE__ */ new Map() }); break; case "aggregate": verifyMatchGroupId(def); this.aggregates.push({ ...def, index: this.aggregates.length }); break; case "group-value-processor": verifyMatchGroupId(def); this.groupProcessors.push({ ...def, index: this.groupProcessors.length }); break; case "property-value-processor": this.propertyProcessors.push({ ...def, index: this.propertyProcessors.length }); break; case "reducer": this.reducers.push({ ...def, index: this.reducers.length }); break; case "processor": this.processors.push({ ...def, index: this.processors.length }); break; } } if (!!this.opts.groupByKeys || this.opts.groupByFn != null) { const ungroupedScopes = new Set(valueScopes.values()); for (const s of keyScopes) { ungroupedScopes.delete(s); } if (ungroupedScopes.size > 0) { throw new Error( `AG Charts - scopes missing key for grouping, illegal configuration: ${[...ungroupedScopes.values()]}` ); } } const ctx = { keys: this.keys, values: this.values, aggregates: this.aggregates, groupProcessors: this.groupProcessors, propertyProcessors: this.propertyProcessors, reducers: this.reducers, processors: this.processors, debug: this.debug, mode: this.mode, bandingConfig: this.opts.domainBandingConfig, suppressFieldDotNotation: this.suppressFieldDotNotation, scopeCache: this.scopeCache }; this.resolvers = new DataModelResolvers(ctx); this.scopeCacheManager = new ScopeCacheManager(ctx); this.domainInitializer = new DomainInitializer(ctx); this.domainManager = new DomainManager(ctx, this.domainInitializer, this.scopeCacheManager); this.reducerManager = new ReducerManager(this.opts.domainBandingConfig); this.dataExtractor = new DataExtractor(ctx, this.domainManager); this.dataGrouper = new DataGrouper(ctx); this.aggregator = new Aggregator(ctx, this.scopeCacheManager, this.resolvers); this.incrementalProcessor = new IncrementalProcessor(ctx, this.reducerManager); } resolveProcessedDataDefById(scope, searchId) { return this.resolvers.resolveProcessedDataDefById(scope, searchId); } resolveProcessedDataIndexById(scope, searchId) { return this.resolvers.resolveProcessedDataIndexById(scope, searchId); } resolveKeysById(scope, searchId, processedData) { return this.resolvers.resolveKeysById(scope, searchId, processedData); } hasColumnById(scope, searchId) { return this.resolvers.hasColumnById(scope, searchId); } resolveColumnById(scope, searchId, processedData) { return this.resolvers.resolveColumnById(scope, searchId, processedData); } resolveColumnNeedsValueOf(scope, searchId, processedData) { return this.resolvers.resolveColumnNeedsValueOf(scope, searchId, processedData); } resolveMissingDataCount(scope) { return this.resolvers.resolveMissingDataCount(scope); } /** * Provides a convenience iterator to iterate over all of the extract datum values in a * specific DataGroup. * * @param scope to which datums should belong * @param group containing the datums * @param processedData containing the group * @param groupIndex index of the group in processedData.groups */ *forEachDatum(scope, processedData, group, groupIndex) { const columnIndex = processedData.columnScopes.findIndex((s) => s.has(scope.id)); for (const relativeDatumIndex of group.datumIndices[columnIndex] ?? []) { const absoluteDatumIndex = this.resolvers.resolveAbsoluteIndex(groupIndex, relativeDatumIndex); yield processedData.columns[columnIndex][absoluteDatumIndex]; } } getUniqueDataSets(processedData) { return new Set(processedData.dataSources.values()); } /** * Provides a convenience iterator to iterate over all of the extracted datum values in a * GroupedData. * * @param scope to which datums should belong * @param processedData to iterate through */ *forEachGroupDatum(scope, processedData) { const columnIndex = processedData.columnScopes.findIndex((s) => s.has(scope.id)); const output = { groupIndex: 0, columnIndex }; const empty = []; for (const group of processedData.groups) { output.group = group; for (const relativeDatumIndex of group.datumIndices[columnIndex] ?? empty) { output.datumIndex = this.resolvers.resolveAbsoluteIndex(output.groupIndex, relativeDatumIndex); yield output; } output.groupIndex++; } } getDomain(scope, searchId, type, processedData) { const domain = this.resolvers.getDomain(scope, searchId, type, processedData); if (type === "key" && domain.length > 0) { const sortMetadata = this.getKeySortMetadata(scope, searchId, processedData); return { domain, sortMetadata }; } return { domain }; } getDomainBetweenRange(scope, searchIds, [i0, i1], processedData) { return this.resolvers.getDomainBetweenRange(scope, searchIds, [i0, i1], processedData); } getKeySortOrder(scope, searchId, processedData) { return this.resolvers.getKeySortOrder(scope, searchId, processedData); } getColumnSortOrder(scope, searchId, processedData) { return this.resolvers.getColumnSortOrder(scope, searchId, processedData); } /** * Get sort metadata for a key column if available. * Returns undefined if metadata is not available, is dirty, or data is unsorted. */ getKeySortMetadata(scope, searchId, processedData) { const entry = this.resolvers.getKeySortEntry(scope, searchId, processedData); if (entry?.sortOrder != null) { return { sortOrder: entry.sortOrder, isUnique: entry.isUnique }; } return void 0; } processData(sources) { const { opts: { groupByKeys, groupByFn }, aggregates, groupProcessors, reducers, processors, propertyProcessors } = this; const start2 = performance.now(); if (groupByKeys && this.keys.length === 0) { return; } let processedData = this.extractData(sources); if (groupByKeys) { processedData = this.dataGrouper.groupData(processedData); } else if (groupByFn) { processedData = this.dataGrouper.groupData(processedData, groupByFn(processedData)); } if (groupProcessors.length > 0 && processedData.type === "grouped") { this.aggregator.postProcessGroups(processedData); } if (aggregates.length > 0 && processedData.type === "ungrouped") { this.aggregator.aggregateUngroupedData(processedData); } else if (aggregates.length > 0 && processedData.type === "grouped") { this.aggregator.aggregateGroupedData(processedData); } if (propertyProcessors.length > 0) { this.postProcessProperties(processedData); } if (reducers.length > 0) { this.reduceData(processedData); } if (processors.length > 0) { this.postProcessData(processedData); } this.warnDataMissingProperties(sources); const end3 = performance.now(); processedData.time = end3 - start2; processedData.version += 1; this.collectOptimizationMetadata(processedData, "full-process"); if (this.debug.check()) { logProcessedData(processedData); } this.processScopeCache(); return processedData; } /** * Determines if incremental reprocessing is supported for the given data. * * Reprocessing is supported when: * - For ungrouped data: No aggregates, reducers, processors, or property processors * - For grouped data: Additionally requires: * - groupsUnique=true (each datum has unique keys) * - Single data source (all scopes share same DataSet) * - No invalid keys (to maintain groups.length === columns.length invariant) * - All group processors support reprocessing * * When unsupported, falls back to full reprocessing automatically. * * @returns true if incremental reprocessing can be used, false otherwise */ isReprocessingSupported(processedData) { return this.incrementalProcessor.isReprocessingSupported(processedData); } reprocessData(processedData, dataSets) { if (!this.isReprocessingSupported(processedData)) { if (this.debug.check()) { this.debug("Falling back to full reprocessing - incremental not supported for current configuration"); } const uniqueDataSets = this.getUniqueDataSets(processedData); for (const dataSet of uniqueDataSets) { dataSet.commitPendingTransactions(); } return this.processData(processedData.dataSources); } const { getProcessValue } = this.initDataDomainProcessor("skip"); return this.incrementalProcessor.reprocessData( processedData, dataSets, getProcessValue, this.reprocessGroupProcessors.bind(this), this.recomputeDomains.bind(this), this.collectOptimizationMetadata.bind(this) ); } /** * Recomputes domains from transformed arrays. * Uses BandedDomain optimization for continuous domains to avoid full rescans. */ recomputeDomains(processedData) { this.domainManager.recomputeDomains(processedData); } warnDataMissingProperties(sources) { this.dataExtractor.warnDataMissingProperties(sources); } processScopeCache() { this.scopeCacheManager.processScopeCache(); } valueGroupIdxLookup(selector) { return this.scopeCacheManager.valueGroupIdxLookup(selector); } valueIdxLookup(scopes, prop) { return this.scopeCacheManager.valueIdxLookup(scopes, prop); } extractData(sources) { return this.dataExtractor.extractData(sources); } /** * Reprocesses group processors for incremental updates. * Only processes newly inserted groups to avoid double-processing. * This is safe only when all group processors support reprocessing. * Deduplicates change descriptions to avoid processing the same groups multiple times * when multiple scopes share the same DataSet. */ reprocessGroupProcessors(processedData, scopeChanges) { const { groupProcessors } = this; const { columns } = processedData; for (const processor of groupProcessors) { if (!processor.supportsReprocessing) { throw new Error( "AG Charts - attempted to reprocess group processor that does not support reprocessing. This is an internal error that should not occur." ); } } const processedChangeDescs = uniqueChangeDescriptions(scopeChanges); for (const processor of groupProcessors) { const valueIndexes = this.valueGroupIdxLookup(processor); const adjustFn = processor.adjust()(); for (const changeDesc of processedChangeDescs) { const { indexMap } = changeDesc; for (const op of indexMap.spliceOps) { if (op.insertCount > 0) { for (let i = 0; i < op.insertCount; i++) { const groupIndex = op.index + i; const dataGroup = processedData.groups[groupIndex]; adjustFn(columns, valueIndexes, dataGroup, groupIndex); } } } } } } postProcessProperties(processedData) { for (const { adjust, property, scopes } of this.propertyProcessors) { for (const idx of this.valueIdxLookup(scopes, property)) { adjust()(processedData, idx); } } } reduceData(processedData) { processedData.reduced ?? (processedData.reduced = {}); for (const def of this.reducers) { if (this.shouldUseReducerBanding(def, processedData)) { processedData.reduced[def.property] = this.reduceWithBands( def, processedData ); } else { processedData.reduced[def.property] = this.reduceStandard( def, processedData ); } } } shouldUseReducerBanding(def, processedData) { return processedData.type === "ungrouped" && def.supportsBanding === true && typeof def.combineResults === "function"; } reduceWithBands(def, processedData) { const result = this.reducerManager.evaluate(def, processedData, { reuseCleanBands: false }); if (result === void 0) { return this.reduceStandard(def, processedData); } return result; } reduceStandard(def, processedData) { const reducer = def.reducer(); if (processedData.type === "grouped") { let accValue = def.initialValue; for (const group of processedData.groups) { accValue = reducer(accValue, group.keys); } return accValue; } const scopeId = isScoped(def) ? def.scopes[0] : first(processedData.scopes); if (scopeId == null) { return def.initialValue; } const rawData = processedData.dataSources.get(scopeId)?.data ?? []; const keyColumns = processedData.keys.map((column) => column.get(scopeId)).filter((column) => column != null); const keysParam = keyColumns.map(() => void 0); return ReducerManager.evaluateRange(def, reducer, { rawData, keyColumns, keysParam }, 0, rawData.length); } postProcessData(processedData) { processedData.reduced ?? (processedData.reduced = {}); for (const def of this.processors) { processedData.reduced[def.property] = def.calculate( processedData, processedData.reduced[def.property] ); } const { diff: diff2 } = processedData.reduced; if (diff2) { this.eventsHub?.emit("datamodel:diff", { diff: diff2 }); } } initDataDomainProcessor(domainMode) { return this.domainManager.initDataDomainProcessor(domainMode); } /** * Collects optimization metadata for debugging purposes. * Only called when debug mode is enabled. */ collectOptimizationMetadata(processedData, pathTaken) { const existingDomainBanding = processedData.optimizations?.domainBanding; const reducerBands = processedData[REDUCER_BANDS]; if (this.reducers.length > 0 && reducerBands) { this.collectReducerBandingMetadata(processedData, reducerBands); } const reducerBanding = processedData.optimizations?.reducerBanding; processedData.optimizations = { performance: { processingTime: processedData.time, pathTaken }, ...existingDomainBanding && { domainBanding: existingDomainBanding }, ...reducerBanding && { reducerBanding } }; const reprocessingSupported = this.isReprocessingSupported(processedData); const reprocessingApplied = pathTaken === "reprocess"; let reprocessingReason; if (!reprocessingSupported) { const reasons = []; if (processedData.type === "grouped") { if (!processedData.groupsUnique) { reasons.push("groupsUnique=false"); } const uniqueDataSets = this.getUniqueDataSets(processedData); if (uniqueDataSets.size !== 1) { reasons.push("multiple data sources"); } const scope = first(processedData.scopes); const invalidKeys = processedData.invalidKeys?.get(scope); if (invalidKeys?.some((invalid) => invalid)) { reasons.push("has invalid keys"); } } if (this.aggregates.length > 0) { reasons.push("has aggregates"); } if (this.reducers.filter((r) => !r.supportsBanding).length > 0) { reasons.push("has reducers"); } if (this.processors.filter((p) => p.incrementalCalculate === void 0).length > 0) { reasons.push("has processors"); } if (this.propertyProcessors.length > 0) { reasons.push("has property processors"); } reprocessingReason = reasons.length > 0 ? reasons.join(", ") : void 0; } processedData.optimizations.reprocessing = { applied: reprocessingApplied, reason: reprocessingReason }; if (processedData.type === "grouped") { let sharedGroupCount = 0; const firstGroup = processedData.groups[0]; if (firstGroup) { const sharedDatumIndices = firstGroup.datumIndices; for (const group of processedData.groups) { if (group.datumIndices === sharedDatumIndices) { sharedGroupCount++; } } } processedData.optimizations.sharedDatumIndices = { applied: sharedGroupCount > 0, sharedGroupCount, totalGroupCount: processedData.groups.length }; } } /** * Collects reducer banding metadata for debugging purposes. * Tracks which reducers used banding and their performance stats. */ collectReducerBandingMetadata(processedData, reducerBands) { if (this.reducers.length === 0) return; processedData.optimizations ?? (processedData.optimizations = {}); const reducerMetadata = []; for (const def of this.reducers) { const bandManager = reducerBands.get(def.property); const isBanded = this.shouldUseReducerBanding(def, processedData) && bandManager != null; let reason; if (!isBanded) { if (def.supportsBanding !== true) { reason = "reducer does not support banding"; } else if (processedData.type !== "ungrouped") { reason = "grouped data not supported"; } else if (def.combineResults === void 0) { reason = "missing combineResults function"; } else { reason = "banding not applied"; } } let stats; if (isBanded && bandManager) { stats = bandManager.getStats(); } reducerMetadata.push({ property: String(def.property), applied: isBanded, reason, stats }); } processedData.optimizations.reducerBanding = { reducers: reducerMetadata }; } buildAccessors(defs) { return this.scopeCacheManager.buildAccessors(defs); } }; function logProcessedData(processedData) { const logValues = (name, data) => { if (data.length > 0) { logger_exports.log(`DataModel.processData() - ${name}`); logger_exports.table(data); } }; logger_exports.log("DataModel.processData() - processedData", processedData); logValues("Key Domains", processedData.domain.keys); logValues("Value Domains", processedData.domain.values); logValues("Aggregate Domains", processedData.domain.aggValues ?? []); if (processedData.optimizations) { logger_exports.log("DataModel.processData() - Optimization Summary"); const opt = processedData.optimizations; if (opt.performance) { logger_exports.log(` Performance: ${opt.performance.processingTime.toFixed(2)}ms (${opt.performance.pathTaken})`); } if (opt.reprocessing) { const symbol = opt.reprocessing.applied ? "\u2713" : "\u2717"; const reason = opt.reprocessing.reason ? ` (${opt.reprocessing.reason})` : ""; logger_exports.log(` Reprocessing: ${symbol}${reason}`); } if (opt.domainBanding) { const keyStats = opt.domainBanding.keyDefs.filter((d) => d.applied); const valueStats = opt.domainBanding.valueDefs.filter((d) => d.applied); const totalApplied = keyStats.length + valueStats.length; const totalDefs = opt.domainBanding.keyDefs.length + opt.domainBanding.valueDefs.length; if (totalApplied > 0) { logger_exports.log(` Domain Banding: \u2713 (${totalApplied}/${totalDefs} definitions)`); for (const def of [...keyStats, ...valueStats]) { if (def.stats) { const pct2 = (def.stats.scanRatio * 100).toFixed(1); logger_exports.log( ` ${def.property}: scanned ${def.stats.dirtyBands}/${def.stats.totalBands} bands (${pct2}%)` ); } } } else { const reasons = [ ...opt.domainBanding.keyDefs.filter((d) => !d.applied).map((d) => d.reason), ...opt.domainBanding.valueDefs.filter((d) => !d.applied).map((d) => d.reason) ]; const uniqueReasons = [...new Set(reasons)].join(", "); logger_exports.log(` Domain Banding: \u2717 (${uniqueReasons})`); } } if (opt.sharedDatumIndices) { const symbol = opt.sharedDatumIndices.applied ? "\u2713" : "\u2717"; const ratio2 = `${opt.sharedDatumIndices.sharedGroupCount}/${opt.sharedDatumIndices.totalGroupCount}`; logger_exports.log(` Shared DatumIndices: ${symbol} (${ratio2} groups)`); } if (opt.batchMerging) { const pct2 = (opt.batchMerging.mergeRatio * 100).toFixed(0); const reduction = `${opt.batchMerging.originalBatchCount} \u2192 ${opt.batchMerging.mergedBatchCount}`; logger_exports.log(` Batch Merging: ${reduction} (${pct2}% reduction)`); } } } // packages/ag-charts-community/src/chart/data/processors.ts var MAX_ANIMATABLE_NODES = 1e3; function combineIntervalBandResults(bandResults, fallback, combiner) { const validResults = bandResults.filter( (result) => typeof result === "number" && Number.isFinite(result) ); return validResults.length > 0 ? combiner(validResults) : fallback; } function processedDataIsAnimatable(processedData) { return processedData.input.count <= MAX_ANIMATABLE_NODES; } function basicContinuousCheckDatumValidation(value) { return value != null && isContinuous(value); } function basicDiscreteCheckDatumValidation(value) { return value != null; } function basicDiscreteCheckDatumValidationAllowNull(_value) { return true; } function getValidationFn(scaleType, allowNullKey) { switch (scaleType) { case "number": case "log": case "time": case "unit-time": case "ordinal-time": case "color": return basicContinuousCheckDatumValidation; default: return allowNullKey ? basicDiscreteCheckDatumValidationAllowNull : basicDiscreteCheckDatumValidation; } } function getValueType(scaleType) { switch (scaleType) { case "number": case "log": case "time": case "color": return "range"; default: return "category"; } } function keyProperty(propName, scaleType, opts = {}) { const allowNullKey = opts.allowNullKey ?? false; const result = { property: propName, type: "key", valueType: getValueType(scaleType), validation: opts.validation ?? getValidationFn(scaleType, allowNullKey), ...opts }; return result; } function valueProperty(propName, scaleType, opts = {}) { const allowNullKey = opts.allowNullKey ?? false; const result = { property: propName, type: "value", valueType: getValueType(scaleType), validation: opts.validation ?? getValidationFn(scaleType, allowNullKey), ...opts }; return result; } function rowCountProperty(propName, opts = {}) { const result = { property: propName, type: "value", valueType: "range", missingValue: 1, processor: function rowCountResetFn() { return function rowCountGroupResetFn() { return 1; }; }, ...opts }; return result; } var noopProcessor = function(v) { return v; }; function processorChain(...chain) { const filteredChain = chain.filter((fn) => fn != null); if (filteredChain.length === 0) { return () => noopProcessor; } if (filteredChain.length === 1) { return filteredChain[0]; } return function processorChainFn() { const processorInstances = filteredChain.map((fn) => fn()); return function processorChainResultFn(value, index) { return processorInstances.reduce((r, p) => p(r, index), value); }; }; } function rangedValueProperty(propName, opts = {}) { const { min = -Infinity, max = Infinity, processor, ...defOpts } = opts; return { type: "value", property: propName, valueType: "range", validation: basicContinuousCheckDatumValidation, processor: processorChain(processor, function clampFnBuilder() { return function clampFn(datum) { return isFiniteNumber(datum) ? clamp(min, datum, max) : datum; }; }), ...defOpts }; } function accumulativeValueProperty(propName, scaleType, opts = {}) { const { onlyPositive, processor, ...defOpts } = opts; const result = { ...valueProperty(propName, scaleType, defOpts), processor: processorChain(processor, accumulatedValue(onlyPositive)) }; return result; } function trailingAccumulatedValueProperty(propName, scaleType, opts = {}) { const result = { ...valueProperty(propName, scaleType, opts), processor: trailingAccumulatedValue() }; return result; } function groupAccumulativeValueProperty(propName, mode, opts, scaleType) { return [ valueProperty(propName, scaleType, opts), accumulateGroup(opts.groupId, mode, opts.separateNegative), ...opts.rangeId == null ? [] : [range2(opts.rangeId, opts.groupId)] ]; } var SMALLEST_KEY_INTERVAL = { type: "reducer", property: "smallestKeyInterval", initialValue: Infinity, reducer() { let prevX = Number.NaN; return function smallestKeyIntervalReducerFn(smallestSoFar, keys) { const key = keys[0]; const nextX = typeof key === "number" ? key : Number(key); if (!Number.isFinite(nextX)) return smallestSoFar; const prevX2 = prevX; prevX = nextX; if (!Number.isFinite(prevX)) return smallestSoFar; const interval = Math.abs(nextX - prevX2); const currentSmallest = smallestSoFar ?? Infinity; if (interval > 0 && interval < currentSmallest) { return interval; } return currentSmallest; }; }, supportsBanding: true, combineResults(bandResults) { return combineIntervalBandResults(bandResults, Infinity, (values) => Math.min(...values)); }, needsOverlap: true }; var LARGEST_KEY_INTERVAL = { type: "reducer", property: "largestKeyInterval", initialValue: -Infinity, reducer() { let prevX = Number.NaN; return function largestKeyIntervalReducerFn(largestSoFar, keys) { const key = keys[0]; const nextX = typeof key === "number" ? key : Number(key); if (!Number.isFinite(nextX)) return largestSoFar; const prevX2 = prevX; prevX = nextX; if (!Number.isFinite(prevX)) return largestSoFar; const interval = Math.abs(nextX - prevX2); const currentLargest = largestSoFar ?? -Infinity; if (interval > 0 && interval > currentLargest) { return interval; } return currentLargest; }; }, supportsBanding: true, combineResults(bandResults) { return combineIntervalBandResults(bandResults, -Infinity, (values) => Math.max(...values)); }, needsOverlap: true }; var SORT_DOMAIN_GROUPS = { type: "processor", property: "sortedGroupDomain", calculate: function sortedGroupDomainFn({ domain: { groups } }) { return groups?.slice().sort((a, b) => { for (let i = 0; i < a.length; i++) { const result = a[i] - b[i]; if (result !== 0) { return result; } } return 0; }); } }; function normaliseFnBuilder({ normaliseTo }) { const normalise2 = (val, extent2) => { if (extent2 === 0) return 0; const result = (val ?? 0) * normaliseTo / extent2; if (result >= 0) { return Math.min(normaliseTo, result); } return Math.max(-normaliseTo, result); }; return () => () => (columns, valueIndexes, dataGroup, groupIndex) => { const extent2 = normaliseFindExtent(columns, valueIndexes, dataGroup, groupIndex); for (const valueIdx of valueIndexes) { const datumIndices = dataGroup.datumIndices[valueIdx]; if (datumIndices == null) continue; for (const relativeDatumIndex of datumIndices) { const datumIndex = groupIndex + relativeDatumIndex; const column = columns[valueIdx]; const value = column[datumIndex]; if (value == null) { column[datumIndex] = void 0; continue; } column[datumIndex] = normalise2(value, extent2); } } }; } function normaliseFindExtent(columns, valueIndexes, dataGroup, groupIndex) { const valuesExtent = [0, 0]; for (const valueIdx of valueIndexes) { const column = columns[valueIdx]; const datumIndices = dataGroup.datumIndices[valueIdx]; if (datumIndices == null) continue; for (const relativeDatumIndex of datumIndices) { const datumIndex = groupIndex + relativeDatumIndex; const value = column[datumIndex]; if (value == null) continue; const valueExtent = typeof value === "number" ? value : Math.max(...value.map((v) => v ?? 0)); const valIdx = valueExtent < 0 ? 0 : 1; if (valIdx === 0) { valuesExtent[valIdx] = Math.min(valuesExtent[valIdx], valueExtent); } else { valuesExtent[valIdx] = Math.max(valuesExtent[valIdx], valueExtent); } } } return Math.max(Math.abs(valuesExtent[0]), valuesExtent[1]); } function normaliseGroupTo(matchGroupIds, normaliseTo) { return { type: "group-value-processor", matchGroupIds, adjust: memo({ normaliseTo }, normaliseFnBuilder) }; } function normalisePropertyFnBuilder({ normaliseTo, zeroDomain, rangeMin, rangeMax }) { const normaliseSpan = normaliseTo[1] - normaliseTo[0]; const normalise2 = function normaliseFn(val, start2, span) { const result = normaliseTo[0] + (val - start2) / span * normaliseSpan; if (span === 0) { return zeroDomain; } else if (result >= normaliseTo[1]) { return normaliseTo[1]; } else if (result < normaliseTo[0]) { return normaliseTo[0]; } return result; }; return function normalisePropertyResetFn() { return function normalisePropertyResultFn(pData, pIdx) { let [start2, end3] = pData.domain.values[pIdx]; if (rangeMin != null) start2 = rangeMin; if (rangeMax != null) end3 = rangeMax; const span = end3 - start2; pData.domain.values[pIdx] = [normaliseTo[0], normaliseTo[1]]; const column = pData.columns[pIdx]; for (let datumIndex = 0; datumIndex < column.length; datumIndex += 1) { column[datumIndex] = normalise2(column[datumIndex], start2, span); } }; }; } function normalisePropertyTo(property, normaliseTo, zeroDomain, rangeMin, rangeMax) { return { type: "property-value-processor", property, adjust: memo({ normaliseTo, rangeMin, rangeMax, zeroDomain }, normalisePropertyFnBuilder) }; } var ANIMATION_VALIDATION_UNIQUE_KEYS = 1; var ANIMATION_VALIDATION_ORDERED_KEYS = 2; function animationValidationProcessValue(def, domainValues, column) { let validation = ANIMATION_VALIDATION_UNIQUE_KEYS | ANIMATION_VALIDATION_ORDERED_KEYS; if (def.valueType === "category") { if (domainValues.length < column.length) validation &= ~ANIMATION_VALIDATION_UNIQUE_KEYS; return validation; } let lastValue = column[0]?.valueOf(); for (let d = 1; validation !== 0 && d < column.length; d++) { const keyValue = column[d]?.valueOf(); if (!Number.isFinite(keyValue) || lastValue > keyValue) validation &= ~ANIMATION_VALIDATION_ORDERED_KEYS; if (Number.isFinite(keyValue) && lastValue === keyValue) validation &= ~ANIMATION_VALIDATION_UNIQUE_KEYS; lastValue = keyValue; } return validation; } function buildAnimationValidationFn(valueKeyIds) { return function calculate(result, _previousValue) { if (!processedDataIsAnimatable(result)) return; const { keys: keysDefs, values: valuesDef } = result.defs; const { input, domain: { values: domainValues }, columns } = result; let uniqueKeys = true; let orderedKeys = true; if (input.count !== 0) { const keySortOrders = result[KEY_SORT_ORDERS]; for (let i = 0; (uniqueKeys || orderedKeys) && i < keysDefs.length; i++) { const def = keysDefs[i]; const entry = keySortOrders.get(i); if (def.valueType === "category") { const domainSize = result.domain.keys[i]?.length ?? 0; const dataSize = result.keys[i]?.values().next().value?.length ?? 0; if (domainSize < dataSize) uniqueKeys = false; } else if (entry) { if (entry.isUnique === false) uniqueKeys = false; if (entry.sortOrder !== 1) orderedKeys = false; } } if (valueKeyIds && valueKeyIds.length > 0) { let validation = ANIMATION_VALIDATION_UNIQUE_KEYS | ANIMATION_VALIDATION_ORDERED_KEYS; for (let i = 0; validation !== 0 && i < valuesDef.length; i++) { const value = valuesDef[i]; if (!valueKeyIds.includes(value.id)) continue; validation &= animationValidationProcessValue(value, domainValues[i], columns[i]); } if ((validation & ANIMATION_VALIDATION_UNIQUE_KEYS) === 0) uniqueKeys = false; if ((validation & ANIMATION_VALIDATION_ORDERED_KEYS) === 0) orderedKeys = false; } } return { uniqueKeys, orderedKeys }; }; } function incrementalCalculateAnimationValidation() { return { uniqueKeys: true, orderedKeys: false }; } function animationValidation(valueKeyIds) { const calculate = memo(valueKeyIds, buildAnimationValidationFn); return { type: "processor", property: "animationValidation", calculate, incrementalCalculate: incrementalCalculateAnimationValidation }; } function buildGroupAccFn({ mode, separateNegative }) { return function buildGroupAccFnResetFn() { return function buildGroupAccFnGroupResetFn() { return function buildGroupAccFnResultFn(columns, valueIndexes, dataGroup, groupIndex) { const acc = [0, 0]; for (const valueIdx of valueIndexes) { const datumIndices = dataGroup.datumIndices[valueIdx]; if (datumIndices == null) continue; const stackNegative = acc[0]; const stackPositive = acc[1]; const column = columns[valueIdx]; let didAccumulate = false; for (const relativeDatumIndex of datumIndices) { const datumIndex = groupIndex + relativeDatumIndex; const currentVal = column[datumIndex]; if (!isFiniteNumber(currentVal)) continue; const useNegative = separateNegative && isNegative(currentVal); const accValue = useNegative ? stackNegative : stackPositive; if (mode === "normal") { column[datumIndex] = accValue + currentVal; } else { column[datumIndex] = accValue; } if (!didAccumulate) { const accIndex = useNegative ? 0 : 1; acc[accIndex] = accValue + currentVal; didAccumulate = true; } } } }; }; }; } function accumulateGroup(matchGroupId, mode, separateNegative = false) { const adjust = memo({ mode, separateNegative }, buildGroupAccFn); return { type: "group-value-processor", matchGroupIds: [matchGroupId], adjust, supportsReprocessing: true }; } function valueIdentifier(value) { return value.id ?? value.property; } function valueIndices(id, previousData, processedData) { const properties = /* @__PURE__ */ new Map(); const previousValues = previousData.defs.values; for (let previousIndex = 0; previousIndex < previousValues.length; previousIndex += 1) { const previousValue = previousValues[previousIndex]; if (previousValue.scopes?.includes(id) === false) continue; const valueId = valueIdentifier(previousValue); if (properties.has(valueId)) return; properties.set(valueId, previousIndex); } const indices = []; const nextValues = processedData.defs.values; for (let nextIndex = 0; nextIndex < nextValues.length; nextIndex += 1) { const nextValue = nextValues[nextIndex]; if (nextValue.scopes?.includes(id) === false) continue; const valueId = valueIdentifier(nextValue); const previousIndex = properties.get(valueId); if (previousIndex == null) return; properties.delete(valueId); indices.push({ previousIndex, nextIndex }); } if (properties.size !== 0) return; return indices; } function columnsEqual(previousColumns, nextColumns, indices, previousDatumIndex, nextDatumIndex) { for (const { previousIndex, nextIndex } of indices) { const previousColumn = previousColumns[previousIndex]; const nextColumn = nextColumns[nextIndex]; const previousValue = previousColumn[previousDatumIndex]; const nextValue = nextColumn[nextDatumIndex]; if (previousValue !== nextValue) { return false; } } return true; } function diff(id, previousData, updateMovedData = true) { return { type: "processor", property: "diff", calculate(processedData, previousValue) { if (!processedDataIsAnimatable(processedData)) return; const moved = /* @__PURE__ */ new Map(); const added = /* @__PURE__ */ new Map(); const updated = /* @__PURE__ */ new Map(); const removed = /* @__PURE__ */ new Map(); const previousKeys = previousData.keys.map((keyMap) => keyMap.get(id)); const keys = processedData.keys.map((keyMap) => keyMap.get(id)); const previousColumns = previousData.columns; const columns = processedData.columns; const indices = valueIndices(id, previousData, processedData); if (indices == null) return previousValue; const length2 = Math.max(previousData.input.count, processedData.input.count); const allowNull = processedData.defs.keys.some((def) => def.allowNullKey === true); for (let i = 0; i < length2; i++) { const hasPreviousDatum = i < previousData.input.count; const hasDatum = i < processedData.input.count; const prevKeys = hasPreviousDatum ? datumKeys(previousKeys, i, allowNull) : void 0; const prevId = prevKeys == null ? "" : createDatumId(...prevKeys); const dKeys = hasDatum ? datumKeys(keys, i, allowNull) : void 0; const datumId = dKeys == null ? "" : createDatumId(...dKeys); if (hasDatum && hasPreviousDatum && prevId === datumId) { if (!columnsEqual(previousColumns, columns, indices, i, i)) { updated.set(datumId, i); } continue; } const removedIndex = removed.get(datumId); if (removedIndex != null) { if (updateMovedData || !columnsEqual(previousColumns, columns, indices, removedIndex, i)) { updated.set(datumId, i); moved.set(datumId, i); } removed.delete(datumId); } else if (hasDatum) { added.set(datumId, i); } const addedIndex = added.get(prevId); if (addedIndex != null) { if (updateMovedData || !columnsEqual(previousColumns, columns, indices, addedIndex, i)) { updated.set(prevId, i); moved.set(prevId, i); } added.delete(prevId); } else if (hasPreviousDatum) { updated.delete(prevId); removed.set(prevId, i); } } const changed = added.size > 0 || updated.size > 0 || removed.size > 0; const value = { changed, added: new Set(added.keys()), updated: new Set(updated.keys()), removed: new Set(removed.keys()), moved: new Set(moved.keys()) }; return { ...previousValue, [id]: value }; } }; } function createDatumId(...keys) { if (keys.length === 1) { const key = transformIntegratedCategoryValue(keys[0]); if (key === null) return NULL_KEY_STRING; if (key === void 0) return UNDEFINED_KEY_STRING; const isPrimitive = typeof key === "boolean" || typeof key === "number" || typeof key === "string"; if (isPrimitive) return key; } return keys.map((key) => { const transformed = transformIntegratedCategoryValue(key); if (transformed === null) return NULL_KEY_STRING; if (transformed === void 0) return UNDEFINED_KEY_STRING; return transformed; }).join("___"); } // packages/ag-charts-community/src/chart/data/dataSet.ts var DataSet = class _DataSet { constructor(data) { this.data = data; this.pendingTransactions = []; } /** * Creates an empty DataSet. */ static empty() { return new _DataSet([]); } /** * Wraps existing data in a DataSet. */ static wrap(data) { return new _DataSet(data); } netSize() { if (!this.hasPendingTransactions()) { return this.data.length; } const changeDesc = this.getChangeDescription(); return changeDesc ? changeDesc.indexMap.finalLength : this.data.length; } /** * Queues a transaction (applied on commit). * Normalizes AG Grid-compatible format (add/addIndex) to internal format (prepend/append). */ addTransaction(transaction) { const normalized2 = this.normalizeTransaction(transaction); this.pendingTransactions.push(normalized2); this.cachedChangeDescription = void 0; } /** * @returns A deep clone of the DataSet. */ deepClone() { return new _DataSet([...this.data]); } /** * Converts AG Grid-compatible transaction format to internal format. * Maps `add` + `addIndex` to prepend, append, or arbitrary insertion based on the index. */ normalizeTransaction(transaction) { const { add: add2, addIndex, prepend, append, remove, update } = transaction; if (add2 === void 0) { return transaction; } const result = { remove, update }; if (prepend) result.prepend = prepend; if (append) result.append = append; if (add2 && add2.length > 0) { const currentSize = this.netSize(); if (addIndex === void 0 || addIndex >= currentSize) { result.append = append ? [...append, ...add2] : add2; } else if (addIndex === 0) { result.prepend = prepend ? [...add2, ...prepend] : add2; } else { result.insertions = [{ index: addIndex, items: add2 }]; } } return result; } hasPendingTransactions() { return this.pendingTransactions.length > 0; } getPendingTransactionCount() { return this.pendingTransactions.length; } /** Applies all pending transactions to the data array. */ commitPendingTransactions() { if (!this.hasPendingTransactions()) { return false; } const changeDescription = this.getChangeDescription(); if (!changeDescription) { return false; } const prependedValues = changeDescription.getPrependedValues(); const insertionValues = changeDescription.getInsertionValues(); const appendedValues = changeDescription.getAppendedValues(); const allInsertionValues = [...prependedValues, ...insertionValues, ...appendedValues]; let insertionValueIndex = 0; changeDescription.applyToArray(this.data, function applyToArrayResultFn(destIndex) { if (insertionValueIndex >= allInsertionValues.length) { throw new Error(`AG Charts - Internal error: No insertion value found for index ${destIndex}`); } return allInsertionValues[insertionValueIndex++]; }); this.pendingTransactions = []; this.cachedChangeDescription = void 0; this.updateItemToIndexCache(changeDescription, appendedValues, prependedValues, insertionValues); return true; } /** Updates item→index cache incrementally, or invalidates for complex changes. */ updateItemToIndexCache(changeDescription, appendedValues, prependedValues, insertionValues) { if (!this.itemToIndexCache) return; const { indexMap } = changeDescription; const { totalPrependCount, totalAppendCount, removedIndices } = indexMap; const hasRemovals = removedIndices.size > 0; const hasArbitraryInsertions = insertionValues.length > 0; if (!hasRemovals && totalPrependCount === 0 && totalAppendCount === 0 && !hasArbitraryInsertions) { return; } if (hasArbitraryInsertions) { this.itemToIndexCache = void 0; return; } let removalsAreContiguousAtStart = false; let contiguousRemovalCount = 0; if (hasRemovals) { const sortedRemovals = Array.from(removedIndices).sort((a, b) => a - b); removalsAreContiguousAtStart = sortedRemovals[0] === 0; if (removalsAreContiguousAtStart) { for (let i = 0; i < sortedRemovals.length; i++) { if (sortedRemovals[i] !== i) { removalsAreContiguousAtStart = false; break; } } if (removalsAreContiguousAtStart) { contiguousRemovalCount = sortedRemovals.length; } } } if (hasRemovals && !removalsAreContiguousAtStart) { this.itemToIndexCache = void 0; return; } const cache = this.itemToIndexCache; const indexShift = totalPrependCount - contiguousRemovalCount; if (indexShift !== 0) { for (const [item, oldIndex] of cache) { if (removedIndices.has(oldIndex)) { cache.delete(item); } else { cache.set(item, oldIndex + indexShift); } } } else if (hasRemovals) { for (const [item, oldIndex] of cache) { if (removedIndices.has(oldIndex)) { cache.delete(item); } } } for (let i = 0; i < prependedValues.length; i++) { const item = prependedValues[i]; if (!cache.has(item)) { cache.set(item, i); } } const appendStartIndex = indexMap.finalLength - totalAppendCount; for (let i = 0; i < appendedValues.length; i++) { const item = appendedValues[i]; if (!cache.has(item)) { cache.set(item, appendStartIndex + i); } } } clearPendingTransactions() { const count = this.pendingTransactions.length; this.pendingTransactions = []; this.cachedChangeDescription = void 0; return count; } getPendingTransactions() { return [...this.pendingTransactions]; } /** Custom JSON serialization (avoids snapshot bloat). */ toJSON() { return this.data; } /** Builds a DataChangeDescription from pending transactions (does not modify data). */ getChangeDescription() { if (!this.hasPendingTransactions()) { return void 0; } if (this.cachedChangeDescription) { return this.cachedChangeDescription; } const { indexMap, prependValues, appendValues, insertionValues } = this.buildIndexMap(); const changeDescription = new DataChangeDescription(indexMap, { prependValues, appendValues, insertionValues }); this.cachedChangeDescription = changeDescription; return changeDescription; } /** * Helper method to remove items from a list of groups. * Mutates the groups in-place and removes found items from toRemove set. * @param groups List of groups to search and remove from * @param toRemove Set of items to remove (modified in-place) */ removeFromGroups(groups, toRemove) { for (const group of groups) { let i = 0; while (i < group.length && toRemove.size > 0) { if (toRemove.has(group[i])) { toRemove.delete(group[i]); group.splice(i, 1); } else { i++; } } if (toRemove.size === 0) break; } } /** * Builds the index transformation map by sequentially applying all pending transactions. * Optimized to: * - Track operation boundaries instead of individual items * - Only scan for values that are actually being removed * - Stop scanning early when all removed values are found * - Support arbitrary insertions at any index * - Track updated items by referential equality */ buildIndexMap() { const originalLength = this.data.length; const effects = this.collectTransactionEffects(); const survivingPrepends = effects.prependsList.flat(); const survivingAppends = effects.appendsList.flat(); const survivingInsertions = effects.insertionsList.flat(); const totalPrependCount = survivingPrepends.length; const totalAppendCount = survivingAppends.length; const totalInsertionCount = survivingInsertions.length; const survivingOriginalCount = originalLength - effects.removedOriginalIndices.size; const finalLength = totalPrependCount + survivingOriginalCount + totalInsertionCount + totalAppendCount; const sortedRemoved = effects.removedOriginalIndices.size > 0 ? this.getSortedRemovedIndices(effects.removedOriginalIndices) : void 0; const spliceOps = this.buildSpliceOperations( totalPrependCount, totalInsertionCount, totalAppendCount, survivingOriginalCount, effects.trackedInsertions, sortedRemoved?.desc, sortedRemoved?.asc ); const updatedFinalIndices = this.resolveUpdatedIndices( totalPrependCount, totalInsertionCount, survivingOriginalCount, effects.updateTracking, sortedRemoved?.asc, effects.updatedOriginalIndices, effects.trackedInsertions ); const indexMap = { originalLength, finalLength, spliceOps, removedIndices: effects.removedOriginalIndices, updatedIndices: updatedFinalIndices, totalPrependCount, totalAppendCount }; return { indexMap, prependValues: survivingPrepends, appendValues: survivingAppends, insertionValues: survivingInsertions }; } getSortedRemovedIndices(removedOriginalIndices) { const asc = Array.from(removedOriginalIndices).sort((a, b) => a - b); return { asc, desc: [...asc].reverse() }; } collectTransactionEffects() { const state = { prependsList: [], appendsList: [], insertionsList: [], trackedInsertions: [], removedOriginalIndices: /* @__PURE__ */ new Set(), updatedOriginalIndices: /* @__PURE__ */ new Set(), virtualLength: this.data.length }; for (const transaction of this.pendingTransactions) { const { prepend, append, insertions, remove, update } = transaction; this.applyPrepends(prepend, state); this.applyInsertions(insertions, state); this.applyAppends(append, state); this.applyRemovals(remove, state); this.applyUpdates(update, state); } return { prependsList: state.prependsList, appendsList: state.appendsList, insertionsList: state.insertionsList, trackedInsertions: state.trackedInsertions, removedOriginalIndices: state.removedOriginalIndices, updatedOriginalIndices: state.updatedOriginalIndices, updateTracking: state.updateTracking }; } applyPrepends(prepend, state) { if (!Array.isArray(prepend) || prepend.length === 0) { return; } state.prependsList.unshift([...prepend]); state.virtualLength += prepend.length; } applyInsertions(insertions, state) { if (!Array.isArray(insertions)) { return; } for (const { index, items } of insertions) { if (index >= 0 && index <= state.virtualLength && items.length > 0) { state.trackedInsertions.push({ virtualIndex: index, items: [...items] }); state.insertionsList.push([...items]); state.virtualLength += items.length; } } } applyAppends(append, state) { if (!Array.isArray(append) || append.length === 0) { return; } state.appendsList.push([...append]); state.virtualLength += append.length; } applyRemovals(remove, state) { if (!Array.isArray(remove) || remove.length === 0) { return; } const toRemove = new Set(remove); this.removeFromGroups(state.prependsList, toRemove); if (toRemove.size > 0) { this.removeFromGroups(state.insertionsList, toRemove); } if (state.trackedInsertions.length > 0) { this.removeFromTrackedInsertions(remove, state); } if (toRemove.size > 0) { this.removeFromGroups(state.appendsList, toRemove); } if (toRemove.size > 0) { for (let i = 0; i < this.data.length && toRemove.size > 0; i++) { const value = this.data[i]; if (toRemove.has(value)) { state.removedOriginalIndices.add(i); toRemove.delete(value); state.virtualLength--; } } } if (toRemove.size > 0) { logger_exports.warnOnce( "applyTransaction() remove includes items not present in current data; ignoring missing items." ); } } applyUpdates(update, state) { if (!Array.isArray(update) || update.length === 0) { return; } const toUpdate = new Set(update); const updatedPrependsIndices = this.collectUpdatedIndicesFromGroups(state.prependsList, toUpdate); const updatedInsertionsIndices = toUpdate.size > 0 ? this.collectUpdatedIndicesFromGroups(state.insertionsList, toUpdate) : []; const updatedAppendsIndices = toUpdate.size > 0 ? this.collectUpdatedIndicesFromGroups(state.appendsList, toUpdate) : []; if (toUpdate.size > 0) { this.collectUpdatedOriginalIndices(toUpdate, state); } state.updateTracking = { updatedPrependsIndices, updatedAppendsIndices, updatedInsertionsIndices }; if (toUpdate.size > 0) { logger_exports.warnOnce( "applyTransaction() update includes items not present in current data; ignoring missing items." ); } } // Flattens grouped inserts to find updated item offsets while consuming the lookup set. collectUpdatedIndicesFromGroups(groups, toUpdate) { if (toUpdate.size === 0 || groups.length === 0) { return []; } const updatedIndices = []; let flatIndex = 0; for (const group of groups) { for (const item of group) { if (toUpdate.has(item)) { updatedIndices.push(flatIndex); toUpdate.delete(item); } flatIndex++; } if (toUpdate.size === 0) { break; } } return updatedIndices; } /** Lazy item→index map for O(1) lookups. */ getItemToIndexMap() { if (this.itemToIndexCache === void 0) { this.itemToIndexCache = /* @__PURE__ */ new Map(); for (let i = 0; i < this.data.length; i++) { if (!this.itemToIndexCache.has(this.data[i])) { this.itemToIndexCache.set(this.data[i], i); } } } return this.itemToIndexCache; } collectUpdatedOriginalIndices(toUpdate, state) { const indexMap = this.getItemToIndexMap(); for (const item of [...toUpdate]) { const idx = indexMap.get(item); if (idx !== void 0 && !state.removedOriginalIndices.has(idx)) { state.updatedOriginalIndices.add(idx); toUpdate.delete(item); } } } removeFromTrackedInsertions(removeValues, state) { for (let trackedIdx = 0; trackedIdx < state.trackedInsertions.length; trackedIdx++) { const tracked = state.trackedInsertions[trackedIdx]; const previousLength = tracked.items.length; const removedOffsets = []; let itemIndex = 0; while (itemIndex < tracked.items.length) { if (removeValues.includes(tracked.items[itemIndex])) { removedOffsets.push(itemIndex + removedOffsets.length); tracked.items.splice(itemIndex, 1); state.virtualLength--; } else { itemIndex++; } } if (removedOffsets.length > 0) { this.adjustLaterInsertionsAfterRemoval( state.trackedInsertions, trackedIdx, tracked, previousLength, removedOffsets ); } } } adjustLaterInsertionsAfterRemoval(trackedInsertions, trackedIdx, tracked, previousLength, removedOffsets) { const removedCount = removedOffsets.length; for (let j = trackedIdx + 1; j < trackedInsertions.length; j++) { const later = trackedInsertions[j]; if (later.virtualIndex <= tracked.virtualIndex) { continue; } const relativeInsertionPosition = Math.min( Math.max(later.virtualIndex - tracked.virtualIndex, 0), previousLength ); let removedBeforeInsertion = 0; for (const offset of removedOffsets) { if (offset < relativeInsertionPosition) { removedBeforeInsertion++; } else { break; } } if (relativeInsertionPosition === previousLength) { removedBeforeInsertion = removedCount; } if (removedBeforeInsertion > 0) { later.virtualIndex -= removedBeforeInsertion; } } } buildSpliceOperations(totalPrependCount, totalInsertionCount, totalAppendCount, survivingOriginalCount, trackedInsertions, sortedRemovedDesc, sortedRemovedAsc) { const spliceOps = []; if (totalPrependCount > 0) { spliceOps.push({ index: 0, deleteCount: 0, insertCount: totalPrependCount }); } if (sortedRemovedDesc && sortedRemovedDesc.length > 0) { let currentGroupStart = sortedRemovedDesc[0]; let currentGroupCount = 1; for (let i = 1; i < sortedRemovedDesc.length; i++) { const currentIndex = sortedRemovedDesc[i]; const prevIndex = sortedRemovedDesc[i - 1]; if (prevIndex - currentIndex === 1) { currentGroupCount++; } else { spliceOps.push({ index: currentGroupStart - currentGroupCount + 1 + totalPrependCount, deleteCount: currentGroupCount, insertCount: 0 }); currentGroupStart = currentIndex; currentGroupCount = 1; } } spliceOps.push({ index: currentGroupStart - currentGroupCount + 1 + totalPrependCount, deleteCount: currentGroupCount, insertCount: 0 }); } if (trackedInsertions.length > 0) { for (const insertion of trackedInsertions) { const removalsBeforeInsertion = this.countRemovalsBeforeIndex( sortedRemovedAsc, totalPrependCount, insertion.virtualIndex ); const adjustedIndex = insertion.virtualIndex - removalsBeforeInsertion; spliceOps.push({ index: adjustedIndex, deleteCount: 0, insertCount: insertion.items.length }); } } if (totalAppendCount > 0) { spliceOps.push({ index: totalPrependCount + survivingOriginalCount + totalInsertionCount, deleteCount: 0, insertCount: totalAppendCount }); } return spliceOps; } countRemovalsBeforeIndex(sortedRemovedAsc, totalPrependCount, insertionVirtualIndex) { if (!sortedRemovedAsc || sortedRemovedAsc.length === 0) { return 0; } let removalsBeforeInsertion = 0; for (const removedIndex of sortedRemovedAsc) { const virtualIndexOfRemoval = removedIndex + totalPrependCount; if (virtualIndexOfRemoval < insertionVirtualIndex) { removalsBeforeInsertion++; } else { break; } } return removalsBeforeInsertion; } resolveUpdatedIndices(totalPrependCount, totalInsertionCount, survivingOriginalCount, updateTracking, sortedRemovedAsc, updatedOriginalIndices, trackedInsertions) { const updatedFinalIndices = /* @__PURE__ */ new Set(); if (updateTracking) { for (const prependIdx of updateTracking.updatedPrependsIndices) { updatedFinalIndices.add(prependIdx); } } if (updatedOriginalIndices.size > 0) { const sortedUpdatedOriginals = Array.from(updatedOriginalIndices).sort((a, b) => a - b); let removalPtr = 0; for (const originalIdx of sortedUpdatedOriginals) { if (sortedRemovedAsc) { while (removalPtr < sortedRemovedAsc.length && sortedRemovedAsc[removalPtr] < originalIdx) { removalPtr++; } } const removalsBeforeCount = sortedRemovedAsc ? removalPtr : 0; const virtualPosOfOriginal = originalIdx + totalPrependCount; let insertionsBeforeCount = 0; for (const insertion of trackedInsertions) { if (insertion.virtualIndex <= virtualPosOfOriginal) { insertionsBeforeCount += insertion.items.length; } } const finalIdx = originalIdx + totalPrependCount - removalsBeforeCount + insertionsBeforeCount; updatedFinalIndices.add(finalIdx); } } if (updateTracking) { const appendStartIdx = totalPrependCount + survivingOriginalCount + totalInsertionCount; for (const appendIdx of updateTracking.updatedAppendsIndices) { updatedFinalIndices.add(appendStartIdx + appendIdx); } if (updateTracking.updatedInsertionsIndices.length > 0 && trackedInsertions.length > 0) { let flatIdx = 0; for (const insertion of trackedInsertions) { const removalsBeforeInsertion = this.countRemovalsBeforeIndex( sortedRemovedAsc, totalPrependCount, insertion.virtualIndex ); const insertionFinalIdx = insertion.virtualIndex - removalsBeforeInsertion; for (let itemOffset = 0; itemOffset < insertion.items.length; itemOffset++) { if (updateTracking.updatedInsertionsIndices.includes(flatIdx)) { updatedFinalIndices.add(insertionFinalIdx + itemOffset); } flatIdx++; } } } } return updatedFinalIndices; } }; // packages/ag-charts-community/src/util/observable.ts var Observable = class { constructor() { this.eventListeners = /* @__PURE__ */ new Map(); } addEventListener(eventType, listener) { if (typeof listener !== "function") { throw new TypeError("AG Charts - listener must be a Function"); } const eventTypeListeners = this.eventListeners.get(eventType); if (eventTypeListeners) { eventTypeListeners.add(listener); } else { this.eventListeners.set(eventType, /* @__PURE__ */ new Set([listener])); } } removeEventListener(type, listener) { const listeners = this.eventListeners.get(type); if (listeners == null) return; listeners.delete(listener); if (listeners.size === 0) { this.eventListeners.delete(type); } } hasEventListener(type) { return this.eventListeners.has(type); } clearEventListeners() { this.eventListeners.clear(); } fireEvent(event) { const listeners = this.eventListeners.get(event.type); if (listeners) { for (const listener of listeners) { listener(event); } } } }; // packages/ag-charts-community/src/chart/series/seriesProperties.ts var HighlightState = /* @__PURE__ */ ((HighlightState3) => { HighlightState3[HighlightState3["None"] = 0] = "None"; HighlightState3[HighlightState3["Item"] = 1] = "Item"; HighlightState3[HighlightState3["Series"] = 2] = "Series"; HighlightState3[HighlightState3["OtherSeries"] = 3] = "OtherSeries"; HighlightState3[HighlightState3["OtherItem"] = 4] = "OtherItem"; return HighlightState3; })(HighlightState || {}); var highlightStates = [ 0 /* None */, 1 /* Item */, 2 /* Series */, 3 /* OtherSeries */, 4 /* OtherItem */ ]; function getHighlightStyleOptionKeys(highlightState) { switch (highlightState) { case 1 /* Item */: return ["highlightedItem", "highlightedSeries"]; case 4 /* OtherItem */: return ["unhighlightedItem", "highlightedSeries"]; case 2 /* Series */: return ["highlightedSeries"]; case 3 /* OtherSeries */: return ["unhighlightedSeries"]; case 0 /* None */: return []; } } function toHighlightString(state) { const unreachable = (a) => a; switch (state) { case 1 /* Item */: return "highlighted-item"; case 4 /* OtherItem */: return "unhighlighted-item"; case 2 /* Series */: return "highlighted-series"; case 3 /* OtherSeries */: return "unhighlighted-series"; case 0 /* None */: return "none"; default: return unreachable(state); } } var SeriesItemHighlightStyle = class extends BaseProperties { }; __decorateClass([ addFakeTransformToInstanceProperty ], SeriesItemHighlightStyle.prototype, "fill", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SeriesItemHighlightStyle.prototype, "fillOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SeriesItemHighlightStyle.prototype, "stroke", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SeriesItemHighlightStyle.prototype, "strokeWidth", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SeriesItemHighlightStyle.prototype, "strokeOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SeriesItemHighlightStyle.prototype, "lineDash", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SeriesItemHighlightStyle.prototype, "lineDashOffset", 2); var HighlightProperties = class extends BaseProperties { constructor() { super(...arguments); this.enabled = true; this.range = "tooltip"; this.bringToFront = true; this.highlightedItem = {}; this.unhighlightedItem = {}; this.highlightedSeries = {}; this.unhighlightedSeries = {}; } getStyle(highlightState) { const keys = getHighlightStyleOptionKeys(highlightState); if (keys.length === 0) return {}; return mergeDefaults(...keys.map((key) => this[key])); } }; __decorateClass([ addFakeTransformToInstanceProperty ], HighlightProperties.prototype, "enabled", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HighlightProperties.prototype, "range", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HighlightProperties.prototype, "bringToFront", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HighlightProperties.prototype, "highlightedItem", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HighlightProperties.prototype, "unhighlightedItem", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HighlightProperties.prototype, "highlightedSeries", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HighlightProperties.prototype, "unhighlightedSeries", 2); var SegmentOptions = class extends BaseProperties { constructor() { super(...arguments); this.fill = "#c16068"; this.fillOpacity = 1; this.stroke = "#874349"; this.strokeWidth = 2; this.strokeOpacity = 1; this.lineDash = [0]; this.lineDashOffset = 0; } }; __decorateClass([ addFakeTransformToInstanceProperty ], SegmentOptions.prototype, "start", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SegmentOptions.prototype, "stop", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SegmentOptions.prototype, "fill", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SegmentOptions.prototype, "fillOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SegmentOptions.prototype, "stroke", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SegmentOptions.prototype, "strokeWidth", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SegmentOptions.prototype, "strokeOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SegmentOptions.prototype, "lineDash", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SegmentOptions.prototype, "lineDashOffset", 2); var Segmentation = class { constructor() { this.key = "x"; this.segments = new PropertiesArray(SegmentOptions); } }; __decorateClass([ addFakeTransformToInstanceProperty ], Segmentation.prototype, "enabled", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Segmentation.prototype, "key", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Segmentation.prototype, "segments", 2); var FillGradientDefaults = class extends BaseProperties { constructor() { super(...arguments); this.type = "gradient"; this.colorStops = []; this.bounds = "item"; this.gradient = "linear"; this.rotation = 0; this.reverse = false; this.colorSpace = "rgb"; } }; __decorateClass([ addFakeTransformToInstanceProperty ], FillGradientDefaults.prototype, "type", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillGradientDefaults.prototype, "colorStops", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillGradientDefaults.prototype, "bounds", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillGradientDefaults.prototype, "gradient", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillGradientDefaults.prototype, "rotation", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillGradientDefaults.prototype, "reverse", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillGradientDefaults.prototype, "colorSpace", 2); var FillPatternDefaults = class extends BaseProperties { constructor() { super(...arguments); this.type = "pattern"; this.colorStops = []; this.bounds = "item"; this.gradient = "linear"; this.rotation = 0; this.scale = 1; this.reverse = false; this.pattern = "forward-slanted-lines"; this.width = 26; this.height = 26; this.padding = 6; this.fill = "black"; this.fillOpacity = 1; this.backgroundFill = "white"; this.backgroundFillOpacity = 1; this.stroke = "black"; this.strokeOpacity = 1; this.strokeWidth = 0; } }; __decorateClass([ addFakeTransformToInstanceProperty ], FillPatternDefaults.prototype, "type", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillPatternDefaults.prototype, "colorStops", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillPatternDefaults.prototype, "bounds", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillPatternDefaults.prototype, "gradient", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillPatternDefaults.prototype, "rotation", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillPatternDefaults.prototype, "scale", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillPatternDefaults.prototype, "reverse", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillPatternDefaults.prototype, "path", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillPatternDefaults.prototype, "pattern", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillPatternDefaults.prototype, "width", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillPatternDefaults.prototype, "height", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillPatternDefaults.prototype, "padding", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillPatternDefaults.prototype, "fill", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillPatternDefaults.prototype, "fillOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillPatternDefaults.prototype, "backgroundFill", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillPatternDefaults.prototype, "backgroundFillOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillPatternDefaults.prototype, "stroke", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillPatternDefaults.prototype, "strokeOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillPatternDefaults.prototype, "strokeWidth", 2); var FillImageDefaults = class extends BaseProperties { constructor() { super(...arguments); this.type = "image"; this.url = ""; this.rotation = 0; this.scale = 1; this.backgroundFill = "black"; this.backgroundFillOpacity = 1; this.repeat = "no-repeat"; this.fit = "contain"; } }; __decorateClass([ addFakeTransformToInstanceProperty ], FillImageDefaults.prototype, "type", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillImageDefaults.prototype, "url", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillImageDefaults.prototype, "rotation", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillImageDefaults.prototype, "scale", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillImageDefaults.prototype, "backgroundFill", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillImageDefaults.prototype, "backgroundFillOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillImageDefaults.prototype, "repeat", 2); __decorateClass([ addFakeTransformToInstanceProperty ], FillImageDefaults.prototype, "fit", 2); var SeriesProperties = class extends BaseProperties { constructor() { super(...arguments); this.visible = true; this.focusPriority = Infinity; this.showInLegend = true; this.cursor = "default"; this.nodeClickRange = "exact"; this.highlight = new HighlightProperties(); } handleUnknownProperties(unknownKeys, properties) { if ("context" in properties) { this.context = properties.context; unknownKeys.delete("context"); } if ("allowNullKeys" in properties) { this.allowNullKeys = properties.allowNullKeys; unknownKeys.delete("allowNullKeys"); } } }; __decorateClass([ addFakeTransformToInstanceProperty ], SeriesProperties.prototype, "id", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SeriesProperties.prototype, "visible", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SeriesProperties.prototype, "focusPriority", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SeriesProperties.prototype, "showInLegend", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SeriesProperties.prototype, "cursor", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SeriesProperties.prototype, "nodeClickRange", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SeriesProperties.prototype, "highlight", 2); // packages/ag-charts-community/src/chart/series/util.ts function datumBoundaryPoints(datum, domain) { if (domain.length === 0) { return [false, false]; } const d0 = domain[0]; const d1 = domain.at(-1); if (typeof d0 === "string" || d0 === null || d0 === void 0) { return [datum === d0, datum === d1]; } if (datum == null) { return [false, false]; } const datumValue = datum.valueOf(); if (d0 == null || d1 == null) { return [false, false]; } let min = d0.valueOf(); let max = d1.valueOf(); if (min > max) { [min, max] = [max, min]; } return [datumValue === min, datumValue === max]; } function datumStylerProperties(xValue, yValue, xKey, yKey, xDomain, yDomain) { const [min, max] = datumBoundaryPoints(yValue, yDomain); const [first2, last] = datumBoundaryPoints(xValue, xDomain); return { xKey, yKey, xValue, yValue, first: first2, last, min, max }; } function visibleRangeIndices(sortOrder, length2, [range0, range1], xRange) { let xMinIndex = findMinIndex(0, length2 - 1, (i) => { const index = sortOrder === 1 ? i : length2 - i; const x1 = xRange(index)?.[1] ?? Number.NaN; return !Number.isFinite(x1) || x1 >= range0; }) ?? 0; let xMaxIndex = findMaxIndex(0, length2 - 1, (i) => { const index = sortOrder === 1 ? i : length2 - i; const x0 = xRange(index)?.[0] ?? Number.NaN; return !Number.isFinite(x0) || x0 <= range1; }) ?? length2 - 1; if (sortOrder === -1) { [xMinIndex, xMaxIndex] = [length2 - xMaxIndex, length2 - xMinIndex]; } xMinIndex = Math.max(xMinIndex, 0); xMaxIndex = Math.min(xMaxIndex + 1, length2); return [xMinIndex, xMaxIndex]; } function getDatumRefPoint(series, datum, movedBounds) { if (movedBounds) { const { x, y, width: width2, height: height2 } = movedBounds; return { canvasX: x + width2 / 2, canvasY: y + height2 / 2 }; } const refPoint = datum.yBar?.upperPoint ?? datum.midPoint ?? series.datumMidPoint?.(datum); if (refPoint) { const { x, y } = Transformable.toCanvasPoint(series.contentGroup, refPoint.x, refPoint.y); return { canvasX: Math.round(x), canvasY: Math.round(y) }; } } function countExpandingSearch(min, max, start2, countUntil, iteratee) { let i = -1; let count = 0; let shift = 0; let reachedAnEnd = false; while (count < countUntil && i <= max - min) { i += 1; const index = start2 + shift; if (!reachedAnEnd) shift *= -1; if (shift >= 0) shift += 1; if (reachedAnEnd && shift < 0) shift -= 1; if (index < min || index > max) { reachedAnEnd = true; continue; } if (iteratee(index)) count += 1; } return count; } function getItemStyles(getItemStyle) { const result = {}; for (const state of highlightStates) { result[state] = getItemStyle(void 0, false, state); } return result; } function getItemStylesPerItemId(getItemStyle, ...itemIds) { const result = {}; for (const itemId of itemIds ?? ["default"]) { for (const state of highlightStates) { const states = result[itemId] ?? (result[itemId] = {}); states[state] = getItemStyle(void 0, false, state, itemId); } } return result; } function hasDimmedOpacity(style2) { return (style2?.opacity ?? 1) < 1 || (style2?.fillOpacity ?? 1) < 1 || (style2?.strokeOpacity ?? 1) < 1; } var opaqueMarkerFillCache = /* @__PURE__ */ new Map(); function isOpaqueMarkerFillStyle(style2) { if (style2 == null) return false; const fill = style2.fill; if (!isString(fill)) return false; const fillString = fill.trim(); const fillLower = fillString.toLowerCase(); if (fillLower === "transparent" || fillLower === "none") return false; let cached = opaqueMarkerFillCache.get(fillString); if (cached == null) { try { cached = Color.fromString(fillString).a === 1; } catch { cached = false; } opaqueMarkerFillCache.set(fillString, cached); } return cached; } function resolveMarkerDrawingMode(baseDrawingMode, style2) { if (baseDrawingMode !== "cutout") return baseDrawingMode; return isOpaqueMarkerFillStyle(style2) ? "cutout" : "overlay"; } function findNodeDatumInArray(itemIdOrIndex, nodeData) { for (const node of nodeData ?? []) { switch (typeof itemIdOrIndex) { case "string": if (node.itemId === itemIdOrIndex) { return node; } break; case "number": if (node.datumIndex === itemIdOrIndex) { return node; } break; default: return itemIdOrIndex; } } return void 0; } // packages/ag-charts-community/src/chart/series/series.ts var SeriesNodePickMode = /* @__PURE__ */ ((SeriesNodePickMode2) => { SeriesNodePickMode2[SeriesNodePickMode2["EXACT_SHAPE_MATCH"] = 0] = "EXACT_SHAPE_MATCH"; SeriesNodePickMode2[SeriesNodePickMode2["NEAREST_NODE"] = 1] = "NEAREST_NODE"; SeriesNodePickMode2[SeriesNodePickMode2["AXIS_ALIGNED"] = 2] = "AXIS_ALIGNED"; return SeriesNodePickMode2; })(SeriesNodePickMode || {}); var CROSS_FILTER_MARKER_FILL_OPACITY_FACTOR = 0.25; var CROSS_FILTER_MARKER_STROKE_OPACITY_FACTOR = 0.125; var SeriesNodeEvent = class { constructor(type, event, nodeDatum, series) { this.type = type; this.event = event; this.defaultPrevented = false; this.datum = nodeDatum.datum; this.seriesId = series.id; } preventDefault() { this.defaultPrevented = true; } }; var SeriesGroupingChangedEvent = class { constructor(series, seriesGrouping) { this.series = series; this.seriesGrouping = seriesGrouping; this.type = "groupingChanged"; } }; function propertyAxisDirection(property) { switch (property) { case "x": return "x" /* X */; case "y": return "y" /* Y */; case "angle": return "angle" /* Angle */; case "radius": return "radius" /* Radius */; } } function axisDirectionProperty(direction) { switch (direction) { case "x" /* X */: return "x"; case "y" /* Y */: return "y"; case "angle" /* Angle */: return "angle"; case "radius" /* Radius */: return "radius"; default: return "x"; } } var Series = class extends Observable { constructor(seriesOpts) { super(); this.cleanup = new CleanupRegistry(); this.usesPlacedLabels = false; this.alwaysClip = false; this.hasChangesOnHighlight = false; this.seriesGrouping = void 0; this.NodeEvent = SeriesNodeEvent; this.internalId = createId(this); // The group node that contains the series rendering in its default (non-highlighted) state. this.contentGroup = new TranslatableGroup({ name: `${this.internalId}-content`, zIndex: 1 /* ANY_CONTENT */ }); // The group node that contains all highlighted series items. This is a performance optimisation // for large-scale data-sets, where the only thing that routinely varies is the currently // highlighted node. this.highlightGroup = new TranslatableGroup({ name: `${this.internalId}-highlight`, zIndex: 1 /* ANY_CONTENT */ }); this.highlightNodeGroup = this.highlightGroup.appendChild( new Group({ name: `${this.internalId}-highlight-node` }) ); this.highlightLabelGroup = this.highlightGroup.appendChild( new Group({ name: `${this.internalId}-highlight-label`, zIndex: 2 /* LABEL */ }) ); // Error bars etc. this.annotationGroup = new TranslatableGroup({ name: `${this.internalId}-annotation` }); // Lazily initialised labelGroup for label presentation. this.labelGroup = new TranslatableGroup({ name: `${this.internalId}-series-labels` }); this.axes = {}; this.directions = ["x" /* X */, "y" /* Y */]; // Flag to determine if we should recalculate node data. this.nodeDataRefresh = true; this.processedDataUpdated = true; this.moduleMap = new ModuleMap(); this.datumCallbackCache = /* @__PURE__ */ new Map(); this.connectsToYAxis = false; this.declarationOrder = -1; this._broughtToFront = false; this.events = new EventEmitter(); this._pickNodeCache = new LRUCache(5); // Use a wrapper to comply with the @typescript-eslint/unbound-method rule. this.fireEventWrapper = (event) => super.fireEvent(event); const { moduleCtx, pickModes, propertyKeys = {}, propertyNames = {}, canHaveAxes = false, usesPlacedLabels = false, alwaysClip = false } = seriesOpts; this.ctx = moduleCtx; this.propertyKeys = propertyKeys; this.propertyNames = propertyNames; this.canHaveAxes = canHaveAxes; this.usesPlacedLabels = usesPlacedLabels; this.pickModes = pickModes; this.alwaysClip = alwaysClip; this.highlightLabelGroup.pointerEvents = 1 /* None */; this.cleanup.register( this.ctx.eventsHub.on("data:update", (data) => this.setChartData(data)), this.ctx.eventsHub.on("highlight:change", (event) => this.onChangeHighlight(event)) ); } get pickModeAxis() { return "main"; } get id() { return this.properties?.id ?? this.internalId; } get type() { return this.constructor.type ?? ""; } get focusable() { return true; } get data() { return this._data ?? this._chartData; } set visible(newVisibility) { this.properties.visible = newVisibility; this.ctx.legendManager.toggleItem(newVisibility, this.id); this.ctx.legendManager.update(); this.visibleMaybeChanged(); } get visible() { return this.ctx.legendManager.getSeriesEnabled(this.id) ?? this.properties.visible; } get hasData() { const dataSet = this.data; if (dataSet == null) return false; return dataSet.netSize() > 0; } get tooltipEnabled() { return this.properties.tooltip?.enabled; } onDataChange() { this.nodeDataRefresh = true; this.processedDataUpdated = true; this._pickNodeCache.clear(); } setOptionsData(input) { this._data = input; this.onDataChange(); } isHighlightEnabled() { return this.properties.highlight.enabled; } setChartData(input) { this._chartData = input; if (this.data === input) { this.onDataChange(); } } onSeriesGroupingChange(prev, next) { const { internalId, type, visible } = this; if (prev) { this.ctx.seriesStateManager.deregisterSeries(this); } if (next) { this.ctx.seriesStateManager.registerSeries({ internalId, type, visible, seriesGrouping: next, // TODO: is there a better way to pass width through here? width: "width" in this.properties ? this.properties.width : 0 }); } this.fireEvent(new SeriesGroupingChangedEvent(this, next)); } getBandScalePadding() { return { inner: 1, outer: 0 }; } attachSeries(seriesContentNode, seriesNode, annotationNode) { seriesContentNode.appendChild(this.contentGroup); seriesNode.appendChild(this.highlightGroup); seriesNode.appendChild(this.labelGroup); annotationNode?.appendChild(this.annotationGroup); } detachSeries(_seriesContentNode, _seriesNode, _annotationNode) { this.contentGroup.remove(); this.highlightGroup.remove(); this.labelGroup.remove(); this.annotationGroup.remove(); } setSeriesIndex(index, forceUpdate = false) { const bringToFront = this.bringToFront(); if (!forceUpdate && index === this.declarationOrder && bringToFront === this._broughtToFront) return false; this.declarationOrder = index; this._broughtToFront = bringToFront; this.setZIndex(bringToFront ? Number.MAX_VALUE : index); this.fireEvent(new SeriesGroupingChangedEvent(this, this.seriesGrouping)); return true; } setZIndex(zIndex) { this.contentGroup.zIndex = [1 /* ANY_CONTENT */, zIndex, 0 /* FOREGROUND */]; this.highlightGroup.zIndex = [1 /* ANY_CONTENT */, zIndex, 1 /* HIGHLIGHT */]; this.labelGroup.zIndex = [1 /* ANY_CONTENT */, zIndex, 2 /* LABEL */]; this.annotationGroup.zIndex = zIndex; } renderToOffscreenCanvas() { return false; } hasHighlightOpacity() { if (!this.properties.highlight.enabled) return false; if (this.ctx.highlightManager.getActiveHighlight() == null) return false; const { unhighlightedItem, unhighlightedSeries } = this.properties.highlight; return hasDimmedOpacity(unhighlightedItem) || hasDimmedOpacity(unhighlightedSeries); } getDrawingMode(isHighlight, highlightDrawingMode = "cutout") { if (isHighlight) { return highlightDrawingMode; } return this.hasHighlightOpacity() ? this.ctx.chartService.highlight?.drawingMode ?? "overlay" : "overlay"; } getAnimationDrawingModes() { const drawingMode = this.getDrawingMode(false); return { start: { drawingMode: "overlay" }, finish: { drawingMode } }; } addEventListener(type, listener) { return super.addEventListener(type, listener); } removeEventListener(type, listener) { return super.removeEventListener(type, listener); } hasEventListener(type) { return super.hasEventListener(type); } updatedDomains() { } destroy() { this.cleanup.flush(); this.resetDatumCallbackCache(); this.ctx.seriesStateManager.deregisterSeries(this); } getPropertyValues(property, properties) { const direction = propertyAxisDirection(property); const resolvedProperty = direction == null ? property : axisDirectionProperty(this.resolveKeyDirection(direction)); const keys = properties?.[resolvedProperty]; const values = []; if (!keys) { return values; } const addValues = (...items) => { for (const value of items) { if (Array.isArray(value)) { addValues(...value); } else if (typeof value === "object") { addValues(...Object.values(value)); } else { values.push(value); } } }; addValues(...keys.map((key) => this.properties[key])); return values; } getKeyAxis(_direction) { return void 0; } getKeys(direction) { return this.getPropertyValues(axisDirectionProperty(direction), this.propertyKeys); } getKeyProperties(direction) { return this.propertyKeys[this.resolveKeyDirection(direction)] ?? []; } getNames(direction) { return this.getPropertyValues(axisDirectionProperty(direction), this.propertyNames); } getFormatterContext(property) { const { id: seriesId } = this; const keys = this.getPropertyValues(property, this.propertyKeys); const names = this.getPropertyValues(property, this.propertyNames); const out = []; for (let idx = 0; idx < keys.length; idx++) { out.push({ seriesId, key: keys[idx], name: names[idx] }); } return out; } resolveKeyDirection(direction) { return direction; } // The union of the series domain ('community') and series-option domains ('enterprise'). getDomain(direction) { const seriesDomain = this.getSeriesDomain(direction); const moduleDomains = this.moduleMap.mapModules((module2) => module2.getDomain(direction)).flat(); if (moduleDomains.length === 0) { return seriesDomain; } return { domain: seriesDomain.domain.concat(moduleDomains) }; } getRange(direction, visibleRange) { return this.getSeriesRange(direction, visibleRange); } getMinimumRangeSeries(_range) { } getMinimumRangeChart(_ranges) { return 0; } getZoomRangeFittingItems(_xVisibleRange, _yVisibleRange, _minVisibleItems) { return void 0; } getVisibleItems(_xVisibleRange, _yVisibleRange, _minVisibleItems) { return Infinity; } toCanvasFromMidPoint(nodeDatum) { const { x = 0, y = 0 } = nodeDatum.midPoint ?? {}; return Transformable.toCanvasPoint(this.contentGroup, x, y); } // Indicate that something external changed and we should recalculate nodeData. markNodeDataDirty() { this.nodeDataRefresh = true; this._pickNodeCache.clear(); this.visibleMaybeChanged(); } visibleMaybeChanged() { const { internalId, seriesGrouping, type, visible } = this; this.ctx.seriesStateManager.updateSeries({ internalId, type, visible, seriesGrouping, // TODO: is there a better way to pass width through here? width: "width" in this.properties ? this.properties.width : 0 }); } getOpacity() { const defaultOpacity = 1; if (!this.properties.highlight) { return defaultOpacity; } const { opacity = defaultOpacity } = this.getHighlightStyle(); return opacity; } getHighlightState(highlightedDatum, isHighlight, datumIndex, legendItemValues) { if (!this.properties.highlight.enabled) { return 0 /* None */; } if (isHighlight) { return 1 /* Item */; } if (highlightedDatum?.series == null) { return 0 /* None */; } if (this.isSeriesHighlighted(highlightedDatum, legendItemValues)) { const itemHighlighted = this.isItemHighlighted(highlightedDatum, datumIndex); if (itemHighlighted == null) { return 2 /* Series */; } return 4 /* OtherItem */; } return 3 /* OtherSeries */; } getHighlightStateString(datum, isHighlight, datumIndex, legendItemValues) { return toHighlightString(this.getHighlightState(datum, isHighlight, datumIndex, legendItemValues)); } onChangeHighlight(event) { const previousHighlightedDatum = event.previousHighlight; const currentHighlightedDatum = event.currentHighlight; const currentHighlightState = this.getHighlightState(currentHighlightedDatum); const previousHighlightState = this.getHighlightState(previousHighlightedDatum); this.setSeriesIndex(this.declarationOrder); const hasItemStylers = this.hasItemStylers(); if (!hasItemStylers && currentHighlightState === previousHighlightState) { this.hasChangesOnHighlight = false; return; } const { highlightedSeries, unhighlightedItem, unhighlightedSeries } = this.properties.highlight; this.hasChangesOnHighlight = hasItemStylers || !isEmptyObject(highlightedSeries) || !isEmptyObject(unhighlightedItem) || !isEmptyObject(unhighlightedSeries); } bringToFront() { return this.properties.highlight.enabled && this.properties.highlight.bringToFront && this.isSeriesHighlighted(this.ctx.highlightManager.getActiveHighlight()); } isSeriesHighlighted(highlightedDatum, _legendItemValues) { if (!this.properties.highlight.enabled) { return false; } return highlightedDatum?.series === this; } isItemHighlighted(highlightedDatum, datumIndex) { if (highlightedDatum?.datumIndex == null || datumIndex == null) return; return highlightedDatum.datumIndex === datumIndex; } getHighlightStyle(isHighlight, datumIndex, highlightState, legendItemValues) { const highlightedDatum = this.ctx.highlightManager?.getActiveHighlight(); highlightState ?? (highlightState = this.getHighlightState(highlightedDatum, isHighlight, datumIndex, legendItemValues)); return this.properties.highlight.getStyle(highlightState); } resolveMarkerDrawingModeForState(drawingMode, style2) { return resolveMarkerDrawingMode(drawingMode, style2); } filterItemStylerFillParams(fill) { if (isGradientFill(fill)) { return without(fill, ["bounds", "colorSpace", "gradient", "reverse"]); } else if (isPatternFill(fill)) { return without(fill, ["padding"]); } return fill; } getModuleTooltipParams() { return this.moduleMap.mapModules((module2) => module2.getTooltipParams()).reduce((total, current) => Object.assign(total, current), {}); } pickNodes(point, intent, exactMatchOnly = false) { const { pickModes, pickModeAxis, visible, contentGroup } = this; if (!visible || !contentGroup.visible) return; if (intent === "highlight" && !this.properties.highlight.enabled) return; if (intent === "highlight-tooltip" && !this.properties.highlight.enabled) return; let maxDistance = Infinity; if (intent === "tooltip" || intent === "highlight-tooltip") { const { tooltip } = this.properties; maxDistance = typeof tooltip.range === "number" ? tooltip.range : Infinity; exactMatchOnly || (exactMatchOnly = tooltip.range === "exact"); } else if (intent === "event" || intent === "context-menu") { const { nodeClickRange } = this.properties; maxDistance = typeof nodeClickRange === "number" ? nodeClickRange : Infinity; exactMatchOnly || (exactMatchOnly = nodeClickRange === "exact"); } const selectedPickModes = pickModes.filter( (m) => !exactMatchOnly || m === 0 /* EXACT_SHAPE_MATCH */ ); const { x, y } = point; const key = JSON.stringify({ x, y, maxDistance, selectedPickModes }); if (this._pickNodeCache.has(key)) { return this._pickNodeCache.get(key); } for (const pickMode of selectedPickModes) { let result; switch (pickMode) { case 0 /* EXACT_SHAPE_MATCH */: { const exact = this.pickNodesExactShape(point); result = exact.length === 0 ? void 0 : { datums: exact, distance: 0 }; break; } case 1 /* NEAREST_NODE */: { const closest = this.pickNodeClosestDatum(point); const exact = closest?.distance === 0 ? this.pickNodesExactShape(point) : void 0; if (exact != null && exact.length !== 0) { result = { datums: exact, distance: 0 }; } else if (closest) { result = { datums: [closest.datum], distance: closest.distance }; } else { result = void 0; } break; } case 2 /* AXIS_ALIGNED */: { const closest = pickModeAxis == null ? void 0 : this.pickNodeMainAxisFirst(point, pickModeAxis === "main-category"); result = closest == null ? void 0 : { datums: [closest.datum], distance: closest.distance }; break; } } if (result && result.distance <= maxDistance) { return this._pickNodeCache.set(key, { pickMode, datums: result.datums, distance: result.distance }); } } return this._pickNodeCache.set(key, void 0); } pickNodesExactShape(point) { const datums = []; for (const node of this.contentGroup.pickNodes(point.x, point.y)) { const datum = node.closestDatum(); if (datum != null && datum.missing !== true) { datums.push(datum); } } return datums; } pickNodeClosestDatum(_point) { throw new Error("AG Charts - Series.pickNodeClosestDatum() not implemented"); } pickNodeNearestDistantObject(point, items) { const match = nearestSquared(point.x, point.y, items); const datum = match.nearest?.closestDatum(); if (datum != null && datum.missing !== true) { return { datum, distance: Math.sqrt(match.distanceSquared) }; } } pickNodeMainAxisFirst(_point, _requireCategoryAxis) { throw new Error("AG Charts - Series.pickNodeMainAxisFirst() not implemented"); } getLabelData() { return []; } updatePlacedLabelData(_labels) { return; } fireEvent(event) { callWithContext([this.properties, this.ctx.chartService], this.fireEventWrapper, event); } fireNodeClickEvent(event, datum) { const clickEvent = new this.NodeEvent("seriesNodeClick", event, datum, this); this.fireEvent(clickEvent); return !clickEvent.defaultPrevented; } fireNodeDoubleClickEvent(event, datum) { const clickEvent = new this.NodeEvent("seriesNodeDoubleClick", event, datum, this); this.fireEvent(clickEvent); return !clickEvent.defaultPrevented; } createNodeContextMenuActionEvent(event, datum) { return new this.NodeEvent("nodeContextMenuAction", event, datum, this); } onLegendInitialState(legendType, initialState) { const { visible = true, itemId, legendItemName } = initialState ?? {}; this.toggleSeriesItem(visible, legendType, itemId, legendItemName); } onLegendItemClick(event) { const { enabled, itemId, series, legendType } = event; const legendItemName = "legendItemName" in this.properties ? this.properties.legendItemName : void 0; const legendItemKey = "legendItemKey" in this.properties ? this.properties.legendItemKey : void 0; const matchedLegendItemName = legendItemName != void 0 && legendItemName === event.legendItemName; if (series.id === this.id || matchedLegendItemName || legendItemKey != void 0) { this.toggleSeriesItem(enabled, legendType, itemId, legendItemName, event); } } onLegendItemDoubleClick(event) { const { enabled, itemId, series, numVisibleItems, legendType } = event; const legendItemName = "legendItemName" in this.properties ? this.properties.legendItemName : void 0; const legendItemKey = "legendItemKey" in this.properties ? this.properties.legendItemKey : void 0; const matchedLegendItemName = legendItemName != void 0 && legendItemName === event.legendItemName; if (series.id === this.id || matchedLegendItemName || legendItemKey != void 0) { this.toggleSeriesItem(true, legendType, itemId, legendItemName, event); } else if (enabled && numVisibleItems === 1) { this.toggleSeriesItem(true, legendType, void 0, legendItemName); } else { this.toggleSeriesItem(false, legendType, void 0, legendItemName); } } toggleSeriesItem(enabled, legendType, itemId, legendItemName, legendEvent) { const seriesId = this.id; if (enabled || legendType !== "category") { this.visible = enabled; } this.nodeDataRefresh = true; this._pickNodeCache.clear(); const event = { type: "seriesVisibilityChange", seriesId, itemId, legendItemName: legendEvent?.legendItemName ?? legendItemName, visible: enabled }; this.fireEvent(event); this.ctx.legendManager.toggleItem(enabled, seriesId, itemId, legendItemName); } isEnabled() { return this.visible; } getModuleMap() { return this.moduleMap; } createModuleContext() { return { ...this.ctx, series: this }; } getAxisValueText(axis, source, value, datum, key, legendItemName, allowNull) { const { id: seriesId, properties } = this; return axis.formatDatum( properties, value, source, seriesId, legendItemName, datum, key, void 0, void 0, void 0, allowNull ); } getLabelText(value, datum, key, property, domain, label, baseParams, allowNullValue = false) { if (value == null && !allowNullValue) return ""; const { axes, canHaveAxes, ctx, id: seriesId, properties } = this; const source = "series-label"; const legendItemName = "legendItemName" in properties ? properties.legendItemName : void 0; const params = { seriesId: this.id, ...baseParams }; const direction = canHaveAxes ? propertyAxisDirection(property) : void 0; const axis = direction == null ? void 0 : axes[this.resolveKeyDirection(direction)]; if (axis != null) { return axis.formatDatum( properties, value, source, seriesId, legendItemName, datum, key, domain, label, params, allowNullValue ); } const { formatManager } = ctx; const formatInContext = this.callWithContext.bind(this); const format = (formatParams) => label.formatValue(formatInContext, formatParams.type, formatParams.value, params) ?? formatManager.format(formatInContext, formatParams) ?? (value == null ? "" : String(value)); const boundSeries = this.getFormatterContext(property); switch (property) { case "y": case "color": case "size": { const fractionDigits = void 0; return format({ type: "number", value, datum, seriesId, legendItemName, key, source, property, domain, boundSeries, fractionDigits, visibleDomain: void 0 }); } case "x": case "radius": case "angle": case "label": case "secondaryLabel": case "calloutLabel": case "sectorLabel": case "legendItem": return format({ type: "category", value, datum, seriesId, legendItemName, key, source, property, domain, boundSeries }); } } getMarkerStyle(marker, { datumIndex, datum, point }, params, opts, defaultOverrideStyle = { size: point?.size ?? marker.size ?? 0 }, inheritedStyle) { const { itemStyler } = marker; const { highlightState, isHighlight = false, checkForHighlight = true, resolveMarkerSubPath = ["marker"], resolveStyler = false } = opts ?? {}; const resolvePath2 = ["series", `${this.declarationOrder}`, ...resolveMarkerSubPath]; if (resolveStyler) { const resolveOpt = { permissivePath: true }; const resolved = this.ctx.optionsGraphService.resolvePartial(resolvePath2, defaultOverrideStyle, resolveOpt); if (resolved) { defaultOverrideStyle = { ...resolved, size: resolved.size ?? defaultOverrideStyle.size }; } } const highlightStyle = checkForHighlight ? this.getHighlightStyle(isHighlight, datumIndex, highlightState) : void 0; const baseStyle = mergeDefaults(highlightStyle, defaultOverrideStyle, marker.getStyle(), inheritedStyle); let markerStyle = baseStyle; if (itemStyler && params) { const highlight5 = this.ctx.highlightManager?.getActiveHighlight(); const highlightStateString = this.getHighlightStateString(highlight5, isHighlight, datumIndex); const fill = this.filterItemStylerFillParams(markerStyle.fill); const style2 = this.cachedCallWithContext(itemStyler, { seriesId: this.id, ...markerStyle, fill, ...params, highlightState: highlightStateString, datum }); const resolved = this.ctx.optionsGraphService.resolvePartial(resolvePath2, style2); markerStyle = mergeDefaults(resolved, markerStyle); } return markerStyle; } applyMarkerStyle(style2, markerNode, point, fillBBox, { applyTranslation = true, selected = true } = {}) { const { shape, size = 0 } = style2; const visible = this.visible && size > 0 && point && !Number.isNaN(point.x) && !Number.isNaN(point.y); markerNode.setStyleProperties(style2, fillBBox); if (applyTranslation) { markerNode.setProperties({ visible, shape, size, x: point?.x, y: point?.y, scalingCenterX: point?.x, scalingCenterY: point?.y }); } else { markerNode.setProperties({ visible, shape, size }); } if (!selected) { markerNode.fillOpacity *= CROSS_FILTER_MARKER_FILL_OPACITY_FACTOR; markerNode.strokeOpacity *= CROSS_FILTER_MARKER_STROKE_OPACITY_FACTOR; } if (typeof shape === "function" && !markerNode.dirtyPath) { markerNode.path.clear(true); markerNode.updatePath(); markerNode.checkPathDirty(); const bb = markerNode.getBBox(); if (point != null && bb.isFinite()) { const center2 = bb.computeCenter(); const [dx, dy] = ["x", "y"].map( (key) => (style2.strokeWidth ?? 0) + Math.abs(center2[key] - point[key]) ); point.focusSize = Math.max(bb.width + dx, bb.height + dy); } } } get nodeDataDependencies() { return this._nodeDataDependencies ?? { seriesRectWidth: Number.NaN, seriesRectHeight: Number.NaN }; } checkResize(newSeriesRect) { const { width: seriesRectWidth, height: seriesRectHeight } = newSeriesRect ?? { width: Number.NaN, height: Number.NaN }; const newNodeDataDependencies = newSeriesRect ? { seriesRectWidth, seriesRectHeight } : void 0; const resize = jsonDiff(this.nodeDataDependencies, newNodeDataDependencies) != null; if (resize) { this._nodeDataDependencies = newNodeDataDependencies; this.markNodeDataDirty(); } return resize; } pickFocus(_opts) { return void 0; } resetDatumCallbackCache() { this.datumCallbackCache.clear(); } cachedDatumCallback(id, fn) { const { datumCallbackCache } = this; const existing = datumCallbackCache.get(id); if (existing != null) return existing; try { const value = fn(); datumCallbackCache.set(id, value); return value; } catch (error2) { logger_exports.error(String(error2)); } } cachedCallWithContext(fn, params) { return this.ctx.callbackCache.call([this.properties, this.ctx.chartService], fn, params); } callWithContext(fn, params) { return callWithContext([this.properties, this.ctx.chartService], fn, params); } formatTooltipWithContext(tooltip, content, params) { return tooltip.formatTooltip([this.properties, this.ctx.chartService], content, params); } // @todo(AG-13777) - Remove this function (see CartesianSeries.ts) minTimeInterval() { return; } needsDataModelDiff() { return !this.ctx.animationManager.isSkipped() || !!this.chart?.flashOnUpdateEnabled; } }; Series.className = "Series"; __decorateClass([ ActionOnSet({ changeValue: function(newVal, oldVal) { this.onSeriesGroupingChange(oldVal, newVal); } }) ], Series.prototype, "seriesGrouping", 2); // packages/ag-charts-community/src/chart/series/dataModelSeries.ts var DataModelSeries = class extends Series { constructor({ clipFocusBox, categoryKey, ...seriesOpts }) { super(seriesOpts); this.categoryKey = categoryKey; this.clipFocusBox = clipFocusBox ?? true; } dataCount() { return this.processedData?.dataSources?.get(this.id)?.data?.length ?? 0; } invalidDataCount() { return this.processedData?.invalidDataCount?.get(this.id) ?? 0; } missingDataCount() { return this.dataModel?.resolveMissingDataCount(this) ?? 0; } get hasData() { return Math.max(0, this.dataCount() - this.invalidDataCount() - this.missingDataCount()) > 0; } getScaleInformation({ xScale, yScale }) { const isContinuousX = ContinuousScale.is(xScale); const isContinuousY = ContinuousScale.is(yScale); return { isContinuousX, isContinuousY, xScaleType: xScale?.type, yScaleType: yScale?.type }; } getModulePropertyDefinitions() { const xScale = this.axes["x" /* X */]?.scale; const yScale = this.axes["y" /* Y */]?.scale; return this.moduleMap.mapModules((m) => m.getPropertyDefinitions(this.getScaleInformation({ xScale, yScale }))).flat(); } // Request data, but with message dispatching to series-options (modules). async requestDataModel(dataController, dataSet, opts) { opts.props.push(...this.getModulePropertyDefinitions()); const { dataModel, processedData } = await dataController.request( this.id, dataSet ?? DataSet.empty(), opts ); this.dataModel = dataModel; this.processedData = processedData; this.events.emit("data-processed", { dataModel, processedData }); return { dataModel, processedData }; } isProcessedDataAnimatable() { const { processedData, ctx } = this; if (!processedData) return false; const nodeData = this.getNodeData(); if (nodeData != null && nodeData.length > ctx.animationManager.maxAnimatableItems) return false; const validationResults = processedData.reduced?.animationValidation; if (!validationResults) return true; const { orderedKeys, uniqueKeys } = validationResults; return orderedKeys && uniqueKeys; } checkProcessedDataAnimatable() { if (!this.isProcessedDataAnimatable()) { this.ctx.animationManager.skipCurrentBatch(); } } findNodeDatum(itemId) { return findNodeDatumInArray(itemId, this.getNodeData()); } pickFocus(opts) { const nodeData = this.getNodeData(); if (nodeData === void 0 || nodeData.length === 0) { return; } const datumIndex = this.computeFocusDatumIndex(opts, nodeData); if (datumIndex === void 0) { return; } const { clipFocusBox } = this; const datum = nodeData[datumIndex]; const derivedOpts = { ...opts, datumIndex }; const bounds = this.computeFocusBounds(derivedOpts); if (bounds !== void 0) { return { bounds, clipFocusBox, datum, datumIndex }; } } pickNodesExactShape(point) { const datums = super.pickNodesExactShape(point); datums.sort((a, b) => a.datumIndex - b.datumIndex); return datums; } isDatumEnabled(nodeData, datumIndex) { const { missing = false, enabled = true, focusable = true } = nodeData[datumIndex]; return !missing && enabled && focusable; } computeFocusDatumIndex(opts, nodeData) { const searchBackward = (datumIndex2, delta3) => { while (datumIndex2 >= 0 && !this.isDatumEnabled(nodeData, datumIndex2)) { datumIndex2 += delta3; } return datumIndex2 === -1 ? void 0 : datumIndex2; }; const searchForward = (datumIndex2, delta3) => { while (datumIndex2 < nodeData.length && !this.isDatumEnabled(nodeData, datumIndex2)) { datumIndex2 += delta3; } return datumIndex2 === nodeData.length ? void 0 : datumIndex2; }; let datumIndex; const clampedIndex = clamp(0, opts.datumIndex, nodeData.length - 1); if (opts.datumIndexDelta < 0) { datumIndex = searchBackward(clampedIndex, opts.datumIndexDelta); } else if (opts.datumIndexDelta > 0) { datumIndex = searchForward(clampedIndex, opts.datumIndexDelta); } else { datumIndex = searchForward(clampedIndex, 1) ?? searchBackward(clampedIndex, -1); } if (datumIndex === void 0) { if (opts.datumIndexDelta === 0) { return; } else { return opts.datumIndex - opts.datumIndexDelta; } } else { return datumIndex; } } // Workaround - it would be nice if this difference didn't exist dataModelPropertyIsKey(key) { const { processedData } = this; if (!processedData) return false; return processedData.defs.keys.some((def) => def.id === key && def.idsMap?.get(this.id)?.has(key) === true); } keysOrValues(xKey) { const { dataModel, processedData } = this; if (!dataModel || !processedData) return []; return this.dataModelPropertyIsKey(xKey) ? dataModel.resolveKeysById(this, xKey, processedData) : dataModel.resolveColumnById(this, xKey, processedData); } sortOrder(xKey) { const { dataModel, processedData } = this; if (!dataModel || !processedData) return; return this.dataModelPropertyIsKey(xKey) ? dataModel.getKeySortOrder(this, xKey, processedData) : dataModel.getColumnSortOrder(this, xKey, processedData); } getCategoryKey() { return this.categoryKey; } getCategoryValue(datumIndex) { const { processedData, dataModel } = this; const categoryKey = this.getCategoryKey(); if (!processedData || !dataModel || !categoryKey) return; const invalid = processedData.invalidData?.get(this.id)?.[datumIndex] ?? false; return invalid ? void 0 : this.keysOrValues(categoryKey)[datumIndex]; } datumIndexForCategoryValue(categoryValue) { const { processedData, dataModel } = this; const categoryKey = this.getCategoryKey(); if (!processedData || !dataModel || !categoryKey) return; categoryValue = categoryValue.valueOf(); const invalidValues = processedData.invalidData?.get(this.id); const xValues = this.keysOrValues(categoryKey); for (let datumIndex = 0; datumIndex < xValues.length; datumIndex += 1) { if (invalidValues?.[datumIndex] === true) continue; const xValue = xValues[datumIndex]?.valueOf(); if (objectsEqual(categoryValue, xValue)) return datumIndex; } } }; // packages/ag-charts-community/src/chart/series/cartesian/cartesianSeries.ts var DEFAULT_CARTESIAN_DIRECTION_KEYS = { ["x" /* X */]: ["xKey"], ["y" /* Y */]: ["yKey"] }; var DEFAULT_CARTESIAN_DIRECTION_NAMES = { ["x" /* X */]: ["xName"], ["y" /* Y */]: ["yName"] }; var CartesianSeriesNodeEvent = class extends SeriesNodeEvent { constructor(type, nativeEvent, datum, series) { super(type, nativeEvent, datum, series); this.xKey = series.properties.xKey; this.yKey = series.properties.yKey; } }; var CartesianSeriesProperties = class extends SeriesProperties { constructor() { super(...arguments); this.xKeyAxis = "x"; this.yKeyAxis = "y"; this.pickOutsideVisibleMinorAxis = false; this.segmentation = new Segmentation(); } }; __decorateClass([ addFakeTransformToInstanceProperty ], CartesianSeriesProperties.prototype, "xKeyAxis", 2); __decorateClass([ addFakeTransformToInstanceProperty ], CartesianSeriesProperties.prototype, "yKeyAxis", 2); __decorateClass([ addFakeTransformToInstanceProperty ], CartesianSeriesProperties.prototype, "legendItemName", 2); __decorateClass([ addFakeTransformToInstanceProperty ], CartesianSeriesProperties.prototype, "pickOutsideVisibleMinorAxis", 2); __decorateClass([ addFakeTransformToInstanceProperty ], CartesianSeriesProperties.prototype, "segmentation", 2); var RENDER_TO_OFFSCREEN_CANVAS_THRESHOLD = 100; var CartesianSeries = class extends DataModelSeries { constructor({ pathsPerSeries = ["path"], pathsZIndexSubOrderOffset = [], datumSelectionGarbageCollection = true, animationAlwaysUpdateSelections = false, animationAlwaysPopulateNodeData = false, segmentedDataNodes = true, animationResetFns, propertyKeys, propertyNames, ...otherOpts }) { super({ propertyKeys, propertyNames, canHaveAxes: true, ...otherOpts }); this.NodeEvent = CartesianSeriesNodeEvent; this.dataNodeGroup = this.contentGroup.appendChild( new SegmentedGroup({ name: `${this.id}-series-dataNodes`, zIndex: 1 }) ); this.labelGroup = this.contentGroup.appendChild( new TranslatableGroup({ name: `${this.id}-series-labels` }) ); this.labelSelection = Selection.select(this.labelGroup, Text); this.highlightSelection = Selection.select(this.highlightNodeGroup, () => this.nodeFactory()); this.highlightLabelSelection = Selection.select( this.highlightLabelGroup, Text ); this.annotationSelections = /* @__PURE__ */ new Set(); this.seriesBelowStackContext = void 0; this.debug = debugLogger_exports.create(); if (!propertyKeys || !propertyNames) throw new Error(`Unable to initialise series type ${this.type}`); this.opts = { pathsPerSeries, pathsZIndexSubOrderOffset, propertyKeys, propertyNames, animationResetFns, animationAlwaysUpdateSelections, animationAlwaysPopulateNodeData, datumSelectionGarbageCollection, segmentedDataNodes }; this.paths = pathsPerSeries.map((path) => { return new SegmentedPath({ name: `${this.id}-${path}` }); }); this.datumSelection = Selection.select( this.dataNodeGroup, () => this.nodeFactory(), datumSelectionGarbageCollection ); this.animationState = new StateMachine( "empty", { empty: { update: { target: "ready", action: (data) => this.animateEmptyUpdateReady(data) }, reset: "empty", skip: "ready", disable: "disabled" }, ready: { updateData: "waiting", clear: "clearing", highlight: (data) => this.animateReadyHighlight(data), resize: (data) => this.animateReadyResize(data), reset: "empty", skip: "ready", disable: "disabled" }, waiting: { update: { target: "ready", action: (data) => { if (this.ctx.animationManager.isSkipped()) { this.resetAllAnimation(data); } else { this.animateWaitingUpdateReady(data); } } }, reset: "empty", skip: "ready", disable: "disabled" }, disabled: { update: (data) => this.resetAllAnimation(data), reset: "empty" }, clearing: { update: { target: "empty", action: (data) => this.animateClearingUpdateEmpty(data) }, reset: "empty", skip: "ready" } }, this.checkProcessedDataAnimatable.bind(this) ); this.cleanup.register( this.ctx.eventsHub.on("legend:item-click", (event) => this.onLegendItemClick(event)), this.ctx.eventsHub.on("legend:item-double-click", (event) => this.onLegendItemDoubleClick(event)) ); } get contextNodeData() { return this._contextNodeData; } getNodeData() { return this.contextNodeData?.nodeData; } getKeyAxis(direction) { if (this.shouldFlipXY()) { if (direction === "x" /* X */) return this.properties.yKeyAxis; if (direction === "y" /* Y */) return this.properties.xKeyAxis; } if (direction === "x" /* X */) return this.properties.xKeyAxis; if (direction === "y" /* Y */) return this.properties.yKeyAxis; } attachSeries(seriesContentNode, seriesNode, annotationNode) { super.attachSeries(seriesContentNode, seriesNode, annotationNode); this.attachPaths(this.paths); } detachSeries(seriesContentNode, seriesNode, annotationNode) { super.detachSeries(seriesContentNode, seriesNode, annotationNode); this.detachPaths(this.paths); } updatedDomains() { this.animationState.transition("updateData"); } attachPaths(paths) { for (const path of paths) { this.contentGroup.appendChild(path); } } detachPaths(paths) { for (const path of paths) { path.remove(); } } renderToOffscreenCanvas() { const nodeData = this.getNodeData(); return nodeData != null && nodeData.length > RENDER_TO_OFFSCREEN_CANVAS_THRESHOLD; } resetAnimation(phase) { if (phase === "initial") { this.animationState.transition("reset"); } else if (phase === "ready") { this.animationState.transition("skip"); } else if (phase === "disabled") { this.animationState.transition("disable"); } } destroy() { super.destroy(); this._contextNodeData = void 0; } isSeriesHighlighted(highlightedDatum) { if (!this.properties.highlight.enabled) { return false; } const { series, legendItemName: activeLegendItemName } = highlightedDatum ?? {}; const { legendItemName } = this.properties; return series === this || legendItemName != null && legendItemName === activeLegendItemName; } strokewidthChange() { const unhighlightedStrokeWidth = ("strokeWidth" in this.properties && this.properties.strokeWidth) ?? 0; const highlightedSeriesStrokeWidth = this.properties.highlight.highlightedSeries.strokeWidth ?? unhighlightedStrokeWidth; const highlightedItemStrokeWidth = this.properties.highlight.highlightedItem?.strokeWidth ?? unhighlightedStrokeWidth; return unhighlightedStrokeWidth > highlightedItemStrokeWidth || highlightedSeriesStrokeWidth > highlightedItemStrokeWidth; } update({ seriesRect }) { const { _contextNodeData: previousContextData } = this; const resize = this.checkResize(seriesRect); const itemHighlighted = this.updateHighlightSelection(); const series = this; this.contentGroup.batchedUpdate(function updateSelections() { const dataChanged = series.updateSelections(); const segments = series.contextNodeData?.segments; if (series.opts.segmentedDataNodes) { series.dataNodeGroup.segments = segments ?? series.dataNodeGroup.segments; } else { series.dataNodeGroup.segments = void 0; } series.updateNodes(itemHighlighted, resize || dataChanged); }); const animationData = this.getAnimationData(seriesRect, previousContextData); if (!animationData) return; if (resize) { this.animationState.transition("resize", animationData); } this.animationState.transition("update", animationData); this.processedDataUpdated = false; } createStackContext() { return void 0; } // ============================================================================ // createNodeData() Pattern Helpers // ============================================================================ // These methods support the optimized createNodeData() pattern with incremental updates. // See series-performance-optimization.md for detailed documentation. /** * Determines if incremental node updates are possible based on common criteria. * * Returns true if existing nodeData exists AND either: * - processedData has a changeDescription (partial update), OR * - animation is disabled (large dataset) * * Series may extend with additional conditions by OR'ing additional checks: * ``` * const canIncrementallyUpdate = * this.canIncrementallyUpdateNodes() || (existingNodeData && seriesSpecificCondition); * ``` * * @param additionalCondition - Optional series-specific condition to OR with base conditions */ canIncrementallyUpdateNodes(additionalCondition = false) { const existingNodeData = this.contextNodeData?.nodeData; if (existingNodeData == null) return false; const { processedData } = this; if (!processedData) return false; return processedData.changeDescription != null || !processedDataIsAnimatable(processedData) || additionalCondition; } /** * Trims the node array after incremental updates. * * Call at the end of createNodeData() when using incremental updates: * ``` * if (ctx.canIncrementallyUpdate) { * this.trimIncrementalNodeArray(ctx.nodes, ctx.nodeIndex); * } * ``` * * @param nodes - The node array to trim * @param nodeIndex - The final write position (new array length) */ trimIncrementalNodeArray(nodes, nodeIndex) { if (nodeIndex < nodes.length) { nodes.length = nodeIndex; } } // ============================================================================ // createNodeData() Template Method Pattern // ============================================================================ // The template method pattern pulls the common createNodeData() flow into the // base class. Subclasses implement hooks to customize behaviour. // // Subclasses that have been migrated implement: // - createNodeDatumContext() - cache expensive lookups // - populateNodeData() - iterate and create/update datums // - initializeResult() - create result object shell // // And optionally override: // - validateCreateNodeDataPreconditions() - custom axis validation // - finalizeNodeData() - custom cleanup (multiple arrays, sorting) // - assembleResult() - add computed fields (segments) /** * Template method for creating node data. * * This implementation provides the common algorithm skeleton: * 1. Validate preconditions (axes, dataModel, processedData) * 2. Create context with cached values * 3. Initialise result object * 4. Populate node data (strategy selection inside) * 5. Finalize (trim arrays, post-processing) * 6. Assemble and return result * * Subclasses that have been migrated to the template pattern should NOT * override this method. Instead, implement the abstract hooks. * * Subclasses that haven't been migrated yet can override this method entirely. */ createNodeData() { const validation = this.validateCreateNodeDataPreconditions(); if (!validation) return void 0; const { xAxis, yAxis } = validation; const ctx = this.createNodeDatumContext(xAxis, yAxis); if (!ctx) return this.getEmptyResult(); const result = this.initializeResult(ctx); if (!this.visible && (this.seriesGrouping == null && !this.opts.animationAlwaysPopulateNodeData || !ctx.animationEnabled)) return result; this.populateNodeData(ctx); this.finalizeNodeData(ctx); return this.assembleResult(ctx, result); } // ============================================================================ // Template Method Hooks (Subclasses MUST implement when NOT overriding createNodeData) // ============================================================================ // Series that override createNodeData() entirely don't need these hooks. // Series using the template method MUST implement createNodeDatumContext, // populateNodeData, and initializeResult. /** * Creates the shared context for datum creation/update operations. * Called once per createNodeData() invocation. * * MUST return an object extending CartesianCreateNodeDataContext with: * - Cached data arrays (resolved from dataModel) * - Cached scales (from axes) * - Pre-computed positioning values * - Property key lookups * - Incremental update state (canIncrementallyUpdate, nodes, nodeIndex) * * @returns Context object or undefined if context cannot be created */ createNodeDatumContext(_xAxis, _yAxis) { throw new Error( `${this.constructor.name}: createNodeDatumContext() must be implemented when using the template method pattern` ); } /** * Populates the node data array by iterating over data. * * Strategy selection happens inside this method: * - Simple iteration for ungrouped data * - Grouped iteration for grouped data * - Aggregation iteration for large datasets * * Implementations should use the upsert pattern: * - prepareNodeDatumState() → validate and compute state * - upsertNodeDatum() → create or update node in place */ populateNodeData(_ctx) { throw new Error( `${this.constructor.name}: populateNodeData() must be implemented when using the template method pattern` ); } /** * Initializes the result context object that will be returned. * Called before populate phase to allow early return for invisible series. * * Should create the context with: * - itemId * - nodeData (reference to ctx.nodes) * - labelData (reference or separate array) * - scales * - visible * - styles */ initializeResult(_ctx) { throw new Error( `${this.constructor.name}: initializeResult() must be implemented when using the template method pattern` ); } // ============================================================================ // Virtual Hooks (Subclasses MAY Override) // ============================================================================ /** * Validates preconditions for createNodeData. * Default: checks axes, dataModel, and processedData exist. * * Override for specialized axis requirements (e.g., category vs value axis in bar series). */ validateCreateNodeDataPreconditions() { const xAxis = this.axes["x" /* X */]; const yAxis = this.axes["y" /* Y */]; if (!xAxis || !yAxis || !this.dataModel || !this.processedData) { return void 0; } return { xAxis, yAxis }; } /** * Returns empty result when context creation fails. * Default: returns undefined. */ getEmptyResult() { return void 0; } /** * Finalizes node data after population phase. * Default: trims incremental node arrays. * * Override to add post-processing (sorting, additional cleanup, multiple arrays). */ finalizeNodeData(ctx) { if (ctx.canIncrementallyUpdate) { this.trimIncrementalNodeArray(ctx.nodes, ctx.nodeIndex); } } /** * Assembles final result from context and initialized result. * Default: returns the initialized result unchanged. * * Override to add computed fields (segments, groupScale). */ assembleResult(_ctx, result) { return result; } updateSelections() { var _a; const animationSkipUpdate = !this.opts.animationAlwaysUpdateSelections && this.ctx.animationManager.isSkipped(); if (!this.visible && animationSkipUpdate) { return false; } const { nodeDataRefresh } = this; if (!nodeDataRefresh && !this.isPathOrSelectionDirty()) { return false; } if (nodeDataRefresh) { this.nodeDataRefresh = false; this.debug(`CartesianSeries.updateSelections() - calling createNodeData() for`, this.id); this.markQuadtreeDirty(); this._contextNodeData = this.createNodeData(); const animationValid = this.isProcessedDataAnimatable(); if (this._contextNodeData) { (_a = this._contextNodeData).animationValid ?? (_a.animationValid = animationValid); const nodeDataSize = this._contextNodeData.nodeData?.length; if (nodeDataSize != null) { debugMetrics_exports.record(`${this.type}:nodeData`, nodeDataSize); } } const { dataModel, processedData } = this; if (dataModel !== void 0 && processedData !== void 0) { this.events.emit("data-update", { dataModel, processedData }); } this.updateSeriesSelections(); } return nodeDataRefresh; } updateSeriesSelections() { const { datumSelection, labelSelection, paths } = this; const contextData = this._contextNodeData; if (!contextData) return; const { nodeData, labelData, itemId } = contextData; this.updatePaths({ itemId, contextData, paths }); this.datumSelection = this.updateDatumSelection({ nodeData, datumSelection }); this.labelGroup.batchedUpdate(() => { this.labelSelection = this.updateLabelSelection({ labelData, labelSelection }) ?? labelSelection; }); } getShapeFillBBox() { const { axes } = this; const xAxis = axes["x" /* X */]; const yAxis = axes["y" /* Y */]; const [axisX1, axisX2] = findMinMax(xAxis?.range ?? [0, 1]); const [axisY1, axisY2] = findMinMax(yAxis?.range ?? [0, 1]); const xSeriesDomain = extractDomain(this.getSeriesDomain("x" /* X */)); const xSeriesRange = [xAxis?.scale.convert(xSeriesDomain.at(0)), xAxis?.scale.convert(xSeriesDomain.at(-1))]; const ySeriesDomain = extractDomain(this.getSeriesDomain("y" /* Y */)); const ySeriesRange = [yAxis?.scale.convert(ySeriesDomain.at(0)), yAxis?.scale.convert(ySeriesDomain.at(-1))]; const [seriesX1, seriesX2] = findMinMax(xSeriesRange); const [seriesY1, seriesY2] = findMinMax(ySeriesRange); return { axis: new BBox(axisX1, axisY1, axisX2 - axisX1, axisY2 - axisY1), series: new BBox(seriesX1, seriesY1, seriesX2 - seriesX1, seriesY2 - seriesY1) }; } updateNodes(itemHighlighted, nodeRefresh) { const { highlightSelection, datumSelection } = this; const animationEnabled = !this.ctx.animationManager.isSkipped(); const visible = this.visible && this._contextNodeData != null; this.contentGroup.visible = animationEnabled || visible; this.highlightGroup.visible = (animationEnabled || visible) && itemHighlighted; this.updateDatumStyles({ datumSelection: highlightSelection, isHighlight: true }); const drawingMode = this.ctx.chartService.highlight?.drawingMode ?? "overlay"; this.updateDatumNodes({ datumSelection: highlightSelection, isHighlight: true, drawingMode }); this.highlightLabelGroup.batchedUpdate(() => { this.updateLabelNodes({ labelSelection: this.highlightLabelSelection, isHighlight: true }); }); this.animationState.transition("highlight", highlightSelection); const { dataNodeGroup, labelSelection, paths, labelGroup } = this; const { itemId } = this.contextNodeData ?? {}; this.updatePathNodes({ itemId, paths, visible, animationEnabled }); dataNodeGroup.visible = animationEnabled || visible; labelGroup.visible = visible; if (!dataNodeGroup.visible) { return; } if (this.hasItemStylers()) { this.updateDatumStyles({ datumSelection, isHighlight: false }); } const redrawAll = this.strokewidthChange() || this.hasChangesOnHighlight; if (nodeRefresh || redrawAll) { this.updateDatumNodes({ datumSelection, isHighlight: false, drawingMode: "overlay" }); if (!this.usesPlacedLabels) { this.labelGroup.batchedUpdate(() => { this.updateLabelNodes({ labelSelection, isHighlight: false }); }); } } } getHighlightData(_nodeData, highlightedItem) { return highlightedItem ? [{ ...highlightedItem }] : void 0; } getHighlightLabelData(labelData, highlightedItem) { const labelItems = labelData.filter( (ld) => ld.datum === highlightedItem.datum && ld.itemId === highlightedItem.itemId ); return labelItems.length === 0 ? void 0 : labelItems; } updateHighlightSelection() { const { highlightSelection, highlightLabelSelection, _contextNodeData: contextNodeData } = this; if (!contextNodeData) return false; const highlightedDatum = this.ctx.highlightManager?.getActiveHighlight(); const seriesHighlighted = this.isSeriesHighlighted(highlightedDatum); const item = seriesHighlighted && highlightedDatum?.datum ? highlightedDatum : void 0; if (item == null) return false; const { nodeData, labelData } = contextNodeData; const highlightItems = this.getHighlightData(nodeData, item); this.highlightSelection = this.updateHighlightSelectionItem({ items: highlightItems, highlightSelection }); const highlightLabelItems = this.getHighlightLabelData(labelData, item) ?? []; this.highlightLabelSelection = this.updateLabelSelection({ labelData: highlightLabelItems, labelSelection: highlightLabelSelection }) ?? highlightLabelSelection; return true; } markQuadtreeDirty() { this.quadtree = void 0; } *datumNodesIter() { for (const { node } of this.datumSelection) { if (node.datum.missing === true) continue; yield node; } } getQuadTree() { if (this.quadtree === void 0) { const canvas = this.ctx.scene?.canvas ?? { width: 0, height: 0 }; const canvasRect = new BBox(0, 0, canvas.width, canvas.height); this.quadtree = new QuadtreeNearest(100, 10, canvasRect); this.initQuadTree(this.quadtree); } return this.quadtree; } initQuadTree(_quadtree) { } pickNodeDataExactShape(point) { const { x, y } = point; const { dataNodeGroup } = this; const matches = dataNodeGroup.pickNodes(x, y).filter((match) => match.datum.missing !== true); if (matches.length !== 0) { const datums = matches.map((match) => match.datum); return datums; } } pickModulesExactShape(point) { for (const mod of this.moduleMap.modules()) { const { datum } = mod.pickNodeExact(point) ?? {}; if (datum == null) continue; if (datum?.missing === true) continue; return [datum]; } } pickNodesExactShape(point) { const result = super.pickNodesExactShape(point); if (result.length !== 0) { return result; } return this.pickNodeDataExactShape(point) ?? this.pickModulesExactShape(point) ?? []; } pickNodeDataClosestDatum(point) { const { x, y } = point; const { axes, _contextNodeData: contextNodeData } = this; if (!contextNodeData) return; const xAxis = axes["x" /* X */]; const yAxis = axes["y" /* Y */]; const hitPoint = { x, y }; let minDistanceSquared = Infinity; let closestDatum; for (const datum of contextNodeData.nodeData) { const { point: { x: datumX = Number.NaN, y: datumY = Number.NaN } = {} } = datum; if (Number.isNaN(datumX) || Number.isNaN(datumY)) { continue; } const isInRange = xAxis?.inRange(datumX) && yAxis?.inRange(datumY); if (!isInRange) { continue; } const distanceSquared2 = Math.max((hitPoint.x - datumX) ** 2 + (hitPoint.y - datumY) ** 2, 0); if (distanceSquared2 < minDistanceSquared) { minDistanceSquared = distanceSquared2; closestDatum = datum; } } if (minDistanceSquared != null) { return { datum: closestDatum, distance: Math.sqrt(minDistanceSquared) }; } } pickModulesClosestDatum(point) { let minDistanceSquared = Infinity; let closestDatum; for (const mod of this.moduleMap.modules()) { const modPick = mod.pickNodeNearest(point); if (modPick !== void 0 && modPick.distanceSquared < minDistanceSquared) { minDistanceSquared = modPick.distanceSquared; closestDatum = modPick.datum; } } if (minDistanceSquared != null) { return { datum: closestDatum, distance: Math.sqrt(minDistanceSquared) }; } } pickNodeClosestDatum(point) { let minDistance = Infinity; let closestDatum; const pick2 = this.pickNodeDataClosestDatum(point); if (pick2 != null && pick2.distance < minDistance) { minDistance = pick2.distance; closestDatum = pick2.datum; } const modPick = this.pickModulesClosestDatum(point); if (modPick != null && modPick.distance < minDistance) { minDistance = modPick.distance; closestDatum = modPick.datum; } if (closestDatum) { const distance2 = Math.max(minDistance - (closestDatum.point?.size ?? 0) / 2, 0); return { datum: closestDatum, distance: distance2 }; } } pickNodeMainAxisFirst(point, requireCategoryAxis) { const { x, y } = point; const { axes, _contextNodeData: contextNodeData } = this; const { pickOutsideVisibleMinorAxis } = this.properties; if (!contextNodeData) return; const xAxis = axes["x" /* X */]; const yAxis = axes["y" /* Y */]; if (xAxis == null || yAxis == null) return; const directions2 = [xAxis, yAxis].filter((axis) => axis.isCategoryLike()).map((a) => a.direction); if (requireCategoryAxis && directions2.length === 0) return; const [majorDirection = "x" /* X */] = directions2; const hitPointCoords = [x, y]; if (majorDirection !== "x" /* X */) hitPointCoords.reverse(); const minDistance = [Infinity, Infinity]; let closestDatum; for (const datum of contextNodeData.nodeData) { const { x: datumX = Number.NaN, y: datumY = Number.NaN } = datum.point ?? datum.midPoint ?? {}; if (Number.isNaN(datumX) || Number.isNaN(datumY) || datum.missing === true) continue; const visible = [xAxis?.inRange(datumX, 1), yAxis?.inRange(datumY, 1)]; if (majorDirection !== "x" /* X */) { visible.reverse(); } if (!visible[0] || !pickOutsideVisibleMinorAxis && !visible[1]) continue; const datumPoint = [datumX, datumY]; if (majorDirection !== "x" /* X */) { datumPoint.reverse(); } let newMinDistance = true; for (let i = 0; i < datumPoint.length; i++) { const dist = Math.abs(datumPoint[i] - hitPointCoords[i]); if (dist > minDistance[i]) { newMinDistance = false; break; } else if (dist < minDistance[i]) { minDistance[i] = dist; minDistance.fill(Infinity, i + 1, minDistance.length); } } if (newMinDistance) { closestDatum = datum; } } if (closestDatum) { let closestDistanceSquared = Math.max( minDistance[0] ** 2 + minDistance[1] ** 2 - (closestDatum.point?.size ?? 0), 0 ); for (const mod of this.moduleMap.modules()) { const modPick = mod.pickNodeMainAxisFirst(point, majorDirection); if (modPick != null && modPick.distanceSquared < closestDistanceSquared) { closestDatum = modPick.datum; closestDistanceSquared = modPick.distanceSquared; break; } } return { datum: closestDatum, distance: Math.sqrt(closestDistanceSquared) }; } } isPathOrSelectionDirty() { return false; } shouldFlipXY() { return false; } visibleRangeIndices(axisKey, visibleRange, indices, sortOrderParams) { let sortOrder; if (sortOrderParams == null) { const { processedData, dataModel } = this; sortOrder = dataModel.getColumnSortOrder(this, axisKey, processedData) ?? 1; } else { sortOrder = sortOrderParams.sortOrder; } const xValues = this.keysOrValues(axisKey); const pixelSize = 0; const [start2, end3] = visibleRangeIndices( sortOrder, indices?.length ?? xValues.length, visibleRange, (topIndex) => { const datumIndex = indices?.[topIndex] ?? topIndex; return this.xCoordinateRange(xValues[datumIndex], pixelSize, datumIndex); } ); return start2 < end3 ? [start2, end3] : [end3, start2]; } domainForVisibleRange(_direction, axisKeys, crossAxisKey, visibleRange, indices) { const { processedData, dataModel } = this; const [r0, r1] = visibleRange; const crossAxisValues = this.keysOrValues(crossAxisKey); const sortOrder = this.sortOrder(crossAxisKey); if (sortOrder != null) { const crossAxisRange = this.visibleRangeIndices(crossAxisKey, visibleRange, indices, { sortOrder }); return dataModel.getDomainBetweenRange(this, axisKeys, crossAxisRange, processedData); } const allAxisValues = axisKeys.map((axisKey) => this.keysOrValues(axisKey)); let axisMin = Infinity; let axisMax = -Infinity; for (const [i, crossAxisValue] of crossAxisValues.entries()) { const [x0, x1] = this.xCoordinateRange(crossAxisValue, 0, i); if (x1 < r0 || x0 > r1) continue; for (let j = 0; j < axisKeys.length; j++) { const axisValue = allAxisValues[j][i]; axisMin = Math.min(axisMin, axisValue); axisMax = Math.max(axisMax, axisValue); } } return axisMin > axisMax ? [Number.NaN, Number.NaN] : [axisMin, axisMax]; } domainForClippedRange(direction, axisKeys, crossAxisKey) { const { processedData, dataModel, axes } = this; const crossDirection = direction === "x" /* X */ ? "y" /* Y */ : "x" /* X */; const crossAxisRange = axisExtent(axes[crossDirection]); if (!crossAxisRange) { return axisKeys.flatMap((axisKey) => dataModel.getDomain(this, axisKey, "value", processedData).domain); } const crossAxisValues = this.keysOrValues(crossAxisKey); const sortOrder = dataModel.getColumnSortOrder(this, crossAxisKey, processedData); if (sortOrder != null) { const crossRange = clippedRangeIndices( sortOrder, crossAxisValues.length, crossAxisRange, (index) => crossAxisValues[index] ); return dataModel.getDomainBetweenRange(this, axisKeys, crossRange, processedData); } const allAxisValues = axisKeys.map((axisKey) => this.keysOrValues(axisKey)); const range0 = crossAxisRange[0].valueOf(); const range1 = crossAxisRange[1].valueOf(); const axisValues = []; for (const [i, crossAxisValue] of crossAxisValues.entries()) { const c = crossAxisValue.valueOf(); if (c < range0 || c > range1) continue; const values = allAxisValues.map((v) => v[i]); if (c >= range0) { axisValues.push(...values); } if (c <= range1) { axisValues.push(...values); } } return axisValues; } zoomFittingVisibleItems(crossAxisKey, _axisKeys, xVisibleRange, yVisibleRange, minVisibleItems) { const { dataModel, processedData } = this; if (!dataModel || !processedData) return; const crossAxis = this.axes["x" /* X */]; if (yVisibleRange != null) return; const sortOrder = this.sortOrder(crossAxisKey); if (sortOrder == null) return; const xValues = this.keysOrValues(crossAxisKey); if (minVisibleItems > xValues.length) { return { x: [0, 1], y: void 0 }; } const crossScale = crossAxis.scale; const crossScaleRange = crossScale.range; crossScale.range = [0, 1]; let [r0, r1] = this.visibleRangeIndices(crossAxisKey, xVisibleRange, void 0, { sortOrder }); r1 -= 1; const pixelSize = 0; if (this.xCoordinateRange(xValues[r0], pixelSize, r0)[0] < xVisibleRange[0]) { r0 += 1; } if (this.xCoordinateRange(xValues[r1], pixelSize, r1)[1] > xVisibleRange[1]) { r1 -= 1; } let xZoom; if (Math.abs(r1 - r0) >= minVisibleItems - 1) { xZoom = xVisibleRange; } else { const midPoint = (xVisibleRange[0] + xVisibleRange[1]) / 2; while (Math.abs(r1 - r0) < minVisibleItems - 1 && (r0 > 0 || r1 < xValues.length - 1)) { if (r0 === 0) { r1 += 1; } else if (r1 === xValues.length - 1) { r0 -= 1; } else { const nextR0X = this.xCoordinateRange(xValues[r0 - 1], pixelSize, r0 - 1)[0]; const nextR1X = this.xCoordinateRange(xValues[r1 + 1], pixelSize, r1 + 1)[1]; if (Math.abs(nextR0X - midPoint) < Math.abs(nextR1X - midPoint)) { r0 -= 1; } else { r1 += 1; } } } const x0 = this.xCoordinateRange(xValues[r0], pixelSize, r0)[0]; const x1 = this.xCoordinateRange(xValues[r1], pixelSize, r1)[1]; xZoom = [Math.min(xVisibleRange[0], x0), Math.max(xVisibleRange[1], x1)]; } crossScale.range = crossScaleRange; return { x: xZoom, y: void 0 }; } countVisibleItems(crossAxisKey, axisKeys, xVisibleRange, yVisibleRange, minVisibleItems) { const { dataModel, processedData } = this; if (!dataModel || !processedData) return Infinity; const crossValues = this.keysOrValues(crossAxisKey); const allAxisValues = axisKeys.map((axisKey) => dataModel.resolveColumnById(this, axisKey, processedData)); const shouldFlipXY = this.shouldFlipXY(); const crossAxis = shouldFlipXY ? this.axes["y" /* Y */] : this.axes["x" /* X */]; const axis = shouldFlipXY ? this.axes["x" /* X */] : this.axes["y" /* Y */]; const crossVisibleRange = shouldFlipXY ? yVisibleRange ?? [0, 1] : xVisibleRange; const axisVisibleRange = shouldFlipXY ? xVisibleRange : yVisibleRange ?? [0, 1]; if (yVisibleRange == null) { const sortOrder = this.sortOrder(crossAxisKey); if (sortOrder != null) { const crossScale = crossAxis.scale; const crossScaleRange = crossScale.range; crossScale.range = [0, 1]; const xValues = this.keysOrValues(crossAxisKey); let [r0, r1] = this.visibleRangeIndices(crossAxisKey, crossVisibleRange, void 0, { sortOrder }); r1 -= 1; if (r1 < r0) return 0; const pixelSize2 = 0; if (this.xCoordinateRange(xValues[r0], pixelSize2, r0)[0] < crossVisibleRange[0]) { r0 += 1; } if (this.xCoordinateRange(xValues[r1], pixelSize2, r1)[1] > crossVisibleRange[1]) { r1 -= 1; } const xItemsVisible = Math.abs(r1 - r0) + 1; crossScale.range = crossScaleRange; return xItemsVisible; } } const convert = (d, r, v) => { return d[0] + (v - r[0]) / (r[1] - r[0]) * (d[1] - d[0]); }; const crossAxisRange = crossAxis.range.toSorted(); const axisRange = axis.range.toSorted(); const crossMin = convert(crossAxisRange, crossAxis.visibleRange, crossVisibleRange[0]); const crossMax = convert(crossAxisRange, crossAxis.visibleRange, crossVisibleRange[1]); const axisMin = convert(axisRange, axis.visibleRange, Math.min(...axisVisibleRange)); const axisMax = convert(axisRange, axis.visibleRange, Math.max(...axisVisibleRange)); const startIndex = Math.round( (crossVisibleRange[0] + (crossVisibleRange[1] - crossVisibleRange[0]) / 2) * crossValues.length ); const pixelSize = 0; return countExpandingSearch(0, crossValues.length - 1, startIndex, minVisibleItems, (index) => { const [cross0, cross1] = this.xCoordinateRange(crossValues[index], pixelSize, index); const [axis0, axis1] = this.yCoordinateRange( allAxisValues.map((axisValues) => axisValues[index]), pixelSize, index ); if (!isFiniteNumber(cross0) || !isFiniteNumber(cross1) || !isFiniteNumber(axis0) || !isFiniteNumber(axis1)) { return false; } return cross0 >= crossMin && cross1 <= crossMax && axis0 >= axisMin && axis1 <= axisMax; }); } // @todo(AG-13777) - Remove this function. // We need data model updates to know if a data set is sorted & unique - and at the same time // it should generate the equivalent of `SMALLEST_KEY_INTERVAL`. We'll use that value here minTimeInterval() { let xValues; try { xValues = this.keysOrValues("xValue"); } catch { } if (xValues == null || xValues.length > 1e3) return; let minInterval = Infinity; let x0 = xValues[0]; let sortOrder; for (let i = 1; i < xValues.length; i++) { const x1 = xValues[i]; if (x1 != null && x0 != null) { const interval = x1.valueOf() - x0.valueOf(); const sign = Math.sign(interval); if (sign === 0) continue; if (sortOrder !== void 0 && sign !== sortOrder) return; minInterval = Math.min(minInterval, Math.abs(interval)); sortOrder = sign; } x0 = x1; } if (Number.isFinite(minInterval)) return minInterval; } updateHighlightSelectionItem(opts) { const { items, highlightSelection } = opts; const nodeData = items ?? []; return this.updateDatumSelection({ nodeData, datumSelection: highlightSelection }); } updateDatumSelection(opts) { return opts.datumSelection; } updateDatumNodes(_opts) { } updateDatumStyles(_opts) { } updatePaths(opts) { for (const p of opts.paths) { p.visible = false; } } updatePathNodes(opts) { const { paths, visible } = opts; for (const path of paths) { path.visible = visible; } } resetPathAnimation(data) { const { path } = this.opts?.animationResetFns ?? {}; if (path) { for (const paths of data.paths) { resetMotion([paths], path); } } } resetDatumAnimation(data) { const { datum } = this.opts?.animationResetFns ?? {}; if (datum) { resetMotion([data.datumSelection], datum); } } resetLabelAnimation(data) { const { label } = this.opts?.animationResetFns ?? {}; if (label) { resetMotion([data.labelSelection], label); } } resetAllAnimation(data) { this.ctx.animationManager.stopByAnimationGroupId(this.id); this.resetPathAnimation(data); this.resetDatumAnimation(data); this.resetLabelAnimation(data); if (data.contextData?.animationValid === false) { this.ctx.animationManager.skipCurrentBatch(); } } animateEmptyUpdateReady(data) { this.ctx.animationManager.skipCurrentBatch(); this.resetAllAnimation(data); } animateWaitingUpdateReady(data) { this.ctx.animationManager.skipCurrentBatch(); this.resetAllAnimation(data); } animateReadyHighlight(data) { const { datum } = this.opts?.animationResetFns ?? {}; if (datum) { resetMotion([data], datum); } } animateReadyResize(data) { this.resetAllAnimation(data); } animateClearingUpdateEmpty(data) { this.ctx.animationManager.skipCurrentBatch(); this.resetAllAnimation(data); } getAnimationData(seriesRect, previousContextData) { const { _contextNodeData: contextData } = this; if (!contextData) return; const animationData = { datumSelection: this.datumSelection, labelSelection: this.labelSelection, annotationSelections: [...this.annotationSelections], contextData, previousContextData, paths: this.paths, seriesRect }; return animationData; } updateLabelSelection(opts) { return opts.labelSelection; } getScaling(scale2) { if (scale2 instanceof LogScale) { const { range: range3, domain } = scale2; return { type: "log", convert: (d) => scale2.convert(d), domain: [domain[0], domain[1]], range: [range3[0], range3[1]] }; } else if (scale2 instanceof ContinuousScale) { const { range: range3, domain } = scale2; return { type: "continuous", domain: [domain[0], domain[1]], range: [range3[0], range3[1]] }; } else if (scale2 instanceof BandScale) { if (scale2 instanceof UnitTimeScale) { const linearParams = scale2.getLinearParams(); const bandCount = scale2.getBandCountForUpdate(); if (linearParams != null && bandCount > 0) { return { type: "category", variant: "unit-time", firstBandTime: linearParams.firstBandTime, lastBandTime: linearParams.firstBandTime + (bandCount - 1) * linearParams.intervalMs, bandCount, intervalMs: linearParams.intervalMs, inset: scale2.inset, step: scale2.step }; } } return { type: "category", domain: scale2.domain, inset: scale2.inset, step: scale2.step }; } } calculateScaling() { const result = {}; for (const direction of Object.values(ChartAxisDirection)) { const axis = this.axes[direction]; if (!axis) continue; const scalingResult = this.getScaling(axis.scale); if (scalingResult != null) { result[direction] = scalingResult; } } return result; } }; function axisExtent(axis) { let min; let max; if (axis instanceof NumberAxis || axis instanceof TimeAxis) { ({ min, max } = axis); } if (min == null && max == null) return; min ?? (min = -Infinity); max ?? (max = Infinity); return [min, max]; } function clippedRangeIndices(sortOrder, length2, range3, xValue) { const range0 = range3[0].valueOf(); const range1 = range3[1].valueOf(); let xMinIndex = findMinIndex(0, length2 - 1, (i) => { const index = sortOrder === 1 ? i : length2 - i; const x = xValue(index)?.valueOf(); return !Number.isFinite(x) || x >= range0; }); let xMaxIndex = findMaxIndex(0, length2 - 1, (i) => { const index = sortOrder === 1 ? i : length2 - i; const x = xValue(index)?.valueOf(); return !Number.isFinite(x) || x <= range1; }); if (xMinIndex == null || xMaxIndex == null) return [0, 0]; if (sortOrder === -1) { [xMinIndex, xMaxIndex] = [length2 - xMaxIndex, length2 - xMinIndex]; } xMinIndex = Math.max(xMinIndex, 0); xMaxIndex = Math.min(xMaxIndex + 1, length2); return [xMinIndex, xMaxIndex]; } // packages/ag-charts-community/src/chart/cartesianUtil.ts function stackCartesianSeries(series) { const seriesGroups = /* @__PURE__ */ new Map(); for (const s of series) { if (!(s instanceof CartesianSeries)) continue; const stackCount = s.seriesGrouping?.stackCount ?? 0; const groupIndex = stackCount > 0 ? s.seriesGrouping?.groupIndex : void 0; if (groupIndex == null) { s.seriesBelowStackContext = void 0; s.createStackContext(); continue; } const groupKey = `${s.type}-${groupIndex}`; let group = seriesGroups.get(groupKey); if (group == null) { group = []; seriesGroups.set(groupKey, group); } group.push(s); } for (const group of seriesGroups.values()) { group.sort((a, b) => (a.seriesGrouping?.stackIndex ?? 0) - (b.seriesGrouping?.stackIndex ?? 0)); let seriesBelowStackContext; for (const s of group) { s.seriesBelowStackContext = seriesBelowStackContext; seriesBelowStackContext = s.createStackContext(); } } } // packages/ag-charts-community/src/util/mutex.ts var Mutex = class { constructor() { this.available = true; this.acquireQueue = []; } acquire(cb) { return new Promise((resolve, reject) => { this.acquireQueue.push([cb, resolve, reject]); if (this.available) { this.dispatchNext().catch(reject); } }); } async acquireImmediately(cb) { if (!this.available) { return false; } await this.acquire(cb); return true; } async waitForClearAcquireQueue() { return this.acquire(() => Promise.resolve(void 0)); } async dispatchNext() { this.available = false; let [next, done, reject] = this.acquireQueue.shift() ?? []; while (next) { try { await next(); done?.(); } catch (error2) { reject?.(error2); } [next, done, reject] = this.acquireQueue.shift() ?? []; } this.available = true; } }; // packages/ag-charts-community/src/util/render.ts function debouncedAnimationFrame(cb) { const window = getWindow(); function scheduleWithAnimationFrame(innerCb, _delayMs) { return window.requestAnimationFrame(innerCb); } function cancelWithAnimationFrame(id) { window.cancelAnimationFrame(id); } return buildScheduler(scheduleWithAnimationFrame, cb, cancelWithAnimationFrame); } function debouncedCallback(cb) { function scheduleWithDelay(innerCb, delayMs = 0) { if (delayMs === 0) { queueMicrotask(innerCb); return void 0; } return setTimeout(innerCb, delayMs); } function cancelWithTimeout(id) { clearTimeout(id); } return buildScheduler(scheduleWithDelay, cb, cancelWithTimeout); } function buildScheduler(scheduleFn, cb, cancelFn) { let scheduleCount = 0; let promiseRunning = false; let awaitingPromise; let awaitingDone; let scheduledId; function busy() { return promiseRunning; } function done() { promiseRunning = false; scheduledId = void 0; awaitingDone?.(); awaitingDone = void 0; awaitingPromise = void 0; if (scheduleCount > 0) { scheduledId = scheduleFn(scheduleCallback); } } function scheduleCallback() { const count = scheduleCount; scheduleCount = 0; promiseRunning = true; const maybePromise = cb({ count }); if (!maybePromise) { done(); return; } maybePromise.then(done, done); } function schedule(delayMs) { if (scheduleCount === 0 && !busy()) { scheduledId = scheduleFn(scheduleCallback, delayMs); } scheduleCount++; } function cancel() { if (scheduledId != null && cancelFn) { cancelFn(scheduledId); scheduledId = void 0; scheduleCount = 0; } } async function waitForCompletion() { if (!busy()) { return; } awaitingPromise ?? (awaitingPromise = new Promise(resolveAwaitingPromise)); while (busy()) { await awaitingPromise; } } function resolveAwaitingPromise(resolve) { awaitingDone = resolve; } return { schedule, cancel, waitForCompletion }; } // packages/ag-charts-community/src/widget/widgetEvents.ts var WIDGET_HTML_EVENTS = [ "blur", "change", "contextmenu", "focus", "keydown", "keyup", "click", "dblclick", "mouseenter", "mousemove", "mouseleave", "wheel", "touchstart", "touchmove", "touchend", "touchcancel" ]; function allocMouseEvent(type, sourceEvent, current) { const { offsetX, offsetY, clientX, clientY } = sourceEvent; const { currentX, currentY } = WidgetEventUtil.calcCurrentXY(current, sourceEvent); return { type, device: "mouse", offsetX, offsetY, clientX, clientY, currentX, currentY, sourceEvent }; } function allocTouchEvent(type, sourceEvent, _current) { return { type, sourceEvent }; } var WidgetAllocators = { blur: (sourceEvent) => { return { type: "blur", sourceEvent }; }, change: (sourceEvent) => { return { type: "change", sourceEvent }; }, contextmenu: (sourceEvent, current) => { return allocMouseEvent("contextmenu", sourceEvent, current); }, focus: (sourceEvent) => { return { type: "focus", sourceEvent }; }, keydown: (sourceEvent) => { return { type: "keydown", sourceEvent }; }, keyup: (sourceEvent) => { return { type: "keyup", sourceEvent }; }, click: (sourceEvent, current) => { return allocMouseEvent("click", sourceEvent, current); }, dblclick: (sourceEvent, current) => { return allocMouseEvent("dblclick", sourceEvent, current); }, mouseenter: (sourceEvent, current) => { return allocMouseEvent("mouseenter", sourceEvent, current); }, mousemove: (sourceEvent, current) => { return allocMouseEvent("mousemove", sourceEvent, current); }, mouseleave: (sourceEvent, current) => { return allocMouseEvent("mouseleave", sourceEvent, current); }, wheel: (sourceEvent) => { const { offsetX, offsetY, clientX, clientY } = sourceEvent; const factor = sourceEvent.deltaMode === 0 ? 0.01 : 1; const deltaX = sourceEvent.deltaX * factor; const deltaY = sourceEvent.deltaY * factor; return { type: "wheel", offsetX, offsetY, clientX, clientY, deltaX, deltaY, sourceEvent }; }, touchstart: (sourceEvent, current) => { return allocTouchEvent("touchstart", sourceEvent, current); }, touchmove: (sourceEvent, current) => { return allocTouchEvent("touchmove", sourceEvent, current); }, touchend: (sourceEvent, current) => { return allocTouchEvent("touchend", sourceEvent, current); }, touchcancel: (sourceEvent, current) => { return allocTouchEvent("touchcancel", sourceEvent, current); } }; var WidgetEventUtil = class { static alloc(type, sourceEvent, current) { return WidgetAllocators[type](sourceEvent, current); } static isHTMLEvent(type) { const htmlTypes = WIDGET_HTML_EVENTS; return htmlTypes.includes(type); } static calcCurrentXY(current, event) { const currentRect = current.getBoundingClientRect(); return { currentX: event.clientX - currentRect.x, currentY: event.clientY - currentRect.y }; } }; // packages/ag-charts-community/src/widget/widgetListenerHTML.ts var WidgetListenerHTML = class { constructor() { this.widgetListeners = {}; this.sourceListeners = {}; } initSourceHandler(type, handler) { this.sourceListeners ?? (this.sourceListeners = {}); this.sourceListeners[type] = handler; } lazyGetWidgetListeners(type, target) { var _a; if (!(type in (this.sourceListeners ?? {}))) { const sourceHandler = (sourceEvent) => { const widgetEvent = WidgetEventUtil.alloc(type, sourceEvent, target.getElement()); this.dispatch(type, target, widgetEvent); }; const opts = {}; if (type.startsWith("touch") || type === "wheel") { opts.passive = false; } this.initSourceHandler(type, sourceHandler); target.getElement().addEventListener(type, sourceHandler, opts); } this.widgetListeners ?? (this.widgetListeners = {}); (_a = this.widgetListeners)[type] ?? (_a[type] = []); return this.widgetListeners[type]; } add(type, target, handler) { const listeners = this.lazyGetWidgetListeners(type, target); listeners.push(handler); } remove(type, target, handler) { const listeners = this.lazyGetWidgetListeners(type, target); const index = listeners.indexOf(handler); if (index > -1) listeners.splice(index, 1); } destroy(target) { this.widgetListeners = void 0; if (this.sourceListeners) { for (const [key, sourceHandler] of entries(this.sourceListeners)) { target.getElement().removeEventListener(key, sourceHandler); } this.sourceListeners = void 0; } } dispatch(type, target, event) { for (const widgetListener of this.widgetListeners?.[type] ?? []) { widgetListener(event, target); } } }; // packages/ag-charts-community/src/widget/mouseDragger.ts var MouseDragger = class { constructor(glob, self, myCallbacks, downEvent) { this.glob = glob; this.self = self; this.window = getWindow(); this.cleanup = new CleanupRegistry(); this.mousegeneral = (generalEvent) => { generalEvent.stopPropagation(); generalEvent.stopImmediatePropagation(); }; this.mousemove = (moveEvent) => { moveEvent.stopPropagation(); moveEvent.stopImmediatePropagation(); this.glob.globalMouseDragCallbacks?.mousemove(moveEvent); }; this.mouseup = (upEvent) => { if (upEvent.button === 0) { upEvent.stopPropagation(); upEvent.stopImmediatePropagation(); this.glob.globalMouseDragCallbacks?.mouseup(upEvent); this.destroy(); } }; const { window, mousegeneral, mousemove, mouseup } = this; this.cleanup.register( attachListener(window, "mousedown", mousegeneral, { capture: true }), attachListener(window, "mouseenter", mousegeneral, { capture: true }), attachListener(window, "mouseleave", mousegeneral, { capture: true }), attachListener(window, "mouseout", mousegeneral, { capture: true }), attachListener(window, "mouseover", mousegeneral, { capture: true }), attachListener(window, "mousemove", mousemove, { capture: true }), attachListener(window, "mouseup", mouseup, { capture: true }) ); self.mouseDragger = this; glob.globalMouseDragCallbacks = myCallbacks; glob.globalMouseDragCallbacks.mousedown(downEvent); downEvent.stopPropagation(); downEvent.stopImmediatePropagation(); } destroy() { this.cleanup.flush(); this.glob.globalMouseDragCallbacks = void 0; this.self.mouseDragger = void 0; } }; function startMouseDrag(glob, self, myCallbacks, downEvent) { if (glob.globalMouseDragCallbacks != null) return void 0; return new MouseDragger(glob, self, myCallbacks, downEvent); } // packages/ag-charts-community/src/widget/touchDragger.ts var LONG_TAP_DURATION_MS = 500; var LONG_TAP_INTERRUPT_MIN_TOUCHMOVE_PXPX = 100; function deltaClientSquared(a, b) { const dx = a.clientX - b.clientX; const dy = a.clientY - b.clientY; return dx * dx + dy * dy; } var gIsInLongTap = false; var TouchDragger = class { constructor(glob, self, myCallbacks, initialTouch, target) { this.glob = glob; this.self = self; this.initialTouch = initialTouch; this.target = target; this.cleanup = new CleanupRegistry(); this.longTapInterrupted = false; this.longtap = () => { const { target, initialTouch } = this; if (!this.longTapInterrupted) { const cleanup = new CleanupRegistry(); target.dispatchEvent(new TouchEvent("touchcancel", { touches: [initialTouch], bubbles: true })); gIsInLongTap = true; const longTapMove = (e) => e.preventDefault(); const longTapEnd = (e) => { gIsInLongTap = false; e.preventDefault(); cleanup.flush(); }; cleanup.register( attachListener(target, "touchmove", longTapMove, { passive: false }), attachListener(target, "touchend", longTapEnd, { passive: false }), attachListener(target, "touchcancel", longTapEnd, { passive: false }) ); const { clientX, clientY } = initialTouch; const contextMenuEvent = new PointerEvent("contextmenu", { bubbles: true, cancelable: true, view: getWindow(), clientX, clientY, pointerType: "touch" }); target.dispatchEvent(contextMenuEvent); } }; this.touchmove = (moveEvent) => { const { glob, self, initialTouch } = this; const touch = this.findInitialFinger(moveEvent.targetTouches); if (touch != null) { this.longTapInterrupted = this.longTapInterrupted || deltaClientSquared(initialTouch, touch) > LONG_TAP_INTERRUPT_MIN_TOUCHMOVE_PXPX; if (self.dragTouchEnabled) { glob.globalTouchDragCallbacks?.touchmove(moveEvent, touch); } } }; this.touchend = (endEvent) => { this.longTapInterrupted = true; const touch = this.findInitialFinger(endEvent.changedTouches, endEvent.touches); if (touch != null) { this.glob.globalTouchDragCallbacks?.touchend(endEvent, touch); } this.destroy(); }; this.longtapTimer = setTimeout(this.longtap, LONG_TAP_DURATION_MS); const { touchmove, touchend } = this; this.cleanup.register( attachListener(target, "touchmove", touchmove, { passive: false }), attachListener(target, "touchstart", touchend, { passive: false }), attachListener(target, "touchend", touchend, { passive: false }), attachListener(target, "touchcancel", touchend, { passive: false }) ); self.touchDragger = this; glob.globalTouchDragCallbacks = myCallbacks; } destroy() { clearTimeout(this.longtapTimer); this.cleanup.flush(); this.glob.globalTouchDragCallbacks = void 0; this.self.touchDragger = void 0; } findInitialFinger(...touchLists) { const touches = touchLists.flatMap((touchList) => Array.from(touchList)); return Array.from(touches).find((v) => v.identifier === this.initialTouch.identifier); } }; function startOneFingerTouch(glob, self, myCallbacks, initialTouch, target) { if (glob.globalTouchDragCallbacks != null || gIsInLongTap) return void 0; return new TouchDragger(glob, self, myCallbacks, initialTouch, target); } // packages/ag-charts-community/src/widget/widgetListenerInternal.ts function makeMouseDrag(current, type, origin3, sourceEvent) { const { currentX, currentY } = WidgetEventUtil.calcCurrentXY(current.getElement(), sourceEvent); const originDeltaX = sourceEvent.pageX - origin3.pageX; const originDeltaY = sourceEvent.pageY - origin3.pageY; return { type, device: "mouse", offsetX: origin3.offsetX + originDeltaX, offsetY: origin3.offsetY + originDeltaY, clientX: sourceEvent.clientX, clientY: sourceEvent.clientY, currentX, currentY, originDeltaX, originDeltaY, sourceEvent }; } function getTouchOffsets(current, { pageX, pageY }) { const { x, y } = current.getElement().getBoundingClientRect(); return { offsetX: pageX - x, offsetY: pageY - y }; } function makeTouchDrag(current, type, origin3, sourceEvent, touch) { const { currentX, currentY } = WidgetEventUtil.calcCurrentXY(current.getElement(), touch); const originDeltaX = touch.pageX - origin3.pageX; const originDeltaY = touch.pageY - origin3.pageY; return { type, device: "touch", offsetX: origin3.offsetX + originDeltaX, offsetY: origin3.offsetY + originDeltaY, clientX: touch.clientX, clientY: touch.clientY, currentX, currentY, originDeltaX, originDeltaY, sourceEvent }; } var GlobalCallbacks = {}; var WidgetListenerInternal = class { constructor(dispatchCallback) { this.dispatchCallback = dispatchCallback; this.dragTouchEnabled = true; } destroy() { this.dragTriggerRemover?.(); this.dragTriggerRemover = void 0; this.listeners?.clear(); this.mouseDragger?.destroy(); this.touchDragger?.destroy(); } getListenerSet(type) { this.listeners ?? (this.listeners = /* @__PURE__ */ new Map()); let result = this.listeners.get(type); if (result === void 0) { result = /* @__PURE__ */ new Set(); this.listeners.set(type, result); } return result; } add(type, target, handler) { this.getListenerSet(type).add(handler); switch (type) { case "drag-start": case "drag-move": case "drag-end": { this.registerDragTrigger(target); break; } } } remove(type, _target, handler) { this.getListenerSet(type).delete(handler); } registerDragTrigger(target) { if (this.dragTriggerRemover == null) { const element2 = target.getElement(); const cleanup = new CleanupRegistry(); cleanup.register( attachListener(element2, "mousedown", (event) => this.triggerMouseDrag(target, event)), attachListener(element2, "touchstart", (event) => this.triggerTouchDrag(target, event), { passive: false }) ); this.dragTriggerRemover = () => cleanup.flush(); } } triggerMouseDrag(current, downEvent) { if (downEvent.button === 0) { this.startMouseDrag(current, downEvent); } } startMouseDrag(current, initialDownEvent) { const origin3 = { pageX: Number.NaN, pageY: Number.NaN, offsetX: Number.NaN, offsetY: Number.NaN }; partialAssign(["pageX", "pageY", "offsetX", "offsetY"], origin3, initialDownEvent); const dragCallbacks = { mousedown: (downEvent) => { const dragStartEvent = makeMouseDrag(current, "drag-start", origin3, downEvent); this.dispatch("drag-start", current, dragStartEvent); }, mousemove: (moveEvent) => { const dragMoveEvent = makeMouseDrag(current, "drag-move", origin3, moveEvent); this.dispatch("drag-move", current, dragMoveEvent); }, mouseup: (upEvent) => { const dragEndEvent = makeMouseDrag(current, "drag-end", origin3, upEvent); this.dispatch("drag-end", current, dragEndEvent); this.endDrag(current, dragEndEvent); } }; this.mouseDragger = startMouseDrag(GlobalCallbacks, this, dragCallbacks, initialDownEvent); } endDrag(target, { sourceEvent, clientX, clientY }) { const elem = target.getElement(); const rect2 = elem.getBoundingClientRect(); if (!boxContains(rect2, clientX, clientY)) { elem.dispatchEvent(new MouseEvent("mouseleave", sourceEvent)); sourceEvent.target?.dispatchEvent(new MouseEvent("mouseenter", sourceEvent)); } } triggerTouchDrag(current, startEvent) { const touch = startEvent.targetTouches[0]; if (startEvent.targetTouches.length === 1 && touch != null) { this.startOneFingerTouch(current, startEvent, touch); } } startOneFingerTouch(current, initialEvent, initialTouch) { const origin3 = { pageX: Number.NaN, pageY: Number.NaN, ...getTouchOffsets(current, initialTouch) }; partialAssign(["pageX", "pageY"], origin3, initialTouch); const dragCallbacks = { touchmove: (moveEvent, touch) => { const dragMoveEvent = makeTouchDrag(current, "drag-move", origin3, moveEvent, touch); this.dispatch("drag-move", current, dragMoveEvent); }, touchend: (cancelEvent, touch) => { const dragMoveEvent = makeTouchDrag(current, "drag-end", origin3, cancelEvent, touch); this.dispatch("drag-end", current, dragMoveEvent); } }; const target = current.getElement(); this.touchDragger = startOneFingerTouch(GlobalCallbacks, this, dragCallbacks, initialTouch, target); const dragStartEvent = makeTouchDrag(current, "drag-start", origin3, initialEvent, initialTouch); this.dispatch("drag-start", current, dragStartEvent); } dispatch(type, current, event) { for (const handler of this.getListenerSet(type)) { handler(event, current); } this.dispatchCallback(type, event); } }; // packages/ag-charts-community/src/widget/widget.ts var WidgetBounds = class { constructor(elem) { this.elem = elem; } setBounds(bounds) { setElementBBox(this.elemContainer ?? this.elem, bounds); } getBounds() { return getElementBBox(this.elemContainer ?? this.elem); } static setElementContainer(widget, elemContainer) { const currentBounds = widget.getBounds(); setElementBBox(elemContainer, currentBounds); setElementStyles(widget.elem, { width: "100%", height: "100%" }); widget.elem.remove(); widget.elemContainer = elemContainer; widget.elemContainer.replaceChildren(widget.elem); } }; var Widget = class extends WidgetBounds { constructor() { super(...arguments); this.index = Number.NaN; this.children = []; } set id(elementId) { setAttribute(this.elem, "id", elementId); } get id() { return getAttribute(this.elem, "id"); } getElement() { return this.elem; } getBoundingClientRect() { return this.elem.getBoundingClientRect(); } get clientWidth() { return this.elem.clientWidth; } get clientHeight() { return this.elem.clientHeight; } destroy() { this.destroyListener?.(); this.destroyListener = void 0; this.remove(); for (const child of this.children) { child.parent = void 0; child.destroy(); } this.children.length = 0; this.destructor(); this.remove(); this.internalListener?.destroy(); this.htmlListener?.destroy(this); } remove() { this.elem.remove(); this.elemContainer?.remove(); } setHidden(hidden) { setElementStyle(this.elem, "display", hidden ? "none" : void 0); } isHidden() { return getWindow()?.getComputedStyle?.(this.elem).display === "none"; } setCursor(cursor) { setElementStyle(this.elem, "cursor", cursor); } setTextContent(textContent) { this.elem.textContent = textContent ?? null; } setAriaDescribedBy(ariaDescribedBy) { setAttribute(this.elem, "aria-describedby", ariaDescribedBy); } setAriaHidden(ariaHidden) { setAttribute(this.elem, "aria-hidden", ariaHidden); } setAriaLabel(ariaLabel) { setAttribute(this.elem, "aria-label", ariaLabel); } setAriaExpanded(ariaExpanded) { setAttribute(this.elem, "aria-expanded", ariaExpanded); } setAriaControls(ariaControls) { setAttribute(this.elem, "aria-controls", ariaControls); } setAriaHasPopup(ariaHasPopup) { setAttribute(this.elem, "aria-haspopup", ariaHasPopup); } setInnerHTML(html) { this.elem.innerHTML = html; } setPointerEvents(pointerEvents) { setElementStyle(this.elem, "pointer-events", pointerEvents); } setCSSVariable(key, value) { this.elem.style.setProperty(key, value); } isDisabled() { return getAttribute(this.elem, "aria-disabled", false); } hasPopup() { const ariaHasPopup = getAttribute(this.elem, "aria-haspopup"); return ariaHasPopup !== void 0 && ariaHasPopup !== "false"; } parseFloat(s) { return s === "" ? 0 : Number.parseFloat(s); } cssLeft() { return this.parseFloat(this.elem.style.left); } cssTop() { return this.parseFloat(this.elem.style.top); } cssWidth() { return this.parseFloat(this.elem.style.width); } cssHeight() { return this.parseFloat(this.elem.style.height); } focus(opts) { this.elem.focus(opts); } setFocusOverride(focus) { setAttribute(this.elem, "data-focus-override", focus); } setPreventsDefault(preventDefault) { setAttribute(this.elem, "data-preventdefault", preventDefault); } setTabIndex(tabIndex) { setAttribute(this.elem, "tabindex", tabIndex); } addChild(child) { this.addChildToDOM(child, this.getBefore(child)); this.children.push(child); child.index = this.children.length - 1; child.parent = this; this.onChildAdded(child); } removeChild(child) { const i = this.children.indexOf(child); this.children.splice(i, 1); this.removeChildFromDOM(child); this.onChildRemoved(child); } moveChild(child, domIndex) { if (child.domIndex === domIndex) return; child.domIndex = domIndex; this.removeChildFromDOM(child); this.addChildToDOM(child, this.getBefore(child)); } addClass(...tokens) { this.elem.classList.add(...tokens); } removeClass(...tokens) { this.elem.classList.remove(...tokens); } toggleClass(token, force) { this.elem.classList.toggle(token, force); } appendOrInsert(child, before) { if (before) { before.getElement().insertAdjacentElement("beforebegin", child); } else { this.elem.appendChild(child); } } addChildToDOM(child, before) { this.appendOrInsert(child.getElement(), before); } removeChildFromDOM(child) { child.getElement().remove(); } onChildAdded(_child) { } onChildRemoved(_child) { } getBefore({ domIndex }) { if (domIndex === void 0) return void 0; return this.children.filter((child) => child.domIndex !== void 0 && child.domIndex > domIndex).reduce((prev, curr) => !prev || curr.domIndex < prev.domIndex ? curr : prev, void 0); } addListener(type, listener) { if (WidgetEventUtil.isHTMLEvent(type)) { this.htmlListener ?? (this.htmlListener = new WidgetListenerHTML()); this.htmlListener.add(type, this, listener); } else { this.internalListener ?? (this.internalListener = new WidgetListenerInternal(this.onDispatch.bind(this))); this.internalListener.add(type, this, listener); } return () => this.removeListener(type, listener); } removeListener(type, listener) { if (WidgetEventUtil.isHTMLEvent(type)) { this.htmlListener?.remove(type, this, listener); } else if (this.htmlListener != null) { this.internalListener?.remove(type, this, listener); } } setDragTouchEnabled(dragTouchEnabled) { this.internalListener ?? (this.internalListener = new WidgetListenerInternal(this.onDispatch.bind(this))); this.internalListener.dragTouchEnabled = dragTouchEnabled; } onDispatch(type, event) { if (!event.sourceEvent?.bubbles) return; let { parent } = this; while (parent != null) { const { internalListener } = parent; if (internalListener != null) { const parentEvent = { ...event, ...WidgetEventUtil.calcCurrentXY(parent.getElement(), event) }; internalListener.dispatch(type, parent, parentEvent); } parent = parent.parent; } } static addWindowEvent(_type, listener) { const pagehideHandler = (event) => { if (event.persisted) { return; } listener(); }; return attachListener(getWindow(), "pagehide", pagehideHandler); } }; // packages/ag-charts-community/src/chart/background/background.ts var Background = class extends AbstractModuleInstance { constructor(ctx) { super(); this.ctx = ctx; this.rectNode = new Rect(); this.textNode = new Text(); this.fill = "white"; this.node = this.createNode(); this.node.append([this.rectNode, this.textNode]); this.visible = true; this.cleanup.register( ctx.scene.attachNode(this.node), ctx.eventsHub.on("layout:complete", (e) => this.onLayoutComplete(e)) ); } createNode() { return new Group({ name: "background", zIndex: 0 /* CHART_BACKGROUND */ }); } onLayoutComplete(e) { const { width: width2, height: height2 } = e.chart; this.rectNode.width = width2; this.rectNode.height = height2; } }; __decorateClass([ addFakeTransformToInstanceProperty, ProxyPropertyOnWrite("node", "visible") ], Background.prototype, "visible", 2); __decorateClass([ addFakeTransformToInstanceProperty, ProxyPropertyOnWrite("rectNode", "fill") ], Background.prototype, "fill", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Background.prototype, "image", 2); __decorateClass([ addFakeTransformToInstanceProperty, ProxyPropertyOnWrite("textNode") ], Background.prototype, "text", 2); // packages/ag-charts-community/src/chart/chartAxes.ts var ChartAxes = class extends Array { destroy() { for (const axis of this) { axis.destroy(); } this.length = 0; } findById(id) { return this.find((a) => a.id === id); } matches(comparison) { return this.length === Object.keys(comparison).length && every( comparison, (id, object2) => isObject(object2) && "type" in object2 && this.findById(id)?.type === object2.type ); } getById(id) { const axis = this.findById(id); if (!axis) throw new Error(`Could not find axis by id [${id}].`); return axis; } }; var CartesianChartAxes = class extends ChartAxes { get ["x" /* X */]() { return this.getById("x" /* X */); } get ["y" /* Y */]() { return this.getById("y" /* Y */); } perpendicular(to) { const direction = to.direction === "x" /* X */ ? "y" /* Y */ : "x" /* X */; return this[direction]; } }; var PolarChartAxes = class extends ChartAxes { get ["angle" /* Angle */]() { return this.getById("angle" /* Angle */); } get ["radius" /* Radius */]() { return this.getById("radius" /* Radius */); } }; // packages/ag-charts-community/src/chart/chartCaptions.ts var ChartCaptions = class { constructor() { this.title = new Caption(); this.subtitle = new Caption(); this.footnote = new Caption(); } positionCaptions({ layoutBox }) { const { title, subtitle, footnote } = this; const maxHeight = layoutBox.height / 10; if (title.enabled) { this.positionCaption("top", title, layoutBox, maxHeight); this.shrinkLayoutByCaption("top", title, layoutBox); } if (subtitle.enabled) { this.positionCaption("top", subtitle, layoutBox, maxHeight); this.shrinkLayoutByCaption("top", subtitle, layoutBox); } if (footnote.enabled) { this.positionCaption("bottom", footnote, layoutBox, maxHeight); this.shrinkLayoutByCaption("bottom", footnote, layoutBox); } } positionAbsoluteCaptions(ctx) { const { title, subtitle, footnote } = this; const { rect: rect2 } = ctx.series; for (const caption of [title, subtitle, footnote]) { if (caption.layoutStyle !== "overlay") continue; if (caption.textAlign === "left") { caption.node.x = rect2.x + caption.padding; } else if (caption.textAlign === "right") { const bbox = caption.node.getBBox(); caption.node.x = rect2.x + rect2.width - bbox.width - caption.padding; } } } computeX(align2, layoutBox) { if (align2 === "left") { return layoutBox.x; } else if (align2 === "right") { return layoutBox.x + layoutBox.width; } return layoutBox.x + layoutBox.width / 2; } positionCaption(vAlign, caption, layoutBox, maxHeight) { if (!caption.text) return; const { lineMetrics } = isArray(caption.text) ? measureTextSegments(caption.text, caption) : cachedTextMeasurer(caption).measureLines(toTextString(caption.text)); const containerHeight = Math.max(lineMetrics[0].height, maxHeight); caption.node.x = this.computeX(caption.textAlign, layoutBox) + caption.padding; caption.node.y = layoutBox.y + (vAlign === "top" ? 0 : layoutBox.height) + caption.padding; caption.node.textBaseline = vAlign; caption.computeTextWrap(layoutBox.width, containerHeight); } shrinkLayoutByCaption(vAlign, caption, layoutBox) { if (caption.layoutStyle === "block") { const bbox = caption.node.getBBox().clone(); const { spacing = 0 } = caption; if (vAlign === "bottom" && isArray(caption.text)) { bbox.y -= bbox.height; } layoutBox.shrink( vAlign === "top" ? Math.ceil(bbox.y - layoutBox.y + bbox.height + spacing) : Math.ceil(layoutBox.y + layoutBox.height - bbox.y + spacing), vAlign ); } } }; __decorateClass([ addFakeTransformToInstanceProperty ], ChartCaptions.prototype, "title", 2); __decorateClass([ addFakeTransformToInstanceProperty ], ChartCaptions.prototype, "subtitle", 2); __decorateClass([ addFakeTransformToInstanceProperty ], ChartCaptions.prototype, "footnote", 2); // packages/ag-charts-community/src/api/preset/chartTypeOriginator.ts var chartTypes = [ "candlestick", "hollow-candlestick", "ohlc", "line", "step-line", "hlc", "high-low" ]; var ChartTypeOriginator = class { constructor(chartService) { this.chartService = chartService; this.mementoOriginatorKey = "chartType"; } createMemento() { let chartType = this.chartService.publicApi?.getOptions()?.chartType; chartType ?? (chartType = "candlestick"); return chartType; } guardMemento(blob) { return blob == null || chartTypes.includes(blob); } restoreMemento(_version, _mementoVersion, memento) { if (memento == null) return; const options = { chartType: memento }; this.chartService.publicApi?.updateDelta(options).catch((e) => logger_exports.error("error restoring state", e)); } }; // packages/ag-charts-community/src/api/state/historyManager.ts var NOT_FOUND = Symbol("previous-memento-not-found"); var HistoryManager = class { constructor(eventsHub) { this.history = []; this.historyIndex = -1; this.originators = /* @__PURE__ */ new Map(); this.clearState = /* @__PURE__ */ new Map(); this.maxHistoryLength = 100; this.debug = debugLogger_exports.create(true, "history"); this.cleanup = new CleanupRegistry(); this.cleanup.register( eventsHub.on("series:undo", this.undo.bind(this)), eventsHub.on("series:redo", this.redo.bind(this)) ); } destroy() { this.cleanup.flush(); } addMementoOriginator(originator) { this.originators.set(originator.mementoOriginatorKey, originator); this.clearState.set(originator.mementoOriginatorKey, originator.createMemento()); this.debugEvent("History add originator:", originator.mementoOriginatorKey); } clear() { this.debug(`History clear:`, Object.keys(this.originators)); this.history = []; this.historyIndex = -1; for (const [mementoOriginatorKey, originator] of this.originators.entries()) { this.clearState.set(mementoOriginatorKey, originator.createMemento()); } } record(label, ...originators) { if (this.historyIndex < this.history.length - 1) { this.history = this.history.slice(0, this.historyIndex + 1); } if (this.history.length > this.maxHistoryLength) { this.history = this.history.slice(-this.maxHistoryLength); } const mementos = /* @__PURE__ */ new Map(); for (const originator of originators) { if (!this.originators.has(originator.mementoOriginatorKey)) { throw new Error( `Originator [${originator.mementoOriginatorKey}] has not been added to the HistoryManager.` ); } mementos.set(originator.mementoOriginatorKey, originator.createMemento()); } this.history.push({ label, mementos }); this.historyIndex = this.history.length - 1; this.debugEvent(`History record: [${label}]`); } undo() { const undoAction = this.history[this.historyIndex]; if (!undoAction) return; for (const mementoOriginatorKey of undoAction.mementos.keys()) { const previousMemento = this.findPreviousMemento(mementoOriginatorKey); if (previousMemento === NOT_FOUND) { throw new Error(`Could not find previous memento for [${mementoOriginatorKey}].`); } this.restoreMemento(mementoOriginatorKey, previousMemento); } this.historyIndex -= 1; this.debugEvent(`History undo: [${undoAction.label}]`); } redo() { const redoAction = this.history[this.historyIndex + 1]; if (!redoAction) return; for (const [mementoOriginatorKey, memento] of redoAction.mementos.entries()) { this.restoreMemento(mementoOriginatorKey, memento); } this.historyIndex += 1; this.debugEvent(`History redo: [${redoAction.label}]`); } findPreviousMemento(mementoOriginatorKey) { for (let i = this.historyIndex - 1; i >= 0; i--) { if (this.history[i].mementos.has(mementoOriginatorKey)) { return this.history[i].mementos.get(mementoOriginatorKey); } } if (this.clearState.has(mementoOriginatorKey)) { return this.clearState.get(mementoOriginatorKey); } return NOT_FOUND; } restoreMemento(mementoOriginatorKey, memento) { this.originators.get(mementoOriginatorKey)?.restoreMemento(VERSION, VERSION, memento); } debugEvent(...logContent) { this.debug( ...logContent, this.history.map((action, index) => index === this.historyIndex ? `** ${action.label} **` : action.label) ); } }; // packages/ag-charts-community/src/api/state/stateManager.ts var StateManager = class { constructor() { this.caretaker = new MementoCaretaker(VERSION); this.state = /* @__PURE__ */ new Map(); } setState(originator, value) { if (objectsEqual(this.state.get(originator.mementoOriginatorKey), value)) { return; } this.setStateAndRestore(originator, value); } setStateAndRestore(originator, value) { this.state.set(originator.mementoOriginatorKey, value); this.restoreState(originator); } restoreState(originator) { const { caretaker, state } = this; if (!state.has(originator.mementoOriginatorKey)) return; const value = state.get(originator.mementoOriginatorKey); caretaker.restore({ version: caretaker.version, [originator.mementoOriginatorKey]: value }, originator); } }; // packages/ag-charts-community/src/styles.css var styles_default = '.ag-charts-wrapper,.ag-charts-wrapper:after,.ag-charts-wrapper:before,.ag-charts-wrapper *,.ag-charts-wrapper *:after,.ag-charts-wrapper *:before{box-sizing:border-box}.ag-charts-wrapper{--align-items: center;--justify-content: center;position:relative;user-select:none;-webkit-user-select:none;-webkit-tap-highlight-color:rgba(0,0,0,0)}.ag-charts-wrapper--safe-horizontal{--justify-content: flex-start}.ag-charts-wrapper--safe-vertical{--align-items: flex-start}.ag-charts-tab-guard{width:0%;height:0%;position:absolute;pointer-events:none}.ag-charts-canvas-background{position:absolute}.ag-charts-canvas-center{width:100%;height:100%;position:absolute;touch-action:auto;pointer-events:auto;display:flex;align-items:var(--align-items);justify-content:var(--justify-content)}.ag-charts-canvas-container,.ag-charts-canvas{position:relative;user-select:none;-webkit-user-select:none}.ag-charts-canvas-container>*,.ag-charts-canvas>*{pointer-events:none}.ag-charts-canvas canvas{display:block}.ag-charts-series-area{outline:none;pointer-events:auto;position:absolute}.ag-charts-swapchain{top:0;left:0;outline:none;opacity:0;pointer-events:none;position:absolute;width:100%;height:100%}.ag-charts-swapchain:focus-visible{opacity:1}.ag-charts-canvas-proxy,.ag-charts-canvas-overlay{inset:0;pointer-events:none;position:absolute;user-select:none;-webkit-user-select:none}.ag-charts-canvas-overlay>*{position:absolute;pointer-events:auto}.ag-charts-theme-default,.ag-charts-theme-default-dark{--ag-charts-accent-color: #2196f3;--ag-charts-background-color: #fff;--ag-charts-border-color: #dddddd;--ag-charts-border-radius: 4px;--ag-charts-chart-background-color: #fff;--ag-charts-chart-padding: 20px;--ag-charts-focus-shadow: 0 0 0 3px #2196f3;--ag-charts-foreground-color: #181d1f;--ag-charts-font-family: Verdana, sans-serif;--ag-charts-font-size: 12px;--ag-charts-font-weight: 400;--ag-charts-popup-shadow: 0 0 16px rgba(0, 0, 0, .15);--ag-charts-subtle-text-color: #8c8c8c;--ag-charts-text-color: #181d1f;--ag-charts-chrome-background-color: #fafafa;--ag-charts-chrome-font-family: Verdana, sans-serif;--ag-charts-chrome-font-size: 12px;--ag-charts-chrome-font-weight: 400;--ag-charts-chrome-subtle-text-color: #8c8c8c;--ag-charts-chrome-text-color: #181d1f;--ag-charts-button-background-color: #fff;--ag-charts-button-border: 1px solid #dddddd;--ag-charts-button-font-weight: normal;--ag-charts-button-text-color: inherit;--ag-charts-input-background-color: #fff;--ag-charts-input-border: 1px solid #dddddd;--ag-charts-input-text-color: #181d1f;--ag-charts-menu-background-color: #fafafa;--ag-charts-menu-border: 1px solid #dddddd;--ag-charts-menu-text-color: #181d1f;--ag-charts-panel-background-color: #fafafa;--ag-charts-panel-text-color: #181d1f;--ag-charts-tooltip-background-color: #fafafa;--ag-charts-tooltip-border: 1px solid #dddddd;--ag-charts-tooltip-text-color: #181d1f;--ag-charts-tooltip-subtle-text-color: #8c8c8c;--ag-charts-crosshair-label-background-color: #fafafa;--ag-charts-crosshair-label-text-color: #181d1f;--ag-charts-spacing: 4px;--ag-charts-icon-size: 16px;--ag-charts-focus-color: color-mix(in srgb, var(--ag-charts-background-color), var(--ag-charts-accent-color) 12%);--ag-charts-input-border-radius: var(--ag-charts-border-radius);--ag-charts-input-focus-border-color: var(--ag-charts-accent-color);--ag-charts-input-focus-text-color: var(--ag-charts-accent-color);--ag-charts-input-disabled-background-color: color-mix( in srgb, var(--ag-charts-chrome-background-color), var(--ag-charts-foreground-color) 6% );--ag-charts-input-disabled-border-color: var(--ag-charts-border-color);--ag-charts-input-disabled-text-color: color-mix( in srgb, var(--ag-charts-chrome-background-color), var(--ag-charts-input-text-color) 50% );--ag-charts-input-placeholder-text-color: color-mix( in srgb, var(--ag-charts-input-background-color), var(--ag-charts-input-text-color) 60% );--ag-charts-button-border-radius: var(--ag-charts-border-radius);--ag-charts-button-focus-background-color: color-mix( in srgb, var(--ag-charts-button-background-color), var(--ag-charts-accent-color) 12% );--ag-charts-button-focus-border-color: var(--ag-charts-accent-color);--ag-charts-button-focus-text-color: var(--ag-charts-accent-color);--ag-charts-button-disabled-background-color: color-mix( in srgb, var(--ag-charts-chrome-background-color), var(--ag-charts-foreground-color) 6% );--ag-charts-button-disabled-border-color: var(--ag-charts-border-color);--ag-charts-button-disabled-text-color: color-mix( in srgb, var(--ag-charts-chrome-background-color), var(--ag-charts-chrome-text-color) 50% );--ag-charts-checkbox-background-color: color-mix( in srgb, var(--ag-charts-background-color), var(--ag-charts-foreground-color) 35% );--ag-charts-checkbox-checked-background-color: var(--ag-charts-accent-color);--ag-charts-tooltip-border-radius: var(--ag-charts-border-radius);--ag-charts-menu-border-radius: var(--ag-charts-border-radius);--ag-charts-chrome-font-size-small: var(--ag-charts-chrome-font-size);--ag-charts-chrome-font-size-medium: calc(var(--ag-charts-chrome-font-size) * (13 / 12));--ag-charts-chrome-font-size-large: calc(var(--ag-charts-chrome-font-size) * (14 / 12));--ag-charts-border: 1px solid var(--ag-charts-border-color);--ag-charts-focus-border: 1px solid var(--ag-charts-accent-color);--ag-charts-focus-border-shadow: 0 0 0 3px color-mix(in srgb, transparent, var(--ag-charts-accent-color) 20%);--ag-charts-layer-menu: 6;--ag-charts-layer-ui-overlay: 5;--ag-charts-layer-tooltip: 4;--ag-charts-layer-toolbar: 3;--ag-charts-layer-crosshair: 2;--ag-charts-layer-annotations: 1}.ag-charts-theme-default-dark{--ag-charts-focus-color: color-mix(in srgb, var(--ag-charts-background-color), var(--ag-charts-accent-color) 22%)}.ag-chart-canvas-wrapper .ag-charts-theme-default{--ag-charts-border-radius: var(--ag-border-radius, 4px);--ag-charts-border: var(--ag-borders-critical, solid 1px) var(--ag-charts-border-color);--ag-charts-focus-shadow: var(--ag-focus-shadow, 0 0 0 3px var(--ag-charts-accent-color));--ag-charts-focus-border-shadow: var( --ag-focus-shadow, 0 0 0 3px color-mix(in srgb, transparent, var(--ag-charts-accent-color) 20%) )}.ag-charts-icon{display:block;width:20px;height:20px;speak:none;speak:never;mask:var(--icon) center / contain no-repeat;background-color:currentColor;transition:background-color .25s ease-in-out}.ag-charts-icon-align-center{--icon: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBmaWxsPSIjMDAwIiBkPSJNNyAxMGg2djFIN3pNNCA3aDEydjFINHptMSA2aDEwdjFINXoiLz48L3N2Zz4=)}.ag-charts-icon-align-left{--icon: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBmaWxsPSIjMDAwIiBkPSJNNCAxMGg2djFINHptMC0zaDEydjFINHptMCA2aDEwdjFINHoiLz48L3N2Zz4=)}.ag-charts-icon-align-right{--icon: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBmaWxsPSIjMDAwIiBkPSJNMTAgMTBoNnYxaC02ek00IDdoMTJ2MUg0em0yIDZoMTB2MUg2eiIvPjwvc3ZnPg==)}.ag-charts-icon-arrow-drawing{--icon: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTE1LjI5MyA0LjVIMTIuNXYtMUgxN3Y0aC0xVjUuMjA3bC05LjY0NiA5LjY0Ny0uNzA4LS43MDh6IiBmaWxsPSIjMDAwIi8+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03IDE2YTIuNSAyLjUgMCAxIDEtNSAwIDIuNSAyLjUgMCAwIDEgNSAwbS0yLjUgMS41YTEuNSAxLjUgMCAxIDAgMC0zIDEuNSAxLjUgMCAwIDAgMCAzIiBmaWxsPSIjMDAwIi8+PC9zdmc+)}.ag-charts-icon-arrow-down-drawing{--icon: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik02IDhMMS41IDhMMTAgMThMMTguNSA4TDE0IDhMMTQgM0w2IDNMNiA4Wk03IDRMNyA5SDMuNjYyNDRMMTAgMTYuNDU2TDE2LjMzNzYgOUwxMyA5TDEzIDRMNyA0WiIgZmlsbD0iYmxhY2siLz4KPC9zdmc+Cg==)}.ag-charts-icon-arrow-up-drawing{--icon: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNCAxMkgxOC41TDEwIDJMMS41IDEySDZMNi4wMDAwMiAxN0gxNFYxMlpNMTMgMTZWMTFIMTYuMzM3NkwxMCAzLjU0NDA1TDMuNjYyNDQgMTFIN0w3LjAwMDAyIDE2SDEzWiIgZmlsbD0iYmxhY2siLz4KPC9zdmc+Cg==)}.ag-charts-icon-callout-annotation{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMyA0LjVBMS41IDEuNSAwIDAgMSA0LjUgM2gxMUExLjUgMS41IDAgMCAxIDE3IDQuNXY4YTEuNSAxLjUgMCAwIDEtMS41IDEuNWgtNC41MTRhMjYgMjYgMCAwIDAtMi4wMTcgMS41NGwtLjMxNC4yNmMtLjU1LjQ1Ny0xLjExNS45MjYtMS43NiAxLjQtLjY2OS40OTEtMS41NjItLjAxMi0xLjU2Mi0uOFYxNEg0LjVBMS41IDEuNSAwIDAgMSAzIDEyLjV6TTQuNSA0YS41LjUgMCAwIDAtLjUuNXY4YS41LjUgMCAwIDAgLjUuNWgxLjgzM3YzLjM3MmEzNiAzNiAwIDAgMCAxLjY3OC0xLjMzOGwuMzItLjI2NWEyNiAyNiAwIDAgMSAyLjIyNS0xLjY4NWwuMTI2LS4wODRIMTUuNWEuNS41IDAgMCAwIC41LS41di04YS41LjUgMCAwIDAtLjUtLjV6IiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48L3N2Zz4=)}.ag-charts-icon-candlestick-series{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzEzMTcyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNNyAxdjNoMnYxMkg3djNINnYtM0g0VjRoMlYxek01IDVoM3YxMEg1ek0xMSAxNFY2aDJWMy4yNWgxVjZoMnY4aC0ydjIuNzVoLTFWMTR6bTEtN2gzdjZoLTN6IiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48L3N2Zz4=)}.ag-charts-icon-close{--icon: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtNSA1IDEwIDEwTTUgMTUgMTUgNSIgc3Ryb2tlPSIjMDAwIi8+PC9zdmc+)}.ag-charts-icon-comment-annotation{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNNy41MTMgMy45OTVhNi41IDYuNSAwIDAgMSA2LjA5OCAxMS40MWMtLjU4OC4zOTMtMS4yMTcuNTM2LTEuODI5LjU4NWExMyAxMyAwIDAgMS0xLjI3LjAxN0EyNyAyNyAwIDAgMCAxMCAxNkg0LjVhLjUuNSAwIDAgMS0uNS0uNVYxMHEwLS4yNDctLjAwNy0uNTEzYy0uMDA4LS40MTYtLjAxNi0uODU3LjAxNy0xLjI2OS4wNS0uNjEyLjE5Mi0xLjI0LjU4NS0xLjgzYTYuNSA2LjUgMCAwIDEgMi45MTgtMi4zOTNtMy41Ni42MWE1LjUgNS41IDAgMCAwLTUuNjQ2IDIuMzRjLS4yNjYuMzk3LS4zNzkuODQyLS40MiAxLjM1NC0uMDMuMzYtLjAyMi43MTgtLjAxNSAxLjEwOFE1IDkuNjg5IDUgMTB2NWg1cS4zMTEuMDAxLjU5My4wMDhjLjM5LjAwNy43NDcuMDE1IDEuMTA4LS4wMTUuNTEyLS4wNDEuOTU3LS4xNTQgMS4zNTUtLjQyYTUuNSA1LjUgMCAwIDAtMS45ODMtOS45NjciIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==)}.ag-charts-icon-crosshair-add-line{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzAwMCIgZD0iTTEwIDUuNWEuNS41IDAgMCAxIC41LjV2My41aDMuODc1YS41LjUgMCAwIDEgMCAxSDEwLjV2NC4yNWEuNS41IDAgMSAxLTEgMFYxMC41SDUuNjI1YS41LjUgMCAxIDEgMC0xSDkuNVY2YS41LjUgMCAwIDEgLjUtLjUiLz48L3N2Zz4=)}.ag-charts-icon-date-range-drawing{--icon: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMiAyaDF2MTZIMnptMTUgMGgxdjE2aC0xeiIgZmlsbD0iIzE4MUQxRiIvPjxwYXRoIGQ9Ik0xMy4xNTcgMTFINXYtMWg3Ljc5M0wxMSA4LjIwN2wuNzA3LS43MDcgMy4xODIgMy4xODItMy4xODIgMy4xODItLjcwNy0uNzA3eiIgZmlsbD0iIzAwMCIvPjwvc3ZnPg==)}.ag-charts-icon-date-price-range-drawing{--icon: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMyAySDJ2MTZoMXptMy41MDcgNC44OUw4LjUgNC44OTVWMTBINXYxaDMuNXY3aDF2LTdoNS4wODhsLTEuOTU3IDEuOTU3LjcwNy43MDcgMy4xODItMy4xODJMMTMuMzM4IDcuM2wtLjcwNy43MDdMMTQuNjI0IDEwSDkuNVY0LjkzMmwxLjk1NyAxLjk1Ny43MDctLjcwN0w4Ljk4MiAzIDUuOCA2LjE4MnoiIGZpbGw9IiMxODFEMUYiLz48L3N2Zz4=)}.ag-charts-icon-delete{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzEzMTcyMiIgZD0iTTguNDk2IDguOTk2QS41LjUgMCAwIDEgOSA5LjQ5MnY0YS41LjUgMCAxIDEtMSAuMDA4di00YS41LjUgMCAwIDEgLjQ5Ni0uNTA0TTEyIDkuNWEuNS41IDAgMCAwLTEgMHY0YS41LjUgMCAwIDAgMSAweiIvPjxwYXRoIGZpbGw9IiMxMzE3MjIiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTYgNVYzLjVBMi41IDIuNSAwIDAgMSA4LjUgMWgzQTIuNSAyLjUgMCAwIDEgMTQgMy41VjVoMi44MzNhLjUuNSAwIDAgMSAwIDFIMTV2MTAuMjVjMCAuNDE1LS4wNjYuODYzLS4zIDEuMjIxLS4yNTcuMzk0LS42NzIuNjEyLTEuMi42MTJoLTdjLS41MjggMC0uOTQzLS4yMTgtMS4yLS42MTItLjIzNC0uMzU4LS4zLS44MDYtLjMtMS4yMjFWNkgzLjMzM2EuNS41IDAgMCAxIDAtMXptMS0xLjVBMS41IDEuNSAwIDAgMSA4LjUgMmgzQTEuNSAxLjUgMCAwIDEgMTMgMy41VjVIN3pNNiAxNi4yNVY2aDh2MTAuMjVjMCAuMzM1LS4wNTkuNTU0LS4xMzguNjc1LS4wNTUuMDg1LS4xNC4xNTgtLjM2Mi4xNThoLTdjLS4yMjIgMC0uMzA3LS4wNzMtLjM2Mi0uMTU4LS4wOC0uMTIxLS4xMzgtLjM0LS4xMzgtLjY3NSIgY2xpcC1ydWxlPSJldmVub2RkIi8+PC9zdmc+)}.ag-charts-icon-disjoint-channel,.ag-charts-icon-disjoint-channel-drawing{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzEzMTcyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTkuMDI4IDE3LjQ2YTIuMjUgMi4yNSAwIDAgMC00LjA5Mi0xLjg1bC05LjUxMS0yLjM3OGEyLjI1IDIuMjUgMCAxIDAtLjIyNS45NzRsOS40NzUgMi4zNjlhMi4yNTEgMi4yNTEgMCAwIDAgNC4zNTMuODg2bS0xLjY2Mi0xLjk2NWExLjI1IDEuMjUgMCAxIDEtLjg4NSAyLjMzOCAxLjI1IDEuMjUgMCAwIDEgLjg4NS0yLjMzOE00LjM0MyAxMy42NjlhMS4yNSAxLjI1IDAgMSAwLTIuMzM4LS44ODUgMS4yNSAxLjI1IDAgMCAwIDIuMzM4Ljg4NU0zLjk3IDguNzY5YTIuMjUgMi4yNSAwIDAgMCAxLjQ1NS0yLjExbDkuNTExLTIuMzc4YTIuMjUgMi4yNSAwIDEgMC0uMjYtLjk2NUw1LjIgNS42ODVhMi4yNSAyLjI1IDAgMSAwLTEuMjMgMy4wODRtLjM3My0yLjU0N2ExLjI1IDEuMjUgMCAxIDEtMi4zMzguODg1IDEuMjUgMS4yNSAwIDAgMSAyLjMzOC0uODg1bTEzLjc1LTMuNDM4YTEuMjUgMS4yNSAwIDEgMS0yLjMzOC44ODUgMS4yNSAxLjI1IDAgMCAxIDIuMzM4LS44ODUiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==)}.ag-charts-icon-drag-handle{--icon: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48Y2lyY2xlIGN4PSI1Ljc1IiBjeT0iNy43NSIgcj0iLjc1IiBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii41Ii8+PGNpcmNsZSBjeD0iOS43NSIgY3k9IjcuNzUiIHI9Ii43NSIgZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIvPjxjaXJjbGUgY3g9IjEzLjc1IiBjeT0iNy43NSIgcj0iLjc1IiBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii41Ii8+PGNpcmNsZSBjeD0iMTMuNzUiIGN5PSIxMS43NSIgcj0iLjc1IiBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii41Ii8+PGNpcmNsZSBjeD0iOS43NSIgY3k9IjExLjc1IiByPSIuNzUiIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjUiLz48Y2lyY2xlIGN4PSI1Ljc1IiBjeT0iMTEuNzUiIHI9Ii43NSIgZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIvPjwvc3ZnPg==)}.ag-charts-icon-fibonacci-retracement-drawing{--icon: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBmaWxsPSIjMDAwIiBkPSJNMiA1aDEydjFIMnoiLz48Y2lyY2xlIGN4PSIxNS43NSIgY3k9IjUuNSIgcj0iMS43NSIgc3Ryb2tlPSIjMDAwIi8+PGNpcmNsZSBjeD0iNC4yNSIgY3k9IjE0LjUiIHI9IjEuNzUiIHN0cm9rZT0iIzAwMCIvPjxwYXRoIGZpbGw9IiMwMDAiIGQ9Ik0xOCAxNUg2di0xaDEyem0wLTQuNUgydi0xaDE2eiIvPjwvc3ZnPg==)}.ag-charts-icon-fibonacci-retracement-trend-based-drawing{--icon: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBmaWxsPSIjMDAwIiBkPSJtNC45OTYgMTIuNjc0IDMuMjkxLTUuNzQzLjg2OC40OTctMy4yOTEgNS43NDN6Ii8+PGNpcmNsZSBjeD0iOS43NSIgY3k9IjUuNSIgcj0iMS43NSIgc3Ryb2tlPSIjMDAwIi8+PGNpcmNsZSBjeD0iNC4zNTEiIGN5PSIxNC41IiByPSIxLjc1IiBzdHJva2U9IiMwMDAiLz48cGF0aCBmaWxsPSIjMDAwIiBkPSJNMTggNmgtN1Y1aDd6bTAgNC41aC03di0xaDd6bTAgNC41SDZ2LTFoMTJ6Ii8+PC9zdmc+)}.ag-charts-icon-fill-color{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJtOC4wNzEgNC4wNi0uOTI0LS45MjQuNzA3LS43MDcgNy4yODggNy4yODgtNC45NSA0Ljk1YTMuNSAzLjUgMCAwIDEtNC45NSAwbC0xLjQxNC0xLjQxNGEzLjUgMy41IDAgMCAxIDAtNC45NXptLjcwNy43MDhMNC41MzYgOS4wMWEyLjUgMi41IDAgMCAwIDAgMy41MzZMNS45NSAxMy45NmEyLjUgMi41IDAgMCAwIDMuNTM1IDBsNC4yNDMtNC4yNDN6bTYuOSA3LjIwMi0uMzQ1LjM2My0uMzQ0LS4zNjNhLjUuNSAwIDAgMSAuNjg4IDBtLS4zNDUgMS4wOGE4IDggMCAwIDAtLjI4LjMyMyA0LjMgNC4zIDAgMCAwLS40MDkuNTgyYy0uMTEzLjIwMS0uMTQ0LjMyNi0uMTQ0LjM3OGEuODMzLjgzMyAwIDAgMCAxLjY2NyAwYzAtLjA1Mi0uMDMxLS4xNzctLjE0NC0uMzc4YTQuMyA0LjMgMCAwIDAtLjQxLS41ODIgOCA4IDAgMCAwLS4yOC0uMzIybS0uMzQ0LTEuMDguMzQ0LjM2My4zNDQtLjM2My4wMDIuMDAyLjAwNC4wMDQuMDEzLjAxMmE2IDYgMCAwIDEgLjIwNi4yMDhjLjEzMS4xMzYuMzA4LjMyNy40ODUuNTQ1LjE3Ni4yMTUuMzYzLjQ2Ny41MDcuNzI0LjEzNy4yNDMuMjczLjU1My4yNzMuODY4YTEuODMzIDEuODMzIDAgMSAxLTMuNjY3IDBjMC0uMzE1LjEzNi0uNjI1LjI3My0uODY4LjE0NC0uMjU3LjMzLS41MDkuNTA3LS43MjRhOSA5IDAgMCAxIC42NDUtLjcwOGwuMDQ2LS4wNDUuMDEzLS4wMTIuMDA0LS4wMDR6IiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48L3N2Zz4=)}.ag-charts-icon-hollow-candlestick-series{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzEzMTcyMiIgZmlsbC1vcGFjaXR5PSIuMTUiIGQ9Ik01IDVoM3YxMEg1eiIvPjxwYXRoIGZpbGw9IiMxMzE3MjIiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTcgMXYzaDJ2MTJIN3YzSDZ2LTNINFY0aDJWMXpNNSA1aDN2MTBINXptNyAyaDN2NmgtM3ptLTEgN1Y2aDJWMy4yNWgxVjZoMnY4aC0ydjIuNzVoLTFWMTR6IiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48L3N2Zz4=)}.ag-charts-icon-horizontal-line,.ag-charts-icon-horizontal-line-drawing{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzEzMTcyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNLjUgOS41aDcuMzA2YTIuMjUgMi4yNSAwIDAgMSA0LjM4OCAwSDE5LjV2MWgtNy4zMDZhMi4yNSAyLjI1IDAgMCAxLTQuMzg4IDBILjV6bTkuNSAxLjc1YTEuMjUgMS4yNSAwIDEgMCAwLTIuNSAxLjI1IDEuMjUgMCAwIDAgMCAyLjUiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==)}.ag-charts-icon-line-color{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTQuMjQyIDIuNzIyYy0uNjEyIDAtMS4yLjI0My0xLjYzMi42NzVsLTEuMzQzIDEuMzQ0YS41LjUgMCAwIDAtLjExMi4xMTJMNC4wNSAxMS45NTljLS4yMDcuMjA3LS4zNi40Ni0uNDQ2Ljc0di4wMDFsLS42OSAyLjc2N3YuMDAyYS44Mi44MiAwIDAgMCAxLjAyMiAxLjAyMWguMDAybDIuNjM0LS44MjJjLjI4LS4wODUuNTM0LS4yMzcuNzQtLjQ0M2w3LjEwNy03LjEwOGEuNS41IDAgMCAwIC4xMTItLjExMmwxLjM0My0xLjM0M2EyLjMwOCAyLjMwOCAwIDAgMC0xLjYzMi0zLjk0TTE0LjEyMiA3bDEuMDQ0LTEuMDQ1YTEuMzA4IDEuMzA4IDAgMSAwLTEuODQ5LTEuODVMMTIuMjcxIDUuMTV6bS0yLjU1OC0xLjE0Mi02LjgwNyA2LjgwOWEuOC44IDAgMCAwLS4xOTYuMzI1bC0uNzUgMi40NjggMi40Ny0uNzQ5YS44LjggMCAwIDAgLjMyNS0uMTk0bDYuODA4LTYuODF6IiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48L3N2Zz4=)}.ag-charts-icon-line-series{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzEzMTcyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJtMTcuMzYyIDQuODczLTQuNTk0IDYuNjU0LTQuODUtMy4zMTctNC4yNTEgNi45NzctLjg1NC0uNTJMNy42MTIgNi43OWw0Ljg5OSAzLjM1IDQuMDI4LTUuODM2eiIgY2xpcC1ydWxlPSJldmVub2RkIi8+PC9zdmc+)}.ag-charts-icon-line-style-dashed{--icon: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBmaWxsPSIjMDAwIiBkPSJNMiA5aDR2MUgyem0xMiAwaDR2MWgtNHpNOCA5aDR2MUg4eiIvPjwvc3ZnPg==)}.ag-charts-icon-line-style-dotted{--icon: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48Y2lyY2xlIGN4PSIyLjUiIGN5PSI5LjUiIHI9Ii41IiBmaWxsPSIjMDAwIi8+PGNpcmNsZSBjeD0iNC41IiBjeT0iOS41IiByPSIuNSIgZmlsbD0iIzAwMCIvPjxjaXJjbGUgY3g9IjYuNSIgY3k9IjkuNSIgcj0iLjUiIGZpbGw9IiMwMDAiLz48Y2lyY2xlIGN4PSI4LjUiIGN5PSI5LjUiIHI9Ii41IiBmaWxsPSIjMDAwIi8+PGNpcmNsZSBjeD0iMTAuNSIgY3k9IjkuNSIgcj0iLjUiIGZpbGw9IiMwMDAiLz48Y2lyY2xlIGN4PSIxMi41IiBjeT0iOS41IiByPSIuNSIgZmlsbD0iIzAwMCIvPjxjaXJjbGUgY3g9IjE0LjUiIGN5PSI5LjUiIHI9Ii41IiBmaWxsPSIjMDAwIi8+PGNpcmNsZSBjeD0iMTYuNSIgY3k9IjkuNSIgcj0iLjUiIGZpbGw9IiMwMDAiLz48L3N2Zz4=)}.ag-charts-icon-line-style-solid{--icon: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBmaWxsPSIjMDAwIiBkPSJNMiA5aDE2djFIMnoiLz48L3N2Zz4=)}.ag-charts-icon-line-with-markers-series{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzEzMTcyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJtMTguMTk4IDQuODg4LTMuNTU2IDQuOTE4YTIuMjUgMi4yNSAwIDEgMS0zLjg2Ni43NWwtMS40MzItLjlhMi4yNCAyLjI0IDAgMCAxLTIuMDA5LjQzNWwtMy44MjggNi40MjgtLjg2LS41MTJMNi40NSA5LjYyM2EyLjI1IDIuMjUgMCAxIDEgMy41MS0uNzYxbDEuMzI5LjgzNWEyLjI0IDIuMjQgMCAwIDEgMi41NTctLjQ5N2wzLjU0Mi00Ljg5OHptLTQuOTYgNS4xNTNhMS4yNSAxLjI1IDAgMSAwLS42NCAyLjQxOSAxLjI1IDEuMjUgMCAwIDAgLjY0LTIuNDE5TTkuMSA4LjMyMXEuMDY2LS4xOTIuMDY3LS40MDRhMS4yNSAxLjI1IDAgMSAwLS4wNjcuNDA0IiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48L3N2Zz4=)}.ag-charts-icon-lock,.ag-charts-icon-locked{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTAuMjA3IDMuNzY0YTIuODk0IDIuODk0IDAgMCAwLTIuODk1IDIuODk0VjloNS43ODlWNi42NThhMi44OTQgMi44OTQgMCAwIDAtMi44OTUtMi44OTRNMTQuMSA5VjYuNjU4YTMuODk0IDMuODk0IDAgMSAwLTcuNzg5IDB2Mi4zNDlBMi41IDIuNSAwIDAgMCA0IDExLjV2M0EyLjUgMi41IDAgMCAwIDYuNSAxN2g4YTIuNSAyLjUgMCAwIDAgMi41LTIuNXYtM0EyLjUgMi41IDAgMCAwIDE0LjUgOXpNNi41IDEwQTEuNSAxLjUgMCAwIDAgNSAxMS41djNBMS41IDEuNSAwIDAgMCA2LjUgMTZoOGExLjUgMS41IDAgMCAwIDEuNS0xLjV2LTNhMS41IDEuNSAwIDAgMC0xLjUtMS41eiIgY2xpcC1ydWxlPSJldmVub2RkIi8+PC9zdmc+)}.ag-charts-icon-measurer-drawing{--icon: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0ibTQuNDYxIDEyLjcxIDEuNTMyLTEuNTMxIDEuNDE0IDEuNDE0LjcwNy0uNzA3TDYuNyAxMC40NzJsMS41MzItMS41MzMgMiAyIC43MDctLjcwNy0yLTIgNi4wMS02LjAxIDIuODMgMi44MjhMNS4wNSAxNy43NzggMi4yMjIgMTQuOTVsMS41MzItMS41MzIgMS40MTQgMS40MTQuNzA3LS43MDd6TS44MDggMTQuOTVsLjcwNy0uNzA3TDE0LjI0MyAxLjUxNWwuNzA3LS43MDcuNzA3LjcwNyAyLjgyOCAyLjgyOC43MDcuNzA3LS43MDcuNzA3TDUuNzU3IDE4LjQ4NWwtLjcwNy43MDctLjcwNy0uNzA3LTIuODI4LTIuODI4em0xMS4wNzgtNi44MzVMMTAuNDcgNi43bC43MDctLjcwNyAxLjQxNSAxLjQxNHptLjgyNC0zLjY1NCAxIDEgLjcwOC0uNzA3LTEtMXoiIGZpbGw9IiMxODFEMUYiLz48L3N2Zz4=)}.ag-charts-icon-note-annotation{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMyA0LjVBMS41IDEuNSAwIDAgMSA0LjUgM2gxMUExLjUgMS41IDAgMCAxIDE3IDQuNXY4YTEuNSAxLjUgMCAwIDEtMS41IDEuNWgtMy4yMWwtMS40NjkgMi41N2ExIDEgMCAwIDEtMS42ODIuMDg1TDcuMjQzIDE0SDQuNUExLjUgMS41IDAgMCAxIDMgMTIuNXpNNC41IDRhLjUuNSAwIDAgMC0uNS41djhhLjUuNSAwIDAgMCAuNS41aDMuMjU3bDIuMTk2IDMuMDc0TDExLjcxIDEzaDMuNzlhLjUuNSAwIDAgMCAuNS0uNXYtOGEuNS41IDAgMCAwLS41LS41eiIgY2xpcC1ydWxlPSJldmVub2RkIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNNi41IDYuNUEuNS41IDAgMCAxIDcgNmg2YS41LjUgMCAwIDEgMCAxSDdhLjUuNSAwIDAgMS0uNS0uNU02LjUgOS41QS41LjUgMCAwIDEgNyA5aDZhLjUuNSAwIDAgMSAwIDFIN2EuNS41IDAgMCAxLS41LS41IiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48L3N2Zz4=)}.ag-charts-icon-ohlc-series{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzEzMTcyMiIgZD0iTTEzIDExaC0zdi0xaDNWM2gxdjJoNHYxaC00djExaC0xek02IDE3di0yaDN2LTFINlY0SDV2MUgydjFoM3YxMXoiLz48L3N2Zz4=)}.ag-charts-icon-pan-end{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzAwMCIgZD0ibTYuNjQ2IDEzLjgxMy0uMzUzLjM1NC43MDcuNzA3LjM1NC0uMzU0ek0xMS4xNjYgMTBsLjM1NC4zNTQuMzU0LS4zNTQtLjM1NC0uMzU0ek03LjM1NSA1LjQ4IDcgNS4xMjZsLS43MDcuNzA3LjM1My4zNTR6bTAgOS4wNCA0LjE2Ni00LjE2Ni0uNzA3LS43MDgtNC4xNjcgNC4xNjd6bTQuMTY2LTQuODc0TDcuMzU0IDUuNDhsLS43MDguNzA3IDQuMTY3IDQuMTY3ek0xMy4wODMgNXYxMGgxVjV6Ii8+PC9zdmc+)}.ag-charts-icon-pan-left{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTIuNzkgNS44MzMgOC42MjUgMTBsNC4xNjYgNC4xNjctLjcwNy43MDdMNy4yMSAxMGw0Ljg3My00Ljg3NHoiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==)}.ag-charts-icon-pan-right{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNNy4yMSAxNC4xNjcgMTEuMzc2IDEwIDcuMjEgNS44MzNsLjcwNy0uNzA3TDEyLjc5IDEwbC00Ljg3MyA0Ljg3NHoiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==)}.ag-charts-icon-pan-start{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzAwMCIgZD0iTTYgNXYxMGgxVjV6TTkuNjI0IDEwbDQuMTY2LTQuMTY3LS43MDctLjcwN0w4LjIxIDEwbDQuODc0IDQuODc0LjcwNy0uNzA3eiIvPjwvc3ZnPg==)}.ag-charts-icon-parallel-channel,.ag-charts-icon-parallel-channel-drawing{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzEzMTcyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTcuNzIgNS4zMzFBMi4yNSAyLjI1IDAgMSAwIDE0LjcwNSAzLjZsLTkuNDkgNC41NjJhMi4yNSAyLjI1IDAgMSAwIC4yMDkgMS4wMWw5LjY2Mi00LjY0NmEyLjI1IDIuMjUgMCAwIDAgMi42MzQuODA1bS4zNzMtMi41NDdhMS4yNSAxLjI1IDAgMSAxLTIuMzM4Ljg4NSAxLjI1IDEuMjUgMCAwIDEgMi4zMzgtLjg4NU00LjM0MyA4LjY3YTEuMjUgMS4yNSAwIDEgMS0yLjMzOC44ODUgMS4yNSAxLjI1IDAgMCAxIDIuMzM4LS44ODVNNS4zMDcgMTYuNzI4YTIuMjUgMi4yNSAwIDEgMS0uNTI1LS44NThsOS45MjMtNC43N2EyLjI1IDIuMjUgMCAxIDEgLjM4MS45MjZ6bS0uOTY0LjI3NGExLjI1IDEuMjUgMCAxIDEtMi4zMzguODg1IDEuMjUgMS4yNSAwIDAgMSAyLjMzOC0uODg1bTEzLjAyMy01LjEwNmExLjI1IDEuMjUgMCAxIDAtLjg4NS0yLjMzOSAxLjI1IDEuMjUgMCAwIDAgLjg4NSAyLjMzOSIgY2xpcC1ydWxlPSJldmVub2RkIi8+PC9zdmc+)}.ag-charts-icon-position-bottom{--icon: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii4yNSIgZD0iTTMgMTBoMTR2MUgzem0zLTNoOHYxSDZ6Ii8+PHBhdGggZmlsbD0iIzAwMCIgZD0iTTYgMTNoOHYxSDZ6Ii8+PC9zdmc+)}.ag-charts-icon-position-center{--icon: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBmaWxsPSIjMDAwIiBkPSJNMyAxMGgxNHYxSDN6Ii8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuMjUiIGQ9Ik02IDdoOHYxSDZ6bTAgNmg4djFINnoiLz48L3N2Zz4=)}.ag-charts-icon-position-top{--icon: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii4yNSIgZD0iTTMgMTBoMTR2MUgzeiIvPjxwYXRoIGZpbGw9IiMwMDAiIGQ9Ik02IDdoOHYxSDZ6Ii8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuMjUiIGQ9Ik02IDEzaDh2MUg2eiIvPjwvc3ZnPg==)}.ag-charts-icon-price-label-annotation{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNNC41IDNBMS41IDEuNSAwIDAgMCAzIDQuNVYxM2ExLjUgMS41IDAgMCAwIDEuNSAxLjVoLjgzM3YuMDU3Yy4yNDItLjI5OS41OTctLjUwMyAxLS41NDhWMTMuNUg0LjVBLjUuNSAwIDAgMSA0IDEzVjQuNWEuNS41IDAgMCAxIC41LS41aDExYS41LjUgMCAwIDEgLjUuNXY4YS41LjUgMCAwIDEtLjUuNWgtNC44MThsLS4xMjYuMDg0YTI2IDI2IDAgMCAwLTIuMjI1IDEuNjg1bC0uMzIuMjY1LS4wNjguMDU2YTEuNSAxLjUgMCAwIDEtMi42MDkgMS4zNTRjLjAzMy43NjMuOTA1IDEuMjM4IDEuNTYuNzU2LjY0Ni0uNDc0IDEuMjEtLjk0MyAxLjc2MS0xLjRsLjMxMy0uMjZBMjYgMjYgMCAwIDEgMTAuOTg2IDE0SDE1LjVhMS41IDEuNSAwIDAgMCAxLjUtMS41di04QTEuNSAxLjUgMCAwIDAgMTUuNSAzeiIgY2xpcC1ydWxlPSJldmVub2RkIi8+PHBhdGggZmlsbD0iIzEzMTcyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNOC43MTYgMTQuODE1YTIuMjUgMi4yNSAwIDEgMS00LjIxIDEuNTkzIDIuMjUgMi4yNSAwIDAgMSA0LjIxLTEuNTkzbS0xLjY2MiAxLjk2NmExLjI1IDEuMjUgMCAxIDAtLjg4NS0yLjMzOSAxLjI1IDEuMjUgMCAwIDAgLjg4NSAyLjMzOSIgY2xpcC1ydWxlPSJldmVub2RkIi8+PC9zdmc+)}.ag-charts-icon-price-range-drawing{--icon: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNOS4wNDYgMTVWNS44NzdoLjk0MlYxNXoiIGZpbGw9IiMxODFEMUYiLz48cGF0aCBkPSJNOS4wNDYgMTVWNS44NzdoLjk0MlYxNXoiIGZpbGw9IiMxODFEMUYiLz48cGF0aCBkPSJNOS41IDYuMjI4IDcuMTY3IDguMzc2IDYuNSA3Ljc2MiA5LjUgNWwzIDIuNzYyLS42NjcuNjE0eiIgZmlsbD0iIzAwMCIvPjxwYXRoIGQ9Ik0yIDE4di0xaDE2djF6TTIgM1YyaDE2djF6IiBmaWxsPSIjMTgxRDFGIi8+PC9zdmc+)}.ag-charts-icon-reset{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTIuMDQgNC40NDVhNS44MSA1LjgxIDAgMCAwLTcuMjU3IDIuNDUzLjUuNSAwIDAgMS0uODY1LS41MDJBNi44MSA2LjgxIDAgMSAxIDMgOS44MTNhLjUuNSAwIDAgMSAxIDAgNS44MSA1LjgxIDAgMSAwIDguMDQtNS4zNjgiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTQuMjg5IDMuMDAyYS41LjUgMCAwIDEgLjUuNXYyLjY1NWgyLjY1NWEuNS41IDAgMCAxIDAgMUg0LjI5YS41LjUgMCAwIDEtLjUtLjVWMy41MDJhLjUuNSAwIDAgMSAuNS0uNSIgY2xpcC1ydWxlPSJldmVub2RkIi8+PC9zdmc+)}.ag-charts-icon-settings{--icon: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjAgMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgc3R5bGU9ImZpbGwtcnVsZTpldmVub2RkIj48cGF0aCBkPSJNMTAgMTNhMyAzIDAgMSAwIDAtNiAzIDMgMCAwIDAgMCA2bTAtMWEyIDIgMCAxIDEtLjAwMS0zLjk5OUEyIDIgMCAwIDEgMTAgMTIiLz48cGF0aCBkPSJNMi4zMSAxNC4zNDVjLS44MTctMS40OTEuMDI3LTIuNDk5LjQ3NC0yLjg2NS41MzEtLjQzNC45NjktLjM2NS45NzItMS40OC0uMDAzLTEuMTE1LS40NDEtMS4wNDYtLjk3Mi0xLjQ4MS0uNDU0LS4zNzEtMS4zMTctMS40MDUtLjQzNC0yLjkzNmwuMDA1LS4wMDljLjg4NC0xLjUyIDIuMjA3LTEuMjkgMi43NTUtMS4wODMuNjQxLjI0My44MDEuNjU2IDEuNzY4LjEwMS45NjQtLjU2LjY4Ni0uOTA0Ljc5Ni0xLjU4Mi4wOTQtLjU3OC41NTktMS44NDMgMi4zMjYtMS44NDNoLjAxYzEuNzU5LjAwNSAyLjIyMiAxLjI2NiAyLjMxNiAxLjg0My4xMS42NzgtLjE2OCAxLjAyMi43OTYgMS41ODIuOTY3LjU1NSAxLjEyNy4xNDIgMS43NjgtLjEwMS41NDktLjIwOCAxLjg3Ni0uNDM4IDIuNzYgMS4wOTJzLjAyIDIuNTY1LS40MzQgMi45MzZjLS41MzEuNDM1LS45NjkuMzY2LS45NzIgMS40ODEuMDAzIDEuMTE1LjQ0MSAxLjA0Ni45NzIgMS40OC40NTQuMzcyIDEuMzE3IDEuNDA2LjQzNCAyLjkzN2wtLjAwNS4wMDljLS44ODQgMS41Mi0yLjIwNyAxLjI5LTIuNzU1IDEuMDgzLS42NDEtLjI0My0uODAxLS42NTYtMS43NjgtLjEwMS0uOTY0LjU2LS42ODYuOTA0LS43OTYgMS41ODEtLjA5NC41NzktLjU1OSAxLjg0NC0yLjMyNiAxLjg0NGgtLjAxYy0xLjc1OS0uMDA1LTIuMjIyLTEuMjY2LTIuMzE2LTEuODQ0LS4xMS0uNjc3LjE2OC0xLjAyMS0uNzk2LTEuNTgxLS45NjctLjU1NS0xLjEyNy0uMTQyLTEuNzY4LjEwMS0uNTQ5LjIwOC0xLjg3Ni40MzgtMi43Ni0xLjA5MmwtLjAyLS4wMzZ6TTkuOTg0IDIuMTYySDEwYzEuMzU1IDAgMS4zNDIgMS4wMzkgMS4zNTMgMS40MjUuMDA4LjMxMi4wNCAxLjE2IDEuMjU5IDEuODcybC4wMTUuMDA4YzEuMjI1LjcgMS45NzYuMzA0IDIuMjUxLjE1NS4zMzctLjE4MyAxLjIyNi0uNzExIDEuOTAyLjQ0NWwuMDA4LjAxNGMuNjc4IDEuMTczLS4yMjkgMS42ODItLjU1OCAxLjg4NC0uMjY2LjE2My0uOTg0LjYxNS0uOTkxIDIuMDI3di4wMTZjLjAwNyAxLjQxMi43MjUgMS44NjQuOTkxIDIuMDI3LjMyOC4yMDEgMS4yMjkuNzA3LjU2NiAxLjg3bC0uMDA4LjAxNGMtLjY3NyAxLjE3NC0xLjU3MS42NDMtMS45MS40NTktLjI3NS0uMTQ5LTEuMDI2LS41NDUtMi4yNTEuMTU0bC0uMDE1LjAwOWMtMS4yMTkuNzEyLTEuMjUxIDEuNTYtMS4yNTkgMS44NzItLjAxMS4zODYuMDAyIDEuNDI1LTEuMzUzIDEuNDI1cy0xLjM0Mi0xLjAzOS0xLjM1My0xLjQyNWMtLjAwOC0uMzEyLS4wNC0xLjE2LTEuMjU5LTEuODcybC0uMDE1LS4wMDljLTEuMjI1LS42OTktMS45NzYtLjMwMy0yLjI1MS0uMTU0LS4zMzYuMTgzLTEuMjE5LjcwNi0xLjg5NC0uNDMybC0uMDE2LS4wMjdjLS42NzgtMS4xNzQuMjI5LTEuNjgyLjU1OC0xLjg4NC4yNjYtLjE2My45ODQtLjYxNS45OTEtMi4wMjd2LS4wMTZjLS4wMDctMS40MTItLjcyNS0xLjg2NC0uOTkxLTIuMDI3LS4zMjgtLjIwMS0xLjIyOS0uNzA3LS41NjYtMS44N2wuMDA4LS4wMTRjLjY3Ny0xLjE3NCAxLjU3MS0uNjQzIDEuOTEtLjQ1OS4yNzUuMTQ5IDEuMDI2LjU0NSAyLjI1MS0uMTU1bC4wMTUtLjAwOGMxLjIxOS0uNzEyIDEuMjUxLTEuNTYgMS4yNTktMS44NzIuMDEtLjM4NC0uMDAyLTEuNDE3IDEuMzM3LTEuNDI1Ii8+PC9zdmc+)}.ag-charts-icon-step-line-series{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzE4MUQxRiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNNiA0aDV2OGgzVjhoNXYxaC00djRoLTVWNUg3djEwSDJ2LTFoNHoiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==)}.ag-charts-icon-text-annotation{--icon: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00IDRIMTZWN0gxNVY1SDEwLjVWMTVIMTRWMTZINlYxNUg5LjVWNUg1VjdINFY0WiIgZmlsbD0iYmxhY2siLz4KPC9zdmc+Cg==)}.ag-charts-icon-trend-line,.ag-charts-icon-trend-line-drawing{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzEzMTcyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNNS4zMTQgMTAuOTM4YTIuMjUgMi4yNSAwIDEgMSAuMDEtMWg5LjM1MmEyLjI1IDIuMjUgMCAxIDEgLjAxIDF6bS0yLjE4OS43MjlhMS4yNSAxLjI1IDAgMSAwIDAtMi41IDEuMjUgMS4yNSAwIDAgMCAwIDIuNW0xMy43NSAwYTEuMjUgMS4yNSAwIDEgMCAwLTIuNSAxLjI1IDEuMjUgMCAwIDAgMCAyLjUiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==)}.ag-charts-icon-unlock,.ag-charts-icon-unlocked{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTAuNjUxIDMuNWEyLjg5NCAyLjg5NCAwIDAgMC0yLjg5NCAyLjg5NFY5SDE0LjVhMi41IDIuNSAwIDAgMSAyLjUgMi41djNhMi41IDIuNSAwIDAgMS0yLjUgMi41aC04QTIuNSAyLjUgMCAwIDEgNCAxNC41di0zQTIuNSAyLjUgMCAwIDEgNi41IDloLjI1N1Y2LjM5NGEzLjg5NCAzLjg5NCAwIDEgMSA3Ljc4OSAwIC41LjUgMCAwIDEtMSAwQTIuODk0IDIuODk0IDAgMCAwIDEwLjY1IDMuNU02LjUgMTBBMS41IDEuNSAwIDAgMCA1IDExLjV2M0ExLjUgMS41IDAgMCAwIDYuNSAxNmg4YTEuNSAxLjUgMCAwIDAgMS41LTEuNXYtM2ExLjUgMS41IDAgMCAwLTEuNS0xLjV6IiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48L3N2Zz4=)}.ag-charts-icon-vertical-line,.ag-charts-icon-vertical-line-drawing{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzEzMTcyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTAuNSA3LjgwNmEyLjI1IDIuMjUgMCAwIDEgMCA0LjM4OFYxOS41aC0xdi03LjMwNmEyLjI1IDIuMjUgMCAwIDEgMC00LjM4OFYuNWgxem0tLjUuOTQ0YTEuMjUgMS4yNSAwIDEgMSAwIDIuNSAxLjI1IDEuMjUgMCAwIDEgMC0yLjUiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==)}.ag-charts-icon-zoom-in{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzAwMCIgZD0iTTEwIDUuNWEuNS41IDAgMCAxIC41LjV2My41aDMuODc1YS41LjUgMCAwIDEgMCAxSDEwLjV2NC4yNWEuNS41IDAgMSAxLTEgMFYxMC41SDUuNjI1YS41LjUgMCAxIDEgMC0xSDkuNVY2YS41LjUgMCAwIDEgLjUtLjUiLz48L3N2Zz4=)}.ag-charts-icon-zoom-out{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNNS41IDEwYS41LjUgMCAwIDEgLjUtLjVoOGEuNS41IDAgMCAxIDAgMUg2YS41LjUgMCAwIDEtLjUtLjUiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==)}.ag-charts-icon-high-low-series{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzEzMTcyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNNyA0aDJ2MTJINFY0aDNNNSA1aDN2MTBINXpNMTEgMTRWNmg1djhoLTVtMS03aDN2NmgtM3oiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==)}.ag-charts-icon-hlc-series{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzEzMTcyMiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJtMTguMTYzIDEuODM3LTUuMzM0IDExLjYyMUw2Ljk1NyA4LjEybC00LjE5OSA5LjYyMi0uOTE2LS40IDQuNzU2LTEwLjlMMTIuNDkgMTEuOCAxNy4yNTQgMS40MnoiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjQiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTUuODI1IDIuNzA0LjU1IDEzLjc4NWwuOTAyLjQzIDQuNzI0LTkuOTE5IDYuMDM0IDUuMDI5IDMuMjU1LTguMTQtLjkyOC0uMzctMi43NDUgNi44NnptNy44NTIgMTQuNjM2IDUuNzgtMTMuMTM5LS45MTUtLjQwMi01LjIxOSAxMS44Ni02LjAwNS01LjUwNC0zLjI3OCA3LjY0OC45Mi4zOTQgMi43MjItNi4zNTJ6IiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48L3N2Zz4=)}.ag-charts-icon-chevron-right{--icon: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjAgMjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03LjQ3IDUuNDdhLjc1Ljc1IDAgMCAxIDEuMDYgMGw0IDRhLjc1Ljc1IDAgMCAxIDAgMS4wNmwtNCA0YS43NS43NSAwIDAgMS0xLjA2LTEuMDZMMTAuOTQgMTAgNy40NyA2LjUzYS43NS43NSAwIDAgMSAwLTEuMDYiIGZpbGw9IiMwMDAiLz48L3N2Zz4=)}.ag-charts-icon-zoom-in-alt{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLXpvb20taW4iPjxjaXJjbGUgY3g9IjExIiBjeT0iMTEiIHI9IjgiLz48bGluZSB4MT0iMjEiIHgyPSIxNi42NSIgeTE9IjIxIiB5Mj0iMTYuNjUiLz48bGluZSB4MT0iMTEiIHgyPSIxMSIgeTE9IjgiIHkyPSIxNCIvPjxsaW5lIHgxPSI4IiB4Mj0iMTQiIHkxPSIxMSIgeTI9IjExIi8+PC9zdmc+)}.ag-charts-icon-zoom-out-alt{--icon: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLXpvb20tb3V0Ij48Y2lyY2xlIGN4PSIxMSIgY3k9IjExIiByPSI4Ii8+PGxpbmUgeDE9IjIxIiB4Mj0iMTYuNjUiIHkxPSIyMSIgeTI9IjE2LjY1Ii8+PGxpbmUgeDE9IjgiIHgyPSIxNCIgeTE9IjExIiB5Mj0iMTEiLz48L3N2Zz4=)}.ag-charts-input{--input-layer-active: 1;--input-layer-focus: 2;--input-padding: calc(var(--ag-charts-spacing) * 2);--input-padding-large: calc(var(--ag-charts-spacing) * 2.5);color:var(--ag-charts-input-text-color);font-family:var(--ag-charts-chrome-font-family);font-size:var(--ag-charts-chrome-font-size-large);transition-duration:.25s;transition-property:none;transition-timing-function:ease-out}.ag-charts-input:focus-visible{outline:var(--ag-charts-focus-border);box-shadow:var(--ag-charts-focus-border-shadow);z-index:var(--input-layer-focus)}.ag-charts-button{background:var(--ag-charts-button-background-color);border:var(--ag-charts-button-border);border-radius:var(--ag-charts-button-border-radius);color:var(--ag-charts-button-text-color);cursor:pointer;padding:var(--input-padding);transition-property:background,border-color}.ag-charts-button:hover{background:var(--ag-charts-focus-color)}.ag-charts-button:has(.ag-charts-icon){padding:2px}.ag-charts-checkbox{--checkbox-transition-duration: .1s;appearance:none;background:var(--ag-charts-checkbox-background-color);border-radius:calc(var(--ag-charts-border-radius) * 9);cursor:pointer;height:18px;margin:0;transition-duration:var(--checkbox-transition-duration);transition-property:margin;width:29px}.ag-charts-checkbox:before{display:block;background:var(--ag-charts-input-background-color);border-radius:calc(var(--ag-charts-border-radius) * 7);content:" ";height:14px;margin:2px;transition-duration:var(--checkbox-transition-duration);transition-property:margin;transition-timing-function:var(--ag-charts-input-transition-easing);width:14px}.ag-charts-checkbox:checked{background:var(--ag-charts-checkbox-checked-background-color)}.ag-charts-checkbox:checked:before{margin-left:13px}.ag-charts-select{background:var(--ag-charts-input-background-color);border:var(--ag-charts-input-border);border-radius:var(--ag-charts-input-border-radius);padding:3px 2px 4px;font-size:inherit}.ag-charts-textarea{--textarea-line-height: 1.38;background:var(--ag-charts-input-background-color);border:var(--ag-charts-input-border);border-radius:var(--ag-charts-input-border-radius);line-height:var(--textarea-line-height);font-family:var(--ag-charts-chrome-font-family);font-size:var(--ag-charts-chrome-font-size-large);padding:var(--input-padding-large) var(--input-padding)}.ag-charts-textarea::placeholder{color:var(--ag-charts-input-placeholder-text-color)}.ag-charts-proxy-container{pointer-events:none;position:absolute}.ag-charts-proxy-legend-toolbar{pointer-events:auto}.ag-charts-proxy-legend-toolbar>div[role=listitem]{pointer-events:none}.ag-charts-proxy-elem{-webkit-appearance:none;appearance:none;background:none;border:none;color:#0000;overflow:hidden;pointer-events:auto;position:absolute}.ag-charts-proxy-elem::-moz-range-thumb,.ag-charts-proxy-elem::-moz-range-track{opacity:0}.ag-charts-proxy-elem::-webkit-slider-runnable-track,.ag-charts-proxy-elem::-webkit-slider-thumb{opacity:0}.ag-charts-proxy-elem:focus-visible{outline:var(--ag-charts-focus-border);box-shadow:var(--ag-charts-focus-border-shadow)}.ag-charts-proxy-elem svg{display:block}.ag-charts-proxy-scrollbar-slider:focus-visible{outline:none;box-shadow:none}.ag-charts-proxy-scrollbar-thumb-focus{border:var(--ag-charts-focus-border);box-shadow:var(--ag-charts-focus-border-shadow);box-sizing:border-box;opacity:0;pointer-events:none}.ag-charts-proxy-scrollbar-slider:focus-visible~.ag-charts-proxy-scrollbar-thumb-focus{opacity:1}.ag-charts-focus-indicator{position:absolute;display:block;pointer-events:none;user-select:none;-webkit-user-select:none;width:100%;height:100%}.ag-charts-focus-indicator>div{position:absolute;outline:solid 1px var(--ag-charts-chrome-background-color);box-shadow:var(--ag-charts-focus-shadow)}.ag-charts-focus-indicator>svg{width:100%;height:100%;fill:none;overflow:visible}.ag-charts-focus-svg-outer-path{stroke:var(--ag-charts-chrome-background-color);stroke-width:4px}.ag-charts-focus-svg-inner-path{stroke:var(--ag-charts-accent-color);stroke-width:2px}.ag-charts-overlay{color:#181d1f;pointer-events:none}.ag-charts-overlay.ag-charts-dark-overlay{color:#fff}.ag-charts-overlay--loading{color:#8c8c8c}.ag-charts-overlay__loading-background{background:#fff;pointer-events:none}.ag-charts-overlay.ag-charts-dark-overlay .ag-charts-overlay__loading-background{background:#192232}.ag-charts-tooltip{--tooltip-arrow-size: 8px;--tooltip-row-spacing: 8px;--tooltip-column-spacing: 16px;position:fixed;inset:unset;margin:0;padding:0;overflow:visible;top:var(--top, 0px);left:var(--left, 0px);width:max-content;max-width:100%;font-family:var(--ag-charts-chrome-font-family);font-size:var(--ag-charts-chrome-font-size);font-weight:var(--ag-charts-chrome-font-weight);color:var(--ag-charts-tooltip-text-color);background:var(--ag-charts-tooltip-background-color);border:var(--ag-charts-tooltip-border);border-radius:var(--ag-charts-tooltip-border-radius);box-shadow:var(--ag-charts-popup-shadow)}.ag-charts-tooltip--compact .ag-charts-tooltip-content{--tooltip-row-spacing: 2px;--tooltip-column-spacing: 8px;padding:3px 6px}.ag-charts-tooltip--arrow-top:before,.ag-charts-tooltip--arrow-right:before,.ag-charts-tooltip--arrow-bottom:before,.ag-charts-tooltip--arrow-left:before{content:"";position:absolute;display:block;width:var(--tooltip-arrow-size);height:var(--tooltip-arrow-size);border:inherit;border-bottom-color:transparent;border-right-color:transparent;background:inherit;clip-path:polygon(0 0,100% 0,100% 1px,1px 100%,0 100%)}.ag-charts-tooltip--arrow-top:before{bottom:100%;left:50%;transform:translate(-50%) translateY(calc(var(--tooltip-arrow-size) * .5)) rotate(45deg)}.ag-charts-tooltip--arrow-bottom:before{top:100%;left:50%;transform:translate(-50%) translateY(calc(var(--tooltip-arrow-size) * -.5)) rotate(225deg)}.ag-charts-tooltip--arrow-left:before{right:100%;top:50%;transform:translateY(-50%) translate(calc(var(--tooltip-arrow-size) * .5)) rotate(315deg)}.ag-charts-tooltip--arrow-right:before{left:100%;top:50%;transform:translateY(-50%) translate(calc(var(--tooltip-arrow-size) * -.5)) rotate(135deg)}.ag-charts-tooltip--no-interaction{pointer-events:none;user-select:none;-webkit-user-select:none}.ag-charts-tooltip--wrap-always{overflow-wrap:break-word;word-break:break-word;hyphens:none}.ag-charts-tooltip--wrap-hyphenate{overflow-wrap:break-word;word-break:break-word;hyphens:auto}.ag-charts-tooltip--wrap-on-space{overflow-wrap:normal;word-break:normal}.ag-charts-tooltip--wrap-never{white-space:nowrap}.ag-charts-tooltip-heading,.ag-charts-tooltip-title,.ag-charts-tooltip-label,.ag-charts-tooltip-value{overflow:hidden;text-overflow:ellipsis}.ag-charts-tooltip-content{display:grid;grid:auto-flow minmax(1em,auto) / 1fr;padding:8px 12px;gap:var(--tooltip-row-spacing)}.ag-charts-tooltip-content:has(.ag-charts-tooltip-symbol){grid:auto-flow minmax(1em,auto) / auto 1fr}.ag-charts-tooltip-heading{grid-column:1 / -1}.ag-charts-tooltip-symbol{grid-column:1 / 2;place-self:center}.ag-charts-tooltip-symbol svg{display:block}.ag-charts-tooltip-title{grid-column:-2 / -1}.ag-charts-tooltip-row{grid-column:1 / -1;display:flex;gap:var(--tooltip-column-spacing);align-items:baseline;justify-content:space-between;overflow:hidden}.ag-charts-tooltip-row--inline{grid-column:-2 / -1}.ag-charts-tooltip-label{flex:1;min-width:0}.ag-charts-tooltip-value{min-width:0}.ag-charts-tooltip-footer{grid-column:1 / -1;color:var(--ag-charts-tooltip-subtle-text-color);text-align:center}.ag-charts-popover{position:absolute;border:var(--ag-charts-border);border-radius:var(--ag-charts-border-radius);background:var(--ag-charts-panel-background-color);color:var(--ag-charts-chrome-text-color);font-family:var(--ag-charts-chrome-font-family);font-size:var(--ag-charts-chrome-font-size);font-weight:var(--ag-charts-chrome-font-weight);box-shadow:var(--ag-charts-popup-shadow);z-index:var(--ag-charts-layer-ui-overlay)}.ag-charts-menu{--item-padding: 6px 12px;--icon-color: var(--ag-charts-menu-text-color);display:grid;grid:auto-flow auto / 1fr;column-gap:12px;font-size:var(--ag-charts-chrome-font-size)}.ag-charts-menu:has(.ag-charts-menu__icon,.ag-charts-menu__row--stroke-width-visible){grid:auto-flow auto / auto 1fr}.ag-charts-menu__row--stroke-width-visible:before{content:"";height:var(--strokeWidth);width:12px;background:var(--icon-color)}.ag-charts-menu__row--stroke-width-visible[aria-disabled=true]:before{filter:grayscale(1);opacity:.5}.ag-charts-menu__row{display:grid;grid-column:1 / -1;grid-template-columns:subgrid;align-items:center;padding:var(--item-padding)}.ag-charts-menu__row:not(.ag-charts-menu__row--active){cursor:pointer}.ag-charts-menu__row:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.ag-charts-menu__row:last-child{border-bottom-left-radius:inherit;border-bottom-right-radius:inherit}.ag-charts-menu__row:focus{background:var(--ag-charts-focus-color)}.ag-charts-menu__row:focus-visible{outline:var(--ag-charts-focus-border);box-shadow:var(--ag-charts-focus-border-shadow);z-index:var(--ag-charts-layer-menu)}.ag-charts-menu__row--active{--icon-color: var(--ag-charts-accent-color);background:var(--ag-charts-focus-color);color:var(--ag-charts-accent-color)}.ag-charts-menu__label{grid-column:-1 / span 1}.ag-charts-toolbar{--toolbar-gap: calc(var(--ag-charts-spacing) * 2);--toolbar-size: 34px;--toolbar-button-padding: 6px;align-items:center;display:flex;flex-wrap:nowrap;position:absolute}.ag-charts-toolbar__button{align-items:center;background:var(--ag-charts-chrome-background-color);border:var(--ag-charts-button-border);color:var(--ag-charts-button-text-color);cursor:pointer;display:flex;font-family:var(--ag-charts-chrome-font-family);font-size:var(--ag-charts-chrome-font-size-medium);font-weight:var(--ag-charts-chrome-font-weight);justify-content:center;min-height:var(--toolbar-size);min-width:var(--toolbar-size);padding:var(--toolbar-button-padding);position:relative;transition:background-color .25s ease-in-out,border-color .25s ease-in-out,color .25s ease-in-out;white-space:nowrap}.ag-charts-toolbar__button:hover{background:var(--ag-charts-focus-color);z-index:1}.ag-charts-toolbar__button:focus-visible{outline:var(--ag-charts-focus-border);box-shadow:var(--ag-charts-focus-border-shadow);z-index:calc(var(--ag-charts-layer-ui-overlay) + 1)}.ag-charts-toolbar__button--active{background:var(--ag-charts-focus-color);border-color:var(--ag-charts-accent-color);color:var(--ag-charts-accent-color);z-index:2;+.ag-charts-toolbar__button{border-left-color:var(--ag-charts-accent-color)}}.ag-charts-toolbar__button[aria-disabled=true]{background:var(--ag-charts-button-disabled-background-color);color:var(--ag-charts-button-disabled-text-color);cursor:default}.ag-charts-toolbar--horizontal{flex-direction:row;.ag-charts-toolbar__button{border-right-width:0}.ag-charts-toolbar__button--first{border-bottom-left-radius:var(--ag-charts-border-radius);border-top-left-radius:var(--ag-charts-border-radius);margin:0}.ag-charts-toolbar__button--last{border-bottom-right-radius:var(--ag-charts-border-radius);border-top-right-radius:var(--ag-charts-border-radius);border-right-width:1px}}.ag-charts-toolbar--vertical{flex-direction:column;.ag-charts-toolbar__button{margin:-1px 0 0;max-width:100%}.ag-charts-toolbar__button--first{border-top-left-radius:var(--ag-charts-border-radius);border-top-right-radius:var(--ag-charts-border-radius);margin:0}.ag-charts-toolbar__button--last{border-bottom-left-radius:var(--ag-charts-border-radius);border-bottom-right-radius:var(--ag-charts-border-radius)}}.ag-charts-toolbar__icon+.ag-charts-toolbar__label{margin-left:var(--toolbar-gap)}.ag-charts-toolbar__icon,.ag-charts-toolbar__label{pointer-events:none}.ag-charts-floating-toolbar{border:none;display:flex;.ag-charts-toolbar{align-items:unset;position:unset}}.ag-charts-floating-toolbar__drag-handle{align-items:center;background:var(--ag-charts-chrome-background-color);border:var(--ag-charts-border);border-bottom-left-radius:var(--ag-charts-border-radius);border-top-left-radius:var(--ag-charts-border-radius);border-right-width:0;cursor:grab;display:flex;justify-content:center;min-width:24px;padding-left:0;padding-right:0}.ag-charts-floating-toolbar__drag-handle--dragging{cursor:grabbing}\n'; // packages/ag-charts-community/src/util/baseManager.ts var BaseManager = class { constructor() { this.cleanup = new CleanupRegistry(); this.destroyed = false; } destroy() { this.cleanup.flush(); this.destroyed = true; } }; // packages/ag-charts-community/src/util/guardedElement.ts var GuardedElement = class _GuardedElement { constructor(element2, topTabGuard, bottomTabGuard) { this.element = element2; this.topTabGuard = topTabGuard; this.bottomTabGuard = bottomTabGuard; this.cleanup = new CleanupRegistry(); this.guardTabIndex = 0; this.hasFocus = false; this.initTabGuard(this.topTabGuard, false); this.initTabGuard(this.bottomTabGuard, true); this.element.addEventListener("focus", () => this.onFocus(), { capture: true }); this.element.addEventListener("blur", (ev) => this.onBlur(ev), { capture: true }); } set tabIndex(index) { this.guardTabIndex = index; if (this.guardTabIndex === 0) { this.setGuardIndices(void 0); } else if (!this.hasFocus) { this.setGuardIndices(this.guardTabIndex); } } destroy() { this.cleanup.flush(); } initTabGuard(guard, reverse) { this.cleanup.register(attachListener(guard, "focus", () => this.onTab(guard, reverse))); } setGuardIndices(index) { const tabindex = index; setAttribute(this.topTabGuard, "tabindex", tabindex); setAttribute(this.bottomTabGuard, "tabindex", tabindex); } onFocus() { this.hasFocus = true; if (this.guardTabIndex !== 0) { this.setGuardIndices(0); } } onBlur({ relatedTarget }) { const { topTabGuard: top, bottomTabGuard: bot } = this; this.hasFocus = false; if (this.guardTabIndex !== 0 && relatedTarget !== top && relatedTarget !== bot) { this.setGuardIndices(this.guardTabIndex); } } onTab(guard, reverse) { if (this.guardTabIndex !== 0) { let focusTarget; if (guard.tabIndex === 0) { focusTarget = this.findExitTarget(!reverse); this.setGuardIndices(this.guardTabIndex); } else { focusTarget = this.findEnterTarget(reverse); } focusTarget?.focus(); } } static queryFocusable(element2, selectors) { const myWindow = getWindow(); return Array.from(element2.querySelectorAll(selectors)).filter((e) => { if (isHTMLElement(e)) { const style2 = myWindow.getComputedStyle(e); return style2.display !== "none" && style2.visibility !== "none"; } return false; }); } findEnterTarget(reverse) { const focusables = _GuardedElement.queryFocusable(this.element, '[tabindex="0"]'); const index = reverse ? focusables.length - 1 : 0; return focusables[index]; } findExitTarget(reverse) { const focusables = _GuardedElement.queryFocusable(getDocument(), "[tabindex]").filter((e) => e.tabIndex > 0).sort((a, b) => a.tabIndex - b.tabIndex); const { before, after } = _GuardedElement.findBeforeAndAfter(focusables, this.guardTabIndex); return reverse ? before : after; } static findBeforeAndAfter(elements, targetTabIndex) { let left = 0; let right = elements.length - 1; let before = void 0; let after = void 0; while (left <= right) { const mid = Math.floor((left + right) / 2); const currentTabIndex = elements[mid].tabIndex; if (currentTabIndex === targetTabIndex) { before = elements[mid - 1] || void 0; after = elements[mid + 1] || void 0; break; } else if (currentTabIndex < targetTabIndex) { before = elements[mid]; left = mid + 1; } else { after = elements[mid]; right = mid - 1; } } return { before, after }; } }; // packages/ag-charts-community/src/util/stateTracker.ts var StateTracker = class extends Map { constructor(defaultValue, defaultState) { super(); this.defaultValue = defaultValue; this.defaultState = defaultState; } set(key, value) { this.delete(key); if (value !== void 0) { super.set(key, value); } delete this.cachedState; delete this.cachedValue; return this; } delete(key) { delete this.cachedState; delete this.cachedValue; return super.delete(key); } stateId() { this.cachedState ?? (this.cachedState = Array.from(this.keys()).pop() ?? this.defaultState); return this.cachedState; } stateValue() { this.cachedValue ?? (this.cachedValue = Array.from(this.values()).pop() ?? this.defaultValue); return this.cachedValue; } }; // packages/ag-charts-community/src/dom/domLayout.html var domLayout_default = ''; // packages/ag-charts-community/src/dom/domManager.ts var DOM_ELEMENT_CLASSES = [ "styles", "canvas", "canvas-background", "canvas-center", "canvas-container", "canvas-overlay", "canvas-proxy", "series-area", "tooltip-container" ]; var MINIMAL_DOM_ELEMENT_ROLES = /* @__PURE__ */ new Set(["styles", "canvas-container", "canvas", "tooltip-container"]); var CONTAINER_MODIFIERS = { safeHorizontal: "ag-charts-wrapper--safe-horizontal", safeVertical: "ag-charts-wrapper--safe-vertical" }; var domElementConfig = /* @__PURE__ */ new Map([ ["styles", { childElementType: "style" }], ["canvas", { childElementType: "canvas" }], ["canvas-proxy", { childElementType: "div" }], ["canvas-overlay", { childElementType: "div" }], ["canvas-center", { childElementType: "div" }], ["series-area", { childElementType: "div" }], ["tooltip-container", { childElementType: "div" }] ]); function setupObserver(element2, cb) { if (typeof IntersectionObserver === "undefined") return; const observer = new IntersectionObserver( (observedEntries) => { for (const entry of observedEntries) { if (entry.target === element2) { cb(entry.intersectionRatio); } } }, { root: element2 } ); observer.observe(element2); return observer; } var NULL_DOMRECT = { x: 0, y: 0, width: 0, height: 0, top: 0, bottom: 0, left: 0, right: 0, toJSON() { return NULL_DOMRECT; } }; function createTabGuardElement(guardedElem, where) { const div = createElement("div"); div.className = "ag-charts-tab-guard"; guardedElem.insertAdjacentElement(where, div); return div; } var _DOMManager = class _DOMManager extends BaseManager { constructor(eventsHub, chart, initialContainer, styleContainer, skipCss, mode = "normal") { super(); this.eventsHub = eventsHub; this.chart = chart; this.styleContainer = styleContainer; this.skipCss = skipCss; this.mode = mode; this.anchorName = `--${createId(this)}`; this.styles = /* @__PURE__ */ new Map(); this.pendingContainer = void 0; this.container = void 0; this.documentRoot = void 0; this.initiallyConnected = void 0; this.containerSize = void 0; this.sizeMonitor = new SizeMonitor(); this.cursorState = new StateTracker("default"); this.minWidth = 0; this.minHeight = 0; this.element = this.initDOM(); this.rootElements = this.initRootElements(); this.rootElements["canvas"].element.style.setProperty("anchor-name", this.anchorName); let hidden = false; this.observer = setupObserver(this.element, (intersectionRatio) => { if (intersectionRatio === 0 && !hidden) { this.eventsHub.emit("dom:hidden", null); } hidden = intersectionRatio === 0; }); this.setSizeOptions(); this.updateContainerSize(); this.addStyles("ag-charts-community", styles_default); this.setContainer(initialContainer); this.cleanup.register(stopPageScrolling(this.element)); if (this.mode === "normal") { const guardedElement = this.rootElements["canvas-center"].element; if (guardedElement == null) throw new Error("Error initializing tab guards"); const topGuard = createTabGuardElement(guardedElement, "beforebegin"); const botGuard = createTabGuardElement(guardedElement, "afterend"); this.tabGuards = new GuardedElement(guardedElement, topGuard, botGuard); } } initDOM() { if (this.mode === "normal") { const templateEl = createElement("div"); templateEl.innerHTML = domLayout_default; return templateEl.firstChild; } const element2 = createElement("div"); element2.role = "presentation"; element2.dataset.agCharts = ""; element2.classList.add("ag-charts-wrapper"); const seriesArea = createElement("div"); element2.appendChild(seriesArea); seriesArea.role = "presentation"; seriesArea.classList.add("ag-charts-series-area"); return element2; } initRootElements() { const { mode, element: element2 } = this; const rootElements = {}; for (const domElement of DOM_ELEMENT_CLASSES) { const className = `ag-charts-${domElement}`; let el; if (mode === "normal") { el = element2.classList.contains(className) ? element2 : element2.getElementsByClassName(className)[0]; } else if (MINIMAL_DOM_ELEMENT_ROLES.has(domElement)) { el = element2; } else { el = element2.getElementsByClassName(className)[0] ?? createElement("div"); } if (el == null) { throw new Error(`AG Charts - unable to find DOM element ${className}`); } rootElements[domElement] = { element: el, children: /* @__PURE__ */ new Map(), listeners: [] }; } return rootElements; } destroy() { super.destroy(); this.observer?.unobserve(this.element); if (this.container) { this.sizeMonitor.unobserve(this.container); } this.pendingContainer = void 0; for (const el of Object.values(this.rootElements)) { for (const c of el.children.values()) { c.remove(); } el.element.remove(); } this.element.remove(); } postRenderUpdate() { this.updateStylesLocation(); if (this.mode === "minimal") return; if (this.pendingContainer == null || this.pendingContainer === this.container) return; if (_DOMManager.batchedUpdateContainer.length === 0) { getWindow().setTimeout(this.applyBatchedUpdateContainer.bind(this), 0); } _DOMManager.batchedUpdateContainer.push(this); } applyBatchedUpdateContainer() { for (const manager of _DOMManager.batchedUpdateContainer) { if (!manager.destroyed) { manager.updateContainer(); } } _DOMManager.batchedUpdateContainer.splice(0); } updateStylesLocation() { if (this.initiallyConnected === true || this.container?.isConnected === false) return; this.documentRoot = this.getShadowDocumentRoot(this.container); this.initiallyConnected = true; for (const id of this.rootElements["styles"].children.keys()) { this.removeChild("styles", id); } for (const [id, styles] of this.styles) { this.addStyles(id, styles); } } setSizeOptions(minWidth = 300, minHeight = 300, optionsWidth, optionsHeight) { const { style: style2 } = this.element; style2.width = `${optionsWidth ?? minWidth}px`; style2.height = `${optionsHeight ?? minHeight}px`; this.minWidth = optionsWidth ?? minWidth; this.minHeight = optionsHeight ?? minHeight; this.updateContainerClassName(); } updateContainerSize() { const { style: centerStyle } = this.rootElements["canvas-center"].element; centerStyle.visibility = this.containerSize == null ? "hidden" : ""; if (this.containerSize) { centerStyle.width = `${this.containerSize.width ?? 0}px`; centerStyle.height = `${this.containerSize.height ?? 0}px`; } else { centerStyle.width = ""; centerStyle.height = ""; } this.updateContainerClassName(); } setTabGuardIndex(tabIndex) { if (!this.tabGuards) return; this.tabGuards.tabIndex = tabIndex; } setContainer(newContainer) { if (newContainer === this.container) return; this.pendingContainer = newContainer; if (this.mode === "minimal" || this.container == null) { this.updateContainer(); } } updateContainer() { const { pendingContainer } = this; if (pendingContainer == null || pendingContainer === this.container) return; if (this.container) { this.element.remove(); this.sizeMonitor.unobserve(this.container); } if (this.documentRoot != null) { for (const id of this.rootElements["styles"].children.keys()) { this.removeChild("styles", id); } } this.container = pendingContainer; this.pendingContainer = void 0; this.documentRoot = this.getShadowDocumentRoot(pendingContainer); this.initiallyConnected = pendingContainer.isConnected; for (const [id, styles] of this.styles) { this.addStyles(id, styles); } pendingContainer.appendChild(this.element); this.sizeMonitor.observe(pendingContainer, (size) => { this.containerSize = size; this.updateContainerSize(); this.eventsHub.emit("dom:resize", null); }); this.eventsHub.emit("dom:container-change", null); } setThemeClass(themeClassName) { const themeClassNamePrefix = "ag-charts-theme-"; for (const className of Array.from(this.element.classList)) { if (className.startsWith(themeClassNamePrefix) && className !== themeClassName) { this.element.classList.remove(className); } } this.element.classList.add(themeClassName); } setThemeParameters(params) { for (const [key, value] of entries(params)) { let formattedValue = `${value}`; if (key.endsWith("Size") || key.endsWith("Radius")) { formattedValue = `${value}px`; } else if (key.endsWith("Border") && typeof value === "boolean") { formattedValue = value ? "var(--ag-charts-border)" : "none"; } this.element.style.setProperty(`--ag-charts-${kebabCase(key)}`, formattedValue); } } updateCanvasLabel(ariaLabel) { setAttribute(this.rootElements["canvas-proxy"].element, "aria-label", ariaLabel); } getEventElement(defaultElem, eventType) { const events = ["focus", "blur", "keydown", "keyup"]; return events.includes(eventType) ? this.rootElements["series-area"].element : defaultElem; } addEventListener(type, listener, options) { const element2 = this.getEventElement(this.element, type); return attachListener(element2, type, listener, options); } removeEventListener(type, listener, options) { this.getEventElement(this.element, type).removeEventListener(type, listener, options); } /** Get the main chart area client bound rect. */ getBoundingClientRect() { return this.rootElements["canvas"].element.getBoundingClientRect(); } /** * Get the client bounding rect for overlay elements that might float outside the bounds of the * main chart area. */ getOverlayClientRect() { const window = getWindow(); const windowBBox = new BBox(0, 0, window.innerWidth, window.innerHeight); const containerBBox = this.getRawOverlayClientRect(); return windowBBox.intersection(containerBBox)?.toDOMRect() ?? NULL_DOMRECT; } getRawOverlayClientRect() { let element2 = this.element; const fullScreenElement = this.element.getRootNode()?.fullscreenElement; while (element2 != null) { let isContainer; if (fullScreenElement != null && element2 === fullScreenElement) { isContainer = true; } else { const styleMap = element2.computedStyleMap?.(); const overflowY = styleMap?.get("overflow-y")?.toString(); isContainer = overflowY === "auto" || overflowY === "scroll"; } if (isContainer) { return BBox.fromObject(element2.getBoundingClientRect()); } element2 = element2.parentElement; } if (this.documentRoot != null) return BBox.fromObject(this.documentRoot.getBoundingClientRect()); const { innerWidth, innerHeight } = getWindow(); return new BBox(0, 0, innerWidth, innerHeight); } getShadowDocumentRoot(current = this.container) { const docRoot = current?.ownerDocument?.body ?? getDocument("body"); while (current != null) { if (current === docRoot) { return void 0; } if (isDocumentFragment(current.parentNode)) { return current; } current = current.parentNode; } } getParent(domElementClass) { return this.rootElements[domElementClass].element; } getChildBoundingClientRect(type) { const { children } = this.rootElements[type]; const childRects = []; for (const child of children.values()) { childRects.push(BBox.fromObject(child.getBoundingClientRect())); } return BBox.merge(childRects); } isManagedChildDOMElement(el, domElementClass, id) { const { children } = this.rootElements[domElementClass]; const search = children?.get(id); return search != null && el.contains(search); } contains(element2, domElementClass) { if (domElementClass == null) return this.element.contains(element2); return this.rootElements[domElementClass].element.contains(element2); } addStyles(id, styles) { const dataAttribute = "data-ag-charts"; this.styles.set(id, styles); if (this.container == null) return; if (this.skipCss) return; const checkId = (el) => { return el.getAttribute(dataAttribute) === id; }; const addStyleElement = (el) => { const metaElements = /* @__PURE__ */ new Set(["TITLE", "META"]); let skippingMetaElements = true; let insertAfterEl; for (const child of el.children) { if (skippingMetaElements && metaElements.has(child.tagName)) { insertAfterEl = child; continue; } skippingMetaElements = false; if (checkId(child)) return; if (child.hasAttribute(dataAttribute)) { insertAfterEl = child; } } const styleEl = createElement("style"); if (this.chart.styleNonce != null) { styleEl.nonce = this.chart.styleNonce; } if (insertAfterEl == null) { el.prepend(styleEl); } else { el.insertBefore(styleEl, insertAfterEl.nextSibling); } return styleEl; }; let styleElement; if (this.styleContainer) { styleElement = addStyleElement(this.styleContainer); } else if (this.initiallyConnected === false) { styleElement = this.addChild("styles", id); } else if (this.documentRoot == null && !_DOMManager.headStyles.has(id)) { styleElement = addStyleElement(getDocument("head")); _DOMManager.headStyles.add(id); } else if (this.documentRoot != null) { styleElement = this.addChild("styles", id); } if (styleElement == null || checkId(styleElement)) return; styleElement.setAttribute(dataAttribute, id); styleElement.innerHTML = styles; } removeStyles(id) { this.removeChild("styles", id); } updateCursor(callerId, style2) { this.cursorState.set(callerId, style2); this.element.style.cursor = this.cursorState.stateValue(); } getCursor() { return this.element.style.cursor; } addChild(domElementClass, id, child, insert) { const { element: element2, children, listeners } = this.rootElements[domElementClass]; if (!children) { throw new Error("AG Charts - unable to create DOM elements after destroy()"); } if (children.has(id)) { return children.get(id); } const { childElementType = "div" } = domElementConfig.get(domElementClass) ?? {}; if (child && child.tagName.toLowerCase() !== childElementType.toLowerCase()) { throw new Error("AG Charts - mismatching DOM element type"); } const newChild = child ?? createElement(childElementType); for (const [type, fn, opts] of listeners) { newChild.addEventListener(type, fn, opts); } children.set(id, newChild); if (childElementType === "style" && this.chart.styleNonce != null) { newChild.nonce = this.chart.styleNonce; } if (insert) { const queryResult = element2.querySelector(insert.query); if (queryResult == null) { throw new Error(`AG Charts - addChild query failed ${insert.query}`); } queryResult.insertAdjacentElement(insert.where, newChild); } else { element2?.appendChild(newChild); } return newChild; } removeChild(domElementClass, id) { const { children } = this.rootElements[domElementClass]; if (!children) return; children.get(id)?.remove(); children.delete(id); } incrementDataCounter(name) { const { dataset } = this.element; dataset[name] ?? (dataset[name] = "0"); dataset[name] = String(Number(dataset[name]) + 1); } setDataBoolean(name, value) { this.element.dataset[name] = String(value); } setDataNumber(name, value) { this.element.dataset[name] = String(value); } updateContainerClassName() { const { element: element2, containerSize, minWidth, minHeight } = this; element2.classList.toggle(CONTAINER_MODIFIERS.safeHorizontal, minWidth >= (containerSize?.width ?? Infinity)); element2.classList.toggle(CONTAINER_MODIFIERS.safeVertical, minHeight >= (containerSize?.height ?? Infinity)); } }; _DOMManager.className = "DOMManager"; _DOMManager.batchedUpdateContainer = []; _DOMManager.headStyles = /* @__PURE__ */ new Set(); var DOMManager = _DOMManager; // packages/ag-charts-community/src/widget/boundedTextWidget.ts var BoundedTextWidget = class extends Widget { constructor() { super(createElement("div")); this.textElement = createSvgElement("text"); this.textElement.role = "presentation"; this.svgElement = createSvgElement("svg"); this.svgElement.appendChild(this.textElement); this.svgElement.style.width = "100%"; this.svgElement.style.opacity = "0"; this.svgElement.role = "presentation"; this.elem.appendChild(this.svgElement); this.elem.role = "presentation"; } set textContent(text) { this.textElement.textContent = text; const bboxCalculator = this.textElement; const bbox = bboxCalculator.getBBox?.(); if (bbox) { this.svgElement.setAttribute("viewBox", `${bbox.x} ${bbox.y} ${bbox.width} ${bbox.height}`); } } get textContent() { return this.textElement.textContent; } destructor() { } }; // packages/ag-charts-community/src/widget/expansionControllerImpl.ts var ExpansionControllerImpl = class { constructor(controller, getDispatcher) { this.getDispatcher = getDispatcher; this.onExpanded = () => { this.controller.setAriaExpanded(true); const dispatcher = this.getDispatcher(); if (dispatcher && this.controls) { const event = { type: "expand-controlled-widget", controlled: this.controls }; dispatcher.dispatch("expand-controlled-widget", this.controller, event); } }; this.onCollapsed = (e) => { this.controller.setAriaExpanded(false); if (e.mode === "0" /* CLOSE */) { this.controller.focus(); } }; controller.setAriaExpanded(false); this.controller = controller; } destroy() { this.controls?.collapse({ mode: "2" /* DESTROY */ }); this.setControlled(void 0); } setControlled(controls) { if (this.controls) { this.controls.removeListener("expand-widget", this.onExpanded); this.controls.removeListener("collapse-widget", this.onCollapsed); } this.controls = controls; if (this.controls) { this.controller.setAriaControls(this.controls.id); this.controls.addListener("expand-widget", this.onExpanded); this.controls.addListener("collapse-widget", this.onCollapsed); } } getControlled() { return this.controls; } expandControlled(opts) { if (!this.controller.isDisabled()) { this.controls?.expand({ controller: this.controller, sourceEvent: void 0, overrideFocusVisible: opts?.overrideFocusVisible }); } } }; // packages/ag-charts-community/src/widget/abstractButtonWidget.ts var AbstractButtonWidget = class extends Widget { constructor(element2, role) { super(element2); setAttribute(this.elem, "role", role); this.setEnabled(true); this.addListener("keydown", ({ sourceEvent }) => { if (isButtonClickEvent(sourceEvent)) { sourceEvent.preventDefault(); this.htmlListener?.dispatch("click", this, { type: "click", device: "keyboard", sourceEvent }); } }); } lazyControllerImpl() { this.controllerImpl ?? (this.controllerImpl = new ExpansionControllerImpl(this, () => this.internalListener)); return this.controllerImpl; } destructor() { this.controllerImpl?.destroy(); } setEnabled(enabled) { setAttribute(this.elem, "aria-disabled", !enabled); } setControlled(controls) { return this.lazyControllerImpl().setControlled(controls); } getControlled() { return this.lazyControllerImpl().getControlled(); } expandControlled(opts) { return this.lazyControllerImpl().expandControlled(opts); } addListener(type, listener) { return super.addListener(type, (ev, current) => { if ((type === "click" || type === "dblclick") && this.isDisabled()) return; listener(ev, current); }); } }; // packages/ag-charts-community/src/widget/buttonWidget.ts var ButtonWidget = class extends AbstractButtonWidget { constructor() { super(createElement("button")); } }; // packages/ag-charts-community/src/widget/groupWidget.ts var GroupWidget = class extends Widget { constructor() { super(createElement("div")); setAttribute(this.elem, "role", "group"); } destructor() { } }; // packages/ag-charts-community/src/widget/rovingTabContainerWidget.ts var RovingTabContainerWidget = class extends Widget { constructor(initialOrientation, role) { super(createElement("div")); this.focusedChildIndex = 0; this.onChildFocus = (_event, child) => { const oldFocus = this.children[this.focusedChildIndex]; this.focusedChildIndex = child.index; oldFocus?.setTabIndex(-1); child.setTabIndex(0); }; this.onChildKeyDown = (event, child) => { const rovingOrientation = this.orientation; const [primaryKeys, secondaryKeys] = rovingOrientation === "both" ? [PREV_NEXT_KEYS["horizontal"], PREV_NEXT_KEYS["vertical"]] : [PREV_NEXT_KEYS[rovingOrientation], void 0]; let targetIndex = -1; if (hasNoModifiers(event.sourceEvent)) { const key = event.sourceEvent.key; if (key === primaryKeys.nextKey || key === secondaryKeys?.nextKey) { targetIndex = child.index + 1; } else if (key === primaryKeys.prevKey || key === secondaryKeys?.prevKey) { targetIndex = child.index - 1; } } this.children[targetIndex]?.focus(); }; setAttribute(this.elem, "role", role); this.orientation = initialOrientation; } get orientation() { return getAttribute(this.elem, "aria-orientation") ?? "both"; } set orientation(orientation) { setAttribute(this.elem, "aria-orientation", orientation === "both" ? void 0 : orientation); } focus() { this.children[this.focusedChildIndex]?.focus(); } clear() { this.focusedChildIndex = 0; for (const child of this.children) { this.removeChildListeners(child); child.parent = void 0; } this.elem.textContent = ""; this.children.length = 0; } addChildListeners(child) { child.addListener("focus", this.onChildFocus); child.addListener("keydown", this.onChildKeyDown); } removeChildListeners(child) { child.removeListener("focus", this.onChildFocus); child.removeListener("keydown", this.onChildKeyDown); } onChildAdded(child) { this.addChildListeners(child); child.setTabIndex(this.children.length === 1 ? 0 : -1); } onChildRemoved(removedChild) { this.removeChildListeners(removedChild); const { focusedChildIndex, children } = this; const removedFocusedChild = focusedChildIndex === removedChild.index; for (let i = 0; i < children.length; i++) { const child = children[i]; if (child.index === focusedChildIndex) { this.focusedChildIndex = i; } child.index = i; } if (removedFocusedChild) { const newFocusChild = children[focusedChildIndex] ?? children[focusedChildIndex - 1]; if (newFocusChild) { this.focusedChildIndex = newFocusChild.index; newFocusChild.setTabIndex(0); } else { this.focusedChildIndex = 0; } } } }; // packages/ag-charts-community/src/widget/listWidget.ts var ListWidget = class extends RovingTabContainerWidget { constructor() { super("both", "list"); this.setHidden(true); } destructor() { for (const c of this.children) { c.getElement().parentElement.remove(); } } addChildToDOM(child, before) { const listItem = createElement("div"); setAttribute(listItem, "role", "listitem"); setElementStyle(listItem, "position", "absolute"); Widget.setElementContainer(child, listItem); this.appendOrInsert(listItem, before); this.setHidden(false); } removeChildFromDOM(child) { child.getElement().parentElement.remove(); this.setHidden(this.children.length === 0); } setHidden(hidden) { if (this.children.length === 0) { hidden = true; } super.setHidden(hidden); } }; // packages/ag-charts-community/src/widget/nativeWidget.ts var NativeWidget = class extends Widget { constructor(elem) { super(elem); } destructor() { } }; // packages/ag-charts-community/src/widget/sliderWidget.ts var _SliderWidget = class _SliderWidget extends Widget { constructor() { super(createElement("input")); this._step = _SliderWidget.STEP_ONE; this.orientation = "both"; } get step() { return this._step; } set step(step) { this._step = step; this.getElement().step = step.attributeValue; } get keyboardStep() { return this._keyboardStep?.step ?? this._step; } set keyboardStep(step) { if (step === this._keyboardStep?.step) return; if (this._keyboardStep !== void 0) { this.removeListener("keydown", this._keyboardStep.onKeyDown); this.removeListener("keyup", this._keyboardStep.onKeyUp); this.removeListener("blur", this._keyboardStep.onBlur); this._keyboardStep = void 0; } if (step !== void 0) { const onKeyDown = () => this.getElement().step = step.attributeValue; const resetStep = () => this.getElement().step = this._step.attributeValue; this._keyboardStep = { step, onKeyDown, onKeyUp: resetStep, onBlur: resetStep }; this.addListener("keydown", this._keyboardStep.onKeyDown); this.addListener("keyup", this._keyboardStep.onKeyUp); this.addListener("blur", this._keyboardStep.onBlur); } } get orientation() { return getAttribute(this.elem, "aria-orientation") ?? "both"; } set orientation(orientation) { setAttribute(this.elem, "aria-orientation", orientation === "both" ? void 0 : orientation); _SliderWidget.registerDefaultPreventers(this, orientation); } destructor() { } clampValueRatio(clampMin, clampMax) { const ratio2 = this.getValueRatio(); const clampedRatio = clamp(clampMin, ratio2, clampMax); if (clampedRatio !== ratio2) { this.setValueRatio(clampedRatio); } return clampedRatio; } setValueRatio(ratio2, opts) { const { divider } = this.step; const value = Math.round(ratio2 * 1e4) / divider; const { ariaValueText = formatPercent(value / divider) } = opts ?? {}; const elem = this.getElement(); elem.value = `${value}`; elem.ariaValueText = ariaValueText; elem.ariaValueNow = `${value}`; } getValueRatio() { return this.getElement().valueAsNumber / this.step.divider; } static registerDefaultPreventers(target, orientation) { if (orientation === "both") { target.removeListener("keydown", _SliderWidget.onKeyDown); } else { target.addListener("keydown", _SliderWidget.onKeyDown); } } static onKeyDown(ev, current) { let ignoredKeys = []; const { orientation } = current; if (orientation === "horizontal") { ignoredKeys = ["ArrowUp", "ArrowDown"]; } else if (orientation === "vertical") { ignoredKeys = ["ArrowLeft", "ArrowRight"]; } if (ignoredKeys.includes(ev.sourceEvent.code)) { ev.sourceEvent.preventDefault(); } } }; _SliderWidget.STEP_ONE = { attributeValue: "1", divider: 1 }; _SliderWidget.STEP_HUNDRETH = { attributeValue: "0.01", divider: 100 }; var SliderWidget = _SliderWidget; // packages/ag-charts-community/src/widget/switchWidget.ts var SwitchWidget = class extends ButtonWidget { constructor() { super(); setAttribute(this.elem, "role", "switch"); this.setChecked(false); } setChecked(checked) { setAttribute(this.elem, "aria-checked", checked); } }; // packages/ag-charts-community/src/widget/toolbarWidget.ts var ToolbarWidget = class extends RovingTabContainerWidget { constructor(orientation = "horizontal") { super(orientation, "toolbar"); } destructor() { } }; // packages/ag-charts-community/src/dom/proxyInteractionService.ts function checkType(type, meta) { return meta.params?.type === type; } function allocateResult(type) { if ("button" === type) { return new ButtonWidget(); } else if ("slider" === type) { return new SliderWidget(); } else if ("toolbar" === type) { return new ToolbarWidget(); } else if ("group" === type) { return new GroupWidget(); } else if ("list" === type) { return new ListWidget(); } else if ("region" === type) { return new NativeWidget(createElement("div")); } else if ("text" === type) { return new BoundedTextWidget(); } else if ("listswitch" === type) { return new SwitchWidget(); } else { throw new Error("AG Charts - error allocating meta"); } } function allocateMeta(params) { const meta = { params, result: void 0 }; meta.result = allocateResult(meta.params.type); return meta; } var ProxyInteractionService = class { constructor(eventsHub, localeManager, domManager) { this.eventsHub = eventsHub; this.localeManager = localeManager; this.domManager = domManager; this.cleanup = new CleanupRegistry(); } destroy() { this.cleanup.flush(); } addLocalisation(fn) { fn(); this.cleanup.register(this.eventsHub.on("locale:change", fn)); } createProxyContainer(args) { const meta = allocateMeta(args); const { params, result } = meta; const div = result.getElement(); this.domManager.addChild("canvas-proxy", params.domManagerId, div); div.classList.add(...params.classList, "ag-charts-proxy-container"); div.role = params.role ?? params.type; if (checkType("toolbar", meta)) { meta.result.orientation = meta.params.orientation; } const { ariaLabel } = params; if (ariaLabel) { this.addLocalisation(() => { div.ariaLabel = this.localeManager.t(ariaLabel.id, ariaLabel.params); }); } return result; } createProxyElement(args) { const meta = allocateMeta(args); if (checkType("button", meta)) { const { params, result } = meta; const button = result.getElement(); this.initInteract(params, result); if (typeof params.textContent === "string") { button.textContent = params.textContent; } else { const { textContent } = params; this.addLocalisation(() => { button.textContent = this.localeManager.t(textContent.id, textContent.params); }); } this.setParent(meta.params, meta.result); } if (checkType("slider", meta)) { const { params, result } = meta; const slider = result.getElement(); this.initInteract(params, result); slider.type = "range"; slider.role = params.role ?? "presentation"; slider.style.margin = "0px"; this.addLocalisation(() => { slider.ariaLabel = this.localeManager.t(params.ariaLabel.id, params.ariaLabel.params); }); this.setParent(meta.params, meta.result); } if (checkType("text", meta)) { const { params, result } = meta; this.initElement(params, result); this.setParent(meta.params, meta.result); } if (checkType("listswitch", meta)) { const { params, result: button } = meta; this.initInteract(params, button); button.setTextContent(params.textContent); button.setChecked(params.ariaChecked); button.setAriaDescribedBy(params.ariaDescribedBy); this.setParent(meta.params, meta.result); } if (checkType("region", meta)) { const { params, result } = meta; const region = result.getElement(); this.initInteract(params, result); region.role = params.role ?? "region"; this.setParent(meta.params, meta.result); } return meta.result; } initElement(params, widget) { const element2 = widget.getElement(); setElementStyle(element2, "cursor", params.cursor); element2.classList.toggle("ag-charts-proxy-elem", true); if (params.classList?.length) { element2.classList.add(...params.classList); } return element2; } initInteract(params, widget) { const { tabIndex, domIndex } = params; const element2 = this.initElement(params, widget); if (tabIndex !== void 0) { element2.tabIndex = tabIndex; } if (domIndex !== void 0) { widget.domIndex = domIndex; } } setParent(params, element2) { if ("parent" in params) { params.parent?.addChild(element2); } else { const insert = { where: params.where, query: ".ag-charts-series-area" }; this.domManager.addChild("canvas-proxy", params.domManagerId, element2.getElement(), insert); element2.destroyListener = () => { this.domManager.removeChild("canvas-proxy", params.domManagerId); }; } } }; // packages/ag-charts-locale/src/en-US.ts var AG_CHARTS_LOCALE_EN_US = { // Initial screen reader alt-text of the series area ariaInitSeriesArea: "interactive chart", // Screen reader announcement when focusing an item in the chart ariaAnnounceHoverDatum: "${datum}", // Screen reader announcement when focusing a chart ariaAnnounceChart: "chart, ${seriesCount}[number] series", // Screen reader announcement when focusing a standalone chart (gauges, pyramid) ariaAnnounceStandaloneChart: "chart, ${caption}", // Screen reader announcement when focusing a hierarchy chart ariaAnnounceHierarchyChart: "hierarchy chart, ${caption}", // Screen reader announcement when focusing a gauge chart ariaAnnounceGaugeChart: "gauge chart, ${caption}", // Screen reader announcement when focusing an item in a treemap or sunburst chart ariaAnnounceHierarchyDatum: "level ${level}[number], ${count}[number] children, ${description}", // Screen reader announcement when focusing a link in a Sankey or chord chart ariaAnnounceFlowProportionLink: "link ${index} of ${count}, from ${from} to ${to}, ${sizeName} ${size}", // Screen reader announcement when focusing a node in a Sankey or chord chart ariaAnnounceFlowProportionNode: "node ${index} of ${count}, ${description}", // Screen reader description for legend items ariaDescriptionLegendItem: "Press Space or Enter to toggle visibility", // Screen reader for the '+' horizontal line button on the Y-axis ariaLabelAddHorizontalLine: "Add Horizontal Line", // Screen reader text for annotations toolbar ariaLabelAnnotationsToolbar: "Annotations", // Screen reader text for annotation-options toolbar ariaLabelAnnotationOptionsToolbar: "Annotation Options", // Screen reader text for annotation-settings dialog ariaLabelAnnotationSettingsDialog: "Annotation Settings", // Screen reader text for the color-code label in the picker dialog ariaLabelColor: "Color", // Screen reader text for the color picker dialog ariaLabelColorPicker: "Color picker", // Screen reader text for the alpha-channel slider label ariaLabelColorPickerAlpha: "Transparency", // Screen reader text for the hue slider label ariaLabelColorPickerHue: "Hue", // Screen reader text for when the color-code is multi-colored ariaLabelColorPickerMultiColor: "Multi Color", // Screen reader text for the 2D palette slider label ariaLabelColorPickerPalette: "Palette", // Screen reader text for the financial charts toolbar ariaLabelFinancialCharts: "Financial Charts", // Screen reader text label for the gauge targets ariaLabelGaugeTarget: "Target", // Screen reader text label for the gauge values ariaLabelGaugeValue: "Value", // Screen reader text for the legend toolbar ariaLabelLegend: "Legend", // Screen reader text for the legend pagination button ariaLabelLegendPagination: "Legend Pagination", // Screen reader text for the previous legend page button ariaLabelLegendPagePrevious: "Previous Legend Page", // Screen reader text for the next legend page button ariaLabelLegendPageNext: "Next Legend Page", // Screen reader text for the an item in the legend ariaLabelLegendItem: "${label}, Legend item ${index}[number] of ${count}[number]", // Screen reader text for the an unknown item in the legend ariaLabelLegendItemUnknown: "Unknown legend item", // Screen reader text for the navigator element ariaLabelNavigator: "Navigator", // Screen reader text for an accessibility control that changes the position of the navigator's range ariaLabelNavigatorRange: "Range", // Screen reader text for the horizontal axis scrollbar ariaLabelScrollbarHorizontal: "X-axis scrollbar", // Screen reader text for the vertical axis scrollbar ariaLabelScrollbarVertical: "Y-axis scrollbar", // Screen reader text for an accessibility control that changes the start of the navigator's range ariaLabelNavigatorMinimum: "Minimum", // Screen reader text for an accessibility control that changes the end of the navigator's range ariaLabelNavigatorMaximum: "Maximum", // Screen reader text for ranges toolbar ariaLabelRangesToolbar: "Ranges", // Screen reader text for the settings dialog tab-bar ariaLabelSettingsTabBar: "Settings", // Screen reader text for zoom toolbar ariaLabelZoomToolbar: "Zoom", // Aria role description for a 2D role="slider" ariaRoleDescription2DSlider: "2D slider", // Screen reader text for color picker's 2D slider palette ariaValueColorPalette: "s ${s}[percent0to2dp], v ${v}[percent0to2dp]", // Screen reader text for color picker's 2D slider palette (when arrowing up or down) ariaValueColorPaletteFirstV: "v ${v}[percent0to2dp], s ${s}[percent0to2dp]", // Screen reader text for the value of the navigator's range ariaValuePanRange: "${min}[percent0to2dp] to ${max}[percent0to2dp]", // Alt-text for the solid line dash style menu item icon iconAltTextLineStyleSolid: "Solid", // Alt-text for the long-dashed line dash style menu item icon iconAltTextLineStyleDashed: "Long-dashed", // Alt-text for the short-dashed line dash style menu item icon iconAltTextLineStyleDotted: "Short-dashed", // Alt-text for the 'position-top' icon iconAltTextPositionTop: "Top", // Alt-text for the 'position-center' icon iconAltTextPositionCenter: "Center", // Alt-text for the 'position-bottom' icon iconAltTextPositionBottom: "Bottom", // Alt-text for the 'position-left' icon iconAltTextAlignLeft: "Left", // Alt-text for the 'align-center' icon iconAltTextAlignCenter: "Center", // Alt-text for the 'position-right' icon iconAltTextAlignRight: "Right", // Alt-text for the 'close' icon iconAltTextClose: "Close", // Default text for the 'loading data' overlay overlayLoadingData: "Loading data...", // Default text for the 'no data' overlay overlayNoData: "No data to display", // Default text for the 'no visible series' overlay overlayNoVisibleSeries: "No visible series", // Default text for the 'unsupported browser' overlay overlayUnsupportedBrowser: "Incompatible browser version. Please upgrade your browser.", // Text for frequency label in Histogram Series tooltip seriesHistogramTooltipFrequency: "Frequency", // Text for sum label in Histogram Series tooltip seriesHistogramTooltipSum: "${yName} (sum)", // Text for sum label in Histogram Series tooltip seriesHistogramTooltipCount: "${yName} (count)", // Text for sum label in Histogram Series tooltip seriesHistogramTooltipMean: "${yName} (mean)", // Text for the series type toolbar's chart type button toolbarSeriesTypeDropdown: "Chart Type", // Text for the series type toolbar's OHLC chart type button toolbarSeriesTypeOHLC: "OHLC", // Text for the series type toolbar's HLC chart type button toolbarSeriesTypeHLC: "HLC", // Text for the series type toolbar's high low chart type button toolbarSeriesTypeHighLow: "High Low", // Text for the series type toolbar's candles chart type button toolbarSeriesTypeCandles: "Candles", // Text for the series type toolbar's hollow candles chart type button toolbarSeriesTypeHollowCandles: "Hollow Candles", // Text for the series type toolbar's line chart type button toolbarSeriesTypeLine: "Line", // Text for the series type toolbar's line with markers chart type button toolbarSeriesTypeLineWithMarkers: "Line with Markers", // Text for the series type toolbar's line with step line chart type button toolbarSeriesTypeStepLine: "Step Line", // Text for the annotation toolbar's trend line button toolbarAnnotationsTrendLine: "Trend Line", // Text for the annotation toolbar's Fibonacci Retracement button toolbarAnnotationsFibonacciRetracement: "Fib Retracement", // Text for the annotation toolbar's Fibonacci Retracement Trend Based button toolbarAnnotationsFibonacciRetracementTrendBased: "Fib Trend Based", // Text for the annotation toolbar's horizontal line button toolbarAnnotationsHorizontalLine: "Horizontal Line", // Text for the annotation toolbar's vertical line button toolbarAnnotationsVerticalLine: "Vertical Line", // Text for the annotation toolbar's parallel channel button toolbarAnnotationsParallelChannel: "Parallel Channel", // Text for the annotation toolbar's disjoint channel button toolbarAnnotationsDisjointChannel: "Disjoint Channel", // Text for the annotation toolbar's clear all button toolbarAnnotationsClearAll: "Clear All", // Text for the annotation toolbar's fill color picker annotation button toolbarAnnotationsFillColor: "Fill Color", // Text for the annotation toolbar's line color picker annotation button toolbarAnnotationsLineColor: "Line Color", // Text for the annotation toolbar's line style type button toolbarAnnotationsLineStyle: "Line Style", // Text for the annotation toolbar's line stroke width button toolbarAnnotationsLineStrokeWidth: "Line Stroke Width", // Text for the annotation toolbar's settings annotation button toolbarAnnotationsSettings: "Settings", // Text for the annotation toolbar's text color picker annotation button toolbarAnnotationsTextColor: "Text Color", // Text for the annotation toolbar's text size picker annotation button toolbarAnnotationsTextSize: "Text Size", // Text for the annotation toolbar's lock annotation button toolbarAnnotationsLock: "Lock", // Text for the annotation toolbar's unlock annotation button toolbarAnnotationsUnlock: "Unlock", // Text for the annotation toolbar's delete annotation button toolbarAnnotationsDelete: "Delete", // Text for the annotation toolbar's drag handle toolbarAnnotationsDragHandle: "Drag Toolbar", // Text for the annotation toolbar's line drawings menu button toolbarAnnotationsLineAnnotations: "Trend Lines", // Text for the annotation toolbar's Fibonacci drawings menu button toolbarAnnotationsFibonacciAnnotations: "Fibonacci", // Text for the annotation toolbar's text annotations menu button toolbarAnnotationsTextAnnotations: "Text Annotations", // Text for the annotation toolbar's shapes menu button toolbarAnnotationsShapeAnnotations: "Arrows", // Text for the annotation toolbar's measurers menu button toolbarAnnotationsMeasurerAnnotations: "Measurers", // Text for the annotation toolbar's callout button toolbarAnnotationsCallout: "Callout", // Text for the annotation toolbar's comment button toolbarAnnotationsComment: "Comment", // Text for the annotation toolbar's note button toolbarAnnotationsNote: "Note", // Text for the annotation toolbar's text button toolbarAnnotationsText: "Text", // Text for the annotation toolbar's arrow button toolbarAnnotationsArrow: "Arrow", // Text for the annotation toolbar's arrow up button toolbarAnnotationsArrowUp: "Arrow Up", // Text for the annotation toolbar's arrow down button toolbarAnnotationsArrowDown: "Arrow Down", // Text for the annotation toolbar's date range button toolbarAnnotationsDateRange: "Date Range", // Text for the annotation toolbar's price range button toolbarAnnotationsPriceRange: "Price Range", // Text for the annotation toolbar's date and price range button toolbarAnnotationsDatePriceRange: "Date and Price", // Text for the annotation toolbar's quick date and price range button toolbarAnnotationsQuickDatePriceRange: "Measure", // Text for the range toolbar's 1 month button toolbarRange1Month: "1M", // Aria label for the range toolbar's 1 month button toolbarRange1MonthAria: "1 month", // Text for the range toolbar's 3 month button toolbarRange3Months: "3M", // Aria label for the range toolbar's 3 month button toolbarRange3MonthsAria: "3 months", // Text for the range toolbar's 6 month button toolbarRange6Months: "6M", // Aria label for the range toolbar's 6 month button toolbarRange6MonthsAria: "6 months", // Text for the range toolbar's year to date button toolbarRangeYearToDate: "YTD", // Aria label for the range toolbar's year to date month button toolbarRangeYearToDateAria: "Year to date", // Text for the range toolbar's 1 year button toolbarRange1Year: "1Y", // Aria label for the range toolbar's 1 year button toolbarRange1YearAria: "1 year", // Text for the range toolbar's full range button toolbarRangeAll: "All", // Aria label for the range toolbar's full range button toolbarRangeAllAria: "All", // Text for the zoom toolbar's zoom out button toolbarZoomZoomOut: "Zoom out", // Text for the zoom toolbar's zoom in button toolbarZoomZoomIn: "Zoom in", // Text for the zoom toolbar's pan left button toolbarZoomPanLeft: "Pan left", // Text for the zoom toolbar's pan right button toolbarZoomPanRight: "Pan right", // Text for the zoom toolbar's pan to the start button toolbarZoomPanStart: "Pan to the start", // Text for the zoom toolbar's pan to the end button toolbarZoomPanEnd: "Pan to the end", // Text for the zoom toolbar's pan reset button toolbarZoomReset: "Reset the zoom", // Text for the context menu's download button contextMenuDownload: "Download", // Text for the context menu's toggle series visibility button contextMenuToggleSeriesVisibility: "Toggle Visibility", // Text for the context menu's toggle other series visibility button contextMenuToggleOtherSeries: "Toggle Other Series", // Text for the context menu's zoom to point button contextMenuZoomToCursor: "Zoom to here", // Text for the context menu's pan to point button contextMenuPanToCursor: "Pan to here", // Text for the context menu's reset zoom button contextMenuResetZoom: "Reset zoom", // Text for the annotation dialog's header channel tab label dialogHeaderChannel: "Channel", // Text for the annotation dialog's header line tab label dialogHeaderLine: "Line", // Text for the annotation dialog's header fibonacci retracement line tab label dialogHeaderFibonacciRange: "Fib Retracement", // Text for the annotation dialog's header date range tab label dialogHeaderDateRange: "Date Range", // Text for the annotation dialog's header price range tab label dialogHeaderPriceRange: "Price Range", // Text for the annotation dialog's header date and price range tab label dialogHeaderDatePriceRange: "Date and Price", // Text for the annotation dialog's header text tab label dialogHeaderText: "Text", // Text for the annotation dialog's text alignment radio label dialogInputAlign: "Align", // Text for the annotation dialog's color picker label dialogInputColorPicker: "Color", // Text for the annotation dialog's color picker alt text dialogInputColorPickerAltText: "Text Color", // Text for the annotation dialog's fill color picker label dialogInputFillColorPicker: "Fill", // Text for the annotation dialog's fill color picker alt text dialogInputFillColorPickerAltText: "Fill Color", // Text for the annotation dialog's extend channel start checkbox dialogInputExtendChannelStart: "Extend channel start", // Text for the annotation dialog's extend channel end checkbox dialogInputExtendChannelEnd: "Extend channel end", // Text for the annotation dialog's extend line start checkbox dialogInputExtendLineStart: "Extend line start", // Text for the annotation dialog's extend line end checkbox dialogInputExtendLineEnd: "Extend line end", // Text for the annotation dialog's extend above checkbox dialogInputExtendAbove: "Extend above", // Text for the annotation dialog's extend below checkbox dialogInputExtendBelow: "Extend below", // Text for the annotation dialog's extend left checkbox dialogInputExtendLeft: "Extend left", // Text for the annotation dialog's extend right checkbox dialogInputExtendRight: "Extend right", // Text for the annotation dialog's reverse checkbox dialogInputReverse: "Reverse", // Text for the annotation dialog's show fill checkbox dialogInputShowFill: "Show Fill", // Text for the annotation dialog's font size select box label dialogInputFontSize: "Size", // Text for the annotation dialog's font size select box alt text dialogInputFontSizeAltText: "Font Size", // Text for the annotation dialog's line style radio label dialogInputLineStyle: "Dash", // Text for the annotation dialog's text position radio label dialogInputPosition: "Position", // Text for the annotation dialog's stroke width label dialogInputStrokeWidth: "Weight", // Text for the annotation dialog's stroke width label dialogInputStrokeWidthAltText: "Line Weight", // Text for the annotation dialog's Fibonacci bands label dialogInputFibonacciBands: "Bands", // Text for the annotation dialog's Fibonacci bands label dialogInputFibonacciBandsAltText: "Fibonacci Bands", // Text for text area input placeholders inputTextareaPlaceholder: "Add Text", // Text for the measurer statistics date range bars value measurerDateRangeBars: "${value}[number] bars", // Text for the measurer statistics price range value measurerPriceRangeValue: "${value}[number]", // Text for the measurer statistics price range percentage measurerPriceRangePercent: "${value}[percent]", // Text for the measurer statistics volume value measurerVolume: "Vol ${value}", // Status when multiple data are under the cursor, and the user can click to cycle through which one appears in the tooltip tooltipPaginationStatus: "${index}[number] of ${count}[number]" }; // packages/ag-charts-community/src/locale/defaultMessageFormatter.ts var messageRegExp = /\$\{(\w+)}(?:\[(\w+)])?/gi; var formatters = { number: new Intl.NumberFormat("en-US"), percent: new Intl.NumberFormat("en-US", { style: "percent", minimumFractionDigits: 2, maximumFractionDigits: 2 }), percent0to2dp: new Intl.NumberFormat("en-US", { style: "percent", minimumFractionDigits: 0, maximumFractionDigits: 2 }), date: new Intl.DateTimeFormat("en-US", { dateStyle: "full" }), time: new Intl.DateTimeFormat("en-US", { timeStyle: "full" }), datetime: new Intl.DateTimeFormat("en-US", { dateStyle: "full", timeStyle: "full" }) }; var defaultMessageFormatter = ({ defaultValue, variables }) => { return defaultValue?.replaceAll(messageRegExp, (_, match, format) => { const value = variables[match]; const formatter2 = format == null ? null : formatters[format]; if (format != null && formatter2 == null) { logger_exports.warnOnce(`Format style [${format}] is not supported`); } if (formatter2 != null) { return formatter2.format(value); } else if (typeof value === "number") { return formatters.number.format(value); } else if (value instanceof Date) { return formatters.datetime.format(value); } return String(value); }); }; // packages/ag-charts-community/src/locale/localeManager.ts var LocaleManager = class { constructor(eventsHub) { this.eventsHub = eventsHub; this.localeText = void 0; this.getLocaleText = void 0; } setLocaleText(localeText) { if (this.localeText !== localeText) { this.localeText = localeText; this.eventsHub.emit("locale:change", null); } } setLocaleTextFormatter(getLocaleText) { this.getLocaleText = getLocaleText; if (this.getLocaleText !== getLocaleText) { this.getLocaleText = getLocaleText; this.eventsHub.emit("locale:change", null); } } t(key, variables = {}) { const { localeText = AG_CHARTS_LOCALE_EN_US, getLocaleText } = this; const defaultValue = localeText[key]; return String( getLocaleText?.({ key, defaultValue, variables }) ?? defaultMessageFormatter({ key, defaultValue, variables }) ?? key ); } }; // packages/ag-charts-community/src/scene/canvas/hdpiCanvas.ts var HdpiCanvas = class { constructor(options) { this.enabled = true; this.width = 600; this.height = 300; const { width: width2, height: height2, canvasElement, willReadFrequently = false } = options; this.pixelRatio = options.pixelRatio ?? getWindow("devicePixelRatio") ?? 1; this.element = canvasElement ?? createElement("canvas"); this.element.style.display = "block"; this.element.style.width = (width2 ?? this.width) + "px"; this.element.style.height = (height2 ?? this.height) + "px"; this.element.width = Math.round((width2 ?? this.width) * this.pixelRatio); this.element.height = Math.round((height2 ?? this.height) * this.pixelRatio); this.context = this.element.getContext("2d", { willReadFrequently }); this.onEnabledChange(); this.resize(width2 ?? 0, height2 ?? 0, this.pixelRatio); debugContext(this.context); } // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents -- OffscreenCanvasRenderingContext2D is intentionally `any` for Angular 13+ compatibility (AG-6969) drawImage(context, dx = 0, dy = 0) { return context.drawImage(this.context.canvas, dx, dy); } toDataURL(type) { return this.element.toDataURL(type); } resize(width2, height2, pixelRatio) { if (!(width2 > 0 && height2 > 0)) return; const { element: element2, context } = this; element2.width = Math.round(width2 * pixelRatio); element2.height = Math.round(height2 * pixelRatio); context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0); element2.style.width = width2 + "px"; element2.style.height = height2 + "px"; this.width = width2; this.height = height2; this.pixelRatio = pixelRatio; } clear() { clearContext(this); } destroy() { this.element.remove(); this.element.width = 0; this.element.height = 0; this.context.clearRect(0, 0, 0, 0); Object.freeze(this); } reset() { this.context.reset(); this.context.verifyDepthZero?.(); } onEnabledChange() { if (this.element) { this.element.style.display = this.enabled ? "" : "none"; } } }; __decorateClass([ ObserveChanges((target) => target.onEnabledChange()) ], HdpiCanvas.prototype, "enabled", 2); // packages/ag-charts-community/src/scene/image/imageLoader.ts var ImageLoader = class extends EventEmitter { constructor() { super(...arguments); this.cache = /* @__PURE__ */ new Map(); this.imageLoadingCount = 0; } loadImage(uri, affectedNode) { const entry = this.cache.get(uri); if (entry?.image) { return entry.image; } else if (entry != null && affectedNode) { entry.nodes.add(affectedNode); return; } if (!affectedNode) { return; } const nextEntry = { image: void 0, nodes: /* @__PURE__ */ new Set([affectedNode]) }; const ImageCtor = getImage(); const image = new ImageCtor(); this.imageLoadingCount++; image.onload = () => { nextEntry.image = image; for (const node of nextEntry.nodes) { node.markDirty(); } nextEntry.nodes.clear(); this.imageLoadingCount--; this.emit("image-loaded", { uri }); }; image.onerror = () => { this.imageLoadingCount--; nextEntry.nodes.clear(); this.emit("image-error", { uri }); }; image.src = uri; this.cache.set(uri, nextEntry); return nextEntry.image; } waitingToLoad() { return this.imageLoadingCount > 0; } destroy() { for (const entry of this.cache.values()) { entry.nodes.clear(); } this.cache.clear(); } }; // packages/ag-charts-community/src/scene/layersManager.ts var LayersManager = class { constructor(canvas) { this.canvas = canvas; this.debug = debugLogger_exports.create(true, "scene"); this.layersMap = /* @__PURE__ */ new Map(); this.nextLayerId = 0; } get size() { return this.layersMap.size; } resize(width2, height2, pixelRatio) { this.canvas.resize(width2, height2, pixelRatio); for (const { canvas } of this.layersMap.values()) { canvas.resize(width2, height2, pixelRatio); } } addLayer(opts) { const { width: width2, height: height2, pixelRatio } = this.canvas; const { name } = opts; const canvas = new HdpiOffscreenCanvas({ width: width2, height: height2, pixelRatio }); this.layersMap.set(canvas, { id: this.nextLayerId++, name, canvas }); this.debug("Scene.addLayer() - layers", this.layersMap); return canvas; } removeLayer(canvas) { if (this.layersMap.has(canvas)) { this.layersMap.delete(canvas); canvas.destroy(); this.debug("Scene.removeLayer() - layers", this.layersMap); } } clear() { for (const layer of this.layersMap.values()) { layer.canvas.destroy(); } this.layersMap.clear(); } }; // packages/ag-charts-community/src/scene/scene.ts var Scene = class extends EventEmitter { constructor(canvasOptions) { super(); this.debug = debugLogger_exports.create(true, "scene" /* SCENE */); this.id = createId(this); this.imageLoader = new ImageLoader(); this.root = null; this.pendingSize = null; this.isDirty = false; this.cleanup = new CleanupRegistry(); this.updateDebugFlags(); this.canvas = new HdpiCanvas(canvasOptions); this.layersManager = new LayersManager(this.canvas); this.cleanup.register( this.imageLoader.on("image-loaded", () => { this.emit("scene-changed", {}); }), this.imageLoader.on("image-error", ({ uri }) => { logger_exports.warnOnce(`Unable to load image ${uri}`); }) ); } waitingForUpdate() { return this.imageLoader?.waitingToLoad() ?? false; } get width() { return this.pendingSize?.[0] ?? this.canvas.width; } get height() { return this.pendingSize?.[1] ?? this.canvas.height; } get pixelRatio() { return this.pendingSize?.[2] ?? this.canvas.pixelRatio; } /** * @deprecated v10.2.0 Only used by AG Grid Sparklines + Mini Charts * * DO NOT REMOVE WITHOUT FIXING THE GRID DEPENDENCIES. */ setContainer(value) { const { element: element2 } = this.canvas; element2.remove(); value.appendChild(element2); return this; } setRoot(node) { if (this.root === node) { return this; } this.isDirty = true; this.root?.setScene(); this.root = node; if (node) { node.visible = true; node.setScene(this); } return this; } updateDebugFlags() { debugLogger_exports.inDevelopmentMode(() => Node._debugEnabled = true); } clearCanvas() { this.canvas.clear(); } attachNode(node) { this.appendChild(node); return () => node.remove(); } appendChild(node) { this.root?.appendChild(node); return this; } removeChild(node) { node.remove(); return this; } download(fileName, fileFormat) { downloadUrl(this.canvas.toDataURL(fileFormat), fileName?.trim() ?? "image"); } /** NOTE: Integrated Charts undocumented image download method. */ getDataURL(fileFormat) { return this.canvas.toDataURL(fileFormat); } resize(width2, height2, pixelRatio) { width2 = Math.round(width2); height2 = Math.round(height2); pixelRatio ?? (pixelRatio = this.pixelRatio); if (width2 > 0 && height2 > 0 && (width2 !== this.width || height2 !== this.height || pixelRatio !== this.pixelRatio)) { this.pendingSize = [width2, height2, pixelRatio]; this.isDirty = true; return true; } return false; } applyPendingResize() { if (this.pendingSize) { this.layersManager.resize(...this.pendingSize); this.pendingSize = null; return true; } return false; } render(opts) { const { debugSplitTimes = { start: performance.now() }, extraDebugStats, seriesRect, debugColors } = opts ?? {}; const { canvas, canvas: { context: ctx } = {}, root, width: width2, height: height2, pixelRatio: devicePixelRatio } = this; if (!ctx) { return; } const statsEnabled = debugLogger_exports.check("scene:stats" /* SCENE_STATS */, "scene:stats:verbose" /* SCENE_STATS_VERBOSE */); if (statsEnabled) { this.ensureDebugStatsRegistration(); } const renderStartTime = performance.now(); const resized = this.applyPendingResize(); if (root && !root.visible) { this.isDirty = false; return; } let rootDirty; if (root instanceof Group) { rootDirty = root.dirty; } if (root != null && rootDirty === false && !this.isDirty) { if (this.debug.check()) { this.debug("Scene.render() - no-op", { tree: buildTree(root, "console") }); } if (statsEnabled) { debugStats( this.layersManager, debugSplitTimes, ctx, void 0, extraDebugStats, seriesRect, debugColors ); } return; } const renderCtx = { ctx, width: width2, height: height2, devicePixelRatio, debugNodes: {} }; if (debugLogger_exports.check("scene:stats:verbose" /* SCENE_STATS_VERBOSE */)) { renderCtx.stats = { layersRendered: 0, layersSkipped: 0, nodesRendered: 0, nodesSkipped: 0, opsPerformed: 0, opsSkipped: 0 }; } prepareSceneNodeHighlight(renderCtx); let canvasCleared = false; if (rootDirty !== false || resized) { canvasCleared = true; canvas.clear(); } if (root && debugLogger_exports.check("scene:dirtyTree" /* SCENE_DIRTY_TREE */)) { const { dirtyTree, paths } = buildDirtyTree(root); debugLogger_exports.create("scene:dirtyTree" /* SCENE_DIRTY_TREE */)("Scene.render() - dirtyTree", { dirtyTree, paths }); } if (root && canvasCleared) { if (root.visible) { root.preRender(renderCtx); } if (this.debug.check()) { const tree = buildTree(root, "console"); this.debug("Scene.render() - before", { canvasCleared, tree }); } if (root.visible) { try { ctx.save(); root.render(renderCtx); ctx.restore(); } catch (e) { this.canvas.reset(); throw e; } } } debugSplitTimes["\u270D\uFE0F"] = performance.now() - renderStartTime; ctx.verifyDepthZero?.(); this.isDirty = false; if (statsEnabled) { debugStats( this.layersManager, debugSplitTimes, ctx, renderCtx.stats, extraDebugStats, seriesRect, debugColors ); } debugSceneNodeHighlight(ctx, renderCtx.debugNodes); if (root && this.debug.check()) { this.debug("Scene.render() - after", { tree: buildTree(root, "console"), canvasCleared }); } } ensureDebugStatsRegistration() { if (this.releaseDebugStats) return; const release = registerDebugStatsConsumer(); const cleanup = () => { release(); this.releaseDebugStats = void 0; }; this.releaseDebugStats = cleanup; this.cleanup.register(cleanup); } toSVG() { const { root, width: width2, height: height2 } = this; if (root == null) return; return Node.toSVG(root, width2, height2); } /** Alternative to destroy() that preserves re-usable resources. */ strip() { const { context, pixelRatio } = this.canvas; context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0); this.layersManager.clear(); this.setRoot(null); this.isDirty = false; this.clear(); } destroy() { this.strip(); this.canvas.destroy(); this.imageLoader.destroy(); this.cleanup.flush(); cleanupDebugStats(); Object.assign(this, { canvas: void 0 }); } }; Scene.className = "Scene"; // packages/ag-charts-community/src/chart/annotation/annotationManager.ts var AnnotationManager = class { constructor(eventsHub, annotationRoot, fireChartEvent) { this.eventsHub = eventsHub; this.annotationRoot = annotationRoot; this.fireChartEvent = fireChartEvent; this.mementoOriginatorKey = "annotations"; this.annotations = []; } createMemento() { return this.annotations; } guardMemento(blob) { return blob == null || isArray(blob); } restoreMemento(_version, _mementoVersion, memento) { this.annotations = this.cleanData(memento ?? []).map((annotation) => { const annotationTheme = this.getAnnotationTypeStyles(annotation.type); return mergeDefaults(annotation, annotationTheme); }); this.eventsHub.emit("annotations:restore", { annotations: this.annotations }); } updateData(annotations) { this.annotations = this.cleanData(annotations ?? []); } fireChangedEvent() { this.fireChartEvent({ type: "annotations", annotations: deepClone([...this.annotations]) }); } attachNode(node) { this.annotationRoot.append(node); return () => { node.remove(); return this; }; } setAnnotationStyles(styles) { this.styles = styles; } getAnnotationTypeStyles(type) { return this.styles?.[type]; } cleanData(annotations) { for (const annotation of annotations) { if ("textAlign" in annotation) delete annotation.textAlign; } return annotations; } }; // packages/ag-charts-community/src/chart/axis/axisManager.ts var AxisManager = class { constructor(eventsHub, sceneRoot) { this.eventsHub = eventsHub; this.sceneRoot = sceneRoot; this.axes = /* @__PURE__ */ new Map(); this.axisGridGroup = new Group({ name: "Axes-Grids", zIndex: 2 /* AXIS_GRID */ }); this.axisGroup = new Group({ name: "Axes", zIndex: 3 /* AXIS */ }); this.axisLabelGroup = new Group({ name: "Axes-Labels", zIndex: 15 /* SERIES_LABEL */ }); this.axisCrosslineRangeGroup = new Group({ name: "Axes-Crosslines-Range", zIndex: 6 /* SERIES_CROSSLINE_RANGE */ }); this.axisCrosslineLineGroup = new Group({ name: "Axes-Crosslines-Line", zIndex: 10 /* SERIES_CROSSLINE_LINE */ }); this.axisCrosslineLabelGroup = new Group({ name: "Axes-Crosslines-Label", zIndex: 15 /* SERIES_LABEL */ }); this.sceneRoot.appendChild(this.axisGroup); this.sceneRoot.appendChild(this.axisGridGroup); this.sceneRoot.appendChild(this.axisLabelGroup); this.sceneRoot.appendChild(this.axisCrosslineRangeGroup); this.sceneRoot.appendChild(this.axisCrosslineLineGroup); this.sceneRoot.appendChild(this.axisCrosslineLabelGroup); } updateAxes(oldAxes, newAxes) { const axisNodes = { axisNode: this.axisGroup, gridNode: this.axisGridGroup, labelNode: this.axisLabelGroup, crossLineRangeNode: this.axisCrosslineRangeGroup, crossLineLineNode: this.axisCrosslineLineGroup, crossLineLabelNode: this.axisCrosslineLabelGroup }; for (const axis of oldAxes) { if (newAxes.includes(axis)) continue; axis.detachAxis(axisNodes); axis.destroy(); } for (const axis of newAxes) { if (oldAxes?.includes(axis)) continue; axis.attachAxis(axisNodes); } this.axes.clear(); for (const axis of newAxes) { const ctx = axis.createAxisContext(); if (this.axes.has(ctx.direction)) { this.axes.get(ctx.direction)?.push(ctx); } else { this.axes.set(ctx.direction, [ctx]); } } this.eventsHub.emit("axis:change", null); } getAxisIdContext(id) { for (const [, contextsInThisDir] of this.axes) { for (const ctx of contextsInThisDir) { if (ctx.axisId === id) return ctx; } } } getAxisContext(direction) { return this.axes.get(direction) ?? []; } destroy() { this.axes.clear(); this.axisGroup.remove(); this.axisGridGroup.remove(); } }; // packages/ag-charts-community/src/chart/data/dataService.ts var DataService = class { constructor(eventsHub, caller, animationManager) { this.eventsHub = eventsHub; this.caller = caller; this.animationManager = animationManager; this.dispatchOnlyLatest = true; this.dispatchThrottle = 0; this.requestThrottle = 300; this.isLoadingInitialData = false; this.isLoadingData = false; this.freshRequests = []; this.requestCounter = 0; this.pendingData = void 0; this.debug = debugLogger_exports.create(true, "data-model", "data-source"); this.throttledFetch = this.createThrottledFetch(this.requestThrottle); this.throttledDispatch = this.createThrottledDispatch(this.dispatchThrottle); } updateCallback(dataSourceCallback) { if (typeof dataSourceCallback !== "function") return; this.debug("DataService - updated data source callback"); this.dataSourceCallback = dataSourceCallback; this.isLoadingInitialData = true; this.animationManager.skip(); this.eventsHub.emit("data:source-change", null); } clearCallback() { this.dataSourceCallback = void 0; } load(params) { const { pendingData } = this; if (pendingData != null && (pendingData.params.windowStart == null && pendingData.params.windowEnd == null || pendingData.params.windowStart?.valueOf() === params.windowStart?.valueOf() && pendingData.params.windowEnd?.valueOf() === params.windowEnd?.valueOf())) { const id = this.requestCounter++; this.isLoadingInitialData = false; this.dispatch(id, pendingData.data); return; } this.isLoadingData = true; this.throttledFetch(params); } isLazy() { return this.dataSourceCallback != null; } isLoading() { return this.isLazy() && (this.isLoadingInitialData || this.isLoadingData); } async getData() { const { latestRequest } = this; if (!latestRequest) return; const { params, fetchRequest } = latestRequest; const data = await fetchRequest; return { params, data }; } restoreData(data) { this.pendingData = data; } createThrottledFetch(requestThrottle) { return throttle( (params) => this.fetch(params).catch((e) => logger_exports.error("callback failed", e)), requestThrottle, { leading: false, trailing: true } ); } createThrottledDispatch(dispatchThrottle) { return throttle((id, data) => this.dispatch(id, data), dispatchThrottle, { leading: true, trailing: true }); } dispatch(id, data) { this.debug(`DataService - dispatching 'data-load' | ${id}`); this.eventsHub.emit("data:load", { data }); } async fetch(params) { if ("context" in this.caller) { params.context = this.caller.context; } const fetchRequest = Promise.resolve().then(async () => { if (!this.dataSourceCallback) { throw new Error("DataService - [dataSource.getData] callback not initialised"); } const start2 = performance.now(); const id = this.requestCounter++; this.debug(`DataService - requesting | ${id}`); let response; try { response = await this.dataSourceCallback(params); this.debug(`DataService - response | ${performance.now() - start2}ms | ${id}`); } catch (error2) { this.debug(`DataService - request failed | ${id}`); logger_exports.errorOnce(`DataService - request failed | [${error2}]`); } this.isLoadingInitialData = false; const requestIndex = this.freshRequests.indexOf(fetchRequest); if (requestIndex === -1 || this.dispatchOnlyLatest && requestIndex !== this.freshRequests.length - 1) { this.debug(`DataService - discarding stale request | ${id}`); return response; } this.freshRequests = this.freshRequests.slice(requestIndex + 1); if (this.freshRequests.length === 0) { this.isLoadingData = false; } if (Array.isArray(response)) { this.throttledDispatch(id, response); } else { this.eventsHub.emit("data:error", null); } return response; }); this.latestRequest = { params, fetchRequest }; this.freshRequests.push(fetchRequest); await fetchRequest; } }; __decorateClass([ ActionOnSet({ newValue(dispatchThrottle) { this.throttledDispatch = this.createThrottledDispatch(dispatchThrottle); } }) ], DataService.prototype, "dispatchThrottle", 2); __decorateClass([ ActionOnSet({ newValue(requestThrottle) { this.throttledFetch = this.createThrottledFetch(requestThrottle); } }) ], DataService.prototype, "requestThrottle", 2); // packages/ag-charts-community/src/chart/fonts/fontManager.ts var FontManager = class { constructor(domManager, updateService) { this.domManager = domManager; this.updateService = updateService; this.observers = []; } updateFonts(fonts) { if (!fonts || fonts.size === 0) return; this.loadFonts(fonts); for (const font of fonts) { this.observeFontStatus(font); } } destroy() { for (const observer of this.observers) { observer.disconnect(); } this.observers = []; } loadFonts(fonts) { const fontStrings = Array.from(fonts).map((font) => encodeURIComponent(font)); const fontStyle = ":wght@100;200;300;400;500;600;700;800;900"; const joinString = `${fontStyle}&family=`; const css = `@import url('https://fonts.googleapis.com/css2?family=${fontStrings.join(joinString)}${fontStyle}&display=swap'); `; this.domManager.addStyles(`google-font-${fontStrings.join("-")}`, css); } observeFontStatus(font) { const ResizeObserverCtor = getResizeObserver(); if (ResizeObserverCtor === void 0) return; const doc = getDocument(); if (!doc) return; const fontCheckElement = doc.createElement("div"); fontCheckElement.style.setProperty("position", "absolute"); fontCheckElement.style.setProperty("top", "0"); fontCheckElement.style.setProperty("margin", "0"); fontCheckElement.style.setProperty("padding", "0"); fontCheckElement.style.setProperty("overflow", "hidden"); fontCheckElement.style.setProperty("visibility", "hidden"); fontCheckElement.style.setProperty("width", "auto"); fontCheckElement.style.setProperty("max-width", "none"); fontCheckElement.style.setProperty("font-synthesis", "none"); fontCheckElement.style.setProperty("font-family", font); fontCheckElement.style.setProperty("font-size", "16px"); fontCheckElement.style.setProperty("white-space", "nowrap"); fontCheckElement.textContent = "UVWxyz"; this.domManager.addChild("canvas-container", `font-check-${encodeURIComponent(font)}`, fontCheckElement); const fontCheckObserver = new ResizeObserverCtor((entries2) => { const width2 = entries2?.at(0)?.contentBoxSize.at(0)?.inlineSize; if (width2 != null && width2 > 0) { cachedTextMeasurer.clear(); this.updateService.update(5 /* PERFORM_LAYOUT */); } }); fontCheckObserver.observe(fontCheckElement); this.observers.push(fontCheckObserver); } }; // packages/ag-charts-community/src/chart/chartOptionsDefs.ts var initialStatePickedOptionsDef = { activeItem: { type: required(strictUnion()("series-node", "legend")), seriesId: string, itemId: required(or(string, positiveNumber)) } }; initialStatePickedOptionsDef.frozen = undocumented(boolean); var commonChartOptions = { mode: undocumented(union("integrated", "standalone")), container: htmlElement, context: () => true, theme: defined, series: array, annotations: defined, navigator: defined, scrollbar: defined, initialState: { active: initialStatePickedOptionsDef, chartType: string, annotations: defined, legend: arrayOfDefs( { visible: boolean, seriesId: string, itemId: string, legendItemName: string }, "legend state array" ), zoom: defined } }; var cartesianChartOptionsDefs = { ...commonChartOptionsDefs, ...commonChartOptions, axes: object, data: array }; var polarChartOptionsDefs = { ...commonChartOptionsDefs, ...commonChartOptions, axes: object, data: array }; var topologyChartOptionsDefs = { ...commonChartOptionsDefs, ...commonChartOptions, data: array, topology: geoJson }; var standaloneChartOptionsDefs = { ...commonChartOptionsDefs, ...commonChartOptions, data: array }; // packages/ag-charts-community/src/chart/interaction/activeManager.ts var ActiveManager = class { constructor(chartService, eventsHub, updateService, interactionManager, fireEvent) { this.chartService = chartService; this.eventsHub = eventsHub; this.interactionManager = interactionManager; this.fireEvent = fireEvent; this.mementoOriginatorKey = "active"; this.updateable = true; // FIXME: same pattern as `ZoomManager`. Perhaps an architectural rewrite is warranted. this.didLayout = false; this.pendingMemento = void 0; const removeListener = updateService.addListener("pre-scene-render", () => { this.didLayout = true; const { pendingMemento } = this; if (pendingMemento) { this.restoreMemento(pendingMemento.version, pendingMemento.mementoVersion, pendingMemento.memento); this.pendingMemento = void 0; } removeListener(); }); } isFrozen() { return this.interactionManager.isState(1 /* Frozen */); } clear() { this.update(void 0, void 0); } update(newItemState, nodeDatum) { this.performUpdate("user-interaction", newItemState, nodeDatum, false); } performUpdate(source, newItemState, nodeDatum, frozenChanged) { if (!this.updateable) return; const oldItemState = this.currentItem; this.currentItem = newItemState; this.eventsHub.emit("active:update", newItemState); if (frozenChanged || !objectsEqual(oldItemState, newItemState)) { const { activeItem } = this.createMemento(); const { datum } = nodeDatum ?? {}; this.fireEvent({ type: "activeChange", source, activeItem, datum }); } } createMemento() { switch (this.currentItem?.type) { case "series-node": case "legend": { const { type, seriesId, itemId } = this.currentItem; return { activeItem: { type, seriesId, itemId } }; } default: this.currentItem?.type; return {}; } } guardMemento(blob, messages) { if (blob == void 0) return true; const validationResult = validate(blob, commonChartOptions.initialState.active); messages.push(...validationResult.invalid.map((err) => err.toString())); return validationResult.invalid.length === 0; } restoreMemento(version, mementoVersion, memento) { if (!this.didLayout) { this.pendingMemento = { version, mementoVersion, memento }; return; } this.updateable = false; const [activeItem, nodeDatum] = this.performRestoration(memento?.activeItem); this.updateable = true; const oldFrozen = this.isFrozen(); const newFrozen = memento?.frozen; const frozenChanged = newFrozen === void 0 ? false : oldFrozen !== newFrozen; if (newFrozen === true) { this.interactionManager.pushState(1 /* Frozen */); } else if (newFrozen === false) { this.interactionManager.popState(1 /* Frozen */); } else { newFrozen; } this.performUpdate("state-change", activeItem, nodeDatum, frozenChanged); } performRestoration(activeItem) { let rejection = false; const reject = () => rejection = true; let nodeDatum = void 0; const setDatum = (d) => nodeDatum = d; const initialState = this.pendingMemento !== void 0; const chartId = this.chartService.id; this.eventsHub.emit("active:load-memento", { initialState, chartId, activeItem, reject, setDatum }); return rejection ? [void 0, void 0] : [activeItem, nodeDatum]; } }; // packages/ag-charts-community/src/chart/interaction/animationBatch.ts var AnimationBatch = class { constructor(maxAnimationTime) { this.maxAnimationTime = maxAnimationTime; this.debug = debugLogger_exports.create(true, "animation"); this.controllers = /* @__PURE__ */ new Map(); this.stoppedCbs = /* @__PURE__ */ new Set(); this.currentPhase = 0; this.phases = new Map(PHASE_ORDER.map((p) => [p, []])); this.skipAnimations = false; this.animationTimeConsumed = 0; /** Guard against premature animation execution. */ this.isReady = false; } get size() { return this.controllers.size; } get consumedTimeMs() { return this.animationTimeConsumed; } isActive() { return this.controllers.size > 0; } getActiveControllers() { return this.phases.get(PHASE_ORDER[this.currentPhase]) ?? []; } checkOverlappingId(id) { if (id != null && this.controllers.has(id)) { this.controllers.get(id).stop(); this.debug(`Skipping animation batch due to update of existing animation: ${id}`); this.skip(); } } addAnimation(animation) { if (animation.isComplete) return; const animationPhaseIdx = PHASE_ORDER.indexOf(animation.phase); if (animationPhaseIdx < this.currentPhase) { this.debug(`Skipping animation due to being for an earlier phase`, animation.id); animation.stop(); return; } this.controllers.set(animation.id, animation); this.phases.get(animation.phase)?.push(animation); } removeAnimation(animation) { this.controllers.delete(animation.id); const phase = this.phases.get(animation.phase); const index = phase?.indexOf(animation); if (index != null && index >= 0) { phase?.splice(index, 1); } } progress(deltaTime) { if (!this.isReady) return; let unusedTime = deltaTime === 0 ? 0.01 : deltaTime; const refresh = () => { const phase2 = PHASE_ORDER[this.currentPhase]; return { phaseControllers: [...this.getActiveControllers()], phase: phase2, phaseMeta: PHASE_METADATA[phase2] }; }; let { phase, phaseControllers, phaseMeta } = refresh(); const arePhasesComplete = () => PHASE_ORDER[this.currentPhase] == null; const progressPhase = () => { ({ phase, phaseControllers, phaseMeta } = refresh()); while (!arePhasesComplete() && phaseControllers.length === 0) { this.currentPhase++; ({ phase, phaseControllers, phaseMeta } = refresh()); this.debug(`AnimationBatch - phase changing to ${phase}`, { unusedTime }, phaseControllers); } }; const total = this.controllers.size; this.debug(`AnimationBatch - ${deltaTime}ms; phase ${phase} with ${phaseControllers?.length} of ${total}`); do { const phaseDeltaTime = unusedTime; const skipPhase = phaseMeta.skipIfNoEarlierAnimations && this.animationTimeConsumed === 0; let completeCount = 0; for (const controller of phaseControllers) { if (skipPhase) { controller.stop(); } else { unusedTime = Math.min(controller.update(phaseDeltaTime), unusedTime); } if (controller.isComplete) { completeCount++; this.removeAnimation(controller); } } this.animationTimeConsumed += phaseDeltaTime - unusedTime; this.debug( `AnimationBatch - updated ${phaseControllers.length} controllers; ${completeCount} completed`, phaseControllers ); this.debug(`AnimationBatch - animationTimeConsumed: ${this.animationTimeConsumed}`); progressPhase(); } while (unusedTime > 0 && !arePhasesComplete()); if (this.animationTimeConsumed > this.maxAnimationTime) { this.debug(`Animation batch exceeded max animation time, skipping`, [...this.controllers]); this.stop(); } } ready() { if (this.isReady) return; this.isReady = true; this.debug(`AnimationBatch - ready; skipped: ${this.skipAnimations}`, [...this.controllers]); let skipAll = true; for (const [, controller] of this.controllers) { if (controller.duration > 0 && PHASE_METADATA[controller.phase].skipIfNoEarlierAnimations !== true) { skipAll = false; break; } } if (!skipAll) { for (const [, controller] of this.controllers) { if (controller.autoplay) { controller.play(true); } } } } skip(skip = true) { if (this.skipAnimations === false && skip === true) { for (const controller of this.controllers.values()) { controller.stop(); } this.controllers.clear(); } this.skipAnimations = skip; } play() { for (const controller of this.controllers.values()) { controller.play(); } } stop() { for (const controller of this.controllers.values()) { try { controller.stop(); this.removeAnimation(controller); } catch (error2) { logger_exports.error("Error during animation stop", error2); } } this.dispatchStopped(); } stopByAnimationId(id) { if (id != null && this.controllers.has(id)) { const controller = this.controllers.get(id); if (controller) { controller.stop(); this.removeAnimation(controller); } } } stopByAnimationGroupId(id) { for (const controller of this.controllers.values()) { if (controller.groupId === id) { this.stopByAnimationId(controller.id); } } } dispatchStopped() { for (const cb of this.stoppedCbs) { cb(); } this.stoppedCbs.clear(); } isSkipped() { return this.skipAnimations; } getRemainingTime(restrictPhase) { if (!this.isActive()) return 0; let total = 0; for (const [phase, controllers] of this.phases) { if (controllers.length === 0) continue; if (restrictPhase != null && restrictPhase !== phase) continue; total += Math.max(...controllers.map((c) => c.isComplete ? 0 : c.delay + c.duration - (c.elapsed ?? 0))); } return total; } destroy() { this.stop(); this.controllers.clear(); } }; // packages/ag-charts-community/src/chart/interaction/animationManager.ts function validAnimationDuration(testee) { if (testee == null) return true; return !Number.isNaN(testee) && testee >= 0 && testee <= 2; } var AnimationManager = class { constructor(interactionManager, chartUpdateMutex) { this.interactionManager = interactionManager; this.chartUpdateMutex = chartUpdateMutex; this.defaultDuration = 1e3; this.maxAnimatableItems = MAX_ANIMATABLE_NODES; this.batch = new AnimationBatch(this.defaultDuration * 1.5); this.debug = debugLogger_exports.create(true, "animation"); this.events = new EventEmitter(); this.rafAvailable = typeof requestAnimationFrame !== "undefined"; this.isPlaying = true; this.requestId = null; this.skipAnimations = true; this.currentAnonymousAnimationId = 0; this.cumulativeAnimationTime = 0; } addListener(eventName, listener) { return this.events.on(eventName, listener); } /** * Create an animation to tween a value between the `from` and `to` properties. If an animation already exists * with the same `id`, immediately stop it. */ animate(opts) { const batch = this.batch; try { batch.checkOverlappingId(opts.id); } catch (error2) { this.failsafeOnError(error2); return; } let { id } = opts; if (id == null) { id = `__${this.currentAnonymousAnimationId}`; this.currentAnonymousAnimationId += 1; } const skip = this.isSkipped() || opts.phase === "none"; if (skip) { this.debug("AnimationManager - skipping animation"); } const { delay, duration } = opts; if (!validAnimationDuration(delay)) { throw new Error(`Animation delay of ${delay} is unsupported (${id})`); } if (!validAnimationDuration(duration)) { throw new Error(`Animation duration of ${duration} is unsupported (${id})`); } const animation = new Animation({ ...opts, id, skip, autoplay: this.isPlaying ? opts.autoplay : false, phase: opts.phase, defaultDuration: this.defaultDuration }); if (this.forceTimeJump(animation, this.defaultDuration)) { return; } this.batch.addAnimation(animation); return animation; } play() { if (this.isPlaying) { return; } this.isPlaying = true; this.debug("AnimationManager.play()"); try { this.batch.play(); } catch (error2) { this.failsafeOnError(error2); } this.requestAnimation(); } stop() { this.isPlaying = false; this.cancelAnimation(); this.debug("AnimationManager.stop()"); this.batch.stop(); } stopByAnimationId(id) { try { this.batch.stopByAnimationId(id); } catch (error2) { this.failsafeOnError(error2); } } stopByAnimationGroupId(id) { try { this.batch.stopByAnimationGroupId(id); } catch (error2) { this.failsafeOnError(error2); } } reset() { if (this.isPlaying) { this.stop(); this.play(); } else { this.stop(); } } skip(skip = true) { this.skipAnimations = skip; } isSkipped() { return !this.rafAvailable || this.skipAnimations || this.batch.isSkipped(); } isActive() { return this.isPlaying && this.batch.isActive(); } getRemainingTime(phase) { return this.batch.getRemainingTime(phase); } getCumulativeAnimationTime() { return this.cumulativeAnimationTime; } skipCurrentBatch() { if (this.debug.check()) { this.debug(`AnimationManager - skipCurrentBatch()`, { stack: new Error("Stack trace for animation skip tracking").stack }); } this.batch.skip(); } /** Mocking point for tests to guarantee that animation updates happen. */ isSkippingFrames() { return true; } /** Mocking point for tests to capture requestAnimationFrame callbacks. */ scheduleAnimationFrame(cb) { this.requestId = getWindow().requestAnimationFrame((t) => { cb(t).catch((e) => logger_exports.error(e)); }); } /** Mocking point for tests to skip animations to a specific point in time. */ forceTimeJump(_animation, _defaultDuration) { return false; } requestAnimation() { if (!this.rafAvailable) return; if (!this.batch.isActive() || this.requestId !== null) return; let prevTime; const onAnimationFrame = async (time3) => { await this.debug.group("AnimationManager.onAnimationFrame()", async () => { const executeAnimationFrame = () => { const deltaTime = time3 - (prevTime ?? time3); prevTime = time3; this.debug("AnimationManager", { controllersCount: this.batch.size, deltaTime }); this.interactionManager.pushState(4 /* Animation */); try { this.batch.progress(deltaTime); this.cumulativeAnimationTime += deltaTime; } catch (error2) { this.failsafeOnError(error2); } this.events.emit("animation-frame", { type: "animation-frame", deltaMs: deltaTime }); }; if (this.isSkippingFrames()) { await this.chartUpdateMutex.acquireImmediately(executeAnimationFrame); } else { await this.chartUpdateMutex.acquire(executeAnimationFrame); } if (this.batch.isActive()) { this.scheduleAnimationFrame(onAnimationFrame); } else { this.batch.stop(); this.events.emit("animation-stop", { type: "animation-stop", deltaMs: this.batch.consumedTimeMs }); } }); }; this.events.emit("animation-start", { type: "animation-start", deltaMs: 0 }); this.scheduleAnimationFrame(onAnimationFrame); } cancelAnimation() { if (this.requestId === null) return; cancelAnimationFrame(this.requestId); this.events.emit("animation-stop", { type: "animation-stop", deltaMs: this.batch.consumedTimeMs }); this.requestId = null; this.startBatch(); } failsafeOnError(error2, cancelAnimation = true) { logger_exports.error("Error during animation, skipping animations", error2); if (cancelAnimation) { this.cancelAnimation(); } } startBatch(skipAnimations) { this.debug(`AnimationManager - startBatch() with skipAnimations=${skipAnimations}.`); this.reset(); this.batch.destroy(); this.batch = new AnimationBatch(this.defaultDuration * 1.5); if (skipAnimations === true) { this.batch.skip(); } } endBatch() { if (this.batch.isActive()) { this.batch.ready(); this.requestAnimation(); } else { this.interactionManager.popState(4 /* Animation */); if (this.batch.isSkipped()) { this.batch.skip(false); } } } onBatchStop(cb) { this.batch.stoppedCbs.add(cb); } destroy() { this.stop(); this.events.clear(); } }; // packages/ag-charts-community/src/chart/interaction/contextMenuTypes.ts var ContextMenuBuiltinItems = class { constructor() { this.download = { type: "action", showOn: "always", label: "contextMenuDownload", enabled: true, action: void 0, items: void 0 }; this["zoom-to-cursor"] = { type: "action", showOn: "series-area", label: "contextMenuZoomToCursor", enabled: true, action: void 0, items: void 0 }; this["pan-to-cursor"] = { type: "action", showOn: "series-area", label: "contextMenuPanToCursor", enabled: true, action: void 0, items: void 0 }; this["reset-zoom"] = { type: "action", showOn: "series-area", label: "contextMenuResetZoom", enabled: true, action: void 0, items: void 0 }; this["toggle-series-visibility"] = { type: "action", showOn: "legend-item", label: "contextMenuToggleSeriesVisibility", enabled: true, action: void 0, items: void 0 }; this["toggle-other-series"] = { type: "action", showOn: "legend-item", label: "contextMenuToggleOtherSeries", enabled: true, action: void 0, items: void 0 }; this["separator"] = { type: "separator", showOn: "always", label: "separator", enabled: true, action: void 0, items: void 0 }; } }; var ContextMenuBuiltinItemLists = class { constructor() { this.defaults = [ "download", "zoom-to-cursor", "pan-to-cursor", "reset-zoom", "toggle-series-visibility", "toggle-other-series" ]; } }; var ContextMenuBuiltins = class { constructor() { this.items = new ContextMenuBuiltinItems(); this.lists = new ContextMenuBuiltinItemLists(); } }; // packages/ag-charts-community/src/chart/interaction/contextMenuRegistry.ts var ContextMenuRegistry = class { constructor(eventsHub) { this.eventsHub = eventsHub; this.builtins = new ContextMenuBuiltins(); this.hiddenActions = /* @__PURE__ */ new Set(); this.toggle("zoom-to-cursor", "hide"); this.toggle("pan-to-cursor", "hide"); this.toggle("reset-zoom", "hide"); } static check(showOn, event) { return event.showOn == showOn; } static checkCallback(desiredShowOn, showOn, _callback) { return desiredShowOn === showOn; } dispatchContext(showOn, pointerEvent, context, position) { const { widgetEvent } = pointerEvent; if (widgetEvent.sourceEvent.defaultPrevented) { return; } const x = position?.x ?? pointerEvent.canvasX; const y = position?.y ?? pointerEvent.canvasY; const event = { showOn, x, y, context, widgetEvent }; this.eventsHub.emit("context-menu:setup", event); this.eventsHub.emit("context-menu:complete", event); } isVisible(id) { return !this.hiddenActions.has(id); } toggle(id, action) { action ?? (action = this.isVisible(id) ? "hide" : "show"); switch (action) { case "show": this.hiddenActions.delete(id); break; case "hide": this.hiddenActions.add(id); break; } } }; // packages/ag-charts-community/src/chart/interaction/highlightManager.ts var _HighlightManager = class _HighlightManager { constructor(eventsHub) { this.eventsHub = eventsHub; this.highlightStates = new StateTracker(); // Track pending unhighlights per caller this.pendingUnhighlights = /* @__PURE__ */ new Map(); // Configurable delay (hardcoded for POC, will be user-configurable later) this.unhighlightDelay = 100; } updateHighlight(callerId, highlightedDatum, delayed = false) { if (highlightedDatum?.series?.isHighlightEnabled() === false) { highlightedDatum = void 0; } const previousHighlight = this.getActiveHighlight(); if (highlightedDatum == null && delayed && this.unhighlightDelay > 0) { if (!this.pendingUnhighlights.has(callerId)) { const scheduler = debouncedCallback(() => { this.applyPendingUnhighlight(callerId); }); this.pendingUnhighlights.set(callerId, { scheduler }); scheduler.schedule(this.unhighlightDelay); } return; } const pending = this.pendingUnhighlights.get(callerId); if (pending) { pending.scheduler.cancel(); this.pendingUnhighlights.delete(callerId); } if (highlightedDatum) { this.highlightStates.set(callerId, highlightedDatum); } else { this.highlightStates.delete(callerId); } this.maybeEmitChange(callerId, previousHighlight); } maybeEmitChange(callerId, previousHighlight) { const currentHighlight = this.getActiveHighlight(); if (!this.isEqual(currentHighlight, previousHighlight)) { this.eventsHub.emit(_HighlightManager.HIGHLIGHT_CHANGE_EVENT, { callerId, currentHighlight, previousHighlight }); } } applyPendingUnhighlight(callerId) { if (!this.pendingUnhighlights.has(callerId)) { return; } this.pendingUnhighlights.delete(callerId); const previousHighlight = this.getActiveHighlight(); this.highlightStates.delete(callerId); const currentHighlight = this.getActiveHighlight(); if (!this.isEqual(currentHighlight, previousHighlight)) { this.eventsHub.emit(_HighlightManager.HIGHLIGHT_CHANGE_EVENT, { callerId, currentHighlight, previousHighlight }); } } getActiveHighlight() { return this.highlightStates.stateValue(); } destroy() { for (const { scheduler } of this.pendingUnhighlights.values()) { scheduler.cancel(); } this.pendingUnhighlights.clear(); } isEqual(a, b) { return a === b || a != null && b != null && a.series === b.series && this.idsMatch(a, b) && a.datum === b.datum; } idsMatch(a, b) { return a.itemId != null && b.itemId != null && a.itemId === b.itemId || a.datumIndex != null && b.datumIndex != null && objectsEqual(a.datumIndex, b.datumIndex); } }; // milliseconds _HighlightManager.HIGHLIGHT_CHANGE_EVENT = "highlight:change"; var HighlightManager = _HighlightManager; // packages/ag-charts-community/src/chart/interaction/tooltipManager.ts var TooltipManager = class { constructor(eventsHub, localeManager, domManager, tooltip) { this.domManager = domManager; this.tooltip = tooltip; this.stateTracker = new StateTracker(); this.suppressState = new StateTracker(false); this.appliedState = null; // Track pending removals per caller this.pendingRemovals = /* @__PURE__ */ new Map(); // Configurable delay (match highlights at 100ms) this.removeDelay = 100; // milliseconds this.cleanup = new CleanupRegistry(); this.cleanup.register( tooltip.setup(localeManager, domManager), eventsHub.on("dom:hidden", () => this.tooltip.hide()) ); } destroy() { for (const { scheduler } of this.pendingRemovals.values()) { scheduler.cancel(); } this.pendingRemovals.clear(); this.cleanup.flush(); } updateTooltip(callerId, meta, content, pagination) { const pending = this.pendingRemovals.get(callerId); if (pending) { pending.scheduler.cancel(); this.pendingRemovals.delete(callerId); } content ?? (content = this.stateTracker.get(callerId)?.content); this.stateTracker.set(callerId, { meta, content, pagination }); this.applyStates(); } removeTooltip(callerId, meta, delayed = false) { if (delayed && this.removeDelay > 0) { const existingPending = this.pendingRemovals.get(callerId); if (existingPending) { if (meta) { existingPending.lastMeta = meta; } return; } const scheduler = debouncedCallback(() => { this.applyPendingRemoval(callerId); }); this.pendingRemovals.set(callerId, { scheduler, lastMeta: meta }); scheduler.schedule(this.removeDelay); return; } const pending = this.pendingRemovals.get(callerId); if (pending) { pending.scheduler.cancel(); this.pendingRemovals.delete(callerId); } this.stateTracker.delete(callerId); this.applyStates(); } suppressTooltip(callerId) { this.suppressState.set(callerId, true); } unsuppressTooltip(callerId) { this.suppressState.delete(callerId); } applyPendingRemoval(callerId) { if (!this.pendingRemovals.has(callerId)) { return; } this.pendingRemovals.delete(callerId); this.stateTracker.delete(callerId); this.applyStates(); } applyStates() { const id = this.stateTracker.stateId(); const state = id ? this.stateTracker.get(id) : void 0; if (this.suppressState.stateValue() || state?.meta == null || state?.content == null) { this.appliedState = null; this.tooltip.hide(); return; } const canvasRect = this.domManager.getBoundingClientRect(); const boundingRect = this.tooltip.bounds === "extended" ? this.domManager.getOverlayClientRect() : canvasRect; if (objectsEqual(this.appliedState?.content, state?.content) && objectsEqual(this.appliedState?.pagination, state?.pagination)) { const renderInstantly = this.tooltip.isVisible(); this.tooltip.show(boundingRect, canvasRect, state?.meta, null, void 0, renderInstantly); } else { this.tooltip.show(boundingRect, canvasRect, state?.meta, state?.content, state?.pagination); } this.appliedState = state; } static makeTooltipMeta(event, series, datum, movedBounds) { const { canvasX, canvasY } = event; const tooltip = series.properties.tooltip; const { placement, anchorTo, xOffset, yOffset } = tooltip.position; const refPoint = getDatumRefPoint(series, datum, movedBounds); const meta = { canvasX, canvasY, nodeCanvasX: refPoint?.canvasX ?? canvasX, nodeCanvasY: refPoint?.canvasY ?? canvasY, enableInteraction: tooltip.interaction?.enabled ?? false, showArrow: tooltip.showArrow, position: { placement, anchorTo, xOffset, yOffset } }; return meta; } }; // packages/ag-charts-community/src/chart/interaction/dragInterpreter.ts var DRAG_THRESHOLD_PX = 3; var DOUBLE_TAP_TIMER_MS = 505; var DOUBLE_TAP_THRESHOLD_PX = 30; function makeSynthetic(type, event) { const { device, offsetX, offsetY, clientX, clientY, currentX, currentY, sourceEvent } = event; return { type, device, offsetX, offsetY, clientX, clientY, currentX, currentY, sourceEvent }; } function checkDragDistance(dx, dy) { const distanceSquared2 = dx * dx + dy * dy; const thresholdSquared = DRAG_THRESHOLD_PX * DRAG_THRESHOLD_PX; return distanceSquared2 >= thresholdSquared; } function checkDoubleTapDistance(t1, t2) { const dx = t1.clientX - t2.clientX; const dy = t1.clientY - t2.clientY; const distanceSquared2 = dx * dx + dy * dy; const thresholdSquared = DOUBLE_TAP_THRESHOLD_PX * DOUBLE_TAP_THRESHOLD_PX; return distanceSquared2 < thresholdSquared; } var DragInterpreter = class { constructor(widget) { this.cleanup = new CleanupRegistry(); this.events = new EventEmitter(); this.isDragging = false; this.touch = { distanceTravelledX: 0, distanceTravelledY: 0, clientX: 0, clientY: 0 }; this.cleanup.register( widget.addListener("touchstart", this.onTouchStart.bind(this)), widget.addListener("touchmove", this.onTouchMove.bind(this)), widget.addListener("touchend", this.onTouchEnd.bind(this)), widget.addListener("mousemove", this.onMouseMove.bind(this)), widget.addListener("dblclick", this.onDblClick.bind(this)), widget.addListener("drag-start", this.onDragStart.bind(this)), widget.addListener("drag-move", this.onDragMove.bind(this)), widget.addListener("drag-end", this.onDragEnd.bind(this)) ); } destroy() { this.cleanup.flush(); } onTouchStart(e) { const { clientX, clientY } = e.sourceEvent.targetTouches[0] ?? { clientX: Infinity, clientY: Infinity }; this.touch.distanceTravelledX = 0; this.touch.distanceTravelledY = 0; this.touch.clientX = clientX; this.touch.clientY = clientY; } onTouchMove(e) { const { clientX, clientY } = e.sourceEvent.targetTouches[0] ?? { clientX: Infinity, clientY: Infinity }; this.touch.distanceTravelledX += Math.abs(this.touch.clientX - clientX); this.touch.distanceTravelledY += Math.abs(this.touch.clientY - clientY); this.touch.clientX = clientX; this.touch.clientY = clientY; } onTouchEnd(event) { event.sourceEvent.preventDefault(); } onMouseMove(event) { this.events.emit("mousemove", event); } onDblClick(event) { this.events.emit("dblclick", event); } onDragStart(event) { this.dragStartEvent = event; } onDragMove(event) { if (this.dragStartEvent != null) { if (checkDragDistance(event.originDeltaX, event.originDeltaY)) { this.events.emit("drag-start", this.dragStartEvent); this.events.emit("drag-move", { ...this.dragStartEvent, type: "drag-move" }); this.dragStartEvent = void 0; this.isDragging = true; } } if (this.isDragging) { this.events.emit("drag-move", event); } } onDragEnd(event) { if (this.isDragging) { this.events.emit("drag-end", event); this.isDragging = false; return; } if (event.device === "mouse") { const click = makeSynthetic("click", event); this.events.emit("click", click); } else if (event.sourceEvent.type === "touchend") { if (checkDragDistance(this.touch.distanceTravelledX, this.touch.distanceTravelledY)) { return; } const click = makeSynthetic("click", event); this.events.emit("click", click); const now = Date.now(); if (this.lastClick !== void 0 && now - this.lastClick.time <= DOUBLE_TAP_TIMER_MS && checkDoubleTapDistance(this.lastClick, event)) { const dblClick = makeSynthetic("dblclick", event); this.events.emit("dblclick", dblClick); this.lastClick = void 0; } else { this.lastClick = { time: now, clientX: event.clientX, clientY: event.clientY }; } } } }; // packages/ag-charts-community/src/chart/interaction/widgetSet.ts var DOMManagerWidget = class extends NativeWidget { constructor(elem) { super(elem); } addChildToDOM() { } removeChildFromDOM() { } }; var WidgetSet = class { constructor(domManager, opts) { this.seriesWidget = new DOMManagerWidget(domManager.getParent("series-area")); this.chartWidget = new DOMManagerWidget(domManager.getParent("canvas-proxy")); this.containerWidget = new DOMManagerWidget(domManager.getParent("canvas-container")); this.containerWidget.addChild(this.chartWidget); this.chartWidget.addChild(this.seriesWidget); if (opts.withDragInterpretation) { this.seriesDragInterpreter = new DragInterpreter(this.seriesWidget); } } destroy() { this.seriesDragInterpreter?.destroy(); this.seriesWidget.destroy(); this.chartWidget.destroy(); this.containerWidget.destroy(); } }; // packages/ag-charts-community/src/util/panToBBox.ts function normalize(screenMin, min, screenMax, max, target) { return min + (max - min) * ((target - screenMin) / (screenMax - screenMin)); } function unnormalize(screenMin, min, screenMax, max, ratio2) { return screenMin + (ratio2 - min) * ((screenMax - screenMin) / (max - min)); } function calcWorldAxis(viewportMin, viewportMax, ratio2) { return [ unnormalize(viewportMin, ratio2.min, viewportMax, ratio2.max, 0), unnormalize(viewportMin, ratio2.min, viewportMax, ratio2.max, 1) ]; } function calcWorldVec4(viewport, ratioX, ratioY) { const [x1, x2] = calcWorldAxis(viewport.x1, viewport.x2, ratioX); const [y1, y2] = calcWorldAxis(viewport.y1, viewport.y2, ratioY); return { x1, x2, y1, y2 }; } function panAxesUnnormalized(worldMin, worldMax, viewportMin, viewportMax, targetMin, targetMax) { if (viewportMin <= targetMin && targetMax <= viewportMax) return viewportMin; const minDiff = targetMin - viewportMin; const maxDiff = targetMax - viewportMax; const diff2 = Math.abs(minDiff) < Math.abs(maxDiff) ? minDiff : maxDiff; return clamp(worldMin, viewportMin + diff2, worldMax); } function calcPanToBBoxRatios(viewportBBox, ratios, targetBBox) { const { x: ratioX = { min: 0, max: 1 }, y: ratioY = { min: 0, max: 1 } } = ratios; const target = vector4_exports.from(targetBBox); const viewport = vector4_exports.from(viewportBBox); const world = calcWorldVec4(viewport, ratioX, ratioY); const x = panAxesUnnormalized(world.x1, world.x2, viewport.x1, viewport.x2, target.x1, target.x2); const y = panAxesUnnormalized(world.y1, world.y2, viewport.y1, viewport.y2, target.y1, target.y2); const result = { x: { min: normalize(viewport.x1, ratioX.min, viewport.x2, ratioX.max, x), max: normalize(viewport.x1, ratioX.min, viewport.x2, ratioX.max, x + viewportBBox.width) }, y: { min: normalize(viewport.y1, ratioY.min, viewport.y2, ratioY.max, y), max: normalize(viewport.y1, ratioY.min, viewport.y2, ratioY.max, y + viewportBBox.height) } }; const diffX = result.x.max - result.x.min; const diffY = result.y.max - result.y.min; result.x.min = clamp(0, result.x.min, 1 - diffX); result.x.max = result.x.min + diffX; result.y.min = clamp(0, result.y.min, 1 - diffY); result.y.max = result.y.min + diffY; return result; } // packages/ag-charts-community/src/chart/interaction/zoomManager.ts var rangeValidator2 = (axis) => attachDescription((value, { options }) => { if (!ContinuousScale.is(axis?.scale) && !DiscreteTimeScale.is(axis?.scale)) return true; if (value == null || options.end == null) return true; return value < options.end; }, `to be less than end`); function validateChanges(changes) { for (const axisId of strictObjectKeys(changes)) { const zoom = changes[axisId]; if (!zoom) continue; const { min, max } = zoom; if (min < 0 || max > 1) { logger_exports.warnOnce( `Attempted to update axis (${axisId}) zoom to an invalid ratio of [{ min: ${min}, max: ${max} }], expecting a ratio of 0 to 1. Ignoring.` ); delete changes[axisId]; } } } function refreshCoreState(nextAxes, state) { const result = {}; for (const { id, direction } of nextAxes) { const { min, max } = state[id] ?? { min: 0, max: 1 }; result[id] = { min, max, direction }; } return result; } function areEqualCoreZooms(p, q) { const pKeys = strictObjectKeys(p); const qKeys = strictObjectKeys(q); if (pKeys.length !== qKeys.length) return false; for (const k of pKeys) if (!qKeys.includes(k)) return false; for (const k of pKeys) { const pVal = p[k]; const qVal = q[k]; if (pVal === qVal) { continue; } else if (pVal == void 0 || qVal == void 0 || pVal.direction !== qVal.direction || pVal.min !== qVal.min || pVal.max !== qVal.max) { return false; } } return true; } function userInteraction(sourceDetail) { return { source: "user-interaction", sourceDetail }; } var ZoomManager = class extends BaseManager { constructor(eventsHub, updateService, fireChartEvent) { super(); this.eventsHub = eventsHub; this.fireChartEvent = fireChartEvent; this.mementoOriginatorKey = "zoom"; this.state = {}; this.axes = []; this.didLayoutAxes = false; this.lastRestoredState = {}; this.independentAxes = false; this.navigatorModule = false; this.zoomModule = false; // The initial state memento can not be restored until the chart has performed its first layout. Instead save it as // pending and restore then delete it on the first layout. this.pendingMemento = void 0; this.cleanup.register( eventsHub.on("zoom:change-request", (event) => { this.constrainZoomToRequiredWidth(event); }), updateService.addListener("pre-series-update", ({ requiredRangeRatio, requiredRangeDirection }) => { this.didLayoutAxes = true; const { pendingMemento } = this; if (pendingMemento) { this.restoreMemento(pendingMemento.version, pendingMemento.mementoVersion, pendingMemento.memento); } else { this.restoreRequiredRange(requiredRangeRatio, requiredRangeDirection); } this.updateZoom({ source: "chart-update", // FIXME(AG-16412): this is "probably" what caused, but we don't really know sourceDetail: "unspecified" }); }), updateService.addListener("update-complete", ({ wasShortcut }) => { if (wasShortcut) return; if (this.pendingZoomEventSource) { const source = this.pendingZoomEventSource; this.fireChartEvent({ type: "zoom", source, ...this.getMementoRanges() }); this.pendingZoomEventSource = void 0; } }) ); } // FIXME: should be private toCoreZoomState(axisZoom) { const result = {}; let ids; const { state } = this; if (this.independentAxes) { const xId = this.getPrimaryAxisId("x" /* X */); const yId = this.getPrimaryAxisId("y" /* Y */); ids = []; if (xId) ids.push(xId); if (yId) ids.push(yId); } else { ids = strictObjectKeys(state); } for (const id of ids) { const { direction } = state[id] ?? {}; if (direction != void 0) { const zoom = axisZoom[direction]; if (zoom) { const { min, max } = zoom; result[id] = { min, max, direction }; } } } return result; } // FIXME: should be private toZoomState(coreZoom) { let x; let y; for (const id of strictObjectKeys(coreZoom)) { const { min, max, direction } = coreZoom[id]; if (direction === "x" /* X */) { x ?? (x = { min, max }); } else if (direction === "y" /* Y */) { y ?? (y = { min, max }); } } if (x || y) { return { x, y }; } } createMemento() { return this.getMementoRanges(); } guardMemento(blob, messages) { if (blob == null) return true; if (!isObject(blob)) return false; const primaryX = this.getPrimaryAxis("x" /* X */); const primaryY = this.getPrimaryAxis("y" /* Y */); const zoomMementoDefs = { rangeX: { start: rangeValidator2(primaryX), end: defined }, rangeY: { start: rangeValidator2(primaryY), end: defined }, ratioX: { start: defined, end: defined }, ratioY: { start: defined, end: defined }, autoScaledAxes: defined }; const { invalid } = validate(blob, zoomMementoDefs); if (invalid.length > 0) { messages.push(...invalid.map(String)); return false; } return true; } restoreMemento(version, mementoVersion, memento) { if (!this.axes || !this.didLayoutAxes) { this.pendingMemento = { version, mementoVersion, memento }; return; } this.pendingMemento = void 0; const zoom = definedZoomState(this.getZoom()); if (memento?.rangeX) { zoom.x = this.rangeToRatioDirection("x" /* X */, memento.rangeX) ?? { min: 0, max: 1 }; } else if (memento?.ratioX) { zoom.x = { min: memento.ratioX.start ?? 0, max: memento.ratioX.end ?? 1 }; } else { zoom.x = { min: 0, max: 1 }; } const { navigatorModule, zoomModule } = this; this.eventsHub.emit("zoom:load-memento", { zoom, memento, navigatorModule, zoomModule }); const changes = this.toCoreZoomState(zoom); this.lastRestoredState = deepFreeze(deepClone(changes)); this.updateChanges({ source: "state-change", sourceDetail: "internal-restoreMemento", changes, isReset: false }); } findAxis(axisId) { for (const a of this.axes) { if (a.id === axisId) return a; } } getAxes() { return this.axes; } setAxes(nextAxes) { const { axes } = this; axes.length = 0; for (const axis of nextAxes) { if ("range" in axis) { axes.push(axis); } } const oldState = this.state; const changes = refreshCoreState(nextAxes, oldState); this.state = changes; this.lastRestoredState = refreshCoreState(nextAxes, this.lastRestoredState); this.updateChanges({ source: "chart-update", sourceDetail: "internal-setAxes", changes, isReset: false }); } setIndependentAxes(independent = true) { this.independentAxes = independent; } setNavigatorEnabled(enabled = true) { this.navigatorModule = enabled; } setZoomModuleEnabled(enabled = true) { this.zoomModule = enabled; } isNavigatorEnabled() { return this.navigatorModule; } isZoomEnabled() { return this.zoomModule; } updateZoom({ source, sourceDetail }, newZoom) { const changes = this.toCoreZoomState(newZoom ?? {}); return this.updateChanges({ source, sourceDetail, changes, isReset: false }); } computeChangedAxesIds(newState) { const result = []; const oldState = this.state; for (const id of strictObjectKeys(newState)) { const newAxisState = newState[id] ?? { min: 0, max: 1 }; const oldAxisState = oldState[id]; if (oldAxisState == void 0 || oldAxisState.min !== newAxisState.min || oldAxisState.max !== newAxisState.max) { result.push(id); } } return result; } updateChanges(params) { const { source, sourceDetail, isReset, changes } = params; validateChanges(changes); const changedAxes = this.computeChangedAxesIds(changes); const oldState = deepClone(this.state); const newState = deepClone(this.state); for (const id of changedAxes) { const axis = newState[id]; if (axis != void 0) { axis.min = changes[id]?.min ?? 0; axis.max = changes[id]?.max ?? 1; } } this.state = newState; return this.dispatch(source, sourceDetail, changedAxes, isReset, oldState); } resetZoom({ source, sourceDetail }) { this.updateChanges({ source, sourceDetail, changes: this.getRestoredZoom(), isReset: true }); } resetAxisZoom({ source, sourceDetail }, axisId) { this.updateChanges({ source, sourceDetail, changes: { [axisId]: this.getRestoredZoom()[axisId] }, isReset: true }); } panToBBox(seriesRect, target) { if (!this.isZoomEnabled() && !this.isNavigatorEnabled()) return false; const zoom = this.getZoom(); if (zoom === void 0 || !zoom.x && !zoom.y) return false; const panIsPossible = seriesRect.width > 0 && seriesRect.height > 0 && Math.abs(target.width) <= Math.abs(seriesRect.width) && Math.abs(target.height) <= Math.abs(seriesRect.height); if (!panIsPossible) { logger_exports.warnOnce(`cannot pan to target BBox - chart too small?`); return false; } const newZoom = calcPanToBBoxRatios(seriesRect, zoom, target); const changes = this.toCoreZoomState(newZoom); return this.updateChanges({ source: "user-interaction", sourceDetail: "internal-panToBBox", changes, isReset: false }); } // Fire this event to signal to listeners that the view is changing through a zoom and/or pan change. fireZoomPanStartEvent(callerId) { this.eventsHub.emit("zoom:pan-start", { callerId }); } extendToEnd(sourcing, direction, extent2) { return this.extendWith(sourcing, direction, (end3) => Number(end3) - extent2); } extendWith({ source, sourceDetail }, direction, fn) { const axis = this.getPrimaryAxis(direction); if (!axis) return; const extents = this.getDomainExtents(axis); if (!extents) return; const [, end3] = extents; const start2 = fn(end3); const ratio2 = this.rangeToRatioAxis(axis, { start: start2 }); if (!ratio2) return; this.updateChanges({ source, sourceDetail, changes: { [direction]: ratio2 }, isReset: false }); } updateWith({ source, sourceDetail }, direction, fn) { const axis = this.getPrimaryAxis(direction); if (!axis) return; const extents = this.getDomainExtents(axis); if (!extents) return; let [start2, end3] = extents; [start2, end3] = fn(start2, end3); const ratio2 = this.rangeToRatioAxis(axis, { start: start2, end: end3 }); if (!ratio2) return; this.updateChanges({ source, sourceDetail, changes: { [direction]: ratio2 }, isReset: false }); } getZoom() { return this.toZoomState(this.state); } getAxisZoom(axisId) { return this.state[axisId] ?? { min: 0, max: 1 }; } getAxisZooms() { return this.state; } getCoreZoom() { return this.state; } getRestoredZoom() { return this.lastRestoredState; } getPrimaryAxisId(direction) { return this.getPrimaryAxis(direction)?.id; } getBoundSeries() { const xAxis = this.getPrimaryAxis("x" /* X */); const yAxis = this.getPrimaryAxis("y" /* Y */); let boundSeries; if (this.independentAxes) { const xBoundSeries = new Set(xAxis?.boundSeries ?? []); const yBoundSeries = new Set(yAxis?.boundSeries ?? []); boundSeries = /* @__PURE__ */ new Set(); for (const series of xBoundSeries) { if (yBoundSeries.has(series)) { boundSeries.add(series); } } } else { boundSeries = /* @__PURE__ */ new Set([...xAxis?.boundSeries ?? [], ...yAxis?.boundSeries ?? []]); } return boundSeries; } constrainZoomToItemCount(zoom, minVisibleItems, shouldAutoscale) { let xVisibleRange = [zoom.x.min, zoom.x.max]; let yVisibleRange = shouldAutoscale ? void 0 : [zoom.y.min, zoom.y.max]; for (const series of this.getBoundSeries()) { const nextZoom = series.getZoomRangeFittingItems(xVisibleRange, yVisibleRange, minVisibleItems); if (nextZoom == null) continue; xVisibleRange = nextZoom.x; yVisibleRange = nextZoom.y; } const x = { min: xVisibleRange[0], max: xVisibleRange[1] }; const y = yVisibleRange ? { min: yVisibleRange[0], max: yVisibleRange[1] } : void 0; return definedZoomState({ x, y }); } isVisibleItemsCountAtLeast(zoom, minVisibleItems, opts) { const boundSeries = this.getBoundSeries(); const xVisibleRange = [zoom.x.min, zoom.x.max]; const yVisibleRange = !opts.includeYVisibleRange && opts.autoScaleYAxis ? void 0 : [zoom.y.min, zoom.y.max]; let visibleItemsCount = 0; for (const series of boundSeries) { const remainingItems = minVisibleItems - (visibleItemsCount ?? 0); const seriesVisibleItems = series.getVisibleItems(xVisibleRange, yVisibleRange, remainingItems); visibleItemsCount += seriesVisibleItems; if (visibleItemsCount >= minVisibleItems) return true; } return boundSeries.size === 0; } getMementoRanges() { const zoom = definedZoomState(this.getZoom()); const memento = { rangeX: this.getRangeDirection("x" /* X */, zoom.x), rangeY: this.getRangeDirection("y" /* Y */, zoom.y), ratioX: { start: zoom.x.min, end: zoom.x.max }, ratioY: { start: zoom.y.min, end: zoom.y.max }, autoScaledAxes: void 0 }; this.eventsHub.emit("zoom:save-memento", { memento }); return memento; } restoreRequiredRange(requiredRangeRatio, requiredRangeDirection) { const { lastRestoredRequiredRange, lastRestoredRequiredRangeDirection } = this; const directionInvalid = requiredRangeDirection !== "x" /* X */ && requiredRangeDirection !== "y" /* Y */; const requiredRangeUnchanged = lastRestoredRequiredRangeDirection === requiredRangeDirection && lastRestoredRequiredRange === requiredRangeRatio; const requiredRangeUnset = requiredRangeRatio === 0 && (lastRestoredRequiredRange == null || lastRestoredRequiredRange === 0); if (directionInvalid || requiredRangeUnchanged || requiredRangeUnset) return; const crossAxisId = this.getPrimaryAxisId(requiredRangeDirection); if (!crossAxisId) return; const crossAxisZoom = this.getAxisZoom(crossAxisId); const requiredZoom = Math.min(1, 1 / requiredRangeRatio); let min = 0; let max = 1; if (requiredRangeDirection === "x" /* X */) { min = clamp(0, 1 - requiredZoom, crossAxisZoom.min); max = clamp(0, min + requiredZoom, 1); } else { max = Math.min(1, crossAxisZoom.max); min = max - requiredZoom; if (min < 0) { max -= min; min = 0; } min = clamp(0, min, 1); max = clamp(0, max, 1); } this.lastRestoredRequiredRange = requiredRangeRatio; this.lastRestoredRequiredRangeDirection = requiredRangeDirection; const zoom = { [requiredRangeDirection]: { min, max } }; const changes = this.toCoreZoomState(zoom); this.lastRestoredState = deepFreeze(deepClone(changes)); this.updateChanges({ source: "state-change", sourceDetail: "internal-requiredWidth", changes, isReset: false }); } constrainZoomToRequiredWidth(event) { if (this.lastRestoredRequiredRange == null || this.lastRestoredRequiredRangeDirection == null) return; const axis = this.lastRestoredRequiredRangeDirection; const crossAxisId = this.getPrimaryAxisId(this.lastRestoredRequiredRangeDirection); if (!crossAxisId) return; const zoom = event.stateAsDefinedZoom(); const oldState = event.oldState[crossAxisId]; const delta3 = zoom[axis].max - zoom[axis].min; const minDelta = 1 / this.lastRestoredRequiredRange; if (delta3 <= minDelta) return; event.constrainZoom({ ...zoom, [axis]: { min: oldState.min, max: oldState.min + minDelta } }); } dispatch(source, sourceDetail, changedAxes, isReset, oldState) { const { x, y } = this.getZoom() ?? {}; const state = this.state; let constrainedState; const zoomManager = this; const event = { source, sourceDetail, isReset, changedAxes, state, oldState, x, y, stateAsDefinedZoom() { return definedZoomState(zoomManager.toZoomState(event.state)); }, constrainZoom(restrictions) { this.constrainChanges(zoomManager.toCoreZoomState(restrictions)); }, constrainChanges(restrictions) { constrainedState ?? (constrainedState = deepClone(state)); for (const id of strictObjectKeys(restrictions)) { const src = restrictions[id]; const dst = constrainedState[id]; if (src && dst) { dst.min = src.min; dst.max = src.max; } } event.state = constrainedState; } }; this.eventsHub.emit("zoom:change-request", event); let wasChangeConstrained = false; if (constrainedState && !areEqualCoreZooms(state, constrainedState)) { wasChangeConstrained = true; this.state = constrainedState; } const changeAccepted = changedAxes.length > 0 || wasChangeConstrained; if (changeAccepted) { const acceptedZoom = this.getZoom() ?? {}; this.eventsHub.emit("zoom:change-complete", { source, sourceDetail, x: acceptedZoom.x }); this.pendingZoomEventSource = source; } return changeAccepted; } getRange(axisId, ratio2) { return this.getRangeAxis(this.findAxis(axisId), ratio2); } getRangeDirection(direction, ratio2) { return this.getRangeAxis(this.getPrimaryAxis(direction), ratio2); } getRangeAxis(axis, ratio2) { if (!axis || !ratio2 || !ContinuousScale.is(axis.scale) && !DiscreteTimeScale.is(axis.scale)) return; const extents = this.getDomainPixelExtents(axis); if (!extents) return; const [d0, d1] = extents; let start2; let end3; if (d0 <= d1) { start2 = axis.scale.invert(0); end3 = axis.scale.invert(d0 + (d1 - d0) * ratio2.max); } else { start2 = axis.scale.invert(d0 - (d0 - d1) * ratio2.min); end3 = axis.scale.invert(0); } return { start: start2, end: end3 }; } rangeToRatio(axisId, range3) { return this.rangeToRatioAxis(this.findAxis(axisId), range3); } rangeToRatioDirection(direction, range3) { return this.rangeToRatioAxis(this.getPrimaryAxis(direction), range3); } rangeToRatioAxis(axis, range3) { if (!axis) return; const extents = this.getDomainPixelExtents(axis); if (!extents) return; const [d0, d1] = extents; const { scale: scale2 } = axis; const { start: start2, end: end3 } = range3; const [startAlignment = 0 /* Leading */, endAlignment = 1 /* Trailing */] = rangeAlignment( start2, end3 ); const r0 = start2 == null ? d0 : scale2.convert(start2, { alignment: startAlignment }); const r1 = end3 == null ? d1 : scale2.convert(end3, { alignment: endAlignment }) + (scale2.bandwidth ?? 0); if (!isFiniteNumber(r0) || !isFiniteNumber(r1)) return; const [dMin, dMax] = [Math.min(d0, d1), Math.max(d0, d1)]; if (r0 < dMin || r0 > dMax) { logger_exports.warnOnce( `Invalid range start [${start2}], expecting a value between [${scale2.invert(d0)}] and [${scale2.invert(d1)}], ignoring.` ); return; } if (r1 < dMin || r1 > dMax) { logger_exports.warnOnce( `Invalid range end [${end3}], expecting a value between [${scale2.invert(d0)}] and [${scale2.invert(d1)}], ignoring.` ); return; } const diff2 = d1 - d0; if (diff2 === 0) return; const min = Math.abs((r0 - d0) / diff2); const max = Math.abs((r1 - d0) / diff2); if (min >= max) return; return { min, max }; } getPrimaryAxis(direction) { return this.axes?.find((a) => a.direction === direction); } getDomainExtents(axis) { const { domain } = axis.scale; const d0 = domain.at(0); const d1 = domain.at(-1); if (d0 == null || d1 == null) return; return [d0, d1]; } getDomainPixelExtents(axis) { const [d0, d1] = axis.scale.range; if (!isFiniteNumber(d0) || !isFiniteNumber(d1)) return; return [d0, d1]; } }; // packages/ag-charts-community/src/chart/layout/seriesLabelLayoutManager.ts var SeriesLabelLayoutManager = class { constructor() { this.labelData = /* @__PURE__ */ new Map(); } updateLabels(placedLabelSeries, padding2, seriesRect = BBox.zero) { const bounds = { x: -padding2.left, y: -padding2.top, width: seriesRect.width + padding2.left + padding2.right, height: seriesRect.height + padding2.top + padding2.bottom }; const expectedSeriesId = new Set(placedLabelSeries.map((s) => s.id)); for (const seriesId of this.labelData.keys()) { if (!expectedSeriesId.has(seriesId)) { this.labelData.delete(seriesId); } } for (const series of placedLabelSeries) { const labelData = series.getLabelData(); if (labelData.every(isPointLabelDatum)) { this.labelData.set(series.id, labelData); } } const placedLabels = placeLabels(this.labelData, bounds, 5); for (const series of placedLabelSeries) { series.updatePlacedLabelData?.(placedLabels.get(series.id) ?? []); } } }; // packages/ag-charts-community/src/chart/legend/legendManager.ts var LegendManager = class { constructor(eventsHub) { this.eventsHub = eventsHub; this.mementoOriginatorKey = "legend"; this.legendDataMap = /* @__PURE__ */ new Map(); } createMemento() { return this.getData().filter(({ hideInLegend, isFixed }) => !hideInLegend && !isFixed).map(({ enabled, seriesId, itemId, legendItemName }) => ({ visible: enabled, seriesId, itemId, legendItemName })); } guardMemento(blob) { return blob == null || isArray(blob); } restoreMemento(_version, _mementoVersion, memento) { if (memento) { for (const datum of memento) { const { seriesId, data } = this.getRestoredData(datum) ?? {}; if (!seriesId || !data) { continue; } this.updateData(seriesId, data); } } this.update(); } getRestoredData(datum) { const { seriesId, itemId, legendItemName, visible } = datum; if (seriesId) { const legendData = this.legendDataMap.get(seriesId) ?? []; const data = legendData.map((d) => { const match = d.seriesId === seriesId && (!itemId || d.itemId === itemId); if (match && d.isFixed) { this.warnFixed(d.seriesId, d.itemId); } return !d.isFixed && match ? { ...d, enabled: visible } : d; }); return { seriesId, data }; } if (itemId == null && legendItemName == null) { return; } for (const legendDatum of this.getData()) { if (itemId != null && legendDatum.itemId !== itemId || legendItemName != null && legendDatum.legendItemName !== legendItemName) { continue; } if (legendDatum.isFixed) { this.warnFixed(legendDatum.seriesId, itemId); return; } const seriesLegendData = (this.legendDataMap.get(legendDatum.seriesId) ?? []).map( (d) => d.itemId === itemId || d.legendItemName === legendItemName ? { ...d, enabled: visible } : d ); return { seriesId: legendDatum.seriesId, data: seriesLegendData }; } } warnFixed(seriesId, itemId) { logger_exports.warnOnce( `The legend item with seriesId [${seriesId}] and itemId [${itemId}] is not configurable, this series item cannot be toggled through the legend.` ); } update(data) { this.eventsHub.emit("legend:change", { legendData: data ?? this.getData() }); } updateData(seriesId, data = []) { this.eventsHub.emit("legend:change-partial", { seriesId, legendData: data }); this.legendDataMap.set(seriesId, data); } clearData() { this.legendDataMap.clear(); } toggleItem(enabled, seriesId, itemId, legendItemName) { if (legendItemName) { for (const datum of this.getData()) { const newData = (this.legendDataMap.get(datum.seriesId) ?? []).map( (d) => d.legendItemName === legendItemName ? { ...d, enabled } : d ); this.updateData(datum.seriesId, newData); } return; } const seriesLegendData = this.getData(seriesId); const singleLegendItem = seriesLegendData.length === 1; const data = seriesLegendData.map( (datum) => itemId == null && singleLegendItem || datum.itemId === itemId ? { ...datum, enabled } : datum ); this.updateData(seriesId, data); } getData(seriesId) { if (seriesId) { return this.legendDataMap.get(seriesId) ?? []; } return [...this.legendDataMap].reduce( (data, [_, legendData]) => data.concat(legendData), [] ); } getDatum({ seriesId, itemId } = {}) { return this.getData(seriesId).find((datum) => datum.itemId === itemId); } getSeriesEnabled(seriesId) { const data = this.getData(seriesId); if (data.length > 0) { return data.some((d) => d.enabled); } } getItemEnabled({ seriesId, itemId } = {}) { return this.getDatum({ seriesId, itemId })?.enabled ?? true; } }; // packages/ag-charts-community/src/chart/optionsGraphService.ts var OptionsGraphService = class { updateCallback(resolvePartialCallback) { this.resolvePartialCallback = resolvePartialCallback; } resolvePartial(path, partialOptions, resolveOptions) { return this.resolvePartialCallback?.(path, partialOptions, resolveOptions); } }; // packages/ag-charts-community/src/scale/irregularBandScale.ts var IrregularBandScale = class extends BandScale { constructor() { super(...arguments); this.type = "category"; // TODO: 'irregular-band'? this.defaultTickCount = 0; this._hasFixedWidth = false; this._paddingInnerWidth = 0; this._domain = []; this._bandRanges = /* @__PURE__ */ new Map(); } set domain(values) { if (this._domain === values) return; if (values.length === 0) { this._bandRanges.clear(); this._hasFixedWidth = false; } this.invalid = true; this._domain = values; } get domain() { return this._domain; } get bands() { return this.domain; } addBand(groupIndex, stackIndex, value) { this._domain.push(this.getDomainValue(groupIndex, stackIndex)); if (!this._bandRanges.has(groupIndex)) { this._bandRanges.set(groupIndex, /* @__PURE__ */ new Map()); } this._bandRanges.get(groupIndex).set(stackIndex, value); this._hasFixedWidth || (this._hasFixedWidth = value != null); this.invalid = true; } getDomainValue(groupIndex, stackIndex) { return `${groupIndex}-${stackIndex}`; } findIndex(value) { let index = 0; for (const key of this._bandRanges.keys()) { if (key === value) return index; index++; } } convert(domainValue) { const { _bandwidth, _bandRanges, _inset, _paddingInnerWidth } = this; let value = _inset; const valueDs = domainValue.split("-"); const valueGroupIndex = Number(valueDs[0]); if (!this._hasFixedWidth) { return super.convert(valueGroupIndex); } for (let i = 0; i < valueGroupIndex; i++) { const stacks = _bandRanges.get(i); if (!stacks) { value += _paddingInnerWidth; continue; } let maxStackWidth = 0; for (const width2 of stacks.values()) { maxStackWidth = Math.max(maxStackWidth, width2 == null ? _bandwidth : width2); } value += maxStackWidth + _paddingInnerWidth; } return value; } invert(_value, _nearest) { return; } getBandCountForUpdate() { return this._bandRanges.size; } update() { if (!this._hasFixedWidth) { return super.update(); } const [r0, r1] = this.range; let { paddingInner } = this; const bandCount = this.getBandCountForUpdate(); if (bandCount === 0) return; let totalBandRange = 0; let bandCountWithUnfixedWidths = bandCount; let bandCountWithOnlyFixedWidths = bandCount; for (const stacks of this._bandRanges.values()) { let maxStackWidth = 0; let hasUnfixed = false; for (const width2 of stacks.values()) { if (width2 == null) { hasUnfixed = true; continue; } maxStackWidth = Math.max(maxStackWidth, width2); } if (hasUnfixed) { bandCountWithOnlyFixedWidths -= 1; } else { bandCountWithUnfixedWidths -= 1; totalBandRange += maxStackWidth; } } if (bandCount === 1) { paddingInner = 0; } const targetRangeDistance = r1 - r0; const paddingInnerWidth = targetRangeDistance / bandCount * paddingInner; const actualRangeDistance = totalBandRange + paddingInnerWidth * (bandCount - 1); const rangeDiff = targetRangeDistance - actualRangeDistance; let inset = r0; let rawBandwidth = bandCountWithUnfixedWidths > 0 && rangeDiff >= 0 ? rangeDiff / bandCountWithUnfixedWidths : targetRangeDistance / bandCount; let bandwidth = rawBandwidth; if (bandCountWithOnlyFixedWidths === bandCount && rangeDiff > 0) { inset += rangeDiff / 2; } const round3 = this.round && Math.floor(bandwidth) > 0; if (round3) { inset = Math.round(inset); bandwidth = Math.round(bandwidth); } if (rangeDiff < 0) { rawBandwidth = 0; bandwidth = 0; } this._inset = inset; this._bandwidth = bandwidth; this._rawBandwidth = rawBandwidth; this._paddingInnerWidth = paddingInnerWidth; } normalizeDomains(..._domains) { return { domain: [], animatable: false }; } toDomain(_value) { return void 0; } }; // packages/ag-charts-community/src/chart/series/seriesStateManager.ts var SeriesStateManager = class { constructor() { this.groups = /* @__PURE__ */ new Map(); this.groupScales = /* @__PURE__ */ new Map(); } registerSeries({ internalId, seriesGrouping, visible, width: width2, type }) { if (!seriesGrouping) return; let group = this.groups.get(type); if (group == null) { group = /* @__PURE__ */ new Map(); this.groups.set(type, group); } group.set(internalId, { grouping: seriesGrouping, visible, width: width2 }); } updateSeries({ internalId, seriesGrouping, visible, width: width2, type }) { if (!seriesGrouping) return; const entry = this.groups.get(type)?.get(internalId); if (entry) { entry.grouping = seriesGrouping; entry.width = width2; entry.visible = visible; } } deregisterSeries({ internalId, type }) { const group = this.groups.get(type); if (group == null) return; group.delete(internalId); if (group.size === 0) { this.groups.delete(type); } } getVisiblePeerGroupIndex({ type, seriesGrouping, visible }) { if (!seriesGrouping) { return { visibleGroupCount: visible ? 1 : 0, visibleSameStackCount: visible ? 1 : 0, index: 0 }; } const visibleGroupsSet = /* @__PURE__ */ new Set(); const visibleSameStackSet = /* @__PURE__ */ new Set(); const group = this.groups.get(type); for (const entry of group?.values() ?? []) { if (!entry.visible) continue; visibleGroupsSet.add(entry.grouping.groupIndex); if (entry.grouping.groupIndex === seriesGrouping.groupIndex) { visibleSameStackSet.add(entry.grouping.stackIndex); } } const visibleGroups = Array.from(visibleGroupsSet); visibleGroups.sort((a, b) => a - b); return { visibleGroupCount: visibleGroups.length, visibleSameStackCount: visibleSameStackSet.size, index: visibleGroups.indexOf(seriesGrouping.groupIndex) }; } updateGroupScale({ type }, bandwidth, axis) { const groupScale = this.groupScales.get(type) ?? new IrregularBandScale(); this.groupScales.set(type, groupScale); groupScale.domain = []; const group = this.groups.get(type); for (const entry of group?.values() ?? []) { if (!entry.visible) continue; groupScale.addBand(entry.grouping.groupIndex, entry.grouping.stackIndex, entry.width); } if (groupScale.domain.length === 0) { groupScale.addBand(0, 0, void 0); } groupScale.range = [0, bandwidth]; if (axis.type === "grouped-category") { groupScale.paddingInner = axis.groupPaddingInner; } else if (axis.type === "category" || axis.type === "unit-time") { groupScale.paddingInner = axis.groupPaddingInner; groupScale.round = groupScale.padding !== 0; } else { groupScale.padding = 0; } groupScale.update(); } getGroupScale({ type }) { return this.groupScales.get(type); } getGroupOffset(series) { const { seriesGrouping } = series; if (!seriesGrouping) return 0; const groupScale = this.getGroupScale(series); if (!groupScale) return 0; const domainValue = groupScale.getDomainValue(seriesGrouping.groupIndex, seriesGrouping.stackIndex); return groupScale.convert(domainValue); } getStackOffset(series, barWidth) { const { seriesGrouping } = series; if (!seriesGrouping) return 0; const group = this.groups.get(series.type); if (!group) return 0; const scale2 = this.getGroupScale(series); if (!scale2) return 0; const stackCount = seriesGrouping.stackCount ?? 0; if (stackCount < 1) return 0; let maxStackWidth = 0; for (const entry of group.values()) { if (!entry.visible) continue; if (entry.grouping.groupIndex !== seriesGrouping.groupIndex) continue; maxStackWidth = Math.max(maxStackWidth, entry.width ?? scale2.bandwidth); } if (maxStackWidth === 0) return 0; return maxStackWidth / 2 - barWidth / 2; } }; // packages/ag-charts-community/src/chart/updateService.ts var UpdateService = class { constructor(updateCallback) { this.updateCallback = updateCallback; this.events = new EventEmitter(); } addListener(eventName, listener) { return this.events.on(eventName, listener); } destroy() { this.events.clear(); } update(type = 0 /* FULL */, options) { this.updateCallback(type, options); } dispatchUpdateComplete(apiUpdate, wasShortcut) { this.events.emit("update-complete", { type: "update-complete", apiUpdate, wasShortcut }); } dispatchPreDomUpdate() { this.events.emit("pre-dom-update", { type: "pre-dom-update" }); } dispatchPreSeriesUpdate(requiredRangeRatio, requiredRangeDirection) { this.events.emit("pre-series-update", { type: "pre-series-update", requiredRangeRatio, requiredRangeDirection }); } dispatchPreSceneRender() { this.events.emit("pre-scene-render", { type: "pre-scene-render" }); } dispatchProcessData({ series }) { this.events.emit("process-data", { type: "process-data", series }); } }; // packages/ag-charts-community/src/chart/chartContext.ts var ChartContext = class { constructor(chart, vars) { this.eventsHub = new EventEmitter(); this.callbackCache = new CallbackCache(); this.highlightManager = new HighlightManager(this.eventsHub); this.formatManager = new FormatManager(); this.layoutManager = new LayoutManager(this.eventsHub); this.localeManager = new LocaleManager(this.eventsHub); this.seriesStateManager = new SeriesStateManager(); this.stateManager = new StateManager(); this.seriesLabelLayoutManager = new SeriesLabelLayoutManager(); this.cleanup = new CleanupRegistry(); const { scene, root, syncManager, container, fireEvent, updateCallback, updateMutex, styleContainer, skipCss, chartType, domMode, withDragInterpretation } = vars; this.chartService = chart; this.syncManager = syncManager; this.domManager = new DOMManager( this.eventsHub, this.chartService, container, styleContainer, skipCss, domMode ); this.widgets = new WidgetSet(this.domManager, { withDragInterpretation }); const canvasElement = this.domManager.addChild( "canvas", "scene-canvas", scene?.canvas.element ); this.scene = scene ?? new Scene({ canvasElement }); this.scene.setRoot(root); this.cleanup.register( this.scene.on("scene-changed", () => { this.updateService.update(9 /* SCENE_RENDER */); }) ); this.axisManager = new AxisManager(this.eventsHub, root); this.legendManager = new LegendManager(this.eventsHub); this.annotationManager = new AnnotationManager(this.eventsHub, chart.annotationRoot, fireEvent); this.chartTypeOriginator = new ChartTypeOriginator(chart); this.interactionManager = new InteractionManager(); this.contextMenuRegistry = new ContextMenuRegistry(this.eventsHub); this.optionsGraphService = new OptionsGraphService(); this.updateService = new UpdateService(updateCallback); this.activeManager = new ActiveManager( this.chartService, this.eventsHub, this.updateService, this.interactionManager, fireEvent ); this.proxyInteractionService = new ProxyInteractionService(this.eventsHub, this.localeManager, this.domManager); this.fontManager = new FontManager(this.domManager, this.updateService); this.historyManager = new HistoryManager(this.eventsHub); this.animationManager = new AnimationManager(this.interactionManager, updateMutex); this.dataService = new DataService(this.eventsHub, chart, this.animationManager); this.tooltipManager = new TooltipManager(this.eventsHub, this.localeManager, this.domManager, chart.tooltip); this.zoomManager = new ZoomManager(this.eventsHub, this.updateService, fireEvent); for (const module2 of moduleRegistry_exports.listModulesByType("plugin" /* Plugin */)) { if (!module2.chartType || module2.chartType === chartType) { module2.patchContext?.(this); } } } destroy() { this.animationManager.destroy(); this.axisManager.destroy(); this.callbackCache.invalidateCache(); this.domManager.destroy(); this.fontManager.destroy(); this.proxyInteractionService.destroy(); this.tooltipManager.destroy(); this.zoomManager.destroy(); this.widgets.destroy(); this.cleanup.flush(); } }; // packages/ag-charts-community/src/chart/chartHighlight.ts var ChartHighlight = class extends BaseProperties { constructor() { super(...arguments); this.range = "tooltip"; this.drawingMode = "cutout"; } }; __decorateClass([ addFakeTransformToInstanceProperty ], ChartHighlight.prototype, "range", 2); __decorateClass([ addFakeTransformToInstanceProperty ], ChartHighlight.prototype, "drawingMode", 2); // packages/ag-charts-community/src/chart/data/caching.ts function setsEqual(a, b) { if (a.size !== b.size) return false; for (const value of a) { if (!b.has(value)) return false; } return true; } function idsMapEqual(a, b) { if (a == null || b == null) return a === b; if (a.size !== b.size) return false; for (const [key, aValue] of a) { const bValue = b.get(key); if (bValue == null) return false; if (!setsEqual(aValue, bValue)) return false; } return true; } function propsEqual(a, b) { if (a.length !== b.length) return false; for (let i = 0; i < a.length; i += 1) { const { type: typeA, idsMap: idsMapA, scopes: scopesA, data: dataA, ...propA } = a[i]; const { type: typeB, idsMap: idsMapB, scopes: scopesB, data: dataB, ...propB } = b[i]; if (typeA !== typeB) return false; if (scopesA && scopesB && !arraysEqual(scopesA, scopesB)) return false; if (dataA && dataB && dataA !== dataB) return false; if (!objectsEqual(propA, propB) || !idsMapEqual(idsMapA, idsMapB)) return false; } return true; } function optsEqual(a, b) { const { props: propsA, ...restA } = a; const { props: propsB, ...restB } = b; return objectsEqual(restA, restB) && propsEqual(propsA, propsB); } function canReuseCachedData(cachedDataItem, dataSet, ids, opts) { if (dataSet !== cachedDataItem.dataSet) { return false; } return arraysEqual(ids, cachedDataItem.ids) && optsEqual(opts, cachedDataItem.opts); } // packages/ag-charts-community/src/chart/data/dataController.ts function getPropertyKeys(props) { return props.filter((p) => p.type === "key").map((p) => p.property).join(";"); } var _DataController = class _DataController { constructor(mode, suppressFieldDotNotation, eventsHub) { this.mode = mode; this.suppressFieldDotNotation = suppressFieldDotNotation; this.eventsHub = eventsHub; this.debug = debugLogger_exports.create(true, "data-model"); this.requested = []; this.status = "setup"; } async request(id, dataSet, opts) { if (this.status !== "setup") { throw new Error(`AG Charts - data request after data setup phase.`); } return new Promise((resolve, reject) => { this.requested.push({ id, opts, dataSet, resolve, reject }); }); } execute(cachedData) { if (this.status !== "setup") { throw new Error(`AG Charts - data request after data setup phase.`); } this.status = "executed"; const dataSets = /* @__PURE__ */ new Map(); for (const request of this.requested) { if (request.dataSet.hasPendingTransactions()) { dataSets.set(request.dataSet, request.dataSet.getChangeDescription()); } request.dataSet.commitPendingTransactions(); } this.debug("DataController.execute() - requested", this.requested); const valid = this.validateRequests(this.requested); this.debug("DataController.execute() - validated", valid); const merged = this.mergeRequested(valid); this.debug("DataController.execute() - merged", merged); if (this.debug.check()) { getWindow().processedData = []; } const nextCachedData = []; for (const { dataSet, ids, opts, resolves, rejects } of merged) { let cachePredicateFn2 = function(cacheItem) { return canReuseCachedData(cacheItem, dataSet, ids, opts); }; var cachePredicateFn = cachePredicateFn2; const reusableCache = cachedData?.find(cachePredicateFn2); const resolveResult = (dataModel2, processedData2) => { if (this.debug.check()) { getWindow("processedData").push(processedData2); } if (processedData2 == null) { for (const cb of rejects) { cb(new Error(`AG Charts - no processed data generated`)); } return; } nextCachedData.push({ opts, dataSet, dataLength: dataSet.data.length, ids, dataModel: dataModel2, processedData: processedData2 }); for (const resolve of resolves) { resolve({ dataModel: dataModel2, processedData: processedData2 }); } }; const fullReprocess = () => { try { const dataModel2 = new DataModel( opts, this.mode, this.suppressFieldDotNotation, this.eventsHub ); const sources = new Map(valid.map((v) => [v.id, v.dataSet])); const processedData2 = dataModel2.processData(sources); resolveResult(dataModel2, processedData2); return dataModel2; } catch (error2) { for (const cb of rejects) { cb(error2); } } }; if (reusableCache == null) { fullReprocess(); continue; } const { dataModel, processedData } = reusableCache; const changeDescription = dataSets.get(dataSet); if (processedData && changeDescription && dataModel.isReprocessingSupported(processedData)) { this.debug("DataController.execute() - reprocessing data", processedData, dataSet); dataModel.reprocessData(processedData, dataSets); if (debugLogger_exports.check("data-model:reprocess-diff")) { const baselineModel = new DataModel( opts, this.mode, this.suppressFieldDotNotation, this.eventsHub ); const sources = new Map(valid.map((v) => [v.id, v.dataSet])); const baselineData = baselineModel.processData(sources); const reprocessedJson = JSON.parse(JSON.stringify(processedData, _DataController.jsonReplacer)); const baselineJson = JSON.parse(JSON.stringify(baselineData, _DataController.jsonReplacer)); delete reprocessedJson.time; delete reprocessedJson.optimizations; delete baselineJson.time; delete baselineJson.optimizations; const diff2 = jsonDiff(baselineJson, reprocessedJson); if (diff2) { logger_exports.log("\u26A0\uFE0F DATA-MODEL REPROCESS DIFF DETECTED \u26A0\uFE0F"); logger_exports.log("Difference between incremental update and full reprocess:"); logger_exports.log(""); logger_exports.log("BASELINE (full reprocess):"); logger_exports.log(JSON.stringify(baselineJson, null, 2)); logger_exports.log(""); logger_exports.log("REPROCESSED (incremental update):"); logger_exports.log(JSON.stringify(reprocessedJson, null, 2)); logger_exports.log(""); logger_exports.log("DIFF (what changed):"); logger_exports.log(JSON.stringify(diff2, null, 2)); } else { logger_exports.log("\u2705 Data-model reprocess matches baseline (no diff)"); } } resolveResult(dataModel, processedData); continue; } fullReprocess(); } return nextCachedData; } validateRequests(requested) { const valid = []; for (const [index, request] of requested.entries()) { if (index > 0 && request.dataSet.data.length !== requested[0].dataSet.data.length && request.opts.groupByData === false && request.opts.groupByKeys === false) { request.reject( new Error("all series[].data arrays must be of the same length and have matching keys.") ); } else { valid.push(request); } } return valid; } mergeRequested(requested) { const grouped = []; for (const request of requested) { const match = grouped.find(_DataController.groupMatch(request)); if (match) { match.push(request); } else { grouped.push([request]); } } return grouped.map(_DataController.mergeRequests); } static groupMatch({ dataSet, opts }) { const { groupByData, groupByKeys = false, groupByFn, props } = opts; const propsKeys = getPropertyKeys(props); return ([group]) => (groupByData === false || group.dataSet === dataSet) && (group.opts.groupByKeys ?? false) === groupByKeys && group.opts.groupByFn === groupByFn && getPropertyKeys(group.opts.props) === propsKeys; } static mergeRequests(requests) { const result = { ids: [], rejects: [], resolves: [], dataSet: requests[0].dataSet, opts: { ...requests[0].opts, props: [] } }; const optsByTypeAndDataId = /* @__PURE__ */ new Map(); const dataIds = /* @__PURE__ */ new Map(); let nextDataId = 0; for (const request of requests) { const { id, dataSet, resolve, reject, opts: { props, ...opts } } = request; result.ids.push(id); result.rejects.push(reject); result.resolves.push(resolve); result.dataSet ?? (result.dataSet = dataSet); result.opts ?? (result.opts = { ...opts, props: [] }); for (const prop of props) { const clone2 = { ...prop, scopes: [id], data: dataSet.data }; _DataController.createIdsMap(id, clone2); let dataId; if (_DataController.crossScopeMergableTypes.has(clone2.type)) { dataId = -1; } else if (dataIds.has(dataSet.data)) { dataId = dataIds.get(dataSet.data); } else { dataId = nextDataId++; dataIds.set(dataSet.data, dataId); } const matchKey = `${clone2.type}-${dataId}-${clone2.groupId}`; const matches = optsByTypeAndDataId.get(matchKey); const match = matches?.find((existing) => _DataController.deepEqual(existing, clone2)); if (matches == null) { result.opts.props.push(clone2); optsByTypeAndDataId.set(matchKey, [clone2]); continue; } else if (match == null) { result.opts.props.push(clone2); matches.push(clone2); continue; } if (clone2.scopes != null) { match.scopes ?? (match.scopes = []); match.scopes.push(...clone2.scopes); } if ((match.type === "key" || match.type === "value") && clone2.idsMap?.size) { match.idsMap ?? (match.idsMap = /* @__PURE__ */ new Map()); _DataController.mergeIdsMap(clone2.idsMap, match.idsMap); } } } return result; } static mergeIdsMap(fromMap, toMap) { for (const [scope, ids] of fromMap) { const toMapValue = toMap.get(scope); if (toMapValue == null) { toMap.set(scope, new Set(ids)); } else { for (const id of ids) { toMapValue.add(id); } } } } static createIdsMap(scope, prop) { if (prop.id == null) return; prop.idsMap ?? (prop.idsMap = /* @__PURE__ */ new Map()); if (prop.idsMap.has(scope)) { prop.idsMap.get(scope).add(prop.id); } else { prop.idsMap.set(scope, /* @__PURE__ */ new Set([prop.id])); } } static deepEqual(a, b) { if (a === b) { return true; } if (a && b && typeof a == "object" && typeof b == "object") { if (a.constructor !== b.constructor) { return false; } let i, length2; if (Array.isArray(a)) { length2 = a.length; if (length2 !== b.length) { return false; } for (i = length2 - 1; i >= 0; i--) { if (!_DataController.deepEqual(a[i], b[i])) { return false; } } return true; } const keys = Object.keys(a); length2 = keys.length; if (length2 !== Object.keys(b).length) { return false; } for (i = length2 - 1; i >= 0; i--) { const key = keys[i]; if (!_DataController.skipKeys.has(key) && (!Object.hasOwn(b, key) || !_DataController.deepEqual(a[key], b[key]))) { return false; } } return true; } return false; } /** JSON replacer for serializing non-JSON-serializable objects like Map and Set */ static jsonReplacer(_key, value) { if (value instanceof Map) { return { __type: "Map", value: Array.from(value.entries()) }; } if (value instanceof Set) { return { __type: "Set", value: Array.from(value) }; } return value; } }; _DataController.crossScopeMergableTypes = /* @__PURE__ */ new Set(["key", "group-value-processor"]); // optimized version of deep equality for `mergeRequests` which can potentially loop over 1M times _DataController.skipKeys = /* @__PURE__ */ new Set(["id", "idsMap", "type", "scopes", "data"]); var DataController = _DataController; // packages/ag-charts-community/src/chart/interaction/syncManager.ts var _SyncManager = class _SyncManager { constructor(chart) { this.chart = chart; } subscribe(groupId = _SyncManager.DEFAULT_GROUP) { let syncGroup = this.get(groupId); if (!syncGroup) { syncGroup = { members: /* @__PURE__ */ new Set() }; _SyncManager.chartsGroups.set(groupId, syncGroup); } syncGroup.members.add(this.chart); return this; } unsubscribe(groupId = _SyncManager.DEFAULT_GROUP) { const groupState = this.get(groupId); groupState?.members.delete(this.chart); delete groupState?.domains?.x?.sources?.[this.chart.id]; delete groupState?.domains?.y?.sources?.[this.chart.id]; return this; } getChart() { return this.chart; } getGroupState(groupId = _SyncManager.DEFAULT_GROUP) { return this.get(groupId); } getGroupMembers(groupId = _SyncManager.DEFAULT_GROUP) { const syncGroup = this.get(groupId); return syncGroup ? Array.from(syncGroup.members) : []; } getGroupSiblings(groupId = _SyncManager.DEFAULT_GROUP) { return this.getGroupMembers(groupId).filter((chart) => chart !== this.chart); } getGroupSyncMode(groupId = _SyncManager.DEFAULT_GROUP) { if (this.getGroupMembers(groupId).some((c) => c.series.length > 1)) { return "multi-series"; } return "single-series"; } get(groupId) { return _SyncManager.chartsGroups.get(groupId); } }; _SyncManager.chartsGroups = /* @__PURE__ */ new Map(); _SyncManager.DEFAULT_GROUP = Symbol("sync-group-default"); var SyncManager = _SyncManager; // packages/ag-charts-community/src/chart/keyboard.ts var Keyboard = class extends BaseProperties { constructor() { super(...arguments); this.enabled = false; } }; __decorateClass([ addFakeTransformToInstanceProperty ], Keyboard.prototype, "enabled", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Keyboard.prototype, "tabIndex", 2); // packages/ag-charts-community/src/chart/mapping/prepareAxis.ts var CartesianAxisPositions = ["right", "top", "left", "bottom"]; function isAxisPosition(position) { return typeof position === "string" && CartesianAxisPositions.includes(position); } function guessInvalidPositions(axes) { const invalidAxes = []; const usedPositions = []; const guesses = [...CartesianAxisPositions]; for (const axis of axes) { if (axis instanceof CartesianAxis) { if (isAxisPosition(axis.position)) { usedPositions.push(axis.position); } else { invalidAxes.push(axis); } } } for (const axis of invalidAxes) { let nextGuess; do { nextGuess = guesses.pop(); } while (nextGuess && usedPositions.includes(nextGuess)); if (nextGuess == null) break; axis.position = nextGuess; } } // packages/ag-charts-community/src/chart/mapping/prepareSeries.ts var DEFAULT_MATCHING_KEYS = ["direction", "xKey", "yKey", "sizeKey", "angleKey", "radiusKey", "normalizedTo"]; function matchSeriesOptions(series, optSeries, oldOptsSeries) { const matchingKeysCache = /* @__PURE__ */ new Map(); const getMatchingKeys = (type) => { if (type === void 0) { return DEFAULT_MATCHING_KEYS; } if (matchingKeysCache.has(type)) return matchingKeysCache.get(type); const matchingKeys = moduleRegistry_exports.getSeriesModule(type)?.matchingKeys ?? DEFAULT_MATCHING_KEYS; matchingKeysCache.set(type, matchingKeys); return matchingKeys; }; const generateKey = (type, i, opts) => { const matchingKeys = getMatchingKeys(type); const result = [type]; for (const key of matchingKeys) { if (key in i && i[key] != null) result.push(`${key}=${i[key]}`); } if (opts?.seriesGrouping) { result.push(`seriesGrouping.groupId=${opts?.seriesGrouping.groupId}`); } return result.join(";"); }; const seriesMap = /* @__PURE__ */ new Map(); let idx = 0; for (const s of series) { const key = generateKey(s.type, s.properties, oldOptsSeries?.[idx]); if (!seriesMap.has(key)) { seriesMap.set(key, []); } seriesMap.get(key)?.push([s, idx++]); } const optsMap = /* @__PURE__ */ new Map(); idx = 0; for (const o of optSeries) { const key = generateKey(o.type, o, o); if (!optsMap.has(key)) { optsMap.set(key, []); } optsMap.get(key)?.push([o, idx++]); } const overlap = [...seriesMap.keys()].some((k) => optsMap.has(k)); if (!overlap) { return { status: "no-overlap", oldKeys: seriesMap.keys(), newKeys: optsMap.keys() }; } const changes = []; for (const [key, optsTuples] of optsMap.entries()) { for (const [opts, targetIdx] of optsTuples) { const seriesArray = seriesMap.get(key); if (seriesArray == null || seriesArray.length < 1) { changes.push({ opts, targetIdx, idx: targetIdx, status: "add" }); seriesMap.delete(key); continue; } const [outputSeries, currentIdx] = seriesArray.shift(); const previousOpts = oldOptsSeries?.[currentIdx] ?? {}; const diff2 = jsonDiff(previousOpts, opts ?? {}); const { groupIndex, stackIndex } = diff2?.seriesGrouping ?? {}; if (groupIndex != null || stackIndex != null) { changes.push({ opts, series: outputSeries, diff: diff2, targetIdx, idx: currentIdx, status: "series-grouping" }); } else if (diff2) { changes.push({ opts, series: outputSeries, diff: diff2, targetIdx, idx: currentIdx, status: "update" }); } else { changes.push({ opts, series: outputSeries, targetIdx, idx: currentIdx, status: "no-op" }); } if (seriesArray.length === 0) { seriesMap.delete(key); } } } for (const seriesArray of seriesMap.values()) { for (const [outputSeries, currentIdx] of seriesArray) { changes.push({ series: outputSeries, idx: currentIdx, targetIdx: -1, status: "remove" }); } } return { status: "overlap", changes }; } // packages/ag-charts-community/src/chart/factory/expectedModules.ts var ExpectedModules = new Map( [ // Chart types { type: "chart", name: "cartesian", moduleId: "CartesianChartModule" }, { type: "chart", name: "standalone", moduleId: "StandaloneChartModule", enterprise: true }, { type: "chart", name: "polar", moduleId: "PolarChartModule" }, { type: "chart", name: "topology", moduleId: "TopologyChartModule", enterprise: true }, // Axis types { type: "axis", name: "number", chartType: "cartesian", moduleId: "NumberAxisModule" }, { type: "axis", name: "log", chartType: "cartesian", moduleId: "LogAxisModule" }, { type: "axis", name: "time", chartType: "cartesian", moduleId: "TimeAxisModule" }, { type: "axis", name: "unit-time", chartType: "cartesian", moduleId: "UnitTimeAxisModule" }, { type: "axis", name: "category", chartType: "cartesian", moduleId: "CategoryAxisModule" }, { type: "axis", name: "grouped-category", chartType: "cartesian", moduleId: "GroupedCategoryAxisModule" }, { type: "axis", name: "ordinal-time", chartType: "cartesian", enterprise: true, moduleId: "OrdinalTimeAxisModule" }, { type: "axis", name: "angle-category", chartType: "polar", enterprise: true, moduleId: "AngleCategoryAxisModule" }, { type: "axis", name: "angle-number", chartType: "polar", enterprise: true, moduleId: "AngleNumberAxisModule" }, { type: "axis", name: "radius-category", chartType: "polar", enterprise: true, moduleId: "RadiusCategoryAxisModule" }, { type: "axis", name: "radius-number", chartType: "polar", enterprise: true, moduleId: "RadiusNumberAxisModule" }, // Series types { type: "series", name: "bar", chartType: "cartesian", moduleId: "BarSeriesModule" }, { type: "series", name: "scatter", chartType: "cartesian", moduleId: "ScatterSeriesModule" }, { type: "series", name: "bubble", chartType: "cartesian", moduleId: "BubbleSeriesModule" }, { type: "series", name: "line", chartType: "cartesian", moduleId: "LineSeriesModule" }, { type: "series", name: "area", chartType: "cartesian", moduleId: "AreaSeriesModule" }, { type: "series", name: "pie", chartType: "polar", moduleId: "PieSeriesModule" }, { type: "series", name: "donut", chartType: "polar", moduleId: "DonutSeriesModule" }, { type: "series", name: "box-plot", chartType: "cartesian", enterprise: true, moduleId: "BoxPlotSeriesModule" }, { type: "series", name: "candlestick", chartType: "cartesian", enterprise: true, moduleId: "CandlestickSeriesModule" }, { type: "series", name: "cone-funnel", chartType: "cartesian", enterprise: true, moduleId: "ConeFunnelSeriesModule" }, { type: "series", name: "funnel", chartType: "cartesian", enterprise: true, moduleId: "FunnelSeriesModule" }, { type: "series", name: "ohlc", chartType: "cartesian", enterprise: true, moduleId: "OhlcSeriesModule" }, { type: "series", name: "heatmap", chartType: "cartesian", enterprise: true, moduleId: "HeatmapSeriesModule" }, { type: "series", name: "histogram", chartType: "cartesian", // enterprise: true, moduleId: "HistogramSeriesModule" }, { type: "series", name: "range-area", chartType: "cartesian", enterprise: true, moduleId: "RangeAreaSeriesModule" }, { type: "series", name: "range-bar", chartType: "cartesian", enterprise: true, moduleId: "RangeBarSeriesModule" }, { type: "series", name: "waterfall", chartType: "cartesian", enterprise: true, moduleId: "WaterfallSeriesModule" }, { type: "series", name: "nightingale", chartType: "polar", enterprise: true, moduleId: "NightingaleSeriesModule" }, { type: "series", name: "radar-area", chartType: "polar", enterprise: true, moduleId: "RadarAreaSeriesModule" }, { type: "series", name: "radar-line", chartType: "polar", enterprise: true, moduleId: "RadarLineSeriesModule" }, { type: "series", name: "radial-bar", chartType: "polar", enterprise: true, moduleId: "RadialBarSeriesModule" }, { type: "series", name: "radial-column", chartType: "polar", enterprise: true, moduleId: "RadialColumnSeriesModule" }, { type: "series", name: "map-shape", chartType: "topology", enterprise: true, moduleId: "MapShapeSeriesModule" }, { type: "series", name: "map-line", chartType: "topology", enterprise: true, moduleId: "MapLineSeriesModule" }, { type: "series", name: "map-marker", chartType: "topology", enterprise: true, moduleId: "MapMarkerSeriesModule" }, { type: "series", name: "map-shape-background", chartType: "topology", enterprise: true, moduleId: "MapShapeBackgroundSeriesModule" }, { type: "series", name: "map-line-background", chartType: "topology", enterprise: true, moduleId: "MapLineBackgroundSeriesModule" }, { type: "series", name: "pyramid", chartType: "standalone", enterprise: true, moduleId: "PyramidSeriesModule" }, { type: "series", name: "linear-gauge", chartType: "standalone", enterprise: true, moduleId: "LinearGaugeModule" }, { type: "series", name: "radial-gauge", chartType: "standalone", enterprise: true, moduleId: "RadialGaugeModule" }, { type: "series", name: "sunburst", chartType: "standalone", enterprise: true, moduleId: "SunburstSeriesModule" }, { type: "series", name: "treemap", chartType: "standalone", enterprise: true, moduleId: "TreemapSeriesModule" }, { type: "series", name: "chord", chartType: "standalone", enterprise: true, moduleId: "ChordSeriesModule" }, { type: "series", name: "sankey", chartType: "standalone", enterprise: true, moduleId: "SankeySeriesModule" }, // Plugins { type: "plugin", name: "animation", enterprise: true, moduleId: "AnimationModule" }, { type: "plugin", name: "annotations", chartType: "cartesian", enterprise: true, moduleId: "AnnotationsModule" }, { type: "plugin", name: "legend", moduleId: "LegendModule" }, { type: "plugin", name: "locale", moduleId: "LocaleModule" }, { type: "plugin", name: "chartToolbar", chartType: "cartesian", enterprise: true, moduleId: "ChartToolbarModule" }, { type: "plugin", name: "contextMenu", enterprise: true, moduleId: "ContextMenuModule" }, { type: "plugin", name: "statusBar", chartType: "cartesian", enterprise: true, moduleId: "StatusBarModule" }, { type: "plugin", name: "dataSource", enterprise: true, moduleId: "DataSourceModule" }, { type: "plugin", name: "sync", chartType: "cartesian", enterprise: true, moduleId: "SyncModule" }, { type: "plugin", name: "ranges", chartType: "cartesian", enterprise: true, moduleId: "RangesModule" }, { type: "plugin", name: "zoom", enterprise: true, moduleId: "ZoomModule" }, { type: "plugin", name: "flashOnUpdate", enterprise: true, moduleId: "FlashOnUpdateModule" }, { type: "plugin", name: "gradientLegend", enterprise: true, moduleId: "GradientLegendModule" }, { type: "plugin", name: "navigator", chartType: "cartesian", enterprise: true, moduleId: "NavigatorModule" }, { type: "plugin", name: "scrollbar", chartType: "cartesian", enterprise: true, moduleId: "ScrollbarModule" }, { type: "axis:plugin", name: "crosshair", chartType: "cartesian", enterprise: true, moduleId: "CrosshairModule" }, { type: "axis:plugin", name: "bandHighlight", chartType: "cartesian", enterprise: true, moduleId: "BandHighlightModule" }, { type: "series:plugin", name: "errorBar", chartType: "cartesian", enterprise: true, moduleId: "ErrorBarsModule" }, { type: "preset", name: "gauge-preset", chartType: "standalone", enterprise: true, moduleId: "GaugePresetModule" }, { type: "preset", name: "price-volume", chartType: "cartesian", enterprise: true, moduleId: "PriceVolumePresetModule" }, { type: "preset", name: "sparkline", moduleId: "SparklinePresetModule" } ].map((m) => [m.name, m]) ); function getSeriesExpectedChartType(seriesName) { const expectedModule = ExpectedModules.get(seriesName); return expectedModule?.type === "series" /* Series */ ? expectedModule.chartType : void 0; } // packages/ag-charts-community/src/chart/mapping/types.ts function detectChartType(input) { const mainSeriesType = input.series?.[0]?.type ?? "line"; return moduleRegistry_exports.getSeriesModule(mainSeriesType)?.chartType ?? getSeriesExpectedChartType(mainSeriesType) ?? "unknown"; } function isAgCartesianChartOptions(input) { return detectChartType(input) === "cartesian"; } // packages/ag-charts-community/src/chart/modulesManager.ts var ModulesManager = class extends ModuleMap { *legends() { for (const module2 of moduleRegistry_exports.listModulesByType("plugin" /* Plugin */)) { if (module2.name === "legend" || module2.name === "gradientLegend") { yield { legendType: module2.name === "legend" ? "category" : "gradient", legend: this.getModule(module2.name) }; } } } }; // packages/ag-charts-community/src/chart/overlay/overlay.ts var DEFAULT_OVERLAY_CLASS = "ag-charts-overlay"; var DEFAULT_OVERLAY_DARK_CLASS = "ag-charts-dark-overlay"; var Overlay = class extends BaseProperties { constructor(className, defaultMessageId) { super(); this.className = className; this.defaultMessageId = defaultMessageId; this.enabled = true; } getText(localeManager) { if (isArray(this.text)) { return toPlainText(this.text); } if (this.rendererAsText) { return this.rendererAsText; } return localeManager.t(toTextString(this.text) || this.defaultMessageId); } getElement(callers, animationManager, localeManager, rect2) { this.content?.remove(); this.rendererAsText = void 0; this.focusBox = rect2; if (this.renderer) { const params = {}; const htmlContent = callWithContext(callers, this.renderer, params); if (isHTMLElement(htmlContent)) { this.content = htmlContent; } else { const tempDiv = createElement("div"); tempDiv.innerHTML = htmlContent; const { firstElementChild } = tempDiv; if (isHTMLElement(firstElementChild) && tempDiv.childElementCount === 1) { this.content = firstElementChild; } else { this.content = tempDiv; } } this.rendererAsText = this.content?.textContent?.trim() ?? void 0; } else { const content = createElement("div", { display: "flex", alignItems: "center", justifyContent: "center", boxSizing: "border-box", height: "100%", margin: "8px", fontFamily: "var(--ag-charts-font-family)", fontSize: "var(--ag-charts-font-size)", fontWeight: "var(--ag-charts-font-weight)" }); if (isArray(this.text)) { const container = createElement("div"); for (const segment of this.text) { const el = createElement("span", { color: segment.color, fontSize: `${segment.fontSize}px`, fontFamily: segment.fontFamily ?? "inherit", fontWeight: String(segment.fontWeight), fontStyle: segment.fontStyle }); el.innerText = toTextString(segment.text); container.appendChild(el); } content.appendChild(container); } else { content.innerText = this.getText(localeManager); } this.content = content; this.content.classList.add(this.className); animationManager?.animate({ from: 0, to: 1, id: "overlay", phase: "add", groupId: "opacity", onUpdate(value) { content.style.opacity = String(value); }, onStop() { content.style.opacity = "1"; } }); } return this.content; } removeElement(cleanup = () => this.content?.remove(), animationManager) { if (!this.content) return; if (animationManager) { const { content } = this; animationManager.animate({ from: 1, to: 0, phase: "remove", id: "overlay", groupId: "opacity", onUpdate(value) { content.style.opacity = String(value); }, onStop() { cleanup?.(); } }); } else { cleanup?.(); } this.content = void 0; this.focusBox = void 0; } }; __decorateClass([ addFakeTransformToInstanceProperty ], Overlay.prototype, "enabled", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Overlay.prototype, "text", 2); __decorateClass([ addFakeTransformToInstanceProperty ], Overlay.prototype, "renderer", 2); // packages/ag-charts-community/src/chart/overlay/chartOverlays.ts var ChartOverlays = class extends BaseProperties { constructor() { super(...arguments); this.darkTheme = false; this.loading = new Overlay("ag-charts-loading-overlay", "overlayLoadingData"); this.noData = new Overlay("ag-charts-no-data-overlay", "overlayNoData"); this.noVisibleSeries = new Overlay("ag-charts-no-visible-series", "overlayNoVisibleSeries"); this.unsupportedBrowser = new Overlay("ag-charts-unsupported-browser", "overlayUnsupportedBrowser"); } getFocusInfo(localeManager) { for (const overlay of [this.loading, this.noData, this.noVisibleSeries, this.unsupportedBrowser]) { if (overlay.focusBox !== void 0) { return { text: overlay.getText(localeManager), rect: overlay.focusBox }; } } return void 0; } destroy() { this.loading.removeElement(); this.noData.removeElement(); this.noVisibleSeries.removeElement(); this.unsupportedBrowser.removeElement(); } }; __decorateClass([ addFakeTransformToInstanceProperty ], ChartOverlays.prototype, "darkTheme", 2); __decorateClass([ addFakeTransformToInstanceProperty ], ChartOverlays.prototype, "loading", 2); __decorateClass([ addFakeTransformToInstanceProperty ], ChartOverlays.prototype, "noData", 2); __decorateClass([ addFakeTransformToInstanceProperty ], ChartOverlays.prototype, "noVisibleSeries", 2); __decorateClass([ addFakeTransformToInstanceProperty ], ChartOverlays.prototype, "unsupportedBrowser", 2); // packages/ag-charts-community/src/chart/overlay/loadingSpinner.ts function getLoadingSpinner(text, defaultDuration) { const { animationDuration } = PHASE_METADATA["add"]; const duration = animationDuration * defaultDuration; const container = createElement("div", `${DEFAULT_OVERLAY_CLASS}--loading`, { display: "flex", alignItems: "center", justifyContent: "center", flexDirection: "column", height: "100%", boxSizing: "border-box", font: "13px Verdana, sans-serif", // FONT_SIZE.MEDIUM userSelect: "none", animation: `ag-charts-loading ${duration}ms linear 50ms both` }); const matrix = createElement("span", { width: "45px", height: "40px", backgroundImage: [ "linear-gradient(#0000 calc(1 * 100% / 6), #ccc 0 calc(3 * 100% / 6), #0000 0), ", "linear-gradient(#0000 calc(2 * 100% / 6), #ccc 0 calc(4 * 100% / 6), #0000 0), ", "linear-gradient(#0000 calc(3 * 100% / 6), #ccc 0 calc(5 * 100% / 6), #0000 0)" ].join(""), backgroundSize: "10px 400%", backgroundRepeat: "no-repeat", animation: "ag-charts-loading-matrix 1s infinite linear" }); const label = createElement("p", { marginTop: "1em" }); label.innerText = text; const background = createElement("div", `${DEFAULT_OVERLAY_CLASS}__loading-background`, { position: "absolute", inset: "0", opacity: "0.5", zIndex: "-1" }); const animationStyles = createElement("style"); animationStyles.innerText = [ "@keyframes ag-charts-loading { from { opacity: 0 } to { opacity: 1 } }", "@keyframes ag-charts-loading-matrix {", "0% { background-position: 0% 0%, 50% 0%, 100% 0%; }", "100% { background-position: 0% 100%, 50% 100%, 100% 100%; }", "}" ].join(" "); container.replaceChildren(animationStyles, matrix, label, background); return container; } // packages/ag-charts-community/src/chart/series-area/seriesArea.ts var SeriesArea = class extends BaseProperties { constructor(ctx) { super(); this.ctx = ctx; this.rectNode = new Rect(); this.border = new Border(this.rectNode); this.cornerRadius = 0; this.padding = 0; this.cleanup = new CleanupRegistry(); this.node = this.createNode(); this.node.append([this.rectNode]); this.rectNode.fill = void 0; this.cleanup.register( ctx.scene.attachNode(this.node), ctx.eventsHub.on("layout:complete", (e) => this.onLayoutComplete(e)) ); } destroy() { this.cleanup.flush(); } getPadding() { const { border, padding: padding2 } = this; const strokeWidth = border.enabled ? border.strokeWidth : 0; if (typeof padding2 === "number") { const total = padding2 + strokeWidth; return { top: total, right: total, bottom: total, left: total }; } return { top: (padding2.top ?? 0) + strokeWidth, right: (padding2.right ?? 0) + strokeWidth, bottom: (padding2.bottom ?? 0) + strokeWidth, left: (padding2.left ?? 0) + strokeWidth }; } createNode() { return new Group({ name: "series-area-container", zIndex: 4 /* SERIES_AREA_CONTAINER */ }); } onLayoutComplete(event) { const { x, y, width: width2, height: height2 } = event.series.paddedRect; this.rectNode.x = x; this.rectNode.y = y; this.rectNode.width = width2; this.rectNode.height = height2; } }; __decorateClass([ addFakeTransformToInstanceProperty ], SeriesArea.prototype, "border", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SeriesArea.prototype, "clip", 2); __decorateClass([ ProxyPropertyOnWrite("rectNode", "cornerRadius"), addFakeTransformToInstanceProperty ], SeriesArea.prototype, "cornerRadius", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SeriesArea.prototype, "padding", 2); // packages/ag-charts-community/src/dom/focusIndicator.ts var FocusIndicator = class { constructor(swapChain) { this.swapChain = swapChain; this.hasBeenActivated = false; this.div = createElement("div"); this.svg = createSvgElement("svg"); this.outerPath = createSvgElement("path"); this.innerPath = createSvgElement("path"); this.svg.append(this.outerPath); this.svg.append(this.innerPath); this.outerPath.classList.add("ag-charts-focus-svg-outer-path"); this.innerPath.classList.add("ag-charts-focus-svg-inner-path"); this.element = createElement("div", "ag-charts-focus-indicator"); this.element.ariaHidden = "true"; this.element.append(this.svg); this.swapChain.addListener("swap", (parent) => this.onSwap(parent)); } clear() { } update(focus, rect2, clip) { if (rect2 == null) return; if (focus instanceof Path) { const transform = (localX, localY) => { let { x, y } = Transformable.toCanvasPoint(focus, localX, localY); x -= rect2.x ?? 0; y -= rect2.y ?? 0; return { x, y }; }; const d = focus.svgPathData(transform); this.outerPath.setAttribute("d", d); this.innerPath.setAttribute("d", d); this.show(this.svg); } else { let bbox; if (clip) { const x0 = Math.max(focus.x - rect2.x, 0); const y0 = Math.max(focus.y - rect2.y, 0); const x1 = Math.min(focus.x + focus.width - rect2.x, rect2.width); const y1 = Math.min(focus.y + focus.height - rect2.y, rect2.height); bbox = new BBox(x0, y0, x1 - x0, y1 - y0); } else { bbox = new BBox(focus.x - rect2.x, focus.y - rect2.y, focus.width, focus.height); } setElementBBox(this.div, bbox); this.show(this.div); } } onSwap(newParent) { if (newParent === this.element.parentElement) return; this.element.remove(); newParent.appendChild(this.element); this.overrideFocusVisible(this.focusVisible); } show(child) { this.hasBeenActivated = true; this.element.innerHTML = ""; this.element.append(child); } overrideFocusVisible(focusVisible) { this.focusVisible = focusVisible; const opacity = { true: "1", false: "0", undefined: "" }; const parent = this.element.parentElement; parent?.style.setProperty("opacity", opacity[`${focusVisible}`]); } // Get the `:focus-visible` CSS state. isFocusVisible(force = false) { if (!force && !this.hasBeenActivated) return false; const parent = this.element.parentElement; return parent != null && getWindow().getComputedStyle(parent).opacity === "1"; } }; // packages/ag-charts-community/src/dom/focusSwapChain.ts var FocusSwapChain = class { constructor(label1, label2, announcerRole, initialAltText) { this.label1 = label1; this.label2 = label2; this.hasFocus = false; this.skipDispatch = false; this.listeners = { blur: [], focus: [], swap: [] }; this.onBlur = (e) => { setElementStyle(e.target, "pointer-events", void 0); return !this.skipDispatch && this.dispatch("blur", e); }; this.onFocus = (e) => { setElementStyle(e.target, "pointer-events", "auto"); return !this.skipDispatch && this.dispatch("focus", e); }; setAttribute(this.label1, "id", createElementId()); setAttribute(this.label2, "id", createElementId()); setElementStyle(this.label1, "display", "none"); setElementStyle(this.label2, "display", "none"); this.label1.textContent = initialAltText; this.label2.textContent = initialAltText; this.activeAnnouncer = this.createAnnouncer(announcerRole); this.inactiveAnnouncer = this.createAnnouncer(announcerRole); setAttribute(this.activeAnnouncer, "tabindex", 0); this.label2.insertAdjacentElement("afterend", this.activeAnnouncer); this.label2.insertAdjacentElement("afterend", this.inactiveAnnouncer); this.swap(initialAltText); } createAnnouncer(role) { const announcer = createElement("div"); announcer.role = role; announcer.className = "ag-charts-swapchain"; announcer.addEventListener("blur", this.onBlur); announcer.addEventListener("focus", this.onFocus); return announcer; } destroy() { for (const announcer of [this.activeAnnouncer, this.inactiveAnnouncer]) { announcer.removeEventListener("blur", this.onBlur); announcer.removeEventListener("focus", this.onFocus); announcer.remove(); } } focus(opts) { this.focusOptions = opts; this.activeAnnouncer.focus(opts); this.focusOptions = void 0; } update(newLabel) { this.skipDispatch = true; this.swap(newLabel); if (this.hasFocus) { this.activeAnnouncer.focus(this.focusOptions); } this.skipDispatch = false; } addListener(type, handler) { this.listeners[type].push(handler); if (type === "swap") { const swapHandler = handler; swapHandler(this.activeAnnouncer); } } dispatch(type, param) { if (type === "focus") this.hasFocus = true; else if (type === "blur") this.hasFocus = false; for (const fn of this.listeners[type]) { fn(param); } } swap(newLabel) { const userTabIndex = this.activeAnnouncer.tabIndex; this.label2.textContent = newLabel; [this.inactiveAnnouncer, this.activeAnnouncer] = [this.activeAnnouncer, this.inactiveAnnouncer]; [this.label1, this.label2] = [this.label2, this.label1]; setAttributes(this.inactiveAnnouncer, { "aria-labelledby": this.label1.id, "aria-hidden": true, tabindex: void 0 }); setAttributes(this.activeAnnouncer, { "aria-labelledby": this.label1.id, "aria-hidden": false, tabindex: userTabIndex }); this.dispatch("swap", this.activeAnnouncer); } }; // packages/ag-charts-community/src/chart/interaction/keyBindings.ts var KEY_BINDINGS = { arrowdown: { bindings: [{ code: "ArrowDown" }] }, arrowleft: { bindings: [{ code: "ArrowLeft" }] }, arrowright: { bindings: [{ code: "ArrowRight" }] }, arrowup: { bindings: [{ code: "ArrowUp" }] }, delete: { bindings: [{ key: "Backspace" }, { key: "Delete" }], activatesFocusIndicator: false }, redo: { bindings: [ { key: "y", ctrlOrMeta: true }, { key: "z", ctrlOrMeta: true, shift: true } ], activatesFocusIndicator: false }, undo: { bindings: [{ key: "z", ctrlOrMeta: true }], activatesFocusIndicator: false }, submit: { bindings: [{ key: "Enter" }, { code: "Enter" }, { code: "Space" }] }, zoomin: { bindings: [{ key: "+" }, { code: "ZoomIn" }, { code: "Add" }], activatesFocusIndicator: false }, zoomout: { bindings: [{ key: "-" }, { code: "ZoomOut" }, { code: "Substract" }], activatesFocusIndicator: false } }; function matchesKeyBinding(e, bindings) { for (const kb of bindings) { if ("code" in kb) { if (kb.code === e.code) return true; } else { const matches = kb.key === e.key && (kb.shift === void 0 || kb.shift === e.shiftKey) && (kb.ctrlOrMeta === void 0 || kb.ctrlOrMeta === e.ctrlKey || kb.ctrlOrMeta === e.metaKey); if (matches) return true; } } return false; } function mapKeyboardEventToAction(event) { for (const [actionName, { activatesFocusIndicator = true, bindings }] of entries(KEY_BINDINGS)) { if (matchesKeyBinding(event, bindings)) { return { name: actionName, activatesFocusIndicator }; } } return void 0; } // packages/ag-charts-community/src/chart/keyboardUtil.ts function computeCenter(series, hoverRect, pick2) { const refPoint = getDatumRefPoint(series, pick2.datum, pick2.movedBounds); if (refPoint != null) return { x: refPoint.canvasX, y: refPoint.canvasY }; const bboxOrPath = pick2.bounds; if (bboxOrPath == null) return; if (bboxOrPath instanceof BBox) { const { x: centerX, y: centerY } = bboxOrPath.computeCenter(); return { x: hoverRect.x + centerX, y: hoverRect.y + centerY }; } return Transformable.toCanvas(bboxOrPath).computeCenter(); } function getPickedFocusBBox({ bounds }) { if (bounds instanceof BBox) return bounds; if (bounds != null) return Transformable.toCanvas(bounds); return BBox.NaN; } function makeKeyboardPointerEvent(series, hoverRect, pick2) { const { x: canvasX, y: canvasY } = computeCenter(series, hoverRect, pick2) ?? {}; if (canvasX !== void 0 && canvasY !== void 0) { return { type: "keyboard", canvasX, canvasY }; } return void 0; } // packages/ag-charts-community/src/chart/series/pickManager.ts function getItemId(node) { if (node.datum.itemId !== void 0) { return node.datum.itemId; } else if (typeof node.datum.datumIndex === "number") { return node.datum.datumIndex; } else { return JSON.stringify(node.datum.datumIndex); } } function pickedNodesEqual(a, b) { return a.series === b.series && objectsEqual(a.datumIndex, b.datumIndex); } function indexOf(candidates, node) { return node == void 0 ? -1 : candidates.findIndex((c) => pickedNodesEqual(c, node)); } var PickManager = class { constructor(activeManager, tooltipProperties, focusState) { this.activeManager = activeManager; this.tooltipProperties = tooltipProperties; this.focusState = focusState; this.candidates = []; this.activeState = new StateTracker(); } getActive() { return this.activeState.stateValue(); } clear() { this.activeState.clear(); this.lastNotifiedActive = void 0; this.candidates.length = 0; this.pendingPickedNodes = void 0; } setSource(source, node) { if (node === void 0) { this.activeState.delete(source); } else { this.activeState.set(source, node); } this.syncActiveManager(); } syncActiveManager() { const resolved = this.getActive(); const prev = this.lastNotifiedActive; if (resolved === prev) return; if (resolved !== void 0 && prev !== void 0 && pickedNodesEqual(resolved, prev)) return; this.lastNotifiedActive = resolved; if (resolved === void 0) { this.activeManager.clear(); } else { const seriesId = resolved.series.id; const itemId = getItemId(resolved); this.activeManager.update({ type: "series-node", seriesId, itemId }, resolved.datum); } } popPendingPickedNodes() { const result = this.pendingPickedNodes; this.pendingPickedNodes = void 0; return result; } // Some user interactive (e.g. mouseleave, blur) has cleared the active datum. onClearUI() { this.activeManager.clear(); this.clear(); } // Active datum was cleared by ActiveManager (`setState` or legend). onClearAPI() { this.clear(); } onPickedNodesHighlight(pickedNodes) { if (pickedNodes !== void 0) { const previousActive = this.getActive(); if (this.tooltipProperties.pagination && previousActive !== void 0) { const tooltipMatch = pickedNodes.matches.find((m) => pickedNodesEqual(m, previousActive)); if (tooltipMatch) { return tooltipMatch; } } } const node = pickedNodes?.matches[0]; this.setSource("highlight", node); return node; } onPickedNodesTooltip(pickedNodes) { if (pickedNodes !== void 0 && this.tooltipProperties.pagination) { const previous = this.getActive(); const nextCandidates = pickedNodes.matches; this.candidates = nextCandidates; let nextIndex = indexOf(nextCandidates, previous); if (nextIndex === -1) nextIndex = 0; const node2 = nextCandidates[nextIndex]; this.setSource("tooltip", node2); const paginationState = { index: nextIndex, length: nextCandidates.length }; return { active: node2, paginationState }; } const node = pickedNodes?.matches[0]; this.setSource("tooltip", node); return { active: node }; } onPickedNodesFocus(pickedFocus) { const { series } = this.focusState; this.clear(); if (series !== void 0 && pickedFocus !== void 0) { const { datum, datumIndex } = pickedFocus; this.setSource("focus", { series, datum, datumIndex }); } } onPickedNodesAPI(debouncedPickedNodes) { this.pendingPickedNodes = debouncedPickedNodes; return debouncedPickedNodes.matches[0]; } onPickedNodesAPIDebounced() { return { active: this.onPickedNodesHighlight(this.popPendingPickedNodes()) }; } nextCandidate() { if (this.tooltipProperties.pagination) { const { candidates } = this; const previous = this.getActive(); const hoverIndex = previous == null ? -1 : candidates.findIndex((c) => pickedNodesEqual(c, previous)); if (hoverIndex === -1) return { active: void 0, paginationState: void 0 }; let nextIndex = hoverIndex + 1; if (nextIndex >= candidates.length) { nextIndex = 0; } const node = candidates[nextIndex]; this.setSource("tooltip", node); const paginationState = { index: nextIndex, length: this.candidates.length }; return { active: node, paginationState }; } return { active: this.getActive() }; } }; // packages/ag-charts-community/src/chart/series/seriesAreaManager.ts var SeriesAreaManager = class extends BaseManager { constructor(chart) { super(); this.chart = chart; this.id = createId(this); this.series = []; this.announceMode = "when-changed"; this.highlight = { /** Last received event that still needs to be applied. */ pendingHoverEvent: void 0, /** Last applied event. */ appliedHoverEvent: void 0, /** Last applied event, which has been temporarily stashed during the main chart update cycle. */ stashedHoverEvent: void 0 }; this.tooltip = { lastHover: void 0 }; this.activeState = { lastActive: void 0 }; /** * A11y Requirements for Tooltip/Highlight (see AG-13051 for details): * * - When the series-area is blurred, allow the mouse to update the tooltip/highlight. * * - When the series-area receives a `focus` event, use `:focus-visible` to guess the input device. * (this is decided by the browser). * * - For keyboard users, `focus` and `keydown` events always updates & shows the tooltip/highlight on * the currently (or newly) focused datum. * * - For keyboard users, `mousemove` events update the tooltip/highlight iff `pickNode` finds a match * for the mouse event offsets. */ this.hoverDevice = "pointer"; this.focus = { sortedSeries: [], series: void 0, seriesIndex: 0, datumIndex: 0, datum: void 0 }; this.cachedTooltipContent = void 0; this.hoverScheduler = debouncedAnimationFrame(() => { if (this.hoverDevice === "setState") { return this.handleHoverFromState(); } if (!this.tooltip.lastHover && !this.highlight.pendingHoverEvent) return; if (this.chart.getUpdateType() <= 7 /* SERIES_UPDATE */) { this.hoverScheduler.schedule(); return; } if (this.highlight.pendingHoverEvent) { this.handleHoverHighlight(false); } if (this.tooltip.lastHover) { this.handleHoverTooltip(this.tooltip.lastHover, false); } }); this.pickManager = new PickManager(chart.ctx.activeManager, chart.tooltip, this.focus); const initialAltText = chart.ctx.localeManager.t("ariaInitSeriesArea"); const label1 = chart.ctx.domManager.addChild("series-area", "series-area-aria-label1"); const label2 = chart.ctx.domManager.addChild("series-area", "series-area-aria-label2"); this.swapChain = new FocusSwapChain(label1, label2, "img", initialAltText); this.swapChain.addListener("blur", (event) => this.onBlur(event)); this.swapChain.addListener("focus", () => this.onFocus()); if (chart.ctx.domManager.mode === "normal") { this.focusIndicator = new FocusIndicator(this.swapChain); this.focusIndicator.overrideFocusVisible(chart.mode === "integrated" ? false : void 0); } const { seriesDragInterpreter, seriesWidget, containerWidget } = chart.ctx.widgets; seriesWidget.setTabIndex(-1); this.cleanup.register( () => chart.ctx.domManager.removeChild("series-area", "series-area-aria-label1"), () => chart.ctx.domManager.removeChild("series-area", "series-area-aria-label2"), seriesWidget.addListener("focus", () => this.swapChain.focus({ preventScroll: true })), seriesWidget.addListener("mousemove", (event) => this.onHover(event, seriesWidget)), seriesWidget.addListener("wheel", (event) => this.onWheel(event)), seriesWidget.addListener("mouseleave", (event) => this.onLeave(event)), seriesWidget.addListener("keydown", (event) => this.onKeyDown(event)), seriesWidget.addListener("contextmenu", (event, current) => this.onContextMenu(event, current)), containerWidget.addListener("contextmenu", (event, current) => this.onContextMenu(event, current)), containerWidget.addListener("click", (event, current) => this.onClick(event, current)), containerWidget.addListener("dblclick", (event, current) => this.onClick(event, current)), chart.ctx.animationManager.addListener("animation-start", () => this.onAnimationStart()), chart.ctx.eventsHub.on("active:load-memento", (event) => this.onActiveLoadMemento(event)), chart.ctx.eventsHub.on("active:update", (event) => this.onActiveUpdate(event)), chart.ctx.eventsHub.on("dom:resize", () => this.clearAll()), chart.ctx.eventsHub.on("highlight:change", (event) => this.changeHighlightDatum(event)), chart.ctx.eventsHub.on("layout:complete", (event) => this.layoutComplete(event)), chart.ctx.updateService.addListener("pre-scene-render", () => this.preSceneRender()), chart.ctx.updateService.addListener("update-complete", () => this.updateComplete()), chart.ctx.eventsHub.on("zoom:change-complete", () => this.clearAll()), chart.ctx.eventsHub.on("zoom:pan-start", () => this.clearAll()) ); if (seriesDragInterpreter) { this.cleanup.register( seriesDragInterpreter.events.on("drag-move", (event) => this.onDragMove(event, seriesWidget)), seriesDragInterpreter.events.on("click", (event) => this.onClick(event, seriesWidget)), seriesDragInterpreter.events.on("dblclick", (event) => this.onClick(event, seriesWidget)) ); } } get bbox() { return (this.seriesRect ?? BBox.zero).clone(); } isState(allowedStates) { return this.chart.ctx.interactionManager.isState(allowedStates); } isIgnoredTouch(event) { if (event.device !== "touch" || event.type === "click") return false; if (this.chart.ctx.chartService.touch.dragAction === "hover") return false; if (this.chart.ctx.chartService.touch.dragAction === "drag") { if (this.isState(18 /* AnnotationsMoveable */)) { return false; } } return true; } dataChanged() { var _a; this.cachedTooltipContent = void 0; if (this.highlight.appliedHoverEvent) { (_a = this.highlight).stashedHoverEvent ?? (_a.stashedHoverEvent = this.highlight.appliedHoverEvent); this.clearHighlight(); } if (this.hoverDevice !== "setState") { this.chart.ctx.tooltipManager.removeTooltip(this.id); this.focusIndicator?.clear(); } } preSceneRender() { if (this.highlight.stashedHoverEvent != null) { this.highlight.pendingHoverEvent = this.tooltip.lastHover ?? this.highlight.stashedHoverEvent; this.highlight.stashedHoverEvent = void 0; this.handleHoverHighlight(true); } if (this.tooltip.lastHover != null) { this.handleHoverTooltip(this.tooltip.lastHover, true); } if (this.hoverDevice === "setState") { this.refreshSetState(); } } updateComplete() { if (this.isState(68 /* Focusable */) && this.focusIndicator?.isFocusVisible()) { if (this.announceMode !== "always") { this.announceMode = "never"; } this.handleFocus(0, 0); } } update(type, opts) { this.chart.ctx.updateService.update(type, opts); } seriesChanged(series) { this.focus.sortedSeries = [...series].sort((a, b) => { let fpA = a.properties.focusPriority ?? Infinity; let fpB = b.properties.focusPriority ?? Infinity; if (fpA === fpB) { [fpA, fpB] = [a.declarationOrder, b.declarationOrder]; } if (fpA < fpB) { return -1; } else if (fpA > fpB) { return 1; } return 0; }); this.series = series; } layoutComplete(event) { this.seriesRect = event.series.rect; this.hoverRect = event.series.rect; this.chart.ctx.widgets.seriesWidget.setBounds(event.series.rect); if (this.chart.ctx.domManager.mode === "normal") { this.chart.ctx.widgets.chartWidget.setBounds(event.chart); } } onAnimationStart() { if (this.hoverDevice !== "setState") { this.clearAll(); } } onContextMenu(event, current) { const { sourceEvent } = event; if (sourceEvent.currentTarget != current.getElement()) return; if (current !== this.chart.ctx.widgets.seriesWidget) { if (this.isState(72 /* ContextMenuable */)) { const { currentX: canvasX2, currentY: canvasY2 } = event; this.chart.ctx.contextMenuRegistry.dispatchContext( "always", { widgetEvent: event, canvasX: canvasX2, canvasY: canvasY2 }, void 0 ); } return; } let pickedNode; let position; if (this.focusIndicator?.isFocusVisible()) { pickedNode = this.chart.ctx.highlightManager.getActiveHighlight(); if (pickedNode && this.seriesRect && pickedNode.midPoint) { position = Transformable.toCanvasPoint( pickedNode.series.contentGroup, pickedNode.midPoint.x, pickedNode.midPoint.y ); } } else if (this.isState(72 /* ContextMenuable */)) { const pick2 = this.pickNodes({ x: event.currentX, y: event.currentY }, "context-menu"); if (pick2) { this.chart.ctx.highlightManager.updateHighlight(this.id); pickedNode = pick2.matches[0].datum; } } const pickedSeries = pickedNode?.series; this.clearAll(); const canvasX = event.currentX + current.cssLeft(); const canvasY = event.currentY + current.cssTop(); const { datumIndex } = pickedNode ?? {}; if (pickedSeries && pickedNode && datumIndex != null) { this.chart.ctx.contextMenuRegistry.dispatchContext( "series-node", { widgetEvent: event, canvasX, canvasY }, { pickedSeries, pickedNode: { ...pickedNode, datumIndex } }, position ); } else { this.chart.ctx.contextMenuRegistry.dispatchContext( "series-area", { widgetEvent: event, canvasX, canvasY }, void 0, position ); } } onLeave(event) { if (!this.isState(82 /* Clickable */)) return; const relatedTarget = event.sourceEvent.relatedTarget; if (relatedTarget?.className === "ag-charts-text-input__textarea") { return; } if (this.maybeEnterInteractiveTooltip(event.sourceEvent)) { return; } this.chart.ctx.domManager.updateCursor(this.id); if (this.hoverDevice !== "keyboard") this.clearAll(true); } onWheel(_event) { if (!this.isState(82 /* Clickable */)) return; this.focusIndicator?.overrideFocusVisible(false); this.hoverDevice = "pointer"; } onDragMove(event, current) { if (!this.isState(82 /* Clickable */)) return; this.focusIndicator?.overrideFocusVisible(false); this.onHoverLikeEvent(event, current); } onHover(event, current) { if (!this.isState(82 /* Clickable */)) return; this.onHoverLikeEvent(event, current); } onHoverLikeEvent(event, current) { if (this.isIgnoredTouch(event)) return; if (event.device === "touch" && this.chart.ctx.chartService.touch.dragAction === "hover") { event.sourceEvent.preventDefault(); } if (current !== this.chart.ctx.widgets.seriesWidget) return; this.tooltip.lastHover = event; this.hoverDevice = "pointer"; this.highlight.pendingHoverEvent = event; this.hoverScheduler.schedule(); let pick2; if (this.isState(64 /* Default */)) { const { currentX: x, currentY: y } = event; pick2 = this.pickNodes({ x, y }, "event"); const matches = pick2?.matches; const found = matches?.[0]; if (found?.series.hasEventListener("seriesNodeClick") || found?.series.hasEventListener("seriesNodeDoubleClick") || matches != null && matches.length > 1 && this.chart.tooltip.pagination) { this.chart.ctx.domManager.updateCursor(this.id, "pointer"); } else { this.chart.ctx.domManager.updateCursor(this.id); } } const consumed = Boolean(pick2?.matches.length); this.emitSeriesAreaHoverEvent(event, consumed); } onClick(event, current) { if (event.device === "keyboard") { return; } if (current === this.chart.ctx.widgets.seriesWidget && this.chart.ctx.animationManager.isActive()) { this.chart.ctx.animationManager.skipCurrentBatch(); } if (event.device === "touch" && current === this.chart.ctx.widgets.seriesWidget) { this.swapChain.focus({ preventScroll: true }); } if (!this.isState(82 /* Clickable */)) return; if (current === this.chart.ctx.widgets.seriesWidget) { if (!current.getElement().contains(event.sourceEvent.target)) { return; } } else if (event.sourceEvent.target != current.getElement()) { return; } this.focusIndicator?.overrideFocusVisible(false); this.onHoverLikeEvent(event, current); const isSeriesWidget = current === this.chart.ctx.widgets.seriesWidget; if (!this.isState(64 /* Default */)) { if (isSeriesWidget) { this.emitSeriesAreaClickEvent(event, false); } return; } if (isSeriesWidget) { const consumed = this.checkSeriesNodeClick(event); if (consumed) { this.emitSeriesAreaClickEvent(event, true); this.update(7 /* SERIES_UPDATE */); event.sourceEvent.preventDefault(); return; } this.emitSeriesAreaClickEvent(event, false); } const newEvent = { type: event.type === "click" ? "click" : "doubleClick", event: event.sourceEvent }; this.chart.fireEvent(newEvent); } emitSeriesAreaHoverEvent(event, consumed) { const { canvasX, canvasY } = this.toCanvasCoordinates(event); const payload = { canvasX, canvasY, consumed, sourceEvent: event.sourceEvent }; this.chart.ctx.eventsHub.emit("series-area:hover", payload); } emitSeriesAreaClickEvent(event, consumed) { if (!("currentX" in event)) return; const { canvasX, canvasY } = this.toCanvasCoordinates(event); const payload = { canvasX, canvasY, consumed, sourceEvent: event.sourceEvent }; this.chart.ctx.eventsHub.emit("series-area:click", payload); } toCanvasCoordinates(event) { return { canvasX: event.currentX + (this.hoverRect?.x ?? this.seriesRect?.x ?? 0), canvasY: event.currentY + (this.hoverRect?.y ?? this.seriesRect?.y ?? 0) }; } onFocus() { if (!this.isState(68 /* Focusable */)) return; this.hoverDevice = this.focusIndicator?.isFocusVisible(true) ? "keyboard" : "pointer"; this.handleFocus(0, 0); } onBlur(event) { if (!this.isState(68 /* Focusable */)) return; this.hoverDevice = "pointer"; if (!this.maybeEnterInteractiveTooltip(event)) { this.clearAll(true); } this.focusIndicator?.overrideFocusVisible(void 0); } onKeyDown(widgetEvent) { if (!this.isState(86 /* Keyable */)) return; const action = mapKeyboardEventToAction(widgetEvent.sourceEvent); if (action?.activatesFocusIndicator === false) { this.focusIndicator?.overrideFocusVisible(this.hoverDevice === "keyboard"); } switch (action?.name) { case "redo": return this.chart.ctx.eventsHub.emit("series:redo", null); case "undo": return this.chart.ctx.eventsHub.emit("series:undo", null); case "zoomin": return this.chart.ctx.eventsHub.emit("series:keynav-zoom", { delta: 1, widgetEvent }); case "zoomout": return this.chart.ctx.eventsHub.emit("series:keynav-zoom", { delta: -1, widgetEvent }); case "arrowup": return this.onArrow(-1, 0, widgetEvent); case "arrowdown": return this.onArrow(1, 0, widgetEvent); case "arrowleft": return this.onArrow(0, -1, widgetEvent); case "arrowright": return this.onArrow(0, 1, widgetEvent); case "submit": return this.onSubmit(widgetEvent); } } onArrow(seriesIndexDelta, datumIndexDelta, event) { if (!this.isState(68 /* Focusable */)) return; this.hoverDevice = "keyboard"; this.focusIndicator?.overrideFocusVisible(true); this.focus.seriesIndex += seriesIndexDelta; this.focus.datumIndex += datumIndexDelta; this.handleFocus(seriesIndexDelta, datumIndexDelta); event.sourceEvent.preventDefault(); this.chart.ctx.eventsHub.emit("series:focus-change", null); } onSubmit(event) { if (!this.isState(68 /* Focusable */)) return; const { series, datum } = this.focus; const sourceEvent = event.sourceEvent; if (series != null && datum != null) { series.fireNodeClickEvent(sourceEvent, datum); } else { this.chart.fireEvent({ type: "click", event: sourceEvent }); } sourceEvent.preventDefault(); } checkSeriesNodeClick(event) { var _a; const pickedNodes = this.pickNodes({ x: event.currentX, y: event.currentY }, "event"); const updated = this.pickManager.onPickedNodesTooltip(pickedNodes); if (pickedNodes === void 0 || updated.active === void 0) return false; const { series, datum } = updated.active; const distance2 = updated.paginationState == null ? pickedNodes.distance : 0; if (event.type === "click") { const defaultBehavior = series.fireNodeClickEvent(event.sourceEvent, datum); if (defaultBehavior) { const next = this.pickManager.nextCandidate(); if (next.active !== void 0) { const { canvasX, canvasY } = this.toCanvasCoordinates(event); (_a = this.highlight).pendingHoverEvent ?? (_a.pendingHoverEvent = this.highlight.appliedHoverEvent); this.handleHoverHighlight(false); this.showTooltip(next.active, canvasX, canvasY, next.paginationState); } } return true; } if (event.type === "dblclick") { event.preventZoomDblClick = distance2 === 0; series.fireNodeDoubleClickEvent(event.sourceEvent, datum); return true; } return false; } handleFocus(seriesIndexDelta, datumIndexDelta) { const overlayFocus = this.chart.overlays.getFocusInfo(this.chart.ctx.localeManager); if (overlayFocus == null) { if (this.handleSeriesFocus(seriesIndexDelta, datumIndexDelta) === 0 /* SUCCESS */) { this.announceMode = "when-changed"; } else { this.announceMode = "always"; } } else { this.focusIndicator?.update(overlayFocus.rect, this.seriesRect, false); this.swapChain.update(overlayFocus.text); this.announceMode = "always"; } } handleSeriesFocus(otherIndexDelta, datumIndexDelta) { if (this.chart.chartType === "standalone") { return this.handleSoloSeriesFocus(otherIndexDelta, datumIndexDelta); } const { focus } = this; const visibleSeries = focus.sortedSeries.filter((s) => s.visible && s.focusable); if (visibleSeries.length === 0) return 1 /* SERIES_NOT_FOUND */; const oldDatumIndex = focus.datumIndex - datumIndexDelta; const oldOtherIndex = focus.seriesIndex - otherIndexDelta; focus.seriesIndex = clamp(0, focus.seriesIndex, visibleSeries.length - 1); focus.series = visibleSeries[focus.seriesIndex]; const datumIndex = this.focus.datumIndex; const otherIndex = this.focus.seriesIndex; return this.updatePickedFocus( datumIndex, datumIndexDelta, oldDatumIndex, otherIndex, otherIndexDelta, oldOtherIndex ); } handleSoloSeriesFocus(otherIndexDelta, datumIndexDelta) { this.focus.series = this.focus.sortedSeries[0]; const datumIndex = this.focus.datumIndex; const otherIndex = this.focus.seriesIndex; const oldDatumIndex = this.focus.datumIndex - datumIndexDelta; const oldOtherIndex = this.focus.seriesIndex - otherIndexDelta; return this.updatePickedFocus( datumIndex, datumIndexDelta, oldDatumIndex, otherIndex, otherIndexDelta, oldOtherIndex ); } pickFocus(series, opts) { const pick2 = series.pickFocus(opts); if (this.hoverDevice === "keyboard") { this.pickManager.onPickedNodesFocus(pick2); } return pick2; } updatePickedFocus(datumIndex, datumIndexDelta, oldDatumIndex, otherIndex, otherIndexDelta, oldOtherIndex) { const { focus, hoverRect, seriesRect } = this; if (focus.series == null || hoverRect == null) return 1 /* SERIES_NOT_FOUND */; const focusInputs = { datumIndex, datumIndexDelta, otherIndex, otherIndexDelta, seriesRect }; const pick2 = this.pickFocus(focus.series, focusInputs); if (!pick2) return 2 /* DATUM_NOT_FOUND */; const { datum } = pick2; focus.datum = datum; focus.datumIndex = pick2.datumIndex; if (pick2.otherIndex != null) { focus.seriesIndex = pick2.otherIndex; } if (this.focusIndicator?.isFocusVisible()) { this.chart.ctx.animationManager.reset(); const focusBBox = getPickedFocusBBox(pick2); const { x, y } = focusBBox.computeCenter(); if (!hoverRect.containsPoint(x, y)) { const panSuccess = this.chart.ctx.zoomManager.panToBBox(hoverRect, focusBBox); if (panSuccess) { return 3 /* PAN_REQUIRED */; } } const { x1, x2, y1, y2 } = vector4_exports.from(focusBBox); const nw = hoverRect.containsPoint(x1, y1); const ne = hoverRect.containsPoint(x2, y1); const sw = hoverRect.containsPoint(x1, y2); const se = hoverRect.containsPoint(x2, y2); if (!(nw || ne || sw || se)) { const hoverBounds = vector4_exports.from(hoverRect); pick2.movedBounds = focusBBox.clone(); if (x1 < hoverBounds.x1 && x2 < hoverBounds.x1) { pick2.movedBounds.x = hoverBounds.x1 - 2; pick2.movedBounds.width = 4; } else if (x1 > hoverBounds.x2 && x2 > hoverBounds.x2) { pick2.movedBounds.x = hoverBounds.x2 - 2; pick2.movedBounds.width = 4; } if (y1 < hoverBounds.y1 && y2 < hoverBounds.y1) { pick2.movedBounds.y = hoverBounds.y1 - 2; pick2.movedBounds.height = 4; } else if (y1 > hoverBounds.y2 && y2 > hoverBounds.y2) { pick2.movedBounds.y = hoverBounds.y2 - 2; pick2.movedBounds.height = 4; } } } this.focusIndicator?.update(pick2.movedBounds ?? pick2.bounds, this.seriesRect, pick2.clipFocusBox); const tooltipContent = this.getTooltipContent(focus.series, datum.datumIndex, datum, "aria-label"); const keyboardEvent = makeKeyboardPointerEvent(focus.series, hoverRect, pick2); if (keyboardEvent != null && this.hoverDevice === "keyboard") { this.clearCachedEvents(); const meta = TooltipManager.makeTooltipMeta(keyboardEvent, focus.series, datum, pick2.movedBounds); this.chart.ctx.highlightManager.updateHighlight(this.id, datum); if (this.isTooltipEnabled(focus.series)) { this.chart.ctx.tooltipManager.updateTooltip(this.id, meta, tooltipContent); } } this.maybeAnnouncePickedFocus( datumIndexDelta, oldDatumIndex, otherIndexDelta, oldOtherIndex, pick2, tooltipContent ); return 0 /* SUCCESS */; } maybeAnnouncePickedFocus(datumIndexDelta, oldDatumIndex, otherIndexDelta, oldOtherIndex, pick2, tooltipContent) { const { focus } = this; let mode; if (this.announceMode === "when-changed") { const shouldAnnouncePick = datumIndexDelta === 0 && otherIndexDelta === 0 || oldDatumIndex !== pick2.datumIndex || oldOtherIndex !== (pick2.otherIndex ?? focus.seriesIndex); if (shouldAnnouncePick) { mode = "always"; } else { mode = "never"; } } else { mode = this.announceMode; } if (mode === "always") { this.swapChain.update(this.getDatumAriaText(pick2.datum, tooltipContent)); } } getDatumAriaText(datum, tooltipContent) { const description = tooltipContent == null ? "" : tooltipContentAriaLabel(tooltipContent); return this.chart.ctx.localeManager.t("ariaAnnounceHoverDatum", { datum: datum.series.getDatumAriaText?.(datum, description) ?? description }); } clearHighlight(delayed = false) { this.highlight.pendingHoverEvent = void 0; this.highlight.appliedHoverEvent = void 0; this.chart.ctx.highlightManager.updateHighlight(this.id, void 0, delayed); } clearTooltip(delayed = false) { this.chart.ctx.tooltipManager.removeTooltip(this.id, void 0, delayed); this.tooltip.lastHover = void 0; } clearAll(delayed = false) { this.pickManager.onClearUI(); this.clearHighlight(delayed); this.clearTooltip(delayed); this.focusIndicator?.clear(); } clearCachedEvents() { this.tooltip.lastHover = void 0; this.highlight.appliedHoverEvent = void 0; this.highlight.pendingHoverEvent = void 0; this.highlight.stashedHoverEvent = void 0; } handleHoverFromState() { const { active, paginationState } = this.pickManager.onPickedNodesAPIDebounced(); if (active === void 0) return; this.chart.ctx.highlightManager.updateHighlight(this.id, active.datum); const refPoint = getDatumRefPoint(active.series, active.datum, void 0); if (this.chart.tooltip.enabled) { if (refPoint) { const { canvasX, canvasY } = refPoint; this.showTooltip(active, canvasX, canvasY, paginationState); } } } handleHoverHighlight(redisplay) { this.highlight.appliedHoverEvent = this.highlight.pendingHoverEvent; this.highlight.pendingHoverEvent = void 0; const event = this.highlight.appliedHoverEvent; if (!event || !this.isState(82 /* Clickable */)) return; const { canvasX, canvasY } = this.toCanvasCoordinates(event); if (redisplay ? this.chart.ctx.animationManager.isActive() : !this.hoverRect?.containsPoint(canvasX, canvasY)) { this.clearHighlight(); return; } const { range: range3 } = this.chart.highlight; const intent = range3 === "tooltip" ? "highlight-tooltip" : "highlight"; const pick2 = this.pickNodes({ x: event.currentX, y: event.currentY }, intent); const active = this.pickManager.onPickedNodesHighlight(pick2); if (active === void 0) { this.chart.ctx.highlightManager.updateHighlight(this.id, void 0, true); } else { this.chart.ctx.highlightManager.updateHighlight(this.id, active.datum, false); } } handleHoverTooltip(event, redisplay) { if (!this.isState(82 /* Clickable */)) return; const { canvasX, canvasY } = this.toCanvasCoordinates(event); const targetElement = event.sourceEvent.target; if (redisplay ? this.chart.ctx.animationManager.isActive() : !this.hoverRect?.containsPoint(canvasX, canvasY)) { if (this.hoverDevice == "pointer") this.clearTooltip(); return; } if (targetElement && this.chart.tooltip.interactive && this.chart.ctx.domManager.isManagedChildDOMElement(targetElement, "canvas-overlay", DEFAULT_TOOLTIP_CLASS)) { return; } const pick2 = this.pickNodes({ x: event.currentX, y: event.currentY }, "tooltip"); const { active, paginationState } = this.pickManager.onPickedNodesTooltip(pick2); if (active === void 0) { if (this.hoverDevice == "pointer") { this.clearTooltip(true); } } else { this.showTooltip(active, canvasX, canvasY, paginationState); } } showTooltip({ series, datum, datumIndex }, canvasX, canvasY, pagination) { const tooltipContent = this.getTooltipContent(series, datumIndex, datum, "tooltip"); const shouldUpdateTooltip = tooltipContent != null; if (shouldUpdateTooltip) { const meta = TooltipManager.makeTooltipMeta( { type: "pointermove", canvasX, canvasY }, series, datum, void 0 ); this.chart.ctx.tooltipManager.updateTooltip(this.id, meta, tooltipContent, pagination); } else { this.chart.ctx.tooltipManager.removeTooltip(this.id, void 0, true); } } maybeEnterInteractiveTooltip(event) { return this.chart.tooltip.maybeEnterInteractiveTooltip(event, () => { this.tooltip.lastHover = void 0; this.chart.ctx.tooltipManager.removeTooltip(this.id); this.chart.ctx.highlightManager.updateHighlight(this.id, void 0, true); }); } changeHighlightDatum(event) { const lastSeries = event.previousHighlight?.series; const newSeries = event.currentHighlight?.series; if (lastSeries?.properties.cursor && event.previousHighlight?.datum) { this.chart.ctx.domManager.updateCursor(lastSeries.id); } if (newSeries?.properties.cursor && newSeries.properties.cursor !== "default" && event.currentHighlight?.datum) { this.chart.ctx.domManager.updateCursor(newSeries.id, newSeries.properties.cursor); } if (this.hoverDevice === "setState" || newSeries == null || lastSeries == null) { this.update(7 /* SERIES_UPDATE */, { clearCallbackCache: true }); } else { this.update(7 /* SERIES_UPDATE */, { seriesToUpdate: new Set([lastSeries, newSeries].filter(Boolean)), clearCallbackCache: true }); } } pickNodes(point, intent, exactMatchOnly) { const reverseSeries = [...this.series].reverse(); const clickIntent = intent === "event" || intent === "context-menu"; const tooltipIntent = intent === "tooltip" || intent === "highlight-tooltip"; const getIntentRange = (series) => { if (clickIntent) return series.properties.nodeClickRange; if (tooltipIntent) return series.properties.tooltip.range; return void 0; }; const { x, y } = point; const seriesContainingPoint = /* @__PURE__ */ new Set(); for (const series of reverseSeries) { if (series.visible && series.contentGroup.visible && getIntentRange(series) === "area") { if (series.isPointInArea?.(x, y)) { seriesContainingPoint.add(series); } } } const hasSeriesContainingPoint = seriesContainingPoint.size > 0; let result; for (const series of reverseSeries) { if (!series.visible || !series.contentGroup.visible) continue; if (hasSeriesContainingPoint) { const hasAreaHit = getIntentRange(series) === "area" && seriesContainingPoint.has(series); if (!hasAreaHit) continue; } const pick2 = series.pickNodes(point, intent, exactMatchOnly); if (pick2 == null || pick2.datums.length === 0) continue; const { datums, distance: distance2 } = pick2; if (pick2.datums.length === 0) continue; if (distance2 === 0) { if (result?.distance !== 0) { result = { matches: [], distance: 0 }; } for (const datum of datums) { const { datumIndex } = datum; result.matches.push({ series, datum, datumIndex }); } } else if (result == null || result.distance > distance2) { const [datum] = datums; const { datumIndex } = datum; result = { matches: [{ series, datum, datumIndex }], distance: distance2 }; } } return result; } isTooltipEnabled(series) { return series.tooltipEnabled ?? this.chart.tooltip.enabled; } getTooltipContent(series, datumIndex, datum, purpose) { let result; if (purpose === "aria-label" || this.isTooltipEnabled(series)) { const { cachedTooltipContent } = this; if (cachedTooltipContent?.series === series && cachedTooltipContent.datumIndex === datumIndex) { result = cachedTooltipContent.content; } else { const content = this.chart.getTooltipContent(series, datumIndex, datum, purpose); this.cachedTooltipContent = { series, datumIndex, content }; result = content; } purpose; result; } else { this.cachedTooltipContent = void 0; purpose; result; } return result; } onActiveLoadMemento(event) { switch (event.activeItem?.type) { case void 0: case "legend": return this.onActiveClear(); case "series-node": return this.onActiveDatum(event.activeItem, event); default: return event.activeItem?.type; } } onActiveUpdate(activeItem) { if (activeItem?.type === "legend") { if (this.hoverDevice === "setState") { this.clearHighlight(); this.clearTooltip(); } this.activeState.lastActive = "legend"; } } onActiveClear() { this.pickManager.onClearAPI(); this.hoverDevice = "setState"; this.activeState.lastActive = void 0; this.clearHighlight(); this.clearTooltip(); } refreshSetState() { if (this.activeState.lastActive === void 0) { this.clearAll(); } else if (this.activeState.lastActive !== "legend") { const { seriesId, itemId } = this.activeState.lastActive; const desiredPickedNodes = this.findPickedNodes(seriesId, itemId); if (desiredPickedNodes) { this.pickManager.onPickedNodesAPI(desiredPickedNodes); this.hoverScheduler.schedule(); } } } onActiveDatum(activeItem, event) { const { seriesId, itemId } = activeItem; const desiredPickedNodes = this.findPickedNodes(seriesId, itemId); if (desiredPickedNodes === void 0) { event.reject(); this.onActiveClear(); } else { const picked = this.pickManager.onPickedNodesAPI(desiredPickedNodes); event.setDatum(picked?.datum); this.hoverDevice = "setState"; this.activeState.lastActive = { seriesId, itemId }; if (event.initialState) { this.chart.ctx.scene.applyPendingResize(); this.handleHoverFromState(); } else { this.clearCachedEvents(); this.hoverScheduler.schedule(); } } } findPickedNodes(desiredSeriesId, desiredItemId) { const desiredSeries = this.series.find((s) => s.id === desiredSeriesId); if (desiredSeries == void 0) { logger_exports.warn(`Cannot find seriesId: "${desiredSeriesId}"`); return void 0; } const desiredDatum = desiredSeries.findNodeDatum(desiredItemId); if (desiredDatum == void 0) { logger_exports.warn(`Cannot find itemId: ${JSON.stringify(desiredItemId)}`); return void 0; } const restoredPick = { datum: desiredDatum, datumIndex: desiredDatum.datumIndex, series: desiredSeries }; return { matches: [restoredPick], distance: 0 }; } }; SeriesAreaManager.className = "SeriesAreaManager"; // packages/ag-charts-community/src/chart/series/seriesLayerManager.ts var SERIES_THRESHOLD_FOR_AGGRESSIVE_LAYER_REDUCTION = 30; var SeriesLayerManager = class { constructor(seriesRoot) { this.seriesRoot = seriesRoot; this.groups = /* @__PURE__ */ new Map(); this.series = /* @__PURE__ */ new Map(); this.expectedSeriesCount = 1; this.mode = "normal"; } setSeriesCount(count) { this.expectedSeriesCount = count; } getGroupIndex(seriesConfig) { const { internalId, seriesGrouping } = seriesConfig; return seriesGrouping?.groupIndex ?? internalId; } getGroupType(seriesConfig, bringToFront) { return bringToFront ? "top" : seriesConfig.type; } requestGroup(seriesConfig) { const { internalId, contentGroup: seriesContentGroup } = seriesConfig; const bringToFront = seriesConfig.bringToFront(); const type = this.getGroupType(seriesConfig, bringToFront); const groupIndex = this.getGroupIndex(seriesConfig); const seriesInfo = this.series.get(internalId); if (seriesInfo != null) { throw new Error(`AG Charts - series already has an allocated layer: ${JSON.stringify(seriesInfo)}`); } if (this.series.size === 0) { this.mode = this.expectedSeriesCount >= SERIES_THRESHOLD_FOR_AGGRESSIVE_LAYER_REDUCTION ? "aggressive-grouping" : "normal"; } let group = this.groups.get(type); if (group == null) { group = /* @__PURE__ */ new Map(); this.groups.set(type, group); } const lookupIndex = this.lookupIdx(groupIndex); let groupInfo = group.get(lookupIndex); if (groupInfo == null) { groupInfo = { type, id: lookupIndex, seriesIds: [], group: this.seriesRoot.appendChild( new Group({ name: `${seriesConfig.contentGroup.name ?? type}-managed-layer`, zIndex: seriesConfig.contentGroup.zIndex, // Set in updateLayerCompositing renderToOffscreenCanvas: false }) ) }; group.set(lookupIndex, groupInfo); } this.series.set(internalId, { layerState: groupInfo, seriesConfig, bringToFront }); groupInfo.seriesIds.push(internalId); groupInfo.group.appendChild(seriesContentGroup); return groupInfo.group; } changeGroup(seriesConfig) { const { internalId, contentGroup } = seriesConfig; const bringToFront = seriesConfig.bringToFront(); const type = this.getGroupType(seriesConfig, bringToFront); const oldGroup = this.series.get(internalId); const oldType = oldGroup ? this.getGroupType(oldGroup.seriesConfig, oldGroup.bringToFront) : void 0; const groupIndex = this.getGroupIndex(seriesConfig); const lookupIndex = this.lookupIdx(groupIndex); const groupInfo = this.groups.get(type)?.get(lookupIndex); if (oldType === type && groupInfo?.seriesIds.includes(internalId) === true) { return; } if (this.series.has(internalId)) { this._releaseGroup({ internalId, contentGroup, type: oldType }); } return this.requestGroup(seriesConfig); } releaseGroup(seriesConfig) { const { internalId, contentGroup } = seriesConfig; const type = this.getGroupType(seriesConfig, seriesConfig.bringToFront()); this._releaseGroup({ internalId, contentGroup, type }); } _releaseGroup(seriesConfig) { const { internalId, contentGroup, type } = seriesConfig; if (!this.series.has(internalId)) { throw new Error(`AG Charts - series doesn't have an allocated layer: ${internalId}`); } const groupInfo = this.series.get(internalId)?.layerState; if (groupInfo) { groupInfo.seriesIds = groupInfo.seriesIds.filter((v) => v !== internalId); contentGroup.remove(); } if (groupInfo?.seriesIds.length === 0) { groupInfo.group.remove(); this.groups.get(groupInfo.type)?.delete(groupInfo.id); this.groups.get(type)?.delete(internalId); } else if (groupInfo != null && groupInfo.seriesIds.length > 0) { groupInfo.group.zIndex = this.getLowestSeriesZIndex(groupInfo.seriesIds); } this.series.delete(internalId); } updateLayerCompositing() { for (const groups of this.groups.values()) { for (const groupInfo of groups.values()) { const { group, seriesIds } = groupInfo; let renderToOffscreenCanvas; if (seriesIds.length === 0) { renderToOffscreenCanvas = false; } else if (seriesIds.length > 1) { renderToOffscreenCanvas = true; } else { const series = this.series.get(seriesIds[0]); renderToOffscreenCanvas = series?.seriesConfig.renderToOffscreenCanvas() === true; } group.renderToOffscreenCanvas = renderToOffscreenCanvas; group.zIndex = this.getLowestSeriesZIndex(seriesIds); } } } lookupIdx(groupIndex) { if (this.mode === "normal") { return groupIndex; } if (typeof groupIndex === "string") { groupIndex = Number(groupIndex.split("-").at(-1)); if (!Number.isFinite(groupIndex)) { return 0; } } return Math.floor( clamp(0, groupIndex / this.expectedSeriesCount, 1) * SERIES_THRESHOLD_FOR_AGGRESSIVE_LAYER_REDUCTION ); } destroy() { for (const groups of this.groups.values()) { for (const groupInfo of groups.values()) { groupInfo.group.remove(); } } this.groups.clear(); this.series.clear(); } getLowestSeriesZIndex(seriesIds) { let lowestSeriesZIndex = void 0; for (const seriesId of seriesIds) { const series = this.series.get(seriesId); const zIndex = series?.seriesConfig.contentGroup.zIndex ?? 1 /* ANY_CONTENT */; if (lowestSeriesZIndex == null || zIndex == null) { lowestSeriesZIndex = zIndex; continue; } lowestSeriesZIndex = compareZIndex(lowestSeriesZIndex, zIndex) <= 0 ? lowestSeriesZIndex : zIndex; } return lowestSeriesZIndex ?? 1 /* ANY_CONTENT */; } }; // packages/ag-charts-community/src/chart/touch.ts var Touch = class extends BaseProperties { constructor() { super(...arguments); this.dragAction = "drag"; } }; __decorateClass([ addFakeTransformToInstanceProperty ], Touch.prototype, "dragAction", 2); // packages/ag-charts-community/src/chart/update/dataWindowProcessor.ts var DataWindowProcessor = class { constructor(chart, eventsHub, dataService, updateService, zoomManager, animationManager) { this.chart = chart; this.eventsHub = eventsHub; this.dataService = dataService; this.updateService = updateService; this.zoomManager = zoomManager; this.animationManager = animationManager; this.dirtyZoom = false; this.dirtyDataSource = false; this.lastAxisZooms = /* @__PURE__ */ new Map(); this.cleanup = new CleanupRegistry(); this.cleanup.register( this.eventsHub.on("data:source-change", () => this.onDataSourceChange()), this.eventsHub.on("data:load", () => this.onDataLoad()), this.eventsHub.on("data:error", () => this.onDataError()), this.updateService.addListener("update-complete", (e) => this.onUpdateComplete(e)), this.eventsHub.on("zoom:change-complete", () => this.onZoomChange()) ); } destroy() { this.cleanup.flush(); } onDataLoad() { this.animationManager.skip(); this.updateService.update(1 /* UPDATE_DATA */); } onDataError() { this.updateService.update(5 /* PERFORM_LAYOUT */); } onDataSourceChange() { this.dirtyDataSource = true; } onUpdateComplete(event) { if (!event.apiUpdate && !this.dirtyZoom && !this.dirtyDataSource) return; if (event.wasShortcut) return; this.updateWindow(event); } onZoomChange() { this.dirtyZoom = true; } updateWindow(event) { if (!this.dataService.isLazy()) return; const axis = this.getValidAxis(); let window; let shouldRefresh = true; if (axis) { const zoom = this.zoomManager.getAxisZoom(axis.id); window = this.getAxisWindow(axis, zoom); shouldRefresh = this.shouldRefresh(event, axis, zoom); } this.dirtyZoom = false; this.dirtyDataSource = false; if (!shouldRefresh) return; this.dataService.load({ windowStart: window?.min, windowEnd: window?.max }); } getValidAxis() { return this.chart.axes.find((axis) => axis.type === "time"); } shouldRefresh(event, axis, zoom) { if (event.apiUpdate) return true; if (this.dirtyDataSource) return true; if (!this.dirtyZoom) return false; const lastZoom = this.lastAxisZooms.get(axis.id); if (lastZoom && zoom.min === lastZoom.min && zoom.max === lastZoom.max) { return false; } this.lastAxisZooms.set(axis.id, zoom); return true; } getAxisWindow(axis, zoom) { const { domain } = axis.scale; if (!zoom || domain.length === 0 || Number.isNaN(Number(domain[0]))) return; const diff2 = Number(domain[1]) - Number(domain[0]); const min = new Date(Number(domain[0]) + diff2 * zoom.min); const max = new Date(Number(domain[0]) + diff2 * zoom.max); return { min, max }; } }; // packages/ag-charts-community/src/util/browser.ts var isSafariRegexp = /^((?!chrome|android).)*safari/i; var safariVersionRegexp = /Version\/(\d+(\.\d+)?)/; var isChromeRegexp = /Chrome/; var chromeVersionRegexp = /Chrome\/(\d+)/; var isEdge = /Edg/; var isOpera = /OPR/; function isUnsupportedBrowser() { const { userAgent } = getWindow("navigator"); if (isSafariRegexp.test(userAgent)) { const versionExec = safariVersionRegexp.exec(userAgent); if (versionExec == null) return false; const version = Number.parseFloat(versionExec[1]); const supported = Math.floor(version) > 16; if (!supported) { logger_exports.warnOnce(`Unsupported Safari version: ${version}; ${userAgent}`); } return !supported; } else if (isChromeRegexp.test(userAgent) && !isEdge.test(userAgent) && !isOpera.test(userAgent)) { const versionExec = chromeVersionRegexp.exec(userAgent); if (versionExec == null) return false; const version = Number.parseInt(versionExec[1], 10); const supported = version > 126; if (!supported) { logger_exports.warnOnce(`Unsupported Chrome version: ${version}; ${userAgent}`); } return !supported; } return false; } // packages/ag-charts-community/src/chart/update/overlaysProcessor.ts var visibleIgnoredSeries = /* @__PURE__ */ new Set(["map-shape-background", "map-line-background"]); var OverlaysProcessor = class { constructor(chartLike, overlays, eventsHub, dataService, localeManager, animationManager, domManager) { this.chartLike = chartLike; this.overlays = overlays; this.eventsHub = eventsHub; this.dataService = dataService; this.localeManager = localeManager; this.animationManager = animationManager; this.domManager = domManager; this.cleanup = new CleanupRegistry(); this.overlayElem = this.domManager.addChild("canvas-overlay", "overlay"); this.overlayElem.role = "status"; this.overlayElem.ariaAtomic = "false"; this.overlayElem.ariaLive = "polite"; this.overlayElem.classList.toggle(DEFAULT_OVERLAY_CLASS); this.cleanup.register(this.eventsHub.on("layout:complete", (e) => this.onLayoutComplete(e))); } destroy() { this.cleanup.flush(); this.domManager.removeChild("canvas-overlay", "overlay"); } onLayoutComplete({ series: { rect: rect2 } }) { const isLoading = this.dataService.isLoading(); const hasData = this.chartLike.series.some((s) => s.hasData); const anySeriesVisible = this.chartLike.series.some((s) => s.visible && !visibleIgnoredSeries.has(s.type)); if (this.overlays.darkTheme) { this.overlayElem.classList.add(DEFAULT_OVERLAY_DARK_CLASS); } else { this.overlayElem.classList.remove(DEFAULT_OVERLAY_DARK_CLASS); } this.overlayElem.style.left = `${rect2.x}px`; this.overlayElem.style.top = `${rect2.y}px`; this.overlayElem.style.width = `${rect2.width}px`; this.overlayElem.style.height = `${rect2.height}px`; const loadingShown = isLoading; const noDataShown = !isLoading && !hasData; const noVisibleSeriesShown = hasData && !anySeriesVisible; const unsupportedBrowser = this.overlays.unsupportedBrowser.enabled && isUnsupportedBrowser(); if (loadingShown) { this.showOverlay(this.overlays.loading, rect2); } else { this.hideOverlay(this.overlays.loading); } if (noDataShown) { this.showOverlay(this.overlays.noData, rect2); } else { this.hideOverlay(this.overlays.noData); } if (noVisibleSeriesShown) { this.showOverlay(this.overlays.noVisibleSeries, rect2); } else { this.hideOverlay(this.overlays.noVisibleSeries); } if (unsupportedBrowser) { this.showOverlay(this.overlays.unsupportedBrowser, rect2); } else { this.hideOverlay(this.overlays.unsupportedBrowser); } const shown = loadingShown || noDataShown || noVisibleSeriesShown || unsupportedBrowser; setAttribute(this.overlayElem, "aria-hidden", !shown); } showOverlay(overlay, seriesRect) { if (!overlay.enabled) return; const element2 = overlay.getElement(this.chartLike, this.animationManager, this.localeManager, seriesRect); this.overlayElem.appendChild(element2); } hideOverlay(overlay) { overlay.removeElement(() => { this.overlayElem.innerText = "\xA0"; }, this.animationManager); } }; // packages/ag-charts-community/src/chart/chart.ts var debug = debugLogger_exports.create(true, "opts"); var _Chart = class _Chart extends Observable { constructor(options, resources) { var _a; super(); this.id = createId(this); this.seriesRoot = new TranslatableGroup({ name: `${this.id}-series-root`, zIndex: 7 /* SERIES_LAYER */ }); this.annotationRoot = new TranslatableGroup({ name: `${this.id}-annotation-root`, zIndex: 11 /* SERIES_ANNOTATION */ }); this.titleGroup = new Group({ name: "titles", zIndex: 15 /* SERIES_LABEL */ }); this.debug = debugLogger_exports.create(true, "chart"); this.extraDebugStats = {}; this.data = DataSet.empty(); this._firstAutoSize = true; this._autoSizeNotify = new AsyncAwaitQueue(); this._requiredRange = 0; this._requiredRangeDirection = "x" /* X */; this.chartCaptions = new ChartCaptions(); this.padding = new Padding(20); this.keyboard = new Keyboard(); this.touch = new Touch(); this.mode = "standalone"; this.styleNonce = void 0; this.formatter = void 0; this.suppressFieldDotNotation = false; this.loadGoogleFonts = false; this.destroyed = false; this.cleanup = new CleanupRegistry(); this.chartAnimationPhase = "initial"; this.modulesManager = new ModulesManager(); this.processors = []; this.queuedUserOptions = []; this.queuedChartOptions = []; this.firstApply = true; this.syncStatus = "init"; // Use a wrapper to comply with the @typescript-eslint/unbound-method rule. this.fireEventWrapper = (event) => super.fireEvent(event); this.apiUpdate = false; this._pendingFactoryUpdatesCount = 0; this._performUpdateSkipAnimations = false; this._performUpdateNotify = new AsyncAwaitQueue(); this.performUpdateType = 10 /* NONE */; this.runningUpdateType = 10 /* NONE */; this.updateShortcutCount = 0; this.seriesToUpdate = /* @__PURE__ */ new Set(); this.updateMutex = new Mutex(); this.clearCallbackCacheOnUpdate = false; this.updateRequestors = {}; this.performUpdateTrigger = debouncedCallback(({ count }) => { if (this.destroyed) return; this.updateMutex.acquire(this.tryPerformUpdate.bind(this, count)).catch((e) => logger_exports.errorOnce(e)); }); this._performUpdateSplits = {}; this._previousSplit = 0; this.axes = this.createChartAxes(); this.series = []; this._cachedData = void 0; this.onSeriesNodeClick = (event) => { this.fireEvent(event); }; this.onSeriesNodeDoubleClick = (event) => { this.fireEvent(event); }; this.onSeriesVisibilityChange = (event) => { this.fireEvent(event); }; this.seriesGroupingChanged = (event) => { if (!(event instanceof SeriesGroupingChangedEvent)) return; const { series, seriesGrouping } = event; if (series.contentGroup.isRoot()) return; const seriesContentNode = this.seriesLayerManager.changeGroup({ internalId: series.internalId, type: series.type, contentGroup: series.contentGroup, bringToFront: () => series.bringToFront(), renderToOffscreenCanvas: () => series.renderToOffscreenCanvas(), seriesGrouping }); if (seriesContentNode != null) { series.attachSeries(seriesContentNode, this.seriesRoot, this.annotationRoot); } }; this.chartOptions = options; const scene = resources?.scene; const container = resources?.container ?? options.processedOptions.container ?? void 0; const styleContainer = resources?.styleContainer ?? options.specialOverrides.styleContainer; const skipCss = options.specialOverrides.skipCss; if (scene) { this._firstAutoSize = false; this._lastAutoSize = [scene.width, scene.height, scene.pixelRatio]; } const root = new Group({ name: "root" }); root.visible = false; root.append(this.seriesRoot); root.append(this.annotationRoot); root.append(this.titleGroup); this.titleGroup.append(this.title.node); this.titleGroup.append(this.subtitle.node); this.titleGroup.append(this.footnote.node); this.tooltip = new Tooltip(); this.seriesLayerManager = new SeriesLayerManager(this.seriesRoot); this.mode = options.userOptions.mode ?? this.mode; this.styleNonce = options.processedOptions.styleNonce; const ctx = this.ctx = new ChartContext(this, { chartType: this.getChartType(), scene, root, container, styleContainer, skipCss, domMode: options.optionMetadata.domMode, withDragInterpretation: options.optionMetadata.withDragInterpretation ?? true, syncManager: new SyncManager(this), fireEvent: (event) => this.fireEvent(event), updateCallback: (type, opts) => this.update(type, opts), updateMutex: this.updateMutex }); if (options.optionMetadata.presetType === "sparkline") { ctx.highlightManager.unhighlightDelay = 0; ctx.tooltipManager.removeDelay = 0; } this.cleanup.register(ctx.eventsHub.on("dom:resize", () => this.parentResize(ctx.domManager.containerSize))); this.overlays = new ChartOverlays(); (_a = this.overlays.loading).renderer ?? (_a.renderer = () => getLoadingSpinner(this.overlays.loading.getText(ctx.localeManager), ctx.animationManager.defaultDuration)); this.processors = [ new DataWindowProcessor( this, ctx.eventsHub, ctx.dataService, ctx.updateService, ctx.zoomManager, ctx.animationManager ), new OverlaysProcessor( this, this.overlays, ctx.eventsHub, ctx.dataService, ctx.localeManager, ctx.animationManager, ctx.domManager ) ]; this.highlight = new ChartHighlight(); this.container = container; const moduleContext = this.getModuleContext(); this.background = enterpriseRegistry.createBackground?.(moduleContext) ?? new Background(moduleContext); this.foreground = enterpriseRegistry.createForeground?.(moduleContext); this.seriesArea = new SeriesArea(moduleContext); ctx.domManager.setDataBoolean("animating", false); ctx.domManager.setDataNumber("animationTimeMs", 0); this.seriesAreaManager = new SeriesAreaManager(this.initSeriesAreaDependencies()); this.cleanup.register( ctx.layoutManager.registerElement(0 /* Caption */, (e) => { e.layoutBox.shrink(this.padding.toJson()); this.chartCaptions.positionCaptions(e); }), ctx.eventsHub.on("layout:complete", (e) => this.chartCaptions.positionAbsoluteCaptions(e)), ctx.eventsHub.on("data:load", (event) => { this.data = new DataSet(event.data); }), this.title.registerInteraction(moduleContext, "beforebegin"), this.subtitle.registerInteraction(moduleContext, "beforebegin"), this.footnote.registerInteraction(moduleContext, "afterend"), () => this.title.destroy(), () => this.subtitle.destroy(), () => this.footnote.destroy(), Widget.addWindowEvent("page-left", () => this.destroy()), ctx.animationManager.addListener("animation-frame", () => { this.update(9 /* SCENE_RENDER */); ctx.domManager.setDataNumber("animationTimeMs", ctx.animationManager.getCumulativeAnimationTime()); }), ctx.animationManager.addListener("animation-start", () => ctx.domManager.setDataBoolean("animating", true)), ctx.animationManager.addListener("animation-stop", () => { ctx.domManager.setDataBoolean("animating", false); ctx.domManager.setDataNumber("animationTimeMs", ctx.animationManager.getCumulativeAnimationTime()); }), ctx.eventsHub.on("zoom:change-complete", () => { for (const s of this.series) { s.animationState?.transition("updateData"); } const skipAnimations = this.chartAnimationPhase !== "initial"; this.update(5 /* PERFORM_LAYOUT */, { forceNodeDataRefresh: true, skipAnimations }); }) ); this.parentResize(ctx.domManager.containerSize); } static getInstance(element2) { return _Chart.chartsInstances.get(element2); } /** NOTE: This is exposed for use by Integrated charts only. */ get canvasElement() { return this.ctx.scene.canvas.element; } download(fileName, fileFormat) { this.ctx.scene.download(fileName, fileFormat); } getCanvasDataURL(fileFormat) { return this.ctx.scene.getDataURL(fileFormat); } toSVG() { return this.ctx.scene.toSVG(); } get seriesAreaBoundingBox() { return this.seriesAreaManager.bbox; } getOptions() { return this.queuedUserOptions.at(-1) ?? this.chartOptions.userOptions; } getChartOptions() { return this.queuedChartOptions.at(-1) ?? this.chartOptions; } isDataTransactionSupported() { return true; } overrideFocusVisible(visible) { this.seriesAreaManager.focusIndicator?.overrideFocusVisible(visible); } fireEvent(event) { callWithContext(this, this.fireEventWrapper, event); } initSeriesAreaDependencies() { const { ctx, tooltip, highlight: highlight5, overlays, seriesRoot, mode } = this; const chartType = this.getChartType(); const fireEvent = this.fireEvent.bind(this); const getUpdateType = () => this.performUpdateType; const getTooltipContent = (series, datumIndex, removeThisDatum, purpose) => this.getTooltipContent(series, datumIndex, removeThisDatum, purpose); return { fireEvent, getUpdateType, getTooltipContent, chartType, ctx, tooltip, highlight: highlight5, overlays, seriesRoot, mode }; } getModuleContext() { return this.ctx; } getTooltipContent(series, datumIndex, removeMeDatum, purpose) { const useTooltip = purpose === "aria-label" || series.properties.tooltip.enabled !== false; const baseTooltipContent = useTooltip ? series.getTooltipContent(datumIndex, removeMeDatum) : void 0; const tooltipContent = baseTooltipContent == null ? [] : [baseTooltipContent]; if (this.tooltip.mode !== "shared" || this.series.length === 1) { return tooltipContent; } const categoryValue = series.getCategoryValue(datumIndex); if (categoryValue == null) return tooltipContent; return this.series.flatMap((s) => { if (s === series) return tooltipContent; if (!s.isEnabled() || s.properties.tooltip.enabled === false) return []; const seriesDatumIndex = s.datumIndexForCategoryValue(categoryValue); const seriesTooltipContent = seriesDatumIndex == null ? void 0 : s.getTooltipContent(seriesDatumIndex, void 0); if (seriesTooltipContent == null) return []; return [seriesTooltipContent]; }); } getCaptionText() { return [this.title, this.subtitle, this.footnote].filter((caption) => caption.enabled && caption.text).map((caption) => caption.text).join(". "); } getAriaLabel() { return this.ctx.localeManager.t("ariaAnnounceChart", { seriesCount: this.series.length }); } refreshSeriesUserVisibility(outdatedOptions, seriesWithUserVisibility) { for (let i = 0; i < this.series.length; i++) { const src = this.series[i]; const dst = outdatedOptions.processedOptions.series?.[i]; if (seriesWithUserVisibility.identifiers.has(src.id) || seriesWithUserVisibility.indices.has(i)) { if (dst !== void 0 && "visible" in dst) { dst.visible = src.visible; } } } } resetAnimations() { this.chartAnimationPhase = "initial"; for (const series of this.series) { series.resetAnimation(this.chartAnimationPhase); } for (const axis of this.axes) { axis.resetAnimation(this.chartAnimationPhase); } this.animationRect = void 0; this.ctx.animationManager.reset(); } skipAnimations() { this.ctx.animationManager.skipCurrentBatch(); this._performUpdateSkipAnimations = true; } detachAndClear() { this.container = void 0; this.ctx.scene.clearCanvas(); } destroy(opts) { if (this.destroyed) { return; } const keepTransferableResources = opts?.keepTransferableResources; let result; this.performUpdateType = 10 /* NONE */; this.cleanup.flush(); for (const p of this.processors) { p.destroy(); } this.overlays.destroy(); this.modulesManager.destroy(); this.background.destroy(); this.foreground?.destroy(); this.seriesArea.destroy(); if (keepTransferableResources) { this.ctx.scene.strip(); result = { container: this.container, scene: this.ctx.scene }; } else { this.ctx.scene.destroy(); this.container = void 0; } this.destroySeries(this.series); this.seriesLayerManager.destroy(); this.axes.destroy(); this.animationRect = void 0; this.ctx.destroy(); this.destroyed = true; Object.freeze(this); return result; } requestFactoryUpdate(cb) { if (this.destroyed) return; this._pendingFactoryUpdatesCount++; this.updateMutex.acquire(async () => { if (this.destroyed) return; try { await cb(this); } finally { if (!this.destroyed) { this._pendingFactoryUpdatesCount--; } } }).catch((e) => logger_exports.errorOnce(e)); } clearCallbackCache() { this.ctx.callbackCache.invalidateCache(); for (const series of this.series) { series.resetDatumCallbackCache(); } } update(type = 0 /* FULL */, opts) { if (this.destroyed) return; const { forceNodeDataRefresh = false, skipAnimations, seriesToUpdate = this.series, newAnimationBatch, apiUpdate = false, clearCallbackCache = false } = opts ?? {}; this.apiUpdate = apiUpdate; this.ctx.widgets.seriesWidget.setDragTouchEnabled(this.touch.dragAction !== "none"); if (forceNodeDataRefresh) { for (const series of this.series) { series.markNodeDataDirty(); } } for (const series of seriesToUpdate) { this.seriesToUpdate.add(series); } if (skipAnimations) { this.ctx.animationManager.skipCurrentBatch(); this._performUpdateSkipAnimations = true; } if (newAnimationBatch && this.ctx.animationManager.isActive()) { this._performUpdateSkipAnimations = true; } if (type === 0 /* FULL */ || clearCallbackCache) { this.clearCallbackCacheOnUpdate = true; } if (this.debug.check()) { let stack = new Error("Stack trace for update tracking").stack ?? ""; stack = stack.replaceAll(/\([^)]*/g, ""); this.updateRequestors[stack] = type; } if (type < this.performUpdateType) { this.performUpdateType = type; this.ctx.domManager.setDataBoolean("updatePending", true); this.performUpdateTrigger.schedule(opts?.backOffMs); } } updateSplits(splitName) { const splits = this._performUpdateSplits; splits[splitName] ?? (splits[splitName] = 0); splits[splitName] += performance.now() - this._previousSplit; this._previousSplit = performance.now(); } async tryPerformUpdate(count) { try { await this.performUpdate(count); } catch (error2) { logger_exports.error("update error", error2, error2.stack); } } async performUpdate(count) { const { performUpdateType, extraDebugStats, _performUpdateSplits: splits, ctx } = this; const seriesToUpdate = [...this.seriesToUpdate]; if (this.clearCallbackCacheOnUpdate) { this.clearCallbackCacheOnUpdate = false; this.clearCallbackCache(); } this.performUpdateType = 10 /* NONE */; this.seriesToUpdate.clear(); this.runningUpdateType = performUpdateType; if (this.updateShortcutCount === 0 && performUpdateType < 9 /* SCENE_RENDER */) { ctx.animationManager.startBatch(this._performUpdateSkipAnimations); ctx.animationManager.onBatchStop(() => this.chartAnimationPhase = "ready"); } this.ctx.scene.updateDebugFlags(); this.debug("Chart.performUpdate() - start", ChartUpdateType[performUpdateType]); this._previousSplit = performance.now(); splits.start ?? (splits.start = this._previousSplit); switch (performUpdateType) { case 0 /* FULL */: if (this.checkUpdateShortcut(0 /* FULL */)) break; this.ctx.updateService.dispatchPreDomUpdate(); this.updateDOM(); case 1 /* UPDATE_DATA */: if (this.checkUpdateShortcut(1 /* UPDATE_DATA */)) break; this.updateData(); this.updateSplits("\u2B07\uFE0F"); case 2 /* PROCESS_DATA */: if (this.checkUpdateShortcut(2 /* PROCESS_DATA */)) break; await this.processData(); this.seriesAreaManager.dataChanged(); if (this.pendingLocaleText) { const localeModule = this.modulesManager.getModule("locale"); if (localeModule && "localeText" in localeModule) { localeModule.localeText = this.pendingLocaleText; } this.pendingLocaleText = void 0; } this.updateSplits("\u{1F4CA}"); case 3 /* PROCESS_DOMAIN */: if (this.checkUpdateShortcut(3 /* PROCESS_DOMAIN */)) break; await this.processDomains(); this.updateSplits("\u26F0\uFE0F"); case 4 /* PROCESS_RANGE */: if (this.checkUpdateShortcut(4 /* PROCESS_RANGE */)) break; this.processRanges(); this.updateSplits("\u{1F4D0}"); case 5 /* PERFORM_LAYOUT */: await this.checkFirstAutoSize(); if (this.checkUpdateShortcut(5 /* PERFORM_LAYOUT */)) break; await this.processLayout(); this.updateSplits("\u2316"); case 6 /* PRE_SERIES_UPDATE */: if (this.checkUpdateShortcut(6 /* PRE_SERIES_UPDATE */)) break; this.preSeriesUpdate(); this.updateSplits("\u2753"); case 7 /* SERIES_UPDATE */: { if (this.checkUpdateShortcut(7 /* SERIES_UPDATE */)) break; this.seriesRoot.renderToOffscreenCanvas = this.highlight.drawingMode === "cutout"; await this.updateSeries(seriesToUpdate); this.updateAriaLabels(); this.seriesLayerManager.updateLayerCompositing(); this.updateSplits("\u{1F914}"); } case 8 /* PRE_SCENE_RENDER */: if (this.checkUpdateShortcut(8 /* PRE_SCENE_RENDER */)) break; ctx.updateService.dispatchPreSceneRender(); this.updateSplits("\u2196"); case 9 /* SCENE_RENDER */: if (this.checkUpdateShortcut(9 /* SCENE_RENDER */)) break; ctx.animationManager.endBatch(); extraDebugStats["updateShortcutCount"] = this.updateShortcutCount; ctx.scene.render({ debugSplitTimes: splits, extraDebugStats, seriesRect: this.seriesRect, debugColors: this.getDebugColors() }); this.extraDebugStats = {}; for (const key of Object.keys(splits)) { delete splits[key]; } this.ctx.domManager.incrementDataCounter("sceneRenders"); this.ctx.domManager.postRenderUpdate(); case 10 /* NONE */: this.updateShortcutCount = 0; this.updateRequestors = {}; this._performUpdateSkipAnimations = false; ctx.animationManager.endBatch(); } if (!this.destroyed) { ctx.updateService.dispatchUpdateComplete(this.apiUpdate, this.updateShortcutCount > 0); this.apiUpdate = false; this.ctx.domManager.setDataBoolean("updatePending", false); this.runningUpdateType = 10 /* NONE */; this.syncStatus = "ready"; } this._performUpdateNotify.notify(); const end3 = performance.now(); this.debug("Chart.performUpdate() - end", { chart: this, durationMs: roundTo(end3 - splits["start"]), count, performUpdateType: ChartUpdateType[performUpdateType] }); } updateThemeClassName() { const themeClassNamePrefix = "ag-charts-theme-"; const validThemeClassNames = [`${themeClassNamePrefix}default`, `${themeClassNamePrefix}default-dark`]; let themeClassName = validThemeClassNames[0]; let isDark = false; let { theme } = this.chartOptions.processedOptions; while (typeof theme !== "string" && theme != null) { theme = theme.baseTheme; } if (typeof theme === "string") { themeClassName = theme.replace("ag-", themeClassNamePrefix); isDark = theme.includes("-dark"); } if (!validThemeClassNames.includes(themeClassName)) { themeClassName = isDark ? validThemeClassNames[1] : validThemeClassNames[0]; } this.ctx.domManager.setThemeClass(themeClassName); } updateDOM() { this.updateThemeClassName(); const { enabled, tabIndex } = this.keyboard; this.ctx.domManager.setTabGuardIndex(enabled ? tabIndex ?? 0 : -1); this.ctx.domManager.setThemeParameters(this.chartOptions.themeParameters); } updateAriaLabels() { this.ctx.domManager.updateCanvasLabel(this.getAriaLabel()); } checkUpdateShortcut(checkUpdateType) { const maxShortcuts = 3; if (this.destroyed) return true; if (this.updateShortcutCount > maxShortcuts) { logger_exports.warn( `exceeded the maximum number of simultaneous updates (${maxShortcuts + 1}), discarding changes and rendering`, this.updateRequestors ); return false; } if (this.performUpdateType <= checkUpdateType) { this.debug("Chart.checkUpdateShortcut() - BLOCKED AT: ", ChartUpdateType[checkUpdateType]); this.updateShortcutCount++; return true; } this.debug("Chart.checkUpdateShortcut() - PROCEEDING TO: ", ChartUpdateType[checkUpdateType]); return false; } async checkFirstAutoSize() { if (this.width != null && this.height != null) { } else if (!this._lastAutoSize) { const success = await this._autoSizeNotify.waitForCompletion(500); if (!success) { this.debug("Chart.checkFirstAutoSize() - timeout for first size update."); } } } createChartAxes() { return new ChartAxes(); } onAxisChange(newValue, oldValue) { if (oldValue == null && newValue.length === 0) return; this.ctx.axisManager.updateAxes(oldValue ?? [], newValue); } onSeriesChange(newValue, oldValue) { const seriesToDestroy = oldValue?.filter((series) => !newValue.includes(series)) ?? []; this.destroySeries(seriesToDestroy); this.seriesLayerManager?.setSeriesCount(newValue.length); for (const series of newValue) { if (oldValue?.includes(series)) continue; const seriesContentNode = this.seriesLayerManager.requestGroup(series); series.attachSeries(seriesContentNode, this.seriesRoot, this.annotationRoot); series.chart = {}; Object.defineProperty(series.chart, "mode", { get: () => this.mode }); Object.defineProperty(series.chart, "isMiniChart", { get: () => false }); Object.defineProperty(series.chart, "flashOnUpdateEnabled", { get: () => !!this.modulesManager.getModule("flashOnUpdate")?.enabled }); Object.defineProperty(series.chart, "seriesRect", { get: () => this.seriesRect }); series.resetAnimation(this.chartAnimationPhase); this.addSeriesListeners(series); } this.seriesAreaManager?.seriesChanged(newValue); } destroySeries(allSeries) { if (allSeries) { for (const series of allSeries) { series.removeEventListener("seriesNodeClick", this.onSeriesNodeClick); series.removeEventListener("seriesNodeDoubleClick", this.onSeriesNodeDoubleClick); series.removeEventListener("groupingChanged", this.seriesGroupingChanged); series.destroy(); this.seriesLayerManager.releaseGroup(series); series.detachSeries(void 0, this.seriesRoot, this.annotationRoot); series.chart = void 0; } } } addSeriesListeners(series) { if (this.hasEventListener("seriesNodeClick")) { series.addEventListener("seriesNodeClick", this.onSeriesNodeClick); } if (this.hasEventListener("seriesNodeDoubleClick")) { series.addEventListener("seriesNodeDoubleClick", this.onSeriesNodeDoubleClick); } if (this.hasEventListener("seriesVisibilityChange")) { series.addEventListener("seriesVisibilityChange", this.onSeriesVisibilityChange); } series.addEventListener("groupingChanged", this.seriesGroupingChanged); } assignSeriesToAxes() { for (const axis of this.axes) { let seriesPredicateFn2 = function(s) { return s.axes[axis.direction] === axis; }; var seriesPredicateFn = seriesPredicateFn2; axis.boundSeries = this.series.filter(seriesPredicateFn2); } } assignAxesToSeries() { for (const series of this.series) { for (const direction of series.directions) { const seriesAxisId = series.getKeyAxis(direction) ?? direction; const newAxis = this.axes.findById(seriesAxisId); if (!newAxis) { logger_exports.warnOnce( `no matching axis for direction [${direction}] and id [${seriesAxisId}]; check series and axes configuration.` ); return; } series.axes[direction] = newAxis; } } } parentResize(size) { if (size == null || this.width != null && this.height != null) return; let { width: width2, height: height2 } = size; const { pixelRatio } = size; width2 = Math.floor(width2); height2 = Math.floor(height2); if (width2 === 0 && height2 === 0) return; const [autoWidth = 0, autoHeight = 0, autoPixelRatio = 1] = this._lastAutoSize ?? []; if (autoWidth === width2 && autoHeight === height2 && autoPixelRatio === pixelRatio) return; this._lastAutoSize = [width2, height2, pixelRatio]; this.resize("SizeMonitor", {}); } resize(source, opts) { const { scene, animationManager } = this.ctx; const { inWidth, inHeight, inMinWidth, inMinHeight, inOverrideDevicePixelRatio } = opts; this.ctx.domManager.setSizeOptions( inMinWidth ?? this.minWidth, inMinHeight ?? this.minHeight, inWidth ?? this.width, inHeight ?? this.height ); const width2 = inWidth ?? this.width ?? this._lastAutoSize?.[0]; const height2 = inHeight ?? this.height ?? this._lastAutoSize?.[1]; const pixelRatio = inOverrideDevicePixelRatio ?? this.overrideDevicePixelRatio ?? this._lastAutoSize?.[2]; this.debug(`Chart.resize() from ${source}`, { width: width2, height: height2, pixelRatio, stack: new Error("Stack trace for resize tracking").stack }); if (width2 == null || height2 == null || !isFiniteNumber(width2) || !isFiniteNumber(height2)) return; if (scene.resize(width2, height2, pixelRatio)) { animationManager.reset(); let skipAnimations = true; if ((this.width == null || this.height == null) && this._firstAutoSize) { skipAnimations = false; this._firstAutoSize = false; } let updateType = 5 /* PERFORM_LAYOUT */; for (const axis of this.axes) { const axisUpdateType = axis.getUpdateTypeOnResize(); if (axisUpdateType < updateType) { updateType = axisUpdateType; } } this.update(updateType, { forceNodeDataRefresh: true, skipAnimations }); this._autoSizeNotify.notify(); } } updateData() { this.ctx.eventsHub.emit("data:update", this.data); } async processData() { if (this.series.some((s) => s.canHaveAxes)) { this.assignAxesToSeries(); this.assignSeriesToAxes(); } const dataController = new DataController(this.mode, this.suppressFieldDotNotation, this.ctx.eventsHub); const promises = []; for (const series of this.series) { promises.push(series.processData(dataController) ?? Promise.resolve()); } for (const module2 of this.modulesManager.modules()) { if (module2?.processData) { promises.push(module2.processData(dataController) ?? Promise.resolve()); } } this._cachedData = dataController.execute(this._cachedData); this.updateSplits("\u{1F3ED}"); await Promise.all(promises); this.updateLegends(); } // eslint-disable-next-line @typescript-eslint/require-await async processDomains() { for (const axis of this.axes) { axis.processData(); } for (const series of this.series) { series.updatedDomains(); } } processRanges() { var _a; const seriesRanges = {}; const chartRanges = {}; const seriesTypes = /* @__PURE__ */ new Map(); this._requiredRangeDirection = "x" /* X */; for (const series of this.series) { if (!series.visible) continue; seriesRanges[_a = series.type] ?? (seriesRanges[_a] = []); series.getMinimumRangeSeries(seriesRanges[series.type]); if (series.resolveKeyDirection("x" /* X */) === "y" /* Y */) { this._requiredRangeDirection = "y" /* Y */; } if (!seriesTypes.has(series.type)) { seriesTypes.set(series.type, series); } } for (const [type, firstSeries] of seriesTypes) { chartRanges[type] = firstSeries.getMinimumRangeChart(seriesRanges[type]); } if (Object.keys(chartRanges).length === 0) { this._requiredRange = 0; } else { this._requiredRange = Math.ceil(Math.max(...Object.values(chartRanges))); } for (const axis of this.axes) { axis.requiredRange = this._requiredRange; } } updateLegends(initialStateLegend) { for (const module2 of moduleRegistry_exports.listModulesByType("plugin" /* Plugin */)) { switch (module2.name) { case "legend": this.setCategoryLegendData(initialStateLegend); break; case "gradientLegend": const moduleInstance = this.modulesManager.getModule("gradientLegend"); moduleInstance.data = this.series.filter((s) => s.properties.showInLegend).flatMap((s) => s.getLegendData("gradient")); break; } } } setCategoryLegendData(initialState) { const { legendManager, stateManager } = this.ctx; if (initialState) { for (const s of this.series) { const seriesState = initialState.find((init) => init.seriesId === s.id); s.onLegendInitialState("category", seriesState); } } const legendData = this.series.flatMap((s) => { const seriesLegendData = s.getLegendData("category"); legendManager.updateData(s.id, seriesLegendData); return seriesLegendData; }); if (initialState) { stateManager.setStateAndRestore(legendManager, initialState); return; } if (this.mode !== "integrated") { const seriesMarkerFills = {}; const seriesMap = new Map(this.series.map((s) => [s.id, s])); for (const { seriesId, symbol: { marker }, label } of legendData.filter((d) => !d.hideInLegend)) { if (marker.fill == null) continue; const series = seriesMap.get(seriesId); if (!series?.hasData) continue; const seriesType = series.type; const markerFill = seriesMarkerFills[seriesType] ?? (seriesMarkerFills[seriesType] = /* @__PURE__ */ new Map()); if (markerFill.has(label.text)) { if (markerFill.get(label.text) !== marker.fill) { logger_exports.warnOnce( `legend item '${toPlainText(label.text)}' has multiple fill colours, this may cause unexpected behaviour.` ); } } else { markerFill.set(label.text, marker.fill); } } } legendManager.update(); } async processLayout() { const oldRect = this.animationRect; const { width: width2, height: height2 } = this.ctx.scene; const ctx = this.ctx.layoutManager.createContext(width2, height2); await this.performLayout(ctx); if (oldRect && !this.animationRect?.equals(oldRect)) { this.ctx.animationManager.skipCurrentBatch(); } this.debug("Chart.performUpdate() - seriesRect", this.seriesRect); } getDebugColors() { const bg = this.background.fill; if (!bg) return void 0; try { const color2 = Color.fromString(bg); const [lightness] = Color.RGBtoOKLCH(color2.r, color2.g, color2.b); return { background: bg, foreground: lightness > 0.5 ? "black" : "white" }; } catch { return { background: bg }; } } preSeriesUpdate() { const { _requiredRange, seriesRect } = this; if (seriesRect == null) return; const dimension = this._requiredRangeDirection === "x" /* X */ ? seriesRect.width : seriesRect.height; const requiredRangeRatio = _requiredRange / dimension || 0; this.ctx.updateService.dispatchPreSeriesUpdate(requiredRangeRatio, this._requiredRangeDirection); } async updateSeries(seriesToUpdate) { const { seriesRect } = this; function seriesUpdate(series) { return series.update({ seriesRect }); } await Promise.all(seriesToUpdate.map(seriesUpdate).filter((p) => p != null)); this.ctx.seriesLabelLayoutManager.updateLabels( this.series.filter((s) => s.visible && s.usesPlacedLabels), this.padding, this.seriesRect ); } async waitForUpdate(timeoutMs, failOnTimeout) { const agChartsDebugTimeout = getWindow("agChartsDebugTimeout"); if (agChartsDebugTimeout == null) { timeoutMs ?? (timeoutMs = 1e4); failOnTimeout ?? (failOnTimeout = false); } else { timeoutMs = agChartsDebugTimeout; failOnTimeout ?? (failOnTimeout = true); } const start2 = performance.now(); while (this._pendingFactoryUpdatesCount > 0 || this.performUpdateType !== 10 /* NONE */ || this.runningUpdateType !== 10 /* NONE */ || this.ctx.scene.waitingForUpdate() || this.data.hasPendingTransactions()) { if (this.destroyed) break; if (this._pendingFactoryUpdatesCount > 0) { await this.updateMutex.waitForClearAcquireQueue(); } if (this.performUpdateType !== 10 /* NONE */ || this.runningUpdateType !== 10 /* NONE */ || this.data.hasPendingTransactions()) { await this._performUpdateNotify.waitForCompletion(); } if (performance.now() - start2 > timeoutMs) { const message = `Chart.waitForUpdate() timeout of ${timeoutMs} reached - first chart update taking too long.`; if (failOnTimeout) { throw new Error(message); } else { logger_exports.warnOnce(message); } } if (isInputPending()) { await pause(); } if (this.ctx.scene.waitingForUpdate()) { await pause(50); } } } filterMiniChartSeries(series) { return series?.filter((s) => s.showInMiniChart !== false); } applyOptions(newChartOptions) { if (newChartOptions.seriesWithUserVisibility) { this.refreshSeriesUserVisibility(this.chartOptions, newChartOptions.seriesWithUserVisibility); } const minimumUpdateType = 5 /* PERFORM_LAYOUT */; const deltaOptions = this.firstApply ? newChartOptions.processedOptions : newChartOptions.diffOptions(this.chartOptions); if (deltaOptions == null || Object.keys(deltaOptions).length === 0) { debug("Chart.applyOptions() - no delta, forcing re-layout", deltaOptions); this.update(minimumUpdateType, { apiUpdate: true, newAnimationBatch: true }); return; } const oldOpts = this.firstApply ? {} : this.chartOptions.processedOptions; const newOpts = newChartOptions.processedOptions; debug("Chart.applyOptions() - applying delta", deltaOptions); const modulesChanged = this.applyModules(); const skip = [ "type", "data", "series", "listeners", "preset", "theme", "legend.listeners", "navigator.miniChart.series", "navigator.miniChart.label", "locale.localeText", "axes", "topology", "nodes", "initialState", "styleContainer", "formatter", "displayNullData" ]; if ("listeners" in deltaOptions) { this.registerListeners(this, deltaOptions.listeners); } jsonApply(this, deltaOptions, { skip }); let forceNodeDataRefresh = false; let seriesStatus = "no-op"; if (deltaOptions.series != null) { seriesStatus = this.applySeries(this, deltaOptions.series, oldOpts?.series); forceNodeDataRefresh = true; } if (seriesStatus === "replaced") { this.resetAnimations(); } if (this.applyAxes(this, newOpts, oldOpts, seriesStatus, [])) { forceNodeDataRefresh = true; } const { userDeltaKeys } = newChartOptions; const userExplicitlyPassedData = userDeltaKeys === void 0 || userDeltaKeys.has("data"); if (deltaOptions.data && userExplicitlyPassedData) { const suppliedData = deltaOptions.data; const userOptionsData = newChartOptions.userOptions.data; const needsClone = Array.isArray(suppliedData) && suppliedData !== userOptionsData; const dataForDataSet = needsClone ? suppliedData.slice() : suppliedData; this.data = new DataSet(dataForDataSet); } if ("legend" in deltaOptions && deltaOptions.legend && "listeners" in deltaOptions.legend && this.modulesManager.isEnabled("legend")) { const legendListeners = deltaOptions.legend.listeners; if (legendListeners) { Object.assign(this.legend.listeners, legendListeners); } else { this.legend.listeners.clear(); } } if (deltaOptions.locale?.localeText) { this.pendingLocaleText = deltaOptions.locale?.localeText; } this.chartOptions = newChartOptions; const navigatorModule = this.modulesManager.getModule("navigator"); const zoomModule = this.modulesManager.getModule("zoom"); const scrollbarModule = this.modulesManager.getModule("scrollbar"); if (!navigatorModule?.enabled && !zoomModule?.enabled && !scrollbarModule?.enabled) { this.ctx.zoomManager.updateZoom( { source: "chart-update", sourceDetail: "internal-applyOptions" }, { x: { min: 0, max: 1 } } ); } const miniChart = navigatorModule?.miniChart; const miniChartSeries = newOpts.navigator?.miniChart?.series ?? newOpts.series; if (miniChart?.enabled === true && miniChartSeries != null) { this.applyMiniChartOptions(miniChart, miniChartSeries, newOpts, oldOpts); } else if (miniChart?.enabled === false) { miniChart.series = []; miniChart.axes = []; } this.ctx.annotationManager.setAnnotationStyles(newChartOptions.annotationThemes); forceNodeDataRefresh || (forceNodeDataRefresh = this.shouldForceNodeDataRefresh(deltaOptions, seriesStatus)); const majorChange = forceNodeDataRefresh || modulesChanged; const updateType = majorChange ? 0 /* FULL */ : minimumUpdateType; this.maybeResetAnimations(seriesStatus); if (this.shouldClearLegendData(newOpts, oldOpts, seriesStatus)) { this.ctx.legendManager.clearData(); } this.applyInitialState(newOpts); this.ctx.formatManager.setFormatter(newOpts.formatter); debug("Chart.applyOptions() - update type", ChartUpdateType[updateType], { seriesStatus, forceNodeDataRefresh }); if (newChartOptions.optionsProcessingTime !== void 0) { this._performUpdateSplits["\u2699\uFE0F"] = newChartOptions.optionsProcessingTime; const optionsStartTime = performance.now() - newChartOptions.optionsProcessingTime; this._performUpdateSplits.start = optionsStartTime; } this.update(updateType, { apiUpdate: true, forceNodeDataRefresh, newAnimationBatch: true, clearCallbackCache: true }); this.firstApply = false; } applyInitialState(options) { const { activeManager, annotationManager, chartTypeOriginator, historyManager, stateManager, zoomManager } = this.ctx; const { initialState } = options; if ("annotations" in options && options.annotations?.enabled && initialState?.annotations != null) { const annotations = initialState.annotations.map((annotation) => { const annotationTheme = annotationManager.getAnnotationTypeStyles(annotation.type); return mergeDefaults(annotation, annotationTheme); }); stateManager.setState(annotationManager, annotations); } if (initialState?.chartType != null) { stateManager.setState(chartTypeOriginator, initialState.chartType); } if ((options.navigator?.enabled || options.zoom?.enabled || options.scrollbar?.enabled) && initialState?.zoom != null) { stateManager.setState(zoomManager, initialState.zoom); } if (initialState?.active != null) { stateManager.setState(activeManager, initialState.active); } if (initialState?.legend != null) { this.updateLegends(initialState.legend); } if (initialState != null) { historyManager.clear(); } } maybeResetAnimations(seriesStatus) { if (this.mode !== "standalone") return; switch (seriesStatus) { case "series-grouping-change": case "replaced": this.resetAnimations(); break; default: } } shouldForceNodeDataRefresh(deltaOptions, seriesStatus) { const seriesDataUpdate = !!deltaOptions.data || seriesStatus === "data-change" || seriesStatus === "replaced"; const optionsHaveLegend = ["legend", "gradientLegend"].some( (legendKey) => deltaOptions[legendKey] != null ); const otherRefreshUpdate = deltaOptions.title != null && deltaOptions.subtitle != null || deltaOptions.formatter != null; return seriesDataUpdate || optionsHaveLegend || otherRefreshUpdate; } shouldClearLegendData(options, oldOpts, seriesStatus) { const seriesChanged = seriesStatus === "replaced" || seriesStatus === "series-count-changed" || seriesStatus === "series-grouping-change" || seriesStatus === "updated" && (options.series?.length !== oldOpts.series?.length || !options.series?.every((s, i) => s.type === oldOpts.series?.[i].type)); const legendRemoved = oldOpts.legend != null && oldOpts.legend.enabled !== false && (options.legend == null || options.legend.enabled === false); return seriesChanged || legendRemoved; } applyMiniChartOptions(miniChart, miniChartSeries, completeOptions, oldOpts) { const oldSeries = oldOpts?.navigator?.miniChart?.series ?? oldOpts?.series; const miniChartSeriesStatus = this.applySeries( miniChart, this.filterMiniChartSeries(miniChartSeries), this.filterMiniChartSeries(oldSeries) ); this.applyAxes(miniChart, completeOptions, oldOpts, miniChartSeriesStatus, [ "tick", "thickness", "title", "crosshair", "gridLine", "label" ]); const series = miniChart.series; for (const s of series) { s.properties.id = void 0; } const axes = miniChart.axes; const horizontalAxis = axes.find((axis) => axis.direction === "x" /* X */); for (const axis of axes) { axis.nice = false; axis.gridLine.enabled = false; axis.label.enabled = axis === horizontalAxis; axis.tick.enabled = false; axis.interactionEnabled = false; } if (horizontalAxis != null) { const miniChartOpts = completeOptions.navigator?.miniChart; const labelOptions = miniChartOpts?.label; const intervalOptions = miniChartOpts?.label?.interval; horizontalAxis.line.enabled = false; horizontalAxis.label.set( without(labelOptions, [ "interval", "autoRotate", "autoRotateAngle", "itemStyler", "minSpacing", "rotation" ]) ); if (horizontalAxis.type === "grouped-category") { horizontalAxis.label.enabled = false; horizontalAxis.label.rotation = 0; const { depthOptions } = horizontalAxis; if (depthOptions.length === 0) { depthOptions.set([{ label: { enabled: true } }]); } else { for (let i = 1; i < depthOptions.length; i++) { depthOptions[i].label.enabled = false; } } } else if (horizontalAxis.type === "time" || horizontalAxis.type === "unit-time" || horizontalAxis.type === "ordinal-time") { horizontalAxis.parentLevel.enabled = false; } horizontalAxis.interval.step = intervalOptions?.step; horizontalAxis.interval.values = intervalOptions?.values; horizontalAxis.interval.minSpacing = intervalOptions?.minSpacing; horizontalAxis.interval.maxSpacing = intervalOptions?.maxSpacing; } } applyModules() { const { type: chartType } = this.constructor; let modulesChanged = false; for (const module2 of moduleRegistry_exports.listModulesByType("plugin" /* Plugin */)) { const shouldBeEnabled = !module2.chartType || module2.chartType === chartType; if (shouldBeEnabled === this.modulesManager.isEnabled(module2.name)) continue; if (shouldBeEnabled) { const moduleInstance = module2.create(this.getModuleContext()); this.modulesManager.addModule(module2.name, moduleInstance); this[module2.name] = moduleInstance; } else { this.modulesManager.removeModule(module2.name); delete this[module2.name]; } modulesChanged = true; } return modulesChanged; } initSeriesDeclarationOrder(series) { for (let idx = 0; idx < series.length; idx++) { series[idx].setSeriesIndex(idx); } } applySeries(chart, optSeries, oldOptSeries) { if (!optSeries) { return "no-change"; } const matchResult = matchSeriesOptions(chart.series, optSeries, oldOptSeries); if (matchResult.status === "no-overlap") { debug(`Chart.applySeries() - creating new series instances, status: ${matchResult.status}`, matchResult); const chartSeries = optSeries.map((opts) => this.createSeries(opts)); this.initSeriesDeclarationOrder(chartSeries); chart.series = chartSeries; return "replaced"; } debug(`Chart.applySeries() - matchResult`, matchResult); const seriesInstances = []; let dataChanged = false; let groupingChanged = false; let isUpdated = false; let seriesCountChanged = false; const changes = matchResult.changes.toSorted((a, b) => a.targetIdx - b.targetIdx); for (const change of changes) { groupingChanged || (groupingChanged = change.status === "series-grouping"); dataChanged || (dataChanged = change.diff?.data != null); isUpdated || (isUpdated = change.status !== "no-op"); seriesCountChanged || (seriesCountChanged = change.status === "add" || change.status === "remove"); switch (change.status) { case "add": { const newSeries = this.createSeries(change.opts); seriesInstances.push(newSeries); debug(`Chart.applySeries() - created new series`, newSeries); break; } case "remove": debug(`Chart.applySeries() - removing series at previous idx ${change.idx}`, change.series); break; case "no-op": seriesInstances.push(change.series); debug(`Chart.applySeries() - no change to series at previous idx ${change.idx}`, change.series); break; case "series-grouping": case "update": default: { const { series, diff: diff2, idx } = change; debug(`Chart.applySeries() - applying series diff previous idx ${idx}`, diff2, series); this.applySeriesValues(series, diff2); series.markNodeDataDirty(); seriesInstances.push(series); } } } this.initSeriesDeclarationOrder(seriesInstances); debug(`Chart.applySeries() - final series instances`, seriesInstances); chart.series = seriesInstances; if (groupingChanged) { return "series-grouping-change"; } if (seriesCountChanged) { return "series-count-changed"; } if (dataChanged) { return "data-change"; } return isUpdated ? "updated" : "no-op"; } applyAxes(chart, options, oldOpts, seriesStatus, skip = []) { if (!("axes" in options) || !options.axes) { return false; } skip = ["type", ...skip]; const axes = options.axes; const forceRecreate = seriesStatus === "replaced"; const matchingTypes = !forceRecreate && chart.axes.matches(axes); if (matchingTypes && isAgCartesianChartOptions(oldOpts)) { for (const axis of chart.axes) { const previousOpts = oldOpts.axes?.[axis.id] ?? {}; const axisDiff = jsonDiff(previousOpts, axes[axis.id]); debug(`Chart.applyAxes() - applying axis diff idx ${axis.id}`, axisDiff); jsonApply(axis, axisDiff, { skip }); } return true; } debug(`Chart.applyAxes() - creating new axes instances; seriesStatus: ${seriesStatus}`); chart.axes = this.createAxes(axes, skip); return true; } createSeries(seriesOptions) { const seriesModule = moduleRegistry_exports.getSeriesModule(seriesOptions.type); const seriesInstance = seriesModule.create(this.getModuleContext()); this.applySeriesOptionModules(seriesInstance, seriesOptions); this.applySeriesValues(seriesInstance, seriesOptions); return seriesInstance; } applySeriesOptionModules(series, options) { const moduleContext = series.createModuleContext(); const moduleMap = series.getModuleMap(); for (const module2 of moduleRegistry_exports.listModulesByType("series:plugin" /* SeriesPlugin */)) { if (module2.name in options && (module2.seriesTypes?.includes(series.type) ?? true)) { moduleMap.addModule(module2.name, module2.create(moduleContext)); } } } applySeriesValues(target, options) { const moduleMap = target.getModuleMap(); const { type, data, listeners, seriesGrouping, showInMiniChart, ...seriesOptions } = options; for (const module2 of moduleRegistry_exports.listModulesByType("series:plugin" /* SeriesPlugin */)) { if (module2.name in seriesOptions) { const moduleInstance = moduleMap.getModule(module2.name); if (moduleInstance) { const moduleOptions = seriesOptions[module2.name]; moduleInstance.properties.set(moduleOptions); delete seriesOptions[module2.name]; } } } if (seriesOptions.visible != null) { target.visible = seriesOptions.visible; } target.properties.set(seriesOptions); if ("data" in options) { target.setOptionsData(data == null ? void 0 : DataSet.wrap(data)); } if ("listeners" in options) { this.registerListeners(target, listeners); if (this.series.includes(target)) { this.addSeriesListeners(target); } } if ("seriesGrouping" in options) { if (seriesGrouping == null) { target.seriesGrouping = void 0; } else { target.seriesGrouping = { ...target.seriesGrouping, ...seriesGrouping }; } } } createAxes(options, skip) { const newAxes = this.createChartAxes(); const moduleContext = this.getModuleContext(); for (const [id, axisOptions] of entries(options)) { const axis = moduleRegistry_exports.getAxisModule(axisOptions.type).create(moduleContext); axis.id = id; this.applyAxisModules(axis, axisOptions); jsonApply(axis, axisOptions, { skip }); newAxes.push(axis); } guessInvalidPositions(newAxes); return newAxes; } applyAxisModules(axis, options) { const moduleContext = axis.createModuleContext(); const moduleMap = axis.getModuleMap(); for (const module2 of moduleRegistry_exports.listModulesByType("axis:plugin" /* AxisPlugin */)) { const shouldBeEnabled = options[module2.name] != null; if (shouldBeEnabled === moduleMap.isEnabled(module2.name)) continue; if (shouldBeEnabled) { moduleMap.addModule(module2.name, module2.create(moduleContext)); axis[module2.name] = moduleMap.getModule(module2.name); } else { moduleMap.removeModule(module2.name); delete axis[module2.name]; } } } registerListeners(source, listeners) { source.clearEventListeners(); if (listeners && typeof listeners === "object") { for (const [property, listener] of entries(listeners)) { if (listener == null) { continue; } source.addEventListener(property, listener); } } } async applyTransaction(transaction) { await this.updateMutex.acquire(() => { this.data.addTransaction(transaction); this.update(1 /* UPDATE_DATA */, { apiUpdate: true, skipAnimations: true }); }); await this.waitForUpdate(); } onSyncActiveClear() { this.seriesAreaManager.onActiveClear(); } }; _Chart.className = "Chart"; _Chart.chartsInstances = /* @__PURE__ */ new WeakMap(); __decorateClass([ ActionOnSet({ newValue(value) { if (this.destroyed) return; this.ctx.domManager.setContainer(value); _Chart.chartsInstances.set(value, this); }, oldValue(value) { _Chart.chartsInstances.delete(value); } }) ], _Chart.prototype, "container", 2); __decorateClass([ ActionOnSet({ newValue(value) { this.resize("width option", { inWidth: value }); } }) ], _Chart.prototype, "width", 2); __decorateClass([ ActionOnSet({ newValue(value) { this.resize("height option", { inHeight: value }); } }) ], _Chart.prototype, "height", 2); __decorateClass([ ActionOnSet({ newValue(value) { this.resize("minWidth option", { inMinWidth: value }); } }) ], _Chart.prototype, "minWidth", 2); __decorateClass([ ActionOnSet({ newValue(value) { this.resize("minHeight option", { inMinHeight: value }); } }) ], _Chart.prototype, "minHeight", 2); __decorateClass([ ActionOnSet({ newValue(value) { this.resize("overrideDevicePixelRatio option", { inOverrideDevicePixelRatio: value }); } }) ], _Chart.prototype, "overrideDevicePixelRatio", 2); __decorateClass([ addFakeTransformToInstanceProperty ], _Chart.prototype, "padding", 2); __decorateClass([ addFakeTransformToInstanceProperty ], _Chart.prototype, "keyboard", 2); __decorateClass([ addFakeTransformToInstanceProperty ], _Chart.prototype, "touch", 2); __decorateClass([ addFakeTransformToInstanceProperty ], _Chart.prototype, "mode", 2); __decorateClass([ addFakeTransformToInstanceProperty ], _Chart.prototype, "styleNonce", 2); __decorateClass([ ProxyProperty("chartCaptions.title") ], _Chart.prototype, "title", 2); __decorateClass([ ProxyProperty("chartCaptions.subtitle") ], _Chart.prototype, "subtitle", 2); __decorateClass([ ProxyProperty("chartCaptions.footnote") ], _Chart.prototype, "footnote", 2); __decorateClass([ addFakeTransformToInstanceProperty ], _Chart.prototype, "formatter", 2); __decorateClass([ addFakeTransformToInstanceProperty ], _Chart.prototype, "suppressFieldDotNotation", 2); __decorateClass([ addFakeTransformToInstanceProperty ], _Chart.prototype, "loadGoogleFonts", 2); __decorateClass([ ActionOnSet({ changeValue(newValue, oldValue) { this.onAxisChange(newValue, oldValue); } }) ], _Chart.prototype, "axes", 2); __decorateClass([ ActionOnSet({ changeValue(newValue, oldValue) { this.onSeriesChange(newValue, oldValue); } }) ], _Chart.prototype, "series", 2); var Chart = _Chart; // packages/ag-charts-community/src/chart/cartesianChart.ts var directions = ["top", "right", "bottom", "left"]; var _CartesianChart = class _CartesianChart extends Chart { constructor(options, resources) { super(options, resources); // TODO should come from theme /** Integrated Charts feature state - not used in Standalone Charts. */ this.paired = true; this.axes = this.createChartAxes(); this.lastUpdateClipRect = void 0; this.lastLayoutWidth = Number.NaN; this.lastLayoutHeight = Number.NaN; } createChartAxes() { return new CartesianChartAxes(); } onAxisChange(newValue, oldValue) { super.onAxisChange(newValue, oldValue); this.syncAxisChanges(newValue, oldValue); if (this.ctx != null) { this.ctx.zoomManager.setAxes(newValue); } } destroySeries(series) { super.destroySeries(series); this.lastLayoutWidth = Number.NaN; this.lastLayoutHeight = Number.NaN; } getChartType() { return "cartesian"; } setRootClipRects(clipRect) { const { seriesRoot, annotationRoot } = this; seriesRoot.setClipRect(clipRect); annotationRoot.setClipRect(clipRect); } async processData() { await super.processData(); if (this.syncStatus === "init") { this.syncStatus = "domains-calculated"; } this.ctx.updateService.dispatchProcessData({ series: { shouldFlipXY: this.shouldFlipXY() } }); } async processDomains() { await super.processDomains(); for (const axis of this.axes) { const syncedDomain = await this.getSyncedDomain(axis); if (syncedDomain != null) { axis.setDomains({ domain: syncedDomain }); } } } performLayout(ctx) { const { seriesRoot, annotationRoot } = this; const { clipSeries, seriesRect, visible } = this.updateAxes(ctx); this.seriesRoot.visible = visible; this.seriesRect = seriesRect; this.animationRect = ctx.layoutBox; const { x, y } = seriesRect; if (ctx.width !== this.lastLayoutWidth || ctx.height !== this.lastLayoutHeight) { for (const group of [seriesRoot, annotationRoot]) { group.translationX = Math.floor(x); group.translationY = Math.floor(y); } } else { const { translationX, translationY } = seriesRoot; staticFromToMotion( this.id, "seriesRect", this.ctx.animationManager, [seriesRoot, annotationRoot], { translationX, translationY }, { translationX: Math.floor(x), translationY: Math.floor(y) }, { phase: "update" } ); } this.lastLayoutWidth = ctx.width; this.lastLayoutHeight = ctx.height; const seriesPaddedRect = seriesRect.clone().grow(this.seriesArea.getPadding()); const alwaysClip = this.series.some((s) => s.alwaysClip); const enableClip = alwaysClip || (this.seriesArea.clip ?? false) || clipSeries; const clipRect = enableClip ? seriesPaddedRect : void 0; const { lastUpdateClipRect } = this; this.lastUpdateClipRect = clipRect; if (this.ctx.animationManager.isActive() && lastUpdateClipRect != null) { this.ctx.animationManager.animate({ id: this.id, groupId: "clip-rect", phase: "update", from: lastUpdateClipRect, to: seriesPaddedRect, onUpdate: (interpolatedClipRect) => this.setRootClipRects(interpolatedClipRect), onStop: () => this.setRootClipRects(clipRect), onComplete: () => this.setRootClipRects(clipRect) }); } else { this.setRootClipRects(clipRect); } this.ctx.layoutManager.emitLayoutComplete(ctx, { axes: fromPairs(this.axes.map((axis) => [axis.id, axis.getLayoutState()])), series: { visible, rect: seriesRect, paddedRect: seriesPaddedRect }, clipSeries }); stackCartesianSeries(this.series); } updateAxes(layoutContext) { const { layoutBox, scrollbars } = layoutContext; const { clipSeries, seriesRect, overflows } = this.resolveAxesLayout(layoutBox, scrollbars); for (const axis of this.axes) { axis.update(); axis.setCrossLinesVisible(!overflows); this.clipAxis(axis, seriesRect, layoutBox); } return { clipSeries, seriesRect, visible: !overflows }; } // Iteratively try to resolve axis widths - since X axis width affects Y axis range, // and vice-versa, we need to iteratively try and find a fit for the axes and their // ticks/labels. resolveAxesLayout(layoutBox, scrollbars) { let newState; let prevState; let iterations = 0; const maxIterations = 10; const crossAtAxes = this.axes.filter((axis) => axis.crossAt?.value != null); do { prevState = newState ?? this.getDefaultState(); newState = this.updateAxesPass( new Map(prevState.axisAreaWidths), layoutBox.clone(), crossAtAxes, scrollbars ); if (iterations++ > maxIterations) { logger_exports.warn("Max iterations reached. Unable to stabilize axes layout."); break; } } while (!this.isLayoutStable(newState, prevState)); this.lastAreaWidths = newState.axisAreaWidths; return newState; } updateAxesPass(axisAreaWidths, axisAreaBound, crossAtAxes, scrollbars) { const axisWidths = /* @__PURE__ */ new Map(); const primaryTickCounts = {}; let overflows = false; let clipSeries = false; const seriesAreaPadding = this.seriesArea.getPadding(); for (const dir of directions) { const padding2 = seriesAreaPadding[dir] ?? 0; const axis = this.axes.findLast((a) => a.position === dir); if (axis) { axis.seriesAreaPadding = padding2; } else { axisAreaBound.shrink(padding2, dir); } } const totalWidth = (axisAreaWidths.get("left") ?? 0) + (axisAreaWidths.get("right") ?? 0); const totalHeight = (axisAreaWidths.get("top") ?? 0) + (axisAreaWidths.get("bottom") ?? 0); const crossLinePadding = this.buildCrossLinePadding(axisAreaWidths); const crossLineHPadding = crossLinePadding.left + crossLinePadding.right; const crossLineVPadding = crossLinePadding.top + crossLinePadding.bottom; if (axisAreaBound.width <= totalWidth + crossLineHPadding || axisAreaBound.height <= totalHeight + crossLineVPadding) { overflows = true; } else { axisAreaBound.shrink(crossLinePadding); } const { scene } = this.ctx; const seriesRect = axisAreaBound.clone().shrink(Object.fromEntries(axisAreaWidths)); for (const axis of this.axes) { const { position = "left", direction } = axis; const isVertical = direction === "y" /* Y */; let axisWidth; this.sizeAxis(axis, seriesRect, position); if (axis.thickness == null) { const availableSize = getSize(isVertical, scene); axisWidth = availableSize * (axis.maxThicknessRatio ?? 1); } else { axisWidth = axis.thickness; } const chartLayout = { sizeLimit: axisWidth - axis.label.spacing, padding: this.padding, scrollbars }; const { primaryTickCount, bbox } = axis.calculateLayout( axis.nice ? primaryTickCounts[direction] : void 0, chartLayout ); primaryTickCounts[direction] ?? (primaryTickCounts[direction] = primaryTickCount); clipSeries || (clipSeries = axis.dataDomain.clipped || axis.visibleRange[0] > 0 || axis.visibleRange[1] < 1); if (axis.thickness == null) { axisWidth = Math.min(getSize(isVertical, bbox) ?? 0, axisWidth); } axisWidths.set(axis.id, Math.ceil(axisWidth)); } let crossPositions; if (crossAtAxes.length > 0) { crossPositions = this.calculateAxesCrossPositions(axisWidths, seriesRect, crossAtAxes); } const axisGroups = groupBy(this.axes, (axis) => axis.position ?? "left"); const newAxisAreaWidths = /* @__PURE__ */ new Map(); const axisOffsets = /* @__PURE__ */ new Map(); for (const [position, axes] of entries(axisGroups)) { let currentOffset = getSize(position !== "left" && position !== "right", scene) % scene.pixelRatio; let totalAxisWidth = 0; for (const axis of axes ?? []) { axisOffsets.set(axis.id, currentOffset); const axisThickness = axisWidths.get(axis.id) ?? 0; totalAxisWidth = Math.max(totalAxisWidth, currentOffset + axisThickness); if (axis.layoutConstraints.stacked) { currentOffset += axisThickness + _CartesianChart.AxesPadding; } } newAxisAreaWidths.set(position, Math.ceil(totalAxisWidth)); } for (const [position, axes] of entries(axisGroups)) { this.positionAxes({ axes: axes ?? [], position, axisWidths, axisOffsets, axisAreaWidths: newAxisAreaWidths, axisBound: axisAreaBound, seriesRect }); } if (crossPositions != null) { this.applyAxisCrossing(seriesRect, crossPositions); } return { clipSeries, seriesRect, axisAreaWidths: newAxisAreaWidths, overflows }; } calculateAxesCrossPositions(axisWidths, seriesRect, crossAtAxes) { const crossPositions = /* @__PURE__ */ new Map(); for (const axis of crossAtAxes) { const { crossPosition, visible } = this.calculateAxisCrossPosition(axis); axis.setAxisVisible(visible); this.adjustAxisWidth(axis, axisWidths, crossPosition, seriesRect, visible); if (crossPosition == void 0) continue; crossPositions.set(axis.id, crossPosition); } return crossPositions; } calculateAxisCrossPosition(axis) { const perpendicularAxis = this.axes.perpendicular(axis); const { scale: { domain, bandwidth }, range: range3 } = perpendicularAxis; const halfBandwidth = (bandwidth ?? 0) / 2; const crossPosition = perpendicularAxis.scale.convert(axis.crossAt?.value, { clamp: false }) + halfBandwidth; if (perpendicularAxis.inRange(crossPosition)) return { crossPosition, visible: true }; if (axis.crossAt?.sticky === false) { return { crossPosition: void 0, visible: false }; } const clampedPosition = Number.isNaN(crossPosition) ? range3[domain[0]] : clampArray(crossPosition, range3); return { crossPosition: clampedPosition, visible: true }; } adjustAxisWidth(axis, axisWidths, crossPosition, seriesRect, visible) { const crosshairModule = axis.getModuleMap().getModule("crosshair"); if (crosshairModule?.enabled) return; const annotationsModule = this.modulesManager.getModule("annotations"); const hasAnnotations = annotationsModule?.enabled === true || this.ctx.annotationManager.createMemento().some((annotation) => { switch (annotation.type) { case "vertical-line": return axis.direction === "x" /* X */; case "horizontal-line": return axis.direction === "y" /* Y */; } }); if (hasAnnotations) return; const currentWidth = axisWidths.get(axis.id) ?? 0; const adjustedWidth = visible ? this.calculateAxisBleedingWidth(axis, currentWidth, crossPosition, seriesRect) : 0; axisWidths.set(axis.id, adjustedWidth); } calculateAxisBleedingWidth(axis, actualWidth, crossPosition, seriesRect) { if (crossPosition == null) return actualWidth; switch (axis.position) { case "left": case "top": return Math.max(0, actualWidth - crossPosition); case "right": return Math.max(0, crossPosition + actualWidth - seriesRect.width); case "bottom": return Math.max(0, crossPosition + actualWidth - seriesRect.height); default: return actualWidth; } } applyAxisCrossing(seriesRect, crossPositions) { for (const axis of this.axes) { const crossPosition = crossPositions.get(axis.id); if (crossPosition == null) { axis.crossAxisTranslation.x = 0; axis.crossAxisTranslation.y = 0; continue; } const isXDirection = axis.direction === "x" /* X */; axis.crossAxisTranslation.x = isXDirection ? 0 : seriesRect.x + crossPosition - axis.translation.x; axis.crossAxisTranslation.y = isXDirection ? seriesRect.y + crossPosition - axis.translation.y : 0; } } buildCrossLinePadding(axisAreaSize) { var _a; const crossLinePadding = { top: 0, right: 0, bottom: 0, left: 0 }; for (const axis of this.axes) { const { position, label } = axis; if (axis.crossLines) { for (const crossLine of axis.crossLines) { if (crossLine instanceof CartesianCrossLine) { crossLine.position = position ?? "top"; (_a = crossLine.label).parallel ?? (_a.parallel = label.parallel); } crossLine.calculatePadding?.(crossLinePadding); } } } for (const [side, padding2 = 0] of entries(crossLinePadding)) { crossLinePadding[side] = Math.max(padding2 - (axisAreaSize.get(side) ?? 0), 0); } return crossLinePadding; } clampToOutsideSeriesRect(seriesRect, value, dimension, direction) { const bound = dimension === "x" ? seriesRect.x : seriesRect.y; const size = dimension === "x" ? seriesRect.width : seriesRect.height; return direction === 1 ? Math.min(value, bound + size) : Math.max(value, bound); } async getSyncedDomain(axis) { const syncModule = this.modulesManager.getModule("sync"); if (!syncModule?.enabled) return; return await syncModule.getSyncedDomain(axis); } syncAxisChanges(newValue, oldValue) { const syncModule = this.modulesManager.getModule("sync"); if (!syncModule?.enabled) return; const removed = new Set(oldValue ?? []); for (const axis of newValue) { removed.delete(axis); } for (const removedAxis of removed) { syncModule.removeAxis(removedAxis); } } sizeAxis(axis, seriesRect, position) { const isNumberAxis = axis instanceof NumberAxis; const isLeftRight = position === "left" || position === "right"; const { width: width2, height: height2 } = seriesRect; const maxEnd = isLeftRight ? height2 : width2; let start2 = 0; let end3 = maxEnd; let { min, max } = this.ctx.zoomManager.getAxisZoom(axis.id); const { width: axisWidth, unit, align: align2 } = axis.layoutConstraints; if (unit === "px") { end3 = start2 + axisWidth; } else { end3 = end3 * axisWidth / 100; } const size = end3 - start2; if (align2 === "end") { start2 = maxEnd - size; end3 = maxEnd; } else if (align2 === "center") { const center2 = start2 + (maxEnd - start2) / 2; start2 = center2 - size / 2; end3 = center2 + size / 2; } else if (align2 === "justify") { end3 = maxEnd; } if (isLeftRight) { if (isNumberAxis) { [start2, end3] = [end3, start2]; } else { [min, max] = [1 - max, 1 - min]; } } axis.range = [start2, end3]; axis.visibleRange = [min, max]; axis.gridLength = isLeftRight ? width2 : height2; axis.lineRange = isLeftRight ? [height2, 0] : [0, width2]; } positionAxes(opts) { const { axes, axisBound, axisWidths, axisOffsets, axisAreaWidths, seriesRect, position } = opts; const axisAreaWidth = axisAreaWidths.get(position) ?? 0; let mainDimension = "x"; let minorDimension = "y"; let direction = 1; if (position === "top" || position === "bottom") { mainDimension = "y"; minorDimension = "x"; } let axisBoundMainOffset = axisBound[mainDimension]; if (position === "right" || position === "bottom") { direction = -1; axisBoundMainOffset += mainDimension === "x" ? axisBound.width : axisBound.height; } for (const axis of axes) { const minorOffset = axisAreaWidths.get(minorDimension === "x" ? "left" : "top") ?? 0; const axisThickness = axisWidths.get(axis.id) ?? 0; const axisOffset = axisOffsets.get(axis.id) ?? 0; axis.gridPadding = axisAreaWidth - axisOffset - axisThickness; axis.translation[minorDimension] = axisBound[minorDimension] + minorOffset; axis.translation[mainDimension] = this.clampToOutsideSeriesRect( seriesRect, axisBoundMainOffset + direction * (axisOffset + axisThickness), mainDimension, direction ); } } shouldFlipXY() { return this.series.every((series) => series instanceof CartesianSeries && series.shouldFlipXY()); } getDefaultState() { const axisAreaWidths = /* @__PURE__ */ new Map(); if (this.lastAreaWidths) { for (const { position = "left" } of this.axes) { const areaWidth = this.lastAreaWidths.get(position); if (areaWidth != null) { axisAreaWidths.set(position, areaWidth); } } } return { axisAreaWidths, clipSeries: false, overflows: false }; } isLayoutStable(newState, prevState) { if (prevState.overflows !== newState.overflows || prevState.clipSeries !== newState.clipSeries) { return false; } for (const key of newState.axisAreaWidths.keys()) { if (!prevState.axisAreaWidths.has(key)) { return false; } } for (const [p, w] of prevState.axisAreaWidths.entries()) { const otherW = newState.axisAreaWidths.get(p); if ((w != null || otherW != null) && w !== otherW) { return false; } } return true; } clipAxis(axis, seriesRect, layoutBBox) { const gridLinePadding = Math.ceil(axis.gridLine?.width ?? 0); const axisLinePadding = Math.ceil(axis.line?.width ?? 0); let { width: width2, height: height2 } = seriesRect; width2 += axis.direction === "x" /* X */ ? gridLinePadding : axisLinePadding; height2 += axis.direction === "y" /* Y */ ? gridLinePadding : axisLinePadding; axis.clipGrid(seriesRect.x, seriesRect.y, width2, height2); switch (axis.position) { case "left": case "right": axis.clipTickLines( layoutBBox.x, seriesRect.y - gridLinePadding, layoutBBox.width + gridLinePadding, seriesRect.height + gridLinePadding * 2 ); break; case "top": case "bottom": axis.clipTickLines( seriesRect.x - gridLinePadding, layoutBBox.y, seriesRect.width + gridLinePadding * 2, layoutBBox.height + gridLinePadding ); break; } } }; _CartesianChart.className = "CartesianChart"; _CartesianChart.type = "cartesian"; _CartesianChart.AxesPadding = 15; __decorateClass([ ActionOnSet({ changeValue(newValue, oldValue) { this.onAxisChange(newValue, oldValue); } }) ], _CartesianChart.prototype, "axes", 2); var CartesianChart = _CartesianChart; function getSize(isVertical, bounds) { return isVertical ? bounds?.width : bounds?.height; } // packages/ag-charts-community/src/chart/cartesianChartModule.ts var histogramAxisTypes = /* @__PURE__ */ new Set(["number", "log", "time"]); var invalidHistogramAxis = (axis) => isObject(axis) && axis.type != null && !histogramAxisTypes.has(axis.type); var CartesianChartModule = { type: "chart", name: "cartesian", version: VERSION, options: cartesianChartOptionsDefs, create(options, resources) { return new CartesianChart(options, resources); }, validate(options, optionsDefs2, path) { const additionalErrors = []; if (options?.series?.[0]?.type === "histogram") { if (Object.values(options?.axes ?? {}).some(invalidHistogramAxis)) { additionalErrors.push( new ValidationError( "invalid", "only continuous axis types when histogram series is used", options.axes, path, "axes" ) ); options = without(options, ["axes"]); } } const result = validate(options, optionsDefs2, path); result.invalid.push(...additionalErrors); return result; } }; // packages/ag-charts-community/src/module/axis-modules/categoryAxisModule.ts var CategoryAxisModule = { type: "axis", name: "category", chartType: "cartesian", version: VERSION, dependencies: [CartesianChartModule], options: categoryAxisOptionsDefs, themeTemplate: { groupPaddingInner: 0.1, label: { autoRotate: true, wrapping: "on-space" }, gridLine: { enabled: false }, interval: { placement: "between" } }, create: (ctx) => new CategoryAxis(ctx) }; // packages/ag-charts-community/src/scale/groupedCategoryScale.ts var MAX_ANIMATABLE_NODES2 = 1e3; var GroupedCategoryScale = class _GroupedCategoryScale extends CategoryScale { constructor() { super(...arguments); this.previousDomainJson = void 0; /** Whether the current domain update is animatable (initial load or no domain change). */ this.animatable = true; } static is(value) { return value instanceof _GroupedCategoryScale; } set domain(values) { if (values.length <= MAX_ANIMATABLE_NODES2) { const currentDomainJson = JSON.stringify(values); this.animatable = this.previousDomainJson === void 0 || this.previousDomainJson === currentDomainJson; this.previousDomainJson = currentDomainJson; } else { this.animatable = this.previousDomainJson === void 0; this.previousDomainJson = ""; } super.domain = values; } get domain() { return super.domain; } normalizeDomains(...domains) { const { domain } = super.normalizeDomains(...domains); return { domain, animatable: false }; } findIndex(value) { return super.findIndex(value) ?? this.getMatchIndex(value); } getMatchIndex(value) { const key = JSON.stringify(value); const match = this._domain.find((d) => JSON.stringify(d) === key); if (match != null) { return super.findIndex(match); } } }; // packages/ag-charts-community/src/chart/axis/tree.ts var Dimensions = class { constructor() { this.top = Infinity; this.right = -Infinity; this.bottom = -Infinity; this.left = Infinity; } update(x, y) { if (x > this.right) { this.right = x; } if (x < this.left) { this.left = x; } if (y > this.bottom) { this.bottom = y; } if (y < this.top) { this.top = y; } } }; var TreeNode = class _TreeNode { constructor(label = "", parent, refId) { this.label = label; this.parent = parent; this.refId = refId; this.position = 0; this.subtreeLeft = Number.NaN; this.subtreeRight = Number.NaN; this.children = []; this.leafCount = 0; this.prelim = 0; this.mod = 0; this.ancestor = this; this.change = 0; this.shift = 0; this.index = 0; // screen is meant to be recomputed from (layout) when the tree is resized (without performing another layout) this.screen = 0; this.depth = parent ? parent.depth + 1 : 0; } insertTick(tick, index) { let current = this; let endNode; for (let i = 0; i < tick.length; i++) { const pathPart = tick[i]; const isNotLeaf = i !== tick.length - 1; const { children } = current; const existingNode = children.find((child) => child.label === pathPart); if (existingNode && isNotLeaf) { current = existingNode; endNode = existingNode; } else { const node = new _TreeNode(pathPart, current, index); node.index = children.length; children.push(node); if (isNotLeaf) { current = node; } endNode = node; } } return endNode; } getLeftSibling() { return this.index > 0 ? this.parent?.children[this.index - 1] : void 0; } getLeftmostSibling() { return this.index > 0 ? this.parent?.children[0] : void 0; } // traverse the left contour of a subtree, return the successor of v on this contour nextLeft() { return this.children[0]; } // traverse the right contour of a subtree, return the successor of v on this contour nextRight() { return this.children.at(-1); } getSiblings() { return this.parent?.children.filter((_, i) => i !== this.index) ?? []; } }; function ticksToTree(ticks) { const maxDepth = ticks.reduce((depth, tick) => Math.max(depth, tick.length), 0); const root = new TreeNode(); const tickNodes = /* @__PURE__ */ new Map(); for (let i = 0; i < ticks.length; i++) { const tick = ticks[i]; while (tick.length < maxDepth) { tick.push(""); } const node = root.insertTick(tick, i); if (node != null) { tickNodes.set(tick, node); } } return { root, tickNodes }; } function moveSubtree(wm, wp, shift) { const subtrees = wp.index - wm.index; const ratio2 = shift / subtrees; wp.change -= ratio2; wp.shift += shift; wm.change += ratio2; wp.prelim += shift; wp.mod += shift; } function ancestor(vim, v, defaultAncestor) { return v.getSiblings().includes(vim.ancestor) ? vim.ancestor : defaultAncestor; } function executeShifts({ children }) { let shift = 0; let change = 0; for (let i = children.length - 1; i >= 0; i--) { const w = children[i]; w.prelim += shift; w.mod += shift; change += w.change; shift += w.shift + change; } } function apportion(v, defaultAncestor) { const w = v.getLeftSibling(); if (w) { let vop = v; let vip = v; let vim = w; let vom = vip.getLeftmostSibling(); let sip = vip.mod; let sop = vop.mod; let sim = vim.mod; let som = vom.mod; while (vim.nextRight() && vip.nextLeft()) { vim = vim.nextRight(); vip = vip.nextLeft(); vom = vom.nextLeft(); vop = vop.nextRight(); vop.ancestor = v; const shift = vim.prelim + sim - (vip.prelim + sip) + 1; if (shift > 0) { moveSubtree(ancestor(vim, v, defaultAncestor), v, shift); sip += shift; sop += shift; } sim += vim.mod; sip += vip.mod; som += vom.mod; sop += vop.mod; } if (vim.nextRight() && !vop.nextRight()) { vop.mod += sim - sop; } else { if (vip.nextLeft() && !vom.nextLeft()) { vom.mod += sip - som; } defaultAncestor = v; } } return defaultAncestor; } function firstWalk(node) { const { children } = node; if (children.length) { let [defaultAncestor] = children; for (const child of children) { firstWalk(child); defaultAncestor = apportion(child, defaultAncestor); } executeShifts(node); const midpoint = (children[0].prelim + children.at(-1).prelim) / 2; const leftSibling = node.getLeftSibling(); if (leftSibling) { node.prelim = leftSibling.prelim + 1; node.mod = node.prelim - midpoint; } else { node.prelim = midpoint; } } else { const leftSibling = node.getLeftSibling(); node.prelim = leftSibling ? leftSibling.prelim + 1 : 0; } } function secondWalk(v, m, layout) { v.position = v.prelim + m; layout.insertNode(v); for (const w of v.children) { secondWalk(w, m + v.mod, layout); } } function thirdWalk(v) { const { children } = v; let leafCount = 0; for (const w of children) { thirdWalk(w); if (w.children.length) { leafCount += w.leafCount; } else { leafCount++; } } v.leafCount = leafCount; if (children.length) { v.subtreeLeft = children[0].subtreeLeft; v.subtreeRight = children.at(-1).subtreeRight; v.position = (v.subtreeLeft + v.subtreeRight) / 2; } else { v.subtreeLeft = v.position; v.subtreeRight = v.position; } } function treeLayout(ticks) { const layout = new TreeLayout(); const { root, tickNodes } = ticksToTree(ticks); firstWalk(root); secondWalk(root, -root.prelim, layout); thirdWalk(root); return { layout, tickNodes }; } var TreeLayout = class { constructor() { this.dimensions = new Dimensions(); this.nodes = []; this.depth = 0; } insertNode(node) { if (this.depth < node.depth) { this.depth = node.depth; } this.dimensions.update(node.position, node.depth); this.nodes.push(node); } scaling(extent2, flip) { let scaling = 1; if (extent2 > 0) { const { left, right } = this.dimensions; if (right !== left) { scaling = extent2 / (right - left); } } if (flip) { scaling *= -1; } return scaling; } }; // packages/ag-charts-community/src/chart/axis/groupedCategoryAxis.ts var MIN_CATEGORY_SPACING = 5; var DepthLabelProperties = class extends BaseProperties { constructor() { super(...arguments); this.enabled = true; this.border = new LabelBorder(); } }; __decorateClass([ addFakeTransformToInstanceProperty ], DepthLabelProperties.prototype, "enabled", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DepthLabelProperties.prototype, "avoidCollisions", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DepthLabelProperties.prototype, "border", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DepthLabelProperties.prototype, "color", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DepthLabelProperties.prototype, "cornerRadius", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DepthLabelProperties.prototype, "spacing", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DepthLabelProperties.prototype, "rotation", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DepthLabelProperties.prototype, "wrapping", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DepthLabelProperties.prototype, "truncate", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DepthLabelProperties.prototype, "fill", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DepthLabelProperties.prototype, "fontStyle", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DepthLabelProperties.prototype, "fontWeight", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DepthLabelProperties.prototype, "fontSize", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DepthLabelProperties.prototype, "fontFamily", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DepthLabelProperties.prototype, "padding", 2); var DepthTickProperties = class extends BaseProperties { constructor() { super(...arguments); this.enabled = true; } }; __decorateClass([ addFakeTransformToInstanceProperty ], DepthTickProperties.prototype, "enabled", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DepthTickProperties.prototype, "width", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DepthTickProperties.prototype, "stroke", 2); var DepthProperties = class extends BaseProperties { constructor() { super(...arguments); this.label = new DepthLabelProperties(); this.tick = new DepthTickProperties(); } }; __decorateClass([ addFakeTransformToInstanceProperty ], DepthProperties.prototype, "label", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DepthProperties.prototype, "tick", 2); var GroupedCategoryAxis = class extends CategoryAxis { constructor(moduleCtx) { super(moduleCtx, new GroupedCategoryScale()); // Label scale (labels are positioned between ticks, tick count = label count + 1). // We don't call is `labelScale` for consistency with other axes. this.tickScale = new GroupedCategoryScale(); this.computedLayout = void 0; this.tickTreeLayout = void 0; this.tickNodes = void 0; this.depthOptions = new PropertiesArray(DepthProperties); this.includeInvisibleDomains = true; this.tickScale.paddingInner = 1; this.tickScale.paddingOuter = 0; } resizeTickTree() { if (!this.tickTreeLayout) return; const { nodes } = this.tickTreeLayout; const { range: range3, step, inset, bandwidth } = this.scale; const width2 = Math.abs(range3[1] - range3[0]) - step; const scaling = this.tickTreeLayout.scaling(width2, range3[0] > range3[1]); const shift = inset + bandwidth / 2; let offset = 0; for (const node of nodes) { const screen = node.position * scaling; if (offset > screen) { offset = screen; } node.screen = screen + shift; } for (const node of nodes) { node.screen -= offset; } } getDepthOptionsMap(maxDepth) { const optionsMap = []; const { depthOptions, label } = this; const defaultNonLeafRotation = this.horizontal ? 0 : -90; for (let i = 0; i < maxDepth; i++) { optionsMap.push( depthOptions[i]?.label.enabled ?? label.enabled ? { enabled: true, spacing: depthOptions[i]?.label.spacing ?? label.spacing, wrapping: depthOptions[i]?.label.wrapping ?? label.wrapping, truncate: depthOptions[i]?.label.truncate ?? label.truncate, rotation: depthOptions[i]?.label.rotation ?? (i ? defaultNonLeafRotation : label.rotation), // Default top-level label rotation only applies to label leaves avoidCollisions: depthOptions[i]?.label.avoidCollisions ?? label.avoidCollisions } : { enabled: false, spacing: 0, rotation: 0, avoidCollisions: false } ); } return optionsMap; } updateCategoryLabels() { if (!this.computedLayout) return; this.tickLabelGroupSelection.update(this.computedLayout.tickLabelLayout).each((node, datum) => { node.fill = datum.color; node.text = datum.text; node.textBaseline = datum.textBaseline; node.textAlign = datum.textAlign ?? "center"; node.pointerEvents = datum.textUntruncated == null ? 1 /* None */ : 0 /* All */; node.setFont(datum); node.setBoxing(datum); }); } updateAxisLine() { if (!this.computedLayout) return; this.lineNode.visible = this.line.enabled; this.lineNode.stroke = this.line.stroke; this.lineNode.strokeWidth = this.line.width; } computeLayout() { this.updateDirection(); this.updateScale(); const { step } = this.scale; const { title, label, range: range3, depthOptions, horizontal, line } = this; const scrollbar = this.chartLayout?.scrollbars?.[this.id]; const scrollbarThickness = this.getScrollbarThickness(scrollbar); this.lineNode.datum = horizontal ? { x1: range3[0], x2: range3[1], y1: 0, y2: 0 } : { x1: 0, x2: 0, y1: range3[0], y2: range3[1] }; this.lineNode.setProperties({ stroke: line.stroke, strokeWidth: line.enabled ? line.width : 0 }); this.resizeTickTree(); if (!this.tickTreeLayout?.depth) { return { bbox: BBox.zero, spacing: 0, depthLabelMaxSize: {}, tickLabelLayout: [] }; } const { depth: maxDepth, nodes: treeLabels } = this.tickTreeLayout; const sideFlag = horizontal ? -label.getSideFlag() : label.getSideFlag(); const tickLabelLayout = []; const labelBBoxes = /* @__PURE__ */ new Map(); const truncatedLabelText = /* @__PURE__ */ new Map(); const tempText = new TransformableText(); const optionsMap = this.getDepthOptionsMap(maxDepth); const labelSpacing = sideFlag * (optionsMap[0].spacing + this.getTickSpacing() + scrollbarThickness); const tickFormatter = this.tickFormatter(this.scale.domain, this.scale.domain, false); const setLabelProps = (datum, index) => { const depth = maxDepth - datum.depth; if (!optionsMap[depth]?.enabled || !inRange(datum.screen, range3)) return false; let maxWidth = (datum.leafCount || 1) * step; if (maxWidth < MIN_CATEGORY_SPACING) return false; const inputText = tickFormatter(datum.label, index - 1); let text = inputText; const labelStyles = this.getLabelStyles( { value: datum.index, formattedValue: text, depth }, depthOptions[depth]?.label ); if (label.avoidCollisions) { const rotation = optionsMap[depth].rotation; let maxHeight = this.thickness; if (rotation != null) { const innerRect = getMaxInnerRectSize(rotation, maxWidth, maxHeight); maxWidth = innerRect.width; maxHeight = innerRect.height; } const wrapOptions = { font: labelStyles, textWrap: optionsMap[depth].wrapping, overflow: optionsMap[depth].truncate ? "ellipsis" : "hide", maxWidth, maxHeight }; text = wrapTextOrSegments(text, wrapOptions) || text; } if (text !== inputText && isTruncated(text)) { truncatedLabelText.set(index, toPlainText(inputText)); } else { truncatedLabelText.delete(index); } tempText.x = horizontal ? datum.screen : labelSpacing; tempText.y = horizontal ? labelSpacing : datum.screen; tempText.rotation = 0; tempText.fill = labelStyles.color; tempText.text = text; tempText.textAlign = "center"; tempText.textBaseline = label.parallel ? "top" : "bottom"; tempText.setFont(labelStyles); tempText.setBoxing(labelStyles); return true; }; const depthLabelMaxSize = {}; for (const [index, datum] of treeLabels.entries()) { const depth = maxDepth - datum.depth; depthLabelMaxSize[depth] ?? (depthLabelMaxSize[depth] = 0); const isLeaf = !datum.children.length; if (isLeaf && step < MIN_CATEGORY_SPACING) continue; const isVisible = setLabelProps(datum, index); if (!isVisible || !tempText.getBBox()) continue; labelBBoxes.set(index, tempText.getBBox()); tempText.rotation = normalizeAngle360FromDegrees(optionsMap[depth]?.rotation); const { width: width2, height: height2 } = tempText.getBBox(); const labelSize = horizontal ? height2 : width2; if (depthLabelMaxSize[depth] < labelSize) { depthLabelMaxSize[depth] = labelSize; } } const idGenerator = createIdsGenerator(); const nestedPadding = (d) => { if (d === 0) return 0; let v = depthLabelMaxSize[0]; for (let i = 1; i <= d; i++) { v += optionsMap[i].spacing; if (i !== d) { v += depthLabelMaxSize[i]; } } return v; }; for (const [index, datum] of treeLabels.entries()) { if (index === 0) continue; const visible = setLabelProps(datum, index); const isLeaf = !datum.children.length; const depth = maxDepth - datum.depth; if (isLeaf && step < MIN_CATEGORY_SPACING) continue; if (!visible) continue; const labelRotation = normalizeAngle360FromDegrees(optionsMap[depth].rotation); const labelBBox = labelBBoxes.get(index); if (!labelBBox) continue; const { width: w, height: h } = labelBBox; const depthPadding = nestedPadding(depth); tempText.textAlign = "center"; tempText.textBaseline = "middle"; tempText.rotation = labelRotation; if (horizontal) { tempText.y += (depthPadding + angularPadding(w / 2, h / 2, labelRotation)) * sideFlag; tempText.rotationCenterX = datum.screen; tempText.rotationCenterY = tempText.y; } else { tempText.x += depthPadding * sideFlag + angularPadding( (optionsMap[depth].spacing * sideFlag + w) / 2, label.mirrored ? w : 0, labelRotation ) - w / 2; tempText.rotationCenterX = tempText.x; tempText.rotationCenterY = datum.screen; } if (optionsMap[depth].avoidCollisions) { const { width: width2, height: height2 } = tempText.getBBox(); const labelSize = horizontal ? width2 : height2; const availableRange = isLeaf ? step : datum.leafCount * step; if (labelSize > availableRange) { labelBBoxes.delete(index); continue; } } const text = tempText.getPlainText(); const boxing = tempText.getBoxingProperties(); tickLabelLayout.push({ text, textUntruncated: truncatedLabelText.get(index), visible: true, tickId: idGenerator(text), range: this.scale.range, border: boxing.border, color: tempText.fill, cornerRadius: boxing.cornerRadius, fill: boxing.fill, fontFamily: tempText.fontFamily, fontSize: tempText.fontSize, fontStyle: tempText.fontStyle, fontWeight: tempText.fontWeight, padding: boxing.padding, rotation: tempText.rotation, rotationCenterX: tempText.rotationCenterX, rotationCenterY: tempText.rotationCenterY, textAlign: tempText.textAlign, textBaseline: tempText.textBaseline, x: tempText.x, y: tempText.y }); labelBBoxes.set(index, Transformable.toCanvas(tempText)); } let maxTickSize = depthLabelMaxSize[0]; for (let i = 0; i < maxDepth; i++) { maxTickSize += optionsMap[i].spacing; if (i !== 0) { maxTickSize += depthLabelMaxSize[i]; } } const maxTickSizeWithScrollbar = maxTickSize + scrollbarThickness; const bboxes = [ this.lineNodeBBox(), BBox.merge(labelBBoxes.values()), new BBox(0, 0, maxTickSizeWithScrollbar * sideFlag, 0) ]; const combined = BBox.merge(bboxes); const labelThickness = horizontal ? combined.height : combined.width; const { spacing, scrollbarLayout } = this.applyScrollbarLayout(bboxes, labelThickness, scrollbar); this.layout.labelThickness = labelThickness; this.layout.scrollbar = scrollbarLayout; if (title.enabled) { bboxes.push(this.titleBBox(this.scale.domain, spacing)); } const mergedBBox = BBox.merge(bboxes); this.layoutCrossLines(); return { bbox: mergedBBox, spacing, depthLabelMaxSize, tickLabelLayout }; } /** * Creates/removes/updates the scene graph nodes that constitute the axis. * Supposed to be called _manually_ after changing _any_ of the axis properties. * This allows to bulk set axis properties before updating the nodes. * The node changes made by this method are rendered on the next animation frame. * We could schedule this method call automatically on the next animation frame * when any of the axis properties change (the way we do when properties of scene graph's * nodes change), but this will mean that we first wait for the next animation * frame to make changes to the nodes of the axis, then wait for another animation * frame to render those changes. It's nice to have everything update automatically, * but this extra level of async indirection will not just introduce an unwanted delay, * it will also make it harder to reason about the program. */ update() { if (!this.computedLayout) return; if (!this.scale.animatable) { this.moduleCtx.animationManager.skipCurrentBatch(); } const { tickScale, tick, gridLine, gridLength, visibleRange, tickTreeLayout } = this; if (!tickTreeLayout) return; const { depthLabelMaxSize, spacing } = this.computedLayout; const { depth: maxDepth } = tickTreeLayout; const optionsMap = this.getDepthOptionsMap(maxDepth); const scrollbar = this.chartLayout?.scrollbars?.[this.id]; const scrollbarThickness = this.getScrollbarThickness(scrollbar); const { position, horizontal, gridPadding } = this; const direction = position === "bottom" || position === "right" ? -1 : 1; const p1 = gridPadding; const p2 = direction * gridLength - gridPadding; const tickParams = { nice: [false, false], interval: void 0, tickCount: void 0, minTickCount: 0, maxTickCount: Infinity }; const { ticks: allTicks } = tickScale.ticks(tickParams, void 0, visibleRange); const { tickInfos: allTickInfos, minSpacingByDepth } = buildTickInfos( allTicks, this.tickNodes, tickScale, maxDepth ); const minDepthToShow = getMinDepthToShow(minSpacingByDepth); const visibleTickInfos = selectVisibleTickInfos(allTickInfos, minDepthToShow, maxDepth, minSpacingByDepth); const gridLineData = visibleTickInfos.map( ({ tickLabel, position: tickPosition }, index) => ({ index: tickScale.findIndex(tickLabel), tickId: createDatumId(index, ...tickLabel), translation: Math.round(tickPosition) }) ); this.gridLineGroupSelection.update( gridLine.enabled && gridLength ? this.calculateGridLines(gridLineData, p1, p2) : [] ); this.gridFillGroupSelection.update( gridLine.enabled && gridLength ? this.calculateGridFills(gridLineData, p1, p2) : [] ); this.tickLineGroupSelection.update( tick.enabled ? visibleTickInfos.map(({ depth }, index) => { const { tickId, translation: offset } = gridLineData[index]; const tickOptions = this.depthOptions[depth]?.tick; let tickSize = depthLabelMaxSize[0]; for (let i = 0; i <= depth; i++) { tickSize += optionsMap[i].spacing; if (i !== 0) { tickSize += depthLabelMaxSize[i]; } } const stroke = tickOptions?.stroke ?? tick.stroke; const strokeWidth = tickOptions?.enabled === false ? 0 : tickOptions?.width ?? tick.width; const h = -direction * tickSize; const tickOffset = -direction * (scrollbarThickness + this.getTickSpacing()); const [x1, y1, x2, y2] = horizontal ? [offset, tickOffset, offset, tickOffset + h] : [tickOffset, offset, tickOffset + h, offset]; const lineDash = void 0; return { tickId, offset, x1, y1, x2, y2, stroke, strokeWidth, lineDash }; }) : [] ); this.updatePosition(); this.updateCategoryLabels(); this.updateAxisLine(); this.updateGridLines(); this.updateGridFills(); this.updateTickLines(); this.updateTitle(this.scale.domain, spacing); this.updateCrossLines(); this.resetSelectionNodes(); } calculateLayout(_primaryTickCount, chartLayout) { this.chartLayout = chartLayout; const { depthLabelMaxSize, tickLabelLayout, spacing, bbox } = this.computeLayout(); this.computedLayout = { depthLabelMaxSize, tickLabelLayout, spacing }; return { bbox, niceDomain: this.scale.domain }; } /** * The length of the grid. The grid is only visible in case of a non-zero value. */ onGridVisibilityChange() { super.onGridVisibilityChange(); this.tickLabelGroupSelection.clear(); } updateScale() { super.updateScale(); this.tickScale.range = this.scale.range; this.scale.paddingOuter = this.scale.paddingInner / 2; } processData() { const { direction } = this; const flatDomains = this.boundSeries.filter((s) => s.visible).flatMap((series) => extractDomain(series.getDomain(direction))); this.dataDomain = { domain: extent(flatDomains) ?? this.filterDuplicateArrays(flatDomains), clipped: false }; if (this.isReversed()) { this.dataDomain.domain.reverse(); } const domain = this.dataDomain.domain.map(convertIntegratedCategoryValue); const { layout, tickNodes } = treeLayout(domain); this.tickTreeLayout = layout; this.tickNodes = tickNodes; const orderedDomain = []; for (const node of this.tickTreeLayout.nodes) { if (node.leafCount || node.refId == null) continue; orderedDomain.push(this.dataDomain.domain[node.refId]); } const sortedDomain = sortBasedOnArray(this.dataDomain.domain, orderedDomain); this.scale.domain = sortedDomain; const tickScaleDomain = sortedDomain.map(convertIntegratedCategoryValue); tickScaleDomain.push([""]); this.tickScale.domain = tickScaleDomain; } filterDuplicateArrays(array2) { const seen = /* @__PURE__ */ new Set(); return array2.filter((item) => { const key = isArray(item) ? JSON.stringify(item) : item; if (seen.has(key)) return false; seen.add(key); return true; }); } }; GroupedCategoryAxis.className = "GroupedCategoryAxis"; GroupedCategoryAxis.type = "grouped-category"; __decorateClass([ addFakeTransformToInstanceProperty ], GroupedCategoryAxis.prototype, "depthOptions", 2); function separatorDepth2(node) { let depth = 0; let current = node; while (current?.index === 0) { depth += 1; current = current.parent; } return depth; } function buildTickInfos(ticks, tickNodes, tickScale, maxDepth) { const tickInfos = new Array(ticks.length); const minSpacingByDepth = new Array(maxDepth).fill(Infinity); const lastPositionByDepth = new Array(maxDepth).fill(Number.NaN); for (let i = 0; i < ticks.length; i++) { const tickLabel = ticks[i]; const node = tickNodes?.get(tickLabel); const depth = node == null ? maxDepth - 1 : Math.min(separatorDepth2(node), maxDepth - 1); const position = tickScale.convert(tickLabel); tickInfos[i] = { tickLabel, depth, position }; if (!Number.isFinite(position)) continue; for (let d = 0; d <= depth; d++) { const lastPosition = lastPositionByDepth[d]; if (Number.isFinite(lastPosition)) { minSpacingByDepth[d] = Math.min(minSpacingByDepth[d], Math.abs(position - lastPosition)); } lastPositionByDepth[d] = position; } } return { tickInfos, minSpacingByDepth }; } function getMinDepthToShow(minSpacingByDepth) { for (let depth = 0; depth < minSpacingByDepth.length; depth++) { const minSpacing = minSpacingByDepth[depth]; if (!Number.isFinite(minSpacing) || minSpacing >= MIN_CATEGORY_SPACING) { return depth; } } return minSpacingByDepth.length; } function getTickStepForSpacing(minSpacing) { if (!Number.isFinite(minSpacing) || minSpacing <= 0) { return 1; } return Math.max(1, Math.ceil(MIN_CATEGORY_SPACING / minSpacing)); } function selectVisibleTickInfos(allTickInfos, minDepthToShow, maxDepth, minSpacingByDepth) { if (minDepthToShow <= 0) { return allTickInfos; } const removedDepth = Math.min(minDepthToShow - 1, maxDepth - 1); if (removedDepth < 0) { return allTickInfos; } const tickStep2 = getTickStepForSpacing(minSpacingByDepth[removedDepth]); const visibleTickInfos = []; let removedIndex = 0; for (const info of allTickInfos) { if (info.depth >= minDepthToShow) { visibleTickInfos.push(info); continue; } if (info.depth !== removedDepth) continue; if (removedIndex % tickStep2 === 0) { visibleTickInfos.push(info); } removedIndex++; } return visibleTickInfos; } function convertIntegratedCategoryValue(datum) { return toArray(isObject(datum) && "value" in datum ? datum.value : datum); } // packages/ag-charts-community/src/module/axis-modules/groupedCategoryAxisModule.ts var GroupedCategoryAxisModule = { type: "axis", name: "grouped-category", chartType: "cartesian", version: VERSION, dependencies: [CartesianChartModule], options: groupedCategoryAxisOptionsDefs, themeTemplate: { tick: { enabled: true, stroke: { $ref: "separationLinesColor" } }, label: { spacing: 10, rotation: 270, wrapping: "on-space" }, maxThicknessRatio: 0.5, paddingInner: 0.4, groupPaddingInner: 0.2 }, create: (ctx) => new GroupedCategoryAxis(ctx) }; // packages/ag-charts-community/src/chart/axis/logAxis.ts var LogAxis = class extends NumberAxis { getVisibleDomain(domain) { const [d0, d1] = domain; const [r0, r1] = this.visibleRange; if (domain.length < 2) { return [d0, d1]; } const min = Math.min(d0, d1); const max = Math.max(d0, d1); if (min >= 0) { const log0 = Math.log(d0); const log1 = Math.log(d1); const span = log1 - log0; return [Math.exp(log0 + r0 * span), Math.exp(log0 + r1 * span)]; } if (max <= 0) { const log0 = -Math.log(-d0); const log1 = -Math.log(-d1); const span = log1 - log0; return [-Math.exp(-(log0 + r0 * span)), -Math.exp(-(log0 + r1 * span))]; } return [Number.NaN, Number.NaN]; } normaliseDataDomain(d) { const { min, max, preferredMin, preferredMax } = this; const { extent: extent2, clipped } = normalisedExtentWithMetadata( d.domain, min, max, preferredMin, preferredMax, void 0, d.sortMetadata?.sortOrder ); if (extent2[0] < 0 && extent2[1] > 0 || d.domain[0] < 0 && d.domain[1] > 0) { logger_exports.warn( `The log axis domain crosses zero, the chart data cannot be rendered. See log axis documentation for more information.` ); return { domain: [], clipped }; } else if (extent2[0] === 0 || extent2[1] === 0 || d.domain[0] === 0 || d.domain[1] === 0) { logger_exports.warn( `The log axis domain contains a value of 0, the chart data cannot be rendered. See log axis documentation for more information.` ); return { domain: [], clipped }; } return { domain: extent2, clipped }; } set base(value) { this.scale.base = value; } get base() { return this.scale.base; } constructor(moduleCtx) { super(moduleCtx, new LogScale()); } }; LogAxis.className = "LogAxis"; LogAxis.type = "log"; // packages/ag-charts-community/src/module/axis-modules/logAxisModule.ts var LogAxisModule = { type: "axis", name: "log", chartType: "cartesian", version: VERSION, dependencies: [CartesianChartModule], options: logAxisOptionsDefs, themeTemplate: { base: 10, line: { enabled: false } }, create: (ctx) => new LogAxis(ctx) }; // packages/ag-charts-community/src/module/axis-modules/numberAxisModule.ts var NumberAxisModule = { type: "axis", name: "number", chartType: "cartesian", version: VERSION, dependencies: [CartesianChartModule], options: numberAxisOptionsDefs, themeTemplate: { line: { enabled: false } }, create: (ctx) => new NumberAxis(ctx) }; // packages/ag-charts-community/src/module/axis-modules/timeAxisModule.ts var TimeAxisModule = { type: "axis", name: "time", chartType: "cartesian", version: VERSION, dependencies: [CartesianChartModule], options: timeAxisOptionsDefs, themeTemplate: { gridLine: { enabled: false } }, create: (ctx) => new TimeAxis(ctx) }; // packages/ag-charts-community/src/chart/axis/discreteTimeAxis.ts var DiscreteTimeAxis = class extends CategoryAxis { calculateGridLine({ index: tickIndex, tickId, translation }, index, p1, p2, ticks) { const { gridLine, horizontal, interval, range: range3 } = this; if (interval.placement !== "between") { return super.calculateGridLine({ index: tickIndex, tickId, translation }, index, p1, p2, ticks); } const prevTick = ticks[index - 1]; const offset = prevTick ? translation - (translation - prevTick.translation) / 2 : range3[0]; const [x1, y1, x2, y2] = horizontal ? [offset, Math.max(p1, p2), offset, Math.min(p1, p2)] : [Math.min(p1, p2), offset, Math.max(p1, p2), offset]; const { style: style2 } = gridLine; const { stroke, strokeWidth = 0, lineDash } = style2[tickIndex % style2.length] ?? {}; return { tickId, offset, x1, y1, x2, y2, stroke, strokeWidth, lineDash }; } calculateGridFills(ticks, p1, p2) { if (this.interval.placement !== "between") { return super.calculateGridFills(ticks, p1, p2); } return ticks.map((tick, index) => this.calculateGridFill(tick, index, tick.index, p1, p2, ticks)); } calculateGridFill({ tickId, translation }, index, gridFillIndex, p1, p2, ticks) { const { gridLine, horizontal, interval, range: range3 } = this; if (interval.placement !== "between") { return super.calculateGridFill({ tickId, translation }, index, gridFillIndex, p1, p2, ticks); } const prevTick = ticks[index - 1]; const nextTick = ticks[index + 1]; const startOffset = prevTick ? translation - (translation - prevTick.translation) / 2 : range3[0]; const endOffset = nextTick ? translation + (nextTick.translation - translation) / 2 : range3[1]; const [x1, y1, x2, y2] = horizontal ? [startOffset, Math.max(p1, p2), endOffset, Math.min(p1, p2)] : [Math.min(p1, p2), startOffset, Math.max(p1, p2), endOffset]; const { fill, fillOpacity } = gridLine.style[gridFillIndex % gridLine.style.length] ?? {}; return { tickId, x1, y1, x2, y2, fill, fillOpacity }; } calculateTickLine({ isPrimary, tickId, translation }, index, direction, ticks, scrollbarThickness = 0) { const { horizontal, interval, primaryTick, range: range3, tick } = this; if (interval.placement !== "between") { return super.calculateTickLine( { isPrimary, tickId, translation }, index, direction, ticks, scrollbarThickness ); } const datumTick = isPrimary && primaryTick?.enabled ? primaryTick : tick; const h = -direction * this.getTickSize(datumTick); const prevTick = ticks[index - 1]; const offset = prevTick ? translation - (translation - prevTick.translation) / 2 : range3[0]; const tickOffset = -direction * (scrollbarThickness + this.getTickSpacing(datumTick)); const [x1, y1, x2, y2] = horizontal ? [offset, tickOffset, offset, tickOffset + h] : [tickOffset, offset, tickOffset + h, offset]; const { stroke, width: strokeWidth } = datumTick; const lineDash = void 0; return { tickId, offset, x1, y1, x2, y2, stroke, strokeWidth, lineDash }; } }; // packages/ag-charts-community/src/chart/axis/unitTimeAxis.ts var UnitTimeAxis = class extends DiscreteTimeAxis { constructor(moduleCtx) { super(moduleCtx, new UnitTimeScale(), false); this.parentLevel = new TimeAxisParentLevel(); this.min = void 0; this.max = void 0; this.preferredMin = void 0; this.preferredMax = void 0; // eslint-disable-next-line sonarjs/use-type-alias this.unit = void 0; this.defaultUnit = void 0; } get primaryLabel() { return this.parentLevel.enabled ? this.parentLevel.label : void 0; } get primaryTick() { return this.parentLevel.enabled ? this.parentLevel.tick : void 0; } hasDefinedDomain() { const { min, max } = this; return min != null && max != null && min < max; } isCategoryLike() { return true; } processData() { super.processData(); let defaultUnit; const { domain } = this.dataDomain; if (domain.length === 2 && domain[0].valueOf() === domain[1].valueOf()) { defaultUnit = lowestGranularityUnitForValue(domain[0]); } else { const { boundSeries, direction, min, max } = this; defaultUnit = calculateDefaultUnit(boundSeries, direction, min, max); } if (!objectsEqual(this.defaultUnit, defaultUnit)) { this.defaultUnit = defaultUnit; } } updateScale() { super.updateScale(); this.scale.interval = this.unit ?? this.defaultUnit; } normaliseDataDomain(d) { const { extent: extent2, clipped } = normalisedTimeExtentWithMetadata( d, this.min, this.max, this.preferredMin, this.preferredMax ); return { domain: extent2, clipped }; } tickFormatParams(domain, ticks, _fractionDigits, timeInterval3) { timeInterval3 ?? (timeInterval3 = lowestGranularityUnitForTicks(ticks)); const truncateDate = dateTruncationForDomain(domain); const unit = intervalUnit(timeInterval3); const step = intervalStep(timeInterval3); const epoch = intervalEpoch(timeInterval3); return { type: "date", unit, step, epoch, truncateDate }; } datumFormatParams(value, params, _fractionDigits, timeInterval3, style2) { const interval = this.unit ?? this.defaultUnit ?? "millisecond"; timeInterval3 ?? (timeInterval3 = interval); const { datum, seriesId, legendItemName, key, source, property, domain, boundSeries } = params; const unit = intervalUnit(timeInterval3); const step = intervalStep(timeInterval3); const epoch = intervalEpoch(timeInterval3); return { type: "date", value: intervalFloor(interval, value), datum, seriesId, legendItemName, key, source, property, domain, boundSeries, unit, step, epoch, style: style2 }; } }; UnitTimeAxis.className = "UnitTimeAxis"; UnitTimeAxis.type = "unit-time"; __decorateClass([ addFakeTransformToInstanceProperty ], UnitTimeAxis.prototype, "parentLevel", 2); __decorateClass([ addFakeTransformToInstanceProperty ], UnitTimeAxis.prototype, "min", 2); __decorateClass([ addFakeTransformToInstanceProperty ], UnitTimeAxis.prototype, "max", 2); __decorateClass([ addFakeTransformToInstanceProperty ], UnitTimeAxis.prototype, "preferredMin", 2); __decorateClass([ addFakeTransformToInstanceProperty ], UnitTimeAxis.prototype, "preferredMax", 2); __decorateClass([ addFakeTransformToInstanceProperty ], UnitTimeAxis.prototype, "unit", 2); // packages/ag-charts-community/src/module/axis-modules/unitTimeAxisModule.ts var UnitTimeAxisModule = { type: "axis", name: "unit-time", chartType: "cartesian", version: VERSION, dependencies: [CartesianChartModule], options: unitTimeAxisOptionsDefs, themeTemplate: { groupPaddingInner: 0.1, label: { autoRotate: false }, gridLine: { enabled: false }, parentLevel: { enabled: true }, interval: { placement: "between" } }, create: (ctx) => new UnitTimeAxis(ctx) }; // packages/ag-charts-community/src/module-bundles/cartesian-axes.ts var AllCartesianAxesModule = [ NumberAxisModule, LogAxisModule, TimeAxisModule, CategoryAxisModule, GroupedCategoryAxisModule, UnitTimeAxisModule ]; // packages/ag-charts-community/src/motion/pathMotion.ts function pathMotion(groupId, subId, animationManager, paths, fns) { const animate = (phase, path, collapsable, updateFn) => { animationManager.animate({ id: `${groupId}_${subId}_${path.id}_${phase}`, groupId, from: collapsable ? 1 : 0, to: 1, ease: easeOut, collapsable, onUpdate(ratio2, preInit) { if (preInit && phase !== "removed") return; path.path.clear(true); updateFn(ratio2, path); path.checkPathDirty(); }, onStop() { if (phase !== "added") return; path.path.clear(true); updateFn(1, path); path.checkPathDirty(); }, phase: NODE_UPDATE_STATE_TO_PHASE_MAPPING[phase] }); }; const tempPath = new Path(); const resultsChange = (updateFn) => { tempPath.resetPathDirty(); updateFn(0, tempPath); tempPath.resetPathDirty(); updateFn(1, tempPath); tempPath.checkPathDirty(); return tempPath.isPathDirty(); }; const { addPhaseFn, updatePhaseFn, removePhaseFn } = fns; for (const path of paths) { if (!animationManager.isSkipped()) { animate("removed", path, !resultsChange(removePhaseFn), removePhaseFn); animate("updated", path, !resultsChange(updatePhaseFn), updatePhaseFn); } animate("added", path, !resultsChange(addPhaseFn), addPhaseFn); } } // packages/ag-charts-community/src/chart/labelUtil.ts function getLabelStyles(series, nodeDatum, params, label, isHighlight, activeHighlight, labelPath = ["series", `${series.declarationOrder}`, "label"]) { if (series.visible && label.itemStyler) { const highlightState = series.getHighlightStateString( activeHighlight, isHighlight || nodeDatum != null && activeHighlight?.series === nodeDatum.series && activeHighlight?.datumIndex === nodeDatum.datumIndex, nodeDatum?.datumIndex ); const itemId = typeof nodeDatum?.datumIndex === "number" ? nodeDatum.datumIndex : nodeDatum?.itemId; const styleParams = { border: label.border, color: label.color, cornerRadius: label.cornerRadius, datum: nodeDatum?.datum, enabled: label.enabled, fill: label.fill, fillOpacity: label.fillOpacity, fontFamily: label.fontFamily, fontSize: label.fontSize, fontStyle: label.fontStyle, fontWeight: label.fontWeight, itemId, itemType: nodeDatum?.itemType, seriesId: series.id, padding: label.padding, highlightState }; const stylerResult = series.ctx.optionsGraphService.resolvePartial( labelPath, series.cachedCallWithContext(label.itemStyler, { ...params, ...styleParams }), { pick: false } ) ?? {}; return mergeDefaults(stylerResult, styleParams); } return label; } function updateLabelNode(series, textNode, params, label, labelDatum, isHighlight, activeHighlight) { if (series.visible && label.enabled && labelDatum) { const style2 = getLabelStyles(series, textNode.datum, params, label, isHighlight, activeHighlight); textNode.visible = true; textNode.x = labelDatum.x; textNode.y = labelDatum.y; textNode.text = labelDatum.text; textNode.fill = style2.color; textNode.setAlign(labelDatum); textNode.setFont(style2); textNode.setBoxing(style2); } else { textNode.visible = false; } } var placements = { "inside-start": { inside: true, direction: -1, textAlignment: 1 }, "inside-end": { inside: true, direction: 1, textAlignment: -1 }, "outside-start": { inside: false, direction: -1, textAlignment: -1 }, "outside-end": { inside: false, direction: 1, textAlignment: 1 } }; function adjustLabelPlacement({ isUpward, isVertical, placement, spacing = 0, rect: rect2 }) { let x = rect2.x + rect2.width / 2; let y = rect2.y + rect2.height / 2; let textAlign = "center"; let textBaseline = "middle"; if (placement !== "inside-center") { const barDirection = (isUpward ? 1 : -1) * (isVertical ? -1 : 1); const { direction, textAlignment } = placements[placement]; const displacementRatio = (direction + 1) * 0.5; if (isVertical) { const y0 = isUpward ? rect2.y + rect2.height : rect2.y; const height2 = rect2.height * barDirection; y = y0 + height2 * displacementRatio + spacing * textAlignment * barDirection; textBaseline = textAlignment === barDirection ? "top" : "bottom"; } else { const x0 = isUpward ? rect2.x : rect2.x + rect2.width; const width2 = rect2.width * barDirection; x = x0 + width2 * displacementRatio + spacing * textAlignment * barDirection; textAlign = textAlignment === barDirection ? "left" : "right"; } } return { x, y, textAlign, textBaseline }; } // packages/ag-charts-community/src/util/deferredExecutor.ts var DeferredExecutor = class { constructor(options) { this.minimumDelay = options?.minimumDelay ?? 50; this.timeout = options?.timeout ?? 100; } /** * Schedule a computation for deferred execution. * If something is already pending, it will be cancelled first. */ schedule(computation, onComplete) { this.cancel(); this.pending = { computation, onComplete }; if (this.minimumDelay > 0) { this.delayTimeoutId = setTimeout(() => { this.delayTimeoutId = void 0; this.scheduleIdleCallback(); }, this.minimumDelay); } else { this.scheduleIdleCallback(); } } /** * Force immediate execution if pending. * @returns The computation result, or undefined if nothing was pending. */ demand() { if (!this.pending) { return void 0; } this.cancelScheduled(); return this.execute(); } /** * Cancel any pending execution without running it. */ cancel() { this.cancelScheduled(); this.pending = void 0; } /** * Check if there's pending work. */ isPending() { return this.pending != null; } scheduleIdleCallback() { const window = getWindow(); const remainingTimeout = Math.max(0, this.timeout - this.minimumDelay); if (typeof window.requestIdleCallback === "function") { this.idleCallbackId = window.requestIdleCallback(this.execute.bind(this), { timeout: remainingTimeout }); } else { this.idleCallbackId = setTimeout(() => this.execute(), remainingTimeout); } } cancelScheduled() { if (this.delayTimeoutId != null) { clearTimeout(this.delayTimeoutId); this.delayTimeoutId = void 0; } if (this.idleCallbackId == null) { return; } const window = getWindow(); if (typeof window.cancelIdleCallback === "function") { window.cancelIdleCallback(this.idleCallbackId); } else { clearTimeout(this.idleCallbackId); } this.idleCallbackId = void 0; } execute() { const pending = this.pending; if (!pending) { return void 0; } this.pending = void 0; this.delayTimeoutId = void 0; this.idleCallbackId = void 0; const result = pending.computation(); pending.onComplete(result); return result; } }; // packages/ag-charts-community/src/chart/series/aggregationManager.ts var AggregationManager = class { constructor() { this._dataLength = 0; this.executor = new DeferredExecutor(); } get filters() { return this._filters; } /** * Perform aggregation with deferred computation of coarser levels. * Returns immediate filters suitable for current zoom level. * * @param options.computePartial - Function to compute partial aggregation (receives existing filters for reuse) * @param options.computeFull - Function to compute full aggregation (receives existing filters for reuse) * @param options.targetRange - Current pixel range for determining which level to compute immediately * @returns The computed filters (immediate level for partial, or all levels for full) */ aggregate(options) { this.executor.cancel(); if (options.targetRange > 1 && options.computePartial) { const partialResult = options.computePartial(this._filters); if (partialResult) { const { immediate, computeRemaining } = partialResult; if (computeRemaining) { this.executor.schedule(computeRemaining, (remaining) => { this.mergeFilters(remaining); }); } this._filters = immediate; return immediate; } } this._filters = options.computeFull(this._filters); return this._filters; } /** * Ensure we have an aggregation level suitable for the given range. * Forces deferred computation if needed. */ ensureLevelForRange(range3) { const hasLevel = this._filters?.some((f) => f.maxRange > range3); if (!hasLevel && this.executor.isPending()) { const remaining = this.executor.demand(); if (remaining) { this.mergeFilters(remaining); } } } /** * Get the best filter for a given range. */ getFilterForRange(range3) { return this._filters?.find((f) => f.maxRange > range3); } /** * Cancel any pending deferred computation. */ cancel() { this.executor.cancel(); } /** * Mark filters as stale or discard them based on data size change. * When data size changes significantly (>10x), filters are discarded to prevent * incorrect array reuse. */ markStale(dataLength) { const ratio2 = this._dataLength > 0 ? dataLength / this._dataLength : 0; if (ratio2 > 10 || ratio2 < 0.1 || this._dataLength === 0) { this._filters = void 0; this._dataLength = dataLength; } else if (this._filters) { for (const f of this._filters) { f.stale = true; } } else { this._dataLength = dataLength; } this.executor.cancel(); } mergeFilters(deferredFilters) { if (!this._filters || deferredFilters.length === 0) return; const allFilters = [...this._filters, ...deferredFilters]; allFilters.sort((a, b) => a.maxRange - b.maxRange); this._filters = allFilters; } }; // packages/ag-charts-community/src/chart/series/seriesLabelUtil.ts function seriesLabelFadeInAnimation({ id }, subId, animationManager, ...labelSelections) { for (const labelSelection of labelSelections) { labelSelection.cleanup(); } staticFromToMotion( id, subId, animationManager, labelSelections, { opacity: 0 }, { opacity: 1 }, { phase: "trailing" } ); } function seriesLabelFadeOutAnimation({ id }, subId, animationManager, ...labelSelections) { staticFromToMotion( id, subId, animationManager, labelSelections, { opacity: 1 }, { opacity: 0 }, { phase: "remove" } ); } function resetLabelFn(_node) { return { opacity: 1 }; } // packages/ag-charts-community/src/chart/series/cartesian/areaAggregation.ts var MAX_POINTS = 10; function aggregationIndexType(xValues, d0, d1, indexData, maxRange, datumIndex, xNeedsValueOf) { const xValue = xValues[datumIndex]; if (xValue === void 0) return -1; const xRatio = Number.isFinite(d0) ? aggregationXRatioForXValue(xValue, d0, d1, xNeedsValueOf) : aggregationXRatioForDatumIndex(datumIndex, xValues.length); const aggIndex = aggregationIndexForXRatio(xRatio, maxRange); if (datumIndex === indexData[aggIndex + AGGREGATION_INDEX_X_MIN] || datumIndex === indexData[aggIndex + AGGREGATION_INDEX_X_MAX] || datumIndex === indexData[aggIndex + AGGREGATION_INDEX_Y_MIN] || datumIndex === indexData[aggIndex + AGGREGATION_INDEX_Y_MAX]) { return aggIndex; } return -1; } function buildIndicesFromAggregation(xValues, d0, d1, indexData, maxRange, xNeedsValueOf, xValuesLength, reuseIndices, reuseMetaIndices) { let indicesCount = 0; let metaIndicesCount = 0; let currentGroup = -1; for (let datumIndex = 0; datumIndex < xValuesLength; datumIndex++) { const group = aggregationIndexType(xValues, d0, d1, indexData, maxRange, datumIndex, xNeedsValueOf); if (group === -1) continue; indicesCount++; if (group !== currentGroup) { metaIndicesCount++; currentGroup = group; } } metaIndicesCount++; const indices = reuseIndices?.length === indicesCount ? reuseIndices : new Uint32Array(indicesCount); const metaIndices = reuseMetaIndices?.length === metaIndicesCount ? reuseMetaIndices : new Uint32Array(metaIndicesCount); let indicesIdx = 0; let metaIndicesIdx = 0; currentGroup = -1; for (let datumIndex = 0; datumIndex < xValuesLength; datumIndex++) { const group = aggregationIndexType(xValues, d0, d1, indexData, maxRange, datumIndex, xNeedsValueOf); if (group === -1) continue; if (group !== currentGroup) { metaIndices[metaIndicesIdx++] = indicesIdx; currentGroup = group; } indices[indicesIdx++] = datumIndex; } metaIndices[metaIndicesIdx] = indicesCount - 1; return { indices, metaIndices }; } function computeAreaAggregation(domain, xValues, yValues, options) { const xValuesLength = xValues.length; if (xValuesLength < AGGREGATION_THRESHOLD) return; const [d0, d1] = domain; const { xNeedsValueOf, yNeedsValueOf, existingFilters } = options; let maxRange = aggregationRangeFittingPoints(xValues, d0, d1, { xNeedsValueOf }); const existingFilter = existingFilters?.find((f) => f.maxRange === maxRange); let { indexData, valueData } = createAggregationIndices(xValues, yValues, yValues, d0, d1, maxRange, { xNeedsValueOf, yNeedsValueOf, reuseIndexData: existingFilter?.indexData, reuseValueData: existingFilter?.valueData }); let { indices, metaIndices } = buildIndicesFromAggregation( xValues, d0, d1, indexData, maxRange, xNeedsValueOf, xValuesLength, existingFilter?.indices, existingFilter?.metaIndices ); const filters = [{ maxRange, metaIndices, indices, indexData, valueData }]; while (indices.length > MAX_POINTS && maxRange > AGGREGATION_MIN_RANGE) { const currentMaxRange = maxRange; const nextMaxRange = Math.trunc(currentMaxRange / 2); const nextExistingFilter = existingFilters?.find((f) => f.maxRange === nextMaxRange); const compacted = compactAggregationIndices(indexData, valueData, currentMaxRange, { reuseIndexData: nextExistingFilter?.indexData, reuseValueData: nextExistingFilter?.valueData }); maxRange = compacted.maxRange; indexData = compacted.indexData; valueData = compacted.valueData; const previousIndices = indices; let indicesCount = 0; let metaIndicesCount = 0; let currentGroup = -1; for (const datumIndex of previousIndices) { const group = aggregationIndexType(xValues, d0, d1, indexData, maxRange, datumIndex, xNeedsValueOf); if (group === -1) continue; indicesCount++; if (group !== currentGroup) { metaIndicesCount++; currentGroup = group; } } metaIndicesCount++; const newIndices = nextExistingFilter?.indices?.length === indicesCount ? nextExistingFilter.indices : new Uint32Array(indicesCount); const newMetaIndices = nextExistingFilter?.metaIndices?.length === metaIndicesCount ? nextExistingFilter.metaIndices : new Uint32Array(metaIndicesCount); let indicesIdx = 0; let metaIndicesIdx = 0; currentGroup = -1; for (const datumIndex of previousIndices) { const group = aggregationIndexType(xValues, d0, d1, indexData, maxRange, datumIndex, xNeedsValueOf); if (group === -1) continue; if (group !== currentGroup) { newMetaIndices[metaIndicesIdx++] = indicesIdx; currentGroup = group; } newIndices[indicesIdx++] = datumIndex; } newMetaIndices[metaIndicesIdx] = indicesCount - 1; indices = newIndices; metaIndices = newMetaIndices; filters.push({ maxRange, metaIndices, indices, indexData, valueData }); } filters.reverse(); return filters; } function computeAreaAggregationPartial(domain, xValues, yValues, options) { const xValuesLength = xValues.length; if (xValuesLength < AGGREGATION_THRESHOLD) return; const [d0, d1] = domain; const { xNeedsValueOf, yNeedsValueOf, targetRange, existingFilters } = options; const finestMaxRange = aggregationRangeFittingPoints(xValues, d0, d1, { xNeedsValueOf }); const targetMaxRange = Math.min(finestMaxRange, nextPowerOf2(Math.max(targetRange, AGGREGATION_MIN_RANGE))); const existingFilter = existingFilters?.find((f) => f.maxRange === targetMaxRange); const { indexData, valueData } = createAggregationIndices(xValues, yValues, yValues, d0, d1, targetMaxRange, { xNeedsValueOf, yNeedsValueOf, reuseIndexData: existingFilter?.indexData, reuseValueData: existingFilter?.valueData }); const { indices, metaIndices } = buildIndicesFromAggregation( xValues, d0, d1, indexData, targetMaxRange, xNeedsValueOf, xValuesLength, existingFilter?.indices, existingFilter?.metaIndices ); const immediateLevel = { maxRange: targetMaxRange, indices, metaIndices, indexData, valueData }; function computeRemaining() { const allLevels = computeAreaAggregation([d0, d1], xValues, yValues, { xNeedsValueOf, yNeedsValueOf, existingFilters }); return allLevels?.filter((level) => level.maxRange !== targetMaxRange) ?? []; } return { immediate: [immediateLevel], computeRemaining }; } function aggregateAreaData(scale2, xValues, yValues, domainInput, xNeedsValueOf, yNeedsValueOf) { const [d0, d1] = aggregationDomain(scale2, domainInput); return computeAreaAggregation([d0, d1], xValues, yValues, { xNeedsValueOf, yNeedsValueOf }); } var memoizedAggregateAreaData = simpleMemorize2(aggregateAreaData); function aggregateAreaDataFromDataModel(scale2, dataModel, processedData, yKey, series, existingFilters) { const xValues = dataModel.resolveKeysById(series, "xValue", processedData); const yValues = dataModel.resolveColumnById(series, yKey, processedData); const domainInput = dataModel.getDomain(series, "xValue", "key", processedData); const xNeedsValueOf = dataModel.resolveColumnNeedsValueOf(series, "xValue", processedData); const yNeedsValueOf = dataModel.resolveColumnNeedsValueOf(series, yKey, processedData); if (existingFilters) { const [d0, d1] = aggregationDomain(scale2, domainInput); return computeAreaAggregation([d0, d1], xValues, yValues, { xNeedsValueOf, yNeedsValueOf, existingFilters }); } return memoizedAggregateAreaData(scale2, xValues, yValues, domainInput, xNeedsValueOf, yNeedsValueOf); } function aggregateAreaDataFromDataModelPartial(scale2, dataModel, processedData, yKey, series, targetRange, existingFilters) { const xValues = dataModel.resolveKeysById(series, "xValue", processedData); const yValues = dataModel.resolveColumnById(series, yKey, processedData); const domainInput = dataModel.getDomain(series, "xValue", "key", processedData); const xNeedsValueOf = dataModel.resolveColumnNeedsValueOf(series, "xValue", processedData); const yNeedsValueOf = dataModel.resolveColumnNeedsValueOf(series, yKey, processedData); const [d0, d1] = aggregationDomain(scale2, domainInput); return computeAreaAggregationPartial([d0, d1], xValues, yValues, { xNeedsValueOf, yNeedsValueOf, targetRange, existingFilters }); } // packages/ag-charts-community/src/scene/dropShadow.ts var DropShadow = class extends ChangeDetectableProperties { constructor() { super(...arguments); this.enabled = true; this.color = "rgba(0, 0, 0, 0.5)"; this.xOffset = 0; this.yOffset = 0; this.blur = 5; } }; __decorateClass([ addFakeTransformToInstanceProperty, SceneChangeDetection() ], DropShadow.prototype, "enabled", 2); __decorateClass([ addFakeTransformToInstanceProperty, SceneChangeDetection() ], DropShadow.prototype, "color", 2); __decorateClass([ addFakeTransformToInstanceProperty, SceneChangeDetection() ], DropShadow.prototype, "xOffset", 2); __decorateClass([ addFakeTransformToInstanceProperty, SceneChangeDetection() ], DropShadow.prototype, "yOffset", 2); __decorateClass([ addFakeTransformToInstanceProperty, SceneChangeDetection() ], DropShadow.prototype, "blur", 2); // packages/ag-charts-community/src/chart/series/seriesMarker.ts var SeriesMarker = class extends ChangeDetectableProperties { constructor() { super(...arguments); this.enabled = true; this.shape = "circle"; this.size = 0; this.fillOpacity = 1; this.strokeWidth = 1; this.strokeOpacity = 1; this.lineDash = [0]; this.lineDashOffset = 0; } getStyle() { const { size, shape, fill, fillOpacity, stroke, strokeWidth, strokeOpacity, lineDash, lineDashOffset } = this; return { size, shape, fill, fillOpacity, stroke, strokeWidth, strokeOpacity, lineDash, lineDashOffset }; } getDiameter() { return this.size + this.strokeWidth; } }; __decorateClass([ addFakeTransformToInstanceProperty, SceneChangeDetection() ], SeriesMarker.prototype, "enabled", 2); __decorateClass([ addFakeTransformToInstanceProperty, SceneObjectChangeDetection({ equals: TRIPLE_EQ }) ], SeriesMarker.prototype, "shape", 2); __decorateClass([ addFakeTransformToInstanceProperty, SceneChangeDetection() ], SeriesMarker.prototype, "size", 2); __decorateClass([ addFakeTransformToInstanceProperty, SceneObjectChangeDetection({ equals: objectsEqual }) ], SeriesMarker.prototype, "fill", 2); __decorateClass([ addFakeTransformToInstanceProperty, SceneChangeDetection() ], SeriesMarker.prototype, "fillOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty, SceneChangeDetection() ], SeriesMarker.prototype, "stroke", 2); __decorateClass([ addFakeTransformToInstanceProperty, SceneChangeDetection() ], SeriesMarker.prototype, "strokeWidth", 2); __decorateClass([ addFakeTransformToInstanceProperty, SceneChangeDetection() ], SeriesMarker.prototype, "strokeOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SeriesMarker.prototype, "lineDash", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SeriesMarker.prototype, "lineDashOffset", 2); __decorateClass([ addFakeTransformToInstanceProperty, SceneObjectChangeDetection({ equals: TRIPLE_EQ }) ], SeriesMarker.prototype, "itemStyler", 2); // packages/ag-charts-community/src/chart/series/seriesTooltip.ts function buildLineWithMarkerDefaults(line, marker) { if (line == null) return void 0; return { enabled: line.enabled ?? true, stroke: line.stroke ?? marker?.stroke ?? "transparent", strokeWidth: line.strokeWidth ?? marker?.strokeWidth ?? 1, strokeOpacity: line.strokeOpacity ?? marker?.strokeOpacity ?? 1, lineDash: line.lineDash ?? marker?.lineDash ?? [] }; } var SeriesTooltipInteraction = class extends BaseProperties { constructor() { super(...arguments); this.enabled = false; } }; __decorateClass([ addFakeTransformToInstanceProperty ], SeriesTooltipInteraction.prototype, "enabled", 2); var SeriesTooltip = class extends BaseProperties { constructor() { super(...arguments); this.interaction = new SeriesTooltipInteraction(); this.position = new TooltipPosition(); this.range = void 0; this.class = void 0; } formatTooltip(callers, content, params) { const overrides = this.renderer == null ? void 0 : callWithContext(callers, this.renderer, params); if (isString(overrides) || isNumber(overrides) || isDate(overrides)) { return { type: "raw", rawHtmlString: toTextString(overrides) }; } if (overrides != null) { const mergedMarker = mergeDefaults(overrides.symbol?.marker, content.symbol?.marker); const mergedLineInput = overrides.symbol?.line ?? content.symbol?.line ? mergeDefaults(overrides.symbol?.line, content.symbol?.line) : void 0; const symbol = content.symbol || overrides.symbol ? { marker: mergedMarker, line: buildLineWithMarkerDefaults(mergedLineInput, mergedMarker) } : void 0; return { type: "structured", ...content, ...overrides, symbol }; } return { type: "structured", ...content }; } }; __decorateClass([ addFakeTransformToInstanceProperty ], SeriesTooltip.prototype, "enabled", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SeriesTooltip.prototype, "showArrow", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SeriesTooltip.prototype, "renderer", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SeriesTooltip.prototype, "interaction", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SeriesTooltip.prototype, "position", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SeriesTooltip.prototype, "range", 2); __decorateClass([ addFakeTransformToInstanceProperty ], SeriesTooltip.prototype, "class", 2); function makeSeriesTooltip() { return new SeriesTooltip(); } // packages/ag-charts-community/src/chart/series/cartesian/areaSeriesProperties.ts var AreaSeriesProperties = class extends CartesianSeriesProperties { constructor() { super(...arguments); this.xName = void 0; this.fill = "#c16068"; this.fillOpacity = 1; this.stroke = "#874349"; this.strokeWidth = 2; this.strokeOpacity = 1; this.lineDash = [0]; this.lineDashOffset = 0; this.interpolation = new InterpolationProperties(); this.shadow = new DropShadow(); this.marker = new SeriesMarker(); this.label = new Label(); this.tooltip = makeSeriesTooltip(); this.connectMissingData = false; } }; __decorateClass([ addFakeTransformToInstanceProperty ], AreaSeriesProperties.prototype, "xKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AreaSeriesProperties.prototype, "xName", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AreaSeriesProperties.prototype, "yKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AreaSeriesProperties.prototype, "yName", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AreaSeriesProperties.prototype, "yFilterKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AreaSeriesProperties.prototype, "stackGroup", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AreaSeriesProperties.prototype, "normalizedTo", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AreaSeriesProperties.prototype, "fill", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AreaSeriesProperties.prototype, "fillOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AreaSeriesProperties.prototype, "stroke", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AreaSeriesProperties.prototype, "strokeWidth", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AreaSeriesProperties.prototype, "strokeOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AreaSeriesProperties.prototype, "lineDash", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AreaSeriesProperties.prototype, "lineDashOffset", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AreaSeriesProperties.prototype, "interpolation", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AreaSeriesProperties.prototype, "styler", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AreaSeriesProperties.prototype, "shadow", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AreaSeriesProperties.prototype, "marker", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AreaSeriesProperties.prototype, "label", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AreaSeriesProperties.prototype, "tooltip", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AreaSeriesProperties.prototype, "connectMissingData", 2); // packages/ag-charts-community/src/chart/series/cartesian/lineInterpolationPlotting.ts function lerp2(a, b, ratio2) { return (b - a) * ratio2 + a; } function linearSupertype(span, stepX) { const { x0, y0, x1, y1 } = span; const m = (y1 - y0) / (x1 - x0); const stepY = m * (stepX - x0) + y0; return { leftCp1x: x0, leftCp1y: y0, leftCp2x: stepX, leftCp2y: stepY, stepX, stepY0: stepY, stepY1: stepY, rightCp1x: stepX, rightCp1y: stepY, rightCp2x: x1, rightCp2y: y1 }; } function bezierSupertype(span, stepX) { const { cp0x, cp0y, cp1x, cp1y, cp2x, cp2y, cp3x, cp3y } = span; const t = solveBezier(cp0x, cp1x, cp2x, cp3x, stepX); const [left, right] = splitBezier2D(cp0x, cp0y, cp1x, cp1y, cp2x, cp2y, cp3x, cp3y, t); const stepY = left[3].y; return { leftCp1x: left[1].x, leftCp1y: left[1].y, leftCp2x: left[2].x, leftCp2y: left[2].y, stepX, stepY0: stepY, stepY1: stepY, rightCp1x: right[1].x, rightCp1y: right[1].y, rightCp2x: right[2].x, rightCp2y: right[2].y }; } function stepSupertype(span) { const { x0, y0, x1, y1, stepX } = span; return { leftCp1x: (x0 + stepX) / 2, leftCp1y: y0, leftCp2x: (x0 + stepX) / 2, leftCp2y: y0, stepX, stepY0: y0, stepY1: y1, rightCp1x: (stepX + x1) / 2, rightCp1y: y1, rightCp2x: (stepX + x1) / 2, rightCp2y: y1 }; } function spanSupertype(span, stepX) { if (span.type === "linear") { return linearSupertype(span, stepX); } else if (span.type === "cubic") { return bezierSupertype(span, stepX); } else if (span.type === "step") { return stepSupertype(span); } else { return linearSupertype(span, stepX); } } function plotStart(path, moveTo, x0, y0, x1, y1, reversed) { switch (moveTo) { case 0 /* MoveTo */: if (reversed) { path.moveTo(x1, y1); } else { path.moveTo(x0, y0); } break; case 1 /* LineTo */: if (reversed) { path.lineTo(x1, y1); } else { path.lineTo(x0, y0); } break; case 2 /* Skip */: break; } } function plotLinear(path, x0, y0, x1, y1, reversed) { if (reversed) { path.lineTo(x0, y0); } else { path.lineTo(x1, y1); } } function plotCubic(path, cp0x, cp0y, cp1x, cp1y, cp2x, cp2y, cp3x, cp3y, reversed) { if (reversed) { path.cubicCurveTo(cp2x, cp2y, cp1x, cp1y, cp0x, cp0y); } else { path.cubicCurveTo(cp1x, cp1y, cp2x, cp2y, cp3x, cp3y); } } function plotStep(path, x0, y0, x1, y1, stepX, reversed) { if (reversed) { path.lineTo(stepX, y1); path.lineTo(stepX, y0); path.lineTo(x0, y0); } else { path.lineTo(stepX, y0); path.lineTo(stepX, y1); path.lineTo(x1, y1); } } function plotMultiLine(path, x0, y0, x1, y1, midPoints, reversed) { if (reversed) { for (let i = midPoints.length - 1; i >= 0; i--) { const { x, y } = midPoints[i]; path.lineTo(x, y); } path.lineTo(x0, y0); } else { for (const { x, y } of midPoints) { path.lineTo(x, y); } path.lineTo(x1, y1); } } function plotSpan(path, span, moveTo, reversed) { const [start2, end3] = spanRange(span); plotStart(path, moveTo, start2.x, start2.y, end3.x, end3.y, reversed); switch (span.type) { case "linear": plotLinear(path, span.x0, span.y0, span.x1, span.y1, reversed); break; case "cubic": plotCubic( path, span.cp0x, span.cp0y, span.cp1x, span.cp1y, span.cp2x, span.cp2y, span.cp3x, span.cp3y, reversed ); break; case "step": plotStep(path, span.x0, span.y0, span.x1, span.y1, span.stepX, reversed); break; case "multi-line": plotMultiLine(path, span.x0, span.y0, span.x1, span.y1, span.midPoints, reversed); break; } } function interpolatedSpanRange(a, b, ratio2) { const [aStart, aEnd] = spanRange(a); const [bStart, bEnd] = spanRange(b); const x0 = lerp2(aStart.x, bStart.x, ratio2); const y0 = lerp2(aStart.y, bStart.y, ratio2); const x1 = lerp2(aEnd.x, bEnd.x, ratio2); const y1 = lerp2(aEnd.y, bEnd.y, ratio2); return [ { x: x0, y: y0 }, { x: x1, y: y1 } ]; } function plotInterpolatedSpans(path, a, b, ratio2, moveTo, reversed) { const [{ x: x0, y: y0 }, { x: x1, y: y1 }] = interpolatedSpanRange(a, b, ratio2); plotStart(path, moveTo, x0, y0, x1, y1, reversed); if (a.type === "cubic" && b.type === "cubic") { const cp1x = lerp2(a.cp1x, b.cp1x, ratio2); const cp1y = lerp2(a.cp1y, b.cp1y, ratio2); const cp2x = lerp2(a.cp2x, b.cp2x, ratio2); const cp2y = lerp2(a.cp2y, b.cp2y, ratio2); plotCubic(path, x0, y0, cp1x, cp1y, cp2x, cp2y, x1, y1, reversed); } else if (a.type === "step" && b.type === "step") { const stepX = lerp2(a.stepX, b.stepX, ratio2); plotStep(path, x0, y0, x1, y1, stepX, reversed); } else if (a.type === "linear" && b.type === "linear") { plotLinear(path, x0, y0, x1, y1, reversed); } else { let defaultStepX; if (a.type === "step") { defaultStepX = a.stepX; } else if (b.type === "step") { defaultStepX = b.stepX; } else { defaultStepX = (x0 + x1) / 2; } const as = spanSupertype(a, defaultStepX); const bs = spanSupertype(b, defaultStepX); const leftCp1x = lerp2(as.leftCp1x, bs.leftCp1x, ratio2); const leftCp1y = lerp2(as.leftCp1y, bs.leftCp1y, ratio2); const leftCp2x = lerp2(as.leftCp2x, bs.leftCp2x, ratio2); const leftCp2y = lerp2(as.leftCp2y, bs.leftCp2y, ratio2); const stepX = lerp2(as.stepX, bs.stepX, ratio2); const stepY0 = lerp2(as.stepY0, bs.stepY0, ratio2); const stepY1 = lerp2(as.stepY1, bs.stepY1, ratio2); const rightCp1x = lerp2(as.rightCp1x, bs.rightCp1x, ratio2); const rightCp1y = lerp2(as.rightCp1y, bs.rightCp1y, ratio2); const rightCp2x = lerp2(as.rightCp2x, bs.rightCp2x, ratio2); const rightCp2y = lerp2(as.rightCp2y, bs.rightCp2y, ratio2); if (reversed) { path.cubicCurveTo(rightCp2x, rightCp2y, rightCp1x, rightCp1y, stepX, stepY1); path.lineTo(stepX, stepY0); path.cubicCurveTo(leftCp2x, leftCp2y, leftCp1x, leftCp1y, x0, y0); } else { path.cubicCurveTo(leftCp1x, leftCp1y, leftCp2x, leftCp2y, stepX, stepY0); path.lineTo(stepX, stepY1); path.cubicCurveTo(rightCp1x, rightCp1y, rightCp2x, rightCp2y, x1, y1); } } } // packages/ag-charts-community/src/chart/series/cartesian/lineInterpolationUtil.ts var MAX_CATEGORIES = 1e3; var CollapseMode = /* @__PURE__ */ ((CollapseMode2) => { CollapseMode2[CollapseMode2["Zero"] = 0] = "Zero"; CollapseMode2[CollapseMode2["Split"] = 1] = "Split"; return CollapseMode2; })(CollapseMode || {}); function integratedCategoryMatch(a, b) { if (a == null || b == null) return false; if (typeof a !== "object" || typeof b !== "object") return false; if ("id" in a && "id" in b) { return a.id === b.id; } return a.toString() === b.toString(); } function toAxisValue(value) { return transformIntegratedCategoryValue(value).valueOf(); } function scale(val, scaling) { if (!scaling) return Number.NaN; if (val instanceof Date) { val = val.getTime(); } if (scaling.type === "continuous" && typeof val === "number") { const domainRatio = (val - scaling.domain[0]) / (scaling.domain[1] - scaling.domain[0]); return domainRatio * (scaling.range[1] - scaling.range[0]) + scaling.range[0]; } if (scaling.type === "log" && typeof val === "number") { return scaling.convert(val); } if (scaling.type !== "category") return Number.NaN; if (isUnitTimeCategoryScaling(scaling)) { if (typeof val === "number") { const { firstBandTime, intervalMs, bandCount, inset, step } = scaling; const matchingIndex2 = Math.round((val - firstBandTime) / intervalMs); if (matchingIndex2 >= 0 && matchingIndex2 < bandCount) { return inset + step * matchingIndex2; } } return Number.NaN; } const axisValue = toAxisValue(val); let matchingIndex = scaling.domain.findIndex((d) => toAxisValue(d) === axisValue); if (matchingIndex === -1) { matchingIndex = scaling.domain.findIndex((d) => integratedCategoryMatch(val, d)); } if (matchingIndex >= 0) { return scaling.inset + scaling.step * matchingIndex; } return Number.NaN; } function getAxisIndices({ data }, values) { return data.map((datum, datumIndex) => ({ xValue0Index: values.indexOf(toAxisValue(datum.xValue0)), xValue1Index: values.indexOf(toAxisValue(datum.xValue1)), datumIndex })); } function isValidScaling(data) { return Object.values(data.scales).every((s) => { if (s.type === "category") { if (isUnitTimeCategoryScaling(s)) { return s.bandCount < MAX_CATEGORIES; } return s.domain.length < MAX_CATEGORIES; } return true; }); } function validateCategorySorting(newData, oldData) { const oldScale = oldData.scales.x; const newScale = newData.scales.x; if (oldScale?.type !== "category" || newScale?.type !== "category") return true; if (isUnitTimeCategoryScaling(oldScale) || isUnitTimeCategoryScaling(newScale)) { return true; } let x0 = -Infinity; for (const oldValue of oldScale.domain) { const x = scale(oldValue, newScale); if (!Number.isFinite(x)) continue; if (x < x0) { return false; } else { x0 = x; } } return true; } function validateAxisEntriesOrder(axisValues, data) { let x0 = -Infinity; for (const axisValue of axisValues) { const x = scale(axisValue.value, data.scales.x); if (!Number.isFinite(x)) continue; if (x < x0) { return false; } else { x0 = x; } } return true; } function spanAxisContext(newData, oldData) { const allAxisEntries = /* @__PURE__ */ new Map(); for (const { xValue0, xValue1 } of newData.data) { const xValue0Value = toAxisValue(xValue0); const xValue1Value = toAxisValue(xValue1); allAxisEntries.set(xValue0Value, xValue0).set(xValue1Value, xValue1); } const newAxisEntries = Array.from(allAxisEntries, ([axisValue, value]) => ({ axisValue, value })); newAxisEntries.sort((a, b) => { return scale(a.value, newData.scales.x) - scale(b.value, newData.scales.x); }); const exclusivelyOldAxisEntries = []; for (const { xValue0, xValue1 } of oldData.data) { const xValue0Value = toAxisValue(xValue0); const xValue1Value = toAxisValue(xValue1); if (!allAxisEntries.has(xValue0Value)) { allAxisEntries.set(xValue0Value, xValue0); exclusivelyOldAxisEntries.push({ axisValue: xValue0Value, value: xValue0 }); } if (!allAxisEntries.has(xValue1Value)) { allAxisEntries.set(xValue1Value, xValue1); exclusivelyOldAxisEntries.push({ axisValue: xValue1Value, value: xValue1 }); } } exclusivelyOldAxisEntries.sort((a, b) => { return scale(a.value, oldData.scales.x) - scale(b.value, oldData.scales.x); }); const axisEntries = newAxisEntries; let insertionIndex = 0; for (const oldAxisEntry of exclusivelyOldAxisEntries) { for (let i = axisEntries.length - 1; i >= insertionIndex; i -= 1) { const oldValueX = scale(oldAxisEntry.value, oldData.scales.x); const newValueX = scale(axisEntries[i].value, oldData.scales.x); if (oldValueX > newValueX) { insertionIndex = i + 1; break; } } axisEntries.splice(insertionIndex, 0, oldAxisEntry); insertionIndex += 1; } if (!validateAxisEntriesOrder(axisEntries, oldData)) return; const axisValues = axisEntries.map((axisEntry) => axisEntry.axisValue); const oldDataAxisIndices = getAxisIndices(oldData, axisValues); const newDataAxisIndices = getAxisIndices(newData, axisValues); return { axisValues, oldDataAxisIndices, newDataAxisIndices }; } function clipSpan(span, data, axisValues, xValue0Index, xIndices) { if (xIndices.xValue1Index === xIndices.xValue0Index + 1) return span; const range3 = spanRange(span); let start2; let end3; if (data.scales.x?.type === "category") { const step = (range3[1].x - range3[0].x) / (xIndices.xValue1Index - xIndices.xValue0Index); start2 = range3[0].x + (xValue0Index - xIndices.xValue0Index) * step; end3 = start2 + step; } else { const xValue0 = axisValues[xValue0Index]; const xValue1 = axisValues[xValue0Index + 1]; start2 = scale(xValue0, data.scales.x); end3 = scale(xValue1, data.scales.x); } return clipSpanX(span, start2, end3); } function axisZeroSpan(span, data) { const [r0, r1] = spanRange(span); const y0 = scale(0, data.scales.y); return rescaleSpan(span, { x: r0.x, y: y0 }, { x: r1.x, y: y0 }); } function collapseSpanToMidpoint(span) { const [r0, r1] = spanRange(span); return collapseSpanToPoint(span, { x: (r0.x + r1.x) / 2, y: (r0.y + r1.y) / 2 }); } function collapseSpan(span, collapseMode, data, axisIndices, indices, range3) { let xValue; let yValue; if (indices.xValue0Index >= range3.xValue1Index) { const datumIndex = axisIndices.findLast((i) => i.xValue1Index <= range3.xValue1Index)?.datumIndex; const datum = datumIndex == null ? void 0 : data.data[datumIndex]; xValue = datum?.xValue1; yValue = datum?.yValue1; } else if (indices.xValue0Index <= range3.xValue0Index) { const datumIndex = axisIndices.find((i) => i.xValue0Index >= range3.xValue0Index)?.datumIndex; const datum = datumIndex == null ? void 0 : data.data[datumIndex]; xValue = datum?.xValue0; yValue = datum?.yValue0; } if (xValue == null || yValue == null) { switch (collapseMode) { case 0 /* Zero */: return axisZeroSpan(span, data); case 1 /* Split */: return collapseSpanToMidpoint(span); } } const x = scale(xValue, data.scales.x); const y = scale(yValue, data.scales.y); const point = { x, y }; return rescaleSpan(span, point, point); } function zeroDataSpan(spanDatum, zeroData) { if (zeroData == null) return; const newSpanXValue0 = toAxisValue(spanDatum.xValue0); const newSpanXValue1 = toAxisValue(spanDatum.xValue1); return zeroData.find( (zeroSpanDatum) => toAxisValue(zeroSpanDatum.xValue0) === newSpanXValue0 && toAxisValue(zeroSpanDatum.xValue1) === newSpanXValue1 )?.span; } function addSpan(newData, collapseMode, newAxisIndices, newIndices, oldZeroData, range3, out) { const newSpanDatum = newData.data[newIndices.datumIndex]; const newSpan = newSpanDatum.span; const zeroSpan = zeroDataSpan(newSpanDatum, oldZeroData); if (zeroSpan == null) { const oldSpan = collapseSpan(newSpan, collapseMode, newData, newAxisIndices, newIndices, range3); out.added.push({ from: oldSpan, to: newSpan }); } else { out.removed.push({ from: zeroSpan, to: zeroSpan }); out.moved.push({ from: zeroSpan, to: newSpan }); out.added.push({ from: newSpan, to: newSpan }); } } function removeSpan(oldData, collapseMode, oldAxisIndices, oldIndices, newZeroData, range3, out) { const oldSpanDatum = oldData.data[oldIndices.datumIndex]; const oldSpan = oldSpanDatum.span; const zeroSpan = zeroDataSpan(oldSpanDatum, newZeroData); if (zeroSpan == null) { const newSpan = collapseSpan(oldSpan, collapseMode, oldData, oldAxisIndices, oldIndices, range3); out.removed.push({ from: oldSpan, to: newSpan }); } else { out.removed.push({ from: oldSpan, to: oldSpan }); out.moved.push({ from: oldSpan, to: zeroSpan }); out.added.push({ from: zeroSpan, to: zeroSpan }); } } function alignSpanToContainingSpan(span, axisValues, preData, postData, postSpanIndices) { const xScale = postData.scales.x; const startXValue0 = axisValues[postSpanIndices.xValue0Index]; const endXValue1 = axisValues[postSpanIndices.xValue1Index]; let startDatum; let endDatum; if (xScale?.type === "continuous" || xScale?.type === "log") { startDatum = preData.data.findLast((spanDatum) => toAxisValue(spanDatum.xValue0) <= startXValue0); endDatum = preData.data.find((spanDatum) => toAxisValue(spanDatum.xValue1) >= endXValue1); } else { startDatum = preData.data.find((spanDatum) => toAxisValue(spanDatum.xValue0) === startXValue0); endDatum = preData.data.find((spanDatum) => toAxisValue(spanDatum.xValue1) === endXValue1); } if (startDatum == null || endDatum == null) return; const [{ x: x0 }, { x: x1 }] = spanRange(span); const startX = scale(startDatum.xValue0, preData.scales.x); const startY = scale(startDatum.yValue0, preData.scales.y); const endX = scale(endDatum.xValue1, preData.scales.x); const endY = scale(endDatum.yValue1, preData.scales.y); let altSpan = postData.data[postSpanIndices.datumIndex].span; altSpan = rescaleSpan(altSpan, { x: startX, y: startY }, { x: endX, y: endY }); altSpan = clipSpanX(altSpan, x0, x1); return altSpan; } function appendSpanPhases(newData, oldData, collapseMode, axisValues, xValue0Index, newAxisIndices, oldAxisIndices, range3, out) { const xValue1Index = xValue0Index + 1; const oldIndices = oldAxisIndices.find((i) => i.xValue0Index <= xValue0Index && i.xValue1Index >= xValue1Index); const newIndices = newAxisIndices.find((i) => i.xValue0Index <= xValue0Index && i.xValue1Index >= xValue1Index); const oldZeroData = oldData.zeroData; const newZeroData = newData.zeroData; if (oldIndices == null && newIndices != null) { addSpan(newData, collapseMode, newAxisIndices, newIndices, oldZeroData, range3, out); return; } else if (oldIndices != null && newIndices == null) { removeSpan(oldData, collapseMode, oldAxisIndices, oldIndices, newZeroData, range3, out); return; } else if (oldIndices == null || newIndices == null) { return; } let ordering; if (oldIndices.xValue0Index === newIndices.xValue0Index && oldIndices.xValue1Index === newIndices.xValue1Index) { ordering = 0; } else if (oldIndices.xValue0Index <= newIndices.xValue0Index && oldIndices.xValue1Index >= newIndices.xValue1Index) { ordering = -1; } else if (oldIndices.xValue0Index >= newIndices.xValue0Index && oldIndices.xValue1Index <= newIndices.xValue1Index) { ordering = 1; } else { ordering = 0; } const oldSpanDatum = oldData.data[oldIndices.datumIndex]; const clippedOldSpanOldScale = clipSpan(oldSpanDatum.span, oldData, axisValues, xValue0Index, oldIndices); const newSpanDatum = newData.data[newIndices.datumIndex]; const clippedNewSpanNewScale = clipSpan(newSpanDatum.span, newData, axisValues, xValue0Index, newIndices); if (ordering === 1) { const clippedPostRemoveOldSpanOldScale = alignSpanToContainingSpan( clippedOldSpanOldScale, axisValues, oldData, newData, newIndices ); if (clippedPostRemoveOldSpanOldScale == null) { removeSpan(oldData, collapseMode, oldAxisIndices, oldIndices, newZeroData, range3, out); } else { out.removed.push({ from: clippedOldSpanOldScale, to: clippedPostRemoveOldSpanOldScale }); out.moved.push({ from: clippedPostRemoveOldSpanOldScale, to: clippedNewSpanNewScale }); out.added.push({ from: clippedNewSpanNewScale, to: clippedNewSpanNewScale }); } } else if (ordering === -1) { const clippedPreAddedNewSpanNewScale = alignSpanToContainingSpan( clippedNewSpanNewScale, axisValues, newData, oldData, oldIndices ); if (clippedPreAddedNewSpanNewScale == null) { addSpan(newData, collapseMode, newAxisIndices, newIndices, oldZeroData, range3, out); } else { out.removed.push({ from: clippedOldSpanOldScale, to: clippedOldSpanOldScale }); out.moved.push({ from: clippedOldSpanOldScale, to: clippedPreAddedNewSpanNewScale }); out.added.push({ from: clippedPreAddedNewSpanNewScale, to: clippedNewSpanNewScale }); } } else { out.removed.push({ from: clippedOldSpanOldScale, to: clippedOldSpanOldScale }); out.moved.push({ from: clippedOldSpanOldScale, to: clippedNewSpanNewScale }); out.added.push({ from: clippedNewSpanNewScale, to: clippedNewSpanNewScale }); } } function phaseAnimation(axisContext, newData, oldData, collapseMode) { const out = { removed: [], moved: [], added: [] }; const { axisValues, oldDataAxisIndices, newDataAxisIndices } = axisContext; const range3 = { xValue0Index: Math.max( oldDataAxisIndices.at(0)?.xValue0Index ?? -Infinity, newDataAxisIndices.at(0)?.xValue0Index ?? -Infinity ), xValue1Index: Math.min( oldDataAxisIndices.at(-1)?.xValue1Index ?? Infinity, newDataAxisIndices.at(-1)?.xValue1Index ?? Infinity ) }; for (let xValue0Index = 0; xValue0Index < axisValues.length - 1; xValue0Index += 1) { appendSpanPhases( newData, oldData, collapseMode, axisValues, xValue0Index, newDataAxisIndices, oldDataAxisIndices, range3, out ); } return out; } function replotXAnimation(newData, oldData) { const removed = []; const moved = []; const added = []; for (let i = 0; i < oldData.data.length; i += 1) { const oldSpan = oldData.data[i].span; const newSpan = newData.data[i].span; removed.push({ from: oldSpan, to: oldSpan }); moved.push({ from: oldSpan, to: newSpan }); added.push({ from: newSpan, to: newSpan }); } return { removed, moved, added }; } function resetSpan(data, spanDatum, collapseMode) { const { span } = spanDatum; switch (collapseMode) { case 0 /* Zero */: return zeroDataSpan(spanDatum, data.zeroData) ?? axisZeroSpan(span, data); case 1 /* Split */: return collapseSpanToMidpoint(span); } } function resetAnimation(newData, oldData, collapseMode) { const added = []; const removed = []; for (const oldSpanDatum of oldData.data) { const oldSpan = oldSpanDatum.span; const collapsedSpan = resetSpan(oldData, oldSpanDatum, collapseMode); removed.push({ from: oldSpan, to: collapsedSpan }); } for (const newSpanDatum of newData.data) { const newSpan = newSpanDatum.span; const collapsedSpan = resetSpan(newData, newSpanDatum, collapseMode); added.push({ from: collapsedSpan, to: newSpan }); } return { removed, moved: [], added }; } function pairUpSpans(newData, oldData, collapseMode) { if (!isValidScaling(newData) || !isValidScaling(oldData)) return; if (!validateCategorySorting(newData, oldData)) return; const axisContext = spanAxisContext(newData, oldData); if (axisContext == null) { return resetAnimation(newData, oldData, collapseMode); } else if (axisContext.axisValues.length === axisContext.oldDataAxisIndices.length + axisContext.newDataAxisIndices.length + 2) { return replotXAnimation(newData, oldData); } else { return phaseAnimation(axisContext, newData, oldData, collapseMode); } } // packages/ag-charts-community/src/chart/series/cartesian/lineUtil.ts function interpolatePoints(points, interpolation) { const pointsIter = points.map((point) => point.point); let spans = linearPoints(pointsIter); switch (interpolation.type) { case "linear": break; case "smooth": spans = smoothPoints(pointsIter, interpolation.tension); break; case "step": spans = stepPoints(pointsIter, interpolation.position); break; } return spans.map(function spanToLinePathSpan(span, i) { return { span, xValue0: points[i].xDatum, yValue0: points[i].yDatum, xValue1: points[i + 1].xDatum, yValue1: points[i + 1].yDatum }; }); } function pointsEq(a, b, delta3 = 1e-3) { return Math.abs(a.x - b.x) < delta3 && Math.abs(a.y - b.y) < delta3; } function plotLinePathStroke({ path }, spans) { let lastPoint; for (const { span } of spans) { const [start2, end3] = spanRange(span); const join = lastPoint != null && pointsEq(lastPoint, start2) ? 2 /* Skip */ : 0 /* MoveTo */; plotSpan(path, span, join, false); lastPoint = end3; } } function plotInterpolatedLinePathStroke(ratio2, path, spans) { let lastPoint; for (const span of spans) { const [start2, end3] = interpolatedSpanRange(span.from, span.to, ratio2); const join = lastPoint != null && pointsEq(lastPoint, start2) ? 2 /* Skip */ : 0 /* MoveTo */; plotInterpolatedSpans(path.path, span.from, span.to, ratio2, join, false); lastPoint = end3; } } function prepareLinePathStrokeAnimationFns(status, spans, visibleToggleMode, targetOpacity = 1) { const removePhaseFn = (ratio2, path) => plotInterpolatedLinePathStroke(ratio2, path, spans.removed); const updatePhaseFn = (ratio2, path) => plotInterpolatedLinePathStroke(ratio2, path, spans.moved); const addPhaseFn = (ratio2, path) => plotInterpolatedLinePathStroke(ratio2, path, spans.added); const pathProperties = prepareLinePathPropertyAnimation(status, visibleToggleMode, targetOpacity); return { status, path: { addPhaseFn, updatePhaseFn, removePhaseFn }, pathProperties }; } function prepareLinePathPropertyAnimation(status, visibleToggleMode, targetOpacity = 1) { const phase = visibleToggleMode === "none" ? "updated" : status; const result = { fromFn(path, datum) { const out = { phase: NODE_UPDATE_STATE_TO_PHASE_MAPPING[phase] }; const segments = path.previousDatum ?? datum; if (segments != null) { out.segments = segments; } if (status === "removed") { out.finish = { visible: false }; } else if (status === "added") { out.start = { visible: true }; } return out; }, toFn(_path, datum) { const out = { phase: NODE_UPDATE_STATE_TO_PHASE_MAPPING[phase] }; const segments = datum; if (segments != null) { out.segments = segments; } return out; } }; if (visibleToggleMode === "fade") { return { fromFn(path, datum) { const opacity = status === "added" ? 0 : targetOpacity; const segments = status === "removed" ? path.previousDatum ?? datum : datum; return { ...result.fromFn(path, datum), opacity, segments }; }, toFn(path, datum) { const opacity = status === "removed" ? 0 : targetOpacity; const segments = status === "removed" ? path.previousDatum ?? datum : datum; return { ...result.toFn(path, datum), opacity, segments }; } }; } return result; } function prepareLinePathAnimation(newData, oldData, diff2, targetOpacity = 1) { const isCategoryBased = newData.scales.x?.type === "category"; const wasCategoryBased = oldData.scales.x?.type === "category"; if (isCategoryBased !== wasCategoryBased || !isScaleValid(newData.scales.x) || !isScaleValid(oldData.scales.x)) { return; } if (newData.strokeData == null || oldData.strokeData == null) { return; } let status = "updated"; if (oldData.visible && !newData.visible) { status = "removed"; } else if (!oldData.visible && newData.visible) { status = "added"; } const strokeSpans = pairUpSpans( { scales: newData.scales, data: newData.strokeData.spans }, { scales: oldData.scales, data: oldData.strokeData.spans }, 1 /* Split */ ); if (strokeSpans == null) return; const stroke = prepareLinePathStrokeAnimationFns(status, strokeSpans, "fade", targetOpacity); const hasMotion = (diff2?.changed ?? true) || !areScalingEqual(newData.scales.x, oldData.scales.x) || !areScalingEqual(newData.scales.y, oldData.scales.y) || status !== "updated"; return { status, stroke, hasMotion }; } // packages/ag-charts-community/src/chart/series/cartesian/areaUtil.ts function plotAreaPathFill({ path }, { spans, phantomSpans }) { let phantomSpanIndex = 0; let sp = { x: Number.NaN, y: Number.NaN }; let pp = { x: Number.NaN, y: Number.NaN }; for (let i = 0; i < spans.length; i += 1) { const { span } = spans[i]; const { span: phantomSpan } = phantomSpans[i]; const { 0: sp0, 1: sp1 } = spanRange(span); const { 0: pp0, 1: pp1 } = spanRange(phantomSpan); if (pointsEq(sp, sp0) && pointsEq(pp, pp0)) { plotSpan(path, span, 2 /* Skip */, false); } else { for (let j = i - 1; j >= phantomSpanIndex; j -= 1) { plotSpan(path, phantomSpans[j].span, 1 /* LineTo */, true); } path.closePath(); plotSpan(path, span, 0 /* MoveTo */, false); phantomSpanIndex = i; } sp = sp1; pp = pp1; } for (let j = spans.length - 1; j >= phantomSpanIndex; j -= 1) { plotSpan(path, phantomSpans[j].span, 1 /* LineTo */, true); } path.closePath(); } function plotInterpolatedAreaSeriesFillSpans(ratio2, { path }, spans, fillPhantomSpans) { for (let i = 0; i < spans.length; i += 1) { const span = spans[i]; const reversedPhantomSpan = fillPhantomSpans[i]; plotInterpolatedSpans(path, span.from, span.to, ratio2, 0 /* MoveTo */, false); plotInterpolatedSpans(path, reversedPhantomSpan.from, reversedPhantomSpan.to, ratio2, 1 /* LineTo */, true); path.closePath(); } } function prepareAreaFillAnimationFns(status, spans, fillPhantomSpans, visibleToggleMode) { const removePhaseFn = (ratio2, path) => plotInterpolatedAreaSeriesFillSpans(ratio2, path, spans.removed, fillPhantomSpans.removed); const updatePhaseFn = (ratio2, path) => plotInterpolatedAreaSeriesFillSpans(ratio2, path, spans.moved, fillPhantomSpans.moved); const addPhaseFn = (ratio2, path) => plotInterpolatedAreaSeriesFillSpans(ratio2, path, spans.added, fillPhantomSpans.added); const pathProperties = prepareLinePathPropertyAnimation(status, visibleToggleMode); return { status, path: { addPhaseFn, updatePhaseFn, removePhaseFn }, pathProperties }; } function prepareAreaPathAnimation(newData, oldData) { const isCategoryBased = newData.scales.x?.type === "category"; const wasCategoryBased = oldData.scales.x?.type === "category"; if (isCategoryBased !== wasCategoryBased || !isScaleValid(newData.scales.x) || !isScaleValid(oldData.scales.x)) { return; } let status = "updated"; if (oldData.visible && !newData.visible) { status = "removed"; } else if (!oldData.visible && newData.visible) { status = "added"; } const fillSpans = pairUpSpans( { scales: newData.scales, data: newData.fillData.spans }, { scales: oldData.scales, data: oldData.fillData.spans }, 0 /* Zero */ ); if (fillSpans == null) return; const fillPhantomSpans = pairUpSpans( { scales: newData.scales, data: newData.fillData.phantomSpans }, { scales: oldData.scales, data: oldData.fillData.phantomSpans }, 0 /* Zero */ ); if (fillPhantomSpans == null) return; const strokeSpans = pairUpSpans( { scales: newData.scales, data: newData.strokeData.spans, zeroData: newData.fillData.phantomSpans }, { scales: oldData.scales, data: oldData.strokeData.spans, zeroData: oldData.fillData.phantomSpans }, 0 /* Zero */ ); if (strokeSpans == null) return; const fadeMode = "none"; const fill = prepareAreaFillAnimationFns(status, fillSpans, fillPhantomSpans, fadeMode); const stroke = prepareLinePathStrokeAnimationFns(status, strokeSpans, fadeMode); return { status, fill, stroke }; } // packages/ag-charts-community/src/chart/series/cartesian/markerUtil.ts function markerFadeInAnimation({ id }, animationManager, status, options, ...markerSelections) { const params = { ...options, phase: options?.phase ?? (status ? NODE_UPDATE_STATE_TO_PHASE_MAPPING[status] : "trailing") }; staticFromToMotion(id, "markers", animationManager, markerSelections, { opacity: 0 }, { opacity: 1 }, params); for (const s of markerSelections) { s.cleanup(); } } function markerScaleInAnimation({ id }, animationManager, ...markerSelections) { staticFromToMotion( id, "markers", animationManager, markerSelections, { scalingX: 0, scalingY: 0 }, { scalingX: 1, scalingY: 1 }, { phase: "initial" } ); for (const s of markerSelections) { s.cleanup(); } } function markerSwipeScaleInAnimation({ id, nodeDataDependencies }, animationManager, options, ...markerSelections) { const seriesWidth = nodeDataDependencies.seriesRectWidth; const fromFn = (_, datum) => { const x = datum.midPoint?.x ?? seriesWidth; let delay = clamp(0, inverseEaseOut(x / seriesWidth), 1); if (Number.isNaN(delay)) { delay = 0; } return { scalingX: 0, scalingY: 0, delay: options?.delay ?? delay, duration: options?.duration ?? QUICK_TRANSITION, phase: options?.phase ?? "initial", start: options?.start, finish: options?.finish }; }; const toFn = () => { return { scalingX: 1, scalingY: 1 }; }; fromToMotion(id, "markers", animationManager, markerSelections, { fromFn, toFn }); } function resetMarkerFn(_node) { return { opacity: 1, scalingX: 1, scalingY: 1 }; } function resetMarkerSelectionsDirect(selections) { for (const selection of selections) { const nodes = selection.nodes(); selection.batchedUpdate(function resetMarkerNodes() { for (const node of nodes) { const datum = node.datum; if (datum?.point == null) continue; const { x, y } = datum.point; if (!Number.isFinite(x) || !Number.isFinite(y)) continue; node.resetAnimationProperties(x, y, node.size, 1, 1, 1); } selection.cleanup(); }); } } function resetMarkerPositionFn(_node, datum) { return { x: datum.point?.x ?? Number.NaN, y: datum.point?.y ?? Number.NaN, scalingCenterX: datum.point?.x ?? Number.NaN, scalingCenterY: datum.point?.y ?? Number.NaN }; } function computeMarkerFocusBounds(series, { datumIndex }) { const nodeData = series.getNodeData(); if (nodeData === void 0) return void 0; const datum = nodeData[datumIndex]; const { point } = datum ?? {}; if (datum == null || point == null) return void 0; const style2 = series.getFormattedMarkerStyle(datum); const anchor = Marker.anchor(style2.shape); const size = point.focusSize ?? style2.size; const paddedSize = 4 + size; const paddedRadius = paddedSize / 2; const anchorX = (anchor.x - 0.5) * size; const anchorY = (anchor.y - 0.5) * size; const x = datum.point.x - paddedRadius - anchorX; const y = datum.point.y - paddedRadius - anchorY; return Transformable.toCanvas(series.contentGroup, new BBox(x, y, paddedSize, paddedSize)); } function markerEnabled(dataCount, scale2, marker, markerStyle = marker) { const enabled = markerStyle.enabled ?? marker.enabled; if (!enabled) return false; const minSpacing = 1; const step = scale2.step ?? findRangeExtent(scale2.range) / Math.max(1, dataCount); return step > minSpacing; } function getMarkerStyles(series, line, marker, inheritedStyle) { inheritedStyle ?? (inheritedStyle = { stroke: line.stroke, strokeOpacity: line.strokeOpacity, strokeWidth: line.strokeWidth }); return highlightStates.reduce( (styles, state) => { styles[state] = series.getMarkerStyle( marker, {}, void 0, { highlightState: state }, void 0, inheritedStyle ); return styles; }, {} ); } // packages/ag-charts-community/src/chart/series/cartesian/pathUtil.ts function pathSwipeInAnimation({ id, visible, nodeDataDependencies }, animationManager, ...paths) { const { seriesRectWidth: width2, seriesRectHeight: height2 } = nodeDataDependencies; staticFromToMotion( id, "path_properties", animationManager, paths, { clipX: 0 }, { clipX: width2 }, { phase: "initial", start: { clip: true, clipY: height2, visible }, finish: { clip: false, visible } } ); } function pathFadeInAnimation({ id }, subId, animationManager, phase = "add", ...selection) { staticFromToMotion(id, subId, animationManager, selection, { opacity: 0 }, { opacity: 1 }, { phase }); } function buildResetPathFn(opts) { return (_node) => ({ visible: opts.getVisible(), opacity: opts.getOpacity(), clipScalingX: 1, clip: false }); } function updateClipPath({ nodeDataDependencies }, path) { const toFinite = (value) => Number.isFinite(value) ? value : 0; path.clipX = toFinite(nodeDataDependencies.seriesRectWidth); path.clipY = toFinite(nodeDataDependencies.seriesRectHeight); } // packages/ag-charts-community/src/chart/series/cartesian/util.ts function isAxisReversed(axis) { return axis.isReversed() !== axis.range[1] < axis.range[0]; } function calculateSegments(segmentation, xAxis, yAxis, seriesRect, chartSize, applyOffset = true) { if (xAxis.scale.domain.length === 0 || yAxis.scale.domain.length === 0) { return; } const axis = segmentation.key === "x" /* X */ ? xAxis : yAxis; const { scale: scale2, direction } = axis; const isXDirection = direction === "x" /* X */; const bandwidth = scale2.bandwidth ?? 0; const offset = applyOffset ? ((scale2.step ?? 0) - bandwidth) / 2 : 0; const horizontalMargin = Math.max(seriesRect.x, chartSize.width - (seriesRect.x + seriesRect.width)); const verticalMargin = Math.max(seriesRect.y, chartSize.height - (seriesRect.y + seriesRect.height)); const getDefaultStart = () => { if (isAxisReversed(isXDirection ? xAxis : yAxis)) { return isXDirection ? seriesRect.width + horizontalMargin : seriesRect.height + verticalMargin; } return isXDirection ? -horizontalMargin : -verticalMargin; }; const getDefaultStop = () => { if (isAxisReversed(isXDirection ? xAxis : yAxis)) { return isXDirection ? -horizontalMargin : -verticalMargin; } return isXDirection ? seriesRect.width + horizontalMargin : seriesRect.height + verticalMargin; }; const getSegments = (segments) => { const result = []; let previousDefinedStopIndex = -1; for (let i = 0; i < segments.length; i++) { const segment = segments[i]; if (isEmptyObject(segment)) { continue; } const { start: start2, stop, ...styles } = segments[i]; const startFallback = segments[previousDefinedStopIndex]?.stop; const stopFallback = segments.slice(i + 1).find((s) => s.start != null)?.start; let startPosition = scale2.convert(start2 ?? startFallback) - offset; let stopPosition = scale2.convert(stop ?? stopFallback) + 2 * offset; const invalidStart = start2 != null && Number.isNaN(startPosition); const invalidStop = stop != null && Number.isNaN(stopPosition); if (invalidStart || invalidStop) { continue; } if (Number.isNaN(startPosition)) startPosition = getDefaultStart(); if (Number.isNaN(stopPosition)) stopPosition = getDefaultStop(); if (stop != null) { previousDefinedStopIndex = i; } result.push({ start: startPosition, stop: stopPosition, ...styles }); } return result; }; return getSegments(segmentation.segments).map(({ stop, start: start2, ...style2 }) => { const x0 = isXDirection ? start2 : -horizontalMargin; const y0 = isXDirection ? -verticalMargin : start2; const x1 = isXDirection ? stop + bandwidth : seriesRect.width + horizontalMargin; const y1 = isXDirection ? seriesRect.height + verticalMargin : stop + bandwidth; return { clipRect: { x0, y0, x1, y1 }, ...style2 }; }); } var TIME_AXIS_KEYS = /* @__PURE__ */ new Set(["time", "timestamp", "date", "datetime"]); function predictCartesianAxis(direction, datum, seriesOptions, { allowPrimitiveTypes = true } = {}) { if (direction !== "x" /* X */ && direction !== "y" /* Y */) return; if (!isObject(datum)) return; const key = getSeriesOptionsKey(direction, seriesOptions); if (key == null || !(key in datum)) return; const value = datum[key]; const position = getAxisPosition(direction, seriesOptions); const groupedCategory = predictGroupedCategoryAxisType(value); if (groupedCategory) { return { type: groupedCategory, position }; } const timeAxis = predictTimeAxisType(key, value); if (timeAxis) { return { type: timeAxis, position }; } if (!allowPrimitiveTypes) return; if (typeof value === "number") { return { type: "number" /* NUMBER */, position }; } return { type: "category" /* CATEGORY */, position }; } function predictCartesianNonPrimitiveAxis(direction, datum, seriesOptions) { return predictCartesianAxis(direction, datum, seriesOptions, { allowPrimitiveTypes: false }); } function predictCartesianFinancialAxis(direction, datum, seriesOptions) { if (direction !== "x" /* X */) return; if (!isObject(datum)) return; const key = getSeriesOptionsKey(direction, seriesOptions); if (key == null || !(key in datum)) return; const value = datum[key]; const position = getAxisPosition(direction, seriesOptions); const ordinalTimeAxis = predictOrdinalTimeAxisType(key, value); if (ordinalTimeAxis) { return { type: ordinalTimeAxis, position }; } if (isString(value)) { return { type: "category", position }; } } function predictGroupedCategoryAxisType(value) { if (isArray(value) && value.every((v) => isString(v) || v === null)) { return "grouped-category" /* GROUPED_CATEGORY */; } } function predictTimeAxisType(key, value) { if (isDate(value) || TIME_AXIS_KEYS.has(key) && isNumber(value)) { return "time" /* TIME */; } } function predictOrdinalTimeAxisType(key, value) { if (isDate(value) || TIME_AXIS_KEYS.has(key) && isNumber(value)) { return "ordinal-time" /* ORDINAL_TIME */; } } function getSeriesOptionsKey(direction, seriesOptions) { if (direction === "x" /* X */ && "xKey" in seriesOptions) { return seriesOptions.xKey; } else if (direction === "y" /* Y */ && "yKey" in seriesOptions) { return seriesOptions.yKey; } } function getAxisPosition(direction, seriesOptions) { if ("direction" in seriesOptions && seriesOptions.direction === "horizontal") { return direction === "x" /* X */ ? "left" /* LEFT */ : "bottom" /* BOTTOM */; } return direction === "x" /* X */ ? "bottom" /* BOTTOM */ : "left" /* LEFT */; } // packages/ag-charts-community/src/chart/series/cartesian/areaSeries.ts var CROSS_FILTER_AREA_FILL_OPACITY_FACTOR = 0.125; var CROSS_FILTER_AREA_STROKE_OPACITY_FACTOR = 0.25; var AreaSeries = class extends CartesianSeries { constructor(moduleCtx) { super({ moduleCtx, propertyKeys: DEFAULT_CARTESIAN_DIRECTION_KEYS, propertyNames: DEFAULT_CARTESIAN_DIRECTION_NAMES, categoryKey: "xValue", pathsPerSeries: ["fill", "stroke"], pathsZIndexSubOrderOffset: [0, 1e3], datumSelectionGarbageCollection: false, segmentedDataNodes: false, pickModes: [2 /* AXIS_ALIGNED */, 0 /* EXACT_SHAPE_MATCH */], animationResetFns: { path: buildResetPathFn({ getVisible: () => this.visible, getOpacity: () => this.getOpacity() }), label: resetLabelFn, datum: (node, datum) => ({ ...resetMarkerFn(node), ...resetMarkerPositionFn(node, datum) }) }, clipFocusBox: false }); this.properties = new AreaSeriesProperties(); this.connectsToYAxis = true; this.aggregationManager = new AggregationManager(); this.backgroundGroup = new Group({ name: `${this.id}-background`, zIndex: 0 /* BACKGROUND */ }); this._isStacked = void 0; this.fillSpans = []; this.phantomSpans = []; this.strokeSpans = []; } get pickModeAxis() { return "main"; } renderToOffscreenCanvas() { const hasMarkers = (this.contextNodeData?.nodeData?.length ?? 0) > 0; return super.renderToOffscreenCanvas() || hasMarkers && this.getDrawingMode(false) === "cutout" || this.contextNodeData != null && (this.contextNodeData.fillData.spans.length > RENDER_TO_OFFSCREEN_CANVAS_THRESHOLD || this.contextNodeData.strokeData.spans.length > RENDER_TO_OFFSCREEN_CANVAS_THRESHOLD); } attachSeries(seriesContentNode, seriesNode, annotationNode) { super.attachSeries(seriesContentNode, seriesNode, annotationNode); seriesContentNode.appendChild(this.backgroundGroup); } detachSeries(seriesContentNode, seriesNode, annotationNode) { super.detachSeries(seriesContentNode, seriesNode, annotationNode); this.backgroundGroup.remove(); } attachPaths([fill, stroke]) { this.backgroundGroup.appendChild(fill); this.contentGroup.appendChild(stroke); stroke.zIndex = -1; } detachPaths([fill, stroke]) { fill.remove(); stroke.remove(); } isStacked() { const stackCount = this.seriesGrouping?.stackCount ?? 1; return stackCount > 1; } isNormalized() { return this.properties.normalizedTo != null; } setSeriesIndex(index) { const isStacked = this.isStacked(); const isStackedChanged = isStacked === this._isStacked; this._isStacked = isStackedChanged; return super.setSeriesIndex(index, isStackedChanged); } setZIndex(zIndex) { super.setZIndex(zIndex); if (this.isStacked()) { this.backgroundGroup.zIndex = [0 /* BACKGROUND */, zIndex]; this.contentGroup.zIndex = [1 /* ANY_CONTENT */, zIndex, 0 /* FOREGROUND */]; } else { this.backgroundGroup.zIndex = [1 /* ANY_CONTENT */, zIndex, 0 /* FOREGROUND */, 0]; this.contentGroup.zIndex = [1 /* ANY_CONTENT */, zIndex, 0 /* FOREGROUND */, 1]; } } async processData(dataController) { if (this.data == null) return; const { data, visible, seriesGrouping: { groupIndex = this.id, stackCount = 1 } = {} } = this; const { xKey, yKey, yFilterKey, connectMissingData, normalizedTo } = this.properties; const animationEnabled = !this.ctx.animationManager.isSkipped(); const xScale = this.axes["x" /* X */]?.scale; const yScale = this.axes["y" /* Y */]?.scale; const { xScaleType, yScaleType } = this.getScaleInformation({ xScale, yScale }); const stacked = stackCount > 1 || normalizedTo != null; const idMap = { value: `area-stack-${groupIndex}-yValue`, marker: `area-stack-${groupIndex}-yValues-marker` }; const common = { invalidValue: null }; if ((isDefined(normalizedTo) || connectMissingData) && stackCount > 1) { common.invalidValue = 0; } if (!visible) { common.forceValue = 0; } const allowNullKey = this.properties.allowNullKeys ?? false; const props = [ keyProperty(xKey, xScaleType, { id: "xValue", allowNullKey }), valueProperty(yKey, yScaleType, { id: `yValueRaw`, ...common }), ...yFilterKey == null ? [] : [valueProperty(yFilterKey, yScaleType, { id: "yFilterRaw" })] ]; if (stacked) { props.push( ...groupAccumulativeValueProperty( yKey, "normal", { id: `yValueCumulative`, ...common, groupId: idMap.marker }, yScaleType ) ); } if (isDefined(normalizedTo)) { props.push( valueProperty(yKey, yScaleType, { id: `yValue`, ...common, groupId: idMap.value }), normaliseGroupTo(Object.values(idMap), normalizedTo) ); } if (animationEnabled) { props.push(animationValidation()); } const { dataModel, processedData } = await this.requestDataModel(dataController, data, { props, groupByKeys: stacked, groupByData: !stacked }); this.aggregateData(dataModel, processedData); this.animationState.transition("updateData"); } xCoordinateRange(xValue, pixelSize) { const { marker } = this.properties; const x = this.axes["x" /* X */].scale.convert(xValue); const r = marker.enabled ? 0.5 * marker.size * pixelSize : 0; return [x - r, x + r]; } yCoordinateRange(yValues, pixelSize) { const { marker } = this.properties; const y = this.axes["y" /* Y */].scale.convert(yValues[0]); const r = marker.enabled ? 0.5 * marker.size * pixelSize : 0; return [y - r, y + r]; } yValueKey() { return this.isNormalized() ? "yValue" : "yValueRaw"; } yCumulativeKey(processData) { return processData.type === "grouped" ? "yValueCumulative" : this.yValueKey(); } getSeriesDomain(direction) { const { dataModel, processedData, axes } = this; if (!dataModel || !processedData) return { domain: [] }; const yAxis = axes["y" /* Y */]; if (direction === "x" /* X */) { const keyDef = dataModel.resolveProcessedDataDefById(this, `xValue`); const keys = dataModel.getDomain(this, `xValue`, "key", processedData); if (keyDef?.def.type === "key" && keyDef.def.valueType === "category") { return keys; } return { domain: fixNumericExtent(extent(keys.domain)) }; } const yExtent = this.domainForClippedRange( "y" /* Y */, [this.yCumulativeKey(processedData)], "xValue" ); if (yAxis instanceof NumberAxis && !(yAxis instanceof LogAxis)) { const fixedYExtent = Number.isFinite(yExtent[1] - yExtent[0]) ? [Math.min(yExtent[0], 0), Math.max(yExtent[1], 0)] : []; return { domain: fixNumericExtent(fixedYExtent) }; } else { return { domain: fixNumericExtent(yExtent) }; } } getSeriesRange(_direction, visibleRange) { const [y0, y1] = this.domainForVisibleRange( "y" /* Y */, [this.yCumulativeKey(this.processedData)], "xValue", visibleRange ); return [Math.min(y0, 0), Math.max(y1, 0)]; } getZoomRangeFittingItems(xVisibleRange, yVisibleRange, minVisibleItems) { return this.zoomFittingVisibleItems( "xValue", [this.yCumulativeKey(this.processedData)], xVisibleRange, yVisibleRange, minVisibleItems ); } getVisibleItems(xVisibleRange, yVisibleRange, minVisibleItems) { return this.countVisibleItems( "xValue", [this.yCumulativeKey(this.processedData)], xVisibleRange, yVisibleRange, minVisibleItems ); } aggregateData(dataModel, processedData) { this.aggregationManager.markStale(processedData.input.count); if (processedDataIsAnimatable(processedData)) return; const xAxis = this.axes["x" /* X */]; if (xAxis == null) return; const targetRange = this.estimateTargetRange(); this.aggregationManager.aggregate({ computePartial: (existingFilters) => aggregateAreaDataFromDataModelPartial( xAxis.scale.type, dataModel, processedData, this.yCumulativeKey(processedData), this, targetRange, existingFilters ), computeFull: (existingFilters) => aggregateAreaDataFromDataModel( xAxis.scale.type, dataModel, processedData, this.yCumulativeKey(processedData), this, existingFilters ), targetRange }); const filters = this.aggregationManager.filters; if (filters && filters.length > 0) { debugMetrics_exports.record( `${this.type}:aggregation`, filters.map((f) => f.maxRange) ); } } estimateTargetRange() { const xAxis = this.axes["x" /* X */]; if (xAxis?.scale?.range) { const [r0, r1] = xAxis.scale.range; return Math.abs(r1 - r0); } return this.ctx.scene?.canvas?.width ?? 800; } stackAggregatedData(aggregation) { const { indices, metaIndices } = aggregation; const { visible, axes, dataModel, processedData, seriesBelowStackContext } = this; const xAxis = axes["x" /* X */]; const yAxis = axes["y" /* Y */]; if (!visible) { this.phantomSpans = []; this.fillSpans = []; this.strokeSpans = []; return seriesBelowStackContext; } if (xAxis == null || yAxis == null || dataModel == null || processedData == null) return; const { scale: xScale } = xAxis; const { scale: yScale } = yAxis; const xOffset = (xScale.bandwidth ?? 0) / 2; const connectMissingData = !this.isStacked() && this.properties.connectMissingData; const invalidData = processedData.invalidData?.get(this.id); const xValues = dataModel.resolveKeysById(this, "xValue", processedData); const yValues = dataModel.resolveColumnById(this, this.yCumulativeKey(processedData), processedData); let [m0, m1] = visibleRangeIndices(1, metaIndices.length - 1, xAxis.range, (metaIndex) => { const startIndex = metaIndices[metaIndex]; const endIndex = metaIndices[metaIndex + 1]; const startDatumIndex = indices[startIndex]; const endDatumIndex = indices[endIndex]; const xValue0 = xValues[startDatumIndex]; const xValue1 = xValues[endDatumIndex]; const { 0: x0 } = this.xCoordinateRange(xValue0, 0); const { 1: x1 } = this.xCoordinateRange(xValue1, 0); return [x0, x1]; }); m0 = Math.max(m0 - 1, 0); m1 = Math.min(m1 + 1, metaIndices.length - 1); let phantomSpans = []; if (seriesBelowStackContext?.fillSpans) { phantomSpans = seriesBelowStackContext?.fillSpans; } else { for (let metaIndex = m0; metaIndex < m1; metaIndex += 1) { const startIndex = metaIndices[metaIndex]; const endIndex = metaIndices[metaIndex + 1]; const startDatumIndex = indices[startIndex]; const endDatumIndex = indices[endIndex]; const xValue0 = xValues[startDatumIndex]; const xValue1 = xValues[endDatumIndex]; const span = { type: "linear", moveTo: false, x0: xScale.convert(xValue0) + xOffset, y0: yScale.convert(0), x1: xScale.convert(xValue1) + xOffset, y1: yScale.convert(0) }; phantomSpans.push({ span, xValue0, xValue1, yValue0: 0, yValue1: 0 }); } } this.phantomSpans = phantomSpans; const fillSpans = []; const strokeSpans = []; let phantomIndex = 0; for (let metaIndex = m0; metaIndex < m1; metaIndex += 1) { const startIndex = metaIndices[metaIndex]; const endIndex = metaIndices[metaIndex + 1]; const startDatumIndex = indices[startIndex]; const endDatumIndex = indices[endIndex]; const spanInvalid = !connectMissingData && this.hasInvalidDatumsInRange(invalidData, yValues, startDatumIndex, endDatumIndex); const phantomSpanDatum = phantomSpans[phantomIndex++]; if (spanInvalid) { fillSpans.push(phantomSpanDatum); strokeSpans.push(phantomSpanDatum); continue; } const bucketPoints = []; for (let i = startIndex; i <= endIndex; i++) { const datumIndex = indices[i]; if (invalidData?.[datumIndex]) continue; const yValue = yValues[datumIndex]; if (!Number.isFinite(yValue)) continue; const xDatum = xValues[datumIndex]; bucketPoints.push({ point: { x: xScale.convert(xDatum) + xOffset, y: yScale.convert(yValue) }, xDatum, yDatum: yValue }); } if (bucketPoints.length < 2) { fillSpans.push(phantomSpanDatum); strokeSpans.push(phantomSpanDatum); continue; } const startPoint = bucketPoints[0]; const endPoint = bucketPoints.at(-1); const midPoints = bucketPoints.slice(1, -1).map((p) => p.point); const span = { type: "multi-line", moveTo: false, x0: startPoint.point.x, y0: startPoint.point.y, x1: endPoint.point.x, y1: endPoint.point.y, midPoints }; const spanDatum = { span, xValue0: startPoint.xDatum, xValue1: endPoint.xDatum, yValue0: startPoint.yDatum, yValue1: endPoint.yDatum }; fillSpans.push(spanDatum); strokeSpans.push(spanDatum); } this.fillSpans = fillSpans; this.strokeSpans = strokeSpans; return { stack: [], fillSpans, strokeSpans }; } hasInvalidDatumsInRange(invalidData, yValues, startIndex, endIndex) { const rangeStart = Math.min(startIndex, endIndex); const rangeEnd = Math.max(startIndex, endIndex); for (let datumIndex = rangeStart; datumIndex <= rangeEnd; datumIndex++) { if (invalidData?.[datumIndex]) { return true; } const yValue = yValues[datumIndex]; if (!Number.isFinite(yValue)) { return true; } } return false; } stackYValueData() { const { visible, axes, dataModel, processedData, seriesBelowStackContext, properties } = this; const xAxis = axes["x" /* X */]; const yAxis = axes["y" /* Y */]; if (xAxis == null || yAxis == null || dataModel == null || processedData == null) return; const { interpolation } = properties; const { scale: xScale } = xAxis; const { scale: yScale } = yAxis; const xOffset = (xScale.bandwidth ?? 0) / 2; let xValues = dataModel.resolveKeysById(this, "xValue", processedData); let yValues = dataModel.resolveColumnById(this, this.yValueKey(), processedData); const connectMissingData = !this.isStacked() && this.properties.connectMissingData; const invalidKeys = processedData.invalidKeys?.get(this.id); const invalidData = connectMissingData ? processedData.invalidData?.get(this.id) : void 0; const indexFilter = invalidData ?? invalidKeys; if (indexFilter != null) { xValues = xValues.filter((_, datumIndex) => indexFilter[datumIndex] === false); yValues = yValues.filter((_, datumIndex) => indexFilter[datumIndex] === false); } let [startIndex, endIndex] = visibleRangeIndices( 1, xValues.length, xAxis.range, (datumIndex) => this.xCoordinateRange(xValues[datumIndex], 0) ); startIndex = Math.max(startIndex - 2, 0); endIndex = Math.min(endIndex + 2, xValues.length); let phantomSpans; if (seriesBelowStackContext?.fillSpans) { phantomSpans = seriesBelowStackContext?.fillSpans; } else { const phantomSpanPoints = []; for (let datumIndex = startIndex; datumIndex < endIndex; datumIndex += 1) { const xDatum = xValues[datumIndex]; phantomSpanPoints.push({ point: { x: xScale.convert(xDatum) + xOffset, y: yScale.convert(0) }, xDatum, yDatum: 0 }); } phantomSpans = interpolatePoints(phantomSpanPoints, { type: "linear" }); } this.phantomSpans = phantomSpans; if (!visible) { this.fillSpans = phantomSpans; this.strokeSpans = []; return seriesBelowStackContext; } let bottomStack = seriesBelowStackContext?.stack; if (bottomStack == null) { bottomStack = []; for (let datumIndex = startIndex; datumIndex < endIndex - 1; datumIndex += 1) { bottomStack.push({ leading: 0, trailing: 0, dataValid: true, breakBefore: false }); } } const topStack = bottomStack.slice(); let trackingValidData = false; for (let stackIndex = 0; stackIndex < topStack.length; stackIndex += 1) { const leadingIndex = startIndex + stackIndex; const trailingIndex = startIndex + stackIndex + 1; let { leading, trailing, breakBefore } = bottomStack[stackIndex]; const leadingValue = yValues[leadingIndex]; const trailingValue = yValues[trailingIndex]; const missingLeading = !Number.isFinite(leadingValue); const missingTrailing = !Number.isFinite(trailingValue); const dataValid = !missingLeading && !missingTrailing; if (dataValid) { leading += leadingValue; trailing += trailingValue; } if (stackIndex !== 0 && dataValid !== trackingValidData) { breakBefore = true; } trackingValidData = dataValid; topStack[stackIndex] = { leading, trailing, dataValid, breakBefore }; } const fillSpans = []; const strokeSpans = []; const topSpanPoints = []; for (let stackIndex = 0; stackIndex < topStack.length; stackIndex += 1) { const { leading, dataValid, breakBefore } = topStack[stackIndex]; const leadingIndex = startIndex + stackIndex; if (breakBefore) { if (topSpanPoints.length !== 0) { const previousStack = topStack[stackIndex - 1]; const previousPoint = { point: { x: xScale.convert(xValues[leadingIndex]) + xOffset, y: yScale.convert(previousStack.trailing) }, xDatum: xValues[leadingIndex], yDatum: previousStack.trailing }; topSpanPoints.push(previousPoint); const spans = interpolatePoints(topSpanPoints, interpolation); fillSpans.push(...spans); strokeSpans.push(...spans); } topSpanPoints.length = 0; } if (dataValid) { const leadingPoint = { point: { x: xScale.convert(xValues[leadingIndex]) + xOffset, y: yScale.convert(leading) }, xDatum: xValues[leadingIndex], yDatum: leading }; topSpanPoints.push(leadingPoint); } else { fillSpans.push(phantomSpans[stackIndex]); } } if (topSpanPoints.length !== 0) { const previousStack = topStack.at(-1); const trailingIndex = startIndex + topStack.length; const trailingPoint = { point: { x: xScale.convert(xValues[trailingIndex]) + xOffset, y: yScale.convert(previousStack.trailing) }, xDatum: xValues[trailingIndex], yDatum: previousStack.trailing }; topSpanPoints.push(trailingPoint); const spans = interpolatePoints(topSpanPoints, interpolation); fillSpans.push(...spans); strokeSpans.push(...spans); topSpanPoints.length = 0; } this.fillSpans = fillSpans; this.strokeSpans = strokeSpans; return { stack: topStack, fillSpans, strokeSpans }; } createStackContext() { const xAxis = this.axes["x" /* X */]; if (xAxis == null) return; const { scale: xScale } = xAxis; const [r0, r1] = xScale.range; const range3 = Math.abs(r1 - r0); this.aggregationManager.ensureLevelForRange(range3); const dataAggregationFilter = this.aggregationManager.getFilterForRange(range3); if (dataAggregationFilter) { return this.stackAggregatedData(dataAggregationFilter); } else { return this.stackYValueData(); } } /** * Creates the context object with cached lookups for createNodeData(). * All expensive operations (data resolution, scale lookups) are performed once here. */ createNodeDatumContext(xAxis, yAxis) { const { dataModel, processedData } = this; if (!dataModel || !processedData) return void 0; const { xKey, xName, yFilterKey, yKey, yName, legendItemName, marker, label, fill: seriesFill, stroke: seriesStroke, normalizedTo } = this.properties; const xScale = xAxis.scale; const yScale = yAxis.scale; const { isContinuousY } = this.getScaleInformation({ xScale, yScale }); const stacked = processedData.type === "grouped"; const [r0, r1] = xScale.range; const range3 = Math.abs(r1 - r0); this.aggregationManager.ensureLevelForRange(range3); const dataAggregationFilter = this.aggregationManager.getFilterForRange(range3); const existingNodeData = this.contextNodeData?.nodeData; const canIncrementallyUpdate = existingNodeData != null && this.canIncrementallyUpdateNodes(dataAggregationFilter != null); return { // Axes (from template method parameters) xAxis, yAxis, // Data arrays (resolved once) rawData: processedData.dataSources.get(this.id)?.data ?? [], xValues: dataModel.resolveKeysById(this, "xValue", processedData), yRawValues: dataModel.resolveColumnById(this, "yValueRaw", processedData), yCumulativeValues: stacked ? dataModel.resolveColumnById(this, "yValueCumulative", processedData) : dataModel.resolveColumnById(this, "yValueRaw", processedData), yFilterValues: yFilterKey == null ? void 0 : dataModel.resolveColumnById(this, "yFilterRaw", processedData), invalidData: processedData.invalidData?.get(this.id), // Scales (cached) xScale, yScale, xOffset: (xScale.bandwidth ?? 0) / 2, yOffset: 0, // Aggregation indices: dataAggregationFilter?.indices, // Pre-computed flags isContinuousY, labelsEnabled: label.enabled, normalizedTo, canIncrementallyUpdate, animationEnabled: !this.ctx.animationManager.isSkipped(), // Property caches xKey, yKey, xName, yName, legendItemName, markerSize: marker.size, markerFill: marker.fill ?? seriesFill, markerStroke: marker.stroke ?? seriesStroke, markerStrokeWidth: marker.strokeWidth ?? this.properties.strokeWidth, yDomain: this.getSeriesDomain("y" /* Y */).domain, // Mutable state (nodes instead of markerData to match base interface) nodes: canIncrementallyUpdate ? existingNodeData : [], labelData: [], nodeIndex: 0, crossFiltering: false }; } /** * Computes the marker coordinate for a datum. * Uses cached context values to avoid repeated lookups. */ computeMarkerCoordinate(ctx, scratch) { let currY; if (isDefined(ctx.normalizedTo) ? ctx.isContinuousY && isContinuous(scratch.yDatum) : !Number.isNaN(scratch.yDatum)) { currY = scratch.yCumulative; } scratch.x = ctx.xScale.convert(scratch.xDatum) + ctx.xOffset; scratch.y = ctx.yScale.convert(currY); if (!Number.isFinite(scratch.x)) { scratch.validPoint = false; } } /** * Processes a single datum and updates the context's marker/label data. * Uses scratch object to avoid allocations in tight loops. */ handleDatum(ctx, scratch, datumIndex) { scratch.xDatum = ctx.xValues[datumIndex]; if (scratch.xDatum === void 0 && !this.properties.allowNullKeys) return; scratch.datum = ctx.rawData[datumIndex]; scratch.yDatum = ctx.yRawValues[datumIndex]; scratch.yCumulative = +ctx.yCumulativeValues[datumIndex]; scratch.validPoint = Number.isFinite(scratch.yDatum) && ctx.invalidData?.[datumIndex] !== true; this.computeMarkerCoordinate(ctx, scratch); scratch.selected = ctx.yFilterValues == null ? void 0 : ctx.yFilterValues[datumIndex] === scratch.yDatum; if (scratch.selected === false) { ctx.crossFiltering = true; } if (scratch.validPoint) { const canReuseNode = ctx.canIncrementallyUpdate && ctx.nodeIndex < ctx.nodes.length; if (canReuseNode) { const existingNode = ctx.nodes[ctx.nodeIndex]; existingNode.datum = scratch.datum; existingNode.datumIndex = datumIndex; existingNode.midPoint = { x: scratch.x, y: scratch.y }; existingNode.cumulativeValue = scratch.yCumulative; existingNode.yValue = scratch.yDatum; existingNode.xValue = scratch.xDatum; existingNode.point = { x: scratch.x, y: scratch.y, size: ctx.markerSize }; existingNode.selected = scratch.selected; } else { ctx.nodes.push({ series: this, datum: scratch.datum, datumIndex, midPoint: { x: scratch.x, y: scratch.y }, cumulativeValue: scratch.yCumulative, yValue: scratch.yDatum, xValue: scratch.xDatum, yKey: ctx.yKey, xKey: ctx.xKey, point: { x: scratch.x, y: scratch.y, size: ctx.markerSize }, fill: ctx.markerFill, stroke: ctx.markerStroke, strokeWidth: ctx.markerStrokeWidth, selected: scratch.selected }); } ctx.nodeIndex++; } if (ctx.labelsEnabled && scratch.validPoint) { const labelText = this.getLabelText( scratch.yDatum, scratch.datum, ctx.yKey, "y", ctx.yDomain, this.properties.label, { value: scratch.yDatum, datum: scratch.datum, xKey: ctx.xKey, yKey: ctx.yKey, xName: ctx.xName, yName: ctx.yName, legendItemName: ctx.legendItemName } ); ctx.labelData.push({ series: this, datum: scratch.datum, datumIndex, x: scratch.x, y: scratch.y, labelText }); } } // ============================================================================ // Template Method Hooks // ============================================================================ /** * Populates the node data array by iterating over visible data. */ populateNodeData(ctx) { const scratch = { datum: void 0, xDatum: void 0, yDatum: void 0, yCumulative: 0, selected: void 0, x: 0, y: 0, validPoint: false }; let [startIndex, endIndex] = this.visibleRangeIndices("xValue", ctx.xAxis.range, ctx.indices); startIndex = Math.max(startIndex - 2, 0); endIndex = Math.min(endIndex + 2, ctx.indices?.length ?? ctx.xValues.length); if (this.processedData.input.count < 1e3) { startIndex = 0; endIndex = this.processedData.input.count; } for (let i = startIndex; i < endIndex; i += 1) { const datumIndex = ctx.indices?.[i] ?? i; this.handleDatum(ctx, scratch, datumIndex); } } /** * Initializes the result context object with default values. * Called before populate phase to allow early return for invisible series. * * Note: We use the actual fillSpans/strokeSpans/phantomSpans because createStackContext() * runs BEFORE createNodeData() and populates these instance fields. They are valid * even in the early return case when !visible. */ initializeResult(ctx) { const { visibleSameStackCount } = this.ctx.seriesStateManager.getVisiblePeerGroupIndex(this); return { itemId: ctx.yKey, // Use actual spans from createStackContext() - valid even for early return fillData: { spans: this.fillSpans, phantomSpans: this.phantomSpans }, strokeData: { spans: this.strokeSpans }, labelData: ctx.labelData, nodeData: ctx.nodes, scales: this.calculateScaling(), visible: this.visible, stackVisible: visibleSameStackCount > 0, crossFiltering: ctx.crossFiltering, styles: getMarkerStyles(this, this.properties, this.properties.marker), segments: void 0 }; } /** * Assembles the final result with computed fields. * Note: fillData/strokeData are already set in initializeResult() from instance fields. */ assembleResult(ctx, result) { result.segments = calculateSegments( this.properties.segmentation, ctx.xAxis, ctx.yAxis, this.chart.seriesRect, this.ctx.scene, false ); return result; } isPathOrSelectionDirty() { return this.properties.marker.isDirty(); } updatePathNodes(opts) { const { paths: [fillPaths, strokePaths], visible, animationEnabled } = opts; const crossFiltering = this.contextNodeData?.crossFiltering === true; const segments = this.contextNodeData?.segments; const merged = mergeDefaults(this.getHighlightStyle(), this.getStyle()); const { strokeWidth, stroke, strokeOpacity, lineDash, lineDashOffset, fill, fillOpacity, opacity } = merged; strokePaths.setProperties({ segments, fill: void 0, lineCap: "round", lineJoin: "round", pointerEvents: 1 /* None */, stroke, strokeWidth, strokeOpacity: strokeOpacity * (crossFiltering ? CROSS_FILTER_AREA_STROKE_OPACITY_FACTOR : 1), lineDash, lineDashOffset, opacity, visible: visible || animationEnabled }); strokePaths.datum = segments; fillPaths.setStyleProperties( { fill, stroke: void 0, fillOpacity: fillOpacity * (crossFiltering ? CROSS_FILTER_AREA_FILL_OPACITY_FACTOR : 1) }, this.getShapeFillBBox() ); fillPaths.setProperties({ segments, lineJoin: "round", pointerEvents: 1 /* None */, fillShadow: this.properties.shadow, opacity, visible: visible || animationEnabled }); fillPaths.datum = segments; updateClipPath(this, strokePaths); updateClipPath(this, fillPaths); } updatePaths(opts) { this.updateAreaPaths(opts.paths, opts.contextData); } updateAreaPaths(paths, contextData) { for (const path of paths) { path.visible = contextData.visible; } if (contextData.visible) { this.updateFillPath(paths, contextData); this.updateStrokePath(paths, contextData); } else { for (const path of paths) { path.path.clear(); path.markDirty("AreaSeries"); } } } updateFillPath(paths, contextData) { const [fill] = paths; fill.path.clear(); plotAreaPathFill(fill, contextData.fillData); fill.markDirty("AreaSeries"); } updateStrokePath(paths, contextData) { const { spans } = contextData.strokeData; const [, stroke] = paths; stroke.path.clear(); plotLinePathStroke(stroke, spans); stroke.markDirty("AreaSeries"); } updateDatumSelection(opts) { const { nodeData, datumSelection } = opts; const { contextNodeData, processedData, axes, properties } = this; const { marker, styler } = properties; const markerStyle = styler ? this.getStyle().marker : void 0; const markersEnabled = contextNodeData?.crossFiltering === true || markerEnabled(processedData.input.count, axes["x" /* X */].scale, marker, markerStyle); if (marker.isDirty()) { datumSelection.clear(); datumSelection.cleanup(); } const data = markersEnabled ? nodeData : []; if (!processedDataIsAnimatable(this.processedData)) { return datumSelection.update(data); } return datumSelection.update(data, void 0, (datum) => createDatumId(datum.xValue)); } updateDatumStyles(opts) { const { datumSelection, isHighlight } = opts; const { marker } = this.properties; const highlightedDatum = this.ctx.highlightManager.getActiveHighlight(); datumSelection.each((node, datum) => { if (!datumSelection.isGarbage(node)) { const highlightState = this.getHighlightState(highlightedDatum, opts.isHighlight, datum.datumIndex); const stylerStyle = this.getStyle(highlightState); const { stroke, strokeWidth, strokeOpacity } = stylerStyle; const params = this.makeItemStylerParams( this.dataModel, this.processedData, datum.datumIndex, stylerStyle.marker ); datum.style = this.getMarkerStyle( marker, datum, params, { isHighlight, highlightState }, stylerStyle.marker, { stroke, strokeWidth, strokeOpacity } ); } }); } updateDatumNodes(opts) { const { contextNodeData } = this; if (!contextNodeData) { return; } const { datumSelection, isHighlight } = opts; const fillBBox = this.getShapeFillBBox(); const highlightedDatum = this.ctx.highlightManager.getActiveHighlight(); const drawingMode = this.getDrawingMode(isHighlight, opts.drawingMode); datumSelection.each((node, datum) => { const state = this.getHighlightState(highlightedDatum, isHighlight, datum.datumIndex); const style2 = datum.style ?? contextNodeData.styles[state]; this.applyMarkerStyle(style2, node, datum.point, fillBBox, { selected: datum.selected }); node.drawingMode = this.resolveMarkerDrawingModeForState(drawingMode, style2); }); if (!isHighlight) { this.properties.marker.markClean(); } } updateLabelSelection(opts) { return opts.labelSelection.update(this.isLabelEnabled() ? opts.labelData : []); } updateLabelNodes(opts) { const { isHighlight = false } = opts; const activeHighlight = this.ctx.highlightManager?.getActiveHighlight(); const params = this.makeLabelFormatterParams(); opts.labelSelection.each((text, datum) => { const style2 = getLabelStyles(this, datum, params, this.properties.label, isHighlight, activeHighlight); const { enabled, fontStyle, fontWeight: fontWeight2, fontSize, fontFamily, color: color2 } = style2; if (enabled && datum?.labelText) { text.fontStyle = fontStyle; text.fontWeight = fontWeight2; text.fontSize = fontSize; text.fontFamily = fontFamily; text.textAlign = "center"; text.textBaseline = "bottom"; text.text = datum.labelText; text.x = datum.x; text.y = datum.y - 10; text.fill = color2; text.visible = true; text.fillOpacity = this.getHighlightStyle(isHighlight, datum.datumIndex).opacity ?? 1; text.setBoxing(style2); } else { text.visible = false; } }); } makeStylerParams(highlightStateEnum) { const { id: seriesId } = this; const { marker, fill, fillOpacity, lineDash, lineDashOffset, stroke, strokeOpacity, strokeWidth, xKey, yKey } = this.properties; const highlightState = toHighlightString(highlightStateEnum ?? 0 /* None */); return { marker: { fill: marker.fill, fillOpacity: marker.fillOpacity, size: marker.size, shape: marker.shape, stroke: marker.stroke, strokeOpacity: marker.strokeOpacity, strokeWidth: marker.strokeWidth, lineDash: marker.lineDash, lineDashOffset: marker.lineDashOffset }, highlightState, fill, fillOpacity, lineDash, lineDashOffset, seriesId, stroke, strokeOpacity, strokeWidth, xKey, yKey }; } makeItemStylerParams(dataModel, processedData, datumIndex, style2) { const { xKey, yKey } = this.properties; const xValue = dataModel.resolveKeysById(this, `xValue`, processedData)[datumIndex]; const yValue = dataModel.resolveColumnById(this, `yValueRaw`, processedData)[datumIndex]; const xDomain = dataModel.getDomain(this, `xValue`, "key", processedData).domain; const yDomain = dataModel.getDomain(this, this.yCumulativeKey(processedData), "value", processedData).domain; const fill = this.filterItemStylerFillParams(style2.fill) ?? style2.fill; return { ...datumStylerProperties(xValue, yValue, xKey, yKey, xDomain, yDomain), xValue, yValue, ...style2, fill }; } makeLabelFormatterParams() { const { xKey, xName, yKey, yName, legendItemName } = this.properties; return { xKey, xName, yKey, yName, legendItemName }; } getTooltipContent(datumIndex) { const { id: seriesId, dataModel, processedData, axes, properties } = this; const { xKey, xName, yKey, yName, tooltip, legendItemName } = properties; const allowNullKeys = properties.allowNullKeys ?? false; const xAxis = axes["x" /* X */]; const yAxis = axes["y" /* Y */]; if (!dataModel || !processedData || !xAxis || !yAxis) return; const datum = processedData.dataSources.get(this.id)?.data?.[datumIndex]; const xValue = dataModel.resolveKeysById(this, `xValue`, processedData)[datumIndex]; const yValue = dataModel.resolveColumnById(this, `yValueRaw`, processedData)[datumIndex]; if (xValue === void 0 && !allowNullKeys) return; const stylerStyle = this.getStyle(); const params = this.makeItemStylerParams(dataModel, processedData, datumIndex, stylerStyle.marker); const format = this.getMarkerStyle( this.properties.marker, { datumIndex, datum }, params, { isHighlight: false }, stylerStyle.marker ); return this.formatTooltipWithContext( tooltip, { heading: this.getAxisValueText(xAxis, "tooltip", xValue, datum, xKey, legendItemName, allowNullKeys), symbol: this.legendItemSymbol(), data: [ { label: yName, fallbackLabel: yKey, value: this.getAxisValueText(yAxis, "tooltip", yValue, datum, yKey, legendItemName), missing: isTooltipValueMissing(yValue) } ] }, { seriesId, datum, title: yName, xKey, xName, yKey, yName, ...format, ...this.getModuleTooltipParams() } ); } legendItemSymbol() { const { fill, stroke, fillOpacity, strokeOpacity, strokeWidth, lineDash, marker } = this.getStyle(); const useAreaFill = !marker.enabled || marker.fill == null; const legendMarkerFill = useAreaFill ? fill : marker.fill; const markerStyle = this.getMarkerStyle( this.properties.marker, {}, void 0, { isHighlight: false, checkForHighlight: false }, { size: marker.size, shape: marker.shape, fill: legendMarkerFill, fillOpacity: useAreaFill ? fillOpacity : marker.fillOpacity, stroke: marker.stroke } ); return { marker: { ...markerStyle, enabled: marker.enabled || strokeWidth <= 0 }, line: { enabled: true, stroke, strokeOpacity, strokeWidth, lineDash } }; } getLegendData(legendType) { if (legendType !== "category") { return []; } const { id: seriesId, ctx: { legendManager }, visible } = this; const { yKey: itemId, yName, legendItemName, showInLegend } = this.properties; return [ { legendType, id: seriesId, itemId, legendItemName, seriesId, enabled: visible && legendManager.getItemEnabled({ seriesId, itemId }), label: { text: legendItemName ?? yName ?? itemId }, symbol: this.legendItemSymbol(), hideInLegend: !showInLegend } ]; } resetDatumAnimation(data) { resetMarkerSelectionsDirect([data.datumSelection]); } animateEmptyUpdateReady(animationData) { const { datumSelection, labelSelection, contextData, paths } = animationData; const { animationManager } = this.ctx; this.updateAreaPaths(paths, contextData); pathSwipeInAnimation(this, animationManager, ...paths); resetMotion([datumSelection], resetMarkerPositionFn); markerSwipeScaleInAnimation( this, animationManager, { ...this.getAnimationDrawingModes(), phase: "initial" }, datumSelection ); seriesLabelFadeInAnimation(this, "labels", animationManager, labelSelection); } animateReadyResize(animationData) { const { contextData, paths } = animationData; this.updateAreaPaths(paths, contextData); super.animateReadyResize(animationData); } animateWaitingUpdateReady(animationData) { const { animationManager } = this.ctx; const { datumSelection, labelSelection, contextData, paths, previousContextData } = animationData; const [fill, stroke] = paths; if (contextData.visible === false && previousContextData?.visible === false) return; if (fill == null && stroke == null) return; this.resetDatumAnimation(animationData); this.resetLabelAnimation(animationData); const update = () => { this.resetPathAnimation(animationData); this.updateAreaPaths(paths, contextData); }; const skip = () => { animationManager.skipCurrentBatch(); update(); }; if (contextData == null || previousContextData == null) { update(); markerFadeInAnimation(this, animationManager, "added", this.getAnimationDrawingModes(), datumSelection); pathFadeInAnimation(this, "fill_path_properties", animationManager, "add", fill); pathFadeInAnimation(this, "stroke_path_properties", animationManager, "add", stroke); seriesLabelFadeInAnimation(this, "labels", animationManager, labelSelection); return; } if (contextData.crossFiltering !== previousContextData.crossFiltering) { skip(); return; } const fns = prepareAreaPathAnimation(contextData, previousContextData); if (fns === void 0) { skip(); return; } else if (fns.status === "no-op") { return; } markerFadeInAnimation(this, animationManager, void 0, this.getAnimationDrawingModes(), datumSelection); fromToMotion(this.id, "fill_path_properties", animationManager, [fill], fns.fill.pathProperties); pathMotion(this.id, "fill_path_update", animationManager, [fill], fns.fill.path); fromToMotion(this.id, "stroke_path_properties", animationManager, [stroke], fns.stroke.pathProperties); pathMotion(this.id, "stroke_path_update", animationManager, [stroke], fns.stroke.path); seriesLabelFadeInAnimation(this, "labels", animationManager, labelSelection); this.ctx.animationManager.animate({ id: this.id, groupId: "reset_after_animation", phase: "trailing", from: {}, to: {}, onComplete: () => this.updateAreaPaths(paths, contextData) }); } isLabelEnabled() { return this.properties.label.enabled; } nodeFactory() { return new Marker(); } getStyle(highlightState) { const { styler, marker, fill, fillOpacity, lineDash, lineDashOffset, stroke, strokeOpacity, strokeWidth } = this.properties; const { size, shape, fill: markerFill = "transparent", fillOpacity: markerFillOpacity } = marker; let stylerResult = {}; if (styler) { const stylerParams = this.makeStylerParams(highlightState); const cbResult = this.cachedCallWithContext(styler, stylerParams) ?? {}; const resolved = this.ctx.optionsGraphService.resolvePartial( ["series", `${this.declarationOrder}`], cbResult, { pick: false } ); stylerResult = resolved ?? {}; } stylerResult.marker ?? (stylerResult.marker = {}); return { fill: stylerResult.fill ?? fill, fillOpacity: stylerResult.fillOpacity ?? fillOpacity, lineDash: stylerResult.lineDash ?? lineDash, lineDashOffset: stylerResult.lineDashOffset ?? lineDashOffset, stroke: stylerResult.stroke ?? stroke, strokeOpacity: stylerResult.strokeOpacity ?? strokeOpacity, strokeWidth: stylerResult.strokeWidth ?? strokeWidth, marker: { enabled: stylerResult.marker.enabled ?? marker.enabled, fill: stylerResult.marker.fill ?? markerFill, fillOpacity: stylerResult.marker.fillOpacity ?? markerFillOpacity, shape: stylerResult.marker.shape ?? shape, size: stylerResult.marker.size ?? size, lineDash: stylerResult.marker.lineDash ?? marker.lineDash ?? lineDash, lineDashOffset: stylerResult.marker.lineDashOffset ?? marker.lineDashOffset ?? lineDashOffset, stroke: stylerResult.marker.stroke ?? marker.stroke ?? stroke, strokeOpacity: stylerResult.marker.strokeOpacity ?? marker.strokeOpacity ?? strokeOpacity, strokeWidth: stylerResult.marker.strokeWidth ?? marker.strokeWidth ?? strokeWidth } }; } getFormattedMarkerStyle(datum) { const stylerStyle = this.getStyle(); const params = this.makeItemStylerParams( this.dataModel, this.processedData, datum.datumIndex, stylerStyle.marker ); return this.getMarkerStyle( this.properties.marker, datum, params, { isHighlight: true }, void 0, stylerStyle ); } isPointInArea(x, y) { let fillPath; for (const child of this.backgroundGroup.children()) { if (child instanceof Path) { fillPath = child; break; } } if (!fillPath?.getBBox().containsPoint(x, y)) { return false; } return fillPath.isPointInPath(x, y); } computeFocusBounds(opts) { return computeMarkerFocusBounds(this, opts); } hasItemStylers() { return this.properties.styler != null || this.properties.marker.itemStyler != null || this.properties.label.itemStyler != null; } }; AreaSeries.className = "AreaSeries"; AreaSeries.type = "area"; // packages/ag-charts-community/src/chart/series/cartesian/areaSeriesModule.ts var themeTemplate = { series: { nodeClickRange: "nearest", fill: { $applySwitch: [ { $path: "type" }, { $palette: "fill" }, ["gradient", FILL_GRADIENT_LINEAR_DEFAULTS], ["image", FILL_IMAGE_DEFAULTS], ["pattern", FILL_PATTERN_DEFAULTS] ] }, stroke: { $palette: "stroke" }, fillOpacity: 0.8, strokeOpacity: 1, strokeWidth: { $isUserOption: ["./stroke", 2, 0] }, lineDash: [0], lineDashOffset: 0, shadow: { enabled: false, color: DEFAULT_SHADOW_COLOUR, xOffset: 3, yOffset: 3, blur: 5 }, interpolation: { type: "linear" }, marker: { enabled: false, shape: "circle", size: 7, strokeWidth: { $isUserOption: ["./stroke", 1, 0] }, fill: { $applySwitch: [ { $path: "type" }, { $palette: "fill" }, ["gradient", FILL_GRADIENT_RADIAL_REVERSED_DEFAULTS], ["pattern", FILL_PATTERN_DEFAULTS] ] }, stroke: { $palette: "stroke" } }, label: { ...LABEL_BOXING_DEFAULTS, enabled: false, fontSize: { $ref: "fontSize" }, fontFamily: { $ref: "fontFamily" }, fontWeight: { $ref: "fontWeight" }, color: { $ref: "textColor" } }, tooltip: { range: { $path: ["/tooltip/range", "nearest"] }, position: { anchorTo: { $path: ["/tooltip/position/anchorTo", "node"] } } }, highlight: MARKER_SERIES_HIGHLIGHT_STYLE, segmentation: SEGMENTATION_DEFAULTS } }; var AreaSeriesModule = { type: "series", name: "area", chartType: "cartesian", stackable: true, version: VERSION, dependencies: [CartesianChartModule], options: areaSeriesOptionsDef, predictAxis: predictCartesianNonPrimitiveAxis, defaultAxes: { y: { type: "number" /* NUMBER */, position: "left" /* LEFT */ }, x: { type: "category" /* CATEGORY */, position: "bottom" /* BOTTOM */ } }, axisKeys: { ["x" /* X */]: "xKeyAxis", ["y" /* Y */]: "yKeyAxis" }, themeTemplate, create: (ctx) => new AreaSeries(ctx) }; // packages/ag-charts-community/src/scene/shape/barShape.ts var FEATHERED_THRESHOLD = 1e-3; var BarShape = class extends Rect { constructor() { super(...arguments); this.direction = "x"; this.featherRatio = 0; } // optimised field accessor /** * High-performance static property setter that bypasses the decorator system entirely. * Writes directly to backing fields (__propertyName) to avoid: * - Decorator setter chains and equality checks * - Multiple onChangeDetection calls per property * - Object.keys() iteration in assignIfNotStrictlyEqual * - Object allocation overhead * * A single markDirty() call at the end ensures the scene graph is properly invalidated. * WARNING: Only use for hot paths where performance is critical and properties don't need * individual change detection (e.g., when updating many nodes in a loop). */ setStaticProperties(drawingMode, topLeftCornerRadius, topRightCornerRadius, bottomRightCornerRadius, bottomLeftCornerRadius, visible, crisp, fillShadow, direction, featherRatio) { this.__direction = direction ?? "x"; this.__featherRatio = featherRatio ?? 0; super.setStaticProperties( drawingMode, topLeftCornerRadius, topRightCornerRadius, bottomRightCornerRadius, bottomLeftCornerRadius, visible, crisp, fillShadow ); } get feathered() { return Math.abs(this.featherRatio) > FEATHERED_THRESHOLD; } isPointInPath(x, y) { if (!this.feathered) { return super.isPointInPath(x, y); } const bbox = this.getBBox(); return bbox.containsPoint(x, y); } updatePath() { if (!this.feathered) { super.updatePath(); return; } const { path, borderPath, __direction: direction, __featherRatio: featherRatio, __x: x, __y: y, __width: width2, __height: height2 } = this; path.clear(); borderPath.clear(); if (direction === "x") { const featherInsetX = Math.abs(featherRatio) * width2; if (featherRatio > 0) { path.moveTo(x, y); path.lineTo(x + width2 - featherInsetX, y); path.lineTo(x + width2, y + height2 / 2); path.lineTo(x + width2 - featherInsetX, y + height2); path.lineTo(x, y + height2); path.closePath(); } else { path.moveTo(x + featherInsetX, y); path.lineTo(x + width2, y); path.lineTo(x + width2, y + height2); path.lineTo(x + featherInsetX, y + height2); path.lineTo(x, y + height2 / 2); path.closePath(); } } else { const featherInsetY = Math.abs(featherRatio) * height2; if (featherRatio > 0) { path.moveTo(x, y + featherInsetY); path.lineTo(x + width2 / 2, y); path.lineTo(x + width2, y + featherInsetY); path.lineTo(x + width2, y + height2); path.lineTo(x, y + height2); path.closePath(); } else { path.moveTo(x, y); path.lineTo(x + width2, y); path.lineTo(x + width2, y + height2 - featherInsetY); path.lineTo(x + width2 / 2, y + height2); path.lineTo(x, y + height2 - featherInsetY); path.closePath(); } } } renderStroke(ctx) { if (!this.feathered) { super.renderStroke(ctx); return; } const { __stroke: stroke, __strokeWidth: strokeWidth, __lineDash: lineDash, __lineDashOffset: lineDashOffset, __lineCap: lineCap, __lineJoin: lineJoin, path } = this; if (stroke && strokeWidth) { const { globalAlpha } = ctx; this.applyStrokeAndAlpha(ctx); ctx.lineWidth = strokeWidth; if (lineDash) { ctx.setLineDash(lineDash); } if (lineDashOffset) { ctx.lineDashOffset = lineDashOffset; } if (lineCap) { ctx.lineCap = lineCap; } if (lineJoin) { ctx.lineJoin = lineJoin; } ctx.stroke(path.getPath2D()); ctx.globalAlpha = globalAlpha; } } }; __decorateClass([ DeclaredSceneChangeDetection() ], BarShape.prototype, "direction", 2); __decorateClass([ DeclaredSceneChangeDetection() ], BarShape.prototype, "featherRatio", 2); // packages/ag-charts-community/src/chart/series/cartesian/quadtreeUtil.ts function addHitTestersToQuadtree(quadtree, hitTesters) { for (const node of hitTesters) { const datum = node.datum; if (datum === void 0) { logger_exports.error("undefined datum"); } else { quadtree.addValue(node, datum); } } } function findQuadtreeMatch(series, point) { const { x, y } = point; const { nearest, distanceSquared: distanceSquared2 } = series.getQuadTree().find(x, y); if (nearest !== void 0) { return { datum: nearest.value, distance: Math.sqrt(distanceSquared2) }; } return void 0; } // packages/ag-charts-community/src/chart/series/cartesian/abstractBarSeries.ts var AbstractBarSeriesProperties = class extends CartesianSeriesProperties { constructor() { super(...arguments); this.direction = "vertical"; this.width = void 0; this.widthRatio = void 0; } }; __decorateClass([ addFakeTransformToInstanceProperty ], AbstractBarSeriesProperties.prototype, "direction", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AbstractBarSeriesProperties.prototype, "width", 2); __decorateClass([ addFakeTransformToInstanceProperty ], AbstractBarSeriesProperties.prototype, "widthRatio", 2); var AbstractBarSeries = class extends CartesianSeries { constructor() { super(...arguments); this.smallestDataInterval = void 0; this.largestDataInterval = void 0; } padBandExtent(keys, alignStart) { const ratio2 = typeof alignStart === "boolean" ? 1 : 0.5; const scalePadding = isFiniteNumber(this.smallestDataInterval) ? this.smallestDataInterval * ratio2 : 0; const keysExtent = extent(keys) ?? [Number.NaN, Number.NaN]; if (typeof alignStart === "boolean") { keysExtent[alignStart ? 0 : 1] -= (alignStart ? 1 : -1) * scalePadding; } else { keysExtent[0] -= scalePadding; keysExtent[1] += scalePadding; } return fixNumericExtent(keysExtent); } getBandScalePadding() { return { inner: 0.3, outer: 0.15 }; } shouldFlipXY() { return !this.isVertical(); } isVertical() { return this.properties.direction === "vertical"; } getBarDirection() { return this.shouldFlipXY() ? "x" /* X */ : "y" /* Y */; } getCategoryDirection() { return this.shouldFlipXY() ? "y" /* Y */ : "x" /* X */; } getValueAxis() { const direction = this.getBarDirection(); return this.axes[direction]; } getCategoryAxis() { const direction = this.getCategoryDirection(); return this.axes[direction]; } getMinimumRangeSeries(ranges) { const { width: width2 } = this.properties; if (width2 == null) return; const axis = this.getCategoryAxis(); if (!axis) return; const { index } = this.ctx.seriesStateManager.getVisiblePeerGroupIndex(this); ranges[index] = Math.max(ranges[index] ?? 0, width2); } getMinimumRangeChart(ranges) { if (ranges.length === 0) return 0; const axis = this.getCategoryAxis(); if (!(axis instanceof GroupedCategoryAxis || axis instanceof CategoryAxis)) return 0; const dataSize = this.data?.netSize() ?? 0; if (dataSize === 0) return 0; const defaultPadding = this.getBandScalePadding(); const { paddingInner = defaultPadding.inner, paddingOuter = defaultPadding.outer, groupPaddingInner } = axis; const width2 = ranges.reduce((sum, range3) => sum + range3, 0); const averageWidth = width2 / ranges.length; const { visibleGroupCount } = this.ctx.seriesStateManager.getVisiblePeerGroupIndex(this); const bandWidth = width2 + groupPaddingInner * averageWidth * (visibleGroupCount - 1); const paddingFactor = (dataSize - paddingInner + paddingOuter * 2) / (1 - paddingInner); return bandWidth * paddingFactor; } /** * Override to use bar-specific axis resolution (category/value vs X/Y). * Bar series can be horizontal or vertical, so we use getCategoryAxis/getValueAxis. */ validateCreateNodeDataPreconditions() { const xAxis = this.getCategoryAxis(); const yAxis = this.getValueAxis(); if (!xAxis || !yAxis || !this.dataModel || !this.processedData) { return void 0; } return { xAxis, yAxis }; } getBandwidth(xAxis, minWidth) { return ContinuousScale.is(xAxis.scale) ? xAxis.scale.calcBandwidth(this.smallestDataInterval, minWidth) : xAxis.scale.bandwidth; } xCoordinateRange(xValue) { const xAxis = this.axes[this.getCategoryDirection()]; const xScale = xAxis.scale; const bandWidth = this.getBandwidth(xAxis, 0) ?? 0; const barOffset = ContinuousScale.is(xScale) ? bandWidth * -0.5 : 0; const x = xScale.convert(xValue) + barOffset; return [x, x + bandWidth]; } yCoordinateRange(yValues) { const yAxis = this.axes[this.getBarDirection()]; const yScale = yAxis.scale; const ys = yValues.map((yValue) => yScale.convert(yValue)); if (ys.length === 1) { const y0 = yScale.convert(0); return [Math.min(ys[0], y0), Math.max(ys[0], y0)]; } return [Math.min(...ys), Math.max(...ys)]; } getBarDimensions() { const categoryAxis = this.getCategoryAxis(); const bandwidth = this.getBandwidth(categoryAxis) ?? 0; this.ctx.seriesStateManager.updateGroupScale(this, bandwidth, categoryAxis); const groupOffset = this.getGroupOffset(); const barWidth = this.getBarWidth(); const barOffset = this.getBarOffset(barWidth); return { groupOffset, barOffset, barWidth }; } getGroupOffset() { return this.ctx.seriesStateManager.getGroupOffset(this); } getBarOffset(barWidth) { const groupScale = this.ctx.seriesStateManager.getGroupScale(this); const xAxis = this.getCategoryAxis(); let barOffset = 0; if (ContinuousScale.is(xAxis.scale)) { barOffset = -barWidth / 2; } else if (this.seriesGrouping == null && groupScale) { const rangeWidth = this.getGroupScaleRangeWidth(groupScale); barOffset = (rangeWidth - barWidth) / 2; } else if (groupScale && this.properties.widthRatio != null) { barOffset = (groupScale.bandwidth - barWidth) / 2; } const stackOffset = this.ctx.seriesStateManager.getStackOffset(this, barWidth); return barOffset + stackOffset; } getBarWidth() { const { seriesGrouping } = this; const { width: width2 } = this.properties; let { widthRatio } = this.properties; const groupScale = this.ctx.seriesStateManager.getGroupScale(this); const bandwidth = groupScale?.bandwidth ?? 0; if (seriesGrouping == null) { widthRatio ?? (widthRatio = 1); } if (widthRatio != null) { let relativeWidth = width2; if (seriesGrouping == null && relativeWidth == null && groupScale) { relativeWidth = this.getGroupScaleRangeWidth(groupScale); } if (relativeWidth == null && bandwidth < 1 && groupScale) { return groupScale.rawBandwidth; } return (relativeWidth ?? bandwidth) * widthRatio; } if (width2 != null) { return width2; } if (bandwidth < 1 && groupScale) { return groupScale.rawBandwidth; } return bandwidth; } getGroupScaleRangeWidth(groupScale) { let rangeWidth = groupScale.range[1] - groupScale.range[0]; if (groupScale.round && rangeWidth > 0) rangeWidth = Math.floor(rangeWidth); return rangeWidth; } resolveKeyDirection(direction) { if (this.getBarDirection() === "x" /* X */) { if (direction === "x" /* X */) { return "y" /* Y */; } return "x" /* X */; } return direction; } initQuadTree(quadtree) { addHitTestersToQuadtree(quadtree, this.datumNodesIter()); } pickNodeClosestDatum(point) { return findQuadtreeMatch(this, point); } }; // packages/ag-charts-community/src/chart/series/cartesian/barAggregation.ts function computeBarAggregation(domain, xValues, yStartValues, yEndValues, options) { if (xValues.length < AGGREGATION_THRESHOLD) return; const [d0, d1] = domain; const { smallestKeyInterval, xNeedsValueOf, yNeedsValueOf, existingFilters } = options; let maxRange = aggregationRangeFittingPoints(xValues, d0, d1, { smallestKeyInterval, xNeedsValueOf }); const existingFilter = existingFilters?.find((f) => f.maxRange === maxRange); let { indexData: positiveIndexData, valueData: positiveValueData, negativeIndexData, negativeValueData } = createAggregationIndices(xValues, yEndValues, yStartValues ?? yEndValues, d0, d1, maxRange, { split: true, xNeedsValueOf, yNeedsValueOf, reuseIndexData: existingFilter?.positiveIndexData, reuseValueData: existingFilter?.positiveValueData, reuseNegativeIndexData: existingFilter?.negativeIndexData, reuseNegativeValueData: existingFilter?.negativeValueData }); if (!negativeIndexData || !negativeValueData) { throw new Error("Negative aggregation data missing in split mode"); } let positiveIndices = getMidpointsForIndices(maxRange, positiveIndexData, existingFilter?.positiveIndices); let negativeIndices = getMidpointsForIndices(maxRange, negativeIndexData, existingFilter?.negativeIndices); const filters = [ { maxRange, positiveIndices, positiveIndexData, positiveValueData, negativeIndices, negativeIndexData, negativeValueData } ]; while (maxRange > 64) { const currentMaxRange = maxRange; const nextMaxRange = Math.trunc(currentMaxRange / 2); const nextExistingFilter = existingFilters?.find((f) => f.maxRange === nextMaxRange); const positiveCompacted = compactAggregationIndices(positiveIndexData, positiveValueData, currentMaxRange, { reuseIndexData: nextExistingFilter?.positiveIndexData, reuseValueData: nextExistingFilter?.positiveValueData }); const negativeCompacted = compactAggregationIndices(negativeIndexData, negativeValueData, currentMaxRange, { reuseIndexData: nextExistingFilter?.negativeIndexData, reuseValueData: nextExistingFilter?.negativeValueData }); maxRange = positiveCompacted.maxRange; positiveIndexData = positiveCompacted.indexData; positiveValueData = positiveCompacted.valueData; positiveIndices = positiveCompacted.midpointData ?? getMidpointsForIndices(maxRange, positiveIndexData, nextExistingFilter?.positiveIndices); negativeIndexData = negativeCompacted.indexData; negativeValueData = negativeCompacted.valueData; negativeIndices = negativeCompacted.midpointData ?? getMidpointsForIndices(maxRange, negativeIndexData, nextExistingFilter?.negativeIndices); filters.push({ maxRange, positiveIndices, positiveIndexData, positiveValueData, negativeIndices, negativeIndexData, negativeValueData }); } filters.reverse(); return filters; } function computeBarAggregationPartial(domain, xValues, yStartValues, yEndValues, options) { if (xValues.length < AGGREGATION_THRESHOLD) return; const [d0, d1] = domain; const { smallestKeyInterval, xNeedsValueOf, yNeedsValueOf, targetRange, existingFilters } = options; const finestMaxRange = aggregationRangeFittingPoints(xValues, d0, d1, { smallestKeyInterval, xNeedsValueOf }); const targetMaxRange = Math.min(finestMaxRange, nextPowerOf2(Math.max(targetRange, AGGREGATION_MIN_RANGE))); const existingFilter = existingFilters?.find((f) => f.maxRange === targetMaxRange); const { indexData: positiveIndexData, valueData: positiveValueData, negativeIndexData, negativeValueData } = createAggregationIndices(xValues, yEndValues, yStartValues ?? yEndValues, d0, d1, targetMaxRange, { split: true, xNeedsValueOf, yNeedsValueOf, reuseIndexData: existingFilter?.positiveIndexData, reuseValueData: existingFilter?.positiveValueData, reuseNegativeIndexData: existingFilter?.negativeIndexData, reuseNegativeValueData: existingFilter?.negativeValueData }); if (!negativeIndexData || !negativeValueData) { throw new Error("Negative aggregation data missing in split mode"); } const immediateLevel = { maxRange: targetMaxRange, positiveIndices: getMidpointsForIndices(targetMaxRange, positiveIndexData, existingFilter?.positiveIndices), positiveIndexData, positiveValueData, negativeIndices: getMidpointsForIndices(targetMaxRange, negativeIndexData, existingFilter?.negativeIndices), negativeIndexData, negativeValueData }; function computeRemaining() { const allLevels = computeBarAggregation([d0, d1], xValues, yStartValues, yEndValues, { smallestKeyInterval, xNeedsValueOf, yNeedsValueOf, existingFilters }); return allLevels?.filter((level) => level.maxRange !== targetMaxRange) ?? []; } return { immediate: [immediateLevel], computeRemaining }; } function aggregateBarData(scale2, xValues, yStartValues, yEndValues, domainInput, smallestKeyInterval, xNeedsValueOf, yNeedsValueOf) { const [d0, d1] = aggregationDomain(scale2, domainInput); return computeBarAggregation([d0, d1], xValues, yStartValues, yEndValues, { smallestKeyInterval, xNeedsValueOf, yNeedsValueOf }); } var memoizedAggregateBarData = simpleMemorize2(aggregateBarData); function aggregateBarDataFromDataModel(scale2, dataModel, processedData, series, existingFilters) { const xValues = dataModel.resolveKeysById(series, "xValue", processedData); const isStacked = dataModel.hasColumnById(series, "yValue-start"); const yStartValues = isStacked ? dataModel.resolveColumnById(series, "yValue-start", processedData) : void 0; const yEndValues = isStacked ? dataModel.resolveColumnById(series, "yValue-end", processedData) : dataModel.resolveColumnById(series, "yValue-raw", processedData); const domainInput = dataModel.getDomain(series, "xValue", "key", processedData); const xNeedsValueOf = dataModel.resolveColumnNeedsValueOf(series, "xValue", processedData); const yNeedsValueOf = dataModel.resolveColumnNeedsValueOf( series, isStacked ? "yValue-end" : "yValue-raw", processedData ); if (existingFilters) { const [d0, d1] = aggregationDomain(scale2, domainInput); return computeBarAggregation([d0, d1], xValues, yStartValues, yEndValues, { smallestKeyInterval: processedData.reduced?.smallestKeyInterval, xNeedsValueOf, yNeedsValueOf, existingFilters }); } return memoizedAggregateBarData( scale2, xValues, yStartValues, yEndValues, domainInput, processedData.reduced?.smallestKeyInterval, xNeedsValueOf, yNeedsValueOf ); } function aggregateBarDataFromDataModelPartial(scale2, dataModel, processedData, series, targetRange, existingFilters) { const xValues = dataModel.resolveKeysById(series, "xValue", processedData); const isStacked = dataModel.hasColumnById(series, "yValue-start"); const yStartValues = isStacked ? dataModel.resolveColumnById(series, "yValue-start", processedData) : void 0; const yEndValues = isStacked ? dataModel.resolveColumnById(series, "yValue-end", processedData) : dataModel.resolveColumnById(series, "yValue-raw", processedData); const domainInput = dataModel.getDomain(series, "xValue", "key", processedData); const xNeedsValueOf = dataModel.resolveColumnNeedsValueOf(series, "xValue", processedData); const yNeedsValueOf = dataModel.resolveColumnNeedsValueOf( series, isStacked ? "yValue-end" : "yValue-raw", processedData ); const [d0, d1] = aggregationDomain(scale2, domainInput); return computeBarAggregationPartial([d0, d1], xValues, yStartValues, yEndValues, { smallestKeyInterval: processedData.reduced?.smallestKeyInterval, xNeedsValueOf, yNeedsValueOf, targetRange, existingFilters }); } // packages/ag-charts-community/src/chart/series/cartesian/barSeriesProperties.ts var BarSeriesLabel = class extends Label { constructor() { super(...arguments); this.placement = "inside-center"; this.spacing = 0; } }; __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesLabel.prototype, "placement", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesLabel.prototype, "spacing", 2); var BarSeriesProperties = class extends AbstractBarSeriesProperties { constructor() { super(...arguments); this.fill = "#c16068"; this.fillOpacity = 1; this.stroke = "#874349"; this.strokeWidth = 1; this.strokeOpacity = 1; this.lineDash = [0]; this.lineDashOffset = 0; this.cornerRadius = 0; this.crisp = void 0; this.shadow = new DropShadow(); this.label = new BarSeriesLabel(); this.tooltip = makeSeriesTooltip(); this.sparklineMode = false; } }; __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesProperties.prototype, "xKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesProperties.prototype, "xName", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesProperties.prototype, "yKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesProperties.prototype, "yName", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesProperties.prototype, "yFilterKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesProperties.prototype, "stackGroup", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesProperties.prototype, "normalizedTo", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesProperties.prototype, "fill", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesProperties.prototype, "fillOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesProperties.prototype, "stroke", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesProperties.prototype, "strokeWidth", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesProperties.prototype, "strokeOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesProperties.prototype, "lineDash", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesProperties.prototype, "lineDashOffset", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesProperties.prototype, "cornerRadius", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesProperties.prototype, "crisp", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesProperties.prototype, "styler", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesProperties.prototype, "itemStyler", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesProperties.prototype, "simpleItemStyler", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesProperties.prototype, "shadow", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesProperties.prototype, "label", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesProperties.prototype, "tooltip", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BarSeriesProperties.prototype, "sparklineMode", 2); // packages/ag-charts-community/src/chart/series/cartesian/barUtil.ts function checkCrisp(scale2, visibleRange, smallestDataInterval, largestDataInterval) { if (visibleRange != null) { const [visibleMin, visibleMax] = visibleRange; const isZoomed = visibleMin !== 0 || visibleMax !== 1; if (isZoomed) return false; } if (ContinuousScale.is(scale2)) { const spacing = scale2.calcBandwidth(largestDataInterval) - scale2.calcBandwidth(smallestDataInterval); if (spacing > 0 && spacing < 1) return false; } if (BandScale.is(scale2)) { const { bandwidth, step } = scale2; if (bandwidth > 0 && bandwidth < 1) return false; const spacing = step - bandwidth; if (spacing > 0 && spacing < 1) return false; } return true; } var isDatumNegative = (datum) => { return isNegative(datum.yValue ?? 0); }; function collapsedStartingBarPosition(isVertical, axes, mode) { const { startingX, startingY } = getStartingValues(isVertical, axes); const calculate = (datum, prevDatum) => { let x = isVertical ? datum.x : startingX; let y = isVertical ? startingY : datum.y; let width2 = isVertical ? datum.width : 0; let height2 = isVertical ? 0 : datum.height; const { opacity = 1 } = datum; if (prevDatum && (Number.isNaN(x) || Number.isNaN(y))) { ({ x, y } = prevDatum); width2 = isVertical ? prevDatum.width : 0; height2 = isVertical ? 0 : prevDatum.height; if (isVertical && !isDatumNegative(prevDatum)) { y += prevDatum.height; } else if (!isVertical && isDatumNegative(prevDatum)) { x += prevDatum.width; } } let clipBBox; if (datum.clipBBox == null) { clipBBox = void 0; } else if (isDatumNegative(datum)) { clipBBox = isVertical ? new BBox(x, y - height2, width2, height2) : new BBox(x - width2, y, width2, height2); } else { clipBBox = new BBox(x, y, width2, height2); } return { x, y, width: width2, height: height2, clipBBox, opacity }; }; return { isVertical, calculate, mode }; } function midpointStartingBarPosition(isVertical, mode) { return { isVertical, calculate: (datum) => { return { x: isVertical ? datum.x : datum.x + datum.width / 2, y: isVertical ? datum.y + datum.height / 2 : datum.y, width: isVertical ? datum.width : 0, height: isVertical ? 0 : datum.height, clipBBox: datum.clipBBox, opacity: datum.opacity ?? 1 }; }, mode }; } function prepareBarAnimationFunctions(initPos, unknownStatus) { const isRemoved = (datum) => datum == null || Number.isNaN(datum.x) || Number.isNaN(datum.y); const fromFn = (rect2, datum, status) => { if (status === "updated" && isRemoved(datum)) { status = "removed"; } else if (status === "updated" && isRemoved(rect2.previousDatum)) { status = "added"; } let source; if (status === "unknown" || status === "added") { if (rect2.previousDatum == null && initPos.mode === "fade") { source = { ...resetBarSelectionsFn(rect2, datum), opacity: 0 }; } else { source = initPos.calculate(datum, rect2.previousDatum); } if (status === "unknown") { status = unknownStatus; } } else { source = { x: rect2.x, y: rect2.y, width: rect2.width, height: rect2.height, clipBBox: rect2.clipBBox, opacity: rect2.opacity ?? 1 }; } const phase = NODE_UPDATE_STATE_TO_PHASE_MAPPING[status]; return { ...source, phase }; }; const toFn = (rect2, datum, status) => { if (status === "removed" && rect2.datum == null && initPos.mode === "fade") { return { ...resetBarSelectionsFn(rect2, datum), opacity: 0 }; } else if (status === "removed" || isRemoved(datum)) { return initPos.calculate(datum, rect2.previousDatum); } else { return { x: datum.x, y: datum.y, width: datum.width, height: datum.height, clipBBox: datum.clipBBox, opacity: datum.opacity ?? 1 }; } }; const applyFn = (rect2, datum, status) => { rect2.resetAnimationProperties(datum.x, datum.y, datum.width, datum.height, datum.opacity ?? 1, datum.clipBBox); rect2.crisp = status === "end" && (rect2.datum?.crisp ?? false); }; return { toFn, fromFn, applyFn }; } function getStartingValues(isVertical, axes) { const axis = axes[isVertical ? "y" /* Y */ : "x" /* X */]; let startingX = Infinity; let startingY = 0; if (!axis) { return { startingX, startingY }; } if (isVertical) { startingY = axis.scale.convert(ContinuousScale.is(axis.scale) ? 0 : Math.max(...axis.range)); } else { startingX = axis.scale.convert(ContinuousScale.is(axis.scale) ? 0 : Math.min(...axis.range)); } return { startingX, startingY }; } function resetBarSelectionsFn(rect2, { x, y, width: width2, height: height2, clipBBox, opacity = 1 }) { return { x, y, width: width2, height: height2, clipBBox, opacity, crisp: rect2.datum?.crisp ?? false }; } function resetBarSelectionsDirect(selections) { for (const selection of selections) { const nodes = selection.nodes(); selection.batchedUpdate(function resetBarNodes() { for (const node of nodes) { const datum = node.datum; if (datum == null) continue; node.resetAnimationProperties( datum.x, datum.y, datum.width, datum.height, datum.opacity ?? 1, datum.clipBBox ); node.crisp = datum.crisp ?? false; } selection.cleanup(); }); } } function computeBarFocusBounds(series, datum) { if (datum === void 0) return void 0; const { x, y, width: width2, height: height2 } = datum; return Transformable.toCanvas(series.contentGroup, new BBox(x, y, width2, height2)); } // packages/ag-charts-community/src/chart/series/cartesian/diffUtil.ts function calculateDataDiff(seriesId, datumSelection, getDatumId, contextNodeData, previousContextNodeData, processedData, processedDataUpdated) { let dataDiff = processedData?.reduced?.diff?.[seriesId]; if (dataDiff?.changed) { return dataDiff; } if (!processedDataUpdated) { return { changed: false, added: /* @__PURE__ */ new Set(), updated: /* @__PURE__ */ new Set(), removed: /* @__PURE__ */ new Set(), moved: /* @__PURE__ */ new Set() }; } const scalingChanged = hasScalingChanged(contextNodeData, previousContextNodeData); if (dataDiff == null && processedData?.reduced?.diff != null) { dataDiff = { changed: true, added: /* @__PURE__ */ new Set(), updated: /* @__PURE__ */ new Set(), removed: /* @__PURE__ */ new Set(), moved: /* @__PURE__ */ new Set() }; if (scalingChanged) { dataDiff.updated = new Set(Array.from(datumSelection, ({ datum }) => getDatumId(datum))); } else { dataDiff.added = new Set(Array.from(datumSelection, ({ datum }) => getDatumId(datum))); } } else if (scalingChanged) { dataDiff = { changed: true, added: /* @__PURE__ */ new Set(), updated: new Set(Array.from(datumSelection, ({ datum }) => getDatumId(datum))), removed: /* @__PURE__ */ new Set(), moved: /* @__PURE__ */ new Set() }; } return dataDiff; } function isGroupScaleContext(ctx) { return typeof ctx === "object" && ctx !== null && "groupScale" in ctx; } function hasScalingChanged(contextNodeData, previousContextNodeData) { if (!previousContextNodeData) return false; const scales = contextNodeData.scales; const prevScales = previousContextNodeData.scales; if (!areScalingEqual(scales.x, prevScales.x)) return true; if (!areScalingEqual(scales.y, prevScales.y)) return true; if (!isGroupScaleContext(contextNodeData) || !isGroupScaleContext(previousContextNodeData)) return false; const groupScale = contextNodeData.groupScale; const prevGroupScale = previousContextNodeData.groupScale; return !areScalingEqual(groupScale, prevGroupScale); } // packages/ag-charts-community/src/chart/series/cartesian/barSeries.ts var BarSeries = class extends AbstractBarSeries { constructor(moduleCtx) { super({ moduleCtx, propertyKeys: DEFAULT_CARTESIAN_DIRECTION_KEYS, propertyNames: DEFAULT_CARTESIAN_DIRECTION_NAMES, categoryKey: "xValue", pickModes: [ 2 /* AXIS_ALIGNED */, // Only used in sparklineMode 1 /* NEAREST_NODE */, 0 /* EXACT_SHAPE_MATCH */ ], pathsPerSeries: [], datumSelectionGarbageCollection: false, animationAlwaysUpdateSelections: true, animationResetFns: { datum: resetBarSelectionsFn, label: resetLabelFn } }); this.properties = new BarSeriesProperties(); this.connectsToYAxis = true; this.aggregationManager = new AggregationManager(); this.phantomGroup = this.contentGroup.appendChild(new Group({ name: "phantom", zIndex: -1 })); this.phantomSelection = Selection.select( this.phantomGroup, () => this.nodeFactory(), false ); this.phantomHighlightGroup = this.highlightGroup.appendChild( new Group({ name: `${this.internalId}-highlight-node` }) ); this.phantomHighlightSelection = Selection.select( this.phantomHighlightGroup, () => this.nodeFactory(), false ); this.phantomGroup.opacity = 0.2; this.phantomHighlightGroup.opacity = 0.2; } get pickModeAxis() { return this.properties.sparklineMode ? "main" : void 0; } crossFilteringEnabled() { return this.properties.yFilterKey != null && (this.seriesGrouping == null || this.seriesGrouping.stackIndex === 0); } async processData(dataController) { if (!this.data) return; const { xKey, yKey, yFilterKey, normalizedTo } = this.properties; const { seriesGrouping: { groupIndex = this.id } = {}, data } = this; const stackCount = this.seriesGrouping?.stackCount ?? 0; const stacked = stackCount > 1 || normalizedTo != null; const grouped = stacked; const animationEnabled = !this.ctx.animationManager.isSkipped(); const xScale = this.getCategoryAxis()?.scale; const yScale = this.getValueAxis()?.scale; const { isContinuousX, xScaleType, yScaleType } = this.getScaleInformation({ xScale, yScale }); const stackGroupName = `bar-stack-${groupIndex}-yValues`; const stackGroupTrailingName = `${stackGroupName}-trailing`; const visibleProps = this.visible ? {} : { forceValue: 0 }; const allowNullKey = this.properties.allowNullKeys ?? false; const props = [ keyProperty(xKey, xScaleType, { id: "xValue", allowNullKey }), valueProperty(yKey, yScaleType, { id: `yValue-raw`, invalidValue: null, ...visibleProps }) ]; if (this.crossFilteringEnabled()) { props.push( valueProperty(yFilterKey, yScaleType, { id: `yFilterValue`, invalidValue: null, ...visibleProps }) ); } if (stacked) { props.push( ...groupAccumulativeValueProperty( yKey, "normal", { id: `yValue-end`, rangeId: `yValue-range`, invalidValue: null, missingValue: 0, groupId: stackGroupName, separateNegative: true, ...visibleProps }, yScaleType ), ...groupAccumulativeValueProperty( yKey, "trailing", { id: `yValue-start`, invalidValue: null, missingValue: 0, groupId: stackGroupTrailingName, separateNegative: true, ...visibleProps }, yScaleType ) ); } if (isContinuousX) { props.push(SMALLEST_KEY_INTERVAL, LARGEST_KEY_INTERVAL); } if (isFiniteNumber(normalizedTo)) { props.push(normaliseGroupTo([stackGroupName, stackGroupTrailingName], Math.abs(normalizedTo))); } if (this.needsDataModelDiff() && this.processedData) { props.push(diff(this.id, this.processedData)); } if (animationEnabled || !grouped) { props.push(animationValidation()); } const { dataModel, processedData } = await this.requestDataModel(dataController, data, { props, groupByKeys: grouped, groupByData: !grouped }); this.aggregateData(dataModel, processedData); this.smallestDataInterval = processedData.reduced?.smallestKeyInterval; this.largestDataInterval = processedData.reduced?.largestKeyInterval; this.animationState.transition("updateData"); } yCumulativeKey(dataModel) { return dataModel.hasColumnById(this, `yValue-end`) ? "yValue-end" : "yValue-raw"; } getSeriesDomain(direction) { const { processedData, dataModel } = this; if (dataModel == null || processedData == null) return { domain: [] }; if (direction === this.getCategoryDirection()) { const keyDef = dataModel.resolveProcessedDataDefById(this, `xValue`); const keys = dataModel.getDomain(this, `xValue`, "key", processedData); if (keyDef?.def.type === "key" && keyDef.def.valueType === "category") { return keys; } return { domain: this.padBandExtent(keys.domain) }; } const yKey = this.yCumulativeKey(dataModel); let yExtent = this.domainForClippedRange(direction, [yKey], "xValue"); const yFilterExtent = this.crossFilteringEnabled() ? dataModel.getDomain(this, `yFilterValue`, "value", processedData).domain : void 0; if (yFilterExtent != null) { yExtent = [Math.min(yExtent[0], yFilterExtent[0]), Math.max(yExtent[1], yFilterExtent[1])]; } const yAxis = this.getValueAxis(); if (yAxis instanceof NumberAxis && !(yAxis instanceof LogAxis)) { const fixedYExtent = Number.isFinite(yExtent[1] - yExtent[0]) ? [Math.min(0, yExtent[0]), Math.max(0, yExtent[1])] : []; return { domain: fixNumericExtent(fixedYExtent) }; } else { return { domain: fixNumericExtent(yExtent) }; } } getSeriesRange(direction, visibleRange) { const selfDirection = this.properties.direction === "horizontal" ? "x" /* X */ : "y" /* Y */; if (selfDirection !== direction) return []; const yKey = this.yCumulativeKey(this.dataModel); const [y0, y1] = this.domainForVisibleRange("y" /* Y */, [yKey], "xValue", visibleRange); return [Math.min(y0, 0), Math.max(y1, 0)]; } getZoomRangeFittingItems(xVisibleRange, yVisibleRange, minVisibleItems) { const yKey = this.yCumulativeKey(this.dataModel); return this.zoomFittingVisibleItems("xValue", [yKey], xVisibleRange, yVisibleRange, minVisibleItems); } getVisibleItems(xVisibleRange, yVisibleRange, minVisibleItems) { const yKey = this.yCumulativeKey(this.dataModel); return this.countVisibleItems("xValue", [yKey], xVisibleRange, yVisibleRange, minVisibleItems); } aggregateData(dataModel, processedData) { this.aggregationManager.markStale(processedData.input.count); if (processedDataIsAnimatable(processedData)) return; const xAxis = this.axes["x" /* X */]; if (xAxis == null) return; const targetRange = this.estimateTargetRange(); this.aggregationManager.aggregate({ computePartial: (existingFilters) => aggregateBarDataFromDataModelPartial( xAxis.scale.type, dataModel, processedData, this, targetRange, existingFilters ), computeFull: (existingFilters) => aggregateBarDataFromDataModel(xAxis.scale.type, dataModel, processedData, this, existingFilters), targetRange }); const filters = this.aggregationManager.filters; if (filters && filters.length > 0) { debugMetrics_exports.record( `${this.type}:aggregation`, filters.map((f) => f.maxRange) ); } } estimateTargetRange() { const xAxis = this.axes["x" /* X */]; if (xAxis?.scale?.range) { const [r0, r1] = xAxis.scale.range; return Math.abs(r1 - r0); } return this.ctx.scene?.canvas?.width ?? 800; } /** * Creates shared context for node datum creation/update operations. * This context is instantiated once and reused across all datum operations * to minimize memory allocations. Only caches values that are expensive to * compute - cheap property lookups use `this` directly. */ createNodeDatumContext(xAxis, yAxis) { const { dataModel, processedData } = this; if (!dataModel || !processedData) return void 0; const rawData = processedData.dataSources?.get(this.id); if (rawData == null) return void 0; const xScale = xAxis.scale; const yScale = yAxis.scale; const range3 = Math.abs(xScale.range[1] - xScale.range[0]); this.aggregationManager.ensureLevelForRange(range3); const dataAggregationFilter = this.aggregationManager.getFilterForRange(range3); const isStacked = dataModel.hasColumnById(this, `yValue-start`); const { label } = this.properties; const canIncrementallyUpdate = this.canIncrementallyUpdateNodes(dataAggregationFilter != null); const { groupOffset, barOffset, barWidth } = this.getBarDimensions(); return { dataSource: rawData, rawData: rawData.data, xValues: dataModel.resolveKeysById(this, `xValue`, processedData), yRawValues: dataModel.resolveColumnById(this, `yValue-raw`, processedData), yFilterValues: this.crossFilteringEnabled() ? dataModel.resolveColumnById(this, `yFilterValue`, processedData) : void 0, yStartValues: isStacked ? dataModel.resolveColumnById(this, `yValue-start`, processedData) : void 0, yEndValues: isStacked ? dataModel.resolveColumnById(this, `yValue-end`, processedData) : void 0, xScale, yScale, xAxis, yAxis, groupOffset, barOffset, barWidth, range: range3, yReversed: yAxis.isReversed(), bboxBottom: yScale.convert(0), labelSpacing: label.spacing + (typeof label.padding === "number" ? label.padding : 0), crisp: dataAggregationFilter == null && (this.properties.crisp ?? checkCrisp(xAxis?.scale, xAxis?.visibleRange, this.smallestDataInterval, this.largestDataInterval)), isStacked, animationEnabled: !this.ctx.animationManager.isSkipped(), dataAggregationFilter, canIncrementallyUpdate, phantomNodes: canIncrementallyUpdate ? this.contextNodeData.phantomNodeData ?? [] : [], nodes: canIncrementallyUpdate ? this.contextNodeData.nodeData : [], labels: canIncrementallyUpdate ? this.contextNodeData.labelData : [], nodeIndex: 0, phantomIndex: 0, barAlongX: this.getBarDirection() === "x" /* X */, shouldFlipXY: this.shouldFlipXY(), xKey: this.properties.xKey, yKey: this.properties.yKey, xName: this.properties.xName, yName: this.properties.yName, legendItemName: this.properties.legendItemName, label, yDomain: this.getSeriesDomain("y" /* Y */).domain }; } /** * Computes the x position for a datum at the given index. */ computeXPosition(ctx, datumIndex) { const x = ctx.xScale.convert(ctx.xValues[datumIndex]); if (!Number.isFinite(x)) return Number.NaN; return x + ctx.groupOffset + ctx.barOffset; } prepareNodeDatumState(ctx, nodeDatumScratch, datumIndex, yStart, yEnd) { if (!Number.isFinite(yEnd)) { return void 0; } const xValue = ctx.xValues[datumIndex]; if (xValue === void 0 && !this.properties.allowNullKeys) { return void 0; } const datum = ctx.dataSource?.data[datumIndex]; const yRawValue = ctx.yRawValues[datumIndex]; const yFilterValue = ctx.yFilterValues == null ? void 0 : Number(ctx.yFilterValues[datumIndex]); if (yFilterValue != null && !Number.isFinite(yFilterValue)) { return void 0; } const labelText = ctx.label.enabled && yRawValue != null ? this.getLabelText( yFilterValue ?? yRawValue, datum, ctx.yKey, "y", ctx.yDomain, ctx.label, { datum, value: yFilterValue ?? yRawValue, xKey: ctx.xKey, yKey: ctx.yKey, xName: ctx.xName, yName: ctx.yName, legendItemName: ctx.legendItemName } ) : void 0; const isPositive = yRawValue >= 0 && !Object.is(yRawValue, -0); nodeDatumScratch.datum = datum; nodeDatumScratch.xValue = xValue; nodeDatumScratch.yRawValue = yRawValue; nodeDatumScratch.yFilterValue = yFilterValue; nodeDatumScratch.labelText = labelText; nodeDatumScratch.inset = yFilterValue != null && yFilterValue > yRawValue; nodeDatumScratch.isPositive = isPositive; nodeDatumScratch.precomputedBottomY = yFilterValue == null ? void 0 : ctx.yScale.convert(yStart); nodeDatumScratch.precomputedIsUpward = yFilterValue == null ? void 0 : isPositive !== ctx.yReversed; return nodeDatumScratch; } /** * Creates a skeleton BarNodeDatum with minimal required fields. * The node will be populated by updateNodeDatum. */ createSkeletonNodeDatum(ctx, params, phantom) { const scratch = params.nodeDatumScratch; return { series: this, datum: scratch.datum, datumIndex: params.datumIndex, cumulativeValue: 0, // Will be updated by updateNodeDatum phantom, xValue: scratch.xValue ?? "", yValue: 0, // Will be updated by updateNodeDatum yKey: ctx.yKey, xKey: ctx.xKey, capDefaults: { lengthRatioMultiplier: 0, // Will be updated by updateNodeDatum lengthMax: 0 // Will be updated by updateNodeDatum }, x: 0, // Will be updated by updateNodeDatum y: 0, // Will be updated by updateNodeDatum width: 0, // Will be updated by updateNodeDatum height: 0, // Will be updated by updateNodeDatum midPoint: { x: 0, y: 0 }, // Required - updated in place by updateNodeDatum opacity: params.opacity, featherRatio: params.featherRatio, topLeftCornerRadius: false, // Will be updated by updateNodeDatum topRightCornerRadius: false, // Will be updated by updateNodeDatum bottomRightCornerRadius: false, // Will be updated by updateNodeDatum bottomLeftCornerRadius: false, // Will be updated by updateNodeDatum clipBBox: void 0, // Will be created/updated by updateNodeDatum crisp: ctx.crisp, label: void 0, // Will be created/updated by updateNodeDatum missing: false, // Will be updated by updateNodeDatum focusable: !phantom }; } /** * Creates a BarNodeDatum (and optionally a phantom node) for a single data point. * Creates skeleton nodes and uses updateNodeDatum to populate them with calculated values. */ createNodeDatum(ctx, params) { const prepared = this.prepareNodeDatumState( ctx, params.nodeDatumScratch, params.datumIndex, params.yStart, params.yEnd ); if (!prepared) { return { nodeData: void 0, phantomNodeData: void 0 }; } const nodeData = this.createSkeletonNodeDatum(ctx, params, false); this.updateNodeDatum(ctx, nodeData, params, prepared); let phantomNodeData; if (prepared.yFilterValue != null) { phantomNodeData = this.createSkeletonNodeDatum(ctx, params, true); this.updateNodeDatum(ctx, phantomNodeData, params); } return { nodeData, phantomNodeData }; } /** * Updates an existing BarNodeDatum in-place for value-only changes. * This is more efficient than recreating the entire node when only data values change * but the structure (insertions/removals) remains the same. */ updateNodeDatum(ctx, node, params, prepared) { prepared ?? (prepared = this.prepareNodeDatumState( ctx, params.nodeDatumScratch, params.datumIndex, params.yStart, params.yEnd )); if (!prepared) { return; } const mutableNode = node; const phantom = node.phantom; const prevY = params.yStart; const yValue = phantom ? prepared.yFilterValue : prepared.yFilterValue ?? prepared.yRawValue; const cumulativeValue = phantom ? prepared.yFilterValue : prepared.yFilterValue ?? params.yEnd; const nodeLabelText = phantom ? void 0 : prepared.labelText; let currY; if (phantom) { currY = params.yEnd; } else if (prepared.yFilterValue == null) { currY = params.yEnd; } else { currY = params.yStart + prepared.yFilterValue; } let nodeYRange; if (phantom) { nodeYRange = params.yRange; } else { nodeYRange = Math.max(params.yStart + (prepared.yFilterValue ?? -Infinity), params.yRange); } let crossScale; if (phantom) { crossScale = void 0; } else if (prepared.inset) { crossScale = 0.6; } else { crossScale = void 0; } const isUpward = prepared.precomputedIsUpward ?? prepared.isPositive !== ctx.yReversed; const y = ctx.yScale.convert(currY); const bottomY = prepared.precomputedBottomY ?? ctx.yScale.convert(prevY); const bboxHeight = ctx.yScale.convert(nodeYRange); const xOffset = params.width * 0.5 * (1 - (crossScale ?? 1)); const rectX = ctx.barAlongX ? Math.min(y, bottomY) : params.x + xOffset; const rectY = ctx.barAlongX ? params.x + xOffset : Math.min(y, bottomY); const rectWidth = ctx.barAlongX ? Math.abs(bottomY - y) : params.width * (crossScale ?? 1); const rectHeight = ctx.barAlongX ? params.width * (crossScale ?? 1) : Math.abs(bottomY - y); const barRectX = ctx.barAlongX ? Math.min(ctx.bboxBottom, bboxHeight) : params.x + xOffset; const barRectY = ctx.barAlongX ? params.x + xOffset : Math.min(ctx.bboxBottom, bboxHeight); const barRectWidth = ctx.barAlongX ? Math.abs(ctx.bboxBottom - bboxHeight) : params.width * (crossScale ?? 1); const barRectHeight = ctx.barAlongX ? params.width * (crossScale ?? 1) : Math.abs(ctx.bboxBottom - bboxHeight); mutableNode.datum = prepared.datum; mutableNode.datumIndex = params.datumIndex; mutableNode.cumulativeValue = cumulativeValue; mutableNode.xValue = prepared.xValue; mutableNode.yValue = yValue; mutableNode.x = barRectX; mutableNode.y = barRectY; mutableNode.width = barRectWidth; mutableNode.height = barRectHeight; const mutableMidPoint = mutableNode.midPoint; mutableMidPoint.x = rectX + rectWidth / 2; mutableMidPoint.y = rectY + rectHeight / 2; const lengthRatioMultiplier = ctx.shouldFlipXY ? rectHeight : rectWidth; mutableNode.capDefaults.lengthRatioMultiplier = lengthRatioMultiplier; mutableNode.capDefaults.lengthMax = lengthRatioMultiplier; mutableNode.opacity = params.opacity; mutableNode.featherRatio = params.featherRatio; mutableNode.topLeftCornerRadius = ctx.barAlongX !== isUpward; mutableNode.topRightCornerRadius = isUpward; mutableNode.bottomRightCornerRadius = ctx.barAlongX === isUpward; mutableNode.bottomLeftCornerRadius = !isUpward; const existingClipBBox = mutableNode.clipBBox; if (existingClipBBox) { existingClipBBox.x = rectX; existingClipBBox.y = rectY; existingClipBBox.width = rectWidth; existingClipBBox.height = rectHeight; } else { mutableNode.clipBBox = new BBox(rectX, rectY, rectWidth, rectHeight); } mutableNode.crisp = ctx.crisp; if (nodeLabelText == null) { mutableNode.label = void 0; } else { const labelPlacement = adjustLabelPlacement({ isUpward, isVertical: !ctx.barAlongX, placement: ctx.label.placement, spacing: ctx.labelSpacing, rect: { x: rectX, y: rectY, width: rectWidth, height: rectHeight } }); const existingLabel = mutableNode.label; if (existingLabel) { existingLabel.text = nodeLabelText; existingLabel.x = labelPlacement.x; existingLabel.y = labelPlacement.y; existingLabel.textAlign = labelPlacement.textAlign; existingLabel.textBaseline = labelPlacement.textBaseline; } else { mutableNode.label = { text: nodeLabelText, ...labelPlacement }; } } mutableNode.missing = isTooltipValueMissing(yValue); } /** * Creates node data using aggregation filters for large datasets. */ createNodeDataWithAggregation(ctx, xPosition, nodeDatumParamsScratch) { const sign = ctx.yReversed ? -1 : 1; for (let p = 0; p < 2; p += 1) { const positive = p === 0; const indices = positive ? ctx.dataAggregationFilter.positiveIndices : ctx.dataAggregationFilter.negativeIndices; const indexData = positive ? ctx.dataAggregationFilter.positiveIndexData : ctx.dataAggregationFilter.negativeIndexData; const Y_MIN = positive ? AGGREGATION_INDEX_Y_MIN : AGGREGATION_INDEX_Y_MAX; const Y_MAX = positive ? AGGREGATION_INDEX_Y_MAX : AGGREGATION_INDEX_Y_MIN; const visibleRange = this.visibleRangeIndices("xValue", ctx.xAxis.range, indices); const start2 = visibleRange[0]; const end3 = visibleRange[1]; for (let i = start2; i < end3; i += 1) { const aggIndex = i * AGGREGATION_SPAN; const xMinIndex = indexData[aggIndex + AGGREGATION_INDEX_X_MIN]; const xMaxIndex = indexData[aggIndex + AGGREGATION_INDEX_X_MAX]; const yMinIndex = indexData[aggIndex + Y_MIN]; const yMaxIndex = indexData[aggIndex + Y_MAX]; if (xMinIndex === AGGREGATION_INDEX_UNSET) continue; if (ctx.xValues[yMaxIndex] == null || ctx.xValues[yMinIndex] == null) continue; const x = xPosition(Math.trunc((xMinIndex + xMaxIndex) / 2)); const width2 = Math.abs(xPosition(xMaxIndex) - xPosition(xMinIndex)) + ctx.barWidth; if (x - width2 < 0 || x > ctx.range) continue; const bandCount = Math.abs(xMaxIndex - xMinIndex) + 1; const opacity = BandScale.is(ctx.xScale) ? Math.min(ctx.xScale.bandwidth * Math.max(bandCount - 1, 1) / (ctx.xScale.step * bandCount), 1) : 1; nodeDatumParamsScratch.datumIndex = yMaxIndex; nodeDatumParamsScratch.x = x; nodeDatumParamsScratch.width = width2; nodeDatumParamsScratch.opacity = opacity; if (ctx.isStacked) { nodeDatumParamsScratch.yStart = Number(ctx.yStartValues[yMinIndex]); nodeDatumParamsScratch.yEnd = Number(ctx.yEndValues[yMaxIndex]); nodeDatumParamsScratch.featherRatio = 0; } else { const yEndMax = Number(ctx.yRawValues[yMaxIndex]); const yEndMin = Number(ctx.yRawValues[yMinIndex]); nodeDatumParamsScratch.yStart = 0; nodeDatumParamsScratch.yEnd = yEndMax; nodeDatumParamsScratch.featherRatio = (positive ? 1 : -1) * sign * (1 - yEndMin / yEndMax); } nodeDatumParamsScratch.yRange = nodeDatumParamsScratch.yEnd; this.upsertNodeDatum(ctx, nodeDatumParamsScratch); } } } /** * Creates node data for grouped data processing. */ createNodeDataGrouped(ctx, xPosition, nodeDatumParamsScratch) { const processedData = this.processedData; const invalidData = processedData.invalidData?.get(this.id); const width2 = ctx.barWidth; const yRangeIndex = ctx.isStacked ? this.dataModel.resolveProcessedDataIndexById(this, `yValue-range`) : -1; const columnIndex = processedData.columnScopes.findIndex((s) => s.has(this.id)); const groups = processedData.groups; const visibleRange = visibleRangeIndices(1, groups.length, ctx.xAxis.range, (groupIndex) => { const group = groups[groupIndex]; const xValue = group.keys[0]; return this.xCoordinateRange(xValue); }); const start2 = visibleRange[0]; const end3 = visibleRange[1]; for (let groupIndex = start2; groupIndex < end3; groupIndex += 1) { const group = groups[groupIndex]; const aggregation = group.aggregation; const datumIndices = group.datumIndices[columnIndex]; if (datumIndices == null) continue; for (const relativeDatumIndex of datumIndices) { const datumIndex = groupIndex + relativeDatumIndex; const x = xPosition(datumIndex); if (invalidData?.[datumIndex] === true) continue; const yRawValue = ctx.yRawValues[datumIndex]; if (yRawValue == null) continue; const isPositive = yRawValue >= 0 && !Object.is(yRawValue, -0); const yStart = ctx.isStacked ? Number(ctx.yStartValues?.[datumIndex]) : 0; const yEnd = ctx.isStacked ? Number(ctx.yEndValues?.[datumIndex]) : yRawValue; let yRange = yEnd; if (ctx.isStacked) { yRange = aggregation[yRangeIndex][isPositive ? 1 : 0]; } nodeDatumParamsScratch.datumIndex = datumIndex; nodeDatumParamsScratch.x = x; nodeDatumParamsScratch.width = width2; nodeDatumParamsScratch.yStart = yStart; nodeDatumParamsScratch.yEnd = yEnd; nodeDatumParamsScratch.yRange = yRange; nodeDatumParamsScratch.featherRatio = 0; nodeDatumParamsScratch.opacity = 1; this.upsertNodeDatum(ctx, nodeDatumParamsScratch); } } } /** * Creates node data for simple (non-grouped) data processing. */ createNodeDataSimple(ctx, xPosition, nodeDatumParamsScratch) { const invalidData = this.processedData.invalidData?.get(this.id); const width2 = ctx.barWidth; const visibleRange = this.visibleRangeIndices("xValue", ctx.xAxis.range); let start2 = visibleRange[0]; let end3 = visibleRange[1]; if (this.processedData.input.count < 1e3) { start2 = 0; end3 = this.processedData.input.count; } for (let datumIndex = start2; datumIndex < end3; datumIndex += 1) { if (invalidData?.[datumIndex] === true) continue; const yRawValue = ctx.yRawValues[datumIndex]; if (yRawValue == null) continue; const x = xPosition(datumIndex); const yEnd = Number(yRawValue); nodeDatumParamsScratch.datumIndex = datumIndex; nodeDatumParamsScratch.x = x; nodeDatumParamsScratch.width = width2; nodeDatumParamsScratch.yStart = 0; nodeDatumParamsScratch.yEnd = yEnd; nodeDatumParamsScratch.yRange = yEnd; nodeDatumParamsScratch.featherRatio = 0; nodeDatumParamsScratch.opacity = 1; this.upsertNodeDatum(ctx, nodeDatumParamsScratch); } } /** * Handles node creation/update - reuses existing nodes when possible for incremental updates. * This method decides whether to update existing nodes in-place or create new ones. */ upsertNodeDatum(ctx, params) { const canReuseNode = ctx.canIncrementallyUpdate && ctx.nodeIndex < ctx.nodes.length; const needsPhantom = ctx.yFilterValues != null; const canReusePhantom = needsPhantom && ctx.canIncrementallyUpdate && ctx.phantomIndex < ctx.phantomNodes.length; let nodeData; let phantomNodeData; if (canReuseNode) { nodeData = ctx.nodes[ctx.nodeIndex]; this.updateNodeDatum(ctx, nodeData, params); if (ctx.nodeIndex >= ctx.labels.length) { ctx.labels.push(nodeData); } } else { const result = this.createNodeDatum(ctx, params); if (result.nodeData) { ctx.nodes.push(result.nodeData); ctx.labels.push(result.nodeData); } phantomNodeData = result.phantomNodeData; } ctx.nodeIndex++; if (!needsPhantom) { return { nodeData: ctx.nodes[ctx.nodeIndex] }; } if (canReusePhantom) { phantomNodeData = ctx.phantomNodes[ctx.phantomIndex]; this.updateNodeDatum(ctx, phantomNodeData, params); } else if (phantomNodeData) { ctx.phantomNodes.push(phantomNodeData); } else { const result = this.createNodeDatum(ctx, params); if (result.phantomNodeData) { ctx.phantomNodes.push(result.phantomNodeData); } } ctx.phantomIndex++; return { nodeData, phantomNodeData }; } // ============================================================================ // Template Method Hooks (createNodeData flow) // ============================================================================ /** * Populates node data by selecting the appropriate strategy based on data type. * Creates scratch objects and delegates to strategy-specific methods. */ populateNodeData(ctx) { const xPosition = (index) => this.computeXPosition(ctx, index); const nodeDatumParamsScratch = { nodeDatumScratch: { datum: void 0, xValue: void 0, yRawValue: 0, yFilterValue: void 0, labelText: void 0, inset: false, isPositive: false, precomputedBottomY: void 0, precomputedIsUpward: void 0 }, datumIndex: 0, x: 0, width: 0, yStart: 0, yEnd: 0, yRange: 0, featherRatio: 0, opacity: 1 }; if (ctx.dataAggregationFilter != null) { this.createNodeDataWithAggregation(ctx, xPosition, nodeDatumParamsScratch); } else if (this.processedData.type === "grouped") { this.createNodeDataGrouped(ctx, xPosition, nodeDatumParamsScratch); } else { this.createNodeDataSimple(ctx, xPosition, nodeDatumParamsScratch); } } /** * Creates the initial result context object. * Note: segments is undefined here - it's computed in assembleResult. */ initializeResult(ctx) { return { itemId: this.properties.yKey, nodeData: ctx.nodes, phantomNodeData: ctx.phantomNodes, labelData: ctx.labels, scales: this.calculateScaling(), visible: this.visible || ctx.animationEnabled, groupScale: this.getScaling(this.ctx.seriesStateManager.getGroupScale(this)), styles: getItemStyles(this.getItemStyle.bind(this)), segments: void 0 }; } /** * Finalizes node data by trimming incremental arrays. * BarSeries has multiple arrays: nodes, phantomNodes, and labels. */ finalizeNodeData(ctx) { if (ctx.canIncrementallyUpdate) { this.trimIncrementalNodeArray(ctx.nodes, ctx.nodeIndex); this.trimIncrementalNodeArray(ctx.phantomNodes, ctx.phantomIndex); this.trimIncrementalNodeArray(ctx.labels, ctx.nodes.length); } } /** * Assembles the final result by computing segments. */ assembleResult(ctx, result) { result.segments = calculateSegments( this.properties.segmentation, ctx.xAxis, ctx.yAxis, this.chart.seriesRect, this.ctx.scene ); return result; } nodeFactory() { return new BarShape(); } updateSeriesSelections() { super.updateSeriesSelections(); this.phantomSelection = this.updateDatumSelection({ nodeData: this.contextNodeData?.phantomNodeData ?? [], datumSelection: this.phantomSelection }); } updateHighlightSelectionItem(opts) { const out = super.updateHighlightSelectionItem(opts); const highlightedDatum = this.ctx.highlightManager?.getActiveHighlight(); const seriesHighlighted = this.isSeriesHighlighted(highlightedDatum); const item = seriesHighlighted && highlightedDatum?.datum ? highlightedDatum : void 0; this.phantomHighlightSelection = this.updateDatumSelection({ nodeData: item ? this.getHighlightData(this.contextNodeData?.phantomNodeData ?? [], item) ?? [] : [], datumSelection: this.phantomHighlightSelection }); return out; } updateNodes(itemHighlighted, nodeRefresh) { super.updateNodes(itemHighlighted, nodeRefresh); this.updateDatumNodes({ datumSelection: this.phantomSelection, isHighlight: false, drawingMode: "overlay" }); this.updateDatumNodes({ datumSelection: this.phantomHighlightSelection, isHighlight: true, drawingMode: "overlay" }); } getHighlightData(nodeData, highlightedItem) { const highlightItem = nodeData.find((nodeDatum) => nodeDatum.datum === highlightedItem.datum); return highlightItem == null ? void 0 : [{ ...highlightItem }]; } updateDatumSelection(opts) { if (!processedDataIsAnimatable(this.processedData)) { return opts.datumSelection.update(opts.nodeData); } return opts.datumSelection.update(opts.nodeData, void 0, this.getDatumId.bind(this)); } makeStylerParams(highlightStateEnum) { const { id: seriesId } = this; const { cornerRadius, fill, fillOpacity, lineDash, lineDashOffset, stackGroup, stroke, strokeOpacity, strokeWidth, xKey, yKey } = this.properties; const highlightState = toHighlightString(highlightStateEnum ?? 0 /* None */); return { cornerRadius, fill, fillOpacity, highlightState, lineDash, lineDashOffset, seriesId, stackGroup, stroke, strokeOpacity, strokeWidth, xKey, yKey }; } makeItemStylerParams(dataModel, processedData, datumIndex, xValue, isHighlight, style2) { const { id: seriesId } = this; const { xKey, yKey, stackGroup } = this.properties; const datum = processedData.dataSources.get(seriesId)?.data?.[datumIndex]; const yValue = dataModel.resolveColumnById(this, `yValue-raw`, processedData)[datumIndex]; const xDomain = dataModel.getDomain(this, `xValue`, "key", processedData).domain; const yDomain = dataModel.getDomain(this, this.yCumulativeKey(dataModel), "value", processedData).domain; const activeHighlight = this.ctx.highlightManager?.getActiveHighlight(); const highlightStateString = this.getHighlightStateString(activeHighlight, isHighlight, datumIndex); const fill = this.filterItemStylerFillParams(style2.fill) ?? style2.fill; return { seriesId, ...datumStylerProperties(xValue, yValue, xKey, yKey, xDomain, yDomain), datum, xValue, yValue, stackGroup, highlightState: highlightStateString, ...style2, fill }; } getStyle(ignoreStylerCallback, highlightState) { const { cornerRadius, fill, fillOpacity, lineDash, lineDashOffset, stroke, strokeOpacity, strokeWidth, styler } = this.properties; let stylerResult = {}; if (!ignoreStylerCallback && styler) { const stylerParams = this.makeStylerParams(highlightState); stylerResult = this.ctx.optionsGraphService.resolvePartial( ["series", `${this.declarationOrder}`], this.cachedCallWithContext(styler, stylerParams) ?? {}, { pick: false } ) ?? {}; } return { cornerRadius: stylerResult.cornerRadius ?? cornerRadius, fill: stylerResult.fill ?? fill, fillOpacity: stylerResult.fillOpacity ?? fillOpacity, lineDash: stylerResult.lineDash ?? lineDash, lineDashOffset: stylerResult.lineDashOffset ?? lineDashOffset, opacity: 1, stroke: stylerResult.stroke ?? stroke, strokeOpacity: stylerResult.strokeOpacity ?? strokeOpacity, strokeWidth: stylerResult.strokeWidth ?? strokeWidth }; } getItemStyle(datumIndex, isHighlight, highlightState) { const { properties, dataModel, processedData } = this; const { itemStyler, simpleItemStyler } = properties; const highlightStyle = this.getHighlightStyle(isHighlight, datumIndex, highlightState); if (simpleItemStyler && processedData != null && datumIndex != null) { const datum = processedData.dataSources.get(this.id)?.data?.[datumIndex]; const overrides = simpleItemStyler(datum); return mergeDefaults( overrides, highlightStyle, this.getStyle(false, highlightState) ); } let style2 = mergeDefaults(highlightStyle, this.getStyle(datumIndex === void 0, highlightState)); if (itemStyler && dataModel != null && processedData != null && datumIndex != null) { const xValue = dataModel.resolveKeysById(this, `xValue`, processedData)[datumIndex]; const overrides = this.cachedDatumCallback( createDatumId(this.getDatumId({ xValue, phantom: false }), isHighlight ? "highlight" : "node"), () => { const params = this.makeItemStylerParams( dataModel, processedData, datumIndex, xValue, isHighlight, style2 ); return this.ctx.optionsGraphService.resolvePartial( ["series", `${this.declarationOrder}`], this.callWithContext(itemStyler, params) ); } ); if (overrides) { style2 = mergeDefaults(overrides, style2); } } return style2; } updateDatumStyles(opts) { const highlightedDatum = this.ctx.highlightManager.getActiveHighlight(); const series = this; function applyDatumStyle(node, datum) { if (!opts.datumSelection.isGarbage(node)) { const highlightState = series.getHighlightState(highlightedDatum, opts.isHighlight, datum.datumIndex); datum.style = series.getItemStyle(datum.datumIndex, opts.isHighlight, highlightState); } } opts.datumSelection.each(applyDatumStyle); } updateDatumNodes(opts) { const { contextNodeData } = this; if (!contextNodeData) { return; } const highlightedDatum = this.ctx.highlightManager.getActiveHighlight(); const { shadow } = this.properties; const categoryAlongX = this.getCategoryDirection() === "x" /* X */; const fillBBox = this.getShapeFillBBox(); const direction = this.getBarDirection(); const { drawingMode, isHighlight } = opts; const series = this; const contextStyles = contextNodeData.styles; function updateDatumNode(rect2, datum) { const style2 = datum.style ?? contextStyles[series.getHighlightState(highlightedDatum, isHighlight, datum.datumIndex)]; rect2.setStyleProperties(style2, fillBBox); const cornerRadius = style2.cornerRadius ?? 0; const visible = categoryAlongX ? (datum.clipBBox?.width ?? datum.width) > 0 : (datum.clipBBox?.height ?? datum.height) > 0; rect2.setStaticProperties( drawingMode, datum.topLeftCornerRadius ? cornerRadius : 0, datum.topRightCornerRadius ? cornerRadius : 0, datum.bottomRightCornerRadius ? cornerRadius : 0, datum.bottomLeftCornerRadius ? cornerRadius : 0, visible, datum.crisp, shadow, direction, datum.featherRatio ); } opts.datumSelection.each(updateDatumNode); } updateLabelSelection(opts) { const data = this.isLabelEnabled() ? opts.labelData : []; return opts.labelSelection.update(data, (text) => { text.pointerEvents = 1 /* None */; }); } updateLabelNodes(opts) { const { isHighlight = false } = opts; const params = { xKey: this.properties.xKey, xName: this.properties.xName ?? this.properties.xKey, yKey: this.properties.yKey, yName: this.properties.yName ?? this.properties.yKey, legendItemName: this.properties.legendItemName ?? this.properties.xName ?? this.properties.xKey }; const activeHighlight = this.ctx.highlightManager?.getActiveHighlight(); opts.labelSelection.each((textNode, datum) => { textNode.fillOpacity = this.getHighlightStyle(isHighlight, datum?.datumIndex).opacity ?? 1; updateLabelNode(this, textNode, params, this.properties.label, datum.label, isHighlight, activeHighlight); }); } getTooltipContent(datumIndex) { const { id: seriesId, dataModel, processedData, properties } = this; const { xKey, xName, yKey, yName, legendItemName, stackGroup, tooltip } = properties; const allowNullKeys = properties.allowNullKeys ?? false; const xAxis = this.getCategoryAxis(); const yAxis = this.getValueAxis(); if (!dataModel || !processedData || !xAxis || !yAxis) return; const datum = processedData.dataSources.get(this.id)?.data?.[datumIndex]; const xValue = dataModel.resolveKeysById(this, `xValue`, processedData)[datumIndex]; const yValue = dataModel.resolveColumnById(this, `yValue-raw`, processedData)[datumIndex]; if (xValue === void 0 && !allowNullKeys) return; const format = this.getItemStyle(datumIndex, false); return this.formatTooltipWithContext( tooltip, { heading: this.getAxisValueText(xAxis, "tooltip", xValue, datum, xKey, legendItemName, allowNullKeys), symbol: this.legendItemSymbol(), data: [ { label: yName, fallbackLabel: yKey, value: this.getAxisValueText(yAxis, "tooltip", yValue, datum, yKey, legendItemName), missing: isTooltipValueMissing(yValue) } ] }, { seriesId, datum, title: yName, xKey, xName, yKey, yName, legendItemName, stackGroup, ...format, ...this.getModuleTooltipParams() } ); } legendItemSymbol() { const { fill, stroke, strokeWidth, fillOpacity, strokeOpacity, lineDash, lineDashOffset } = this.getStyle( false, 0 /* None */ ); return { marker: { fill: fill ?? "rgba(0, 0, 0, 0)", stroke: stroke ?? "rgba(0, 0, 0, 0)", fillOpacity, strokeOpacity, strokeWidth, lineDash, lineDashOffset } }; } getLegendData(legendType) { const { showInLegend } = this.properties; if (legendType !== "category") { return []; } const { id: seriesId, ctx: { legendManager }, visible } = this; const { yKey: itemId, yName, legendItemName } = this.properties; return [ { legendType: "category", id: seriesId, itemId, seriesId, enabled: visible && legendManager.getItemEnabled({ seriesId, itemId }), label: { text: legendItemName ?? yName ?? itemId }, symbol: this.legendItemSymbol(), legendItemName, hideInLegend: !showInLegend } ]; } resetDatumAnimation(data) { resetBarSelectionsDirect([data.datumSelection, this.phantomSelection]); } animateReadyHighlight(data) { resetBarSelectionsDirect([data, this.phantomHighlightSelection]); } animateEmptyUpdateReady({ datumSelection, labelSelection, annotationSelections }) { const { phantomSelection } = this; const fns = prepareBarAnimationFunctions( collapsedStartingBarPosition(this.isVertical(), this.axes, "normal"), "unknown" ); fromToMotion(this.id, "nodes", this.ctx.animationManager, [datumSelection, phantomSelection], fns); seriesLabelFadeInAnimation(this, "labels", this.ctx.animationManager, labelSelection); seriesLabelFadeInAnimation(this, "annotations", this.ctx.animationManager, ...annotationSelections); } animateWaitingUpdateReady(data) { const { phantomSelection } = this; const { datumSelection, labelSelection, annotationSelections, contextData, previousContextData } = data; this.ctx.animationManager.stopByAnimationGroupId(this.id); const dataDiff = calculateDataDiff( this.id, datumSelection, this.getDatumId.bind(this), data.contextData, previousContextData, this.processedData, this.processedDataUpdated ); const mode = previousContextData == null ? "fade" : "normal"; const fns = prepareBarAnimationFunctions( collapsedStartingBarPosition(this.isVertical(), this.axes, mode), "added" ); fromToMotion( this.id, "nodes", this.ctx.animationManager, [datumSelection, phantomSelection], fns, (_, datum) => this.getDatumId(datum), dataDiff ); if (!dataDiff || dataDiff?.changed || !areScalingEqual(contextData.groupScale, previousContextData?.groupScale)) { seriesLabelFadeInAnimation(this, "labels", this.ctx.animationManager, labelSelection); seriesLabelFadeInAnimation(this, "annotations", this.ctx.animationManager, ...annotationSelections); } } getDatumId(datum) { return createDatumId(datum.xValue, datum.phantom); } isLabelEnabled() { return this.properties.label.enabled; } computeFocusBounds({ datumIndex }) { const datumBox = this.contextNodeData?.nodeData[datumIndex].clipBBox; return computeBarFocusBounds(this, datumBox); } hasItemStylers() { return this.properties.styler != null || this.properties.itemStyler != null || this.properties.simpleItemStyler != null || this.properties.label.itemStyler != null; } }; BarSeries.className = "BarSeries"; BarSeries.type = "bar"; // packages/ag-charts-community/src/chart/series/cartesian/barSeriesModule.ts var themeTemplate2 = { series: { direction: "vertical", fill: { $applySwitch: [ { $path: "type" }, { $palette: "fill" }, ["gradient", FILL_GRADIENT_LINEAR_DEFAULTS], ["image", FILL_IMAGE_DEFAULTS], ["pattern", FILL_PATTERN_DEFAULTS] ] }, stroke: { $palette: "stroke" }, fillOpacity: 1, strokeWidth: { $isUserOption: ["./stroke", 2, 0] }, lineDash: [0], lineDashOffset: 0, label: { ...LABEL_BOXING_DEFAULTS, padding: { $isUserOption: ["./spacing", 0, 8] }, // compatibility with old `padding` property (now named `spacing`). enabled: false, fontWeight: { $ref: "fontWeight" }, fontSize: { $ref: "fontSize" }, fontFamily: { $ref: "fontFamily" }, color: { $if: [ { $or: [ { $eq: [{ $path: "./placement" }, "outside-start"] }, { $eq: [{ $path: "./placement" }, "outside-end"] } ] }, { $ref: "textColor" }, { $ref: "chartBackgroundColor" } ] }, placement: "inside-center" }, shadow: { enabled: false, color: DEFAULT_SHADOW_COLOUR, xOffset: 3, yOffset: 3, blur: 5 }, highlight: MULTI_SERIES_HIGHLIGHT_STYLE, segmentation: SEGMENTATION_DEFAULTS } }; var BarSeriesModule = { type: "series", name: "bar", chartType: "cartesian", stackable: true, groupable: true, version: VERSION, dependencies: [CartesianChartModule], options: barSeriesOptionsDef, predictAxis: predictCartesianNonPrimitiveAxis, defaultAxes: DIRECTION_SWAP_AXES, axisKeys: { ["x" /* X */]: "xKeyAxis", ["y" /* Y */]: "yKeyAxis" }, axisKeysFlipped: { ["x" /* X */]: "yKeyAxis", ["y" /* Y */]: "xKeyAxis" }, themeTemplate: themeTemplate2, create: (ctx) => new BarSeries(ctx) }; // packages/ag-charts-community/src/chart/series/cartesian/bubbleAggregation.ts var SIZE_QUANTIZATION = 3; var FILTER_DATUM_THRESHOLD = 5; var FILTER_RANGE_THRESHOLD = 0.05; function getPrimaryDatumIndex(context, indices, bounds) { const { xValues, yValues, xDomain, yDomain, xNeedsValueOf, yNeedsValueOf } = context; const { x0, y0, x1, y1 } = bounds; let currentIndex = 0; let currentDistanceSquared = Infinity; const midX = (x0 + x1) / 2; const midY = (y0 + y1) / 2; for (const datumIndex of indices) { const xValue = xValues[datumIndex]; const yValue = yValues[datumIndex]; if (xValue == null || yValue == null) continue; const xRatio = aggregationXRatioForXValue(xValue, xDomain.min, xDomain.max, xNeedsValueOf); const yRatio = aggregationXRatioForXValue(yValue, yDomain.min, yDomain.max, yNeedsValueOf); const distanceSquared2 = (xRatio - midX) ** 2 + (yRatio - midY) ** 2; if (distanceSquared2 < currentDistanceSquared) { currentDistanceSquared = distanceSquared2; currentIndex = datumIndex; } } return currentIndex; } function countVisibleItems(context, indices, bounds) { const { xValues, yValues, xDomain, yDomain, xNeedsValueOf, yNeedsValueOf } = context; const { x0, y0, x1, y1 } = bounds; let count = 0; for (const datumIndex of indices) { const xValue = xValues[datumIndex]; const yValue = yValues[datumIndex]; if (xValue == null || yValue == null) continue; const xRatio = aggregationXRatioForXValue(xValue, xDomain.min, xDomain.max, xNeedsValueOf); const yRatio = aggregationXRatioForXValue(yValue, yDomain.min, yDomain.max, yNeedsValueOf); if (xRatio >= x0 && xRatio <= x1 && yRatio >= y0 && yRatio <= y1) { count += 1; } } return count; } function quadChildren(context, indices, bounds) { const { xValues, yValues, xDomain, yDomain, xNeedsValueOf, yNeedsValueOf } = context; const { x0, y0, x1, y1 } = bounds; const childBuckets = [ { x0: 1, y0: 1, x1: 0, y1: 0, indices: [] }, { x0: 1, y0: 1, x1: 0, y1: 0, indices: [] }, { x0: 1, y0: 1, x1: 0, y1: 0, indices: [] }, { x0: 1, y0: 1, x1: 0, y1: 0, indices: [] } ]; const midX = (x0 + x1) / 2; const midY = (y0 + y1) / 2; for (const datumIndex of indices) { const xValue = xValues[datumIndex]; const yValue = yValues[datumIndex]; if (xValue == null || yValue == null) continue; const xRatio = aggregationXRatioForXValue(xValue, xDomain.min, xDomain.max, xNeedsValueOf); const yRatio = aggregationXRatioForXValue(yValue, yDomain.min, yDomain.max, yNeedsValueOf); const childIndex = (xRatio > midX ? 1 : 0) + (yRatio > midY ? 2 : 0); const childBucket = childBuckets[childIndex]; childBucket.indices.push(datumIndex); childBucket.x0 = Math.min(childBucket.x0, xRatio); childBucket.y0 = Math.min(childBucket.y0, yRatio); childBucket.x1 = Math.max(childBucket.x1, xRatio); childBucket.y1 = Math.max(childBucket.y1, yRatio); } const children = []; for (const childBucket of childBuckets) { const { indices: childIndices, x0: cx0, x1: cx1, y0: cy0, y1: cy1 } = childBucket; if (childIndices.length === 0) continue; const child = aggregateQuad(context, childIndices, { x0: cx0, y0: cy0, x1: cx1, y1: cy1 }); children.push(child); } return children; } function aggregateQuad(context, indices, bounds) { const { x0, y0, x1, y1 } = bounds; const terminate = indices.length < FILTER_DATUM_THRESHOLD && x1 - x0 < FILTER_RANGE_THRESHOLD && y1 - y0 < FILTER_RANGE_THRESHOLD || x0 === x1 && y0 === y1; let children = terminate ? null : quadChildren(context, indices, bounds); if (children?.length === 1) { return children[0]; } else if (children?.length === 0) { children = null; } const scale2 = Math.hypot(x1 - x0, y1 - y0); const primaryDatumIndex = getPrimaryDatumIndex(context, indices, bounds); return { scale: scale2, x0, y0, x1, y1, indices, primaryDatumIndex, children }; } function computeBubbleAggregation(xDomain, yDomain, xValues, yValues, sizeValues, sizeDomain, options) { const [xd0, xd1] = xDomain; const [yd0, yd1] = yDomain; const [sd0, sd1] = sizeDomain; const { xNeedsValueOf, yNeedsValueOf } = options; const context = { xValues, yValues, xDomain: { min: xd0, max: xd1 }, yDomain: { min: yd0, max: yd1 }, xNeedsValueOf, yNeedsValueOf }; const filters = []; if (sizeValues != null && sd1 > sd0) { const sizeIndices = Array.from({ length: SIZE_QUANTIZATION }, () => []); for (let datumIndex = 0; datumIndex < sizeValues.length; datumIndex += 1) { const sizeValue = sizeValues[datumIndex]; const sizeRatio = (sizeValue - sd0) / (sd1 - sd0); const sizeIndex = Math.trunc(sizeRatio * SIZE_QUANTIZATION); if (sizeIndex >= 0 && sizeIndex < SIZE_QUANTIZATION) { sizeIndices[sizeIndex].push(datumIndex); } } for (let i = 0; i < sizeIndices.length; i += 1) { const indices = sizeIndices[i]; const node = aggregateQuad(context, indices, { x0: 0, y0: 0, x1: 1, y1: 1 }); if (node != null) { const sizeRatio = i / SIZE_QUANTIZATION; filters.push({ sizeRatio, node }); } } } else { const indices = xValues.map((_, i) => i); const node = aggregateQuad(context, indices, { x0: 0, y0: 0, x1: 1, y1: 1 }); if (node != null) { filters.push({ sizeRatio: 0, node }); } } return filters.length > 0 ? { xValues, yValues, xd0, xd1, yd0, yd1, filters, xNeedsValueOf, yNeedsValueOf } : void 0; } function aggregateBubbleData(xScale, yScale, xValues, yValues, sizeValues, xDomainInput, yDomainInput, sizeDomain, xNeedsValueOf, yNeedsValueOf) { const [xd0, xd1] = aggregationDomain(xScale, xDomainInput); const [yd0, yd1] = aggregationDomain(yScale, yDomainInput); return computeBubbleAggregation( [xd0, xd1], [yd0, yd1], xValues, yValues, sizeValues, [sizeDomain[0], sizeDomain[1]], { xNeedsValueOf, yNeedsValueOf } ); } function aggregateBubbleDataFromDataModel(xScale, yScale, dataModel, processedData, sizeScale, hasSizeKey, series) { const xValues = dataModel.resolveColumnById(series, "xValue", processedData); const yValues = dataModel.resolveColumnById(series, "yValue", processedData); const sizeValues = hasSizeKey ? dataModel.resolveColumnById(series, "sizeValue", processedData) : void 0; const xDomain = dataModel.getDomain(series, "xValue", "value", processedData); const yDomain = dataModel.getDomain(series, "yValue", "value", processedData); const sizeDomain = hasSizeKey ? sizeScale.domain : [0, 0]; const xNeedsValueOf = dataModel.resolveColumnNeedsValueOf(series, "xValue", processedData); const yNeedsValueOf = dataModel.resolveColumnNeedsValueOf(series, "yValue", processedData); return aggregateBubbleData( xScale, yScale, xValues, yValues, sizeValues, xDomain, yDomain, sizeDomain, xNeedsValueOf, yNeedsValueOf ); } function computeBubbleAggregationCountIndices(dilation, dataAggregation, options, counter, groupedAggregation, singleDatumIndices) { const { xRange, yRange, xVisibleRange: [xvr0, xvr1], yVisibleRange: [yvr0, yvr1], minSize, maxSize } = options; const { xValues, yValues, xd0, xd1, yd0, yd1, xNeedsValueOf, yNeedsValueOf } = dataAggregation; const baseScalingFactor = 1 / Math.min(xRange / (xvr1 - xvr0), yRange / (yvr1 - yvr0)); const context = { xValues, yValues, xDomain: { min: xd0, max: xd1 }, yDomain: { min: yd0, max: yd1 }, xNeedsValueOf, yNeedsValueOf }; for (const { sizeRatio, node } of dataAggregation.filters) { const radius = 0.5 * (minSize + sizeRatio * (maxSize - minSize)); const baseMinScale = radius * baseScalingFactor; const minScale = dilation * baseMinScale; const x0 = xvr0 - radius / xRange; const x1 = xvr1 + radius / xRange; const y0 = yvr0 - radius / yRange; const y1 = yvr1 + radius / yRange; const queue = [node]; while (queue.length > 0) { const item = queue.pop(); if (item.x1 < x0 || item.x0 > x1 || item.y1 < y0 || item.y0 > y1) { continue; } if (dilation !== 1 && item.scale <= minScale) { if (counter != null) { counter.count += 1; } groupedAggregation?.push({ datumIndex: item.primaryDatumIndex, count: item.indices.length, area: (item.x1 - item.x0) * (item.y1 - item.y0), dilation: clamp(1, item.scale / baseMinScale, dilation) }); } else if (item.children == null) { const { indices } = item; if (counter != null) { const fullyVisible = item.x0 >= xvr0 && item.x1 <= xvr1 && item.y0 >= yvr0 && item.y1 <= yvr1; const itemCount = fullyVisible ? indices.length : countVisibleItems(context, indices, { x0: xvr0, y0: yvr0, x1: xvr1, y1: yvr1 }); counter.count += itemCount; } singleDatumIndices?.push(...indices); } else { queue.push(...item.children); } } } } function computeBubbleAggregationCount(dilation, dataAggregation, options) { const counter = { count: 0 }; computeBubbleAggregationCountIndices(dilation, dataAggregation, options, counter, void 0, void 0); return counter.count; } var MAX_AGGREGATION_DILATION = 100; var DILATION_ITERATIONS = 12; function computeBubbleAggregationDilation(dataAggregation, aggregationOptions, maxRenderedItems) { if (computeBubbleAggregationCount(1, dataAggregation, aggregationOptions) <= maxRenderedItems) { return 1; } let minDilation = 1; let maxDilation = 2; while (computeBubbleAggregationCount(maxDilation, dataAggregation, aggregationOptions) > maxRenderedItems && maxDilation < MAX_AGGREGATION_DILATION) { minDilation *= 2; maxDilation *= 2; } for (let i = 0; i < DILATION_ITERATIONS; i += 1) { const dilation = (maxDilation + minDilation) / 2; const count = computeBubbleAggregationCount(dilation, dataAggregation, aggregationOptions); if (count > maxRenderedItems) { minDilation = dilation; } else { maxDilation = dilation; } } return (minDilation + maxDilation) / 2; } function computeBubbleAggregationData(dilation, dataAggregation, options) { const groupedAggregation = []; const singleDatumIndices = []; computeBubbleAggregationCountIndices( dilation, dataAggregation, options, void 0, groupedAggregation, singleDatumIndices ); return { groupedAggregation, singleDatumIndices }; } // packages/ag-charts-community/src/chart/series/cartesian/bubbleSeriesProperties.ts var BubbleSeriesMarker = class extends SeriesMarker { constructor() { super(...arguments); this.maxSize = 30; } }; __decorateClass([ addFakeTransformToInstanceProperty, SceneChangeDetection() ], BubbleSeriesMarker.prototype, "maxSize", 2); __decorateClass([ addFakeTransformToInstanceProperty, SceneArrayChangeDetection() ], BubbleSeriesMarker.prototype, "domain", 2); var BubbleSeriesLabel = class extends Label { constructor() { super(...arguments); this.placement = "top"; } }; __decorateClass([ addFakeTransformToInstanceProperty ], BubbleSeriesLabel.prototype, "placement", 2); var BubbleSeriesProperties = class extends CartesianSeriesProperties { constructor() { super(...arguments); this.label = new BubbleSeriesLabel(); this.tooltip = makeSeriesTooltip(); this.maxRenderedItems = Infinity; // No validation. Not a part of the options contract. this.marker = new BubbleSeriesMarker(); } }; __decorateClass([ addFakeTransformToInstanceProperty ], BubbleSeriesProperties.prototype, "xKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BubbleSeriesProperties.prototype, "yKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BubbleSeriesProperties.prototype, "sizeKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BubbleSeriesProperties.prototype, "labelKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BubbleSeriesProperties.prototype, "xFilterKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BubbleSeriesProperties.prototype, "yFilterKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BubbleSeriesProperties.prototype, "sizeFilterKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BubbleSeriesProperties.prototype, "xName", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BubbleSeriesProperties.prototype, "yName", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BubbleSeriesProperties.prototype, "sizeName", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BubbleSeriesProperties.prototype, "labelName", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BubbleSeriesProperties.prototype, "title", 2); __decorateClass([ ProxyProperty("marker.shape") ], BubbleSeriesProperties.prototype, "shape", 2); __decorateClass([ ProxyProperty("marker.size") ], BubbleSeriesProperties.prototype, "size", 2); __decorateClass([ ProxyProperty("marker.maxSize") ], BubbleSeriesProperties.prototype, "maxSize", 2); __decorateClass([ ProxyProperty("marker.domain") ], BubbleSeriesProperties.prototype, "domain", 2); __decorateClass([ ProxyProperty("marker.fill") ], BubbleSeriesProperties.prototype, "fill", 2); __decorateClass([ ProxyProperty("marker.fillOpacity") ], BubbleSeriesProperties.prototype, "fillOpacity", 2); __decorateClass([ ProxyProperty("marker.stroke") ], BubbleSeriesProperties.prototype, "stroke", 2); __decorateClass([ ProxyProperty("marker.strokeWidth") ], BubbleSeriesProperties.prototype, "strokeWidth", 2); __decorateClass([ ProxyProperty("marker.strokeOpacity") ], BubbleSeriesProperties.prototype, "strokeOpacity", 2); __decorateClass([ ProxyProperty("marker.lineDash") ], BubbleSeriesProperties.prototype, "lineDash", 2); __decorateClass([ ProxyProperty("marker.lineDashOffset") ], BubbleSeriesProperties.prototype, "lineDashOffset", 2); __decorateClass([ ProxyProperty("marker.itemStyler") ], BubbleSeriesProperties.prototype, "itemStyler", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BubbleSeriesProperties.prototype, "styler", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BubbleSeriesProperties.prototype, "label", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BubbleSeriesProperties.prototype, "tooltip", 2); __decorateClass([ addFakeTransformToInstanceProperty ], BubbleSeriesProperties.prototype, "maxRenderedItems", 2); // packages/ag-charts-community/src/chart/series/cartesian/cartesianSeriesUtil.ts function upsertNodeDatum(ctx, params, createNode, updateNode) { const canReuseNode = ctx.canIncrementallyUpdate && ctx.nodeIndex < ctx.nodes.length; let node; if (canReuseNode) { node = ctx.nodes[ctx.nodeIndex]; updateNode(ctx, node, params); } else { node = createNode(ctx, params); if (node != null) { ctx.nodes.push(node); } } ctx.nodeIndex++; return node; } // packages/ag-charts-community/src/chart/series/cartesian/bubbleSeries.ts var BubbleScatterSeriesNodeEvent = class extends CartesianSeriesNodeEvent { constructor(type, nativeEvent, datum, series) { super(type, nativeEvent, datum, series); this.sizeKey = series.properties.sizeKey; } }; var BubbleSeries = class extends CartesianSeries { constructor(moduleCtx) { super({ moduleCtx, propertyKeys: { ...DEFAULT_CARTESIAN_DIRECTION_KEYS, label: ["labelKey"], size: ["sizeKey"] }, propertyNames: { ...DEFAULT_CARTESIAN_DIRECTION_NAMES, label: ["labelName"], size: ["sizeName"] }, categoryKey: void 0, pickModes: [ 2 /* AXIS_ALIGNED */, 1 /* NEAREST_NODE */, 0 /* EXACT_SHAPE_MATCH */ ], pathsPerSeries: [], datumSelectionGarbageCollection: false, animationResetFns: { label: resetLabelFn, datum: resetMarkerFn }, usesPlacedLabels: true, clipFocusBox: false }); this.NodeEvent = BubbleScatterSeriesNodeEvent; this.properties = new BubbleSeriesProperties(); this.dataAggregation = void 0; this.sizeScale = new LinearScale(); this.placedLabelData = []; } get pickModeAxis() { return "main-category"; } get type() { return super.type; } async processData(dataController) { if (this.data == null || !this.visible) return; const xScale = this.axes["x" /* X */]?.scale; const yScale = this.axes["y" /* Y */]?.scale; const { xScaleType, yScaleType } = this.getScaleInformation({ xScale, yScale }); const sizeScaleType = this.sizeScale.type; const { xKey, yKey, sizeKey, xFilterKey, yFilterKey, sizeFilterKey, labelKey, marker } = this.properties; const allowNullKey = this.properties.allowNullKeys ?? false; const { dataModel, processedData } = await this.requestDataModel(dataController, this.data, { props: [ valueProperty(xKey, xScaleType, { id: `xValue`, allowNullKey }), valueProperty(yKey, yScaleType, { id: `yValue`, allowNullKey }), ...xFilterKey == null ? [] : [valueProperty(xFilterKey, xScaleType, { id: `xFilterValue` })], ...yFilterKey == null ? [] : [valueProperty(yFilterKey, yScaleType, { id: `yFilterValue` })], ...sizeFilterKey == null ? [] : [valueProperty(sizeFilterKey, sizeScaleType, { id: `sizeFilterValue` })], ...sizeKey ? [valueProperty(sizeKey, sizeScaleType, { id: `sizeValue` })] : [], ...labelKey ? [valueProperty(labelKey, "category", { id: `labelValue` })] : [] ] }); const sizeKeyIdx = sizeKey ? dataModel.resolveProcessedDataIndexById(this, `sizeValue`) : void 0; const mutableMarkerDomain = marker.domain ? [marker.domain[0], marker.domain[1]] : void 0; this.sizeScale.domain = mutableMarkerDomain ?? (sizeKeyIdx == null ? void 0 : processedData.domain.values[sizeKeyIdx]) ?? []; this.dataAggregation = this.aggregateData(dataModel, processedData); this.animationState.transition("updateData"); } xCoordinateRange(xValue, pixelSize, index) { const { properties, sizeScale } = this; const { size, sizeKey } = properties; const x = this.axes["x" /* X */].scale.convert(xValue); const sizeValues = sizeKey == null ? void 0 : this.dataModel.resolveColumnById(this, `sizeValue`, this.processedData); const sizeValue = sizeValues == null ? size : sizeScale.convert(sizeValues[index]); const r = 0.5 * sizeValue * pixelSize; return [x - r, x + r]; } yCoordinateRange(yValues, pixelSize, index) { const { properties, sizeScale } = this; const { size, sizeKey } = properties; const y = this.axes["y" /* Y */].scale.convert(yValues[0]); const sizeValues = sizeKey == null ? void 0 : this.dataModel.resolveColumnById(this, `sizeValue`, this.processedData); const sizeValue = sizeValues == null ? size : sizeScale.convert(sizeValues[index]); const r = 0.5 * sizeValue * pixelSize; return [y - r, y + r]; } getSeriesDomain(direction) { const { dataModel, processedData } = this; if (!processedData || !dataModel) return { domain: [] }; const dataValues = { ["x" /* X */]: "xValue", ["y" /* Y */]: "yValue" }; const id = dataValues[direction]; const dataDef = dataModel.resolveProcessedDataDefById(this, id); const domainData = dataModel.getDomain(this, id, "value", processedData); if (dataDef?.def.type === "value" && dataDef?.def.valueType === "category") { return { domain: domainData.domain }; } const crossDirection = direction === "x" /* X */ ? "y" /* Y */ : "x" /* X */; const crossId = dataValues[crossDirection]; const ext = this.domainForClippedRange(direction, [id], crossId); return { domain: fixNumericExtent(extent(ext)) }; } getSeriesRange(_direction, visibleRange) { return this.domainForVisibleRange("y" /* Y */, ["yValue"], "xValue", visibleRange); } getVisibleItems(xVisibleRange, yVisibleRange, minVisibleItems) { const { dataAggregation, axes } = this; const xAxis = axes["x" /* X */]; const yAxis = axes["y" /* Y */]; if (dataAggregation == null || xAxis == null || yAxis == null) { return this.countVisibleItems("xValue", ["yValue"], xVisibleRange, yVisibleRange, minVisibleItems); } const aggregationOptions = this.aggregationOptions(xAxis, yAxis, xVisibleRange, yVisibleRange ?? [0, 1]); return computeBubbleAggregationCount(0, dataAggregation, aggregationOptions); } aggregateData(dataModel, processedData) { if (processedData.type === "grouped") return; if (processedData.input.count <= this.properties.maxRenderedItems) return; const xAxis = this.axes["x" /* X */]; const yAxis = this.axes["y" /* Y */]; if (xAxis == null || yAxis == null) return; const xScale = xAxis.scale; const yScale = yAxis.scale; if (!ContinuousScale.is(xScale) || !ContinuousScale.is(yScale)) return; return aggregateBubbleDataFromDataModel( xScale.type, yScale.type, dataModel, processedData, this.sizeScale, this.properties.sizeKey != null, this ); } aggregationOptions(xAxis, yAxis, xVisibleRange = xAxis.visibleRange, yVisibleRange = yAxis.visibleRange) { const { processedData, dataModel } = this; const { sizeKey } = this.properties; const [markerSize, markerMaxSize] = this.getSizeRange(); const xRange = Math.abs(xAxis.range[1] - xAxis.range[0]); const yRange = Math.abs(yAxis.range[1] - yAxis.range[0]); const minSize = Math.max(markerSize, 1); const maxSize = sizeKey ? Math.max(markerMaxSize, 1) : minSize; const xScale = xAxis.scale; const yScale = yAxis.scale; if (processedData != null && dataModel != null) { if (ContinuousScale.is(xScale)) { xVisibleRange = rescaleVisibleRange( xVisibleRange, xScale.domain.map(dateToNumber), dataModel.getDomain(this, `xValue`, "value", processedData).domain.map(dateToNumber) ); } if (ContinuousScale.is(yScale)) { yVisibleRange = rescaleVisibleRange( yVisibleRange, yScale.domain.map(dateToNumber), dataModel.getDomain(this, `yValue`, "value", processedData).domain.map(dateToNumber) ); } } return { xRange, yRange, minSize, maxSize, xVisibleRange, yVisibleRange }; } /** * Creates and returns a context object that caches expensive property lookups * and scale conversions. Called once per createNodeData() invocation. */ createNodeDatumContext(xAxis, yAxis) { const { dataModel, processedData, sizeScale, visible } = this; if (!dataModel || !processedData) return void 0; const rawData = processedData.dataSources.get(this.id)?.data; if (rawData == null) return void 0; const { xKey, yKey, sizeKey, xFilterKey, yFilterKey, sizeFilterKey, labelKey, xName, yName, sizeName, labelName, label, legendItemName, marker } = this.properties; const xScale = xAxis.scale; const yScale = yAxis.scale; const canIncrementallyUpdate = processedData.changeDescription != null && this.contextNodeData?.nodeData != null; let labelTextDomain; if (labelKey) { labelTextDomain = []; } else if (sizeKey) { labelTextDomain = dataModel.getDomain(this, `sizeValue`, "value", processedData).domain; } else { labelTextDomain = []; } const xDataValues = dataModel.resolveColumnById(this, `xValue`, processedData); return { // Axes (from template method parameters) xAxis, yAxis, // Data arrays rawData, xValues: xDataValues, // Base interface field xDataValues, // BubbleSeries-specific alias yDataValues: dataModel.resolveColumnById(this, `yValue`, processedData), sizeDataValues: sizeKey == null ? void 0 : dataModel.resolveColumnById(this, `sizeValue`, processedData), labelDataValues: labelKey == null ? void 0 : dataModel.resolveColumnById(this, `labelValue`, processedData), xFilterDataValues: xFilterKey == null ? void 0 : dataModel.resolveColumnById(this, `xFilterValue`, processedData), yFilterDataValues: yFilterKey == null ? void 0 : dataModel.resolveColumnById(this, `yFilterValue`, processedData), sizeFilterDataValues: sizeFilterKey == null ? void 0 : dataModel.resolveColumnById(this, `sizeFilterValue`, processedData), // Scales xScale, yScale, sizeScale, // Computed positioning xOffset: (xScale.bandwidth ?? 0) / 2, yOffset: (yScale.bandwidth ?? 0) / 2, // Property lookups xKey, yKey, sizeKey, labelKey, xName, yName, sizeName, labelName, legendItemName, // Label properties labelsEnabled: label.enabled, labelPlacement: label.placement, labelAnchor: Marker.anchor(marker.shape), labelTextDomain, labelPadding: expandLabelPadding(label), labelTextMeasurer: cachedTextMeasurer(label), label, // Other state animationEnabled: !this.ctx.animationManager.isSkipped(), visible, // Incremental update support canIncrementallyUpdate, nodes: canIncrementallyUpdate ? this.contextNodeData.nodeData : [], nodeIndex: 0 }; } // ============================================================================ // Template Method Hooks // ============================================================================ /** * Populates the node data array by iterating over visible data. * Strategy selection happens inside: simple or aggregation path. */ populateNodeData(ctx) { this.sizeScale.range = this.getSizeRange(); const scratch = { datum: void 0, xDatum: void 0, yDatum: void 0, sizeValue: void 0, x: 0, y: 0, selected: void 0, nodeLabel: { text: "", width: 0, height: 0 }, markerSize: 0, count: 1, dilation: 1, area: 0 }; const { dataAggregation } = this; if (dataAggregation == null) { this.createNodeDataSimple(ctx, scratch); } else { this.createNodeDataWithAggregation(ctx, scratch, ctx.xAxis, ctx.yAxis, dataAggregation); } } /** * Initializes the result context object with default values. * Called before populate phase to allow early return for invisible series. */ initializeResult(ctx) { const { marker } = this.properties; return { itemId: ctx.yKey, nodeData: ctx.nodes, labelData: ctx.labelsEnabled ? ctx.nodes : [], scales: this.calculateScaling(), visible: this.visible || ctx.animationEnabled, styles: getMarkerStyles(this, this.properties, marker) }; } /** * Validates datum state and upserts node - centralizes duplicated upsert pattern. */ upsertBubbleNodeDatum(ctx, scratch, datumIndex) { if (!this.prepareNodeDatumState(ctx, scratch, datumIndex)) return; upsertNodeDatum( ctx, { scratch, datumIndex }, (c, p) => { const node = this.createSkeletonNodeDatum(c, p.scratch, p.datumIndex); this.updateNodeDatum(c, node, p.scratch, p.datumIndex); return node; }, (c, n, p) => this.updateNodeDatum(c, n, p.scratch, p.datumIndex) ); } /** * Simple iteration path for ungrouped data without aggregation. */ createNodeDataSimple(ctx, scratch) { const dataLength = ctx.rawData.length; for (let datumIndex = 0; datumIndex < dataLength; datumIndex++) { scratch.count = 1; scratch.dilation = 1; scratch.area = 0; this.upsertBubbleNodeDatum(ctx, scratch, datumIndex); } } /** * Aggregation path for large datasets using quadtree-based 2D spatial aggregation. */ createNodeDataWithAggregation(ctx, scratch, xAxis, yAxis, dataAggregation) { const { maxRenderedItems } = this.properties; const aggregationOptions = this.aggregationOptions(xAxis, yAxis); const aggregationDilation = computeBubbleAggregationDilation( dataAggregation, aggregationOptions, maxRenderedItems ); const { groupedAggregation, singleDatumIndices } = computeBubbleAggregationData( aggregationDilation, dataAggregation, aggregationOptions ); for (const { datumIndex, count, dilation, area: area2 } of groupedAggregation) { scratch.count = count; scratch.dilation = dilation; scratch.area = area2; this.upsertBubbleNodeDatum(ctx, scratch, datumIndex); } for (const datumIndex of singleDatumIndices) { scratch.count = 1; scratch.dilation = 1; scratch.area = 0; this.upsertBubbleNodeDatum(ctx, scratch, datumIndex); } } /** * Validates and prepares state needed for node creation/update. * Returns undefined if datum should be skipped (invalid data). */ prepareNodeDatumState(ctx, scratch, datumIndex) { const datum = ctx.rawData[datumIndex]; const xDatum = ctx.xDataValues[datumIndex]; const yDatum = ctx.yDataValues[datumIndex]; const allowNullKeys = this.properties.allowNullKeys ?? false; if ((xDatum === void 0 || yDatum === void 0) && !allowNullKeys) return void 0; const sizeValue = ctx.sizeDataValues?.[datumIndex]; const x = ctx.xScale.convert(xDatum) + ctx.xOffset; const y = ctx.yScale.convert(yDatum) + ctx.yOffset; if (!Number.isFinite(x) || !Number.isFinite(y)) return void 0; let selected; if (ctx.xFilterDataValues != null && ctx.yFilterDataValues != null) { selected = ctx.xFilterDataValues[datumIndex] === xDatum && ctx.yFilterDataValues[datumIndex] === yDatum; if (ctx.sizeFilterDataValues != null) { selected && (selected = ctx.sizeFilterDataValues[datumIndex] === sizeValue); } } let nodeLabel; if (ctx.labelsEnabled) { nodeLabel = this.computeLabel(ctx, datum, yDatum, sizeValue, datumIndex); } else { nodeLabel = { text: "", width: 0, height: 0 }; } const markerSize = sizeValue == null ? ctx.sizeScale.range[0] : ctx.sizeScale.convert(sizeValue); scratch.datum = datum; scratch.xDatum = xDatum; scratch.yDatum = yDatum; scratch.sizeValue = sizeValue; scratch.x = x; scratch.y = y; scratch.selected = selected; scratch.nodeLabel = nodeLabel; scratch.markerSize = markerSize; return scratch; } /** * Computes label text and measurements for a datum. * Separated to enable skipping when labels are disabled. */ computeLabel(ctx, datum, yDatum, sizeValue, datumIndex) { let labelTextValue; let labelTextKey; let labelTextProperty; if (ctx.labelKey && ctx.labelDataValues) { labelTextValue = ctx.labelDataValues[datumIndex]; labelTextKey = ctx.labelKey; labelTextProperty = "label"; } else if (ctx.sizeKey) { labelTextValue = sizeValue; labelTextKey = ctx.sizeKey; labelTextProperty = "size"; } else { labelTextValue = yDatum; labelTextKey = ctx.yKey; labelTextProperty = "y"; } const labelText = this.getLabelText( labelTextValue, datum, labelTextKey, labelTextProperty, ctx.labelTextDomain, ctx.label, { value: labelTextValue, datum, xKey: ctx.xKey, yKey: ctx.yKey, // sizeKey may be undefined for ScatterSeries (which extends BubbleSeries) sizeKey: ctx.sizeKey, labelKey: ctx.labelKey, xName: ctx.xName, yName: ctx.yName, sizeName: ctx.sizeName, labelName: ctx.labelName, legendItemName: ctx.legendItemName } ); let { width: width2, height: height2 } = isArray(labelText) ? measureTextSegments(labelText, ctx.label) : ctx.labelTextMeasurer.measureLines(String(labelText)); width2 += ctx.labelPadding.left + ctx.labelPadding.right; height2 += ctx.labelPadding.bottom + ctx.labelPadding.top; return { text: labelText, width: width2, height: height2 }; } /** * Creates a minimal skeleton node - actual values set by updateNodeDatum. */ createSkeletonNodeDatum(ctx, _scratch, _datumIndex) { return { series: this, yKey: ctx.yKey, xKey: ctx.xKey, datum: void 0, datumIndex: 0, xValue: void 0, yValue: void 0, sizeValue: void 0, capDefaults: { lengthRatioMultiplier: this.properties.marker.getDiameter(), lengthMax: Infinity }, point: { x: 0, y: 0, size: 0 }, midPoint: { x: 0, y: 0 }, label: { text: "", width: 0, height: 0 }, anchor: ctx.labelAnchor, placement: ctx.labelPlacement, count: 1, dilation: 1, area: 0, selected: void 0 }; } /** * Updates node properties in-place. * Shared by both create (skeleton + update) and incremental update paths. */ updateNodeDatum(ctx, node, scratch, datumIndex) { const mutableNode = node; const { x, y, markerSize, dilation } = scratch; mutableNode.datum = scratch.datum; mutableNode.datumIndex = datumIndex; mutableNode.xValue = scratch.xDatum; mutableNode.yValue = scratch.yDatum; mutableNode.sizeValue = scratch.sizeValue; mutableNode.selected = scratch.selected; mutableNode.count = scratch.count; mutableNode.dilation = scratch.dilation; mutableNode.area = scratch.area; mutableNode.label = scratch.nodeLabel; mutableNode.anchor = ctx.labelAnchor; mutableNode.placement = ctx.labelPlacement; const mutablePoint = mutableNode.point; mutablePoint.x = x; mutablePoint.y = y; mutablePoint.size = Math.sqrt(dilation) * markerSize; const mutableMidPoint = mutableNode.midPoint; mutableMidPoint.x = x; mutableMidPoint.y = y; } isPathOrSelectionDirty() { return this.properties.marker.isDirty(); } getLabelData() { if (!this.isLabelEnabled()) return []; return this.contextNodeData?.labelData ?? []; } updateDatumSelection(opts) { const { nodeData, datumSelection } = opts; if (this.properties.marker.isDirty()) { datumSelection.clear(); datumSelection.cleanup(); } if (!processedDataIsAnimatable(this.processedData)) { return datumSelection.update(nodeData); } const { sizeKey } = this.properties; let getId; if (sizeKey) { getId = (datum) => createDatumId(datum.xValue, datum.yValue, datum.sizeValue, toPlainText(datum.label.text)); } return datumSelection.update(nodeData, void 0, getId); } updateDatumStyles(opts) { const { datumSelection, isHighlight } = opts; const { xKey, yKey, sizeKey, labelKey, marker } = this.properties; const params = { xKey, yKey, sizeKey, labelKey }; const highlightedDatum = this.ctx.highlightManager.getActiveHighlight(); datumSelection.each((node, datum) => { if (!datumSelection.isGarbage(node)) { const highlightState = this.getHighlightState(highlightedDatum, opts.isHighlight, datum.datumIndex); const stylerStyle = this.getStyle(highlightState); datum.style = this.getMarkerStyle( marker, datum, params, { isHighlight, highlightState, resolveMarkerSubPath: [] }, stylerStyle ); } }); } updateDatumNodes(opts) { const { contextNodeData } = this; if (!contextNodeData) return; const { datumSelection, isHighlight, drawingMode } = opts; this.sizeScale.range = this.getSizeRange(); const fillBBox = this.getShapeFillBBox(); const aggregated = this.dataAggregation != null; const highlightedDatum = this.ctx.highlightManager.getActiveHighlight(); datumSelection.each((node, datum, index) => { const { point: { size }, count, area: area2, dilation } = datum; const state = this.getHighlightState(highlightedDatum, isHighlight, datum.datumIndex); const style2 = { ...datum.style ?? contextNodeData.styles[state] }; style2.size = size; if (dilation > 1) { const fillOpacity = style2.fillOpacity ?? 0; const opacityScale = 0.269669 + 683e-6 * count + -37.534348 * area2 + 4449e-6 * count * area2 + -0 * count ** 2 + 44.428603 * area2 ** 2; style2.fillOpacity = clamp(fillOpacity / dilation, fillOpacity / 0.1 * opacityScale, 1); } this.applyMarkerStyle(style2, node, datum.point, fillBBox, { selected: datum.selected }); node.drawingMode = this.resolveMarkerDrawingModeForState(drawingMode, style2); node.zIndex = aggregated ? [-count, index] : 0; }); if (!isHighlight) { this.properties.marker.markClean(); } } updatePlacedLabelData(labelData) { this.placedLabelData = labelData; this.labelSelection.update( labelData.map((v) => ({ ...v.datum, point: { x: v.x, y: v.y, size: v.datum.point.size } })), (text) => { text.pointerEvents = 1 /* None */; } ); this.updateLabelNodes({ labelSelection: this.labelSelection }); this.updateHighlightLabelSelection(); } updateHighlightLabelSelection() { const highlightedDatum = this.ctx.highlightManager?.getActiveHighlight(); const highlightItem = this.isSeriesHighlighted(highlightedDatum) && highlightedDatum?.datum ? highlightedDatum : void 0; const highlightLabelData = highlightItem == null ? [] : this.placedLabelData.filter((label) => label.datum.datumIndex === highlightItem.datumIndex).map((label) => ({ ...label.datum, point: { x: label.x, y: label.y, size: label.datum.point.size } })); this.highlightLabelSelection = this.updateLabelSelection({ labelData: highlightLabelData, labelSelection: this.highlightLabelSelection }) ?? this.highlightLabelSelection; this.highlightLabelGroup.visible = highlightLabelData.length > 0; this.highlightLabelGroup.batchedUpdate(() => { this.updateLabelNodes({ labelSelection: this.highlightLabelSelection, isHighlight: true }); }); } updateLabelNodes(opts) { const { isHighlight = false } = opts; const activeHighlight = this.ctx.highlightManager?.getActiveHighlight(); const params = this.makeLabelFormatterParams(); opts.labelSelection.each((text, datum) => { const style2 = getLabelStyles(this, datum, params, this.properties.label, isHighlight, activeHighlight); text.text = datum.label.text; text.fill = style2.color; text.x = datum.point?.x ?? 0; text.y = datum.point?.y ?? 0; text.fontStyle = style2.fontStyle; text.fontWeight = style2.fontWeight; text.fontSize = style2.fontSize; text.fontFamily = style2.fontFamily; text.textBaseline = "top"; text.fillOpacity = this.getHighlightStyle(isHighlight, datum.datumIndex).opacity ?? 1; text.setBoxing(style2); }); } updateLabelSelection(opts) { const { labelData, labelSelection } = opts; return labelSelection.update(labelData, (text) => { text.pointerEvents = 1 /* None */; }); } makeStylerParams(highlightStateEnum) { const { id: seriesId, properties: { size, maxSize, shape, fill, fillOpacity, lineDash, lineDashOffset, stroke, strokeOpacity, strokeWidth, xKey, yKey, sizeKey, labelKey } } = this; const highlightState = toHighlightString(highlightStateEnum ?? 0 /* None */); if (this.type === "bubble") { return { highlightState, size, maxSize, shape, fill, fillOpacity, lineDash, lineDashOffset, seriesId, sizeKey, stroke, strokeOpacity, strokeWidth, xKey, yKey, labelKey }; } else if (this.type === "scatter") { return { highlightState, size, shape, fill, fillOpacity, lineDash, lineDashOffset, seriesId, stroke, strokeOpacity, strokeWidth, xKey, yKey, labelKey }; } else { return this.type; } } makeLabelFormatterParams() { const { xKey, xName, yKey, yName, sizeKey, sizeName, labelKey, labelName, legendItemName } = this.properties; return { xKey, xName, yKey, yName, sizeKey, sizeName, labelKey, labelName, legendItemName }; } getTooltipContent(datumIndex) { const { id: seriesId, dataModel, processedData, axes, properties, ctx } = this; const { formatManager } = ctx; const { xKey, xName, yKey, yName, sizeKey, sizeName, labelKey, labelName, title, tooltip, marker, legendItemName } = properties; const xAxis = axes["x" /* X */]; const yAxis = axes["y" /* Y */]; if (!dataModel || !processedData || !xAxis || !yAxis) return; const datum = processedData.dataSources.get(this.id)?.data?.[datumIndex]; const xValue = dataModel.resolveColumnById(this, `xValue`, processedData)[datumIndex]; const yValue = dataModel.resolveColumnById(this, `yValue`, processedData)[datumIndex]; const allowNullKeys = this.properties.allowNullKeys ?? false; if (xValue === void 0 && !allowNullKeys) return; const data = []; if (this.isLabelEnabled() && labelKey != null) { const value = dataModel.resolveColumnById(this, `labelValue`, processedData)[datumIndex]; const content = formatManager.format(this.callWithContext.bind(this), { type: "category", value, datum, seriesId, legendItemName, key: labelKey, source: "tooltip", property: "label", domain: [], boundSeries: this.getFormatterContext("label") }); data.push({ label: labelName, fallbackLabel: labelKey, value: content ?? formatValue(value) }); } data.push( { label: xName, fallbackLabel: xKey, value: this.getAxisValueText(xAxis, "tooltip", xValue, datum, xKey, legendItemName, allowNullKeys), missing: isTooltipValueMissing(xValue, allowNullKeys) }, { label: yName, fallbackLabel: yKey, value: this.getAxisValueText(yAxis, "tooltip", yValue, datum, yKey, legendItemName, allowNullKeys), missing: isTooltipValueMissing(yValue, allowNullKeys) } ); if (sizeKey != null) { const value = dataModel.resolveColumnById(this, `sizeValue`, processedData)[datumIndex]; if (value != null) { const domain = dataModel.getDomain(this, `sizeValue`, "value", processedData).domain; const content = formatManager.format(this.callWithContext.bind(this), { type: "number", value, datum, seriesId, legendItemName, key: sizeKey, source: "tooltip", property: "size", boundSeries: this.getFormatterContext("size"), domain, fractionDigits: void 0, visibleDomain: void 0 }); data.push({ label: sizeName, fallbackLabel: sizeKey, value: content ?? formatValue(value) }); } } const activeStyle = this.getMarkerStyle( marker, { datum, datumIndex }, { xKey, yKey, sizeKey, labelKey }, { resolveMarkerSubPath: [] } ); return this.formatTooltipWithContext( tooltip, { title, symbol: this.legendItemSymbol(), data }, { seriesId, datum, title: yKey, xKey, xName, yKey, yName, sizeKey, sizeName, labelKey, labelName, legendItemName, ...activeStyle, ...this.getModuleTooltipParams() } ); } legendItemSymbol() { const style2 = this.getStyle(); const marker = this.getMarkerStyle( this.properties.marker, {}, void 0, { isHighlight: false, checkForHighlight: false, resolveMarkerSubPath: [] }, style2 ); return { marker }; } getLegendData() { const { id: seriesId, ctx: { legendManager }, visible } = this; const { yKey: itemId, yName, legendItemName, title, showInLegend } = this.properties; return [ { legendType: "category", id: seriesId, itemId, seriesId, enabled: visible && legendManager.getItemEnabled({ seriesId, itemId }), label: { text: legendItemName ?? title ?? yName ?? itemId }, symbol: this.legendItemSymbol(), legendItemName, hideInLegend: !showInLegend } ]; } animateEmptyUpdateReady({ datumSelection, labelSelection }) { markerScaleInAnimation(this, this.ctx.animationManager, datumSelection); seriesLabelFadeInAnimation(this, "labels", this.ctx.animationManager, labelSelection); } resetDatumAnimation(data) { resetMarkerSelectionsDirect([data.datumSelection]); } isLabelEnabled() { return this.properties.label.enabled; } nodeFactory() { return new Marker(); } getStyle(highlightState) { const { properties } = this; let stylerResult = {}; if (properties.styler) { const stylerParams = this.makeStylerParams(highlightState); const cbResult = this.cachedCallWithContext(properties.styler, stylerParams) ?? {}; const resolved = this.ctx.optionsGraphService.resolvePartial( ["series", `${this.declarationOrder}`], cbResult, { pick: false } ); stylerResult = resolved ?? {}; } return { fill: stylerResult.fill ?? properties.fill, fillOpacity: stylerResult.fillOpacity ?? properties.fillOpacity, lineDash: stylerResult.lineDash ?? properties.lineDash, lineDashOffset: stylerResult.lineDashOffset ?? properties.lineDashOffset, shape: stylerResult.shape ?? properties.shape, size: stylerResult.size ?? properties.size, maxSize: stylerResult.maxSize ?? properties.maxSize, stroke: stylerResult.stroke ?? properties.stroke, strokeOpacity: stylerResult.strokeOpacity ?? properties.strokeOpacity, strokeWidth: stylerResult.strokeWidth ?? properties.strokeWidth }; } getSizeRange() { const { size, maxSize } = this.getStyle(); return [size, maxSize]; } getFormattedMarkerStyle(datum) { const { xKey, yKey, sizeKey, labelKey, marker } = this.properties; return this.getMarkerStyle(marker, datum, { xKey, yKey, sizeKey, labelKey }, { resolveMarkerSubPath: [] }); } computeFocusBounds(opts) { return computeMarkerFocusBounds(this, opts); } hasItemStylers() { const { styler, itemStyler, marker, label } = this.properties; return !!(styler ?? itemStyler ?? marker.itemStyler ?? label.itemStyler); } initQuadTree(quadtree) { addHitTestersToQuadtree(quadtree, this.datumNodesIter()); } pickNodeDataClosestDatum(point) { return findQuadtreeMatch(this, point); } }; BubbleSeries.className = "BubbleSeries"; BubbleSeries.type = "bubble"; // packages/ag-charts-community/src/chart/series/cartesian/bubbleSeriesOptionsDef.ts var bubbleSeriesThemeableOptionsDef = { title: string, domain: arrayOf(number), maxSize: positiveNumber, showInMiniChart: boolean, label: { placement: union("top", "right", "bottom", "left"), ...seriesLabelOptionsDefs }, tooltip: tooltipOptionsDefs, styler: callbackDefs({ ...markerOptionsDefs, maxSize: positiveNumber }), maxRenderedItems: number, ...commonSeriesThemeableOptionsDefs, ...without(markerOptionsDefs, ["enabled"]), highlight: multiSeriesHighlightOptionsDef(shapeHighlightOptionsDef, shapeHighlightOptionsDef) }; var bubbleSeriesOptionsDef = { ...bubbleSeriesThemeableOptionsDef, ...commonSeriesOptionsDefs, type: required(constant("bubble")), xKey: required(string), yKey: required(string), sizeKey: required(string), labelKey: string, xName: string, yName: string, sizeName: string, labelName: string, legendItemName: string, xKeyAxis: string, yKeyAxis: string, highlight: multiSeriesHighlightOptionsDef(shapeHighlightOptionsDef, shapeHighlightOptionsDef) }; bubbleSeriesOptionsDef.xFilterKey = undocumented(string); bubbleSeriesOptionsDef.yFilterKey = undocumented(string); bubbleSeriesOptionsDef.sizeFilterKey = undocumented(string); // packages/ag-charts-community/src/chart/series/cartesian/bubbleSeriesModule.ts var themeTemplate3 = { series: { shape: "circle", size: 7, maxSize: 30, fill: { $applySwitch: [ { $path: "type" }, { $palette: "fill" }, ["gradient", FILL_GRADIENT_RADIAL_REVERSED_DEFAULTS], ["image", FILL_IMAGE_DEFAULTS], ["pattern", FILL_PATTERN_DEFAULTS] ] }, stroke: { $palette: "stroke" }, fillOpacity: 0.8, maxRenderedItems: 2e3, label: { ...LABEL_BOXING_DEFAULTS, enabled: false, fontSize: { $ref: "fontSize" }, fontFamily: { $ref: "fontFamily" }, fontWeight: { $ref: "fontWeight" }, color: { $ref: "textColor" } }, tooltip: { range: { $if: [ { $eq: [{ $path: ["/tooltip/range", "nearest"] }, "area"] }, "nearest", { $path: ["/tooltip/range", "nearest"] } ] }, position: { anchorTo: { $path: ["/tooltip/position/anchorTo", "node"] } } }, highlight: MULTI_SERIES_HIGHLIGHT_STYLE } }; var BubbleSeriesModule = { type: "series", name: "bubble", chartType: "cartesian", version: VERSION, dependencies: [CartesianChartModule], options: bubbleSeriesOptionsDef, predictAxis: predictCartesianAxis, defaultAxes: { x: { type: "number" /* NUMBER */, position: "bottom" /* BOTTOM */ }, y: { type: "number" /* NUMBER */, position: "left" /* LEFT */ } }, axisKeys: { ["x" /* X */]: "xKeyAxis", ["y" /* Y */]: "yKeyAxis" }, themeTemplate: themeTemplate3, create: (ctx) => new BubbleSeries(ctx) }; // packages/ag-charts-community/src/chart/series/cartesian/histogramSeriesProperties.ts var HistogramSeriesProperties = class extends CartesianSeriesProperties { constructor() { super(...arguments); this.fillOpacity = 1; this.strokeWidth = 1; this.strokeOpacity = 1; this.lineDash = [0]; this.lineDashOffset = 0; this.cornerRadius = 0; this.areaPlot = false; this.aggregation = "sum"; this.shadow = new DropShadow(); this.label = new Label(); this.tooltip = makeSeriesTooltip(); } getStyle() { const { fill, fillOpacity, stroke, strokeWidth, strokeOpacity, lineDash, lineDashOffset, cornerRadius } = this; return { fill, fillOpacity, stroke, strokeWidth, strokeOpacity, lineDash, lineDashOffset, cornerRadius, opacity: 1 }; } }; __decorateClass([ addFakeTransformToInstanceProperty ], HistogramSeriesProperties.prototype, "xKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HistogramSeriesProperties.prototype, "yKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HistogramSeriesProperties.prototype, "xName", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HistogramSeriesProperties.prototype, "yName", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HistogramSeriesProperties.prototype, "fill", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HistogramSeriesProperties.prototype, "fillOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HistogramSeriesProperties.prototype, "stroke", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HistogramSeriesProperties.prototype, "strokeWidth", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HistogramSeriesProperties.prototype, "strokeOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HistogramSeriesProperties.prototype, "lineDash", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HistogramSeriesProperties.prototype, "lineDashOffset", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HistogramSeriesProperties.prototype, "cornerRadius", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HistogramSeriesProperties.prototype, "areaPlot", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HistogramSeriesProperties.prototype, "bins", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HistogramSeriesProperties.prototype, "aggregation", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HistogramSeriesProperties.prototype, "binCount", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HistogramSeriesProperties.prototype, "shadow", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HistogramSeriesProperties.prototype, "label", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HistogramSeriesProperties.prototype, "tooltip", 2); // packages/ag-charts-community/src/chart/series/cartesian/histogramSeries.ts var defaultBinCount = 10; var HistogramSeries = class extends CartesianSeries { constructor(moduleCtx) { super({ moduleCtx, propertyKeys: DEFAULT_CARTESIAN_DIRECTION_KEYS, propertyNames: DEFAULT_CARTESIAN_DIRECTION_NAMES, categoryKey: void 0, pickModes: [1 /* NEAREST_NODE */, 0 /* EXACT_SHAPE_MATCH */], datumSelectionGarbageCollection: true, animationAlwaysPopulateNodeData: true, alwaysClip: true, animationResetFns: { datum: resetBarSelectionsFn, label: resetLabelFn } }); this.properties = new HistogramSeriesProperties(); this.calculatedBins = []; } get hasData() { return this.calculatedBins.length > 0; } // During processData phase, used to unify different ways of the user specifying // the bins. Returns bins in format[[min1, max1], [min2, max2], ... ]. deriveBins(xDomain) { const binStarts = createTicks(xDomain[0], xDomain[1], defaultBinCount).ticks; const binSize = tickStep(xDomain[0], xDomain[1], defaultBinCount); const [firstBinEnd] = binStarts; const expandStartToBin = (n) => [n, n + binSize]; return [[firstBinEnd - binSize, firstBinEnd], ...binStarts.map(expandStartToBin)]; } calculateNiceBins(domain, binCount) { const startGuess = Math.floor(domain[0]); const stop = domain[1]; const segments = binCount || 1; const { start: start2, binSize } = this.calculateNiceStart(startGuess, stop, segments); return this.getBins(start2, stop, binSize, segments); } getBins(start2, stop, step, count) { const bins = []; const precision = this.calculatePrecision(step); for (let i = 0; i < count; i++) { const a = Math.round((start2 + i * step) * precision) / precision; let b = Math.round((start2 + (i + 1) * step) * precision) / precision; if (i === count - 1) { b = Math.max(b, stop); } bins[i] = [a, b]; } return bins; } calculatePrecision(step) { let precision = 10; if (Number.isFinite(step) && step > 0) { while (step < 1) { precision *= 10; step *= 10; } } return precision; } calculateNiceStart(a, b, segments) { const binSize = Math.abs(b - a) / segments; const order = Math.floor(Math.log10(binSize)); const magnitude = Math.pow(10, order); const start2 = Math.floor(a / magnitude) * magnitude; return { start: start2, binSize }; } async processData(dataController) { const { visible } = this; const { xKey, yKey, areaPlot, aggregation } = this.properties; const xScale = this.axes["x" /* X */]?.scale; const yScale = this.axes["y" /* Y */]?.scale; const { xScaleType, yScaleType } = this.getScaleInformation({ yScale, xScale }); const visibleProps = visible ? {} : { forceValue: 0 }; const props = [keyProperty(xKey, xScaleType), SORT_DOMAIN_GROUPS]; if (yKey) { let aggProp = groupCount("groupAgg", { visible }); if (aggregation === "count") { } else if (aggregation === "sum") { aggProp = groupSum("groupAgg", { visible }); } else if (aggregation === "mean") { aggProp = groupAverage("groupAgg", { visible }); } if (areaPlot) { aggProp = area("groupAgg", aggProp); } props.push(valueProperty(yKey, yScaleType, { invalidValue: void 0, ...visibleProps }), aggProp); } else { props.push(rowCountProperty("count")); let aggProp = groupCount("groupAgg", { visible }); if (areaPlot) { aggProp = area("groupAgg", aggProp); } props.push(aggProp); } let calculatedBinDomains = []; const groupByFn = (dataSet) => { const xExtent = fixNumericExtent(dataSet.domain.keys[0]); if (xExtent.length === 0) { dataSet.domain.groups = []; return () => []; } const bins = isNumber(this.properties.binCount) ? this.calculateNiceBins(xExtent, this.properties.binCount) : this.properties.bins ?? this.deriveBins(xExtent); const binCount = bins.length; calculatedBinDomains = [...bins]; return (keys) => { let xValue = keys[0]; if (isDate(xValue)) { xValue = xValue.getTime(); } if (!isNumber(xValue)) return []; for (let i = 0; i < binCount; i++) { const nextBin = bins[i]; if (xValue >= nextBin[0] && xValue < nextBin[1]) { return nextBin; } if (i === binCount - 1 && xValue <= nextBin[1]) { return nextBin; } } return []; }; }; const { dataModel, processedData: p } = await this.requestDataModel(dataController, this.data, { props, groupByFn }); const processedData = p; const groups = /* @__PURE__ */ new Map(); for (const [groupIndex, group] of processedData.groups.entries()) { const domain = group.keys; groups.set(createDatumId(...domain), { group, groupIndex }); } this.calculatedBins = calculatedBinDomains.map((domain) => { const g = groups.get(createDatumId(...domain)); if (g) { const { group, groupIndex } = g; const [[negativeAgg, positiveAgg] = [0, 0]] = group.aggregation; const datum = [...dataModel.forEachDatum(this, processedData, group, groupIndex)]; const frequency = this.frequency(group); const total = negativeAgg + positiveAgg; return { domain, datum, groupIndex, frequency, total }; } else { return { domain, datum: [], groupIndex: -1, frequency: 0, total: 0 }; } }); this.animationState.transition("updateData"); } xCoordinateRange() { return [Number.NaN, Number.NaN]; } yCoordinateRange() { return [Number.NaN, Number.NaN]; } getSeriesDomain(direction) { const { processedData, dataModel } = this; if (!processedData || !dataModel || !this.calculatedBins.length) return { domain: [] }; const yDomain = dataModel.getDomain(this, `groupAgg`, "aggregate", processedData).domain; const xDomainMin = this.calculatedBins[0].domain[0]; const xDomainMax = this.calculatedBins[(this.calculatedBins?.length ?? 0) - 1].domain[1]; if (direction === "x" /* X */) { return { domain: fixNumericExtent([xDomainMin, xDomainMax]) }; } return { domain: fixNumericExtent(yDomain) }; } getSeriesRange(_direction, [r0, r1]) { const { dataModel, processedData } = this; if (!dataModel || processedData?.type !== "grouped") return [Number.NaN, Number.NaN]; const xScale = this.axes["x" /* X */].scale; const yMin = 0; let yMax = -Infinity; for (const { keys, aggregation } of processedData.groups) { const [[negativeAgg, positiveAgg] = [0, 0]] = aggregation; const [xDomainMin, xDomainMax] = keys; const [x0, x1] = findMinMax([xScale.convert(xDomainMin), xScale.convert(xDomainMax)]); if (x1 >= r0 && x0 <= r1) { const total = negativeAgg + positiveAgg; yMax = Math.max(yMax, total); } } if (yMin > yMax) return [Number.NaN, Number.NaN]; return [yMin, yMax]; } frequency(group) { return group.datumIndices.reduce((acc, datumIndices) => acc + datumIndices.length, 0); } /** * Creates the shared context for datum creation. * Caches expensive lookups and computations that are constant across all datums. * * Note: rawData and xValues are empty arrays because HistogramSeries * iterates over calculatedBins rather than raw data. */ createNodeDatumContext(xAxis, yAxis) { const { xKey, yKey, xName, yName, label } = this.properties; const { contextNodeData, processedData } = this; const canIncrementallyUpdate = contextNodeData?.nodeData != null && processedData?.changeDescription != null; return { // Axes (from template method parameters) xAxis, yAxis, // Scales xScale: xAxis.scale, yScale: yAxis.scale, yAxisReversed: yAxis.isReversed(), // Data source (empty arrays - histogram uses calculatedBins instead) rawData: [], xValues: [], // Property lookups xKey, yKey, xName, yName, label, // Animation flag animationEnabled: !this.ctx.animationManager.isSkipped(), // Incremental update support canIncrementallyUpdate, nodes: canIncrementallyUpdate ? contextNodeData.nodeData : [], nodeIndex: 0 }; } /** * Creates label data for a histogram bin if labels are enabled. */ createLabelData(ctx, bin, x, y, w, h) { const { label, yKey, xKey, xName, yName } = ctx; const { total, datum } = bin; if (!label.enabled || total === 0) { return void 0; } return { x: x + w / 2, y: y + h / 2, text: this.getLabelText(total, datum, yKey, "y", [], label, { value: total, datum, xKey, yKey, xName, yName }) }; } /** * Creates a skeleton HistogramNodeDatum with minimal required fields. * The node will be populated by updateNodeDatum. */ createSkeletonNodeDatum(ctx, bin) { const { xKey, yKey } = ctx; const { domain, datum, groupIndex, frequency, total } = bin; return { series: this, datumIndex: groupIndex, datum, aggregatedValue: total, frequency, domain, yKey, xKey, x: 0, y: 0, xValue: 0, yValue: 0, width: 0, height: 0, midPoint: { x: 0, y: 0 }, topLeftCornerRadius: false, topRightCornerRadius: false, bottomRightCornerRadius: false, bottomLeftCornerRadius: false, label: void 0, crisp: true }; } /** * Updates an existing HistogramNodeDatum in-place. * This is more efficient than recreating the entire node when only data values change. */ updateNodeDatum(ctx, node, bin) { const { xScale, yScale, yAxisReversed } = ctx; const { domain, datum, groupIndex, frequency, total } = bin; const mutableNode = node; const [xDomainMin, xDomainMax] = domain; const xMinPx = xScale.convert(xDomainMin); const xMaxPx = xScale.convert(xDomainMax); const yZeroPx = yScale.convert(0); const yMaxPx = yScale.convert(total); const w = Math.abs(xMaxPx - xMinPx); const h = Math.abs(yMaxPx - yZeroPx); const x = Math.min(xMinPx, xMaxPx); const y = Math.min(yZeroPx, yMaxPx); mutableNode.datumIndex = groupIndex; mutableNode.datum = datum; mutableNode.aggregatedValue = total; mutableNode.frequency = frequency; mutableNode.domain = domain; mutableNode.x = x; mutableNode.y = y; mutableNode.xValue = xMinPx; mutableNode.yValue = yMaxPx; mutableNode.width = w; mutableNode.height = h; if (mutableNode.midPoint) { mutableNode.midPoint.x = x + w / 2; mutableNode.midPoint.y = y + h / 2; } else { mutableNode.midPoint = { x: x + w / 2, y: y + h / 2 }; } mutableNode.topLeftCornerRadius = !yAxisReversed; mutableNode.topRightCornerRadius = !yAxisReversed; mutableNode.bottomRightCornerRadius = yAxisReversed; mutableNode.bottomLeftCornerRadius = yAxisReversed; mutableNode.label = this.createLabelData(ctx, bin, x, y, w, h); } /** * Creates a HistogramNodeDatum for a single bin. * Creates a skeleton node and uses updateNodeDatum to populate it. */ createNodeDatum(ctx, bin) { const node = this.createSkeletonNodeDatum(ctx, bin); this.updateNodeDatum(ctx, node, bin); return node; } /** * Template method hook: Iterates over calculated bins and creates/updates node datums. */ populateNodeData(ctx) { const { processedData } = this; if (processedData?.type !== "grouped") { return; } for (const bin of this.calculatedBins) { upsertNodeDatum( ctx, bin, (c, b) => this.createNodeDatum(c, b), (c, n, b) => this.updateNodeDatum(c, n, b) ); } } /** * Template method hook: Creates the result object shell. */ initializeResult(ctx) { return { itemId: this.properties.yKey ?? this.id, nodeData: ctx.nodes, labelData: ctx.nodes, scales: this.calculateScaling(), animationValid: true, visible: this.visible || ctx.animationEnabled, styles: getItemStyles(this.getItemStyle.bind(this)) }; } /** * Template method hook: Trims arrays and sorts nodes for keyboard navigation. */ finalizeNodeData(ctx) { super.finalizeNodeData(ctx); ctx.nodes.sort((a, b) => a.x - b.x); } nodeFactory() { return new Rect(); } updateDatumSelection(opts) { const { nodeData, datumSelection } = opts; if (!processedDataIsAnimatable(this.processedData)) { return datumSelection.update(nodeData); } return datumSelection.update( nodeData, void 0, (datum) => createDatumId(...datum.domain) ); } getItemStyle(datumIndex, isHighlight, highlightState) { const { properties } = this; const highlightStyle = this.getHighlightStyle(isHighlight, datumIndex, highlightState); return mergeDefaults(highlightStyle, properties.getStyle()); } updateDatumStyles(opts) { const { datumSelection, isHighlight } = opts; datumSelection.each((node, datum) => { if (!datumSelection.isGarbage(node)) { datum.style = this.getItemStyle(datum.datumIndex, isHighlight); } }); } updateDatumNodes(opts) { const { contextNodeData } = this; if (!contextNodeData) { return; } const highlightedDatum = this.ctx.highlightManager.getActiveHighlight(); const { shadow } = this.properties; const fillBBox = this.getShapeFillBBox(); opts.datumSelection.each((rect2, datum) => { const style2 = datum.style ?? contextNodeData.styles[this.getHighlightState(highlightedDatum, opts.isHighlight, datum.datumIndex)]; const { cornerRadius = 0 } = style2; const { topLeftCornerRadius, topRightCornerRadius, bottomRightCornerRadius, bottomLeftCornerRadius } = datum; rect2.setStyleProperties(style2, fillBBox); rect2.topLeftCornerRadius = topLeftCornerRadius ? cornerRadius : 0; rect2.topRightCornerRadius = topRightCornerRadius ? cornerRadius : 0; rect2.bottomRightCornerRadius = bottomRightCornerRadius ? cornerRadius : 0; rect2.bottomLeftCornerRadius = bottomLeftCornerRadius ? cornerRadius : 0; rect2.crisp = datum.crisp; rect2.fillShadow = shadow; }); } updateLabelSelection(opts) { const { labelData, labelSelection } = opts; return labelSelection.update(labelData, (text) => { text.pointerEvents = 1 /* None */; text.textAlign = "center"; text.textBaseline = "middle"; }); } updateLabelNodes(opts) { const labelEnabled = this.isLabelEnabled(); const { isHighlight = false } = opts; const activeHighlight = this.ctx.highlightManager?.getActiveHighlight(); opts.labelSelection.each((text, datum) => { const style2 = getLabelStyles( this, datum, this.properties, this.properties.label, isHighlight, activeHighlight ); const { enabled, fontStyle, fontWeight: fontWeight2, fontSize, fontFamily, color: color2 } = style2; if (enabled && labelEnabled && datum?.label) { text.text = datum.label.text; text.x = datum.label.x; text.y = datum.label.y; text.fontStyle = fontStyle; text.fontWeight = fontWeight2; text.fontFamily = fontFamily; text.fontSize = fontSize; text.fill = color2; text.visible = true; text.fillOpacity = this.getHighlightStyle(isHighlight, datum.datumIndex).opacity ?? 1; text.setBoxing(style2); } else { text.visible = false; } }); } initQuadTree(quadtree) { const { value: childNode } = this.contentGroup.children().next(); if (childNode instanceof Group) { addHitTestersToQuadtree(quadtree, childNode.children()); } } pickNodeClosestDatum(point) { return findQuadtreeMatch(this, point); } getTooltipContent(datumIndex) { const { id: seriesId, dataModel, processedData, axes, properties, ctx: { localeManager } } = this; const { xKey, xName, yKey, yName, tooltip, legendItemName } = properties; const xAxis = axes["x" /* X */]; const yAxis = axes["y" /* Y */]; if (!dataModel || processedData?.type !== "grouped" || !xAxis || !yAxis) { return; } const group = processedData.groups[datumIndex]; const { aggregation, keys } = group; const [[negativeAgg, positiveAgg] = [0, 0]] = aggregation; const frequency = this.frequency(group); const domain = keys; const [rangeMin, rangeMax] = domain; const aggregatedValue = negativeAgg + positiveAgg; const datum = { data: [...dataModel.forEachDatum(this, processedData, group, datumIndex)], aggregatedValue, frequency, domain }; const data = [ { label: xName, fallbackLabel: xKey, value: `${this.getAxisValueText(xAxis, "tooltip", rangeMin, datum, xKey, legendItemName)} - ${this.getAxisValueText(xAxis, "tooltip", rangeMax, datum, xKey, legendItemName)}` }, { label: localeManager.t("seriesHistogramTooltipFrequency"), value: this.getAxisValueText(yAxis, "tooltip", frequency, datum, yKey, legendItemName) } ]; if (yKey != null) { let label; switch (properties.aggregation) { case "sum": label = localeManager.t("seriesHistogramTooltipSum", { yName: yName ?? yKey }); break; case "mean": label = localeManager.t("seriesHistogramTooltipMean", { yName: yName ?? yKey }); break; case "count": label = localeManager.t("seriesHistogramTooltipCount", { yName: yName ?? yKey }); break; } data.push({ label, value: this.getAxisValueText(yAxis, "tooltip", aggregatedValue, datum, yKey, legendItemName) }); } return this.formatTooltipWithContext( tooltip, { symbol: this.legendItemSymbol(), data }, { seriesId, datum, title: yName, xKey, // HistogramSeries is an outlier since it's callbacks don't use TDatum. xName, yKey, // HistogramSeries is an outlier since it's callbacks don't use TDatum. yName, xRange: [rangeMin, rangeMax], frequency, ...this.getItemStyle(datumIndex, false) } ); } legendItemSymbol() { const { fill, fillOpacity, stroke, strokeWidth, strokeOpacity, lineDash, lineDashOffset } = this.properties; return { marker: { fill: deepClone(fill) ?? "rgba(0, 0, 0, 0)", stroke: stroke ?? "rgba(0, 0, 0, 0)", fillOpacity, strokeOpacity, strokeWidth, lineDash, lineDashOffset } }; } getLegendData(legendType) { if (legendType !== "category") { return []; } const { id: seriesId, ctx: { legendManager }, visible } = this; const { xKey: itemId, yName, showInLegend } = this.properties; return [ { legendType: "category", id: seriesId, itemId, seriesId, enabled: visible && legendManager.getItemEnabled({ seriesId, itemId }), label: { text: yName ?? itemId ?? "Frequency" }, symbol: this.legendItemSymbol(), hideInLegend: !showInLegend } ]; } resetDatumAnimation(data) { resetBarSelectionsDirect([data.datumSelection]); } animateEmptyUpdateReady({ datumSelection, labelSelection }) { const fns = prepareBarAnimationFunctions(collapsedStartingBarPosition(true, this.axes, "normal"), "unknown"); fromToMotion(this.id, "datums", this.ctx.animationManager, [datumSelection], fns); seriesLabelFadeInAnimation(this, "labels", this.ctx.animationManager, labelSelection); } animateWaitingUpdateReady(data) { const fns = prepareBarAnimationFunctions(collapsedStartingBarPosition(true, this.axes, "normal"), "added"); const dataDiff = { changed: true, added: /* @__PURE__ */ new Set(), updated: /* @__PURE__ */ new Set(), removed: /* @__PURE__ */ new Set(), moved: /* @__PURE__ */ new Set() }; fromToMotion( this.id, "datums", this.ctx.animationManager, [data.datumSelection], fns, (_, datum) => createDatumId(...datum.domain), dataDiff ); if (dataDiff?.changed) { seriesLabelFadeInAnimation(this, "labels", this.ctx.animationManager, data.labelSelection); } } isLabelEnabled() { return this.properties.label.enabled; } computeFocusBounds({ datumIndex }) { return computeBarFocusBounds(this, this.contextNodeData?.nodeData[datumIndex]); } hasItemStylers() { return this.properties.label.itemStyler != null; } }; HistogramSeries.className = "HistogramSeries"; HistogramSeries.type = "histogram"; // packages/ag-charts-community/src/chart/series/cartesian/histogramSeriesOptionsDef.ts var histogramSeriesThemeableOptionsDef = { showInMiniChart: boolean, cornerRadius: positiveNumber, label: seriesLabelOptionsDefs, tooltip: tooltipOptionsDefs, shadow: shadowOptionsDefs, ...commonSeriesThemeableOptionsDefs, ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef, highlight: multiSeriesHighlightOptionsDef(shapeHighlightOptionsDef, shapeHighlightOptionsDef), areaPlot: boolean, aggregation: union("count", "sum", "mean"), bins: arrayOf(arrayOf(number)), binCount: positiveNumber }; var histogramSeriesOptionsDef = { ...commonSeriesOptionsDefs, ...histogramSeriesThemeableOptionsDef, type: required(constant("histogram")), xKey: required(string), yKey: string, xKeyAxis: string, yKeyAxis: string, xName: string, yName: string }; // packages/ag-charts-community/src/chart/series/cartesian/histogramSeriesModule.ts var themeTemplate4 = { series: { fill: { $applySwitch: [ { $path: "type" }, { $palette: "fill" }, ["gradient", FILL_GRADIENT_LINEAR_DEFAULTS], ["image", FILL_IMAGE_DEFAULTS], ["pattern", FILL_PATTERN_DEFAULTS] ] }, stroke: { $palette: "stroke" }, strokeWidth: 1, fillOpacity: 1, strokeOpacity: 1, lineDash: [0], lineDashOffset: 0, label: { ...LABEL_BOXING_DEFAULTS, enabled: false, fontSize: { $ref: "fontSize" }, fontFamily: { $ref: "fontFamily" }, fontWeight: { $ref: "fontWeight" }, color: { $ref: "chartBackgroundColor" } }, shadow: { enabled: false, color: DEFAULT_SHADOW_COLOUR, xOffset: 3, yOffset: 3, blur: 5 }, highlight: MULTI_SERIES_HIGHLIGHT_STYLE } }; var HistogramSeriesModule = { type: "series", name: "histogram", chartType: "cartesian", // enterprise: true, version: VERSION, dependencies: [CartesianChartModule], options: histogramSeriesOptionsDef, predictAxis: predictCartesianNonPrimitiveAxis, defaultAxes: { x: { type: "number" /* NUMBER */, position: "bottom" /* BOTTOM */ }, y: { type: "number" /* NUMBER */, position: "left" /* LEFT */ } }, axisKeys: { ["x" /* X */]: "xKeyAxis", ["y" /* Y */]: "yKeyAxis" }, themeTemplate: themeTemplate4, create: (ctx) => new HistogramSeries(ctx) }; // packages/ag-charts-community/src/chart/series/cartesian/lineAggregation.ts var MAX_POINTS2 = 10; function isIndexInAggregation(xValues, d0, d1, indexData, maxRange, datumIndex, xNeedsValueOf, xValuesLength) { const xValue = xValues[datumIndex]; if (xValue === void 0) return false; const xRatio = Number.isFinite(d0) ? aggregationXRatioForXValue(xValue, d0, d1, xNeedsValueOf) : aggregationXRatioForDatumIndex(datumIndex, xValuesLength); const aggIndex = aggregationIndexForXRatio(xRatio, maxRange); return datumIndex === indexData[aggIndex + AGGREGATION_INDEX_X_MIN] || datumIndex === indexData[aggIndex + AGGREGATION_INDEX_X_MAX] || datumIndex === indexData[aggIndex + AGGREGATION_INDEX_Y_MIN] || datumIndex === indexData[aggIndex + AGGREGATION_INDEX_Y_MAX]; } function buildIndicesFromAggregation2(xValues, d0, d1, indexData, maxRange, xNeedsValueOf, xValuesLength, reuseArray) { let count = 0; for (let datumIndex = 0; datumIndex < xValuesLength; datumIndex++) { if (isIndexInAggregation(xValues, d0, d1, indexData, maxRange, datumIndex, xNeedsValueOf, xValuesLength)) { count++; } } const indices = reuseArray?.length === count ? reuseArray : new Uint32Array(count); let idx = 0; for (let datumIndex = 0; datumIndex < xValuesLength; datumIndex++) { if (isIndexInAggregation(xValues, d0, d1, indexData, maxRange, datumIndex, xNeedsValueOf, xValuesLength)) { indices[idx++] = datumIndex; } } return indices; } function filterIndicesFromPrevious(prevIndices, xValues, d0, d1, indexData, maxRange, xNeedsValueOf, xValuesLength, reuseArray) { let count = 0; for (const datumIndex of prevIndices) { if (isIndexInAggregation(xValues, d0, d1, indexData, maxRange, datumIndex, xNeedsValueOf, xValuesLength)) { count++; } } const indices = reuseArray?.length === count ? reuseArray : new Uint32Array(count); let idx = 0; for (const datumIndex of prevIndices) { if (isIndexInAggregation(xValues, d0, d1, indexData, maxRange, datumIndex, xNeedsValueOf, xValuesLength)) { indices[idx++] = datumIndex; } } return indices; } function computeLineAggregation(domain, xValues, yValues, options) { const xValuesLength = xValues.length; if (xValuesLength < AGGREGATION_THRESHOLD) return; const [d0, d1] = domain; const { xNeedsValueOf, yNeedsValueOf, existingFilters } = options; let maxRange = aggregationRangeFittingPoints(xValues, d0, d1, { xNeedsValueOf }); const existingFilter = existingFilters?.find((f) => f.maxRange === maxRange); let { indexData, valueData } = createAggregationIndices(xValues, yValues, yValues, d0, d1, maxRange, { xNeedsValueOf, yNeedsValueOf, reuseIndexData: existingFilter?.indexData, reuseValueData: existingFilter?.valueData }); let indices = buildIndicesFromAggregation2( xValues, d0, d1, indexData, maxRange, xNeedsValueOf, xValuesLength, existingFilter?.indices ); const filters = [{ maxRange, indices, indexData, valueData }]; while (indices.length > MAX_POINTS2 && maxRange > 64) { const currentMaxRange = maxRange; const nextMaxRange = Math.trunc(currentMaxRange / 2); const nextExistingFilter = existingFilters?.find((f) => f.maxRange === nextMaxRange); const compacted = compactAggregationIndices(indexData, valueData, currentMaxRange, { reuseIndexData: nextExistingFilter?.indexData, reuseValueData: nextExistingFilter?.valueData }); maxRange = compacted.maxRange; indexData = compacted.indexData; valueData = compacted.valueData; indices = filterIndicesFromPrevious( indices, xValues, d0, d1, indexData, maxRange, xNeedsValueOf, xValuesLength, nextExistingFilter?.indices ); filters.push({ maxRange, indices, indexData, valueData }); } filters.reverse(); return filters; } function computeLineAggregationPartial(domain, xValues, yValues, options) { const xValuesLength = xValues.length; if (xValuesLength < AGGREGATION_THRESHOLD) return; const [d0, d1] = domain; const { xNeedsValueOf, yNeedsValueOf, targetRange, existingFilters } = options; const finestMaxRange = aggregationRangeFittingPoints(xValues, d0, d1, { xNeedsValueOf }); const targetMaxRange = Math.min(finestMaxRange, nextPowerOf2(Math.max(targetRange, AGGREGATION_MIN_RANGE))); const existingFilter = existingFilters?.find((f) => f.maxRange === targetMaxRange); const { indexData, valueData } = createAggregationIndices(xValues, yValues, yValues, d0, d1, targetMaxRange, { xNeedsValueOf, yNeedsValueOf, reuseIndexData: existingFilter?.indexData, reuseValueData: existingFilter?.valueData }); const indices = buildIndicesFromAggregation2( xValues, d0, d1, indexData, targetMaxRange, xNeedsValueOf, xValuesLength, existingFilter?.indices ); const immediateLevel = { maxRange: targetMaxRange, indices, indexData, valueData }; function computeRemaining() { const allLevels = computeLineAggregation([d0, d1], xValues, yValues, { xNeedsValueOf, yNeedsValueOf, existingFilters }); return allLevels?.filter((level) => level.maxRange !== targetMaxRange) ?? []; } return { immediate: [immediateLevel], computeRemaining }; } function aggregateLineData(scale2, xValues, yValues, domainInput, xNeedsValueOf, yNeedsValueOf) { const [d0, d1] = aggregationDomain(scale2, domainInput); return computeLineAggregation([d0, d1], xValues, yValues, { xNeedsValueOf, yNeedsValueOf }); } var memoizedAggregateLineData = simpleMemorize2(aggregateLineData); function aggregateLineDataFromDataModel(scale2, dataModel, processedData, yKey, series, existingFilters) { const xValues = dataModel.resolveColumnById(series, "xValue", processedData); const yValues = dataModel.resolveColumnById(series, yKey, processedData); const domainInput = dataModel.getDomain(series, "xValue", "value", processedData); const xNeedsValueOf = dataModel.resolveColumnNeedsValueOf(series, "xValue", processedData); const yNeedsValueOf = dataModel.resolveColumnNeedsValueOf(series, yKey, processedData); if (existingFilters) { const [d0, d1] = aggregationDomain(scale2, domainInput); return computeLineAggregation([d0, d1], xValues, yValues, { xNeedsValueOf, yNeedsValueOf, existingFilters }); } return memoizedAggregateLineData(scale2, xValues, yValues, domainInput, xNeedsValueOf, yNeedsValueOf); } function aggregateLineDataFromDataModelPartial(scale2, dataModel, processedData, yKey, series, targetRange, existingFilters) { const xValues = dataModel.resolveColumnById(series, "xValue", processedData); const yValues = dataModel.resolveColumnById(series, yKey, processedData); const domainInput = dataModel.getDomain(series, "xValue", "value", processedData); const xNeedsValueOf = dataModel.resolveColumnNeedsValueOf(series, "xValue", processedData); const yNeedsValueOf = dataModel.resolveColumnNeedsValueOf(series, yKey, processedData); const [d0, d1] = aggregationDomain(scale2, domainInput); return computeLineAggregationPartial([d0, d1], xValues, yValues, { xNeedsValueOf, yNeedsValueOf, targetRange, existingFilters }); } // packages/ag-charts-community/src/chart/series/cartesian/lineSeriesProperties.ts var LineSeriesProperties = class extends CartesianSeriesProperties { constructor() { super(...arguments); this.stroke = "#874349"; this.strokeWidth = 2; this.strokeOpacity = 1; this.lineDash = [0]; this.lineDashOffset = 0; this.interpolation = new InterpolationProperties(); this.marker = new SeriesMarker(); this.label = new Label(); this.tooltip = makeSeriesTooltip(); this.connectMissingData = false; this.sparklineMode = false; } }; __decorateClass([ addFakeTransformToInstanceProperty ], LineSeriesProperties.prototype, "xKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LineSeriesProperties.prototype, "yKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LineSeriesProperties.prototype, "xName", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LineSeriesProperties.prototype, "yName", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LineSeriesProperties.prototype, "yFilterKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LineSeriesProperties.prototype, "stackGroup", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LineSeriesProperties.prototype, "normalizedTo", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LineSeriesProperties.prototype, "title", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LineSeriesProperties.prototype, "stroke", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LineSeriesProperties.prototype, "strokeWidth", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LineSeriesProperties.prototype, "strokeOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LineSeriesProperties.prototype, "lineDash", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LineSeriesProperties.prototype, "lineDashOffset", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LineSeriesProperties.prototype, "interpolation", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LineSeriesProperties.prototype, "styler", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LineSeriesProperties.prototype, "marker", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LineSeriesProperties.prototype, "label", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LineSeriesProperties.prototype, "tooltip", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LineSeriesProperties.prototype, "connectMissingData", 2); __decorateClass([ addFakeTransformToInstanceProperty ], LineSeriesProperties.prototype, "sparklineMode", 2); // packages/ag-charts-community/src/chart/series/cartesian/lineSeries.ts var CROSS_FILTER_LINE_STROKE_OPACITY_FACTOR = 0.25; var LineSeries = class extends CartesianSeries { constructor(moduleCtx) { super({ moduleCtx, propertyKeys: DEFAULT_CARTESIAN_DIRECTION_KEYS, propertyNames: DEFAULT_CARTESIAN_DIRECTION_NAMES, categoryKey: "xValue", pickModes: [ 2 /* AXIS_ALIGNED */, 1 /* NEAREST_NODE */, 0 /* EXACT_SHAPE_MATCH */ ], datumSelectionGarbageCollection: false, segmentedDataNodes: false, animationResetFns: { path: buildResetPathFn({ getVisible: () => this.visible, getOpacity: () => this.getOpacity() }), label: resetLabelFn, datum: (node, datum) => ({ ...resetMarkerFn(node), ...resetMarkerPositionFn(node, datum) }) }, clipFocusBox: false }); this.properties = new LineSeriesProperties(); this.aggregationManager = new AggregationManager(); } get pickModeAxis() { return this.properties.sparklineMode ? "main" : "main-category"; } isNormalized() { return this.properties.normalizedTo != null; } renderToOffscreenCanvas() { const hasMarkers = (this.contextNodeData?.nodeData?.length ?? 0) > 0; return hasMarkers && this.getDrawingMode(false) === "cutout" || super.renderToOffscreenCanvas(); } async processData(dataController) { if (this.data == null) return; const { data, visible, seriesGrouping: { groupIndex = this.id, stackCount = 0 } = {} } = this; const { xKey, yKey, yFilterKey, connectMissingData, normalizedTo } = this.properties; const xScale = this.axes["x" /* X */]?.scale; const yScale = this.axes["y" /* Y */]?.scale; const { isContinuousX, xScaleType, yScaleType } = this.getScaleInformation({ xScale, yScale }); const stacked = stackCount > 1 || normalizedTo != null; const common = { invalidValue: null }; if (connectMissingData && stacked) { common.invalidValue = 0; } if (stacked && !visible) { common.forceValue = 0; } const idMap = { value: `area-stack-${groupIndex}-yValue`, marker: `area-stack-${groupIndex}-yValues-marker` }; const props = []; const allowNullKey = this.properties.allowNullKeys ?? false; if (!isContinuousX || stacked) { props.push(keyProperty(xKey, xScaleType, { id: "xKey", allowNullKey })); } props.push( valueProperty(xKey, xScaleType, { id: "xValue", allowNullKey }), valueProperty(yKey, yScaleType, { id: `yValueRaw`, ...common, invalidValue: void 0 }) ); if (yFilterKey != null) { props.push(valueProperty(yFilterKey, yScaleType, { id: "yFilterRaw" })); } if (stacked) { props.push( ...groupAccumulativeValueProperty( yKey, "normal", { id: `yValueCumulative`, ...common, groupId: idMap.marker }, yScaleType ) ); } if (isDefined(normalizedTo)) { props.push( valueProperty(yKey, yScaleType, { id: `yValue`, ...common, groupId: idMap.value }), normaliseGroupTo(Object.values(idMap), normalizedTo) ); } if (this.needsDataModelDiff()) { props.push(animationValidation(isContinuousX ? ["xValue"] : void 0)); if (this.processedData) { props.push(diff(this.id, this.processedData)); } } const { dataModel, processedData } = await this.requestDataModel(dataController, data, { props, groupByKeys: stacked, groupByData: !stacked }); this.aggregateData(dataModel, processedData); this.animationState.transition("updateData"); } yValueKey() { return this.isNormalized() ? "yValue" : "yValueRaw"; } yCumulativeKey(processData) { return processData.type === "grouped" ? "yValueCumulative" : this.yValueKey(); } xCoordinateRange(xValue, pixelSize) { const { marker } = this.properties; const x = this.axes["x" /* X */].scale.convert(xValue); const r = marker.enabled ? 0.5 * marker.size * pixelSize : 0; return [x - r, x + r]; } yCoordinateRange(yValues, pixelSize) { const { marker } = this.properties; const y = this.axes["y" /* Y */].scale.convert(yValues[0]); const r = marker.enabled ? 0.5 * marker.size * pixelSize : 0; return [y - r, y + r]; } getSeriesDomain(direction) { const { dataModel, processedData, axes } = this; if (!dataModel || !processedData) return { domain: [] }; const yAxis = axes["y" /* Y */]; if (direction === "x" /* X */) { const xDef = dataModel.resolveProcessedDataDefById(this, `xValue`); const xDomain = dataModel.getDomain(this, `xValue`, "value", processedData); if (xDef?.def.type === "value" && xDef.def.valueType === "category") { const sortMetadata = dataModel.getKeySortMetadata(this, "xValue", processedData); return { domain: xDomain.domain, sortMetadata }; } return { domain: fixNumericExtent(extent(xDomain.domain)) }; } const yExtent = this.domainForClippedRange( "y" /* Y */, [this.yCumulativeKey(processedData)], "xValue" ); if (this.isNormalized() && yAxis instanceof NumberAxis && !(yAxis instanceof LogAxis)) { const fixedYExtent = Number.isFinite(yExtent[1] - yExtent[0]) ? [Math.min(yExtent[0], 0), Math.max(yExtent[1], 0)] : []; return { domain: fixNumericExtent(fixedYExtent) }; } else { return { domain: fixNumericExtent(yExtent) }; } } getSeriesRange(_direction, visibleRange) { return this.domainForVisibleRange( "y" /* Y */, [this.yCumulativeKey(this.processedData)], "xValue", visibleRange ); } getZoomRangeFittingItems(xVisibleRange, yVisibleRange, minVisibleItems) { return this.zoomFittingVisibleItems( "xValue", [this.yCumulativeKey(this.processedData)], xVisibleRange, yVisibleRange, minVisibleItems ); } getVisibleItems(xVisibleRange, yVisibleRange, minVisibleItems) { return this.countVisibleItems( "xValue", [this.yCumulativeKey(this.processedData)], xVisibleRange, yVisibleRange, minVisibleItems ); } aggregateData(dataModel, processedData) { this.aggregationManager.markStale(processedData.input.count); if (processedData.type !== "ungrouped") return; if (processedDataIsAnimatable(processedData)) return; const xAxis = this.axes["x" /* X */]; if (xAxis == null) return; const targetRange = this.estimateTargetRange(); this.aggregationManager.aggregate({ computePartial: (existingFilters) => aggregateLineDataFromDataModelPartial( xAxis.scale.type, dataModel, processedData, this.yCumulativeKey(processedData), this, targetRange, existingFilters ), computeFull: (existingFilters) => aggregateLineDataFromDataModel( xAxis.scale.type, dataModel, processedData, this.yCumulativeKey(processedData), this, existingFilters ), targetRange }); const filters = this.aggregationManager.filters; if (filters && filters.length > 0) { debugMetrics_exports.record( `${this.type}:aggregation`, filters.map((f) => f.maxRange) ); } } estimateTargetRange() { const xAxis = this.axes["x" /* X */]; if (xAxis?.scale?.range) { const [r0, r1] = xAxis.scale.range; return Math.abs(r1 - r0); } return this.ctx.scene?.canvas?.width ?? 800; } /** * Creates the context object for efficient node datum creation. * Caches expensive-to-compute values that are reused across all datum iterations * to minimize memory allocations. Only caches values that are expensive to * compute - cheap property lookups use `this` directly. */ createNodeDatumContext(xAxis, yAxis) { const { dataModel, processedData } = this; if (!dataModel || !processedData) return void 0; const xScale = xAxis.scale; const yScale = yAxis.scale; const rawData = processedData.dataSources.get(this.id)?.data ?? []; const [r0, r1] = xScale.range; const range3 = Math.abs(r1 - r0); this.aggregationManager.ensureLevelForRange(range3); const dataAggregationFilter = this.aggregationManager.getFilterForRange(range3); const canIncrementallyUpdate = this.canIncrementallyUpdateNodes(dataAggregationFilter != null); return { xAxis, yAxis, rawData, xValues: dataModel.resolveColumnById(this, "xValue", processedData), yRawValues: dataModel.resolveColumnById(this, "yValueRaw", processedData), yCumulativeValues: dataModel.resolveColumnById(this, this.yCumulativeKey(processedData), processedData), selectionValues: this.properties.yFilterKey ? dataModel.resolveColumnById(this, "yFilterRaw", processedData) : void 0, xScale, yScale, xOffset: (xScale.bandwidth ?? 0) / 2, yOffset: (yScale.bandwidth ?? 0) / 2, size: this.properties.marker.enabled ? this.properties.marker.size : 0, yDomain: this.getSeriesDomain("y" /* Y */).domain, labelsEnabled: this.properties.label.enabled, animationEnabled: !this.ctx.animationManager.isSkipped(), canIncrementallyUpdate, dataAggregationFilter, range: range3, xKey: this.properties.xKey, yKey: this.properties.yKey, xName: this.properties.xName, yName: this.properties.yName, legendItemName: this.properties.legendItemName, connectMissingData: this.properties.connectMissingData, capDefaults: { lengthRatioMultiplier: this.properties.marker.getDiameter(), lengthMax: Infinity }, nodes: canIncrementallyUpdate ? this.contextNodeData.nodeData : [], spanPoints: [], nodeIndex: 0 }; } /** * Processes a single datum and updates the context's nodes and spanPoints arrays. * Uses the scratch object to avoid per-iteration allocations. */ handleDatum(ctx, scratch, datumIndex) { scratch.datum = ctx.rawData[datumIndex]; scratch.xDatum = ctx.xValues[datumIndex]; scratch.yDatum = ctx.yRawValues[datumIndex]; scratch.yCumulative = ctx.yCumulativeValues[datumIndex]; scratch.selected = ctx.selectionValues?.[datumIndex]; scratch.x = ctx.xScale.convert(scratch.xDatum) + ctx.xOffset; scratch.y = ctx.yScale.convert(scratch.yCumulative) + ctx.yOffset; if (!Number.isFinite(scratch.x)) return; if (scratch.yDatum != null) { const labelText = ctx.labelsEnabled ? this.getLabelText( scratch.yDatum, scratch.datum, ctx.yKey, "y", ctx.yDomain, this.properties.label, { value: scratch.yDatum, datum: scratch.datum, xKey: ctx.xKey, yKey: ctx.yKey, xName: ctx.xName, yName: ctx.yName, legendItemName: ctx.legendItemName } ) : void 0; const canReuseNode = ctx.canIncrementallyUpdate && ctx.nodeIndex < ctx.nodes.length; if (canReuseNode) { const existingNode = ctx.nodes[ctx.nodeIndex]; existingNode.datum = scratch.datum; existingNode.datumIndex = datumIndex; existingNode.point = { x: scratch.x, y: scratch.y, size: ctx.size }; existingNode.midPoint = { x: scratch.x, y: scratch.y }; existingNode.cumulativeValue = scratch.yCumulative; existingNode.yValue = scratch.yDatum; existingNode.xValue = scratch.xDatum; existingNode.labelText = labelText; existingNode.selected = scratch.selected; } else { ctx.nodes.push({ series: this, datum: scratch.datum, datumIndex, yKey: ctx.yKey, xKey: ctx.xKey, point: { x: scratch.x, y: scratch.y, size: ctx.size }, midPoint: { x: scratch.x, y: scratch.y }, cumulativeValue: scratch.yCumulative, yValue: scratch.yDatum, xValue: scratch.xDatum, capDefaults: ctx.capDefaults, labelText, selected: scratch.selected }); } ctx.nodeIndex++; } this.updateSpanPoints(ctx, scratch); } /** * Updates span points array based on current scratch values. */ updateSpanPoints(ctx, scratch) { const currentSpanPoints = ctx.spanPoints.at(-1); if (scratch.yDatum != null) { const spanPoint = { point: { x: scratch.x, y: scratch.y }, xDatum: scratch.xDatum, yDatum: scratch.yCumulative }; if (Array.isArray(currentSpanPoints)) { currentSpanPoints.push(spanPoint); } else if (currentSpanPoints == null) { ctx.spanPoints.push([spanPoint]); } else { currentSpanPoints.skip += 1; ctx.spanPoints.push([spanPoint]); } } else if (!ctx.connectMissingData) { if (Array.isArray(currentSpanPoints) || currentSpanPoints == null) { ctx.spanPoints.push({ skip: 0 }); } else { currentSpanPoints.skip += 1; } } } /** * Populates node data by iterating over the visible range. */ populateNodeData(ctx) { const scratch = { datum: void 0, xDatum: void 0, yDatum: void 0, yCumulative: 0, selected: void 0, x: 0, y: 0 }; const indices = ctx.dataAggregationFilter?.indices; let [start2, end3] = this.visibleRangeIndices("xValue", ctx.xAxis.range, indices); start2 = Math.max(start2 - 1, 0); end3 = Math.min(end3 + 1, indices?.length ?? ctx.xValues.length); if (this.processedData.input.count < 1e3) { start2 = 0; end3 = this.processedData.input.count; } for (let i = start2; i < end3; i += 1) { this.handleDatum(ctx, scratch, indices?.[i] ?? i); } } /** * Creates the initial result context object. * Note: strokeData and segments are computed in assembleResult, but we need valid defaults * for the early return case (when !this.visible). */ initializeResult(ctx) { return { itemId: ctx.yKey, nodeData: ctx.nodes, labelData: ctx.nodes, strokeData: { itemId: ctx.yKey, spans: [] }, // Default for early return scales: this.calculateScaling(), visible: this.visible, crossFiltering: false, styles: getMarkerStyles(this, this.properties, this.properties.marker), segments: void 0 }; } /** * Assembles the final result by computing strokeData, crossFiltering, and segments. */ assembleResult(ctx, result) { const strokeSpans = ctx.spanPoints.flatMap((p) => { return Array.isArray(p) ? interpolatePoints(p, this.properties.interpolation) : []; }); result.strokeData = { itemId: ctx.yKey, spans: strokeSpans }; result.crossFiltering = ctx.selectionValues?.some((selectionValue, index) => selectionValue === ctx.yRawValues[index]) ?? false; result.segments = calculateSegments( this.properties.segmentation, ctx.xAxis, ctx.yAxis, this.chart.seriesRect, this.ctx.scene, false ); return result; } isPathOrSelectionDirty() { return this.properties.marker.isDirty(); } updatePathNodes(opts) { const { paths: [lineNode], visible, animationEnabled } = opts; const crossFiltering = this.contextNodeData?.crossFiltering === true; const merged = mergeDefaults(this.getHighlightStyle(), this.getStyle()); const { strokeWidth, stroke, strokeOpacity, lineDash, lineDashOffset, opacity } = merged; const segments = this.contextNodeData?.segments; lineNode.setProperties({ segments, fill: void 0, lineJoin: "round", pointerEvents: 1 /* None */, opacity, stroke, strokeWidth, strokeOpacity: strokeOpacity * (crossFiltering ? CROSS_FILTER_LINE_STROKE_OPACITY_FACTOR : 1), lineDash, lineDashOffset }); lineNode.datum = segments; if (!animationEnabled) { lineNode.visible = visible; } updateClipPath(this, lineNode); } updateDatumSelection(opts) { let { nodeData } = opts; const { datumSelection } = opts; const { contextNodeData, processedData, axes, properties } = this; const { marker } = properties; const markersEnabled = contextNodeData?.crossFiltering === true || markerEnabled(processedData.input.count, axes["x" /* X */].scale, marker); nodeData = markersEnabled ? nodeData : []; if (marker.isDirty()) { datumSelection.clear(); datumSelection.cleanup(); } if (!processedDataIsAnimatable(this.processedData)) { return datumSelection.update(nodeData); } return datumSelection.update(nodeData, void 0, (datum) => createDatumId(datum.xValue)); } updateDatumStyles(opts) { const { datumSelection, isHighlight } = opts; const { marker } = this.properties; const highlightedDatum = this.ctx.highlightManager.getActiveHighlight(); datumSelection.each((node, datum) => { if (!datumSelection.isGarbage(node)) { const highlightState = this.getHighlightState(highlightedDatum, opts.isHighlight, datum.datumIndex); const stylerStyle = this.getStyle(highlightState); const { stroke, strokeWidth, strokeOpacity } = stylerStyle; const params = this.makeItemStylerParams( this.dataModel, this.processedData, datum.datumIndex, stylerStyle.marker ); datum.style = this.getMarkerStyle( marker, datum, params, { isHighlight, highlightState }, stylerStyle.marker, { stroke, strokeWidth, strokeOpacity } ); } }); } updateDatumNodes(opts) { const { contextNodeData } = this; if (!contextNodeData) { return; } const { datumSelection, isHighlight } = opts; const applyTranslation = this.ctx.animationManager.isSkipped(); const fillBBox = this.getShapeFillBBox(); const highlightedDatum = this.ctx.highlightManager.getActiveHighlight(); const drawingMode = this.getDrawingMode(isHighlight, opts.drawingMode); datumSelection.each((node, datum) => { const state = this.getHighlightState(highlightedDatum, isHighlight, datum.datumIndex); const style2 = datum.style ?? contextNodeData.styles[state]; this.applyMarkerStyle(style2, node, datum.point, fillBBox, { applyTranslation, selected: datum.selected }); node.drawingMode = this.resolveMarkerDrawingModeForState(drawingMode, style2); }); if (!isHighlight) { this.properties.marker.markClean(); } } updateLabelSelection(opts) { return opts.labelSelection.update(this.isLabelEnabled() ? opts.labelData : []); } updateLabelNodes(opts) { const { isHighlight = false } = opts; const activeHighlight = this.ctx.highlightManager?.getActiveHighlight(); const params = this.makeLabelFormatterParams(); opts.labelSelection.each((text, datum) => { const style2 = getLabelStyles(this, datum, params, this.properties.label, isHighlight, activeHighlight); const { enabled, fontStyle, fontWeight: fontWeight2, fontSize, fontFamily, color: color2 } = style2; if (enabled && datum?.labelText) { text.fontStyle = fontStyle; text.fontWeight = fontWeight2; text.fontSize = fontSize; text.fontFamily = fontFamily; text.textAlign = "center"; text.textBaseline = "bottom"; text.text = datum.labelText; text.x = datum.point.x; text.y = datum.point.y - 10; text.fill = color2; text.visible = true; text.fillOpacity = this.getHighlightStyle(isHighlight, datum.datumIndex).opacity ?? 1; text.setBoxing(style2); } else { text.visible = false; } }); } makeStylerParams(highlightStateEnum) { const { id: seriesId } = this; const { marker, lineDash, lineDashOffset, stroke, strokeOpacity, strokeWidth, xKey, yKey } = this.properties; const highlightState = toHighlightString(highlightStateEnum ?? 0 /* None */); return { marker: { fill: marker.fill, fillOpacity: marker.fillOpacity, size: marker.size, shape: marker.shape, stroke: marker.stroke, strokeOpacity: marker.strokeOpacity, strokeWidth: marker.strokeWidth, lineDash: marker.lineDash, lineDashOffset: marker.lineDashOffset }, highlightState, lineDash, lineDashOffset, seriesId, stroke, strokeOpacity, strokeWidth, xKey, yKey }; } makeItemStylerParams(dataModel, processedData, datumIndex, style2) { const { xKey, yKey } = this.properties; const xValue = dataModel.resolveColumnById(this, `xValue`, processedData)[datumIndex]; const yValue = dataModel.resolveColumnById(this, `yValueRaw`, processedData)[datumIndex]; const xDomain = dataModel.getDomain(this, `xValue`, "key", processedData).domain; const yDomain = dataModel.getDomain(this, this.yCumulativeKey(processedData), "value", processedData).domain; const fill = this.filterItemStylerFillParams(style2.fill) ?? style2.fill; return { ...datumStylerProperties(xValue, yValue, xKey, yKey, xDomain, yDomain), xValue, yValue, ...style2, fill }; } makeLabelFormatterParams() { const { xKey, xName, yKey, yName, legendItemName } = this.properties; return { xKey, xName, yKey, yName, legendItemName }; } getTooltipContent(datumIndex) { const { id: seriesId, dataModel, processedData, axes, properties } = this; const { xKey, xName, yKey, yName, tooltip, legendItemName } = properties; const allowNullKeys = properties.allowNullKeys ?? false; const xAxis = axes["x" /* X */]; const yAxis = axes["y" /* Y */]; if (!dataModel || !processedData || !xAxis || !yAxis) return; const datum = processedData.dataSources.get(this.id)?.data?.[datumIndex]; const xValue = dataModel.resolveColumnById(this, `xValue`, processedData)[datumIndex]; const yValue = dataModel.resolveColumnById(this, `yValueRaw`, processedData)[datumIndex]; if (xValue === void 0 && !allowNullKeys) return; const stylerStyle = this.getStyle(); const params = this.makeItemStylerParams(dataModel, processedData, datumIndex, stylerStyle.marker); const format = this.getMarkerStyle( this.properties.marker, { datumIndex, datum }, params, { isHighlight: false }, stylerStyle.marker ); return this.formatTooltipWithContext( tooltip, { heading: this.getAxisValueText(xAxis, "tooltip", xValue, datum, xKey, legendItemName, allowNullKeys), symbol: this.legendItemSymbol(), data: [ { label: yName, fallbackLabel: yKey, value: this.getAxisValueText(yAxis, "tooltip", yValue, datum, yKey, legendItemName), missing: isTooltipValueMissing(yValue) } ] }, { seriesId, datum, title: yName, xKey, xName, yKey, yName, ...format, ...this.getModuleTooltipParams() } ); } legendItemSymbol() { const { stroke, strokeOpacity, strokeWidth, lineDash, marker } = this.getStyle(); const markerStyle = this.getMarkerStyle( this.properties.marker, {}, void 0, { isHighlight: false, checkForHighlight: false }, { size: marker.size, shape: marker.shape, fill: marker.fill, fillOpacity: marker.fillOpacity, stroke: marker.stroke, strokeOpacity: marker.strokeOpacity, strokeWidth: marker.strokeWidth, lineDash: marker.lineDash, lineDashOffset: marker.lineDashOffset } ); return { marker: { ...markerStyle, enabled: this.properties.marker.enabled }, line: { enabled: true, stroke, strokeOpacity, strokeWidth, lineDash } }; } getLegendData(legendType) { if (legendType !== "category") { return []; } const { id: seriesId, ctx: { legendManager }, visible } = this; const { yKey: itemId, yName, title, legendItemName, showInLegend } = this.properties; return [ { legendType, id: seriesId, itemId, legendItemName, seriesId, enabled: visible && legendManager.getItemEnabled({ seriesId, itemId }), label: { text: legendItemName ?? title ?? yName ?? itemId }, symbol: this.legendItemSymbol(), hideInLegend: !showInLegend } ]; } updatePaths(opts) { this.updateLinePaths(opts.paths, opts.contextData); } updateLinePaths(paths, contextData) { const spans = contextData.strokeData.spans; const [lineNode] = paths; lineNode.path.clear(); plotLinePathStroke(lineNode, spans); lineNode.markDirty("LineSeries"); } resetDatumAnimation(data) { resetMarkerSelectionsDirect([data.datumSelection]); } animateEmptyUpdateReady(animationData) { const { datumSelection, labelSelection, annotationSelections, contextData, paths } = animationData; const { animationManager } = this.ctx; this.updateLinePaths(paths, contextData); pathSwipeInAnimation(this, animationManager, ...paths); resetMotion([datumSelection], resetMarkerPositionFn); markerSwipeScaleInAnimation( this, animationManager, { ...this.getAnimationDrawingModes(), phase: "initial" }, datumSelection ); seriesLabelFadeInAnimation(this, "labels", animationManager, labelSelection); seriesLabelFadeInAnimation(this, "annotations", animationManager, ...annotationSelections); } animateReadyResize(animationData) { const { contextData, paths } = animationData; this.updateLinePaths(paths, contextData); super.animateReadyResize(animationData); } animateWaitingUpdateReady(animationData) { const { animationManager } = this.ctx; const { datumSelection, labelSelection: labelSelections, annotationSelections, contextData, paths, previousContextData } = animationData; const [path] = paths; if (contextData.visible === false && previousContextData?.visible === false) return; this.resetDatumAnimation(animationData); this.resetLabelAnimation(animationData); const update = () => { this.resetPathAnimation(animationData); this.updateLinePaths(paths, contextData); }; const skip = () => { animationManager.skipCurrentBatch(); update(); }; if (contextData == null || previousContextData == null) { update(); markerFadeInAnimation(this, animationManager, "added", this.getAnimationDrawingModes(), datumSelection); staticFromToMotion( this.id, "path_properties", animationManager, [path], { opacity: 0 }, { opacity: this.getOpacity() }, { phase: "add" } ); seriesLabelFadeInAnimation(this, "labels", animationManager, labelSelections); seriesLabelFadeInAnimation(this, "annotations", animationManager, ...annotationSelections); return; } if (contextData.crossFiltering !== previousContextData.crossFiltering) { skip(); return; } const fns = prepareLinePathAnimation( contextData, previousContextData, this.processedData?.reduced?.diff?.[this.id], this.getOpacity() ); if (fns === void 0) { skip(); return; } else if (fns.status === "no-op") { return; } fromToMotion(this.id, "path_properties", animationManager, [path], fns.stroke.pathProperties); if (fns.status === "added") { this.updateLinePaths(paths, contextData); } else if (fns.status === "removed") { this.updateLinePaths(paths, previousContextData); } else { pathMotion(this.id, "path_update", animationManager, [path], fns.stroke.path); } if (fns.hasMotion) { markerFadeInAnimation(this, animationManager, void 0, this.getAnimationDrawingModes(), datumSelection); seriesLabelFadeInAnimation(this, "labels", animationManager, labelSelections); seriesLabelFadeInAnimation(this, "annotations", animationManager, ...annotationSelections); } this.ctx.animationManager.animate({ id: this.id, groupId: "reset_after_animation", phase: "trailing", from: {}, to: {}, onComplete: () => this.updateLinePaths(paths, contextData) }); } isLabelEnabled() { return this.properties.label.enabled; } getBandScalePadding() { return { inner: 1, outer: 0.1 }; } nodeFactory() { return new Marker(); } getStyle(highlightState) { const { styler, marker, lineDash, lineDashOffset, stroke, strokeOpacity, strokeWidth } = this.properties; const { size, shape, fill = "transparent", fillOpacity } = marker; let stylerResult = {}; if (styler) { const stylerParams = this.makeStylerParams(highlightState); const cbResult = this.cachedCallWithContext(styler, stylerParams) ?? {}; const resolved = this.ctx.optionsGraphService.resolvePartial( ["series", `${this.declarationOrder}`], cbResult, { pick: false } ); stylerResult = resolved ?? {}; } stylerResult.marker ?? (stylerResult.marker = {}); return { lineDash: stylerResult.lineDash ?? lineDash, lineDashOffset: stylerResult.lineDashOffset ?? lineDashOffset, stroke: stylerResult.stroke ?? stroke, strokeOpacity: stylerResult.strokeOpacity ?? strokeOpacity, strokeWidth: stylerResult.strokeWidth ?? strokeWidth, marker: { fill: stylerResult.marker.fill ?? fill, fillOpacity: stylerResult.marker.fillOpacity ?? fillOpacity, shape: stylerResult.marker.shape ?? shape, size: stylerResult.marker.size ?? size, lineDash: stylerResult.marker.lineDash ?? marker.lineDash ?? lineDash, lineDashOffset: stylerResult.marker.lineDashOffset ?? marker.lineDashOffset ?? lineDashOffset, stroke: stylerResult.marker.stroke ?? marker.stroke ?? stroke, strokeOpacity: stylerResult.marker.strokeOpacity ?? marker.strokeOpacity ?? strokeOpacity, strokeWidth: stylerResult.marker.strokeWidth ?? marker.strokeWidth ?? strokeWidth } }; } getFormattedMarkerStyle(datum) { const stylerStyle = this.getStyle(); const params = this.makeItemStylerParams( this.dataModel, this.processedData, datum.datumIndex, stylerStyle.marker ); return this.getMarkerStyle( this.properties.marker, datum, params, { isHighlight: true }, void 0, stylerStyle ); } computeFocusBounds(opts) { return computeMarkerFocusBounds(this, opts); } hasItemStylers() { return this.properties.styler != null || this.properties.marker.itemStyler != null || this.properties.label.itemStyler != null; } }; LineSeries.className = "LineSeries"; LineSeries.type = "line"; // packages/ag-charts-community/src/chart/series/cartesian/lineSeriesModule.ts var themeTemplate5 = { series: { stroke: SAFE_STROKE_FILL_OPERATION, strokeWidth: 2, strokeOpacity: 1, lineDash: [0], lineDashOffset: 0, interpolation: { type: "linear" }, marker: { shape: "circle", size: 7, strokeWidth: { $isUserOption: ["./stroke", 1, 0] }, fill: { $applySwitch: [ { $path: "type" }, { $palette: "fill" }, ["gradient", FILL_GRADIENT_RADIAL_REVERSED_DEFAULTS], ["image", FILL_IMAGE_DEFAULTS], ["pattern", FILL_PATTERN_DEFAULTS] ] }, stroke: { $palette: "stroke" } }, label: { ...LABEL_BOXING_DEFAULTS, enabled: false, fontSize: { $ref: "fontSize" }, fontFamily: { $ref: "fontFamily" }, fontWeight: { $ref: "fontWeight" }, color: { $ref: "textColor" } }, tooltip: { range: { $if: [ { $eq: [{ $path: ["/tooltip/range", "nearest"] }, "area"] }, "nearest", { $path: ["/tooltip/range", "nearest"] } ] }, position: { anchorTo: { $path: ["/tooltip/position/anchorTo", "node"] } } }, highlight: MARKER_SERIES_HIGHLIGHT_STYLE, segmentation: SEGMENTATION_DEFAULTS } }; var LineSeriesModule = { type: "series", name: "line", chartType: "cartesian", stackable: true, version: VERSION, dependencies: [CartesianChartModule], options: lineSeriesOptionsDef, predictAxis: predictCartesianNonPrimitiveAxis, defaultAxes: { y: { type: "number" /* NUMBER */, position: "left" /* LEFT */ }, x: { type: "category" /* CATEGORY */, position: "bottom" /* BOTTOM */ } }, axisKeys: { ["x" /* X */]: "xKeyAxis", ["y" /* Y */]: "yKeyAxis" }, themeTemplate: themeTemplate5, create: (ctx) => new LineSeries(ctx) }; // packages/ag-charts-community/src/chart/series/cartesian/scatterSeries.ts var ScatterSeries = class extends BubbleSeries { }; ScatterSeries.className = "ScatterSeries"; ScatterSeries.type = "scatter"; // packages/ag-charts-community/src/chart/series/cartesian/scatterSeriesOptionsDef.ts var scatterSeriesThemeableOptionsDef = { title: string, showInMiniChart: boolean, label: { placement: union("top", "right", "bottom", "left"), ...seriesLabelOptionsDefs }, tooltip: tooltipOptionsDefs, errorBar: errorBarThemeableOptionsDefs, styler: callbackDefs(markerOptionsDefs), maxRenderedItems: number, ...commonSeriesThemeableOptionsDefs, ...without(markerOptionsDefs, ["enabled"]), highlight: multiSeriesHighlightOptionsDef(shapeHighlightOptionsDef, shapeHighlightOptionsDef) }; var scatterSeriesOptionsDef = { ...scatterSeriesThemeableOptionsDef, ...commonSeriesOptionsDefs, type: required(constant("scatter")), xKey: required(string), yKey: required(string), labelKey: string, xName: string, yName: string, labelName: string, legendItemName: string, xKeyAxis: string, yKeyAxis: string, errorBar: errorBarOptionsDefs, highlight: multiSeriesHighlightOptionsDef(shapeHighlightOptionsDef, shapeHighlightOptionsDef) }; scatterSeriesOptionsDef.xFilterKey = undocumented(string); scatterSeriesOptionsDef.yFilterKey = undocumented(string); scatterSeriesOptionsDef.sizeFilterKey = undocumented(string); // packages/ag-charts-community/src/chart/series/cartesian/scatterSeriesModule.ts var themeTemplate6 = { series: { shape: "circle", size: 7, fill: { $applySwitch: [ { $path: "type" }, { $palette: "fill" }, ["gradient", FILL_GRADIENT_RADIAL_REVERSED_DEFAULTS], ["image", FILL_IMAGE_DEFAULTS], ["pattern", FILL_PATTERN_DEFAULTS] ] }, stroke: { $palette: "stroke" }, fillOpacity: 0.8, maxRenderedItems: 2e3, label: { ...LABEL_BOXING_DEFAULTS, enabled: false, fontSize: { $ref: "fontSize" }, fontFamily: { $ref: "fontFamily" }, fontWeight: { $ref: "fontWeight" }, color: { $ref: "textColor" } }, tooltip: { range: { $if: [ { $eq: [{ $path: ["/tooltip/range", "nearest"] }, "area"] }, "nearest", { $path: ["/tooltip/range", "nearest"] } ] }, position: { anchorTo: { $path: ["/tooltip/position/anchorTo", "node"] } } }, highlight: MULTI_SERIES_HIGHLIGHT_STYLE } }; var ScatterSeriesModule = { type: "series", name: "scatter", chartType: "cartesian", version: VERSION, dependencies: [CartesianChartModule], options: scatterSeriesOptionsDef, predictAxis: predictCartesianAxis, defaultAxes: { x: { type: "number" /* NUMBER */, position: "bottom" /* BOTTOM */ }, y: { type: "number" /* NUMBER */, position: "left" /* LEFT */ } }, axisKeys: { ["x" /* X */]: "xKeyAxis", ["y" /* Y */]: "yKeyAxis" }, themeTemplate: themeTemplate6, create: (ctx) => new ScatterSeries(ctx) }; // packages/ag-charts-community/src/module-bundles/cartesian-series.ts var AllCartesianSeriesModule = [ AreaSeriesModule, BarSeriesModule, BubbleSeriesModule, HistogramSeriesModule, LineSeriesModule, ScatterSeriesModule ]; // packages/ag-charts-community/src/module-bundles/cartesian.ts var AllCartesianModule = [ AllCartesianAxesModule, AllCartesianSeriesModule, LegendModule, LocaleModule ].flat(); // packages/ag-charts-community/src/chart/series/polar/polarSeries.ts var DEFAULT_POLAR_DIRECTION_KEYS = { ["angle" /* Angle */]: ["angleKey"], ["radius" /* Radius */]: ["radiusKey"] }; var DEFAULT_POLAR_DIRECTION_NAMES = { ["angle" /* Angle */]: ["angleName"], ["radius" /* Radius */]: ["radiusName"] }; var PolarSeries = class extends DataModelSeries { constructor({ categoryKey, pickModes = [1 /* NEAREST_NODE */, 0 /* EXACT_SHAPE_MATCH */], canHaveAxes = false, animationResetFns, ...opts }) { super({ ...opts, categoryKey, pickModes, canHaveAxes }); this.directions = ["angle" /* Angle */, "radius" /* Radius */]; this.itemGroup = this.contentGroup.appendChild(new Group({ name: "items" })); this.nodeData = []; this.itemSelection = Selection.select( this.itemGroup, () => this.nodeFactory(), false ); this.labelSelection = Selection.select( this.labelGroup, () => this.labelFactory(), false ); this.highlightSelection = Selection.select( this.highlightNodeGroup, () => this.nodeFactory() ); this.highlightLabelSelection = Selection.select( this.highlightLabelGroup, () => this.labelFactory() ); /** * The center of the polar series (for example, the center of a pie). * If the polar chart has multiple series, all of them will have their * center set to the same value as a result of the polar chart layout. * The center coordinates are not supposed to be set by the user. */ this.centerX = 0; this.centerY = 0; /** * The maximum radius the series can use. * This value is set automatically as a result of the polar chart layout * and is not supposed to be set by the user. */ this.radius = 0; /** * The largest marker size of any series in the same chart. Used to determine the extent of pointer interaction * with the series. */ this.maxChartMarkerSize = 0; this.animationResetFns = animationResetFns; this.animationState = new StateMachine( "empty", { empty: { update: { target: "ready", action: (data) => this.animateEmptyUpdateReady(data) }, reset: "empty", skip: "ready" }, ready: { updateData: "waiting", clear: "clearing", highlight: (data) => this.animateReadyHighlight(data), highlightMarkers: (data) => this.animateReadyHighlightMarkers(data), resize: (data) => this.animateReadyResize(data), reset: "empty", skip: "ready" }, waiting: { update: { target: "ready", action: (data) => this.animateWaitingUpdateReady(data) }, reset: "empty", skip: "ready" }, clearing: { update: { target: "empty", action: (data) => this.animateClearingUpdateEmpty(data) }, reset: "empty", skip: "ready" } }, () => this.checkProcessedDataAnimatable() ); this.cleanup.register( this.ctx.eventsHub.on("legend:item-click", (event) => this.onLegendItemClick(event)), this.ctx.eventsHub.on("legend:item-double-click", (event) => this.onLegendItemDoubleClick(event)) ); } getItemNodes() { return [...this.itemGroup.children()]; } getNodeData() { return this.nodeData; } getKeyAxis(direction) { if (direction === "angle" /* Angle */) return this.properties.angleKeyAxis; if (direction === "radius" /* Radius */) return this.properties.radiusKeyAxis; } setZIndex(zIndex) { super.setZIndex(zIndex); this.contentGroup.zIndex = [zIndex, 1 /* FOREGROUND */]; this.highlightGroup.zIndex = [zIndex, 2 /* HIGHLIGHT */]; this.labelGroup.zIndex = [zIndex, 3 /* LABEL */]; } resetAnimation(phase) { if (phase === "initial") { this.animationState.transition("reset"); } else if (phase === "ready") { this.animationState.transition("skip"); } } labelFactory() { const text = new Text(); text.pointerEvents = 1 /* None */; return text; } getInnerRadius() { return 0; } computeLabelsBBox(_options, _seriesRect) { return null; } getShapeFillBBox() { const outerRadius = this.radius; return { series: new BBox(-outerRadius, -outerRadius, outerRadius * 2, outerRadius * 2), axis: new BBox(-outerRadius, -outerRadius, outerRadius * 2, outerRadius * 2) }; } resetAllAnimation() { const { item, label } = this.animationResetFns ?? {}; this.ctx.animationManager.stopByAnimationGroupId(this.id); if (item) { resetMotion([this.itemSelection, this.highlightSelection], item); } if (label) { resetMotion([this.labelSelection, this.highlightLabelSelection], label); } this.itemSelection.cleanup(); this.labelSelection.cleanup(); this.highlightSelection.cleanup(); this.highlightLabelSelection.cleanup(); } animateEmptyUpdateReady(_data) { this.ctx.animationManager.skipCurrentBatch(); this.resetAllAnimation(); } animateWaitingUpdateReady(_data) { this.ctx.animationManager.skipCurrentBatch(); this.resetAllAnimation(); } animateReadyHighlight(_data) { const { item, label } = this.animationResetFns ?? {}; if (item) { resetMotion([this.highlightSelection], item); } if (label) { resetMotion([this.highlightLabelSelection], label); } } animateReadyHighlightMarkers(_data) { } animateReadyResize(_data) { this.resetAllAnimation(); } animateClearingUpdateEmpty(_data) { this.ctx.animationManager.skipCurrentBatch(); this.resetAllAnimation(); } computeFocusBounds(opts) { const datum = this.getNodeData()?.[opts.datumIndex]; if (datum !== void 0) { return this.itemSelection.select((node) => node instanceof Path && node.datum === datum)[0]; } return void 0; } getSeriesRange() { return [Number.NaN, Number.NaN]; } isSeriesHighlighted(highlightedDatum, legendItemValues) { if (!this.properties.highlight.enabled) { return false; } const { series, legendItemName: activeLegendItemName, datumIndex } = highlightedDatum ?? {}; const legendItemName = typeof datumIndex === "number" ? legendItemValues?.[datumIndex] : void 0; return series === this || legendItemName != null && legendItemName === activeLegendItemName; } }; // packages/ag-charts-community/src/chart/polarChart.ts var PolarChart = class extends Chart { constructor(options, resources) { super(options, resources); this.axes = this.createChartAxes(); this.padding = new Padding(40); this.ctx.axisManager.axisGroup.zIndex = 8 /* AXIS_FOREGROUND */; } createChartAxes() { return new PolarChartAxes(); } getChartType() { return "polar"; } isDataTransactionSupported() { return !this.series.some((s) => s.type === "pie" || s.type === "donut"); } async performLayout(ctx) { const seriesRect = ctx.layoutBox.clone().shrink(this.seriesArea.getPadding()); this.seriesRect = seriesRect; this.animationRect = seriesRect; this.seriesRoot.translationX = seriesRect.x; this.seriesRoot.translationY = seriesRect.y; await this.computeCircle(seriesRect); for (const axis of this.axes) { axis.update(); } let maxMarkerSize = 0; for (const series of this.series) { maxMarkerSize = Math.max(maxMarkerSize, series.properties.marker?.size ?? 0); } for (const series of this.series.filter(isPolarSeries)) { series.maxChartMarkerSize = maxMarkerSize; } this.ctx.layoutManager.emitLayoutComplete(ctx, { series: { visible: true, rect: seriesRect, paddedRect: ctx.layoutBox } }); } updateAxes(seriesBox, cx, cy, radius) { if (this.axes.length === 0) return; const angleAxis = this.axes["angle" /* Angle */]; const radiusAxis = this.axes["radius" /* Radius */]; const angleScale = angleAxis.scale; const innerRadiusRatio = radiusAxis.innerRadiusRatio; angleAxis.innerRadiusRatio = innerRadiusRatio; angleAxis.computeRange(); angleAxis.gridLength = radius; radiusAxis.gridAngles = angleScale.ticks({ nice: [angleAxis.nice, angleAxis.nice], interval: void 0, tickCount: void 0, minTickCount: 0, maxTickCount: Infinity })?.ticks?.map((value) => angleScale.convert(value)); radiusAxis.gridRange = angleAxis.range; radiusAxis.range = [radius, radius * innerRadiusRatio]; for (const axis of [angleAxis, radiusAxis]) { axis.translation.x = seriesBox.x + cx; axis.translation.y = seriesBox.y + cy; axis.calculateLayout(); } } async computeCircle(seriesBox) { const polarSeries = this.series.filter(isPolarSeries); const setSeriesCircle = (cx, cy, r) => { this.updateAxes(seriesBox, cx, cy, r); for (const series of polarSeries) { series.centerX = cx; series.centerY = cy; series.radius = r; } const pieSeries = polarSeries.filter((s) => s.type === "donut" || s.type === "pie"); if (pieSeries.length > 1) { const innerRadii = pieSeries.map((series) => { const innerRadius = series.getInnerRadius(); return { series, innerRadius }; }).sort((a, b) => a.innerRadius - b.innerRadius); innerRadii.at(-1).series.surroundingRadius = void 0; for (let i = 0; i < innerRadii.length - 1; i++) { innerRadii[i].series.surroundingRadius = innerRadii[i + 1].innerRadius; } } }; const centerX = seriesBox.width / 2; const centerY = seriesBox.height / 2; const initialRadius = Math.max(0, Math.min(seriesBox.width, seriesBox.height) / 2); let radius = initialRadius; setSeriesCircle(centerX, centerY, radius); const shake = async ({ hideWhenNecessary = false } = {}) => { const labelBoxes = []; for (const series of iterate(this.axes, polarSeries)) { const box = await series.computeLabelsBBox({ hideWhenNecessary }, seriesBox); if (box) { labelBoxes.push(box); } } if (labelBoxes.length === 0) { setSeriesCircle(centerX, centerY, initialRadius); return; } const labelBox = BBox.merge(labelBoxes); const refined = this.refineCircle(labelBox, radius, seriesBox); setSeriesCircle(refined.centerX, refined.centerY, refined.radius); radius = refined.radius; }; await shake(); await shake(); await shake(); await shake({ hideWhenNecessary: true }); await shake({ hideWhenNecessary: true }); for (const series of iterate(this.axes, polarSeries)) { await series.computeLabelsBBox({ hideWhenNecessary: true }, seriesBox); } return { radius, centerX, centerY }; } refineCircle(labelsBox, radius, seriesBox) { const minCircleRatio = 0.5; const circleLeft = -radius; const circleTop = -radius; const circleRight = radius; const circleBottom = radius; let padLeft = Math.max(0, circleLeft - labelsBox.x); let padTop = Math.max(0, circleTop - labelsBox.y); let padRight = Math.max(0, labelsBox.x + labelsBox.width - circleRight); let padBottom = Math.max(0, labelsBox.y + labelsBox.height - circleBottom); padLeft = padRight = Math.max(padLeft, padRight); padTop = padBottom = Math.max(padTop, padBottom); const availCircleWidth = seriesBox.width - padLeft - padRight; const availCircleHeight = seriesBox.height - padTop - padBottom; let newRadius = Math.min(availCircleWidth, availCircleHeight) / 2; const minHorizontalRadius = minCircleRatio * seriesBox.width / 2; const minVerticalRadius = minCircleRatio * seriesBox.height / 2; const minRadius = Math.min(minHorizontalRadius, minVerticalRadius); if (newRadius < minRadius) { newRadius = minRadius; const horizontalPadding = padLeft + padRight; const verticalPadding = padTop + padBottom; if (2 * newRadius + verticalPadding > seriesBox.height) { const padHeight = seriesBox.height - 2 * newRadius; if (Math.min(padTop, padBottom) * 2 > padHeight) { padTop = padHeight / 2; padBottom = padHeight / 2; } else if (padTop > padBottom) { padTop = padHeight - padBottom; } else { padBottom = padHeight - padTop; } } if (2 * newRadius + horizontalPadding > seriesBox.width) { const padWidth = seriesBox.width - 2 * newRadius; if (Math.min(padLeft, padRight) * 2 > padWidth) { padLeft = padWidth / 2; padRight = padWidth / 2; } else if (padLeft > padRight) { padLeft = padWidth - padRight; } else { padRight = padWidth - padLeft; } } } const newWidth = padLeft + 2 * newRadius + padRight; const newHeight = padTop + 2 * newRadius + padBottom; return { centerX: (seriesBox.width - newWidth) / 2 + padLeft + newRadius, centerY: (seriesBox.height - newHeight) / 2 + padTop + newRadius, radius: newRadius }; } }; PolarChart.className = "PolarChart"; PolarChart.type = "polar"; function isPolarSeries(series) { return series instanceof PolarSeries; } // packages/ag-charts-community/src/chart/polarChartModule.ts var PolarChartModule = { type: "chart", name: "polar", version: VERSION, options: polarChartOptionsDefs, create(options, resources) { return new PolarChart(options, resources); }, validate(options, optionsDefs2, path) { const additionalErrors = []; const baseType = options?.series?.[0]?.type; if (baseType === "pie" || baseType === "donut") { if (options?.axes) { additionalErrors.push(new UnknownError([], options.axes, path, "axes")); options = without(options, ["axes"]); } } const result = validate(options, optionsDefs2, path); result.invalid.push(...additionalErrors); return result; } }; // packages/ag-charts-community/src/scene/sectorBox.ts var SectorBox = class _SectorBox { constructor(startAngle, endAngle, innerRadius, outerRadius) { this.startAngle = startAngle; this.endAngle = endAngle; this.innerRadius = innerRadius; this.outerRadius = outerRadius; } clone() { const { startAngle, endAngle, innerRadius, outerRadius } = this; return new _SectorBox(startAngle, endAngle, innerRadius, outerRadius); } equals(other) { return this.startAngle === other.startAngle && this.endAngle === other.endAngle && this.innerRadius === other.innerRadius && this.outerRadius === other.outerRadius; } [interpolate](other, d) { return new _SectorBox( this.startAngle * (1 - d) + other.startAngle * d, this.endAngle * (1 - d) + other.endAngle * d, this.innerRadius * (1 - d) + other.innerRadius * d, this.outerRadius * (1 - d) + other.outerRadius * d ); } }; // packages/ag-charts-community/src/scene/util/sector.ts function sectorBox({ startAngle, endAngle, innerRadius, outerRadius }) { let x0 = Infinity; let y0 = Infinity; let x1 = -Infinity; let y1 = -Infinity; const addPoint = (x, y) => { x0 = Math.min(x, x0); y0 = Math.min(y, y0); x1 = Math.max(x, x1); y1 = Math.max(y, y1); }; addPoint(innerRadius * Math.cos(startAngle), innerRadius * Math.sin(startAngle)); addPoint(innerRadius * Math.cos(endAngle), innerRadius * Math.sin(endAngle)); addPoint(outerRadius * Math.cos(startAngle), outerRadius * Math.sin(startAngle)); addPoint(outerRadius * Math.cos(endAngle), outerRadius * Math.sin(endAngle)); if (isBetweenAngles(0, startAngle, endAngle)) { addPoint(outerRadius, 0); } if (isBetweenAngles(Math.PI * 0.5, startAngle, endAngle)) { addPoint(0, outerRadius); } if (isBetweenAngles(Math.PI, startAngle, endAngle)) { addPoint(-outerRadius, 0); } if (isBetweenAngles(Math.PI * 1.5, startAngle, endAngle)) { addPoint(0, -outerRadius); } return new BBox(x0, y0, x1 - x0, y1 - y0); } function isPointInSector(x, y, sector) { const radius = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); const { innerRadius, outerRadius } = sector; if (sector.startAngle === sector.endAngle || radius < Math.min(innerRadius, outerRadius) || radius > Math.max(innerRadius, outerRadius)) { return false; } const startAngle = normalizeAngle180(sector.startAngle); const endAngle = normalizeAngle180(sector.endAngle); const angle2 = Math.atan2(y, x); return startAngle < endAngle ? angle2 <= endAngle && angle2 >= startAngle : angle2 <= endAngle && angle2 >= -Math.PI || angle2 >= startAngle && angle2 <= Math.PI; } function arcIntersections(cx, cy, r, startAngle, endAngle, counterClockwise, x1, y1, x2, y2) { if (Number.isNaN(cx) || Number.isNaN(cy)) { return 0; } if (counterClockwise) { [endAngle, startAngle] = [startAngle, endAngle]; } const k = (y2 - y1) / (x2 - x1); const y0 = y1 - k * x1; const a = Math.pow(k, 2) + 1; const b = 2 * (k * (y0 - cy) - cx); const c = Math.pow(cx, 2) + Math.pow(y0 - cy, 2) - Math.pow(r, 2); const d = Math.pow(b, 2) - 4 * a * c; if (d < 0) { return 0; } const i1x = (-b + Math.sqrt(d)) / 2 / a; const i2x = (-b - Math.sqrt(d)) / 2 / a; let intersections = 0; for (const x of [i1x, i2x]) { const isXInsideLine = x >= Math.min(x1, x2) && x <= Math.max(x1, x2); if (!isXInsideLine) { continue; } const y = k * x + y0; const adjacent = x - cx; const opposite = y - cy; const angle2 = Math.atan2(opposite, adjacent); if (isBetweenAngles(angle2, startAngle, endAngle)) { intersections++; } } return intersections; } function lineCollidesSector(line, sector) { const { startAngle, endAngle, innerRadius, outerRadius } = sector; const outerStart = { x: outerRadius * Math.cos(startAngle), y: outerRadius * Math.sin(startAngle) }; const outerEnd = { x: outerRadius * Math.cos(endAngle), y: outerRadius * Math.sin(endAngle) }; const innerStart = innerRadius === 0 ? { x: 0, y: 0 } : { x: innerRadius * Math.cos(startAngle), y: innerRadius * Math.sin(startAngle) }; const innerEnd = innerRadius === 0 ? { x: 0, y: 0 } : { x: innerRadius * Math.cos(endAngle), y: innerRadius * Math.sin(endAngle) }; return segmentIntersection( line.start.x, line.start.y, line.end.x, line.end.y, outerStart.x, outerStart.y, innerStart.x, innerStart.y ) || segmentIntersection( line.start.x, line.start.y, line.end.x, line.end.y, outerEnd.x, outerEnd.y, innerEnd.x, innerEnd.y ) || arcIntersections( 0, 0, outerRadius, startAngle, endAngle, true, line.start.x, line.start.y, line.end.x, line.end.y ); } function boxCollidesSector(box, sector) { const topLeft = { x: box.x, y: box.y }; const topRight = { x: box.x + box.width, y: box.y }; const bottomLeft = { x: box.x, y: box.y + box.height }; const bottomRight = { x: box.x + box.width, y: box.y + box.height }; return lineCollidesSector({ start: topLeft, end: topRight }, sector) || lineCollidesSector({ start: bottomLeft, end: bottomRight }, sector); } function radiiScalingFactor(r, sweep, a, b) { if (a === 0 && b === 0) return 0; const fs1 = Math.asin(Math.abs(1 * a) / (r + 1 * a)) + Math.asin(Math.abs(1 * b) / (r + 1 * b)) - sweep; if (fs1 < 0) return 1; let start2 = 0; let end3 = 1; for (let i = 0; i < 8; i += 1) { const s = (start2 + end3) / 2; const fs = Math.asin(Math.abs(s * a) / (r + s * a)) + Math.asin(Math.abs(s * b) / (r + s * b)) - sweep; if (fs < 0) { start2 = s; } else { end3 = s; } } return start2; } var delta2 = 1e-6; function clockwiseAngle(angle2, relativeToStartAngle) { if (angleBetween(angle2, relativeToStartAngle) < delta2) { return relativeToStartAngle; } else { return normalizeAngle360(angle2 - relativeToStartAngle) + relativeToStartAngle; } } function clockwiseAngles(startAngle, endAngle, relativeToStartAngle = 0) { const fullPie = Math.abs(endAngle - startAngle) >= 2 * Math.PI; const sweepAngle = fullPie ? 2 * Math.PI : normalizeAngle360(endAngle - startAngle); startAngle = clockwiseAngle(startAngle, relativeToStartAngle); endAngle = startAngle + sweepAngle; return { startAngle, endAngle }; } function arcRadialLineIntersectionAngle(cx, cy, r, startAngle, endAngle, clipAngle) { const sinA = Math.sin(clipAngle); const cosA = Math.cos(clipAngle); const c = cx ** 2 + cy ** 2 - r ** 2; let p0x; let p0y; let p1x; let p1y; if (cosA > 0.5) { const tanA = sinA / cosA; const a = 1 + tanA ** 2; const b = -2 * (cx + cy * tanA); const d = b ** 2 - 4 * a * c; if (d < 0) return; const x0 = (-b + Math.sqrt(d)) / (2 * a); const x1 = (-b - Math.sqrt(d)) / (2 * a); p0x = x0; p0y = x0 * tanA; p1x = x1; p1y = x1 * tanA; } else { const cotA = cosA / sinA; const a = 1 + cotA ** 2; const b = -2 * (cy + cx * cotA); const d = b ** 2 - 4 * a * c; if (d < 0) return; const y0 = (-b + Math.sqrt(d)) / (2 * a); const y1 = (-b - Math.sqrt(d)) / (2 * a); p0x = y0 * cotA; p0y = y0; p1x = y1 * cotA; p1y = y1; } const normalisedX = cosA; const normalisedY = sinA; const p0DotNormalized = p0x * normalisedX + p0y * normalisedY; const p1DotNormalized = p1x * normalisedX + p1y * normalisedY; const a0 = p0DotNormalized > 0 ? clockwiseAngle(Math.atan2(p0y - cy, p0x - cx), startAngle) : Number.NaN; const a1 = p1DotNormalized > 0 ? clockwiseAngle(Math.atan2(p1y - cy, p1x - cx), startAngle) : Number.NaN; if (a0 >= startAngle && a0 <= endAngle) { return a0; } else if (a1 >= startAngle && a1 <= endAngle) { return a1; } } function arcCircleIntersectionAngle(cx, cy, r, startAngle, endAngle, circleR) { const d = Math.hypot(cx, cy); const d1 = (d ** 2 - r ** 2 + circleR ** 2) / (2 * d); const d2 = d - d1; const theta = Math.atan2(cy, cx); const deltaTheta = Math.acos(-d2 / r); const a0 = clockwiseAngle(theta + deltaTheta, startAngle); const a1 = clockwiseAngle(theta - deltaTheta, startAngle); if (a0 >= startAngle && a0 <= endAngle) { return a0; } else if (a1 >= startAngle && a1 <= endAngle) { return a1; } } // packages/ag-charts-community/src/scene/shape/sector.ts var Arc = class { constructor(cx, cy, r, a0, a1) { this.cx = cx; this.cy = cy; this.r = r; this.a0 = a0; this.a1 = a1; if (this.a0 >= this.a1) { this.a0 = Number.NaN; this.a1 = Number.NaN; } } isValid() { return Number.isFinite(this.a0) && Number.isFinite(this.a1); } pointAt(a) { return { x: this.cx + this.r * Math.cos(a), y: this.cy + this.r * Math.sin(a) }; } clipStart(a) { if (a == null || !this.isValid() || a < this.a0) return; this.a0 = a; if (Number.isNaN(a) || this.a0 >= this.a1) { this.a0 = Number.NaN; this.a1 = Number.NaN; } } clipEnd(a) { if (a == null || !this.isValid() || a > this.a1) return; this.a1 = a; if (Number.isNaN(a) || this.a0 >= this.a1) { this.a0 = Number.NaN; this.a1 = Number.NaN; } } }; var Sector = class extends Path { constructor() { super(...arguments); this.centerX = 0; this.centerY = 0; this.innerRadius = 10; this.outerRadius = 20; this.startAngle = 0; this.endAngle = Math.PI * 2; this.clipSector = void 0; this.concentricEdgeInset = 0; this.radialEdgeInset = 0; this.startOuterCornerRadius = 0; this.endOuterCornerRadius = 0; this.startInnerCornerRadius = 0; this.endInnerCornerRadius = 0; } set inset(value) { this.concentricEdgeInset = value; this.radialEdgeInset = value; } set cornerRadius(value) { this.startOuterCornerRadius = value; this.endOuterCornerRadius = value; this.startInnerCornerRadius = value; this.endInnerCornerRadius = value; } computeBBox() { return sectorBox(this).translate(this.centerX, this.centerY); } normalizedRadii() { const { concentricEdgeInset } = this; let { innerRadius, outerRadius } = this; innerRadius = innerRadius > 0 ? innerRadius + concentricEdgeInset : 0; outerRadius = Math.max(outerRadius - concentricEdgeInset, 0); return { innerRadius, outerRadius }; } normalizedClipSector() { const { clipSector } = this; if (clipSector == null) return; const { startAngle, endAngle } = clockwiseAngles(this.startAngle, this.endAngle); const { innerRadius, outerRadius } = this.normalizedRadii(); const clipAngles = clockwiseAngles(clipSector.startAngle, clipSector.endAngle, startAngle); return new SectorBox( Math.max(startAngle, clipAngles.startAngle), Math.min(endAngle, clipAngles.endAngle), Math.max(innerRadius, clipSector.innerRadius), Math.min(outerRadius, clipSector.outerRadius) ); } getAngleOffset(radius) { return radius > 0 ? this.radialEdgeInset / radius : 0; } arc(r, angleSweep, a0, a1, outerArc, innerArc, start2, inner) { if (r <= 0) return; const { startAngle, endAngle } = clockwiseAngles(this.startAngle, this.endAngle); const { innerRadius, outerRadius } = this.normalizedRadii(); const clipSector = this.normalizedClipSector(); if (inner && innerRadius <= 0) return; const angleOffset = inner ? this.getAngleOffset(innerRadius + r) : this.getAngleOffset(outerRadius - r); const angle2 = start2 ? startAngle + angleOffset + angleSweep : endAngle - angleOffset - angleSweep; const radius = inner ? innerRadius + r : outerRadius - r; const cx = radius * Math.cos(angle2); const cy = radius * Math.sin(angle2); if (clipSector != null) { const delta3 = 1e-6; if (!start2 && !(angle2 >= startAngle - delta3 && angle2 <= clipSector.endAngle - delta3)) return; if (start2 && !(angle2 >= clipSector.startAngle + delta3 && angle2 <= endAngle - delta3)) return; if (inner && radius < clipSector.innerRadius - delta3) return; if (!inner && radius > clipSector.outerRadius + delta3) return; } const arc = new Arc(cx, cy, r, a0, a1); if (clipSector != null) { if (inner) { arc.clipStart(arcRadialLineIntersectionAngle(cx, cy, r, a0, a1, clipSector.endAngle)); arc.clipEnd(arcRadialLineIntersectionAngle(cx, cy, r, a0, a1, clipSector.startAngle)); } else { arc.clipStart(arcRadialLineIntersectionAngle(cx, cy, r, a0, a1, clipSector.startAngle)); arc.clipEnd(arcRadialLineIntersectionAngle(cx, cy, r, a0, a1, clipSector.endAngle)); } let circleClipStart; let circleClipEnd; if (start2) { circleClipStart = arcCircleIntersectionAngle(cx, cy, r, a0, a1, clipSector.innerRadius); circleClipEnd = arcCircleIntersectionAngle(cx, cy, r, a0, a1, clipSector.outerRadius); } else { circleClipStart = arcCircleIntersectionAngle(cx, cy, r, a0, a1, clipSector.outerRadius); circleClipEnd = arcCircleIntersectionAngle(cx, cy, r, a0, a1, clipSector.innerRadius); } arc.clipStart(circleClipStart); arc.clipEnd(circleClipEnd); if (circleClipStart != null) { const { x: x2, y: y2 } = arc.pointAt(circleClipStart); const theta2 = clockwiseAngle(Math.atan2(y2, x2), startAngle); if (start2) { innerArc?.clipStart(theta2); } else { outerArc.clipEnd(theta2); } } if (circleClipEnd != null) { const { x: x2, y: y2 } = arc.pointAt(circleClipEnd); const theta2 = clockwiseAngle(Math.atan2(y2, x2), startAngle); if (start2) { outerArc.clipStart(theta2); } else { innerArc?.clipEnd(theta2); } } } if (clipSector != null) { const { x: x2, y: y2 } = arc.pointAt((arc.a0 + arc.a1) / 2); if (!isPointInSector(x2, y2, clipSector)) return; } const { x, y } = arc.pointAt(start2 === inner ? arc.a0 : arc.a1); const theta = clockwiseAngle(Math.atan2(y, x), startAngle); const radialArc = inner ? innerArc : outerArc; if (start2) { radialArc?.clipStart(theta); } else { radialArc?.clipEnd(theta); } return arc; } updatePath() { const delta3 = 1e-6; const { path, centerX, centerY, concentricEdgeInset, radialEdgeInset } = this; let { startOuterCornerRadius, endOuterCornerRadius, startInnerCornerRadius, endInnerCornerRadius } = this; const { startAngle, endAngle } = clockwiseAngles(this.startAngle, this.endAngle); const { innerRadius, outerRadius } = this.normalizedRadii(); const clipSector = this.normalizedClipSector(); const sweepAngle = endAngle - startAngle; const fullPie = sweepAngle >= 2 * Math.PI - delta3; path.clear(); const innerAngleOffset = this.getAngleOffset(innerRadius); const adjustedSweep = sweepAngle - 2 * innerAngleOffset; const radialLength = outerRadius - innerRadius; const innerCornerDistance = innerRadius > 0 && adjustedSweep > 0 ? 2 * innerRadius * Math.sin(adjustedSweep / 2) : 0; const outerCornerDistance = outerRadius > 0 && adjustedSweep > 0 ? 2 * outerRadius * Math.sin(adjustedSweep / 2) : 0; startOuterCornerRadius = Math.floor( Math.max(0, Math.min(startOuterCornerRadius, outerCornerDistance / 2, radialLength / 2)) ); endOuterCornerRadius = Math.floor( Math.max(0, Math.min(endOuterCornerRadius, outerCornerDistance / 2, radialLength / 2)) ); startInnerCornerRadius = Math.floor( Math.max(0, Math.min(startInnerCornerRadius, innerCornerDistance / 2, radialLength / 2)) ); endInnerCornerRadius = Math.floor( Math.max(0, Math.min(endInnerCornerRadius, innerCornerDistance / 2, radialLength / 2)) ); const isInvalid = innerRadius === 0 && outerRadius === 0 || innerRadius > outerRadius || innerCornerDistance < 0 || outerCornerDistance <= 0; if (isInvalid) { return; } else if ((clipSector?.startAngle ?? startAngle) === (clipSector?.endAngle ?? endAngle)) { return; } else if (fullPie && this.clipSector == null && startOuterCornerRadius === 0 && endOuterCornerRadius === 0 && startInnerCornerRadius === 0 && endInnerCornerRadius === 0) { path.moveTo(centerX + outerRadius * Math.cos(startAngle), centerY + outerRadius * Math.sin(startAngle)); path.arc(centerX, centerY, outerRadius, startAngle, endAngle); if (innerRadius > concentricEdgeInset) { path.moveTo(centerX + innerRadius * Math.cos(endAngle), centerY + innerRadius * Math.sin(endAngle)); path.arc(centerX, centerY, innerRadius, endAngle, startAngle, true); } path.closePath(); return; } else if (this.clipSector == null && Math.abs(innerRadius - outerRadius) < 1e-6) { path.arc(centerX, centerY, outerRadius, startAngle, endAngle, false); path.arc(centerX, centerY, outerRadius, endAngle, startAngle, true); path.closePath(); return; } const outerAngleOffset = this.getAngleOffset(outerRadius); const outerAngleExceeded = sweepAngle < 2 * outerAngleOffset; if (outerAngleExceeded) return; const hasInnerSweep = (clipSector?.innerRadius ?? innerRadius) > concentricEdgeInset; const innerAngleExceeded = innerRadius < concentricEdgeInset || sweepAngle < 2 * innerAngleOffset; const maxRadialLength = Math.max( startOuterCornerRadius, startInnerCornerRadius, endOuterCornerRadius, endInnerCornerRadius ); const initialScalingFactor = maxRadialLength > 0 ? Math.min(radialLength / maxRadialLength, 1) : 1; startOuterCornerRadius *= initialScalingFactor; endOuterCornerRadius *= initialScalingFactor; startInnerCornerRadius *= initialScalingFactor; endInnerCornerRadius *= initialScalingFactor; const outerScalingFactor = radiiScalingFactor( outerRadius, sweepAngle - 2 * outerAngleOffset, -startOuterCornerRadius, -endOuterCornerRadius ); startOuterCornerRadius *= outerScalingFactor; endOuterCornerRadius *= outerScalingFactor; if (!innerAngleExceeded && hasInnerSweep) { const innerScalingFactor = radiiScalingFactor( innerRadius, sweepAngle - 2 * innerAngleOffset, startInnerCornerRadius, endInnerCornerRadius ); startInnerCornerRadius *= innerScalingFactor; endInnerCornerRadius *= innerScalingFactor; } else { startInnerCornerRadius = 0; endInnerCornerRadius = 0; } const maxCombinedRadialLength = Math.max( startOuterCornerRadius + startInnerCornerRadius, endOuterCornerRadius + endInnerCornerRadius ); const edgesScalingFactor = maxCombinedRadialLength > 0 ? Math.min(radialLength / maxCombinedRadialLength, 1) : 1; startOuterCornerRadius *= edgesScalingFactor; endOuterCornerRadius *= edgesScalingFactor; startInnerCornerRadius *= edgesScalingFactor; endInnerCornerRadius *= edgesScalingFactor; let startOuterCornerRadiusAngleSweep = 0; let endOuterCornerRadiusAngleSweep = 0; const startOuterCornerRadiusSweep = startOuterCornerRadius / (outerRadius - startOuterCornerRadius); const endOuterCornerRadiusSweep = endOuterCornerRadius / (outerRadius - endOuterCornerRadius); if (startOuterCornerRadiusSweep >= 0 && startOuterCornerRadiusSweep < 1 - delta3) { startOuterCornerRadiusAngleSweep = Math.asin(startOuterCornerRadiusSweep); } else { startOuterCornerRadiusAngleSweep = sweepAngle / 2; const maxStartOuterCornerRadius = outerRadius / (1 / Math.sin(startOuterCornerRadiusAngleSweep) + 1); startOuterCornerRadius = Math.min(maxStartOuterCornerRadius, startOuterCornerRadius); } if (endOuterCornerRadiusSweep >= 0 && endOuterCornerRadiusSweep < 1 - delta3) { endOuterCornerRadiusAngleSweep = Math.asin(endOuterCornerRadiusSweep); } else { endOuterCornerRadiusAngleSweep = sweepAngle / 2; const maxEndOuterCornerRadius = outerRadius / (1 / Math.sin(endOuterCornerRadiusAngleSweep) + 1); endOuterCornerRadius = Math.min(maxEndOuterCornerRadius, endOuterCornerRadius); } const startInnerCornerRadiusAngleSweep = Math.asin( startInnerCornerRadius / (innerRadius + startInnerCornerRadius) ); const endInnerCornerRadiusAngleSweep = Math.asin(endInnerCornerRadius / (innerRadius + endInnerCornerRadius)); const outerArcRadius = clipSector?.outerRadius ?? outerRadius; const outerArcRadiusOffset = this.getAngleOffset(outerArcRadius); const outerArc = new Arc( 0, 0, outerArcRadius, startAngle + outerArcRadiusOffset, endAngle - outerArcRadiusOffset ); const innerArcRadius = clipSector?.innerRadius ?? innerRadius; const innerArcRadiusOffset = this.getAngleOffset(innerArcRadius); const innerArc = hasInnerSweep ? new Arc(0, 0, innerArcRadius, startAngle + innerArcRadiusOffset, endAngle - innerArcRadiusOffset) : void 0; if (clipSector != null) { outerArc.clipStart(clipSector.startAngle); outerArc.clipEnd(clipSector.endAngle); innerArc?.clipStart(clipSector.startAngle); innerArc?.clipEnd(clipSector.endAngle); } const startOuterArc = this.arc( startOuterCornerRadius, startOuterCornerRadiusAngleSweep, startAngle - Math.PI * 0.5, startAngle + startOuterCornerRadiusAngleSweep, outerArc, innerArc, true, false ); const endOuterArc = this.arc( endOuterCornerRadius, endOuterCornerRadiusAngleSweep, endAngle - endOuterCornerRadiusAngleSweep, endAngle + Math.PI * 0.5, outerArc, innerArc, false, false ); const endInnerArc = this.arc( endInnerCornerRadius, endInnerCornerRadiusAngleSweep, endAngle + Math.PI * 0.5, endAngle + Math.PI - endInnerCornerRadiusAngleSweep, outerArc, innerArc, false, true ); const startInnerArc = this.arc( startInnerCornerRadius, startInnerCornerRadiusAngleSweep, startAngle + Math.PI + startInnerCornerRadiusAngleSweep, startAngle + Math.PI * 1.5, outerArc, innerArc, true, true ); if (innerAngleExceeded && hasInnerSweep) { } else if (innerAngleExceeded) { const x = sweepAngle < Math.PI * 0.5 ? radialEdgeInset * (1 + Math.cos(sweepAngle)) / Math.sin(sweepAngle) : Number.NaN; let r; if (x > 0 && x < outerRadius) { r = Math.max(Math.hypot(radialEdgeInset, x), innerRadius); } else { r = radialEdgeInset; } r = Math.max(r, innerRadius); const midAngle = startAngle + sweepAngle * 0.5; path.moveTo(centerX + r * Math.cos(midAngle), centerY + r * Math.sin(midAngle)); } else if (startInnerArc?.isValid() === true || innerArc?.isValid() === true) { } else { const midAngle = startAngle + sweepAngle / 2; const cx = innerRadius * Math.cos(midAngle); const cy = innerRadius * Math.sin(midAngle); path.moveTo(centerX + cx, centerY + cy); } if (startOuterArc?.isValid() === true) { const { cx, cy, r, a0, a1 } = startOuterArc; path.arc(centerX + cx, centerY + cy, r, a0, a1); } if (outerArc.isValid()) { const { r, a0, a1 } = outerArc; path.arc(centerX, centerY, r, a0, a1); } if (endOuterArc?.isValid() === true) { const { cx, cy, r, a0, a1 } = endOuterArc; path.arc(centerX + cx, centerY + cy, r, a0, a1); } if (!innerAngleExceeded) { if (endInnerArc?.isValid() === true) { const { cx, cy, r, a0, a1 } = endInnerArc; path.arc(centerX + cx, centerY + cy, r, a0, a1); } if (innerArc?.isValid() === true) { const { r, a0, a1 } = innerArc; path.arc(centerX, centerY, r, a1, a0, true); } if (startInnerArc?.isValid() === true) { const { cx, cy, r, a0, a1 } = startInnerArc; path.arc(centerX + cx, centerY + cy, r, a0, a1); } } path.closePath(); } isPointInPath(x, y) { const { startAngle, endAngle, innerRadius, outerRadius } = this.clipSector ?? this; return isPointInSector(x - this.centerX, y - this.centerY, { startAngle, endAngle, innerRadius: Math.min(innerRadius, outerRadius), outerRadius: Math.max(innerRadius, outerRadius) }); } }; Sector.className = "Sector"; __decorateClass([ SceneChangeDetection() ], Sector.prototype, "centerX", 2); __decorateClass([ SceneChangeDetection() ], Sector.prototype, "centerY", 2); __decorateClass([ SceneChangeDetection() ], Sector.prototype, "innerRadius", 2); __decorateClass([ SceneChangeDetection() ], Sector.prototype, "outerRadius", 2); __decorateClass([ SceneChangeDetection() ], Sector.prototype, "startAngle", 2); __decorateClass([ SceneChangeDetection() ], Sector.prototype, "endAngle", 2); __decorateClass([ SceneObjectChangeDetection({ equals: (lhs, rhs) => lhs.equals(rhs) }) ], Sector.prototype, "clipSector", 2); __decorateClass([ SceneChangeDetection() ], Sector.prototype, "concentricEdgeInset", 2); __decorateClass([ SceneChangeDetection() ], Sector.prototype, "radialEdgeInset", 2); __decorateClass([ SceneChangeDetection() ], Sector.prototype, "startOuterCornerRadius", 2); __decorateClass([ SceneChangeDetection() ], Sector.prototype, "endOuterCornerRadius", 2); __decorateClass([ SceneChangeDetection() ], Sector.prototype, "startInnerCornerRadius", 2); __decorateClass([ SceneChangeDetection() ], Sector.prototype, "endInnerCornerRadius", 2); // packages/ag-charts-community/src/chart/themes/defaultColors.ts var DEFAULT_FILLS = { BLUE: "#5090dc", ORANGE: "#ffa03a", GREEN: "#459d55", CYAN: "#34bfe1", YELLOW: "#e1cc00", VIOLET: "#9669cb", GRAY: "#b5b5b5", MAGENTA: "#bd5aa7", BROWN: "#8a6224", RED: "#ef5452" }; var DEFAULT_STROKES = { BLUE: "#2b5c95", ORANGE: "#cc6f10", GREEN: "#1e652e", CYAN: "#18859e", YELLOW: "#a69400", VIOLET: "#603c88", GRAY: "#575757", MAGENTA: "#7d2f6d", BROWN: "#4f3508", RED: "#a82529" }; // packages/ag-charts-community/src/chart/series/polar/donutSeriesProperties.ts var DonutTitle = class extends Caption { constructor() { super(...arguments); this.showInLegend = false; } }; __decorateClass([ addFakeTransformToInstanceProperty ], DonutTitle.prototype, "showInLegend", 2); var DonutInnerLabel = class extends Label { constructor() { super(...arguments); this.spacing = 2; } set(properties, _reset) { return super.set(properties); } }; __decorateClass([ addFakeTransformToInstanceProperty ], DonutInnerLabel.prototype, "text", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutInnerLabel.prototype, "spacing", 2); var DonutInnerCircle = class extends BaseProperties { constructor() { super(...arguments); this.fill = "transparent"; this.fillOpacity = 1; } }; __decorateClass([ addFakeTransformToInstanceProperty ], DonutInnerCircle.prototype, "fill", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutInnerCircle.prototype, "fillOpacity", 2); var DonutSeriesCalloutLabel = class extends Label { constructor() { super(...arguments); this.offset = 3; this.minAngle = 0; this.minSpacing = 4; this.maxCollisionOffset = 50; this.avoidCollisions = true; } }; __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesCalloutLabel.prototype, "offset", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesCalloutLabel.prototype, "minAngle", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesCalloutLabel.prototype, "minSpacing", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesCalloutLabel.prototype, "maxCollisionOffset", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesCalloutLabel.prototype, "avoidCollisions", 2); var DonutSeriesSectorLabel = class extends Label { constructor() { super(...arguments); this.positionOffset = 0; this.positionRatio = 0.5; } }; __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesSectorLabel.prototype, "positionOffset", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesSectorLabel.prototype, "positionRatio", 2); var DonutSeriesCalloutLine = class extends BaseProperties { constructor() { super(...arguments); this.length = 10; this.strokeWidth = 1; } }; __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesCalloutLine.prototype, "colors", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesCalloutLine.prototype, "length", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesCalloutLine.prototype, "strokeWidth", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesCalloutLine.prototype, "itemStyler", 2); var DonutSeriesProperties = class extends SeriesProperties { constructor() { super(...arguments); this.defaultColorRange = []; this.defaultPatternFills = []; this.fills = Object.values(DEFAULT_FILLS); this.strokes = Object.values(DEFAULT_STROKES); this.fillOpacity = 1; this.strokeOpacity = 1; this.lineDash = [0]; this.lineDashOffset = 0; this.cornerRadius = 0; this.rotation = 0; this.outerRadiusOffset = 0; this.outerRadiusRatio = 1; this.strokeWidth = 1; this.sectorSpacing = 0; this.hideZeroValueSectorsInLegend = false; this.innerLabels = new PropertiesArray(DonutInnerLabel); this.title = new DonutTitle(); this.innerCircle = new DonutInnerCircle(); this.shadow = new DropShadow(); this.calloutLabel = new DonutSeriesCalloutLabel(); this.sectorLabel = new DonutSeriesSectorLabel(); this.calloutLine = new DonutSeriesCalloutLine(); this.tooltip = makeSeriesTooltip(); } }; __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "angleKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "angleName", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "angleFilterKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "radiusKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "radiusName", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "radiusMin", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "radiusMax", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "calloutLabelKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "calloutLabelName", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "sectorLabelKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "sectorLabelName", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "legendItemKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "defaultColorRange", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "defaultPatternFills", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "fills", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "strokes", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "fillOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "strokeOpacity", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "lineDash", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "lineDashOffset", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "cornerRadius", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "itemStyler", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "rotation", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "outerRadiusOffset", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "outerRadiusRatio", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "innerRadiusOffset", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "innerRadiusRatio", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "strokeWidth", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "sectorSpacing", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "hideZeroValueSectorsInLegend", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "innerLabels", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "title", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "innerCircle", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "shadow", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "calloutLabel", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "sectorLabel", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "calloutLine", 2); __decorateClass([ addFakeTransformToInstanceProperty ], DonutSeriesProperties.prototype, "tooltip", 2); // packages/ag-charts-community/src/chart/series/polar/pieUtil.ts function preparePieSeriesAnimationFunctions(initialLoad, rotationDegrees, scaleFn, oldScaleFn) { const scale2 = [scaleFn.convert(0), scaleFn.convert(1)]; const oldScale = [oldScaleFn.convert(0), oldScaleFn.convert(1)]; const rotation = Math.PI / -2 + toRadians(rotationDegrees); const phase = initialLoad ? "initial" : "update"; const scaleToNewRadius = ({ radius }) => { return { innerRadius: scale2[0], outerRadius: scale2[0] + (scale2[1] - scale2[0]) * radius }; }; const scaleToOldRadius = ({ radius }) => { return { innerRadius: oldScale[0], outerRadius: oldScale[0] + (oldScale[1] - oldScale[0]) * radius }; }; const fromFn = (sect, datum, status, { prevFromProps }) => { let { startAngle, endAngle, innerRadius, outerRadius } = sect; let { fill, stroke } = datum.sectorFormat; if (status === "updated" && sect.previousDatum == null) { status = "added"; } if (status === "unknown" || status === "added" && !prevFromProps) { startAngle = rotation; endAngle = rotation; innerRadius = datum.innerRadius; outerRadius = datum.outerRadius; } else if (status === "added" && prevFromProps) { startAngle = prevFromProps.endAngle ?? rotation; endAngle = prevFromProps.endAngle ?? rotation; innerRadius = prevFromProps.innerRadius ?? datum.innerRadius; outerRadius = prevFromProps.outerRadius ?? datum.outerRadius; } if (status === "added" && !initialLoad) { const radii = scaleToOldRadius(datum); innerRadius = radii.innerRadius; outerRadius = radii.outerRadius; } if (status === "updated") { fill = sect.fill ?? fill; stroke = (typeof sect.stroke === "string" ? sect.stroke : void 0) ?? stroke; } const animatableFill = typeof fill === "string" ? { fill } : {}; return { startAngle, endAngle, innerRadius, outerRadius, stroke, phase, ...animatableFill }; }; const toFn = (_sect, datum, status, { prevLive }) => { let { startAngle, endAngle, innerRadius, outerRadius } = datum; const { stroke, fill } = datum.sectorFormat; if (status === "removed" && prevLive) { startAngle = prevLive.datum?.endAngle; endAngle = prevLive.datum?.endAngle; } else if (status === "removed" && !prevLive) { startAngle = rotation; endAngle = rotation; } if (status === "removed") { const radii = scaleToNewRadius(datum); innerRadius = radii.innerRadius; outerRadius = radii.outerRadius; } const animatableFill = typeof fill === "string" ? { fill } : {}; return { startAngle, endAngle, outerRadius, innerRadius, stroke, ...animatableFill }; }; const innerCircleFromFn = (node, _) => { return { size: node.previousDatum?.radius ?? node.size ?? 0, phase }; }; const innerCircleToFn = (_, datum) => { return { size: datum.radius ?? 0 }; }; return { nodes: { toFn, fromFn }, innerCircle: { fromFn: innerCircleFromFn, toFn: innerCircleToFn } }; } function resetPieSelectionsFn(_node, datum) { return { startAngle: datum.startAngle, endAngle: datum.endAngle, innerRadius: datum.innerRadius, outerRadius: datum.outerRadius, stroke: datum.sectorFormat.stroke }; } function pickByMatchingAngle(series, point) { const dy = point.y - series.centerY; const dx = point.x - series.centerX; const angle2 = Math.atan2(dy, dx); const sectors = series.getItemNodes(); for (const sector of sectors) { if (sector.datum.missing === true) continue; if (isBetweenAngles(angle2, sector.startAngle, sector.endAngle)) { const radius = Math.hypot(dx, dy); let distance2 = 0; if (radius < sector.innerRadius) { distance2 = sector.innerRadius - radius; } else if (radius > sector.outerRadius) { distance2 = radius - sector.outerRadius; } return { datum: sector.datum, distance: distance2 }; } } return void 0; } // packages/ag-charts-community/src/chart/series/polar/donutSeries.ts var PieDonutSeriesNodeEvent = class extends SeriesNodeEvent { constructor(type, nativeEvent, datum, series) { super(type, nativeEvent, datum, series); this.angleKey = series.properties.angleKey; this.radiusKey = series.properties.radiusKey; this.calloutLabelKey = series.properties.calloutLabelKey; this.sectorLabelKey = series.properties.sectorLabelKey; } }; var DonutSeries = class extends PolarSeries { constructor(moduleCtx) { super({ moduleCtx, categoryKey: void 0, propertyKeys: { ...DEFAULT_POLAR_DIRECTION_KEYS, sectorLabel: ["sectorLabelKey"], calloutLabel: ["calloutLabelKey"] }, propertyNames: { ...DEFAULT_POLAR_DIRECTION_NAMES, sectorLabel: ["sectorLabelName"], calloutLabel: ["calloutLabelName"] }, pickModes: [1 /* NEAREST_NODE */, 0 /* EXACT_SHAPE_MATCH */], animationResetFns: { item: resetPieSelectionsFn, label: resetLabelFn } }); this.properties = new DonutSeriesProperties(); this.phantomNodeData = void 0; this.backgroundGroup = new TranslatableGroup({ name: `${this.id}-background`, zIndex: 0 /* BACKGROUND */ }); this.noVisibleData = false; this.previousRadiusScale = new LinearScale(); this.radiusScale = new LinearScale(); this.phantomGroup = this.contentGroup.appendChild(new Group({ name: "phantom", zIndex: -1 })); this.phantomSelection = Selection.select( this.phantomGroup, () => this.nodeFactory(), false ); this.phantomHighlightGroup = this.highlightGroup.appendChild(new Group({ name: "phantom", zIndex: -1 })); this.phantomHighlightSelection = Selection.select( this.phantomHighlightGroup, () => this.nodeFactory(), false ); this.calloutLabelGroup = this.contentGroup.appendChild(new Group({ name: "pieCalloutLabels" })); this.calloutLabelSelection = new Selection( this.calloutLabelGroup, Group ); // AG-6193 If the sum of all datums is 0, then we'll draw 1 or 2 rings to represent the empty series. this.zerosumRingsGroup = this.backgroundGroup.appendChild(new Group({ name: `${this.id}-zerosumRings` })); this.zerosumOuterRing = this.zerosumRingsGroup.appendChild(new Marker({ shape: "circle" })); this.zerosumInnerRing = this.zerosumRingsGroup.appendChild(new Marker({ shape: "circle" })); this.innerLabelsGroup = this.contentGroup.appendChild(new Group({ name: "innerLabels" })); this.innerCircleGroup = this.backgroundGroup.appendChild(new Group({ name: `${this.id}-innerCircle` })); this.innerLabelsSelection = Selection.select(this.innerLabelsGroup, Text); this.innerCircleSelection = Selection.select( this.innerCircleGroup, () => new Marker({ shape: "circle" }) ); this.surroundingRadius = void 0; this.NodeEvent = PieDonutSeriesNodeEvent; this.angleScale = new LinearScale(); this.angleScale.domain = [0, 1]; this.angleScale.range = [-Math.PI, Math.PI].map((angle2) => angle2 + Math.PI / 2); this.phantomGroup.opacity = 0.2; this.phantomHighlightGroup.opacity = 0.2; this.innerLabelsGroup.pointerEvents = 1 /* None */; } get calloutNodeData() { return this.phantomNodeData ?? this.nodeData; } attachSeries(seriesContentNode, seriesNode, annotationNode) { super.attachSeries(seriesContentNode, seriesNode, annotationNode); seriesContentNode?.appendChild(this.backgroundGroup); } detachSeries(seriesContentNode, seriesNode, annotationNode) { super.detachSeries(seriesContentNode, seriesNode, annotationNode); this.backgroundGroup.remove(); } setZIndex(zIndex) { super.setZIndex(zIndex); this.backgroundGroup.zIndex = [0 /* BACKGROUND */, zIndex]; } nodeFactory() { const sector = new Sector(); sector.miterLimit = 1e9; return sector; } getSeriesDomain(direction) { if (direction === "angle" /* Angle */) { return { domain: this.angleScale.domain }; } else { return { domain: this.radiusScale.domain }; } } async processData(dataController) { if (this.data == null) return; const { visible, id: seriesId, ctx: { legendManager } } = this; const { angleKey, angleFilterKey, radiusKey, calloutLabelKey, sectorLabelKey, legendItemKey } = this.properties; const processor = () => (value, index) => { if (visible && legendManager.getItemEnabled({ seriesId, itemId: index })) { return value; } return 0; }; const animationEnabled = !this.ctx.animationManager.isSkipped(); const allowNullKey = this.properties.allowNullKeys ?? false; const extraKeyProps = []; const extraProps = []; if (legendItemKey) { extraKeyProps.push(keyProperty(legendItemKey, "category", { id: `legendItemKey`, allowNullKey })); } else if (calloutLabelKey) { extraKeyProps.push(keyProperty(calloutLabelKey, "category", { id: `calloutLabelKey`, allowNullKey })); } else if (sectorLabelKey) { extraKeyProps.push(keyProperty(sectorLabelKey, "category", { id: `sectorLabelKey`, allowNullKey })); } const radiusScaleType = this.radiusScale.type; const angleScaleType = this.angleScale.type; if (radiusKey) { extraProps.push( rangedValueProperty(radiusKey, { id: "radiusValue", min: this.properties.radiusMin ?? 0, max: this.properties.radiusMax, missingValue: this.properties.radiusMax ?? 1, processor }), valueProperty(radiusKey, radiusScaleType, { id: `radiusRaw`, processor }), // Raw value pass-through. normalisePropertyTo("radiusValue", [0, 1], 1, this.properties.radiusMin ?? 0, this.properties.radiusMax) ); } if (calloutLabelKey) { extraProps.push(valueProperty(calloutLabelKey, "category", { id: `calloutLabelValue`, allowNullKey })); } if (sectorLabelKey) { extraProps.push(valueProperty(sectorLabelKey, "category", { id: `sectorLabelValue`, allowNullKey })); } if (legendItemKey) { extraProps.push(valueProperty(legendItemKey, "category", { id: `legendItemValue`, allowNullKey })); } if (angleFilterKey) { extraProps.push( accumulativeValueProperty(angleFilterKey, angleScaleType, { id: `angleFilterValue`, onlyPositive: true, invalidValue: 0, processor }), valueProperty(angleFilterKey, angleScaleType, { id: `angleFilterRaw` }), normalisePropertyTo("angleFilterValue", [0, 1], 0, 0) ); } if (animationEnabled && this.processedData?.reduced?.animationValidation?.uniqueKeys && extraKeyProps.length > 0) { extraProps.push(diff(this.id, this.processedData)); } extraProps.push(animationValidation()); await this.requestDataModel(dataController, this.data, { props: [ ...extraKeyProps, accumulativeValueProperty(angleKey, angleScaleType, { id: `angleValue`, onlyPositive: true, invalidValue: 0, processor }), valueProperty(angleKey, angleScaleType, { id: `angleRaw` }), // Raw value pass-through. normalisePropertyTo("angleValue", [0, 1], 0, 0), ...extraProps ] }); for (const valueDef of this.processedData?.defs?.values ?? []) { const { id, missing, property } = valueDef; const missCount = getMissCount(this, missing); if (id !== "angleRaw" && missCount > 0) { logger_exports.warnOnce( `no value was found for the key '${String(property)}' on ${missCount} data element${missCount > 1 ? "s" : ""}` ); } } this.animationState.transition("updateData"); } maybeRefreshNodeData() { if (!this.nodeDataRefresh) return; const { nodeData = [], phantomNodeData } = this.createNodeData() ?? {}; this.nodeData = nodeData; this.phantomNodeData = phantomNodeData; if (nodeData.length > 0) { debugMetrics_exports.record(`${this.type}:nodeData`, nodeData.length); } this.nodeDataRefresh = false; } getProcessedDataValues(dataModel, processedData) { const angleValues = dataModel.resolveColumnById(this, `angleValue`, processedData); const angleRawValues = dataModel.resolveColumnById(this, `angleRaw`, processedData); const angleFilterValues = this.properties.angleFilterKey == null ? void 0 : dataModel.resolveColumnById(this, `angleFilterValue`, processedData); const angleFilterRawValues = this.properties.angleFilterKey == null ? void 0 : dataModel.resolveColumnById(this, `angleFilterRaw`, processedData); const radiusValues = this.properties.radiusKey ? dataModel.resolveColumnById(this, `radiusValue`, processedData) : void 0; const radiusRawValues = this.properties.radiusKey ? dataModel.resolveColumnById(this, `radiusRaw`, processedData) : void 0; const calloutLabelValues = this.properties.calloutLabelKey ? dataModel.resolveColumnById(this, `calloutLabelValue`, processedData) : void 0; const sectorLabelValues = this.properties.sectorLabelKey ? dataModel.resolveColumnById(this, `sectorLabelValue`, processedData) : void 0; const legendItemValues = this.properties.legendItemKey ? dataModel.resolveColumnById(this, `legendItemValue`, processedData) : void 0; return { angleValues, angleRawValues, angleFilterValues, angleFilterRawValues, radiusValues, radiusRawValues, calloutLabelValues, sectorLabelValues, legendItemValues }; } createNodeData() { const { id: seriesId, processedData, dataModel, angleScale, ctx: { legendManager }, visible } = this; const { rotation, innerRadiusRatio } = this.properties; if (!dataModel || processedData?.type !== "ungrouped") return; const processedDataValues = this.getProcessedDataValues(dataModel, processedData); const { angleValues, angleRawValues, angleFilterValues, angleFilterRawValues, radiusValues, radiusRawValues, legendItemValues } = processedDataValues; const useFilterAngles = angleFilterRawValues?.some((filterRawValue, index) => { return filterRawValue > angleRawValues[index]; }) ?? false; let currentStart = 0; let sum = 0; const nodes = []; const phantomNodes = angleFilterRawValues == null ? void 0 : []; const rawData = processedData.dataSources.get(this.id)?.data ?? []; const invalidData = processedData.invalidData?.get(this.id); for (const [datumIndex, datum] of rawData.entries()) { if (invalidData?.[datumIndex] === true) continue; const currentValue = useFilterAngles ? angleFilterValues[datumIndex] : angleValues[datumIndex]; const crossFilterScale = angleFilterRawValues != null && !useFilterAngles ? Math.sqrt(angleFilterRawValues[datumIndex] / angleRawValues[datumIndex]) : 1; const startAngle = angleScale.convert(currentStart) + toRadians(rotation); currentStart = currentValue; sum += currentValue; const endAngle = angleScale.convert(currentStart) + toRadians(rotation); const span = Math.abs(endAngle - startAngle); const midAngle = startAngle + span / 2; const angleValue = angleRawValues[datumIndex]; const radiusRaw = radiusValues?.[datumIndex] ?? 1; const radius = radiusRaw * crossFilterScale; const radiusValue = radiusRawValues?.[datumIndex]; const legendItemValue = legendItemValues?.[datumIndex]; const nodeLabels = this.getLabels(datumIndex, datum, midAngle, span, processedDataValues); const sectorFormat = this.getItemStyle({ datum, datumIndex }, false); const node = { series: this, datum, datumIndex, angleValue, midAngle, midCos: Math.cos(midAngle), midSin: Math.sin(midAngle), startAngle, endAngle, radius, innerRadius: Math.max(this.radiusScale.convert(0), 0), outerRadius: Math.max(this.radiusScale.convert(radius), 0), sectorFormat, radiusValue, legendItemValue, enabled: visible && legendManager.getItemEnabled({ seriesId, itemId: datumIndex }), focusable: true, ...nodeLabels }; nodes.push(node); if (phantomNodes != null) { phantomNodes.push({ ...node, radius: 1, innerRadius: Math.max(this.radiusScale.convert(0), 0), outerRadius: Math.max(this.radiusScale.convert(1), 0), focusable: false }); } } this.zerosumOuterRing.visible = sum === 0; this.zerosumInnerRing.visible = sum === 0 && innerRadiusRatio != null && innerRadiusRatio !== 1 && innerRadiusRatio > 0; return { itemId: seriesId, nodeData: nodes, labelData: nodes, phantomNodeData: phantomNodes }; } getLabelContent(datumIndex, datum, values) { const { id: seriesId, ctx, properties } = this; const { formatManager } = ctx; const { calloutLabel, sectorLabel, calloutLabelKey, sectorLabelKey, legendItemKey } = properties; const allowNullKeys = properties.allowNullKeys ?? false; const calloutLabelValue = values.calloutLabelValues?.[datumIndex]; const sectorLabelValue = values.sectorLabelValues?.[datumIndex]; const legendItemValue = values.legendItemValues?.[datumIndex]; const labelFormatterParams = { datum, angleKey: this.properties.angleKey, angleName: this.properties.angleName, radiusKey: this.properties.radiusKey, radiusName: this.properties.radiusName, calloutLabelKey: this.properties.calloutLabelKey, calloutLabelName: this.properties.calloutLabelName, sectorLabelKey: this.properties.sectorLabelKey, sectorLabelName: this.properties.sectorLabelName, legendItemKey: this.properties.legendItemKey }; const result = { callout: void 0, sector: void 0, legendItem: void 0 }; if (calloutLabelKey) { result.callout = this.getLabelText( calloutLabelValue, datum, calloutLabelKey, "calloutLabel", [], calloutLabel, { ...labelFormatterParams, value: calloutLabelValue }, allowNullKeys ); } if (sectorLabelKey) { result.sector = this.getLabelText( sectorLabelValue, datum, sectorLabelKey, "sectorLabel", [], sectorLabel, { ...labelFormatterParams, value: sectorLabelValue }, allowNullKeys ); } if (legendItemKey != null && (legendItemValue != null || allowNullKeys)) { const legendItemDisplay = legendItemValue ?? ""; result.legendItem = formatManager.format(this.callWithContext.bind(this), { type: "category", value: allowNullKeys ? legendItemValue : legendItemDisplay, datum, seriesId, legendItemName: void 0, key: legendItemKey, source: "legend-label", property: "legendItem", domain: [], boundSeries: this.getFormatterContext("legendItem") }) ?? legendItemDisplay; } return result; } getLabels(datumIndex, datum, midAngle, span, values) { const { properties } = this; const { calloutLabel, sectorLabel, legendItemKey } = properties; const formats = this.getLabelContent(datumIndex, datum, values); const result = {}; if (calloutLabel.enabled && formats.callout && span >= toRadians(calloutLabel.minAngle)) { result.calloutLabel = { ...this.getTextAlignment(midAngle), text: formats.callout, hidden: false, collisionTextAlign: void 0, collisionOffsetY: 0, box: void 0 }; } if (sectorLabel.enabled && formats.sector) { result.sectorLabel = { text: formats.sector }; } if (legendItemKey && formats.legendItem) { result.legendItem = { key: legendItemKey, text: formats.legendItem }; } return result; } getTextAlignment(midAngle) { const quadrantTextOpts = [ { textAlign: "center", textBaseline: "bottom" }, { textAlign: "left", textBaseline: "middle" }, { textAlign: "center", textBaseline: "top" }, { textAlign: "right", textBaseline: "middle" } ]; const midAngle180 = normalizeAngle180(midAngle); const quadrantStart = -0.75 * Math.PI; const quadrantOffset = midAngle180 - quadrantStart; const quadrant = Math.floor(quadrantOffset / (Math.PI / 2)); const quadrantIndex = modulus(quadrant, quadrantTextOpts.length); return quadrantTextOpts[quadrantIndex]; } getFillParams(fill, innerRadius, outerRadius) { if (!isGradientFill(fill) || fill.bounds === "item") return; return { centerX: 0, centerY: 0, innerRadius, outerRadius }; } getItemStyle({ datum, datumIndex }, isHighlight, highlightState, legendItemValues) { const { fills, strokes, itemStyler } = this.properties; const defaultStroke = strokes[datumIndex]; const defaultFill = fills[datumIndex]; const { fill, fillOpacity, stroke, strokeWidth, strokeOpacity, lineDash, lineDashOffset, cornerRadius, opacity } = mergeDefaults( this.getHighlightStyle(isHighlight, datumIndex, highlightState, legendItemValues), { fill: defaultFill, stroke: defaultStroke }, this.properties ); let overrides; if (itemStyler) { overrides = this.cachedDatumCallback( this.getDatumId(datumIndex) + (isHighlight ? "-highlight" : "-hide"), () => { const params = this.makeItemStylerParams(datum, datumIndex, isHighlight, { fill, fillOpacity, stroke, strokeWidth, strokeOpacity, lineDash, lineDashOffset, cornerRadius }); return this.ctx.optionsGraphService.resolvePartial( ["series", `${this.declarationOrder}`], this.callWithContext(itemStyler, params), { proxyPaths: { fill: ["fills", `${datumIndex}`], stroke: ["strokes", `${datumIndex}`] } } ); } ); } return { fill: overrides?.fill ?? fill, fillOpacity: overrides?.fillOpacity ?? fillOpacity, stroke: overrides?.stroke ?? stroke, strokeWidth: overrides?.strokeWidth ?? strokeWidth, strokeOpacity: overrides?.strokeOpacity ?? strokeOpacity, lineDash: overrides?.lineDash ?? lineDash, lineDashOffset: overrides?.lineDashOffset ?? lineDashOffset, cornerRadius: overrides?.cornerRadius ?? cornerRadius, opacity }; } makeItemStylerParams(datum, datumIndex, isHighlight, style2) { const { angleKey, radiusKey, calloutLabelKey, sectorLabelKey, legendItemKey } = this.properties; const fill = this.filterItemStylerFillParams(style2.fill) ?? style2.fill; return { datum, angleKey, radiusKey, calloutLabelKey, sectorLabelKey, legendItemKey, ...style2, fill, highlightState: this.getHighlightStateString( this.ctx.highlightManager?.getActiveHighlight(), isHighlight, datumIndex ), seriesId: this.id }; } getCalloutLineStyle(nodeDatum, highlighted) { const { properties } = this; let itemStylerResult = {}; if (properties.calloutLine.itemStyler) { const highlightState = this.getHighlightStateString( this.ctx.highlightManager?.getActiveHighlight(), highlighted, nodeDatum.datumIndex ); const params = { angleKey: properties.angleKey, angleName: properties.angleName ?? properties.angleKey, calloutLabelKey: properties.calloutLabelKey, calloutLabelName: properties.calloutLabelName ?? properties.calloutLabelKey, datum: nodeDatum.datum, highlightState, legendItemKey: properties.legendItemKey, radiusKey: properties.radiusKey, radiusName: properties.radiusName ?? properties.radiusKey, sectorLabelKey: properties.sectorLabelKey, sectorLabelName: properties.sectorLabelName ?? properties.sectorLabelKey, seriesId: this.id }; itemStylerResult = this.cachedCallWithContext(properties.calloutLine.itemStyler, params) ?? {}; } return { length: itemStylerResult.length ?? properties.calloutLine.length, strokeWidth: itemStylerResult.strokeWidth ?? properties.calloutLine.strokeWidth, color: itemStylerResult.color, colors: properties.calloutLine.colors }; } getInnerRadius() { const { radius } = this; const { innerRadiusRatio = 1, innerRadiusOffset = 0 } = this.properties; const innerRadius = radius * innerRadiusRatio + innerRadiusOffset; if (innerRadius === radius || innerRadius < 0) { return 0; } return innerRadius; } getOuterRadius() { const { outerRadiusRatio, outerRadiusOffset } = this.properties; return Math.max(this.radius * outerRadiusRatio + outerRadiusOffset, 0); } updateRadiusScale(resize) { const newRange = [this.getInnerRadius(), this.getOuterRadius()]; this.radiusScale.range = newRange; if (resize) { this.previousRadiusScale.range = newRange; } const setRadii = (d) => ({ ...d, innerRadius: Math.max(this.radiusScale.convert(0), 0), outerRadius: Math.max(this.radiusScale.convert(d.radius), 0) }); this.nodeData = this.nodeData.map(setRadii); this.phantomNodeData = this.phantomNodeData?.map(setRadii); } getTitleTranslationY() { const outerRadius = Math.max(0, this.radiusScale.range[1]); if (outerRadius === 0) { return Number.NaN; } const spacing = this.properties.title?.spacing ?? 0; const titleOffset = 2 + spacing; const dy = Math.max(0, -outerRadius); return -outerRadius - titleOffset - dy; } update({ seriesRect }) { const { title } = this.properties; const newNodeDataDependencies = { seriesRectWidth: seriesRect?.width, seriesRectHeight: seriesRect?.height }; const resize = jsonDiff(this.nodeDataDependencies, newNodeDataDependencies) != null; if (resize) { this._nodeDataDependencies = newNodeDataDependencies; } this.maybeRefreshNodeData(); this.updateTitleNodes(); this.updateRadiusScale(resize); this.contentGroup.translationX = this.centerX; this.contentGroup.translationY = this.centerY; this.highlightGroup.translationX = this.centerX; this.highlightGroup.translationY = this.centerY; this.backgroundGroup.translationX = this.centerX; this.backgroundGroup.translationY = this.centerY; if (this.labelGroup) { this.labelGroup.translationX = this.centerX; this.labelGroup.translationY = this.centerY; } if (title) { const dy = this.getTitleTranslationY(); title.node.y = Number.isFinite(dy) ? dy : 0; const titleBox = title.node.getBBox(); title.node.visible = title.enabled && Number.isFinite(dy) && !this.bboxIntersectsSurroundingSeries(titleBox); } for (const circle of [this.zerosumInnerRing, this.zerosumOuterRing]) { circle.fillOpacity = 0; circle.stroke = this.properties.calloutLabel.color; circle.strokeWidth = 1; circle.strokeOpacity = 1; } this.updateNodeMidPoint(); this.updateSelections(); this.updateNodes(seriesRect); } updateTitleNodes() { const { oldTitle } = this; const { title } = this.properties; if (oldTitle !== title) { if (oldTitle) { oldTitle.node.remove(); } if (title) { title.node.textBaseline = "bottom"; this.labelGroup?.appendChild(title.node); } this.oldTitle = title; } } updateNodeMidPoint() { const setMidPoint = (d) => { const radius = d.innerRadius + (d.outerRadius - d.innerRadius) / 2; d.midPoint = { x: d.midCos * Math.max(0, radius), y: d.midSin * Math.max(0, radius) }; }; for (const datum of this.nodeData) { setMidPoint(datum); } if (this.phantomNodeData) { for (const datum of this.phantomNodeData) { setMidPoint(datum); } } } updateSelections() { this.updateGroupSelection(); this.updateInnerCircleSelection(); } updateGroupSelection() { const { itemSelection, highlightSelection, phantomSelection, phantomHighlightSelection, calloutLabelSelection, labelSelection, highlightLabelSelection, innerLabelsSelection } = this; const highlightedNodeData = this.nodeData.map((datum) => ({ ...datum, // Allow mutable sectorFormat, so formatted sector styles can be updated and varied // between normal and highlighted cases. sectorFormat: { ...datum.sectorFormat } })); const phantomHighlightedNodeData = this.phantomNodeData?.map((datum) => ({ ...datum, // Allow mutable sectorFormat, so formatted sector styles can be updated and varied // between normal and highlighted cases. sectorFormat: { ...datum.sectorFormat } })); const update = (selection, nodeData) => { selection.update(nodeData, void 0, (datum) => this.getDatumId(datum.datumIndex)); if (this.ctx.animationManager.isSkipped()) { selection.cleanup(); } }; update(itemSelection, this.nodeData); update(highlightSelection, highlightedNodeData); update(phantomSelection, this.phantomNodeData ?? []); update(phantomHighlightSelection, phantomHighlightedNodeData ?? []); calloutLabelSelection.update(this.calloutNodeData, (group) => { const line = new Line(); line.tag = 0 /* CalloutLine */; line.pointerEvents = 1 /* None */; group.appendChild(line); const text = new Text(); text.tag = 1 /* CalloutLabel */; text.pointerEvents = 1 /* None */; group.appendChild(text); }); labelSelection.update(this.nodeData); highlightLabelSelection.update(highlightedNodeData); innerLabelsSelection.update(this.properties.innerLabels, (node) => { node.pointerEvents = 1 /* None */; }); } updateInnerCircleSelection() { const { innerCircle } = this.properties; let radius = 0; const innerRadius = this.getInnerRadius(); if (innerRadius > 0) { const circleRadius = Math.min(innerRadius, this.getOuterRadius()); const antiAliasingPadding = 1; radius = Math.ceil(circleRadius * 2 + antiAliasingPadding); } const datums = innerCircle ? [{ radius }] : []; this.innerCircleSelection.update(datums); } updateNodes(seriesRect) { const highlightedDatum = this.ctx.highlightManager.getActiveHighlight(); const { visible, dataModel, processedData } = this; this.backgroundGroup.visible = visible; this.contentGroup.visible = visible; if (!dataModel || !processedData) return; const { legendItemValues } = this.getProcessedDataValues(dataModel, processedData); const seriesHighlighted = this.isSeriesHighlighted(highlightedDatum, legendItemValues); const drawingMode = this.ctx.chartService.highlight?.drawingMode ?? "overlay"; this.highlightGroup.visible = visible && seriesHighlighted; this.labelGroup.visible = visible; this.innerCircleSelection.each((node, { radius }) => { node.setProperties({ fill: this.properties.innerCircle?.fill, opacity: this.properties.innerCircle?.fillOpacity, size: radius }); }); const innerRadius = this.radiusScale.range[0]; const outerRadius = this.radiusScale.range[1]; const fillBBox = this.getShapeFillBBox(); const animationDisabled = this.ctx.animationManager.isSkipped(); const updateSectorFn = (sector, datum, _index, isDatumHighlighted, mode) => { const format = this.getItemStyle(datum, isDatumHighlighted, void 0, legendItemValues); datum.sectorFormat.fill = format.fill; datum.sectorFormat.stroke = format.stroke; if (animationDisabled) { sector.startAngle = datum.startAngle; sector.endAngle = datum.endAngle; sector.innerRadius = datum.innerRadius; sector.outerRadius = datum.outerRadius; } if (isDatumHighlighted || animationDisabled) { sector.fill = format.fill; sector.stroke = format.stroke; } const fillParams = this.getFillParams(format.fill, innerRadius, outerRadius); sector.setStyleProperties(format, fillBBox, fillParams); sector.drawingMode = mode; sector.cornerRadius = format.cornerRadius; sector.fillShadow = this.properties.shadow; const inset = Math.max( (this.properties.sectorSpacing + (format.stroke == null ? 0 : format.strokeWidth)) / 2, 0 ); sector.inset = inset; sector.lineJoin = this.properties.sectorSpacing >= 0 || inset > 0 ? "miter" : "round"; }; this.itemSelection.each((node, datum, index) => updateSectorFn(node, datum, index, false, "overlay")); this.phantomSelection.each((node, datum, index) => updateSectorFn(node, datum, index, false, "overlay")); this.highlightSelection.each((node, datum, index) => { updateSectorFn(node, datum, index, true, drawingMode); node.visible = datum.datumIndex === highlightedDatum?.datumIndex; }); this.phantomHighlightSelection.each((node, datum, index) => { updateSectorFn(node, datum, index, true, drawingMode); node.visible = datum.datumIndex === highlightedDatum?.datumIndex; }); this.updateCalloutLineNodes(); this.updateCalloutLabelNodes(seriesRect); this.updateSectorLabelNodes(); this.updateInnerLabelNodes(); this.updateZerosumRings(); this.animationState.transition("update"); } updateCalloutLineNodes() { const { strokes } = this.properties; const { offset } = this.properties.calloutLabel; const highlightedDatum = this.ctx.highlightManager?.getActiveHighlight(); const seriesHighlighted = this.isSeriesHighlighted(highlightedDatum); for (const line of this.calloutLabelSelection.selectByTag(0 /* CalloutLine */)) { const datum = line.closestDatum(); const isDatumHighlighted = seriesHighlighted && this.isItemHighlighted(highlightedDatum, datum.datumIndex) === true; const { length: calloutLength, strokeWidth, color: color2, colors } = this.getCalloutLineStyle(datum, false); const calloutStrokeWidth = strokeWidth; const calloutColors = isStringFillArray(colors) ? colors : strokes; const { calloutLabel: label, outerRadius, datumIndex } = datum; if (label?.text && !label.hidden && outerRadius !== 0) { line.visible = true; line.strokeWidth = calloutStrokeWidth; line.stroke = color2 ?? calloutColors[datumIndex % calloutColors.length]; line.strokeOpacity = this.getHighlightStyle(isDatumHighlighted, datum.datumIndex).opacity ?? 1; line.fill = void 0; const x1 = datum.midCos * outerRadius; const y1 = datum.midSin * outerRadius; let x2 = datum.midCos * (outerRadius + calloutLength); let y2 = datum.midSin * (outerRadius + calloutLength); const isMoved = label.collisionTextAlign ?? label.collisionOffsetY !== 0; if (isMoved && label.box != null) { const box = label.box; let cx = x2; let cy = y2; if (x2 < box.x) { cx = box.x; } else if (x2 > box.x + box.width) { cx = box.x + box.width; } if (y2 < box.y) { cy = box.y; } else if (y2 > box.y + box.height) { cy = box.y + box.height; } const dx = cx - x2; const dy = cy - y2; const length2 = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)); const paddedLength = length2 - offset; if (paddedLength > 0) { x2 = x2 + dx * paddedLength / length2; y2 = y2 + dy * paddedLength / length2; } } line.x1 = x1; line.y1 = y1; line.x2 = x2; line.y2 = y2; } else { line.visible = false; } } } getLabelOverflow(box, seriesRect) { const seriesLeft = -this.centerX; const seriesRight = seriesLeft + seriesRect.width; const seriesTop = -this.centerY; const seriesBottom = seriesTop + seriesRect.height; const errPx = 1; let maxWidth = box.width; if (box.x + errPx < seriesLeft) { maxWidth = (box.x + box.width - seriesLeft) / box.width; } else if (box.x + box.width - errPx > seriesRight) { maxWidth = (seriesRight - box.x) / box.width; } const hasVerticalOverflow = box.y + errPx < seriesTop || box.y + box.height - errPx > seriesBottom; const hasSurroundingSeriesOverflow = this.bboxIntersectsSurroundingSeries(box); return { maxWidth, hasVerticalOverflow, hasSurroundingSeriesOverflow }; } bboxIntersectsSurroundingSeries(box) { const { surroundingRadius } = this; if (surroundingRadius == null) { return false; } const corners = [ { x: box.x, y: box.y }, { x: box.x + box.width, y: box.y }, { x: box.x + box.width, y: box.y + box.height }, { x: box.x, y: box.y + box.height } ]; const sur2 = surroundingRadius ** 2; return corners.some((corner) => corner.x ** 2 + corner.y ** 2 > sur2); } computeCalloutLabelCollisionOffsets() { const { radiusScale } = this; const { calloutLabel } = this.properties; const { offset, minSpacing } = calloutLabel; const innerRadius = radiusScale.convert(0); const shouldSkip = (datum) => { const label = datum.calloutLabel; return !label || datum.outerRadius === 0; }; const fullData = this.calloutNodeData; const data = fullData.filter((t) => !shouldSkip(t)); for (const datum of data) { const label = datum.calloutLabel; if (label == null) continue; label.hidden = false; label.collisionTextAlign = void 0; label.collisionOffsetY = 0; } if (data.length <= 1) { return; } const leftLabels = data.filter((d) => d.midCos < 0).sort((a, b) => a.midSin - b.midSin); const rightLabels = data.filter((d) => d.midCos >= 0).sort((a, b) => a.midSin - b.midSin); const topLabels = data.filter((d) => d.midSin < 0 && d.calloutLabel?.textAlign === "center").sort((a, b) => a.midCos - b.midCos); const bottomLabels = data.filter((d) => d.midSin >= 0 && d.calloutLabel?.textAlign === "center").sort((a, b) => a.midCos - b.midCos); const getTextBBox = (datum) => { const label = datum.calloutLabel; if (label == null) return BBox.zero.clone(); const style2 = this.getLabelStyle(datum, calloutLabel, "calloutLabel"); const padding2 = expandLabelPadding(style2); const calloutLength = this.getCalloutLineStyle(datum, false).length; const labelRadius = datum.outerRadius + calloutLength + offset; const x = datum.midCos * labelRadius; const y = datum.midSin * labelRadius + label.collisionOffsetY; const textAlign = label.collisionTextAlign ?? label.textAlign; const textBaseline = label.textBaseline; return Text.measureBBox(label.text, x, y, { font: this.properties.calloutLabel, textAlign, textBaseline }).grow(padding2); }; const avoidNeighbourYCollision = (label, next, direction) => { const box = getTextBBox(label).grow(minSpacing / 2); const other = getTextBBox(next).grow(minSpacing / 2); const collidesOrBehind = box.x < other.x + other.width && box.x + box.width > other.x && (direction === "to-top" ? box.y < other.y + other.height : box.y + box.height > other.y); if (collidesOrBehind) { next.calloutLabel.collisionOffsetY = direction === "to-top" ? box.y - other.y - other.height : box.y + box.height - other.y; } }; const avoidYCollisions = (labels) => { const midLabel = labels.slice().sort((a, b) => Math.abs(a.midSin) - Math.abs(b.midSin))[0]; const midIndex = labels.indexOf(midLabel); for (let i = midIndex - 1; i >= 0; i--) { const prev = labels[i + 1]; const next = labels[i]; avoidNeighbourYCollision(prev, next, "to-top"); } for (let i = midIndex + 1; i < labels.length; i++) { const prev = labels[i - 1]; const next = labels[i]; avoidNeighbourYCollision(prev, next, "to-bottom"); } }; const avoidXCollisions = (labels) => { const labelsCollideLabelsByY = data.some((datum) => datum.calloutLabel.collisionOffsetY !== 0); const boxes = labels.map((label) => getTextBBox(label)); const paddedBoxes = boxes.map((box) => box.clone().grow(minSpacing / 2)); let labelsCollideLabelsByX = false; for (let i = 0; i < paddedBoxes.length && !labelsCollideLabelsByX; i++) { const box = paddedBoxes[i]; for (let j = i + 1; j < labels.length; j++) { const other = paddedBoxes[j]; if (box.collidesBBox(other)) { labelsCollideLabelsByX = true; break; } } } const sectors = fullData.map((datum) => { const { startAngle, endAngle, outerRadius } = datum; return { startAngle, endAngle, innerRadius, outerRadius }; }); const labelsCollideSectors = boxes.some((box) => sectors.some((sector) => boxCollidesSector(box, sector))); if (!labelsCollideLabelsByX && !labelsCollideLabelsByY && !labelsCollideSectors) return; for (const d of labels) { if (d.calloutLabel.textAlign !== "center") continue; const label = d.calloutLabel; if (d.midCos < 0) { label.collisionTextAlign = "right"; } else if (d.midCos > 0) { label.collisionTextAlign = "left"; } else { label.collisionTextAlign = "center"; } } }; avoidYCollisions(leftLabels); avoidYCollisions(rightLabels); avoidXCollisions(topLabels); avoidXCollisions(bottomLabels); } getLabelStyle(datum, label, labelPath, isHighlight = false) { const activeHighlight = this.ctx.highlightManager?.getActiveHighlight(); return getLabelStyles(this, datum, this.properties, label, isHighlight, activeHighlight, [ "series", `${this.declarationOrder}`, labelPath ]); } updateCalloutLabelNodes(seriesRect) { const { radiusScale } = this; const { calloutLabel } = this.properties; const tempTextNode = new Text(); const highlightedDatum = this.ctx.highlightManager?.getActiveHighlight(); const seriesHighlighted = this.isSeriesHighlighted(highlightedDatum); for (const text of this.calloutLabelSelection.selectByTag(1 /* CalloutLabel */)) { const datum = text.closestDatum(); const label = datum.calloutLabel; const radius = radiusScale.convert(datum.radius); const outerRadius = Math.max(0, radius); if (!label?.text || outerRadius === 0 || label.hidden) { text.visible = false; continue; } const isDatumHighlighted = seriesHighlighted && this.isItemHighlighted(highlightedDatum, datum.datumIndex) === true; const style2 = this.getLabelStyle(datum, calloutLabel, "calloutLabel", isDatumHighlighted); const calloutLength = this.getCalloutLineStyle(datum, false).length; const labelRadius = outerRadius + calloutLength + calloutLabel.offset; const x = datum.midCos * labelRadius; const y = datum.midSin * labelRadius + label.collisionOffsetY; const align2 = { textAlign: label.collisionTextAlign ?? label.textAlign, textBaseline: label.textBaseline }; tempTextNode.text = label.text; tempTextNode.x = x; tempTextNode.y = y; tempTextNode.setFont(style2); tempTextNode.setAlign(align2); tempTextNode.setBoxing(style2); const box = tempTextNode.getBBox(); let displayText = label.text; let visible = true; if (calloutLabel.avoidCollisions) { const { maxWidth, hasVerticalOverflow } = this.getLabelOverflow(box, seriesRect); if (box.width > maxWidth) { const options = { font: this.properties.calloutLabel, textWrap: "on-space", overflow: "hide", maxWidth }; displayText = wrapTextOrSegments(label.text, options); } visible = !hasVerticalOverflow; } text.text = displayText; text.x = x; text.y = y; text.setFont(style2); text.setAlign(align2); text.setBoxing(style2); text.fill = style2.color; text.fillOpacity = this.getHighlightStyle(isDatumHighlighted, datum.datumIndex).opacity ?? 1; text.visible = visible; } } computeLabelsBBox(options, seriesRect) { const { calloutLabel } = this.properties; const { offset, maxCollisionOffset, minSpacing } = calloutLabel; if (!calloutLabel.avoidCollisions) { return null; } this.maybeRefreshNodeData(); this.updateRadiusScale(false); this.computeCalloutLabelCollisionOffsets(); const textBoxes = []; const text = new Text(); let titleBox = void 0; const { title } = this.properties; if (title?.text && title.enabled) { const dy = this.getTitleTranslationY(); if (Number.isFinite(dy)) { text.text = title.text; text.x = 0; text.y = dy; text.setFont(title); text.setAlign({ textBaseline: "bottom", textAlign: "center" }); titleBox = text.getBBox(); textBoxes.push(titleBox); } } for (const datum of this.calloutNodeData) { const label = datum.calloutLabel; if (!label || datum.outerRadius === 0) { continue; } const style2 = this.getLabelStyle(datum, calloutLabel, "calloutLabel"); const calloutLength = this.getCalloutLineStyle(datum, false).length; const labelRadius = datum.outerRadius + calloutLength + offset; const x = datum.midCos * labelRadius; const y = datum.midSin * labelRadius + label.collisionOffsetY; text.text = label.text; text.x = x; text.y = y; text.setFont(style2); text.setAlign({ textAlign: label.collisionTextAlign ?? label.textAlign, textBaseline: label.textBaseline }); text.setBoxing(style2); const box = text.getBBox(); label.box = box; if (Math.abs(label.collisionOffsetY) > maxCollisionOffset) { label.hidden = true; continue; } if (titleBox) { const seriesTop = -this.centerY; const titleCleanArea = new BBox( titleBox.x - minSpacing, seriesTop, titleBox.width + 2 * minSpacing, titleBox.y + titleBox.height + minSpacing - seriesTop ); if (box.collidesBBox(titleCleanArea)) { label.hidden = true; continue; } } if (options.hideWhenNecessary) { const { maxWidth, hasVerticalOverflow, hasSurroundingSeriesOverflow } = this.getLabelOverflow( box, seriesRect ); const isTooShort = box.width > maxWidth; if (hasVerticalOverflow || isTooShort || hasSurroundingSeriesOverflow) { label.hidden = true; continue; } } label.hidden = false; textBoxes.push(box); } if (textBoxes.length === 0) { return null; } return BBox.merge(textBoxes); } updateSectorLabelNodes() { const { properties } = this; const { positionOffset, positionRatio } = this.properties.sectorLabel; const highlightedDatum = this.ctx.highlightManager?.getActiveHighlight(); const seriesHighlighted = this.isSeriesHighlighted(highlightedDatum); const innerRadius = this.radiusScale.convert(0); const shouldPutTextInCenter = innerRadius <= 0 && // is donut? this.ctx.legendManager.getData(this.id)?.filter((d) => d.enabled).length === 1; const align2 = { textAlign: "center", textBaseline: "middle" }; const updateSelection = (selection) => selection.each((text, datum) => { const { outerRadius, startAngle, endAngle } = datum; const isDatumHighlighted = seriesHighlighted && this.isItemHighlighted(highlightedDatum, datum.datumIndex) === true; let isTextVisible = false; if (datum.sectorLabel && outerRadius !== 0) { const style2 = this.getLabelStyle(datum, properties.sectorLabel, "sectorLabel", isDatumHighlighted); const labelRadius = innerRadius * (1 - positionRatio) + outerRadius * positionRatio + positionOffset; text.fill = style2.color; text.fillOpacity = this.getHighlightStyle(isDatumHighlighted, datum.datumIndex).opacity ?? 1; text.text = datum.sectorLabel.text; if (shouldPutTextInCenter) { text.x = 0; text.y = 0; } else { text.x = datum.midCos * labelRadius; text.y = datum.midSin * labelRadius; } text.setFont(style2); text.setAlign(align2); text.setBoxing(style2); const bbox = text.getBBox(); const corners = [ [bbox.x, bbox.y], [bbox.x + bbox.width, bbox.y], [bbox.x + bbox.width, bbox.y + bbox.height], [bbox.x, bbox.y + bbox.height] ]; const sectorBounds = { startAngle, endAngle, innerRadius, outerRadius }; if (corners.every(([x, y]) => isPointInSector(x, y, sectorBounds))) { isTextVisible = true; } } text.visible = isTextVisible; }); updateSelection(this.labelSelection); updateSelection(this.highlightLabelSelection); } updateInnerLabelNodes() { const textBBoxes = []; const margins = []; this.innerLabelsSelection.each((text, datum) => { const { fontStyle, fontWeight: fontWeight2, fontSize, fontFamily, color: color2 } = datum; text.fontStyle = fontStyle; text.fontWeight = fontWeight2; text.fontSize = fontSize; text.fontFamily = fontFamily; text.text = datum.text; text.x = 0; text.y = 0; text.fill = color2; text.textAlign = "center"; textBBoxes.push(text.getBBox()); margins.push(datum.spacing); }); const getMarginTop = (index) => index === 0 ? 0 : margins[index]; const getMarginBottom = (index) => index === margins.length - 1 ? 0 : margins[index]; const totalWidth = textBBoxes.reduce((max, bbox) => Math.max(max, bbox.width), 0); const totalHeight = textBBoxes.reduce( (sum, bbox, i) => sum + bbox.height + getMarginTop(i) + getMarginBottom(i), 0 ); const innerRadius = this.getInnerRadius(); const labelRadius = Math.sqrt(Math.pow(totalWidth / 2, 2) + Math.pow(totalHeight / 2, 2)); const labelsVisible = labelRadius <= (innerRadius > 0 ? innerRadius : this.getOuterRadius()); const textBottoms = []; for (let i = 0, prev = -totalHeight / 2; i < textBBoxes.length; i++) { const bbox = textBBoxes[i]; const bottom = bbox.height + prev + getMarginTop(i); textBottoms.push(bottom); prev = bottom + getMarginBottom(i); } this.innerLabelsSelection.each((text, _datum, index) => { text.visible = labelsVisible; if (Array.isArray(text.text)) { text.y = textBottoms[index] - textBBoxes[index].height; } else { text.y = textBottoms[index]; } }); } updateZerosumRings() { this.zerosumOuterRing.size = this.getOuterRadius() * 2; this.zerosumInnerRing.size = this.getInnerRadius() * 2; } pickNodeClosestDatum(point) { return pickByMatchingAngle(this, point); } getTooltipContent(datumIndex) { const { id: seriesId, dataModel, processedData, properties, ctx: { formatManager } } = this; const { legendItemKey, calloutLabelKey, calloutLabelName, sectorLabelKey, sectorLabelName, angleKey, angleName, radiusKey, radiusName, tooltip } = properties; const title = this.properties.title.node.getPlainText(); if (!dataModel || !processedData) return; const datum = processedData.dataSources.get(this.id)?.data?.[datumIndex]; const processedDataValues = this.getProcessedDataValues(dataModel, processedData); const { angleRawValues } = processedDataValues; const angleRawValue = angleRawValues[datumIndex]; const labelValues = this.getLabelContent(datumIndex, datum, processedDataValues); const label = labelValues.legendItem ?? labelValues.callout ?? labelValues.sector ?? angleName; const domain = extractDomain(dataModel.getDomain(this, `angleRaw`, "value", processedData)); const angleContent = formatManager.format(this.callWithContext.bind(this), { type: "number", value: angleRawValue, datum, seriesId, legendItemName: void 0, key: angleKey, source: "tooltip", property: "angle", domain, boundSeries: this.getFormatterContext("angle"), fractionDigits: void 0, visibleDomain: void 0 }) ?? formatValue(angleRawValue, 3); return this.formatTooltipWithContext( tooltip, { title, symbol: this.legendItemSymbol(datumIndex), data: [{ label: toPlainText(label), fallbackLabel: angleKey, value: angleContent }] }, { seriesId, datum, title: angleName, legendItemKey, calloutLabelKey, calloutLabelName, sectorLabelKey, sectorLabelName, angleKey, angleName, radiusKey, radiusName, ...this.getItemStyle({ datum, datumIndex }, false) } ); } legendItemSymbol(datumIndex) { const datum = this.processedData?.dataSources.get(this.id)?.data?.[datumIndex]; const sectorFormat = this.getItemStyle({ datum, datumIndex }, false); const { fillOpacity, strokeOpacity, strokeWidth, lineDash, lineDashOffset } = this.properties; let { fill } = sectorFormat; const { stroke } = sectorFormat; if (isGradientFill(fill)) { fill = { ...fill, gradient: "linear", rotation: 0, reverse: false }; } return { marker: { fill, stroke, fillOpacity, strokeOpacity, strokeWidth, lineDash, lineDashOffset } }; } getLegendData(legendType) { const { visible, processedData, dataModel, id: seriesId, ctx: { legendManager } } = this; if (!dataModel || !processedData || legendType !== "category") { return []; } const { angleKey, calloutLabelKey, sectorLabelKey, legendItemKey, showInLegend } = this.properties; if (!legendItemKey && (!calloutLabelKey || calloutLabelKey === angleKey) && (!sectorLabelKey || sectorLabelKey === angleKey)) { return []; } const processedDataValues = this.getProcessedDataValues(dataModel, processedData); const { angleRawValues } = processedDataValues; const titleText = this.properties.title?.showInLegend && this.properties.title.text; const legendData = []; const hideZeros = this.properties.hideZeroValueSectorsInLegend; const rawData = processedData.dataSources.get(this.id)?.data; const invalidData = processedData.invalidData?.get(this.id); for (let datumIndex = 0; datumIndex < processedData.input.count; datumIndex++) { const datum = rawData?.[datumIndex]; const angleRawValue = angleRawValues[datumIndex]; if (invalidData?.[datumIndex] === true || hideZeros && angleRawValue === 0) { continue; } const labelParts = []; if (titleText) { labelParts.push(titleText); } const labels = this.getLabelContent(datumIndex, datum, processedDataValues); if (legendItemKey && labels.legendItem !== void 0) { labelParts.push(labels.legendItem); } else if (calloutLabelKey && calloutLabelKey !== angleKey && labels.callout !== void 0) { labelParts.push(labels.callout); } else if (sectorLabelKey && sectorLabelKey !== angleKey && labels.sector !== void 0) { labelParts.push(labels.sector); } if (labelParts.length === 0) continue; legendData.push({ legendType: "category", id: seriesId, datum, itemId: datumIndex, seriesId, hideToggleOtherSeries: true, enabled: visible && legendManager.getItemEnabled({ seriesId, itemId: datumIndex }), label: { text: labelParts.map((s) => toPlainText(s)).join(" - ") }, symbol: this.legendItemSymbol(datumIndex), legendItemName: legendItemKey == null ? void 0 : datum[legendItemKey], hideInLegend: !showInLegend }); } return legendData; } // Used for grid setLegendState(enabledItems) { const { id: seriesId, ctx: { legendManager, updateService } } = this; for (const [itemId, enabled] of enabledItems.entries()) { legendManager.toggleItem(enabled, seriesId, itemId); } legendManager.update(); updateService.update(7 /* SERIES_UPDATE */); } animateEmptyUpdateReady(_data) { const { animationManager } = this.ctx; const fns = preparePieSeriesAnimationFunctions( true, this.properties.rotation, this.radiusScale, this.previousRadiusScale ); fromToMotion( this.id, "nodes", animationManager, [this.itemSelection, this.highlightSelection, this.phantomSelection, this.phantomHighlightSelection], fns.nodes, (_, datum) => this.getDatumId(datum.datumIndex) ); fromToMotion(this.id, `innerCircle`, animationManager, [this.innerCircleSelection], fns.innerCircle); seriesLabelFadeInAnimation(this, "callout", animationManager, this.calloutLabelSelection); seriesLabelFadeInAnimation(this, "sector", animationManager, this.labelSelection, this.highlightLabelSelection); seriesLabelFadeInAnimation(this, "inner", animationManager, this.innerLabelsSelection); this.previousRadiusScale.range = this.radiusScale.range; } animateWaitingUpdateReady() { const { itemSelection, highlightSelection, phantomSelection, phantomHighlightSelection, processedData, radiusScale, previousRadiusScale } = this; const { animationManager } = this.ctx; const dataDiff = processedData?.reduced?.diff?.[this.id]; this.ctx.animationManager.stopByAnimationGroupId(this.id); const supportedDiff = (dataDiff?.moved.size ?? 0) === 0; const hasKeys = (processedData?.defs.keys.length ?? 0) > 0; const hasUniqueKeys = processedData?.reduced?.animationValidation?.uniqueKeys ?? true; if (!supportedDiff || !hasKeys || !hasUniqueKeys) { this.ctx.animationManager.skipCurrentBatch(); } const noVisibleData = !this.nodeData.some((n) => n.enabled); const fns = preparePieSeriesAnimationFunctions( false, this.properties.rotation, radiusScale, previousRadiusScale ); fromToMotion( this.id, "nodes", animationManager, [itemSelection, highlightSelection, phantomSelection, phantomHighlightSelection], fns.nodes, (_, datum) => this.getDatumId(datum.datumIndex), dataDiff ); fromToMotion(this.id, `innerCircle`, animationManager, [this.innerCircleSelection], fns.innerCircle); seriesLabelFadeInAnimation(this, "callout", this.ctx.animationManager, this.calloutLabelSelection); seriesLabelFadeInAnimation( this, "sector", this.ctx.animationManager, this.labelSelection, this.highlightLabelSelection ); if (this.noVisibleData !== noVisibleData) { this.noVisibleData = noVisibleData; seriesLabelFadeInAnimation(this, "inner", this.ctx.animationManager, this.innerLabelsSelection); } this.previousRadiusScale.range = this.radiusScale.range; } animateClearingUpdateEmpty() { const { itemSelection, highlightSelection, phantomSelection, phantomHighlightSelection, radiusScale, previousRadiusScale } = this; const { animationManager } = this.ctx; const fns = preparePieSeriesAnimationFunctions( false, this.properties.rotation, radiusScale, previousRadiusScale ); fromToMotion( this.id, "nodes", animationManager, [itemSelection, highlightSelection, phantomSelection, phantomHighlightSelection], fns.nodes, (_, datum) => this.getDatumId(datum.datumIndex) ); fromToMotion(this.id, `innerCircle`, animationManager, [this.innerCircleSelection], fns.innerCircle); seriesLabelFadeOutAnimation(this, "callout", this.ctx.animationManager, this.calloutLabelSelection); seriesLabelFadeOutAnimation( this, "sector", this.ctx.animationManager, this.labelSelection, this.highlightLabelSelection ); seriesLabelFadeOutAnimation(this, "inner", this.ctx.animationManager, this.innerLabelsSelection); this.previousRadiusScale.range = this.radiusScale.range; } getDatumId(datumIndex) { const { dataModel, processedData } = this; if (!dataModel || !processedData) { return `${datumIndex}`; } const { calloutLabelKey, sectorLabelKey, legendItemKey } = this.properties; if (!processedData.reduced?.animationValidation?.uniqueKeys) { return `${datumIndex}`; } if (legendItemKey) { const legendItemKeys = dataModel.resolveKeysById(this, "legendItemKey", processedData); return createDatumId(legendItemKeys[datumIndex]); } else if (calloutLabelKey) { const calloutLabelKeys = dataModel.resolveKeysById(this, "calloutLabelKey", processedData); return createDatumId(calloutLabelKeys[datumIndex]); } else if (sectorLabelKey) { const sectorLabelKeys = dataModel.resolveKeysById(this, "sectorLabelKey", processedData); return createDatumId(sectorLabelKeys[datumIndex]); } return `${datumIndex}`; } hasItemStylers() { return !(this.properties.itemStyler == null && this.properties.calloutLabel.itemStyler == null && this.properties.sectorLabel.itemStyler == null && this.properties.innerLabels.every((innerLabel) => innerLabel.itemStyler == null)); } }; DonutSeries.className = "DonutSeries"; DonutSeries.type = "donut"; // packages/ag-charts-community/src/chart/series/polar/pieSeriesOptionsDef.ts var highlight4 = multiSeriesHighlightOptionsDef(shapeHighlightOptionsDef, shapeHighlightOptionsDef); var pieSeriesThemeableOptionsDef = { ...commonSeriesThemeableOptionsDefs, radiusMin: positiveNumber, radiusMax: positiveNumber, rotation: number, outerRadiusOffset: number, outerRadiusRatio: ratio, hideZeroValueSectorsInLegend: boolean, sectorSpacing: positiveNumber, cornerRadius: positiveNumber, itemStyler: callbackDefs({ ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef, cornerRadius: positiveNumber }), title: { enabled: boolean, text: string, showInLegend: boolean, spacing: positiveNumber, ...fontOptionsDef }, calloutLabel: { enabled: boolean, offset: number, minAngle: positiveNumber, avoidCollisions: boolean, formatter: callbackOf(textOrSegments), format: string, itemStyler: callbackDefs({ enabled: boolean, ...labelBoxOptionsDef, ...fontOptionsDef }), ...labelBoxOptionsDef, ...fontOptionsDef }, sectorLabel: { enabled: boolean, positionOffset: number, positionRatio: ratio, formatter: callbackOf(textOrSegments), format: string, itemStyler: callbackDefs({ enabled: boolean, ...labelBoxOptionsDef, ...fontOptionsDef }), ...labelBoxOptionsDef, ...fontOptionsDef }, calloutLine: { colors: arrayOf(color), length: positiveNumber, strokeWidth: positiveNumber, itemStyler: callbackDefs({ color, length: positiveNumber, strokeWidth: positiveNumber }) }, fills: arrayOf(colorUnion), strokes: arrayOf(color), tooltip: tooltipOptionsDefs, shadow: shadowOptionsDefs, highlight: highlight4, ...lineDashOptionsDef, ...without(fillOptionsDef, ["fill"]), ...without(strokeOptionsDef, ["stroke"]) }; var pieSeriesOptionsDef = { ...pieSeriesThemeableOptionsDef, ...commonSeriesOptionsDefs, type: required(constant("pie")), angleKey: required(string), radiusKey: string, calloutLabelKey: string, sectorLabelKey: string, legendItemKey: string, angleName: string, radiusName: string, calloutLabelName: string, sectorLabelName: string, highlight: highlight4 }; pieSeriesOptionsDef.angleFilterKey = undocumented(string); pieSeriesOptionsDef.defaultColorRange = undocumented(arrayOf(arrayOf(color))); pieSeriesOptionsDef.defaultPatternFills = undocumented(arrayOf(color)); pieSeriesOptionsDef.title._enabledFromTheme = undocumented(boolean); pieSeriesOptionsDef.calloutLabel._enabledFromTheme = undocumented(boolean); pieSeriesOptionsDef.sectorLabel._enabledFromTheme = undocumented(boolean); pieSeriesOptionsDef.angleKeyAxis = undocumented(string); pieSeriesOptionsDef.radiusKeyAxis = undocumented(string); // packages/ag-charts-community/src/chart/series/polar/donutSeriesOptionsDef.ts var donutSeriesThemeableOptionsDef = { ...pieSeriesThemeableOptionsDef, innerRadiusOffset: number, innerRadiusRatio: ratio, innerCircle: { fill: string, fillOpacity: ratio }, innerLabels: { spacing: positiveNumber, ...fontOptionsDef, ...labelBoxOptionsDef } }; var donutSeriesOptionsDef = { ...donutSeriesThemeableOptionsDef, ...pieSeriesOptionsDef, type: required(constant("donut")), innerLabels: arrayOfDefs( { text: required(string), spacing: positiveNumber, ...fontOptionsDef, ...labelBoxOptionsDef }, "inner label options array" ) }; donutSeriesOptionsDef.angleFilterKey = undocumented(string); // packages/ag-charts-community/src/chart/series/polar/pieTheme.ts var pieTheme = { series: { title: { enabled: true, fontWeight: { $ref: "fontWeight" }, fontSize: { $rem: FONT_SIZE_RATIO.LARGE }, fontFamily: { $ref: "fontFamily" }, color: { $ref: "subtleTextColor" }, spacing: 5 }, calloutLabel: { ...LABEL_BOXING_DEFAULTS, enabled: true, fontSize: { $ref: "fontSize" }, fontFamily: { $ref: "fontFamily" }, fontWeight: { $ref: "fontWeight" }, color: { $ref: "textColor" }, offset: 3, minAngle: 1e-3 }, sectorLabel: { ...LABEL_BOXING_DEFAULTS, enabled: true, fontWeight: { $ref: "fontWeight" }, fontSize: { $ref: "fontSize" }, fontFamily: { $ref: "fontFamily" }, color: { $ref: "chartBackgroundColor" }, positionOffset: 0, positionRatio: 0.5 }, calloutLine: { length: 10, strokeWidth: 2, colors: { $map: [ { $if: [ { $or: [ { $isGradient: { $value: "$1" } }, { $isPattern: { $value: "$1" } }, { $isImage: { $value: "$1" } } ] }, { $path: ["../../strokes/$index", { $ref: "foregroundColor" }] }, { $value: "$1" } ] }, { $if: [ { $eq: [{ $path: "../strokeWidth" }, 0] }, { $path: "../fills" }, { $path: "../strokes" } ] } ] } }, fills: { $applyCycle: [ { $cacheMax: { $size: { $path: ["./data", { $path: "/data" }] } } }, { $palette: "fills" }, { $applySwitch: [ { $path: ["/type", void 0, { $value: "$1" }] }, { $value: "$1" }, ["gradient", FILL_GRADIENT_RADIAL_REVERSED_SERIES_DEFAULTS], ["pattern", FILL_PATTERN_DEFAULTS], ["image", FILL_IMAGE_DEFAULTS] ] } ] }, strokes: { $applyCycle: [{ $cacheMax: { $size: { $path: ["./data", { $path: "/data" }] } } }, { $palette: "strokes" }] }, fillOpacity: 1, strokeOpacity: 1, strokeWidth: { $isUserOption: ["./strokes/0", 2, 0] }, lineDash: [0], lineDashOffset: 0, rotation: 0, sectorSpacing: 1, shadow: { enabled: false, color: DEFAULT_SHADOW_COLOUR, xOffset: 3, yOffset: 3, blur: 5 }, highlight: PART_WHOLE_HIGHLIGHT_STYLE }, legend: { enabled: true } }; // packages/ag-charts-community/src/chart/series/polar/donutTheme.ts var donutTheme = { ...pieTheme, series: { ...pieTheme.series, innerRadiusRatio: { $if: [{ $eq: [{ $path: ["./innerRadiusOffset", void 0] }, void 0] }, 0.7, void 0] }, innerLabels: { $apply: { ...LABEL_BOXING_DEFAULTS, fontSize: { $ref: "fontSize" }, fontFamily: { $ref: "fontFamily" }, fontWeight: { $ref: "fontWeight" }, color: { $ref: "textColor" }, spacing: 2 } }, highlight: PART_WHOLE_HIGHLIGHT_STYLE } }; // packages/ag-charts-community/src/chart/series/polar/donutSeriesModule.ts var DonutSeriesModule = { type: "series", name: "donut", chartType: "polar", version: VERSION, dependencies: [PolarChartModule], options: donutSeriesOptionsDef, themeTemplate: donutTheme, create: (ctx) => new DonutSeries(ctx) }; // packages/ag-charts-community/src/chart/series/polar/pieSeries.ts var PieSeries = class extends DonutSeries { }; PieSeries.className = "PieSeries"; PieSeries.type = "pie"; // packages/ag-charts-community/src/chart/series/polar/pieSeriesModule.ts var PieSeriesModule = { type: "series", name: "pie", chartType: "polar", version: VERSION, dependencies: [PolarChartModule], options: pieSeriesOptionsDef, themeTemplate: pieTheme, create: (ctx) => new PieSeries(ctx) }; // packages/ag-charts-community/src/module-bundles/polar.ts var AllPolarModule = [ PolarChartModule, DonutSeriesModule, PieSeriesModule, LegendModule, LocaleModule ]; // packages/ag-charts-community/src/module-bundles/all.ts var AllCommunityModule = [ AllCartesianModule, AllPolarModule, // Presets SparklinePresetModule ].flat(); // packages/ag-charts-community/src/util/time-interop.ts function createTimeInterval(unit, step, epoch, utc) { return { unit, step, epoch, utc, every(count) { return createTimeInterval(this.unit, (this.step ?? 1) * count, this.epoch, this.utc); } }; } var cachedInstances = {}; function getTimeInterval(unit, step = 1, epoch, utc = false) { logger_exports.warnOnce("time import is deprecated, use object notation instead"); const key = `${unit}:${step}:${epoch?.getTime() ?? 0}:${utc}`; let instance = cachedInstances[key]; if (instance == null) { instance = createTimeInterval(unit, step, epoch, utc); cachedInstances[key] = instance; } return instance; } var time2 = { get millisecond() { return getTimeInterval("millisecond"); }, get second() { return getTimeInterval("second"); }, get minute() { return getTimeInterval("minute"); }, get hour() { return getTimeInterval("hour"); }, get day() { return getTimeInterval("day"); }, get monday() { return getTimeInterval("day", 7, new Date(1970, 0, 5)); }, get tuesday() { return getTimeInterval("day", 7, new Date(1970, 0, 6)); }, get wednesday() { return getTimeInterval("day", 7, new Date(1970, 0, 7)); }, get thursday() { return getTimeInterval("day", 7, new Date(1970, 0, 1)); }, get friday() { return getTimeInterval("day", 7, new Date(1970, 0, 2)); }, get saturday() { return getTimeInterval("day", 7, new Date(1970, 0, 3)); }, get sunday() { return getTimeInterval("day", 7, new Date(1970, 0, 4)); }, get month() { return getTimeInterval("month"); }, get year() { return getTimeInterval("year"); }, get utcMillisecond() { return getTimeInterval("millisecond", 1, void 0, true); }, get utcSecond() { return getTimeInterval("second", 1, void 0, true); }, get utcMinute() { return getTimeInterval("minute", 1, void 0, true); }, get utcHour() { return getTimeInterval("hour", 1, void 0, true); }, get utcDay() { return getTimeInterval("day", 1, void 0, true); }, get utcMonth() { return getTimeInterval("month", 1, void 0, true); }, get utcYear() { return getTimeInterval("year", 1, void 0, true); } }; // packages/ag-charts-community/src/module/coreModulesTypes.ts function paletteType(partial) { if (partial?.up || partial?.down || partial?.neutral) { return "user-full"; } else if (partial?.fills || partial?.strokes) { return "user-indexed"; } return "inbuilt"; } // packages/ag-charts-community/src/chart/themes/chartTheme.ts var DEFAULT_BACKGROUND_FILL = "white"; var PRESET_OVERRIDES_TYPES = { "radial-gauge": true, "linear-gauge": true }; function hasUserOptionLessThan1(key) { return { $some: [ { $and: [ { $or: [ { $isSeriesType: "line" }, { $isSeriesType: "scatter" }, { $isSeriesType: "area" }, { $isSeriesType: "radar" }, { $isSeriesType: "rangeArea" } ] }, { $isUserOption: [ `/series/$index/${key}`, { $lessThan: [{ $path: `/series/$index/${key}` }, 1] }, false ] } ] }, { $path: "/series" } ] }; } function isPresetOverridesType(type) { return PRESET_OVERRIDES_TYPES[type] === true; } var _ChartTheme = class _ChartTheme { static getDefaultColors() { return { fills: DEFAULT_FILLS, fillsFallback: Object.values(DEFAULT_FILLS), strokes: DEFAULT_STROKES, sequentialColors: getSequentialColors(DEFAULT_FILLS), divergingColors: [DEFAULT_FILLS.ORANGE, DEFAULT_FILLS.YELLOW, DEFAULT_FILLS.GREEN], hierarchyColors: ["#fff", "#e0e5ea", "#c1ccd5", "#a3b4c1", "#859cad"], secondSequentialColors: Color.interpolate( [ Color.fromHexString(DEFAULT_FILLS.BLUE), Color.fromHexString("#cbdef5") // TODO: Color.lighten(DEFAULT_FILLS.BLUE, ?) ], 8 ).map((color2) => color2.toString()), secondDivergingColors: [DEFAULT_FILLS.GREEN, DEFAULT_FILLS.YELLOW, DEFAULT_FILLS.RED], secondHierarchyColors: ["#fff", "#c5cbd1", "#a4b1bd", "#8498a9", "#648096"], up: { fill: DEFAULT_FILLS.GREEN, stroke: DEFAULT_STROKES.GREEN }, down: { fill: DEFAULT_FILLS.RED, stroke: DEFAULT_STROKES.RED }, neutral: { fill: DEFAULT_FILLS.GRAY, stroke: DEFAULT_STROKES.GRAY }, altUp: { fill: DEFAULT_FILLS.BLUE, stroke: DEFAULT_STROKES.BLUE }, altDown: { fill: DEFAULT_FILLS.ORANGE, stroke: DEFAULT_STROKES.ORANGE }, altNeutral: { fill: DEFAULT_FILLS.GRAY, stroke: DEFAULT_STROKES.GRAY } }; } static getDefaultPublicParameters() { return { accentColor: "#2196f3", axisColor: { $foregroundBackgroundMix: 0.325 }, backgroundColor: DEFAULT_BACKGROUND_FILL, borderColor: { $foregroundOpacity: 0.15 }, borderRadius: 4, chartBackgroundColor: { $ref: "backgroundColor" }, chartPadding: 20, focusShadow: "0 0 0 3px var(--ag-charts-accent-color)", foregroundColor: "#181d1f", fontFamily: "Verdana, sans-serif", fontSize: BASE_FONT_SIZE, fontWeight: 400, gridLineColor: { $foregroundBackgroundMix: 0.1 }, popupShadow: "0 0 16px rgba(0, 0, 0, 0.15)", subtleTextColor: { $mix: [{ $ref: "textColor" }, { $ref: "chartBackgroundColor" }, 0.38] }, textColor: { $ref: "foregroundColor" }, separationLinesColor: { $foregroundBackgroundMix: 0.17 }, chromeBackgroundColor: { $foregroundBackgroundMix: 0.02 }, chromeFontFamily: { $ref: "fontFamily" }, chromeFontSize: { $ref: "fontSize" }, chromeFontWeight: { $ref: "fontWeight" }, chromeTextColor: { $ref: "foregroundColor" }, chromeSubtleTextColor: { $mix: [{ $ref: "chromeTextColor" }, { $ref: "backgroundColor" }, 0.38] }, buttonBackgroundColor: { $ref: "backgroundColor" }, buttonBorder: true, buttonFontWeight: 400, buttonTextColor: { $ref: "textColor" }, inputBackgroundColor: { $ref: "backgroundColor" }, inputBorder: true, inputTextColor: { $ref: "textColor" }, menuBackgroundColor: { $ref: "chromeBackgroundColor" }, menuBorder: true, menuTextColor: { $ref: "chromeTextColor" }, panelBackgroundColor: { $ref: "chromeBackgroundColor" }, panelSubtleTextColor: { $ref: "chromeSubtleTextColor" }, tooltipBackgroundColor: { $ref: "chromeBackgroundColor" }, tooltipBorder: true, tooltipTextColor: { $ref: "chromeTextColor" }, tooltipSubtleTextColor: { $ref: "chromeSubtleTextColor" }, crosshairLabelBackgroundColor: { $ref: "foregroundColor" }, crosshairLabelTextColor: { $ref: "chartBackgroundColor" } }; } static getAxisDefaults({ title, time: time3 }) { return mergeDefaults( title && { title: { enabled: false, text: "Axis Title", spacing: 25, fontWeight: { $ref: "fontWeight" }, fontSize: { $rem: FONT_SIZE_RATIO.MEDIUM }, fontFamily: { $ref: "fontFamily" }, color: { $ref: "textColor" } } }, time3 && { parentLevel: { enabled: false, label: { // TODO: { $merge: [{ $path: '../../label' }, { fontWeight: 'bold' }]} enabled: { $path: "../../label/enabled" }, border: { enabled: { $or: [ { $isUserOption: ["../border", true, false] }, { $path: "../../../label/border/enabled" } ] }, strokeWidth: { $path: "../../../label/border/strokeWidth" }, stroke: { $path: "../../../label/border/stroke" } }, fill: { $path: "../../label/fill" }, fontSize: { $path: "../../label/fontSize" }, fontFamily: { $path: "../../label/fontFamily" }, fontWeight: "bold", spacing: { $path: "../../label/spacing" }, color: { $path: "../../label/color" }, cornerRadius: { $path: "../../label/cornerRadius" }, padding: { $path: "../../label/padding" }, avoidCollisions: { $path: "../../label/avoidCollisions" } }, tick: { enabled: { $path: "../../tick/enabled" }, width: { $path: "../../tick/width" }, size: { $path: "../../tick/size" }, stroke: { $path: "../../tick/stroke" } } } }, { label: { enabled: true, fontSize: { $ref: "fontSize" }, fontFamily: { $ref: "fontFamily" }, fontWeight: { $ref: "fontWeight" }, spacing: 11, color: { $ref: "textColor" }, avoidCollisions: true, cornerRadius: 4, border: { enabled: { $isUserOption: ["../border", true, false] }, strokeWidth: 1, stroke: { $foregroundOpacity: 0.08 } }, padding: { $if: [ { $eq: [{ $path: "./border/enabled" }, true] }, { left: 12, right: 12, top: 8, bottom: 8 }, void 0 ] } }, line: { enabled: true, width: 1, stroke: { $ref: "axisColor" } }, tick: { enabled: false, size: 6, width: 1, stroke: { $ref: "axisColor" } }, gridLine: { enabled: true, width: 1, style: { $apply: [ { fillOpacity: 1, stroke: { $ref: "gridLineColor" }, strokeWidth: { $path: "../../width" }, lineDash: [] }, [ { fillOpacity: 1, stroke: { $ref: "gridLineColor" }, strokeWidth: { $path: "../../width" }, lineDash: [] } ] ] } }, crossLines: { $apply: [ { enabled: true, fill: { $ref: "foregroundColor" }, stroke: { $ref: "foregroundColor" }, fillOpacity: 0.08, strokeWidth: 1, label: { fontSize: { $ref: "fontSize" }, fontFamily: { $ref: "fontFamily" }, fontWeight: { $ref: "fontWeight" }, padding: 5, color: { $ref: "textColor" }, border: { enabled: false, stroke: { $ref: "foregroundColor" }, strokeOpacity: 1, strokeWidth: { $isUserOption: ["./stroke", 1, 0] } } } }, void 0, // TODO: can we just infer this common path? // `axisType` path is relative to the axis that is currently being resolved // e.g. `/axes/x/crossLines/[variables]` + `../type` = `/axes/x/type` { $pathString: ["/common/axes/$axisType/crossLines", { axisType: { $path: ["../type"] } }] }, { $pathString: [ "/$seriesType/axes/$axisType/crossLines", { seriesType: { $path: ["/series/0/type", "line"] }, axisType: { $path: ["../type"] } } ] } ] } } ); } getChartDefaults() { return { minHeight: 300, minWidth: 300, background: { visible: true, fill: { $ref: "chartBackgroundColor" } }, padding: { top: { $ref: "chartPadding" }, right: { $ref: "chartPadding" }, bottom: { $ref: "chartPadding" }, left: { $ref: "chartPadding" } }, seriesArea: { border: { enabled: false, stroke: { $ref: "foregroundColor" }, strokeOpacity: 1, strokeWidth: 1 }, cornerRadius: 4, padding: { $if: [{ $eq: [{ $path: "./border/enabled" }, true] }, 5, 0] } }, keyboard: { enabled: true }, title: { enabled: false, text: "Title", spacing: { $if: [{ $path: "../subtitle/enabled" }, 10, 20] }, fontWeight: { $ref: "fontWeight" }, fontSize: { $rem: FONT_SIZE_RATIO.LARGEST }, fontFamily: { $ref: "fontFamily" }, color: { $ref: "textColor" }, wrapping: "hyphenate", layoutStyle: DEFAULT_CAPTION_LAYOUT_STYLE, textAlign: DEFAULT_CAPTION_ALIGNMENT }, subtitle: { enabled: false, text: "Subtitle", spacing: 20, fontWeight: { $ref: "fontWeight" }, fontSize: { $rem: FONT_SIZE_RATIO.MEDIUM }, fontFamily: { $ref: "fontFamily" }, color: { $ref: "subtleTextColor" }, wrapping: "hyphenate", layoutStyle: DEFAULT_CAPTION_LAYOUT_STYLE, textAlign: DEFAULT_CAPTION_ALIGNMENT }, footnote: { enabled: false, text: "Footnote", spacing: 20, fontSize: { $rem: FONT_SIZE_RATIO.MEDIUM }, fontFamily: { $ref: "fontFamily" }, fontWeight: { $ref: "fontWeight" }, color: { $ref: "subtleTextColor" }, wrapping: "hyphenate", layoutStyle: DEFAULT_CAPTION_LAYOUT_STYLE, textAlign: DEFAULT_CAPTION_ALIGNMENT }, highlight: { drawingMode: { $if: [ { $or: [ hasUserOptionLessThan1("highlight/highlightedItem/opacity"), hasUserOptionLessThan1("highlight/unhighlightedItem/opacity"), hasUserOptionLessThan1("highlight/highlightedSeries/opacity"), hasUserOptionLessThan1("highlight/unhighlightedSeries/opacity"), hasUserOptionLessThan1("fillOpacity"), hasUserOptionLessThan1("marker/fillOpacity") ] }, "overlap", "cutout" ] } }, tooltip: { enabled: true, darkTheme: IS_DARK_THEME, delay: 0, pagination: false, mode: { $if: [ { $or: [ { $and: [ { $isChartType: "cartesian" }, { $not: { $hasSeriesType: "bubble" } }, { $not: { $hasSeriesType: "scatter" } }, { $greaterThan: [{ $size: { $path: "/series" } }, 1] }, { $lessThan: [{ $size: { $path: "/series" } }, 4] } ] }, { $and: [ { $isChartType: "polar" }, { $greaterThan: [{ $size: { $path: "/series" } }, 1] }, { $lessThan: [{ $size: { $path: "/series" } }, 4] } ] } ] }, "shared", "single" ] } }, overlays: { darkTheme: IS_DARK_THEME }, listeners: {}, // TODO: remove this series: { tooltip: { range: { $if: [ { $eq: [{ $path: ["/tooltip/range", "exact"] }, "area"] }, "exact", { $path: ["/tooltip/range", "exact"] } ] }, position: { anchorTo: { $path: ["/tooltip/position/anchorTo", "pointer"] }, placement: { $path: ["/tooltip/position/placement", void 0] }, xOffset: { $path: ["/tooltip/position/xOffset", 0] }, yOffset: { $path: ["/tooltip/position/yOffset", 0] } } } } }; } constructor(options = {}) { const { overrides, palette, params } = deepClone(options); const defaults = this.createChartConfigPerChartType(this.getDefaults()); const presets = {}; if (overrides) { this.processOverrides(presets, overrides); } const { fills, strokes, sequentialColors, ...otherColors } = this.getDefaultColors(); this.palette = deepFreeze( mergeDefaults(palette, { fills: Object.values(fills), strokes: Object.values(strokes), sequentialColors: Object.values(sequentialColors), ...otherColors }) ); this.paletteType = paletteType(palette); this.params = mergeDefaults(params, this.getPublicParameters()); this.config = deepFreeze(deepClone(defaults)); this.overrides = deepFreeze(overrides); this.presets = deepFreeze(presets); } processOverrides(presets, overrides) { for (const s of moduleRegistry_exports.listModulesByType("series" /* Series */)) { const seriesType = s.name; const seriesOverrides = overrides[seriesType]; if (isPresetOverridesType(seriesType)) { presets[seriesType] = seriesOverrides; delete overrides[seriesType]; } } } createChartConfigPerChartType(config) { var _a; for (const chartModule of moduleRegistry_exports.listModulesByType("chart" /* Chart */)) { for (const seriesModule of moduleRegistry_exports.listModulesByType("series" /* Series */)) { if (seriesModule.chartType !== chartModule.name) continue; config[_a = seriesModule.name] ?? (config[_a] = chartModule.themeTemplate); } } return config; } getDefaults() { const getOverridesByType = (chartType, seriesTypes) => { const result = {}; const chartTypeDefaults = mergeDefaults( { axes: {} }, ...Array.from(moduleRegistry_exports.listModulesByType("plugin" /* Plugin */), (p) => ({ [p.name]: p.themeTemplate })), moduleRegistry_exports.getChartModule(chartType)?.themeTemplate, this.getChartDefaults() ); for (const seriesType of seriesTypes) { result[seriesType] = mergeDefaults( getSeriesThemeTemplate(seriesType), result[seriesType] ?? chartTypeDefaults ); const { axes } = result[seriesType]; for (const axisModule of moduleRegistry_exports.listModulesByType("axis" /* Axis */)) { axes[axisModule.name] = mergeDefaults( axes[axisModule.name], !axisModule.chartType || axisModule.chartType === chartType ? getAxisThemeTemplate(axisModule.name) : null, _ChartTheme.axisDefault[axisModule.name] ); } if (seriesType === "map-shape-background" || seriesType === "map-line-background") { delete result[seriesType].series.tooltip; } } return result; }; const seriesModules = [...moduleRegistry_exports.listModulesByType("series" /* Series */)]; const seriesByChartType = groupBy(seriesModules, (s) => s.chartType || "unknown"); return mergeDefaults( ...Object.keys(seriesByChartType).map( (chartType) => getOverridesByType(chartType, seriesByChartType[chartType]?.map((s) => s.name) ?? []) ) ); } static applyTemplateTheme(node, _other, params) { if (isArray(node)) { for (let i = 0; i < node.length; i++) { const symbol = node[i]; if (typeof symbol === "symbol" && params?.has(symbol)) { node[i] = params.get(symbol); } } } else { for (const name of Object.keys(node)) { const value = node[name]; if (typeof value === "symbol" && params?.has(value)) { node[name] = params.get(value); } } } } templateTheme(themeTemplate7, clone2 = true) { const themeInstance = clone2 ? deepClone(themeTemplate7) : themeTemplate7; const params = this.getTemplateParameters(); jsonWalk(themeInstance, _ChartTheme.applyTemplateTheme, void 0, void 0, params); return themeInstance; } getDefaultColors() { return _ChartTheme.getDefaultColors(); } getPublicParameters() { return _ChartTheme.getDefaultPublicParameters(); } // Private parameters that are not exposed in the themes API. getTemplateParameters() { const params = /* @__PURE__ */ new Map(); params.set(IS_DARK_THEME, false); params.set(DEFAULT_SHADOW_COLOUR, "#00000080"); params.set(DEFAULT_SPARKLINE_CROSSHAIR_STROKE, "#aaa"); params.set(DEFAULT_CAPTION_LAYOUT_STYLE, "block"); params.set(DEFAULT_CAPTION_ALIGNMENT, "center"); params.set(DEFAULT_FIBONACCI_STROKES, [ "#797b86", "#e24c4a", "#f49d2d", "#65ab58", "#409682", "#4db9d2", "#5090dc", "#3068f9", "#e24c4a", "#913aac", "#d93e64" ]); params.set(DEFAULT_POLAR_SERIES_STROKE, DEFAULT_BACKGROUND_FILL); params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR, DEFAULT_FILLS.BLUE); params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL, DEFAULT_FILLS.BLUE); params.set(DEFAULT_TEXT_ANNOTATION_COLOR, DEFAULT_FILLS.BLUE); params.set(DEFAULT_ANNOTATION_HANDLE_FILL, DEFAULT_BACKGROUND_FILL); params.set(DEFAULT_ANNOTATION_STATISTICS_FILL, "#fafafa"); params.set(DEFAULT_ANNOTATION_STATISTICS_STROKE, "#ddd"); params.set(DEFAULT_ANNOTATION_STATISTICS_COLOR, "#000"); params.set(DEFAULT_ANNOTATION_STATISTICS_DIVIDER_STROKE, "#181d1f"); params.set(DEFAULT_ANNOTATION_STATISTICS_DOWN_FILL, "#e35c5c"); params.set(DEFAULT_ANNOTATION_STATISTICS_DOWN_STROKE, "#e35c5c"); params.set(DEFAULT_TEXTBOX_FILL, "#fafafa"); params.set(DEFAULT_TEXTBOX_STROKE, "#ddd"); params.set(DEFAULT_TEXTBOX_COLOR, "#000"); params.set(DEFAULT_TOOLBAR_POSITION, "top"); const defaultColors = this.getDefaultColors(); params.set(PALETTE_UP_STROKE, this.palette.up?.stroke ?? defaultColors.up.stroke); params.set(PALETTE_UP_FILL, this.palette.up?.fill ?? defaultColors.up.fill); params.set(PALETTE_DOWN_STROKE, this.palette.down?.stroke ?? defaultColors.down.stroke); params.set(PALETTE_DOWN_FILL, this.palette.down?.fill ?? defaultColors.down.fill); params.set(PALETTE_NEUTRAL_STROKE, this.palette.neutral?.stroke ?? defaultColors.neutral.stroke); params.set(PALETTE_NEUTRAL_FILL, this.palette.neutral?.fill ?? defaultColors.neutral.fill); params.set(PALETTE_ALT_UP_STROKE, this.palette.altUp?.stroke ?? defaultColors.up.stroke); params.set(PALETTE_ALT_UP_FILL, this.palette.altUp?.fill ?? defaultColors.up.fill); params.set(PALETTE_ALT_DOWN_STROKE, this.palette.altDown?.stroke ?? defaultColors.down.stroke); params.set(PALETTE_ALT_DOWN_FILL, this.palette.altDown?.fill ?? defaultColors.down.fill); params.set(PALETTE_ALT_NEUTRAL_FILL, this.palette.altNeutral?.fill ?? defaultColors.altNeutral.fill); params.set(PALETTE_ALT_NEUTRAL_STROKE, this.palette.altNeutral?.stroke ?? defaultColors.altNeutral.stroke); return params; } }; _ChartTheme.axisDefault = { ["number" /* NUMBER */]: _ChartTheme.getAxisDefaults({ title: true, time: false }), ["log" /* LOG */]: _ChartTheme.getAxisDefaults({ title: true, time: false }), ["category" /* CATEGORY */]: _ChartTheme.getAxisDefaults({ title: true, time: false }), ["grouped-category" /* GROUPED_CATEGORY */]: _ChartTheme.getAxisDefaults({ title: true, time: false }), ["time" /* TIME */]: _ChartTheme.getAxisDefaults({ title: true, time: true }), ["unit-time" /* UNIT_TIME */]: _ChartTheme.getAxisDefaults({ title: true, time: true }), ["ordinal-time" /* ORDINAL_TIME */]: _ChartTheme.getAxisDefaults({ title: true, time: true }), ["angle-category" /* ANGLE_CATEGORY */]: _ChartTheme.getAxisDefaults({ title: false, time: false }), ["angle-number" /* ANGLE_NUMBER */]: _ChartTheme.getAxisDefaults({ title: false, time: false }), ["radius-category" /* RADIUS_CATEGORY */]: _ChartTheme.getAxisDefaults({ title: true, time: false }), ["radius-number" /* RADIUS_NUMBER */]: _ChartTheme.getAxisDefaults({ title: true, time: false }) }; var ChartTheme = _ChartTheme; function getAxisThemeTemplate(axisType) { let themeTemplate7 = moduleRegistry_exports.getAxisModule(axisType)?.themeTemplate ?? {}; for (const module2 of moduleRegistry_exports.listModulesByType("axis:plugin" /* AxisPlugin */)) { if (module2.axisTypes?.includes(axisType) ?? true) { themeTemplate7 = mergeDefaults({ [module2.name]: module2.themeTemplate }, themeTemplate7); } } return themeTemplate7; } function getSeriesThemeTemplate(seriesType) { let themeTemplate7 = moduleRegistry_exports.getSeriesModule(seriesType)?.themeTemplate ?? {}; for (const module2 of moduleRegistry_exports.listModulesByType("series:plugin" /* SeriesPlugin */)) { if (module2.seriesTypes?.includes(seriesType) ?? true) { themeTemplate7 = mergeDefaults({ series: { [module2.name]: module2.themeTemplate } }, themeTemplate7); } } return themeTemplate7; } // packages/ag-charts-community/src/chart/factory/processModuleOptions.ts var SkippedModules = /* @__PURE__ */ new Set(["foreground"]); function sanitizeThemeModules(theme) { const missingModules = /* @__PURE__ */ new Map(); for (const [name, { type }] of ExpectedModules) { if (moduleRegistry_exports.hasModule(name)) continue; if (missingModules.has(type)) { missingModules.get(type).add(name); } else { missingModules.set(type, /* @__PURE__ */ new Set([name])); } } if (missingModules.size === 0) return theme; function prunePlugins(target) { const missingPlugins = missingModules.get("plugin" /* Plugin */); if (!isObject(target) || !missingPlugins) return; for (const pluginName of missingPlugins) { if (pluginName in target && target[pluginName].enabled !== true) { delete target[pluginName]; } } } function pruneSeriesPlugins(target) { const missingSeriesPlugins = missingModules.get("series:plugin" /* SeriesPlugin */); if (!isObject(target) || !missingSeriesPlugins) return; for (const pluginName of missingSeriesPlugins) { if (pluginName in target) { delete target[pluginName]; } } } function pruneAxisPlugins(target) { const missingAxisPlugins = missingModules.get("axis:plugin" /* AxisPlugin */); if (!isObject(target) || !missingAxisPlugins) return; for (const pluginName of missingAxisPlugins) { if (pluginName in target && target[pluginName].enabled !== true) { delete target[pluginName]; } } } function pruneAxes(axes) { if (!isObject(axes)) return; for (const axisName of Object.keys(axes)) { if (missingModules.get("axis" /* Axis */)?.has(axisName)) { delete axes[axisName]; continue; } pruneAxisPlugins(axes[axisName]); } } function pruneSeriesEntry(entry) { if (!isObject(entry)) return; pruneAxes(entry.axes); prunePlugins(entry); pruneSeriesPlugins(entry.series); } const config = deepClone(theme.config); const overrides = deepClone(theme.overrides); const presets = deepClone(theme.presets); for (const seriesType of Object.keys(config)) { if (missingModules.get("series" /* Series */)?.has(seriesType)) { delete config[seriesType]; continue; } pruneSeriesEntry(config[seriesType]); } if (isObject(overrides)) { const overridesObj = overrides; if (isObject(overridesObj.common)) { pruneAxes(overridesObj.common.axes); prunePlugins(overridesObj.common); } for (const seriesType of Object.keys(overridesObj)) { if (seriesType === "common") continue; if (missingModules.get("series" /* Series */)?.has(seriesType)) { delete overridesObj[seriesType]; continue; } pruneSeriesEntry(overridesObj[seriesType]); } } if (isObject(presets)) { const presetsObj = presets; for (const presetName of Object.keys(presetsObj)) { if (missingModules.get("preset" /* Preset */)?.has(presetName) || missingModules.get("series" /* Series */)?.has(presetName)) { delete presetsObj[presetName]; continue; } prunePlugins(presetsObj[presetName]); pruneAxes(presetsObj[presetName]?.axes); } } return Object.create(theme, { config: { value: deepFreeze(config), enumerable: true }, overrides: { value: isObject(overrides) ? deepFreeze(overrides) : overrides, enumerable: true }, presets: { value: isObject(presets) ? deepFreeze(presets) : presets, enumerable: true } }); } function processModuleOptions(chartType, options, additionalMissingModules) { const missingModules = unique(removeUnregisteredModuleOptions(chartType, options).concat(additionalMissingModules)); if (!missingModules.length) return; const installationReferenceUrl = moduleRegistry_exports.isIntegrated() ? "https://www.ag-grid.com/data-grid/integrated-charts-installation/" : "https://www.ag-grid.com/charts/r/module-registry/"; const missingOptions = groupBy(missingModules, (module2) => module2.enterprise ? "enterprise" : "community"); if (missingModules.length) { const packageName = moduleRegistry_exports.isEnterprise() || missingOptions.enterprise?.length ? "enterprise" : "community"; logger_exports.errorOnce( [ "required modules are not registered. Check if you have registered the modules:", "", moduleRegistry_exports.isUmd() ? `Install and register 'ag-charts-enterprise' before creating the chart.` : createRegistrySnippet(missingModules.map(formatMissingModuleName), packageName), "", `See ${installationReferenceUrl} for more details.` ].join("\n") ); } } function formatMissingModuleName(module2) { return module2.moduleId ?? module2.name; } function formatImportItem(name) { return ` ${name},`; } function formatImports(imports, packageName) { return imports.length ? `import { ${imports.map(formatImportItem).join("\n")} } from 'ag-charts-${packageName}';` : null; } function createRegistrySnippet(moduleNames, packageName) { const imports = formatImports(["ModuleRegistry"].concat(moduleNames), packageName); const moduleList = moduleNames.map(formatImportItem).join("\n"); return `${imports} ModuleRegistry.registerModules([ ${moduleList} ]);`; } function removeUnregisteredModuleOptions(chartType, options) { const missingModules = /* @__PURE__ */ new Map(); const optionsAxes = "axes" in options && isObject(options.axes) ? options.axes : {}; const axisTypesInOptions = new Set( Object.values(optionsAxes).map((axis) => axis?.type).filter(isDefined) ); const seriesTypesInOptions = new Set(options.series?.map((series) => series.type).filter(isDefined)); function addMissingModule(module2) { missingModules.set(module2.name, module2); } for (const module2 of ExpectedModules.values()) { if (moduleRegistry_exports.hasModule(module2.name)) continue; if (SkippedModules.has(module2.name)) continue; if (chartType && module2.chartType && chartType !== module2.chartType) continue; switch (module2.type) { case "chart": break; case "axis": if (axisTypesInOptions.has(module2.name)) { for (const key of Object.keys(optionsAxes)) { if (optionsAxes?.[key].type === module2.name) { delete optionsAxes[key]; } } addMissingModule(module2); } break; case "series": if (seriesTypesInOptions.has(module2.name)) { options.series = options.series.filter((series) => series.type !== module2.name); addMissingModule(module2); } break; case "plugin": const optionsKey = module2.name; const pluginValue = options[optionsKey]; if (isObject(pluginValue)) { if (pluginValue.enabled !== false) { addMissingModule(module2); } delete options[optionsKey]; } break; case "axis:plugin": for (const axis of Object.values(optionsAxes)) { const axisModuleKey = module2.name; if (axis?.[axisModuleKey]) { if (axis[axisModuleKey].enabled !== false) { addMissingModule(module2); } delete axis[axisModuleKey]; } } break; case "series:plugin": for (const series of options.series ?? []) { if (series[module2.name]) { delete series[module2.name]; addMissingModule(module2); } } break; } } for (const seriesType of seriesTypesInOptions) { const expectedSeriesModule = ExpectedModules.get(seriesType); if (expectedSeriesModule?.type === "series" /* Series */ && !moduleRegistry_exports.hasModule(expectedSeriesModule.name) && !missingModules.has(expectedSeriesModule.name)) { options.series = options.series.filter((series) => series.type !== expectedSeriesModule.name); addMissingModule(expectedSeriesModule); } } return Array.from(missingModules.values()); } function removeIncompatibleModuleOptions(chartType, options) { const hasAxesOptions = "axes" in options && isObject(options.axes); const hasSeriesOptions = "series" in options && isArray(options.series); const matchChartType = (module2) => chartType == null || !module2.chartType || module2.chartType === chartType; const incompatibleModules = []; for (const module2 of moduleRegistry_exports.listModules()) { if (moduleRegistry_exports.isModuleType("plugin" /* Plugin */, module2)) { if (!matchChartType(module2)) { delete options[module2.name]; incompatibleModules.push(module2.name); } } else if (moduleRegistry_exports.isModuleType("axis:plugin" /* AxisPlugin */, module2)) { if (hasAxesOptions && !matchChartType(module2)) { for (const axis of Object.values(options.axes)) { delete axis[module2.name]; } incompatibleModules.push(module2.name); } } else if (moduleRegistry_exports.isModuleType("series:plugin" /* SeriesPlugin */, module2)) { if (hasSeriesOptions && !matchChartType(module2)) { for (const series of options.series) { delete series[module2.name]; } incompatibleModules.push(module2.name); } } } return incompatibleModules; } // packages/ag-charts-community/src/chart/themes/darkTheme.ts var DEFAULT_DARK_BACKGROUND_FILL = "#192232"; var DEFAULT_DARK_FILLS = { BLUE: "#5090dc", ORANGE: "#ffa03a", GREEN: "#459d55", CYAN: "#34bfe1", YELLOW: "#e1cc00", VIOLET: "#9669cb", GRAY: "#b5b5b5", MAGENTA: "#bd5aa7", BROWN: "#8a6224", RED: "#ef5452" }; var DEFAULT_DARK_STROKES = { BLUE: "#74a8e6", ORANGE: "#ffbe70", GREEN: "#6cb176", CYAN: "#75d4ef", YELLOW: "#f6e559", VIOLET: "#aa86d8", GRAY: "#a1a1a1", MAGENTA: "#ce7ab9", BROWN: "#997b52", RED: "#ff7872" }; var DarkTheme = class extends ChartTheme { getDefaultColors() { return { fills: DEFAULT_DARK_FILLS, fillsFallback: Object.values(DEFAULT_DARK_FILLS), strokes: DEFAULT_DARK_STROKES, sequentialColors: getSequentialColors(DEFAULT_DARK_FILLS), divergingColors: [DEFAULT_DARK_FILLS.ORANGE, DEFAULT_DARK_FILLS.YELLOW, DEFAULT_DARK_FILLS.GREEN], hierarchyColors: ["#192834", "#253746", "#324859", "#3f596c", "#4d6a80"], secondSequentialColors: [ "#5090dc", "#4882c6", "#4073b0", "#38659a", "#305684", "#28486e", "#203a58", "#182b42" ], secondDivergingColors: [DEFAULT_DARK_FILLS.GREEN, DEFAULT_DARK_FILLS.YELLOW, DEFAULT_DARK_FILLS.RED], secondHierarchyColors: ["#192834", "#3b5164", "#496275", "#577287", "#668399"], up: { fill: DEFAULT_DARK_FILLS.GREEN, stroke: DEFAULT_DARK_STROKES.GREEN }, down: { fill: DEFAULT_DARK_FILLS.RED, stroke: DEFAULT_DARK_STROKES.RED }, neutral: { fill: DEFAULT_DARK_FILLS.GRAY, stroke: DEFAULT_DARK_STROKES.GRAY }, altUp: { fill: DEFAULT_DARK_FILLS.BLUE, stroke: DEFAULT_DARK_STROKES.BLUE }, altDown: { fill: DEFAULT_DARK_FILLS.ORANGE, stroke: DEFAULT_DARK_STROKES.ORANGE }, altNeutral: { fill: DEFAULT_DARK_FILLS.GRAY, stroke: DEFAULT_DARK_STROKES.GRAY } }; } getPublicParameters() { return { ...super.getPublicParameters(), axisColor: { $foregroundBackgroundMix: 0.737 }, backgroundColor: DEFAULT_DARK_BACKGROUND_FILL, borderColor: { $foregroundBackgroundMix: 0.216 }, chromeBackgroundColor: { $foregroundBackgroundMix: 0.07 }, foregroundColor: "#fff", gridLineColor: { $foregroundBackgroundMix: 0.257 }, popupShadow: "0 0 16px rgba(0, 0, 0, 0.33)", subtleTextColor: { $mix: [{ $ref: "textColor" }, { $ref: "chartBackgroundColor" }, 0.57] }, separationLinesColor: { $foregroundBackgroundMix: 0.44 }, crosshairLabelBackgroundColor: { $foregroundBackgroundMix: 0.65 } }; } getTemplateParameters() { const params = super.getTemplateParameters(); params.set(IS_DARK_THEME, true); params.set(DEFAULT_POLAR_SERIES_STROKE, DEFAULT_DARK_BACKGROUND_FILL); params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR, DEFAULT_DARK_FILLS.BLUE); params.set(DEFAULT_TEXT_ANNOTATION_COLOR, "#fff"); params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL, DEFAULT_DARK_FILLS.BLUE); params.set(DEFAULT_ANNOTATION_HANDLE_FILL, DEFAULT_DARK_BACKGROUND_FILL); params.set(DEFAULT_ANNOTATION_STATISTICS_FILL, "#28313e"); params.set(DEFAULT_ANNOTATION_STATISTICS_STROKE, "#4b525d"); params.set(DEFAULT_ANNOTATION_STATISTICS_COLOR, "#fff"); params.set(DEFAULT_ANNOTATION_STATISTICS_DIVIDER_STROKE, "#fff"); params.set(DEFAULT_TEXTBOX_FILL, "#28313e"); params.set(DEFAULT_TEXTBOX_STROKE, "#4b525d"); params.set(DEFAULT_TEXTBOX_COLOR, "#fff"); return params; } constructor(options) { super(options); } }; // packages/ag-charts-community/src/chart/themes/financialDark.ts var FINANCIAL_DARK_FILLS = { GREEN: "#089981", RED: "#F23645", BLUE: "#5090dc", GRAY: "#A9A9A9" }; var FINANCIAL_DARK_STROKES = { GREEN: "#089981", RED: "#F23645", BLUE: "#5090dc", GRAY: "#909090" }; var FinancialDark = class extends DarkTheme { getDefaultColors() { return { ...super.getDefaultColors(), fills: { ...FINANCIAL_DARK_FILLS }, fillsFallback: Object.values({ ...FINANCIAL_DARK_FILLS }), strokes: { ...FINANCIAL_DARK_STROKES }, sequentialColors: getSequentialColors(FINANCIAL_DARK_FILLS), divergingColors: [FINANCIAL_DARK_FILLS.GREEN, FINANCIAL_DARK_FILLS.BLUE, FINANCIAL_DARK_FILLS.RED], // hierarchyColors: [], secondSequentialColors: [ "#5090dc", "#4882c6", "#4073b0", "#38659a", "#305684", "#28486e", "#203a58", "#182b42" ], // secondDivergingColors: [], // secondHierarchyColors: [], up: { fill: FINANCIAL_DARK_FILLS.GREEN, stroke: FINANCIAL_DARK_STROKES.GREEN }, down: { fill: FINANCIAL_DARK_FILLS.RED, stroke: FINANCIAL_DARK_STROKES.RED }, neutral: { fill: FINANCIAL_DARK_FILLS.BLUE, stroke: FINANCIAL_DARK_STROKES.BLUE }, altUp: { fill: FINANCIAL_DARK_FILLS.GREEN, stroke: FINANCIAL_DARK_STROKES.GREEN }, altDown: { fill: FINANCIAL_DARK_FILLS.RED, stroke: FINANCIAL_DARK_STROKES.RED }, altNeutral: { fill: FINANCIAL_DARK_FILLS.GRAY, stroke: FINANCIAL_DARK_STROKES.GRAY } }; } getPublicParameters() { return { ...super.getPublicParameters(), chartPadding: 0, gridLineColor: { $foregroundBackgroundMix: 0.12 } }; } getTemplateParameters() { const params = super.getTemplateParameters(); params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR, FINANCIAL_DARK_FILLS.BLUE); params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL, FINANCIAL_DARK_FILLS.BLUE); params.set(DEFAULT_CAPTION_LAYOUT_STYLE, "overlay"); params.set(DEFAULT_CAPTION_ALIGNMENT, "left"); params.set(DEFAULT_TOOLBAR_POSITION, "bottom"); return params; } }; // packages/ag-charts-community/src/chart/themes/financialLight.ts var FINANCIAL_LIGHT_FILLS = { GREEN: "#089981", RED: "#F23645", BLUE: "#5090dc", GRAY: "#A9A9A9" }; var FINANCIAL_LIGHT_STROKES = { GREEN: "#089981", RED: "#F23645", BLUE: "#5090dc", GRAY: "#909090" }; var FinancialLight = class extends ChartTheme { getDefaultColors() { return { ...super.getDefaultColors(), fills: { ...FINANCIAL_LIGHT_FILLS }, fillsFallback: Object.values({ ...FINANCIAL_LIGHT_FILLS }), strokes: { ...FINANCIAL_LIGHT_STROKES }, sequentialColors: getSequentialColors(FINANCIAL_LIGHT_FILLS), divergingColors: [FINANCIAL_LIGHT_FILLS.GREEN, FINANCIAL_LIGHT_FILLS.BLUE, FINANCIAL_LIGHT_FILLS.RED], // hierarchyColors: [], // secondSequentialColors: [], // secondDivergingColors: [], // secondHierarchyColors: [], up: { fill: FINANCIAL_LIGHT_FILLS.GREEN, stroke: FINANCIAL_LIGHT_STROKES.GREEN }, down: { fill: FINANCIAL_LIGHT_FILLS.RED, stroke: FINANCIAL_LIGHT_STROKES.RED }, neutral: { fill: FINANCIAL_LIGHT_FILLS.BLUE, stroke: FINANCIAL_LIGHT_STROKES.BLUE }, altUp: { fill: FINANCIAL_LIGHT_FILLS.GREEN, stroke: FINANCIAL_LIGHT_STROKES.GREEN }, altDown: { fill: FINANCIAL_LIGHT_FILLS.RED, stroke: FINANCIAL_LIGHT_STROKES.RED }, altNeutral: { fill: FINANCIAL_LIGHT_FILLS.GRAY, stroke: FINANCIAL_LIGHT_STROKES.GRAY } }; } getPublicParameters() { return { ...super.getPublicParameters(), chartPadding: 0, gridLineColor: { $foregroundBackgroundMix: 0.06 } }; } getTemplateParameters() { const params = super.getTemplateParameters(); params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR, FINANCIAL_LIGHT_FILLS.BLUE); params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL, FINANCIAL_LIGHT_FILLS.BLUE); params.set(DEFAULT_CAPTION_LAYOUT_STYLE, "overlay"); params.set(DEFAULT_CAPTION_ALIGNMENT, "left"); params.set(DEFAULT_TOOLBAR_POSITION, "bottom"); return params; } }; // packages/ag-charts-community/src/chart/themes/materialDark.ts var MATERIAL_DARK_FILLS = { BLUE: "#2196F3", ORANGE: "#FF9800", GREEN: "#4CAF50", CYAN: "#00BCD4", YELLOW: "#FFEB3B", VIOLET: "#7E57C2", GRAY: "#9E9E9E", MAGENTA: "#F06292", BROWN: "#795548", RED: "#F44336" }; var MATERIAL_DARK_STROKES = { BLUE: "#90CAF9", ORANGE: "#FFCC80", GREEN: "#A5D6A7", CYAN: "#80DEEA", YELLOW: "#FFF9C4", VIOLET: "#B39DDB", GRAY: "#E0E0E0", MAGENTA: "#F48FB1", BROWN: "#A1887F", RED: "#EF9A9A" }; var MaterialDark = class extends DarkTheme { getDefaultColors() { return { ...super.getDefaultColors(), fills: MATERIAL_DARK_FILLS, fillsFallback: Object.values(MATERIAL_DARK_FILLS), strokes: MATERIAL_DARK_STROKES, sequentialColors: getSequentialColors(MATERIAL_DARK_FILLS), divergingColors: [MATERIAL_DARK_FILLS.ORANGE, MATERIAL_DARK_FILLS.YELLOW, MATERIAL_DARK_FILLS.GREEN], // hierarchyColors: [], secondSequentialColors: [ "#2196f3", // 500 "#208FEC", // (interpolated) "#1E88E5", // 600 "#1C7FDC", // (interpolated) "#1976d2", // 700 "#176EC9", // (interpolated) "#1565c0" // 800 ], secondDivergingColors: [MATERIAL_DARK_FILLS.GREEN, MATERIAL_DARK_FILLS.YELLOW, MATERIAL_DARK_FILLS.RED], // secondHierarchyColors: [], up: { fill: MATERIAL_DARK_FILLS.GREEN, stroke: MATERIAL_DARK_STROKES.GREEN }, down: { fill: MATERIAL_DARK_FILLS.RED, stroke: MATERIAL_DARK_STROKES.RED }, neutral: { fill: MATERIAL_DARK_FILLS.GRAY, stroke: MATERIAL_DARK_STROKES.GRAY }, altUp: { fill: MATERIAL_DARK_FILLS.BLUE, stroke: MATERIAL_DARK_STROKES.BLUE }, altDown: { fill: MATERIAL_DARK_FILLS.RED, stroke: MATERIAL_DARK_STROKES.RED }, altNeutral: { fill: MATERIAL_DARK_FILLS.GRAY, stroke: MATERIAL_DARK_STROKES.GRAY } }; } getTemplateParameters() { const params = super.getTemplateParameters(); params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR, MATERIAL_DARK_FILLS.BLUE); params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL, MATERIAL_DARK_FILLS.BLUE); return params; } }; // packages/ag-charts-community/src/chart/themes/materialLight.ts var MATERIAL_LIGHT_FILLS = { BLUE: "#2196F3", ORANGE: "#FF9800", GREEN: "#4CAF50", CYAN: "#00BCD4", YELLOW: "#FFEB3B", VIOLET: "#7E57C2", GRAY: "#9E9E9E", MAGENTA: "#F06292", BROWN: "#795548", RED: "#F44336" }; var MATERIAL_LIGHT_STROKES = { BLUE: "#1565C0", ORANGE: "#E65100", GREEN: "#2E7D32", CYAN: "#00838F", YELLOW: "#F9A825", VIOLET: "#4527A0", GRAY: "#616161", MAGENTA: "#C2185B", BROWN: "#4E342E", RED: "#B71C1C" }; var MaterialLight = class extends ChartTheme { getDefaultColors() { return { ...super.getDefaultColors(), fills: MATERIAL_LIGHT_FILLS, fillsFallback: Object.values(MATERIAL_LIGHT_FILLS), strokes: MATERIAL_LIGHT_STROKES, sequentialColors: getSequentialColors(MATERIAL_LIGHT_FILLS), divergingColors: [MATERIAL_LIGHT_FILLS.ORANGE, MATERIAL_LIGHT_FILLS.YELLOW, MATERIAL_LIGHT_FILLS.GREEN], // hierarchyColors: [], secondSequentialColors: [ "#2196f3", // 500 "#329EF4", // (interpolated) "#42a5f5", // 400 "#53ADF6", // (interpolated) "#64b5f6", // 300 "#7AC0F8", // (interpolated) "#90caf9" // 200 ], secondDivergingColors: [MATERIAL_LIGHT_FILLS.GREEN, MATERIAL_LIGHT_FILLS.YELLOW, MATERIAL_LIGHT_FILLS.RED], // secondHierarchyColors: [], up: { fill: MATERIAL_LIGHT_FILLS.GREEN, stroke: MATERIAL_LIGHT_STROKES.GREEN }, down: { fill: MATERIAL_LIGHT_FILLS.RED, stroke: MATERIAL_LIGHT_STROKES.RED }, neutral: { fill: MATERIAL_LIGHT_FILLS.GRAY, stroke: MATERIAL_LIGHT_STROKES.GRAY }, altUp: { fill: MATERIAL_LIGHT_FILLS.BLUE, stroke: MATERIAL_LIGHT_STROKES.BLUE }, altDown: { fill: MATERIAL_LIGHT_FILLS.RED, stroke: MATERIAL_LIGHT_STROKES.RED }, altNeutral: { fill: MATERIAL_LIGHT_FILLS.GRAY, stroke: MATERIAL_LIGHT_STROKES.GRAY } }; } getTemplateParameters() { const params = super.getTemplateParameters(); params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR, MATERIAL_LIGHT_FILLS.BLUE); params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL, MATERIAL_LIGHT_FILLS.BLUE); return params; } }; // packages/ag-charts-community/src/chart/themes/polychromaDark.ts var POLYCHROMA_DARK_FILLS = { BLUE: "#436ff4", PURPLE: "#9a7bff", MAGENTA: "#d165d2", PINK: "#f0598b", RED: "#f47348", ORANGE: "#f2a602", YELLOW: "#e9e201", GREEN: "#21b448", CYAN: "#00b9a2", MODERATE_BLUE: "#00aee4", GRAY: "#bbbbbb" }; var POLYCHROMA_DARK_STROKES = { BLUE: "#6698ff", PURPLE: "#c0a3ff", MAGENTA: "#fc8dfc", PINK: "#ff82b1", RED: "#ff9b70", ORANGE: "#ffcf4e", YELLOW: "#ffff58", GREEN: "#58dd70", CYAN: "#51e2c9", MODERATE_BLUE: "#4fd7ff", GRAY: "#eeeeee" }; var PolychromaDark = class extends DarkTheme { getDefaultColors() { return { fills: POLYCHROMA_DARK_FILLS, fillsFallback: Object.values(POLYCHROMA_DARK_FILLS), strokes: POLYCHROMA_DARK_STROKES, sequentialColors: getSequentialColors(POLYCHROMA_DARK_FILLS), divergingColors: [POLYCHROMA_DARK_FILLS.BLUE, POLYCHROMA_DARK_FILLS.RED], hierarchyColors: [], secondSequentialColors: [ POLYCHROMA_DARK_FILLS.BLUE, POLYCHROMA_DARK_FILLS.PURPLE, POLYCHROMA_DARK_FILLS.MAGENTA, POLYCHROMA_DARK_FILLS.PINK, POLYCHROMA_DARK_FILLS.RED, POLYCHROMA_DARK_FILLS.ORANGE, POLYCHROMA_DARK_FILLS.YELLOW, POLYCHROMA_DARK_FILLS.GREEN ], secondDivergingColors: [POLYCHROMA_DARK_FILLS.BLUE, POLYCHROMA_DARK_FILLS.RED], secondHierarchyColors: [], up: { fill: POLYCHROMA_DARK_FILLS.GREEN, stroke: POLYCHROMA_DARK_STROKES.GREEN }, down: { fill: POLYCHROMA_DARK_FILLS.RED, stroke: POLYCHROMA_DARK_STROKES.RED }, neutral: { fill: POLYCHROMA_DARK_FILLS.GRAY, stroke: POLYCHROMA_DARK_STROKES.GRAY }, altUp: { fill: POLYCHROMA_DARK_FILLS.BLUE, stroke: POLYCHROMA_DARK_STROKES.BLUE }, altDown: { fill: POLYCHROMA_DARK_FILLS.RED, stroke: POLYCHROMA_DARK_STROKES.RED }, altNeutral: { fill: POLYCHROMA_DARK_FILLS.GRAY, stroke: POLYCHROMA_DARK_STROKES.GRAY } }; } getTemplateParameters() { const params = super.getTemplateParameters(); params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR, POLYCHROMA_DARK_FILLS.BLUE); params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL, POLYCHROMA_DARK_FILLS.BLUE); return params; } }; // packages/ag-charts-community/src/chart/themes/polychromaLight.ts var POLYCHROMA_LIGHT_FILLS = { BLUE: "#436ff4", PURPLE: "#9a7bff", MAGENTA: "#d165d2", PINK: "#f0598b", RED: "#f47348", ORANGE: "#f2a602", YELLOW: "#e9e201", GREEN: "#21b448", CYAN: "#00b9a2", MODERATE_BLUE: "#00aee4", GRAY: "#bbbbbb" }; var POLYCHROMA_LIGHT_STROKES = { BLUE: "#2346c9", PURPLE: "#7653d4", MAGENTA: "#a73da9", PINK: "#c32d66", RED: "#c84b1c", ORANGE: "#c87f00", YELLOW: "#c1b900", GREEN: "#008c1c", CYAN: "#00927c", MODERATE_BLUE: "#0087bb", GRAY: "#888888" }; var PolychromaLight = class extends ChartTheme { getDefaultColors() { return { ...super.getDefaultColors(), fills: POLYCHROMA_LIGHT_FILLS, fillsFallback: Object.values(POLYCHROMA_LIGHT_FILLS), strokes: POLYCHROMA_LIGHT_STROKES, sequentialColors: getSequentialColors(POLYCHROMA_LIGHT_FILLS), divergingColors: [POLYCHROMA_LIGHT_FILLS.BLUE, POLYCHROMA_LIGHT_FILLS.RED], hierarchyColors: [], secondSequentialColors: [ POLYCHROMA_LIGHT_FILLS.BLUE, POLYCHROMA_LIGHT_FILLS.PURPLE, POLYCHROMA_LIGHT_FILLS.MAGENTA, POLYCHROMA_LIGHT_FILLS.PINK, POLYCHROMA_LIGHT_FILLS.RED, POLYCHROMA_LIGHT_FILLS.ORANGE, POLYCHROMA_LIGHT_FILLS.YELLOW, POLYCHROMA_LIGHT_FILLS.GREEN ], secondDivergingColors: [POLYCHROMA_LIGHT_FILLS.BLUE, POLYCHROMA_LIGHT_FILLS.RED], secondHierarchyColors: [], up: { fill: POLYCHROMA_LIGHT_FILLS.GREEN, stroke: POLYCHROMA_LIGHT_STROKES.GREEN }, down: { fill: POLYCHROMA_LIGHT_FILLS.RED, stroke: POLYCHROMA_LIGHT_STROKES.RED }, neutral: { fill: POLYCHROMA_LIGHT_FILLS.GRAY, stroke: POLYCHROMA_LIGHT_STROKES.GRAY }, altUp: { fill: POLYCHROMA_LIGHT_FILLS.BLUE, stroke: POLYCHROMA_LIGHT_STROKES.BLUE }, altDown: { fill: POLYCHROMA_LIGHT_FILLS.RED, stroke: POLYCHROMA_LIGHT_STROKES.RED }, altNeutral: { fill: POLYCHROMA_LIGHT_FILLS.GRAY, stroke: POLYCHROMA_LIGHT_STROKES.GRAY } }; } getTemplateParameters() { const params = super.getTemplateParameters(); params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR, POLYCHROMA_LIGHT_FILLS.BLUE); params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL, POLYCHROMA_LIGHT_FILLS.BLUE); return params; } }; // packages/ag-charts-community/src/chart/themes/sheetsDark.ts var SHEETS_DARK_FILLS = { BLUE: "#4472C4", ORANGE: "#ED7D31", GRAY: "#A5A5A5", YELLOW: "#FFC000", MODERATE_BLUE: "#5B9BD5", GREEN: "#70AD47", DARK_GRAY: "#7B7B7B", DARK_BLUE: "#264478", VERY_DARK_GRAY: "#636363", DARK_YELLOW: "#997300" }; var SHEETS_DARK_STROKES = { BLUE: "#6899ee", ORANGE: "#ffa55d", GRAY: "#cdcdcd", YELLOW: "#ffea53", MODERATE_BLUE: "#82c3ff", GREEN: "#96d56f", DARK_GRAY: "#a1a1a1", DARK_BLUE: "#47689f", VERY_DARK_GRAY: "#878787", DARK_YELLOW: "#c0993d" }; var SheetsDark = class extends DarkTheme { getDefaultColors() { return { ...super.getDefaultColors(), fills: { ...SHEETS_DARK_FILLS, RED: SHEETS_DARK_FILLS.ORANGE }, fillsFallback: Object.values({ ...SHEETS_DARK_FILLS, RED: SHEETS_DARK_FILLS.ORANGE }), strokes: { ...SHEETS_DARK_STROKES, RED: SHEETS_DARK_STROKES.ORANGE }, sequentialColors: getSequentialColors({ ...SHEETS_DARK_FILLS, RED: SHEETS_DARK_FILLS.ORANGE }), divergingColors: [SHEETS_DARK_FILLS.ORANGE, SHEETS_DARK_FILLS.YELLOW, SHEETS_DARK_FILLS.GREEN], // hierarchyColors: [], secondSequentialColors: [ "#5090dc", "#4882c6", "#4073b0", "#38659a", "#305684", "#28486e", "#203a58", "#182b42" ], secondDivergingColors: [SHEETS_DARK_FILLS.GREEN, SHEETS_DARK_FILLS.YELLOW, SHEETS_DARK_FILLS.ORANGE], // secondHierarchyColors: [], up: { fill: SHEETS_DARK_FILLS.GREEN, stroke: SHEETS_DARK_STROKES.GREEN }, down: { fill: SHEETS_DARK_FILLS.ORANGE, stroke: SHEETS_DARK_STROKES.ORANGE }, neutral: { fill: SHEETS_DARK_FILLS.GRAY, stroke: SHEETS_DARK_STROKES.GRAY }, altUp: { fill: SHEETS_DARK_FILLS.BLUE, stroke: SHEETS_DARK_STROKES.BLUE }, altDown: { fill: SHEETS_DARK_FILLS.ORANGE, stroke: SHEETS_DARK_STROKES.ORANGE }, altNeutral: { fill: SHEETS_DARK_FILLS.GRAY, stroke: SHEETS_DARK_STROKES.GRAY } }; } getTemplateParameters() { const params = super.getTemplateParameters(); params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR, SHEETS_DARK_FILLS.BLUE); params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL, SHEETS_DARK_FILLS.BLUE); return params; } }; // packages/ag-charts-community/src/chart/themes/sheetsLight.ts var SHEETS_LIGHT_FILLS = { BLUE: "#5281d5", ORANGE: "#ff8d44", GRAY: "#b5b5b5", YELLOW: "#ffd02f", MODERATE_BLUE: "#6aabe6", GREEN: "#7fbd57", DARK_GRAY: "#8a8a8a", DARK_BLUE: "#335287", VERY_DARK_GRAY: "#717171", DARK_YELLOW: "#a98220" }; var SHEETS_LIGHT_STROKES = { BLUE: "#214d9b", ORANGE: "#c25600", GRAY: "#7f7f7f", YELLOW: "#d59800", MODERATE_BLUE: "#3575ac", GREEN: "#4b861a", DARK_GRAY: "#575757", DARK_BLUE: "#062253", VERY_DARK_GRAY: "#414141", DARK_YELLOW: "#734f00" }; var SheetsLight = class extends ChartTheme { getDefaultColors() { return { ...super.getDefaultColors(), fills: { ...SHEETS_LIGHT_FILLS, RED: SHEETS_LIGHT_FILLS.ORANGE }, fillsFallback: Object.values({ ...SHEETS_LIGHT_FILLS, RED: SHEETS_LIGHT_FILLS.ORANGE }), strokes: { ...SHEETS_LIGHT_STROKES, RED: SHEETS_LIGHT_STROKES.ORANGE }, sequentialColors: getSequentialColors({ ...SHEETS_LIGHT_FILLS, RED: SHEETS_LIGHT_FILLS.ORANGE }), divergingColors: [SHEETS_LIGHT_FILLS.ORANGE, SHEETS_LIGHT_FILLS.YELLOW, SHEETS_LIGHT_FILLS.GREEN], // hierarchyColors: [], secondSequentialColors: [ "#5090dc", "#629be0", "#73a6e3", "#85b1e7", "#96bcea", "#a8c8ee", "#b9d3f1", "#cbdef5" ], secondDivergingColors: [SHEETS_LIGHT_FILLS.GREEN, SHEETS_LIGHT_FILLS.YELLOW, SHEETS_LIGHT_FILLS.ORANGE], secondHierarchyColors: [], up: { fill: SHEETS_LIGHT_FILLS.GREEN, stroke: SHEETS_LIGHT_STROKES.GREEN }, down: { fill: SHEETS_LIGHT_FILLS.ORANGE, stroke: SHEETS_LIGHT_STROKES.ORANGE }, neutral: { fill: SHEETS_LIGHT_STROKES.GRAY, stroke: SHEETS_LIGHT_STROKES.GRAY }, altUp: { fill: SHEETS_LIGHT_FILLS.BLUE, stroke: SHEETS_LIGHT_STROKES.BLUE }, altDown: { fill: SHEETS_LIGHT_FILLS.ORANGE, stroke: SHEETS_LIGHT_STROKES.ORANGE }, altNeutral: { fill: SHEETS_LIGHT_FILLS.GRAY, stroke: SHEETS_LIGHT_STROKES.GRAY } }; } getTemplateParameters() { const params = super.getTemplateParameters(); params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR, SHEETS_LIGHT_FILLS.BLUE); params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL, SHEETS_LIGHT_FILLS.BLUE); return params; } }; // packages/ag-charts-community/src/chart/axesOptionsEnterpriseDefs.ts var ordinalTimeAxisOptionsDefs = { ...cartesianAxisOptionsDefs, type: constant("ordinal-time"), paddingInner: ratio, paddingOuter: ratio, groupPaddingInner: ratio, label: cartesianTimeAxisLabel, parentLevel: cartesianTimeAxisParentLevel, interval: discreteTimeAxisIntervalOptionsDefs, crosshair: cartesianAxisCrosshairOptions(true, true), bandHighlight: cartesianAxisBandHighlightOptions, bandAlignment: union("justify", "start", "center", "end") }; var angleNumberAxisOptionsDefs = { ...commonAxisOptionsDefs, ...continuousAxisOptions(number), type: constant("angle-number"), crossLines: arrayOfDefs(commonCrossLineOptionsDefs), startAngle: number, endAngle: number, label: { ...commonAxisLabelOptionsDefs, orientation: union("fixed", "parallel", "perpendicular"), format: numberFormatValidator } }; var angleCategoryAxisOptionsDefs = { ...commonAxisOptionsDefs, type: constant("angle-category"), shape: union("polygon", "circle"), crossLines: arrayOfDefs(commonCrossLineOptionsDefs), startAngle: number, endAngle: number, paddingInner: ratio, groupPaddingInner: ratio, label: { ...commonAxisLabelOptionsDefs, orientation: union("fixed", "parallel", "perpendicular") } }; angleCategoryAxisOptionsDefs.innerRadiusRatio = ratio; var radiusNumberAxisOptionsDefs = { ...commonAxisOptionsDefs, ...continuousAxisOptions(number), type: constant("radius-number"), shape: union("polygon", "circle"), positionAngle: number, innerRadiusRatio: ratio, crossLines: arrayOfDefs( { ...commonCrossLineOptionsDefs, label: { ...commonCrossLineLabelOptionsDefs, positionAngle: number } }, "cross-line options" ), title: { enabled: boolean, text: textOrSegments, spacing: positiveNumber, formatter: callbackOf(textOrSegments), ...fontOptionsDef }, label: { ...commonAxisLabelOptionsDefs, format: numberFormatValidator } }; var radiusCategoryAxisOptionsDefs = { ...commonAxisOptionsDefs, type: constant("radius-category"), positionAngle: number, innerRadiusRatio: ratio, paddingInner: ratio, paddingOuter: ratio, groupPaddingInner: ratio, label: commonAxisLabelOptionsDefs, crossLines: arrayOfDefs( { ...commonCrossLineOptionsDefs, label: { ...commonCrossLineLabelOptionsDefs, positionAngle: number } }, "cross-line options" ), title: { enabled: boolean, text: textOrSegments, spacing: positiveNumber, formatter: callbackOf(textOrSegments), ...fontOptionsDef } }; // packages/ag-charts-community/src/chart/themes/annotationOptionsDef.ts var annotationLineOptionsDef = { lineStyle: union("solid", "dashed", "dotted"), ...lineDashOptionsDef }; var annotationHandleStylesDefs = { ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }; var annotationTextStylesDef = { visible: boolean, locked: boolean, readOnly: boolean, handle: annotationHandleStylesDefs, ...fontOptionsDef }; var annotationLineTextDefs = { position: union("top", "center", "bottom"), alignment: union("left", "center", "right"), ...fontOptionsDef }; var annotationChannelTextDefs = { position: union("top", "inside", "bottom"), alignment: union("left", "center", "right"), ...fontOptionsDef }; var annotationAxisLabelOptionsDef = { enabled: boolean, cornerRadius: positiveNumber, ...fontOptionsDef, ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }; var annotationChannelMiddleDefs = { visible: boolean, ...annotationLineOptionsDef, ...strokeOptionsDef }; var annotationMeasurerStatisticsOptionsDefs = { divider: strokeOptionsDef, ...fillOptionsDef, ...strokeOptionsDef, ...fontOptionsDef }; var annotationQuickMeasurerDirectionStylesDefs = { handle: annotationHandleStylesDefs, statistics: annotationMeasurerStatisticsOptionsDefs, ...annotationLineOptionsDef, ...fillOptionsDef, ...strokeOptionsDef }; var annotationLineStyleDefs = { visible: boolean, locked: boolean, readOnly: boolean, extendStart: boolean, extendEnd: boolean, handle: annotationHandleStylesDefs, text: annotationLineTextDefs, ...annotationLineOptionsDef, ...strokeOptionsDef }; var annotationCrossLineStyleDefs = { visible: boolean, locked: boolean, readOnly: boolean, axisLabel: annotationAxisLabelOptionsDef, handle: annotationHandleStylesDefs, text: annotationLineTextDefs, ...annotationLineOptionsDef, ...strokeOptionsDef }; var annotationChannelStyleDefs = { visible: boolean, locked: boolean, readOnly: boolean, extendStart: boolean, extendEnd: boolean, handle: annotationHandleStylesDefs, text: annotationChannelTextDefs, background: fillOptionsDef, ...annotationLineOptionsDef, ...strokeOptionsDef }; var annotationDisjointChannelStyleDefs = { ...annotationChannelStyleDefs }; var annotationParallelChannelStyleDefs = { ...annotationChannelStyleDefs, middle: annotationChannelMiddleDefs }; var annotationFibonacciStylesDefs = { label: fontOptionsDef, showFill: boolean, isMultiColor: boolean, strokes: arrayOf(color), rangeStroke: color, bands: union(4, 6, 10), ...annotationLineStyleDefs }; var annotationCalloutStylesDefs = { ...fillOptionsDef, ...strokeOptionsDef, ...annotationTextStylesDef }; var annotationCommentStylesDefs = { ...fillOptionsDef, ...strokeOptionsDef, ...annotationTextStylesDef }; var annotationNoteStylesDefs = { ...fillOptionsDef, ...strokeOptionsDef, ...annotationTextStylesDef, background: { ...fillOptionsDef, ...strokeOptionsDef } }; var annotationShapeStylesDefs = { visible: boolean, locked: boolean, readOnly: boolean, handle: annotationHandleStylesDefs, ...fillOptionsDef }; var annotationMeasurerStylesDefs = { visible: boolean, locked: boolean, readOnly: boolean, extendStart: boolean, extendEnd: boolean, handle: annotationHandleStylesDefs, text: annotationLineTextDefs, background: fillOptionsDef, statistics: annotationMeasurerStatisticsOptionsDefs, ...annotationLineOptionsDef, ...strokeOptionsDef }; var annotationQuickMeasurerStylesDefs = { visible: boolean, up: annotationQuickMeasurerDirectionStylesDefs, down: annotationQuickMeasurerDirectionStylesDefs }; var annotationOptionsDef = { enabled: boolean, axesButtons: { enabled: boolean, axes: union("x", "y", "xy") }, toolbar: { enabled: boolean, padding: positiveNumber, buttons: arrayOfDefs( { ...toolbarButtonOptionsDefs, value: union( "line-menu", "fibonacci-menu", "text-menu", "shape-menu", "measurer-menu", "line", "horizontal-line", "vertical-line", "parallel-channel", "disjoint-channel", "fibonacci-retracement", "fibonacci-retracement-trend-based", "text", "comment", "callout", "note", "clear" ) }, "annotation toolbar buttons array" ) }, optionsToolbar: { enabled: boolean, buttons: arrayOf( or( optionsDefs({ ...toolbarButtonOptionsDefs, value: required( union( "line-stroke-width", "line-style-type", "line-color", "fill-color", "text-color", "text-size", "delete", "settings" ) ) }), optionsDefs({ ...toolbarButtonOptionsDefs, value: required(union("lock")), checkedOverrides: toolbarButtonOptionsDefs }) ) ) } }; annotationOptionsDef.data = undocumented(array); annotationOptionsDef.xKey = undocumented(string); annotationOptionsDef.volumeKey = undocumented(string); annotationOptionsDef.snap = undocumented(boolean); // packages/ag-charts-community/src/chart/themes/enterpriseThemeableOptionsDef.ts var hierarchyHighlightStyleOptionsDef = { ...fillOptionsDef, ...strokeOptionsDef, opacity: ratio }; var boxPlotStyleOptionsDef = { ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef, cornerRadius: positiveNumber, whisker: { ...strokeOptionsDef, ...lineDashOptionsDef }, cap: { lengthRatio: ratio } }; var boxPlotHighlightStyleOptionsDef = { ...boxPlotStyleOptionsDef, opacity: ratio }; var boxPlotStyler = callbackDefs({ ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef, cornerRadius: positiveNumber, whisker: { ...strokeOptionsDef, ...lineDashOptionsDef }, cap: { lengthRatio: ratio } }); var boxPlotSeriesThemeableOptionsDef = { direction: union("horizontal", "vertical"), showInMiniChart: boolean, styler: boxPlotStyler, itemStyler: boxPlotStyler, tooltip: tooltipOptionsDefs, ...commonSeriesThemeableOptionsDefs, ...boxPlotStyleOptionsDef, highlight: multiSeriesHighlightOptionsDef(boxPlotHighlightStyleOptionsDef, boxPlotHighlightStyleOptionsDef), segmentation: shapeSegmentation, width: positiveNumberNonZero, widthRatio: ratio }; var candlestickSeriesItemOptionsDef = { cornerRadius: positiveNumber, wick: { ...strokeOptionsDef, ...lineDashOptionsDef }, ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }; var candlestickHighlightStyleOptionsDef = { ...candlestickSeriesItemOptionsDef, opacity: ratio }; var candlestickSeriesThemeableOptionsDef = { item: { up: candlestickSeriesItemOptionsDef, down: candlestickSeriesItemOptionsDef }, itemStyler: callbackDefs({ ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef, cornerRadius: positiveNumber, wick: { ...strokeOptionsDef, ...lineDashOptionsDef } }), showInMiniChart: boolean, tooltip: tooltipOptionsDefs, ...commonSeriesThemeableOptionsDefs, highlight: multiSeriesHighlightOptionsDef(candlestickHighlightStyleOptionsDef, candlestickHighlightStyleOptionsDef) }; var chordSeriesThemeableOptionsDef = { fills: arrayOf(colorUnion), strokes: arrayOf(color), label: { spacing: positiveNumber, maxWidth: positiveNumber, ...seriesLabelOptionsDefs }, link: { tension: ratio, itemStyler: callbackDefs({ ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef, tension: ratio }), ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }, node: { width: positiveNumber, spacing: positiveNumber, itemStyler: callbackDefs({ ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }), ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }, tooltip: tooltipOptionsDefs, ...commonSeriesThemeableOptionsDefs }; var coneFunnelSeriesThemeableOptionsDef = { direction: union("horizontal", "vertical"), fills: arrayOf(colorUnion), strokes: arrayOf(color), label: { spacing: positiveNumber, placement: union("before", "middle", "after"), ...seriesLabelOptionsDefs }, stageLabel: { placement: union("before", "after"), format: numberFormatValidator, ...commonAxisLabelOptionsDefs }, tooltip: tooltipOptionsDefs, ...without(commonSeriesThemeableOptionsDefs, ["showInLegend"]), ...without(fillOptionsDef, ["fill"]), ...without(strokeOptionsDef, ["stroke"]), ...lineDashOptionsDef, highlight: highlightOptionsDef(lineHighlightOptionsDef) }; var funnelSeriesThemeableOptionsDef = { direction: union("horizontal", "vertical"), fills: arrayOf(colorUnion), strokes: arrayOf(color), itemStyler: callbackDefs({ ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }), spacingRatio: ratio, crisp: boolean, dropOff: { enabled: boolean, ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }, stageLabel: { placement: union("before", "after"), format: numberFormatValidator, ...commonAxisLabelOptionsDefs }, label: seriesLabelOptionsDefs, tooltip: tooltipOptionsDefs, shadow: shadowOptionsDefs, ...without(commonSeriesThemeableOptionsDefs, ["showInLegend"]), ...without(fillOptionsDef, ["fill"]), ...without(strokeOptionsDef, ["stroke"]), ...lineDashOptionsDef }; var heatmapSeriesThemeableOptionsDef = { title: string, textAlign: union("left", "center", "right"), verticalAlign: union("top", "middle", "bottom"), itemPadding: positiveNumber, itemStyler: callbackDefs({ ...fillOptionsDef, ...strokeOptionsDef }), showInMiniChart: boolean, label: autoSizedLabelOptionsDefs, tooltip: tooltipOptionsDefs, ...commonSeriesThemeableOptionsDefs, ...strokeOptionsDef }; var ohlcSeriesThemeableOptionsDef = { showInMiniChart: boolean, itemStyler: callbackDefs({ ...strokeOptionsDef, ...lineDashOptionsDef }), item: { up: { ...strokeOptionsDef, ...lineDashOptionsDef }, down: { ...strokeOptionsDef, ...lineDashOptionsDef } }, tooltip: tooltipOptionsDefs, ...commonSeriesThemeableOptionsDefs, highlight: multiSeriesHighlightOptionsDef(lineHighlightOptionsDef, lineHighlightOptionsDef) }; var mapLineSeriesThemeableOptionsDef = { maxStrokeWidth: positiveNumber, itemStyler: callbackDefs({ ...strokeOptionsDef, ...lineDashOptionsDef }), sizeDomain: arrayOf(positiveNumber), label: seriesLabelOptionsDefs, tooltip: tooltipOptionsDefs, ...commonSeriesThemeableOptionsDefs, ...strokeOptionsDef, ...lineDashOptionsDef, highlight: multiSeriesHighlightOptionsDef(lineHighlightOptionsDef, lineHighlightOptionsDef) }; var mapLineBackgroundSeriesThemeableOptionsDef = { ...strokeOptionsDef, ...lineDashOptionsDef }; var mapMarkerSeriesThemeableOptionsDef = { colorRange: arrayOf(color), maxSize: positiveNumber, sizeDomain: arrayOf(positiveNumber), label: { placement: union("top", "bottom", "left", "right"), ...seriesLabelOptionsDefs }, tooltip: tooltipOptionsDefs, ...commonSeriesThemeableOptionsDefs, ...without(markerOptionsDefs, ["enabled"]), highlight: multiSeriesHighlightOptionsDef(shapeHighlightOptionsDef, shapeHighlightOptionsDef) }; var mapShapeSeriesThemeableOptionsDef = { colorRange: arrayOf(color), padding: positiveNumber, itemStyler: callbackDefs({ ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }), label: autoSizedLabelOptionsDefs, tooltip: tooltipOptionsDefs, ...commonSeriesThemeableOptionsDefs, ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef, highlight: multiSeriesHighlightOptionsDef(shapeHighlightOptionsDef, shapeHighlightOptionsDef) }; var mapShapeBackgroundSeriesThemeableOptionsDef = { ...commonSeriesThemeableOptionsDefs, ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }; var radialSeriesStylerDef = callbackDefs({ ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef, cornerRadius: positiveNumber }); var nightingaleSeriesThemeableOptionsDef = { cornerRadius: positiveNumber, styler: radialSeriesStylerDef, itemStyler: radialSeriesStylerDef, label: seriesLabelOptionsDefs, tooltip: tooltipOptionsDefs, ...commonSeriesThemeableOptionsDefs, ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef, highlight: multiSeriesHighlightOptionsDef(barHighlightOptionsDef, barHighlightOptionsDef) }; var pyramidSeriesThemeableOptionsDef = { direction: union("horizontal", "vertical"), aspectRatio: positiveNumber, spacing: positiveNumber, reverse: boolean, itemStyler: callbackDefs({ ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }), fills: arrayOf(colorUnion), strokes: arrayOf(color), label: seriesLabelOptionsDefs, stageLabel: { spacing: positiveNumber, placement: union("before", "after"), ...seriesLabelOptionsDefs }, tooltip: tooltipOptionsDefs, shadow: shadowOptionsDefs, ...commonSeriesThemeableOptionsDefs, ...without(fillOptionsDef, ["fill"]), ...without(strokeOptionsDef, ["stroke"]), ...lineDashOptionsDef }; var radarAreaSeriesThemeableOptionsDef = { connectMissingData: boolean, marker: markerOptionsDefs, styler: callbackDefs({ marker: markerStyleOptionsDefs, ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }), label: seriesLabelOptionsDefs, tooltip: tooltipOptionsDefs, ...commonSeriesThemeableOptionsDefs, ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef, highlight: multiSeriesHighlightOptionsDef(shapeHighlightOptionsDef, shapeHighlightOptionsDef) }; var radarLineSeriesThemeableOptionsDef = { connectMissingData: boolean, marker: markerOptionsDefs, styler: callbackDefs({ marker: markerStyleOptionsDefs, ...strokeOptionsDef, ...lineDashOptionsDef }), label: seriesLabelOptionsDefs, tooltip: tooltipOptionsDefs, ...commonSeriesThemeableOptionsDefs, ...strokeOptionsDef, ...lineDashOptionsDef, highlight: multiSeriesHighlightOptionsDef(shapeHighlightOptionsDef, lineHighlightOptionsDef) }; var radialBarSeriesThemeableOptionsDef = { cornerRadius: positiveNumber, styler: radialSeriesStylerDef, itemStyler: radialSeriesStylerDef, label: seriesLabelOptionsDefs, tooltip: tooltipOptionsDefs, ...commonSeriesThemeableOptionsDefs, ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef, highlight: multiSeriesHighlightOptionsDef(barHighlightOptionsDef, barHighlightOptionsDef) }; var radialColumnSeriesThemeableOptionsDef = { cornerRadius: positiveNumber, columnWidthRatio: ratio, maxColumnWidthRatio: ratio, styler: radialSeriesStylerDef, itemStyler: radialSeriesStylerDef, label: seriesLabelOptionsDefs, tooltip: tooltipOptionsDefs, ...commonSeriesThemeableOptionsDefs, ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef, highlight: multiSeriesHighlightOptionsDef(barHighlightOptionsDef, barHighlightOptionsDef) }; var rangeAreaSeriesLineThemeableOptionsDef = { marker: markerOptionsDefs, ...strokeOptionsDef, ...lineDashOptionsDef }; var rangeAreaSeriesItemLineThemeableOptionsDef = { marker: { enabled: boolean, ...markerStyleOptionsDefs }, ...strokeOptionsDef, ...lineDashOptionsDef }; var rangeAreaSeriesLineStyleDef = { marker: markerStyleOptionsDefs, ...strokeOptionsDef, ...lineDashOptionsDef }; var rangeAreaSeriesThemeableOptionsDef = { showInMiniChart: boolean, connectMissingData: boolean, interpolation: interpolationOptionsDefs, label: { ...seriesLabelOptionsDefs, placement: union("inside", "outside"), spacing: positiveNumber }, tooltip: tooltipOptionsDefs, shadow: shadowOptionsDefs, ...commonSeriesThemeableOptionsDefs, ...fillOptionsDef, ...rangeAreaSeriesLineThemeableOptionsDef, item: { low: { ...rangeAreaSeriesItemLineThemeableOptionsDef }, high: { ...rangeAreaSeriesItemLineThemeableOptionsDef } }, styler: callbackDefs({ ...fillOptionsDef, item: { low: { ...rangeAreaSeriesLineStyleDef }, high: { ...rangeAreaSeriesLineStyleDef } } }), highlight: multiSeriesHighlightOptionsDef(shapeHighlightOptionsDef, shapeHighlightOptionsDef), segmentation: shapeSegmentation, invertedStyle: { enabled: boolean, ...fillOptionsDef } }; var rangeBarStyleCallback = callbackDefs({ ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef, cornerRadius: positiveNumber }); var rangeBarSeriesThemeableOptionsDef = { direction: union("horizontal", "vertical"), grouped: boolean, showInMiniChart: boolean, cornerRadius: positiveNumber, styler: rangeBarStyleCallback, itemStyler: rangeBarStyleCallback, label: { ...seriesLabelOptionsDefs, placement: union("inside", "outside"), spacing: positiveNumber }, tooltip: tooltipOptionsDefs, shadow: shadowOptionsDefs, ...commonSeriesThemeableOptionsDefs, ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef, highlight: multiSeriesHighlightOptionsDef(barHighlightOptionsDef, barHighlightOptionsDef), segmentation: shapeSegmentation, width: positiveNumberNonZero, widthRatio: ratio }; var sankeySeriesThemeableOptionsDef = { fills: arrayOf(colorUnion), strokes: arrayOf(color), label: { ...seriesLabelOptionsDefs, spacing: positiveNumber, placement: union("left", "right", "center"), edgePlacement: union("inside", "outside") }, link: { itemStyler: callbackDefs({ ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }), ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }, node: { width: positiveNumber, spacing: positiveNumber, minSpacing: and(positiveNumber, lessThanOrEqual("spacing")), alignment: union("left", "center", "right", "justify"), verticalAlignment: union("top", "bottom", "center"), sort: union("data", "ascending", "descending", "auto"), itemStyler: callbackDefs({ ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }), ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }, tooltip: tooltipOptionsDefs, ...commonSeriesThemeableOptionsDefs }; var sunburstSeriesThemeableOptionsDef = { fills: arrayOf(colorUnion), strokes: arrayOf(color), colorRange: arrayOf(color), sectorSpacing: positiveNumber, cornerRadius: positiveNumber, padding: positiveNumber, itemStyler: callbackDefs({ ...fillOptionsDef, ...strokeOptionsDef }), label: { spacing: positiveNumber, ...autoSizedLabelOptionsDefs }, secondaryLabel: autoSizedLabelOptionsDefs, tooltip: tooltipOptionsDefs, ...without(commonSeriesThemeableOptionsDefs, ["highlight", "showInLegend"]), ...without(fillOptionsDef, ["fill"]), ...without(strokeOptionsDef, ["stroke"]), highlight: { highlightedItem: hierarchyHighlightStyleOptionsDef, highlightedBranch: hierarchyHighlightStyleOptionsDef, unhighlightedItem: hierarchyHighlightStyleOptionsDef, unhighlightedBranch: hierarchyHighlightStyleOptionsDef } }; var treemapSeriesThemeableOptionsDef = { fills: arrayOf(colorUnion), strokes: arrayOf(color), colorRange: arrayOf(color), itemStyler: callbackDefs({ ...fillOptionsDef, ...strokeOptionsDef }), group: { gap: positiveNumber, padding: positiveNumber, cornerRadius: positiveNumber, textAlign: union("left", "center", "right"), interactive: boolean, highlight: { highlightedItem: hierarchyHighlightStyleOptionsDef, unhighlightedItem: hierarchyHighlightStyleOptionsDef }, label: { ...seriesLabelOptionsDefs, spacing: positiveNumber }, ...fillOptionsDef, ...strokeOptionsDef }, tile: { gap: positiveNumber, padding: positiveNumber, cornerRadius: positiveNumber, textAlign: union("left", "center", "right"), verticalAlign: union("top", "middle", "bottom"), label: { ...seriesLabelOptionsDefs, spacing: positiveNumber, lineHeight: positiveNumber, minimumFontSize: positiveNumber, wrapping: union("never", "always", "hyphenate", "on-space"), overflowStrategy: union("ellipsis", "hide") }, secondaryLabel: { ...seriesLabelOptionsDefs, lineHeight: positiveNumber, minimumFontSize: positiveNumber, wrapping: union("never", "always", "hyphenate", "on-space"), overflowStrategy: union("ellipsis", "hide") }, highlight: { highlightedItem: hierarchyHighlightStyleOptionsDef, highlightedBranch: hierarchyHighlightStyleOptionsDef, unhighlightedItem: hierarchyHighlightStyleOptionsDef, unhighlightedBranch: hierarchyHighlightStyleOptionsDef }, ...fillOptionsDef, ...strokeOptionsDef }, tooltip: tooltipOptionsDefs, ...without(commonSeriesThemeableOptionsDefs, ["highlight", "showInLegend"]) }; var waterfallSeriesItemOptionsDef = { name: string, cornerRadius: positiveNumber, itemStyler: callbackDefs({ ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef, cornerRadius: positiveNumber }), label: { ...seriesLabelOptionsDefs, placement: union("inside-start", "inside-center", "inside-end", "outside-start", "outside-end"), spacing: positiveNumber }, tooltip: tooltipOptionsDefs, shadow: shadowOptionsDefs, ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef }; var waterfallSeriesThemeableOptionsDef = { direction: union("horizontal", "vertical"), showInMiniChart: boolean, item: { positive: waterfallSeriesItemOptionsDef, negative: waterfallSeriesItemOptionsDef, total: waterfallSeriesItemOptionsDef }, line: { enabled: boolean, ...strokeOptionsDef, ...lineDashOptionsDef }, tooltip: tooltipOptionsDefs, width: positiveNumberNonZero, widthRatio: ratio, ...commonSeriesThemeableOptionsDefs }; // packages/ag-charts-community/src/chart/themes/themeOptionsDef.ts var serializableDate = optionsDefs( { __type: required(constant("date")), value: or(string, number) }, "a serializable date object" ); var navigatorHandleOptionsDef = { width: positiveNumber, height: positiveNumber, grip: boolean, fill: color, stroke: color, strokeWidth: positiveNumber, cornerRadius: positiveNumber }; var navigatorOptionsDef = { enabled: boolean, height: positiveNumber, spacing: positiveNumber, cornerRadius: number, mask: { fill: color, fillOpacity: ratio, stroke: color, strokeWidth: positiveNumber }, minHandle: navigatorHandleOptionsDef, maxHandle: navigatorHandleOptionsDef, miniChart: { enabled: boolean, padding: { top: positiveNumber, bottom: positiveNumber }, label: { enabled: boolean, avoidCollisions: boolean, spacing: positiveNumber, format: numberFormatValidator, formatter: callbackOf(textOrSegments), interval: { minSpacing: positiveNumber, maxSpacing: positiveNumber, values: array, step: number }, ...fontOptionsDef }, series: defined } }; var scrollbarTrackOptionsDef = { ...fillOptionsDef, ...strokeOptionsDef, ...lineDashOptionsDef, cornerRadius: positiveNumber, opacity: ratio }; var scrollbarThumbOptionsDef = { ...scrollbarTrackOptionsDef, minSize: positiveNumber, hoverStyle: { fill: fillOptionsDef.fill, stroke: strokeOptionsDef.stroke } }; var scrollbarBaseOptionsDef = { enabled: boolean, thickness: positiveNumber, spacing: positiveNumber, tickSpacing: positiveNumber, visible: union("auto", "always", "never"), placement: union("outer", "inner"), track: scrollbarTrackOptionsDef, thumb: scrollbarThumbOptionsDef }; var scrollbarHorizontalOrientationOptionsDef = { ...scrollbarBaseOptionsDef, position: union("top", "bottom") }; var scrollbarVerticalOrientationOptionsDef = { ...scrollbarBaseOptionsDef, position: union("left", "right") }; var scrollbarOptionsDef = { enabled: boolean, thickness: positiveNumber, spacing: positiveNumber, tickSpacing: positiveNumber, visible: union("auto", "always", "never"), placement: union("outer", "inner"), track: scrollbarTrackOptionsDef, thumb: scrollbarThumbOptionsDef, horizontal: scrollbarHorizontalOrientationOptionsDef, vertical: scrollbarVerticalOrientationOptionsDef }; var cartesianCrossLineThemeableOptionsDefs = without(cartesianCrossLineOptionsDefs, ["type", "value", "range"]); var cartesianAxesThemeDef = { number: { ...without(numberAxisOptionsDefs, ["type", "crossLines"]), top: without(numberAxisOptionsDefs, ["type", "crossLines", "position"]), right: without(numberAxisOptionsDefs, ["type", "crossLines", "position"]), bottom: without(numberAxisOptionsDefs, ["type", "crossLines", "position"]), left: without(numberAxisOptionsDefs, ["type", "crossLines", "position"]), crossLines: cartesianCrossLineThemeableOptionsDefs }, log: { ...without(logAxisOptionsDefs, ["type", "crossLines"]), top: without(logAxisOptionsDefs, ["type", "crossLines", "position"]), right: without(logAxisOptionsDefs, ["type", "crossLines", "position"]), bottom: without(logAxisOptionsDefs, ["type", "crossLines", "position"]), left: without(logAxisOptionsDefs, ["type", "crossLines", "position"]), crossLines: cartesianCrossLineThemeableOptionsDefs }, category: { ...without(categoryAxisOptionsDefs, ["type", "crossLines"]), top: without(categoryAxisOptionsDefs, ["type", "crossLines", "position"]), right: without(categoryAxisOptionsDefs, ["type", "crossLines", "position"]), bottom: without(categoryAxisOptionsDefs, ["type", "crossLines", "position"]), left: without(categoryAxisOptionsDefs, ["type", "crossLines", "position"]), crossLines: cartesianCrossLineThemeableOptionsDefs }, time: { ...without(timeAxisOptionsDefs, ["type", "crossLines"]), top: without(timeAxisOptionsDefs, ["type", "crossLines", "position"]), right: without(timeAxisOptionsDefs, ["type", "crossLines", "position"]), bottom: without(timeAxisOptionsDefs, ["type", "crossLines", "position"]), left: without(timeAxisOptionsDefs, ["type", "crossLines", "position"]), crossLines: cartesianCrossLineThemeableOptionsDefs }, "unit-time": { ...without(unitTimeAxisOptionsDefs, ["type", "crossLines"]), top: without(unitTimeAxisOptionsDefs, ["type", "crossLines", "position"]), right: without(unitTimeAxisOptionsDefs, ["type", "crossLines", "position"]), bottom: without(unitTimeAxisOptionsDefs, ["type", "crossLines", "position"]), left: without(unitTimeAxisOptionsDefs, ["type", "crossLines", "position"]), crossLines: cartesianCrossLineThemeableOptionsDefs }, "grouped-category": { ...without(groupedCategoryAxisOptionsDefs, ["type"]), top: without(groupedCategoryAxisOptionsDefs, ["type", "position"]), right: without(groupedCategoryAxisOptionsDefs, ["type", "position"]), bottom: without(groupedCategoryAxisOptionsDefs, ["type", "position"]), left: without(groupedCategoryAxisOptionsDefs, ["type", "position"]), crossLines: cartesianCrossLineThemeableOptionsDefs }, "ordinal-time": { ...without(ordinalTimeAxisOptionsDefs, ["type", "crossLines"]), top: without(ordinalTimeAxisOptionsDefs, ["type", "crossLines", "position"]), right: without(ordinalTimeAxisOptionsDefs, ["type", "crossLines", "position"]), bottom: without(ordinalTimeAxisOptionsDefs, ["type", "crossLines", "position"]), left: without(ordinalTimeAxisOptionsDefs, ["type", "crossLines", "position"]), crossLines: cartesianCrossLineThemeableOptionsDefs } }; var polarAxesThemeDef = { "angle-category": { ...without(angleCategoryAxisOptionsDefs, ["type", "crossLines"]), crossLines: without(commonCrossLineOptionsDefs, ["type"]) }, "angle-number": { ...without(angleNumberAxisOptionsDefs, ["type", "crossLines"]), crossLines: without(commonCrossLineOptionsDefs, ["type"]) }, "radius-category": { ...without(radiusCategoryAxisOptionsDefs, ["type", "crossLines"]), crossLines: { ...without(commonCrossLineOptionsDefs, ["type"]), label: { ...commonCrossLineLabelOptionsDefs, positionAngle: number } } }, "radius-number": { ...without(radiusNumberAxisOptionsDefs, ["type", "crossLines"]), crossLines: { ...without(commonCrossLineOptionsDefs, ["type"]), label: { ...commonCrossLineLabelOptionsDefs, positionAngle: number } } } }; var undocumentedSeriesOptionsDef = { visible: undocumented(boolean) }; var themeOverridesOptionsDef = { common: { ...commonChartOptionsDefs, navigator: navigatorOptionsDef, scrollbar: scrollbarOptionsDef, axes: { ...cartesianAxesThemeDef, ...polarAxesThemeDef }, annotations: { ...annotationOptionsDef, line: annotationLineStyleDefs, "horizontal-line": annotationCrossLineStyleDefs, "vertical-line": annotationCrossLineStyleDefs, "disjoint-channel": annotationDisjointChannelStyleDefs, "parallel-channel": annotationParallelChannelStyleDefs, "fibonacci-retracement": annotationFibonacciStylesDefs, "fibonacci-retracement-trend-based": annotationFibonacciStylesDefs, callout: annotationCalloutStylesDefs, comment: annotationCommentStylesDefs, note: annotationNoteStylesDefs, text: annotationTextStylesDef, arrow: annotationLineStyleDefs, "arrow-up": annotationShapeStylesDefs, "arrow-down": annotationShapeStylesDefs, "date-range": annotationMeasurerStylesDefs, "price-range": annotationMeasurerStylesDefs, "date-price-range": annotationMeasurerStylesDefs, "quick-date-price-range": annotationQuickMeasurerStylesDefs }, chartToolbar: { enabled: boolean }, initialState: { legend: arrayOfDefs( { visible: boolean, seriesId: string, itemId: string, legendItemName: string }, "legend state array" ), zoom: { rangeX: { start: or(number, serializableDate), end: or(number, serializableDate) }, rangeY: { start: or(number, serializableDate), end: or(number, serializableDate) }, ratioX: { start: ratio, end: ratio }, ratioY: { start: ratio, end: ratio }, autoScaledAxes: arrayOf(constant("y")) } } }, line: { ...commonChartOptionsDefs, axes: cartesianAxesThemeDef, series: lineSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, scrollbar: scrollbarOptionsDef, ...undocumentedSeriesOptionsDef }, scatter: { ...commonChartOptionsDefs, axes: cartesianAxesThemeDef, series: scatterSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, scrollbar: scrollbarOptionsDef, ...undocumentedSeriesOptionsDef, // @ts-expect-error undocumented option - required by grid paired: undocumented(boolean) }, bubble: { ...commonChartOptionsDefs, axes: cartesianAxesThemeDef, series: bubbleSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, scrollbar: scrollbarOptionsDef, ...undocumentedSeriesOptionsDef }, area: { ...commonChartOptionsDefs, axes: cartesianAxesThemeDef, series: areaSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, scrollbar: scrollbarOptionsDef, ...undocumentedSeriesOptionsDef }, bar: { ...commonChartOptionsDefs, axes: cartesianAxesThemeDef, series: barSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, scrollbar: scrollbarOptionsDef, ...undocumentedSeriesOptionsDef }, "box-plot": { ...commonChartOptionsDefs, axes: cartesianAxesThemeDef, series: boxPlotSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, scrollbar: scrollbarOptionsDef, ...undocumentedSeriesOptionsDef }, candlestick: { ...commonChartOptionsDefs, axes: cartesianAxesThemeDef, series: candlestickSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, scrollbar: scrollbarOptionsDef, ...undocumentedSeriesOptionsDef }, "cone-funnel": { ...commonChartOptionsDefs, axes: cartesianAxesThemeDef, series: coneFunnelSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, scrollbar: scrollbarOptionsDef, ...undocumentedSeriesOptionsDef }, funnel: { ...commonChartOptionsDefs, axes: cartesianAxesThemeDef, series: funnelSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, scrollbar: scrollbarOptionsDef, ...undocumentedSeriesOptionsDef }, ohlc: { ...commonChartOptionsDefs, axes: cartesianAxesThemeDef, series: ohlcSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, scrollbar: scrollbarOptionsDef, ...undocumentedSeriesOptionsDef }, histogram: { ...commonChartOptionsDefs, axes: without(cartesianAxesThemeDef, ["category", "grouped-category", "unit-time", "ordinal-time"]), series: histogramSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, scrollbar: scrollbarOptionsDef, ...undocumentedSeriesOptionsDef }, heatmap: { ...commonChartOptionsDefs, axes: cartesianAxesThemeDef, series: heatmapSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, scrollbar: scrollbarOptionsDef, ...undocumentedSeriesOptionsDef }, waterfall: { ...commonChartOptionsDefs, axes: cartesianAxesThemeDef, series: waterfallSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, scrollbar: scrollbarOptionsDef, ...undocumentedSeriesOptionsDef }, "range-bar": { ...commonChartOptionsDefs, axes: cartesianAxesThemeDef, series: rangeBarSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, scrollbar: scrollbarOptionsDef, ...undocumentedSeriesOptionsDef }, "range-area": { ...commonChartOptionsDefs, axes: cartesianAxesThemeDef, series: rangeAreaSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, scrollbar: scrollbarOptionsDef, ...undocumentedSeriesOptionsDef }, donut: { ...commonChartOptionsDefs, series: donutSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, ...undocumentedSeriesOptionsDef }, pie: { ...commonChartOptionsDefs, series: pieSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, ...undocumentedSeriesOptionsDef }, "radar-line": { ...commonChartOptionsDefs, axes: polarAxesThemeDef, series: radarLineSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, ...undocumentedSeriesOptionsDef }, "radar-area": { ...commonChartOptionsDefs, axes: polarAxesThemeDef, series: radarAreaSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, ...undocumentedSeriesOptionsDef }, "radial-bar": { ...commonChartOptionsDefs, axes: polarAxesThemeDef, series: radialBarSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, ...undocumentedSeriesOptionsDef }, "radial-column": { ...commonChartOptionsDefs, axes: polarAxesThemeDef, series: radialColumnSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, ...undocumentedSeriesOptionsDef }, nightingale: { ...commonChartOptionsDefs, axes: polarAxesThemeDef, series: nightingaleSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, ...undocumentedSeriesOptionsDef }, sunburst: { ...commonChartOptionsDefs, series: sunburstSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, ...undocumentedSeriesOptionsDef }, treemap: { ...commonChartOptionsDefs, series: treemapSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, ...undocumentedSeriesOptionsDef }, "map-shape": { ...commonChartOptionsDefs, series: mapShapeSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, ...undocumentedSeriesOptionsDef }, "map-line": { ...commonChartOptionsDefs, series: mapLineSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, ...undocumentedSeriesOptionsDef }, "map-marker": { ...commonChartOptionsDefs, series: mapMarkerSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, ...undocumentedSeriesOptionsDef }, "map-shape-background": { ...commonChartOptionsDefs, series: mapShapeBackgroundSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, ...undocumentedSeriesOptionsDef }, "map-line-background": { ...commonChartOptionsDefs, series: mapLineBackgroundSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, ...undocumentedSeriesOptionsDef }, sankey: { ...commonChartOptionsDefs, series: sankeySeriesThemeableOptionsDef, navigator: navigatorOptionsDef, ...undocumentedSeriesOptionsDef }, chord: { ...commonChartOptionsDefs, series: chordSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, ...undocumentedSeriesOptionsDef }, pyramid: { ...commonChartOptionsDefs, series: pyramidSeriesThemeableOptionsDef, navigator: navigatorOptionsDef, ...undocumentedSeriesOptionsDef }, "radial-gauge": { ...commonChartOptionsDefs, ...radialGaugeSeriesThemeableOptionsDef, targets: without(radialGaugeTargetOptionsDef, ["value"]), tooltip: { ...radialGaugeSeriesThemeableOptionsDef.tooltip, ...commonChartOptionsDefs.tooltip } }, "linear-gauge": { ...commonChartOptionsDefs, ...linearGaugeSeriesThemeableOptionsDef, targets: without(linearGaugeTargetOptionsDef, ["value"]), tooltip: { ...linearGaugeSeriesThemeableOptionsDef.tooltip, ...commonChartOptionsDefs.tooltip } } }; function mapValues2(object2, mapper) { const result = {}; for (const key of Reflect.ownKeys(object2)) { result[key] = mapper(object2[key], key, object2); } return result; } var themeOverridesOptionsWithOperatorsDef = mapValues2( themeOverridesOptionsDef, function themeOperatorMapper(value, key) { if (isSymbol(key)) return value; if (isFunction(value)) { return or(value, themeOperator, isSymbol); } else if (isObject(value)) { return or( optionsDefs( unionSymbol in value ? mapValues2(value, (val) => isObject(val) ? mapValues2(val, themeOperatorMapper) : val) : mapValues2(value, themeOperatorMapper) ), themeOperator, isSymbol ); } throw new Error(`Invalid theme override value: ${String(value)}`); } ); // packages/ag-charts-community/src/chart/themes/vividDark.ts var VIVID_DARK_FILLS = { BLUE: "#0083ff", ORANGE: "#ff6600", GREEN: "#00af00", CYAN: "#00ccff", YELLOW: "#f7c700", VIOLET: "#ac26ff", GRAY: "#a7a7b7", MAGENTA: "#e800c5", BROWN: "#b54300", RED: "#ff0000" }; var VIVID_DARK_STROKES = { BLUE: "#67b7ff", ORANGE: "#ffc24d", GREEN: "#5cc86f", CYAN: "#54ebff", VIOLET: "#fff653", YELLOW: "#c18aff", GRAY: "#aeaeae", MAGENTA: "#f078d4", BROWN: "#ba8438", RED: "#ff726e" }; var VividDark = class extends DarkTheme { getDefaultColors() { return { fills: VIVID_DARK_FILLS, fillsFallback: Object.values(VIVID_DARK_FILLS), strokes: VIVID_DARK_STROKES, sequentialColors: getSequentialColors(VIVID_DARK_FILLS), divergingColors: [VIVID_DARK_FILLS.ORANGE, VIVID_DARK_FILLS.YELLOW, VIVID_DARK_FILLS.GREEN], hierarchyColors: [], secondSequentialColors: [ "#0083ff", "#0076e6", "#0069cc", "#005cb3", "#004f99", "#004280", "#003466", "#00274c" ], secondDivergingColors: [VIVID_DARK_FILLS.GREEN, VIVID_DARK_FILLS.YELLOW, VIVID_DARK_FILLS.RED], secondHierarchyColors: [], up: { fill: VIVID_DARK_FILLS.GREEN, stroke: VIVID_DARK_STROKES.GREEN }, down: { fill: VIVID_DARK_FILLS.RED, stroke: VIVID_DARK_STROKES.RED }, neutral: { fill: VIVID_DARK_FILLS.GRAY, stroke: VIVID_DARK_STROKES.GRAY }, altUp: { fill: VIVID_DARK_FILLS.BLUE, stroke: VIVID_DARK_STROKES.BLUE }, altDown: { fill: VIVID_DARK_FILLS.ORANGE, stroke: VIVID_DARK_STROKES.ORANGE }, altNeutral: { fill: VIVID_DARK_FILLS.GRAY, stroke: VIVID_DARK_STROKES.GRAY } }; } getTemplateParameters() { const params = super.getTemplateParameters(); params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR, VIVID_DARK_FILLS.BLUE); params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL, VIVID_DARK_FILLS.BLUE); return params; } }; // packages/ag-charts-community/src/chart/themes/vividLight.ts var VIVID_FILLS = { BLUE: "#0083ff", ORANGE: "#ff6600", GREEN: "#00af00", CYAN: "#00ccff", YELLOW: "#f7c700", VIOLET: "#ac26ff", GRAY: "#a7a7b7", MAGENTA: "#e800c5", BROWN: "#b54300", RED: "#ff0000" }; var VIVID_STROKES = { BLUE: "#0f68c0", ORANGE: "#d47100", GREEN: "#007922", CYAN: "#009ac2", VIOLET: "#bca400", YELLOW: "#753cac", GRAY: "#646464", MAGENTA: "#9b2685", BROWN: "#6c3b00", RED: "#cb0021" }; var VividLight = class extends ChartTheme { getDefaultColors() { return { ...super.getDefaultColors(), fills: VIVID_FILLS, fillsFallback: Object.values(VIVID_FILLS), strokes: VIVID_STROKES, sequentialColors: getSequentialColors(VIVID_FILLS), divergingColors: [VIVID_FILLS.ORANGE, VIVID_FILLS.YELLOW, VIVID_FILLS.GREEN], hierarchyColors: [], secondSequentialColors: [ "#0083ff", "#1a8fff", "#339cff", "#4da8ff", "#66b5ff", "#80c1ff", "#99cdff", "#b3daff" ], secondDivergingColors: [VIVID_FILLS.GREEN, VIVID_FILLS.YELLOW, VIVID_FILLS.RED], secondHierarchyColors: [], up: { fill: VIVID_FILLS.GREEN, stroke: VIVID_STROKES.GREEN }, down: { fill: VIVID_FILLS.RED, stroke: VIVID_STROKES.RED }, neutral: { fill: VIVID_FILLS.GRAY, stroke: VIVID_STROKES.GRAY }, altUp: { fill: VIVID_FILLS.BLUE, stroke: VIVID_STROKES.BLUE }, altDown: { fill: VIVID_FILLS.ORANGE, stroke: VIVID_STROKES.ORANGE }, altNeutral: { fill: VIVID_FILLS.GRAY, stroke: VIVID_STROKES.GRAY } }; } getTemplateParameters() { const params = super.getTemplateParameters(); params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR, VIVID_FILLS.BLUE); params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL, VIVID_FILLS.BLUE); return params; } }; // packages/ag-charts-community/src/chart/mapping/themes.ts var lightTheme = simpleMemorize(() => new ChartTheme()); var themeCacheDebug = debugLogger_exports.create(true, "perf"); var cacheCallback = (status, fn, keys) => { themeCacheDebug(`[CACHE] ChartTheme`, status, fn.name, keys); }; var themes = { // darkThemes, "ag-default-dark": simpleMemorize(() => new DarkTheme()), "ag-sheets-dark": simpleMemorize(() => new SheetsDark(), cacheCallback), "ag-polychroma-dark": simpleMemorize(() => new PolychromaDark(), cacheCallback), "ag-vivid-dark": simpleMemorize(() => new VividDark(), cacheCallback), "ag-material-dark": simpleMemorize(() => new MaterialDark(), cacheCallback), "ag-financial-dark": simpleMemorize(() => new FinancialDark(), cacheCallback), // lightThemes, "ag-default": lightTheme, "ag-sheets": simpleMemorize(() => new SheetsLight(), cacheCallback), "ag-polychroma": simpleMemorize(() => new PolychromaLight(), cacheCallback), "ag-vivid": simpleMemorize(() => new VividLight(), cacheCallback), "ag-material": simpleMemorize(() => new MaterialLight(), cacheCallback), "ag-financial": simpleMemorize(() => new FinancialLight(), cacheCallback) }; var getChartTheme = simpleMemorize(createChartTheme, cacheCallback); function createChartTheme(value) { if (value instanceof ChartTheme) { return value; } else if (!validateStructure(value)) { return lightTheme(); } if (value == null || typeof value === "string") { const stockTheme = themes[value ?? "ag-default"]; if (stockTheme) { return stockTheme(); } throw new Error(`Cannot find theme \`${value}\`.`); } const { cleared, invalid } = validate(reduceThemeOptions(value), themeOptionsDef, "theme"); for (const error2 of invalid) { logger_exports.warnOnce(String(error2)); } const baseTheme = cleared?.baseTheme ? getChartTheme(cleared.baseTheme) : lightTheme(); return cleared ? new baseTheme.constructor(cleared) : baseTheme; } function reduceThemeOptions(options) { if (!isObject(options) || !isObject(options.baseTheme)) return options; let maybeNested = options; let palette; let params; const overrides = []; while (typeof maybeNested === "object") { palette ?? (palette = maybeNested.palette); params ?? (params = maybeNested.params); if (maybeNested.overrides) { overrides.push(maybeNested.overrides); } maybeNested = maybeNested.baseTheme; } return { baseTheme: maybeNested, overrides: mergeDefaults(...overrides), params, palette }; } var themeOptionsDef = { baseTheme: or(string, object), overrides: themeOverridesOptionsWithOperatorsDef, params: { accentColor: color, axisColor: color, backgroundColor: color, borderColor: color, borderRadius: number, chartBackgroundColor: color, chartPadding: number, focusShadow: string, foregroundColor: color, fontFamily: fontFamilyFull, fontSize: number, fontWeight, gridLineColor: color, popupShadow: string, subtleTextColor: color, textColor: color, separationLinesColor: color, chromeBackgroundColor: color, chromeFontFamily: fontFamilyFull, chromeFontSize: number, chromeFontWeight: fontWeight, chromeSubtleTextColor: color, chromeTextColor: color, buttonBackgroundColor: color, buttonBorder: boolean, buttonFontWeight: fontWeight, buttonTextColor: color, inputBackgroundColor: color, inputBorder: boolean, inputTextColor: color, menuBackgroundColor: color, menuBorder: boolean, menuTextColor: color, panelBackgroundColor: color, panelSubtleTextColor: color, tooltipBackgroundColor: color, tooltipBorder: boolean, tooltipTextColor: color, tooltipSubtleTextColor: color, crosshairLabelBackgroundColor: color, crosshairLabelTextColor: color }, palette: { fills: arrayOf(colorUnion), strokes: arrayOf(color), up: { fill: or(color, gradientStrict), stroke: color }, down: { fill: or(color, gradientStrict), stroke: color }, neutral: { fill: or(color, gradientStrict), stroke: color } } }; var themeNameValidator = union( "ag-default", "ag-default-dark", "ag-sheets", "ag-sheets-dark", "ag-polychroma", "ag-polychroma-dark", "ag-vivid", "ag-vivid-dark", "ag-material", "ag-material-dark", "ag-financial", "ag-financial-dark" ); function validateStructure(value) { const { invalid } = validate( { theme: value }, { theme: or(themeNameValidator, object) } ); for (const error2 of invalid) { logger_exports.warnOnce(String(error2)); } return invalid.length === 0; } // packages/ag-charts-community/src/module/optionsGraphUtils.ts var PATH_EDGE = "path"; var PATH_ARRAY_EDGE = "pathArray"; var DEFAULTS_EDGE = "default"; var OVERRIDES_EDGE = "override"; var USER_OPTIONS_EDGE = "user"; var USER_PARTIAL_OPTIONS_EDGE = "userPartial"; var OPERATION_EDGE = "operation"; var OPERATION_VALUE_EDGE = "operationValue"; var DEPENDENCY_EDGE = "dependency"; var AUTO_ENABLE_EDGE = "autoEnable"; var AUTO_ENABLE_VALUE_EDGE = "autoEnableValue"; var PRUNE_EDGE = "prune"; var CHILDREN_SOURCE_EDGE = "childrenSource"; function isRatio(value) { return isNumber(value) && value >= 0 && value <= 1; } function hasPathSafe(object2, path) { let result = object2; for (const part of path) { const isPartKey = typeof part === "string" && result != null && (typeof result === "object" || Array.isArray(result)) && part in result; if (!isPartKey) return false; result = result[part]; } return true; } function getPathSafe(object2, path) { let result = object2; for (const part of path) { const isPartKey = typeof part === "string" && result != null && (typeof result === "object" || Array.isArray(result)) && part in result; if (!isPartKey) return; result = result[part]; } return result; } function setPathSafe(object2, path, value) { const pathLength = path.length; if (pathLength === 0) return; let result = object2; const lastIndex = pathLength - 1; const lastPart = path[lastIndex]; for (let i = 0; i < lastIndex; i++) { const part = path[i]; const nextPart = path[i + 1]; let currentValue = result[part]; if (currentValue == null || !isObjectLike(currentValue)) { currentValue = Number.isNaN(Number(nextPart)) ? {} : []; result[part] = currentValue; } result = currentValue; } result[lastPart] = value; } var DIGITS_ONLY_REGEX = /^\d+$/; function getPathLastIndexIndex(pathArray, offset = 0) { let count = 0; for (let i = pathArray.length - 1; i >= 0; i--) { const part = pathArray[i]; if (DIGITS_ONLY_REGEX.test(part)) { count++; if (count > offset) return i; } } return -1; } function getPathLastIndex(pathArray, offset = 0) { const indexIndex = getPathLastIndexIndex(pathArray, offset); return Number(pathArray[indexIndex]); } function resolvePath(currentPath, path, variables) { const relativePathParts = path.split("/"); let resolvedPath = [...currentPath]; if (path.startsWith("/")) { resolvedPath = []; relativePathParts.shift(); } let prevPartWasTwoDots = false; for (const part of relativePathParts) { if (part === "..") { resolvedPath.pop(); if (!prevPartWasTwoDots) resolvedPath.pop(); } else if (part === ".") { resolvedPath.pop(); } else if (part === "$index") { const index = getPathLastIndex(currentPath); if (Number.isNaN(index)) return UNRESOLVABLE_PATH; resolvedPath.push(`${index}`); } else if (part === "$prevIndex") { const index = getPathLastIndex(currentPath); if (Number.isNaN(index) || Number(index) <= 0) return UNRESOLVABLE_PATH; resolvedPath.push(`${Number(index) - 1}`); } else if (part.startsWith("$")) { const variable = variables?.[part.slice(1)]; if (variable == null) return UNRESOLVABLE_PATH; resolvedPath.push(variable); } else if (part.length !== 0) { resolvedPath.push(part); } prevPartWasTwoDots = part === ".."; } return resolvedPath; } var UNRESOLVABLE_PATH = Symbol("unresolvable-path"); var RESOLVED_TO_BRANCH = Symbol("resolved-to-branch"); // packages/ag-charts-community/src/module/optionsGraphOperations.ts function getOperation(value, keys) { if (value == null || typeof value !== "object" || Array.isArray(value)) return; keys ?? (keys = Object.keys(value)); if (keys.length === 0) return; const operation = keys[0]; if (!operationTypes.has(operation)) return; return { operation, values: Array.isArray(value[operation]) ? value[operation] : [value[operation]] }; } function getOperationTargetVertex(graph, vertex, valueVertex) { const operation = getOperation(graph.getVertexValue(valueVertex)); switch (operation?.operation) { case "$path" /* Path */: { const [relativePath] = operation.values; const pathArray = graph.getPathArray(vertex); const path = resolvePath(pathArray, relativePath); if (path === UNRESOLVABLE_PATH) return; return graph.findVertexAtPath(path); } case "$value" /* Value */: return vertex; } } var cacheOperations = { $cacheMax: cacheMaxOperation }; function cacheMaxOperation(graph, vertex, values) { const [valueVertex] = values; const pathArray = graph.getPathArray(vertex); const cached = graph.getCachedValue(pathArray, "$cacheMax" /* CacheMax */); const value = graph.resolveVertexValue(vertex, valueVertex); if (typeof value !== "number") return cached; if (typeof cached !== "number") { graph.setCachedValue(pathArray, "$cacheMax" /* CacheMax */, value); return value; } const maxValue = Math.max(cached, value); graph.setCachedValue(pathArray, "$cacheMax" /* CacheMax */, maxValue); return maxValue; } var chartOperations = { $hasSeriesType: { dependencies: seriesTypeDependencyFactory, resolve: hasSeriesTypeOperation }, $isChartType: { dependencies: seriesTypeDependencyFactory, resolve: isChartTypeOperation }, $isSeriesType: { dependencies: seriesTypeDependencyFactory, resolve: isSeriesTypeOperation } }; function seriesTypeDependencyFactory(graph, vertex, _values) { const dependencyVertex = graph.findVertexAtPath(["series", "0", "type"]); if (dependencyVertex) { graph.addEdge(vertex, dependencyVertex, DEPENDENCY_EDGE); } } function hasSeriesTypeOperation(graph, vertex, values) { const [valueVertex] = values; const value = graph.resolveVertexValue(vertex, valueVertex); const series = graph.getResolvedPath(["series"]); if (!Array.isArray(series)) return false; for (const s of series) { if (s.type === value) return true; } return false; } function isSeriesTypeOperation(graph, vertex, values) { const [valueVertex] = values; const value = graph.resolveVertexValue(vertex, valueVertex); const seriesType = graph.getResolvedPath(["series", "0", "type"]); return seriesType === value; } function isChartTypeOperation(graph, vertex, values) { const [valueVertex] = values; const value = graph.resolveVertexValue(vertex, valueVertex); const seriesType = graph.getResolvedPath(["series", "0", "type"]); if (typeof seriesType !== "string") return false; const seriesModule = moduleRegistry_exports.getSeriesModule(seriesType); if (seriesModule == null) return false; switch (value) { case "cartesian": return seriesModule.chartType === "cartesian"; case "polar": return seriesModule.chartType === "polar"; case "standalone": return seriesModule.chartType === "standalone"; } return false; } var colorOperations = { $foregroundBackgroundMix: foregroundBackgroundMixOperation, $foregroundOpacity: foregroundOpacityOperation, $interpolate: interpolateOperation, $isGradient: isGradientOperation, $isImage: isImageOperation, $isPattern: isPatternOperation, $mix: mixOperation }; function foregroundBackgroundMixOperation(graph, vertex, values) { const [foregroundRatioVertex] = values; const foregroundRatio = graph.resolveVertexValue(vertex, foregroundRatioVertex); const foregroundColor = graph.getParamValue("foregroundColor"); const backgroundColor = graph.getParamValue("backgroundColor"); if (typeof foregroundColor === "string" && typeof backgroundColor === "string" && isRatio(foregroundRatio)) { return Color.mix( Color.fromString(foregroundColor), Color.fromString(backgroundColor), 1 - foregroundRatio ).toString(); } debugLogger_exports.inDevelopmentMode( () => logger_exports.warnOnce( `\`$foregroundBackgroundMix\` json operation failed on [${String(foregroundRatio)}}}] at [${graph.getPathArray(vertex).join(".")}], expecting a number between 0 and 1.` ) ); } function foregroundOpacityOperation(graph, vertex, values) { const [opacityVertex] = values; const opacity = graph.resolveVertexValue(vertex, opacityVertex); const foregroundColor = graph.getParamValue("foregroundColor"); if (typeof foregroundColor === "string" && isRatio(opacity)) { const color2 = Color.fromString(foregroundColor); return new Color(color2.r, color2.g, color2.b, opacity).toString(); } debugLogger_exports.inDevelopmentMode( () => logger_exports.warnOnce( `\`$foregroundOpacity\` json operation failed on [${String(opacity)}}}] at [${graph.getPathArray(vertex).join(".")}], expecting a number between 0 and 1.` ) ); } function interpolateOperation(graph, vertex, values) { const [colorsVertex, countVertex] = values; const colors = graph.resolveVertexValue(vertex, colorsVertex); const count = graph.resolveVertexValue(vertex, countVertex); if (!isArray(colors) || !isNumber(count)) return; return Color.interpolate( colors.map((color2) => Color.fromString(color2)), count ).map((color2) => color2.toString()); } function isGradientOperation(graph, vertex, values) { const [valueVertex] = values; const value = graph.resolveVertexValue(vertex, valueVertex); return isGradientFill(value); } function isImageOperation(graph, vertex, values) { const [valueVertex] = values; const value = graph.resolveVertexValue(vertex, valueVertex); return isImageFill(value); } function isPatternOperation(graph, vertex, values) { const [valueVertex] = values; const value = graph.resolveVertexValue(vertex, valueVertex); return isPatternFill(value); } function mixOperation(graph, vertex, values) { const [colorAVertex, colorBVertex, ratioVertex] = values; const colorA = graph.resolveVertexValue(vertex, colorAVertex); const colorB = graph.resolveVertexValue(vertex, colorBVertex); const ratio2 = graph.resolveVertexValue(vertex, ratioVertex); const pathArray = graph.getPathArray(vertex); const warningPrefix = `\`$mix\` json operation failed on [${String(colorA)}, ${String(colorB)}, ${String(ratio2)}] at [${pathArray.join(".")}], expecting`; const warningMessage = `${warningPrefix} two colors and a number between 0 and 1.`; if (typeof colorB !== "string" || !isRatio(ratio2)) { debugLogger_exports.inDevelopmentMode(() => logger_exports.warnOnce(warningMessage)); return; } if (typeof colorA === "string") { try { return Color.mix(Color.fromString(colorA), Color.fromString(colorB), ratio2).toString(); } catch { debugLogger_exports.inDevelopmentMode(() => logger_exports.warnOnce(warningMessage)); return; } } if (!isGradientFill(colorA)) { debugLogger_exports.inDevelopmentMode(() => logger_exports.warnOnce(warningMessage)); return; } let colorStops = colorA.colorStops; try { colorStops = colorStops?.map((value) => { let color2; if (typeof value.color === "string") { color2 = Color.mix(Color.fromString(value.color), Color.fromString(colorB), ratio2).toString(); } return { ...value, color: color2 }; }); } catch { debugLogger_exports.inDevelopmentMode( () => logger_exports.warnOnce(`${warningPrefix} a gradient, a color and a number between 0 and 1.`) ); return; } return { ...colorA, colorStops }; } var fontOperations = { $rem: remOperation }; function remOperation(graph, vertex, values) { const [valueVertex] = values; const value = graph.getVertexValue(valueVertex); const fontSize = graph.getParamValue("fontSize"); if (typeof fontSize === "number" && typeof value === "number") { return Math.round(value * fontSize); } debugLogger_exports.inDevelopmentMode( () => logger_exports.warnOnce( `\`$rem\` json operation failed on [${String(value)}] at [${graph.getPathArray(vertex).join(".")}], expecting a number.` ) ); } var logicOperations = { $and: andOperation, $eq: eqOperation, $every: everyOperation, $greaterThan: greaterThanOperation, $if: ifOperation, $lessThan: lessThanOperation, $not: notOperation, $or: orOperation, $some: someOperation, $switch: switchOperation }; function andOperation(graph, vertex, values) { for (const valueVertex of values) { const value = graph.resolveVertexValue(vertex, valueVertex); if (values.length === 1 && Array.isArray(value)) { return value.every((v) => Boolean(v)); } if (!value) return false; } return true; } function eqOperation(graph, vertex, values) { let compare; let first2 = true; for (const valueVertex of values) { const value = graph.resolveVertexValue(vertex, valueVertex); if (first2) { compare = value; first2 = false; } else if (value !== compare) { return false; } } return true; } function everyOperation(graph, vertex, values) { const [mapOperationVertex, mapValuesVertex] = values; const mapOperationValue = graph.getVertexValue(mapOperationVertex); const mapValues3 = graph.resolveVertexValue(vertex, mapValuesVertex); if (!Array.isArray(mapValues3)) return; let index = 0; for (const value of mapValues3) { const resolved = graph.graftAndResolveOrphanValue(vertex, `${index}`, mapOperationValue, value); if (!resolved) return false; index++; } return true; } function greaterThanOperation(graph, vertex, values) { const [value, compare] = values; return graph.resolveVertexValue(vertex, value) > graph.resolveVertexValue(vertex, compare); } function ifOperation(graph, vertex, values) { const [conditionVertex, thenVertex, elseVertex] = values; const condition = graph.resolveVertexValue(vertex, conditionVertex); const valueVertex = condition ? thenVertex : elseVertex; const neighbours = graph.neighboursWithEdgeValue(valueVertex, PATH_EDGE); if (neighbours) { for (const neighbour of neighbours) { graph.addEdge(vertex, neighbour, PATH_EDGE); } } return graph.resolveVertexValue(vertex, valueVertex); } function lessThanOperation(graph, vertex, values) { const [value, compare] = values; return graph.resolveVertexValue(vertex, value) < graph.resolveVertexValue(vertex, compare); } function notOperation(graph, vertex, values) { const [valueVertex] = values; if (!valueVertex) return; return !graph.resolveVertexValue(vertex, valueVertex); } function orOperation(graph, vertex, values) { for (const valueVertex of values) { const value = graph.resolveVertexValue(vertex, valueVertex); if (values.length === 1 && Array.isArray(value)) { return value.some((v) => Boolean(v)); } if (value) return true; } return false; } function someOperation(graph, vertex, values) { const [mapOperationVertex, mapValuesVertex] = values; const mapOperationValue = graph.getVertexValue(mapOperationVertex); const mapValues3 = graph.resolveVertexValue(vertex, mapValuesVertex); if (!Array.isArray(mapValues3)) return; let index = 0; for (const value of mapValues3) { const resolved = graph.graftAndResolveOrphanValue(vertex, `${index}`, mapOperationValue, value); if (resolved) return true; index++; } return false; } function switchOperation(graph, vertex, values) { const [conditionValueVertex, defaultValueVertex, ...caseVertices] = values; const conditionValue = graph.resolveVertexValue(vertex, conditionValueVertex); for (const caseVertex of caseVertices) { const caseValue = graph.getVertexValue(caseVertex); if (!Array.isArray(caseValue)) continue; const [caseConditionValue, caseResultValue] = caseValue; if (conditionValue === caseConditionValue || Array.isArray(caseConditionValue) && caseConditionValue.includes(conditionValue)) { return caseResultValue; } } return graph.resolveVertexValue(vertex, defaultValueVertex); } var locationOperations = { $isUserOption: isUserOptionOperation, $palette: paletteOperation, $mapPalette: mapPaletteOperation, $path: { dependencies: pathOperationDependenciesFactory, resolve: pathOperation }, $pathString: { dependencies: pathOperationDependenciesFactory, resolve: pathStringOperation }, $ref: refOperation }; function isUserOptionOperation(graph, vertex, values) { const [relativePathVertices, thenVertex, elseVertex] = values; const children = graph.neighboursWithEdgeValue(relativePathVertices, PATH_EDGE); if (children) { for (const child of children) { const relativePathVertex = graph.findNeighbour(child, DEFAULTS_EDGE); if (relativePathVertex && isUserOptionCheck(graph, vertex, relativePathVertex)) { return graph.resolveVertexValue(vertex, thenVertex); } } } else if (isUserOptionCheck(graph, vertex, relativePathVertices)) { return graph.resolveVertexValue(vertex, thenVertex); } return graph.resolveVertexValue(vertex, elseVertex); } function isUserOptionCheck(graph, vertex, relativePathVertex) { const relativePath = graph.resolveVertexValue(vertex, relativePathVertex); if (!isString(relativePath)) { throw new Error(`\`$isUserOption\` json operation failed on [${String(relativePath)}], expecting a string.`); } const pathArray = graph.getPathArray(vertex); const path = resolvePath(pathArray, relativePath); if (path === UNRESOLVABLE_PATH) return false; return graph.hasUserOption(path); } var PALETTE_INDEX_KEYS = /* @__PURE__ */ new Set(["fill", "fillFallback", "stroke", "gradient", "range2"]); function paletteOperation(graph, vertex, values) { const [keyVertex] = values; const key = graph.resolveVertexValue(vertex, keyVertex); if (!isString(key)) return; if (PALETTE_INDEX_KEYS.has(key)) { const pathArray = graph.getPathArray(vertex); const index = getPathLastIndex(pathArray); if (Number.isNaN(index)) return; switch (key) { case "fill": return circularSliceArray(graph.palette.fills, 1, index)[0]; case "fillFallback": return circularSliceArray(graph.palette.fillsFallback, 1, index)[0]; case "stroke": return circularSliceArray(graph.palette.strokes, 1, index)[0]; case "gradient": return circularSliceArray(graph.palette.sequentialColors, 1, index)[0]; case "range2": return circularSliceArray(graph.palette.fills, 2, index); } return; } if (key === "gradients") { return graph.palette.sequentialColors; } if (key === "type") { return graph.paletteType; } const value = getPathSafe(graph.palette, key.split(".")); if (Array.isArray(value)) return [...value]; if (typeof value === "object") return { ...value }; return value; } function mapPaletteOperation(graph, vertex, values) { const [keyVertex] = values; const key = graph.resolveVertexValue(vertex, keyVertex); if (!isString(key)) return; if (PALETTE_INDEX_KEYS.has(key)) { const pathArray = graph.getPathArray(vertex); let index = getPathLastIndex(pathArray); let ignoreIndexOffset = 0; const path = ["series", "0", "type"]; for (let i = 0; i < index; i++) { path[1] = `${i}`; const siblingSeriesType = graph.getResolvedPath(path); if ("map-shape-background" === siblingSeriesType || "map-line-background" === siblingSeriesType) { ignoreIndexOffset++; } } index -= ignoreIndexOffset; if (Number.isNaN(index)) return; switch (key) { case "fill": return circularSliceArray(graph.palette.fills, 1, index)[0]; case "fillFallback": return circularSliceArray(graph.palette.fillsFallback, 1, index)[0]; case "stroke": return circularSliceArray(graph.palette.strokes, 1, index)[0]; case "gradient": return circularSliceArray(graph.palette.sequentialColors, 1, index)[0]; case "range2": return circularSliceArray(graph.palette.fills, 2, index); } return; } if (key === "gradients") { return graph.palette.sequentialColors; } if (key === "type") { return graph.paletteType; } const value = getPathSafe(graph.palette, key.split(".")); if (Array.isArray(value)) return [...value]; if (typeof value === "object") return { ...value }; return value; } function pathOperationDependenciesFactory(graph, vertex, values) { const [relativePathVertex] = values; const relativePath = graph.getVertexValue(relativePathVertex); if (isString(relativePath)) { const pathArray = graph.getPathArray(vertex); const path = resolvePath(pathArray, relativePath); if (path === UNRESOLVABLE_PATH) { return; } const dependencyVertex = graph.findVertexAtPath(path); if (dependencyVertex) { graph.addEdge(vertex, dependencyVertex, DEPENDENCY_EDGE); } } } function pathOperation(graph, vertex, values) { const hasDefaultValue = values.length > 1; const hasCustomBranch = values.length > 2; const [relativePathVertex, defaultValueVertex, customBranchVertex] = values; const relativePath = graph.resolveVertexValue(vertex, relativePathVertex); const customBranch = hasCustomBranch ? graph.resolveVertexValue(vertex, customBranchVertex) : null; if (!isString(relativePath)) { throw new Error(`\`$path\` json operation failed on [${String(relativePath)}], expecting a string.`); } const pathArray = graph.getPathArray(vertex); const path = resolvePath(pathArray, relativePath); if (path === UNRESOLVABLE_PATH) { return; } const resolved = customBranch ? getPathSafe(customBranch, path) : graph.getResolvedPath(path); if (resolved != null) { return resolved; } if (hasDefaultValue) { return graph.resolveVertexValue(vertex, defaultValueVertex); } } function pathStringOperation(graph, vertex, values) { const [relativePathVertex, variablesVertex] = values; const relativePath = graph.resolveVertexValue(vertex, relativePathVertex); if (!isString(relativePath)) { throw new Error(`\`$path\` json operation failed on [${String(relativePath)}], expecting a string.`); } let variables; if (variablesVertex) { variables = graph.graftAndResolveOrphan(vertex, variablesVertex); } const pathArray = graph.getPathArray(vertex); const path = resolvePath(pathArray, relativePath, variables); if (path === UNRESOLVABLE_PATH) { throw new Error(`Unresolvable path [${relativePath}] at [${pathArray.join(".")}]`); } return path; } function refOperation(graph, _vertex, values) { const [value] = values; const paramKey = graph.getVertexValue(value); return graph.getParamValue(paramKey); } var transformOperations = { $apply: applyOperation, $applyCycle: applyCycleOperation, $applySwitch: applySwitchOperation, $applyTheme: applyThemeOperation, $clone: cloneOperation, $findFirstSiblingNotOperation: findFirstSiblingNotOperationOperation, $map: mapOperation, $merge: mergeOperation, $omit: omitOperation, $size: sizeOperation, $shallow: shallowOperation, $shallowSimple: shallowSimpleOperation, $value: valueOperation }; function applyOperation(graph, vertex, values) { const [objectVertex, defaultValueVertex, overridesPathVertex1, overridesPathVertex2] = values; const object2 = graph.getVertexValue(objectVertex); if (!isPlainObject(object2)) return; const defaultValue = defaultValueVertex ? graph.getVertexValue(defaultValueVertex) : void 0; const children = graph.neighboursWithEdgeValue(vertex, PATH_EDGE); const hasChildren = children && children.length > 0; if (!hasChildren && defaultValue == null) { return RESOLVED_TO_BRANCH; } const overridesPath1 = overridesPathVertex1 ? graph.resolveVertexValue(vertex, overridesPathVertex1) : void 0; const overridesPath2 = overridesPathVertex2 ? graph.resolveVertexValue(vertex, overridesPathVertex2) : void 0; if (!hasChildren && defaultValue != null) { if (getOperation(defaultValue)) { const resolvedDefaultValue = graph.resolveVertexValue(vertex, defaultValueVertex); if (isPlainObject(resolvedDefaultValue)) { graph.graftObject(vertex, resolvedDefaultValue, [overridesPath1, overridesPath2]); } } else { graph.graftObject(vertex, defaultValue, [overridesPath1, overridesPath2]); } } if (!hasChildren) return RESOLVED_TO_BRANCH; for (const child of children) { const childNeighbours = graph.neighboursWithEdgeValue(child, PATH_EDGE); if (!childNeighbours || childNeighbours.length === 0) { const stubVertex = graph.addVertex({}); graph.addEdge(child, stubVertex, DEFAULTS_EDGE); } else { graph.graftObject(child, object2, [overridesPath1, overridesPath2]); } } return RESOLVED_TO_BRANCH; } function applyCycleOperation(graph, vertex, values) { const [sizeVertex, defaultValuesVertex, operationVertex] = values; const size = graph.resolveVertexValue(vertex, sizeVertex); if (typeof size !== "number") return; const pathArray = graph.getPathArray(vertex); const userOption = graph.dangerouslyGetUserOption(pathArray); const hasThemeOverride = graph.hasThemeOverride(pathArray); const graftEdge = userOption == null ? void 0 : USER_OPTIONS_EDGE; const cycledValues = userOption ?? graph.resolveVertexValue(vertex, defaultValuesVertex); if (!Array.isArray(cycledValues)) return; const operation = operationVertex ? graph.getVertexValue(operationVertex) : void 0; for (let index = 0; index < size; index++) { const value = cycledValues[index % cycledValues.length]; if (value == null) continue; if (userOption || !hasThemeOverride) { graph.graftValue(vertex, `${index}`, value, void 0, graftEdge); } if (operation) { graph.graftValue(vertex, `${index}`, operation, value, graftEdge); } } return RESOLVED_TO_BRANCH; } function applySwitchOperation(graph, vertex, values) { const [conditionValueVertex, defaultValueVertex, ...caseVertices] = values; const conditionValue = graph.resolveVertexValue(vertex, conditionValueVertex); for (const caseVertex of caseVertices) { const caseValue = graph.getVertexValue(caseVertex); if (!Array.isArray(caseValue)) continue; const [caseConditionValue, caseResultValue] = caseValue; if (conditionValue === caseConditionValue || Array.isArray(caseConditionValue) && caseConditionValue.includes(conditionValue)) { graph.graftObject(vertex, caseResultValue, [], DEFAULTS_EDGE); return RESOLVED_TO_BRANCH; } } return graph.resolveVertexValue(vertex, defaultValueVertex); } function applyThemeOperation(graph, vertex, values) { const [fromPathVertex, variablesVertex, ignorePathsVertex] = values; let fromPaths = graph.getVertexValue(fromPathVertex); if (typeof fromPaths === "string") { fromPaths = [fromPaths]; } if (!Array.isArray(fromPaths)) return; const children = graph.neighboursWithEdgeValue(vertex, PATH_EDGE); const ignorePathsValue = ignorePathsVertex ? graph.getVertexValue(ignorePathsVertex) : []; const ignorePaths = Array.isArray(ignorePathsValue) ? new Set(ignorePathsValue) : /* @__PURE__ */ new Set(); if (!children) return RESOLVED_TO_BRANCH; for (const child of children) { const variables = graph.graftAndResolveOrphan(child, variablesVertex); for (const fromPath of fromPaths) { const fromPathResolved = resolvePath([], fromPath, variables); if (fromPathResolved === UNRESOLVABLE_PATH) { continue; } graph.graftConfig(child, fromPathResolved, ignorePaths); } } return RESOLVED_TO_BRANCH; } function cloneOperation(graph, vertex, values) { const [valueVertex] = values; const value = graph.resolveVertexValue(vertex, valueVertex); if (!isPlainObject(value)) return; graph.graftObject(vertex, value, void 0, USER_OPTIONS_EDGE); return RESOLVED_TO_BRANCH; } function findFirstSiblingNotOperationOperation(graph, vertex, values) { const [defaultValueVertex] = values; const pathArray = graph.getPathArray(vertex); const parentPathArray = resolvePath(pathArray, ".."); if (parentPathArray === UNRESOLVABLE_PATH) return; const parentVertex = graph.findVertexAtPath(parentPathArray); if (!parentVertex) { return graph.resolveVertexValue(vertex, defaultValueVertex); } const siblings = graph.neighboursWithEdgeValue(parentVertex, PATH_EDGE); if (siblings) { for (const sibling of siblings) { const siblingPathArray = graph.getPathArray(sibling); if (siblingPathArray[parentPathArray.length] === pathArray[parentPathArray.length]) continue; const siblingChildPathArray = siblingPathArray.concat(pathArray.slice(parentPathArray.length + 1)); const siblingChildVertex = graph.findVertexAtPath(siblingChildPathArray); if (!siblingChildVertex) continue; const siblingChildUserOptionsValue = graph.findNeighbourValue(siblingChildVertex, USER_OPTIONS_EDGE); if (siblingChildUserOptionsValue != null) { return siblingChildUserOptionsValue; } const siblingChildOverridesValue = graph.findNeighbourValue(siblingChildVertex, OVERRIDES_EDGE); if (siblingChildOverridesValue != null) { return siblingChildOverridesValue; } } } return graph.resolveVertexValue(vertex, defaultValueVertex); } function mapOperation(graph, vertex, values) { const [mapOperationVertex, mapValuesVertex] = values; const mapOperationValue = graph.getVertexValue(mapOperationVertex); const mapValues3 = graph.resolveVertexValue(vertex, mapValuesVertex); if (!Array.isArray(mapValues3)) return; const neighbours = graph.neighboursWithEdgeValue(vertex, PATH_EDGE); if (neighbours && neighbours.length > 0) { return; } let index = 0; for (const value of mapValues3) { graph.graftValue(vertex, `${index}`, mapOperationValue, value); index++; } return RESOLVED_TO_BRANCH; } function mergeOperation(graph, vertex, values) { for (const valueVertex of values) { const value = graph.resolveVertexValue(vertex, valueVertex); if (!isPlainObject(value)) continue; graph.graftObject(vertex, value); } return RESOLVED_TO_BRANCH; } function omitOperation(graph, vertex, values) { const [keysVertex, objectVertex] = values; let keys = graph.getVertexValue(keysVertex); if (!Array.isArray(keys)) { const targetVertex = getOperationTargetVertex(graph, vertex, objectVertex); if (!targetVertex) return; keys = graph.resolveVertexValue(targetVertex, keysVertex); } const object2 = graph.resolveVertexValue(vertex, objectVertex); if (!Array.isArray(keys) || !isPlainObject(object2)) return; return without(object2, keys); } function sizeOperation(graph, vertex, values) { const [valueVertex] = values; const value = graph.resolveVertexValue(vertex, valueVertex); if (!isObjectLike(value)) return 0; if ("length" in value) return value.length; return Object.keys(value).length; } function shallowSimpleOperation(graph, _vertex, values) { const shallowValues = []; for (const valueVertex of values) { shallowValues.push(graph.getVertexValue(valueVertex)); } return shallowValues; } function shallowOperation(graph, vertex, values) { const pathArray = graph.getPathArray(vertex); const hasUserOption = graph.hasUserOption(pathArray); if (!hasUserOption && values.length === 1) { return graph.resolveVertexValue(vertex, values[0]); } const shallowValues = []; for (const valueVertex of values) { shallowValues.push(graph.getVertexValue(valueVertex)); } if (hasUserOption) { graph.prune(vertex, [OVERRIDES_EDGE, DEFAULTS_EDGE]); return RESOLVED_TO_BRANCH; } graph.graftObject(vertex, shallowValues); return RESOLVED_TO_BRANCH; } function valueOperation(graph, vertex, values) { const [valueVertex] = values; const value = graph.getVertexValue(valueVertex); const pathArray = graph.getPathArray(vertex); if (value === "$index") { return getPathLastIndex(pathArray); } if (value === "$parentIndex") { return getPathLastIndex(pathArray, 1); } if (value === "$1") { return graph.resolveValue$1(pathArray); } } var numericOperations = { $isEven: isEvenOperation }; function isEvenOperation(graph, vertex, values) { const [valueVertex] = values; const value = graph.resolveVertexValue(vertex, valueVertex); if (Number.isNaN(Number(value))) return false; return Number(value) % 2 === 0; } var operations = { ...cacheOperations, ...chartOperations, ...colorOperations, ...fontOperations, ...locationOperations, ...logicOperations, ...numericOperations, ...transformOperations }; var operationTypes = new Set(Object.keys(operations)); function isOperation(value) { return operationTypes.has(value); } // packages/ag-charts-community/src/module/optionsGraph.ts var debug2 = debugLogger_exports.create("opts", "options-graph"); var createOptionsGraph = simpleMemorize(createOptionsGraphFn); function createOptionsGraphFn(theme, options) { return debug2.group("OptionsGraph.constructor()", () => { const optionsGraph = new OptionsGraph( theme.config, options, theme.params, theme.palette, theme.overrides, theme.getTemplateParameters() ); return { resolve() { return optionsGraph.resolve(); }, resolveParams() { return optionsGraph.resolveParams(); }, resolveAnnotationThemes() { return optionsGraph.resolveAnnotationThemes(); }, resolvePartial(path, partialOptions, resolveOptions) { return optionsGraph.resolvePartial(path, partialOptions, resolveOptions); }, clearSafe() { return optionsGraph.clearSafe(); } }; }); } var _OptionsGraph = class _OptionsGraph extends AdjacencyListGraph { constructor(config = {}, userOptions = {}, params = void 0, palette = {}, overrides = void 0, internalParams = /* @__PURE__ */ new Map()) { super(PATH_EDGE, OPERATION_EDGE, /* @__PURE__ */ new Set([USER_PARTIAL_OPTIONS_EDGE, USER_OPTIONS_EDGE])); this.config = config; this.userOptions = userOptions; this.palette = palette; this.overrides = overrides; this.internalParams = internalParams; // The current priority order in which to resolve options values. this.edgePriority = [..._OptionsGraph.EDGE_PRIORITY]; // The edge value to use when grafting new branches onto the graph from operations. this.graftEdge = _OptionsGraph.GRAFT_EDGE; this.resolvedParams = {}; this.resolvedAnnotations = {}; // The current value referenced by operations that use `$1`. this.value$1 = /* @__PURE__ */ new Map(); this.cachedPathVertices = /* @__PURE__ */ new Map(); this.hasUnsafeClearKeys = false; this.rollbackVertices = []; this.rollbackEdgesFrom = []; this.rollbackEdgesTo = []; this.rollbackEdgesValue = []; this.isRollingBack = false; // Records the already resolved root ancestors, i.e. vertices with a path of a single segment this.resolvedRootAncestorsPaths = /* @__PURE__ */ new Set(); this.EMPTY_PATH_ARRAY_VERTEX = this.addVertex([]); this.root = this.addVertex("root"); this.params = this.addVertex("params"); this.annotations = this.addVertex("annotations"); this.paletteType = isObject(userOptions?.theme) ? paletteType(userOptions.theme?.palette) : "inbuilt"; const seriesType = userOptions.series?.[0]?.type ?? "line"; debug2("build user"); this.buildGraphFromObject(this.root, USER_OPTIONS_EDGE, without(userOptions, ["theme"])); debug2("build defaults"); this.buildGraphFromObject(this.root, DEFAULTS_EDGE, without(config[seriesType], _OptionsGraph.COMPLEX_KEYS)); const seriesOverrides = overrides ? without(overrides[seriesType], _OptionsGraph.COMPLEX_KEYS) : {}; if (Object.keys(seriesOverrides).length > 0) { debug2("build series overrides"); this.buildGraphFromObject(this.root, OVERRIDES_EDGE, seriesOverrides); } const commonOverrides = overrides ? without(overrides.common, _OptionsGraph.COMPLEX_KEYS) : {}; if (Object.keys(commonOverrides).length > 0) { debug2("build common overrides"); this.buildGraphFromObject( this.root, OVERRIDES_EDGE, moduleRegistry_exports.getSeriesModule(seriesType)?.chartType === "cartesian" ? commonOverrides : without(commonOverrides, ["zoom", "navigator"]) ); } if (params) { debug2("build params"); this.buildGraphFromObject(this.params, DEFAULTS_EDGE, params); } const axesVertex = this.findNeighbourWithValue(this.root, "axes", PATH_EDGE); const seriesVertex = this.findNeighbourWithValue(this.root, "series", PATH_EDGE); if (axesVertex) { debug2("build axes"); this.buildGraphFromObject(axesVertex, DEFAULTS_EDGE, { $applyTheme: [ ["/$seriesType/axes/$axisType/$position", "/$seriesType/axes/$axisType"], { seriesType: { $path: ["/series/0/type", "line"] }, axisType: { $path: ["./type", "category"] }, position: { $path: ["./position"] } }, ["top", "right", "bottom", "left"] ] }); } if (seriesVertex) { debug2("build series"); this.buildGraphFromObject(seriesVertex, DEFAULTS_EDGE, { $applyTheme: ["/$seriesType/series", { seriesType: { $path: ["./type", "line"] } }] }); } const annotationsTypeConfig = without( config[seriesType]?.annotations ?? {}, _OptionsGraph.ANNOTATIONS_OPTIONS_KEYS ); if (Object.keys(annotationsTypeConfig).length > 0) { debug2("build annotations type config"); this.buildGraphFromObject(this.annotations, DEFAULTS_EDGE, annotationsTypeConfig); } const annotationsTypeOverrides = without( overrides?.common?.annotations ?? {}, _OptionsGraph.ANNOTATIONS_OPTIONS_KEYS ); if (Object.keys(annotationsTypeOverrides).length > 0) { debug2("build annotations type overrides"); this.buildGraphFromObject(this.annotations, OVERRIDES_EDGE, annotationsTypeOverrides); } const annotationsConfig = pick(config[seriesType]?.annotations ?? {}, _OptionsGraph.ANNOTATIONS_OPTIONS_KEYS); if (Object.keys(annotationsConfig).length > 0) { debug2("build annotations config"); this.buildGraphFromObject(this.root, DEFAULTS_EDGE, { annotations: annotationsConfig }); } const annotationsOverrides = pick(overrides?.common?.annotations ?? {}, _OptionsGraph.ANNOTATIONS_OPTIONS_KEYS); if (Object.keys(annotationsOverrides).length > 0) { debug2("build annotations overrides"); this.buildGraphFromObject(this.root, OVERRIDES_EDGE, { annotations: annotationsOverrides }); } this.buildDependencyGraph(); } static clearValueCache() { _OptionsGraph.valueCache.clear(); } clear() { debug2.group("OptionsGraph.clear()", () => { super.clear(); this.cachedPathVertices.clear(); this.root = void 0; this.params = void 0; this.annotations = void 0; debug2("cleared"); }); } clearSafe() { if (this.hasUnsafeClearKeys) return; this.clear(); } resolve() { return debug2.group("OptionsGraph.resolve()", () => { this.resolved = {}; this.resolvedParams = {}; this.resolvedAnnotations = {}; debug2("resolve params"); this.resolveVertex(this.params, this.resolvedParams); debug2("resolve annotations"); this.resolveVertex(this.annotations, this.resolvedAnnotations); debug2("resolve root"); this.resolveVertex(this.root); debug2("resolved root", this.resolved); debug2("vertex count", this.getVertexCount()); debug2("edge count", this.getEdgeCount()); return this.resolved; }); } resolveParams() { return this.resolvedParams; } resolveAnnotationThemes() { return this.resolvedAnnotations; } addVertex(value) { const vertex = super.addVertex(value); if (this.isRollingBack) { this.rollbackVertices.push(vertex); } return vertex; } addEdge(from3, to, edge) { const hasEdge = (this.neighboursWithEdgeValue(from3, edge)?.indexOf(to) ?? -1) !== -1; if (this.isRollingBack && !hasEdge) { this.rollbackEdgesFrom.push(from3); this.rollbackEdgesTo.push(to); this.rollbackEdgesValue.push(edge); } super.addEdge(from3, to, edge); } /** * Resolve partial options against the existing graph at a given path without overriding the existing user values. * Returns an object with only those keys that were also present within `partialOptions`. */ resolvePartial(path, partialOptions, resolveOptions) { if (!partialOptions) return; if (!this.root) return; const { permissivePath, proxyPaths } = resolveOptions ?? {}; const partialKeys = Object.keys(partialOptions); if (debug2.check()) { console.groupCollapsed(`OptionsGraph.resolvePartial() - ${path.join(".")} [${partialKeys}]`); } if (partialKeys.length === 0) return {}; const parentVertex = this.findVertexAtPath(path); if (!parentVertex) { if (permissivePath) { return void 0; } else { throw new Error(`Could not find vertex in OptionsGraph at path [${path.join(".")}].`); } } const pathArrayVertex = this.findNeighbour(parentVertex, PATH_ARRAY_EDGE); this.userPartialOptions = {}; setPathSafe(this.userPartialOptions, path, partialOptions); if (proxyPaths) { for (const proxyFrom of Object.keys(proxyPaths)) { const proxyTo = proxyPaths[proxyFrom]; const proxyValue = getPathSafe(partialOptions, [proxyFrom]); if (proxyValue != null) { setPathSafe(partialOptions, proxyTo, proxyValue); setPathSafe(this.userPartialOptions, [...path, ...proxyTo], proxyValue); delete partialOptions[proxyFrom]; delete this.userPartialOptions[proxyFrom]; } } } this.graftEdge = USER_PARTIAL_OPTIONS_EDGE; this.edgePriority = [USER_PARTIAL_OPTIONS_EDGE, ..._OptionsGraph.EDGE_PRIORITY]; this.snapshot(); this.buildGraphFromObject(parentVertex, USER_PARTIAL_OPTIONS_EDGE, partialOptions, pathArrayVertex); for (const key of partialKeys) { const childVertex = proxyPaths?.[key] ? this.findVertexAtPath([...path, ...proxyPaths[key]]) : this.findNeighbourWithValue(parentVertex, key, PATH_EDGE); if (childVertex) { this.refreshPendingProcessingEdges(childVertex); } } this.buildDependencyGraph(); const resolved = {}; this.resolveVertex(parentVertex, resolved); this.rollback(); this.graftEdge = _OptionsGraph.GRAFT_EDGE; this.edgePriority = _OptionsGraph.EDGE_PRIORITY; this.userPartialOptions = void 0; if (proxyPaths) { for (const proxyFrom of Object.keys(proxyPaths)) { const proxyTo = proxyPaths[proxyFrom]; const proxyValue = getPathSafe(resolved, [...path, ...proxyTo]); setPathSafe(resolved, [...path, proxyFrom], proxyValue); } } const pathed = getPathSafe(resolved, path); const shouldPick = resolveOptions?.pick ?? true; const partial = shouldPick ? pick(getPathSafe(resolved, path), partialKeys) : pathed; debug2("vertex count", this.getVertexCount()); debug2("edge count", this.getEdgeCount()); debug2("resolved partial", partial); if (debug2.check()) { console.groupEnd(); } return partial; } findVertexAtPath(path) { const key = path.join("."); if (this.cachedPathVertices.has(key)) { return this.cachedPathVertices.get(key); } const vertex = this.findVertexAlongEdge(this.root, path, PATH_EDGE); if (!vertex) return; this.cachedPathVertices.set(key, vertex); return vertex; } hasUserOption(path) { const hasUserOptionSimple = hasPathSafe(this.userOptions, path); if (hasUserOptionSimple) return true; const pathVertex = this.findVertexAtPath(path); if (pathVertex) { if (this.findNeighbour(pathVertex, USER_OPTIONS_EDGE) != null) return true; if (this.findNeighbour(pathVertex, USER_PARTIAL_OPTIONS_EDGE) != null) return true; const childrenSource = this.findNeighbourValue(pathVertex, CHILDREN_SOURCE_EDGE); return childrenSource === USER_OPTIONS_EDGE || childrenSource === USER_PARTIAL_OPTIONS_EDGE; } return false; } /** * Get the value from the user options at the given path. This method is dangerous since it does not resolve * through the graph, however is useful for operations that operate on their own path where attempting to * resolve would cause an infinite loop. */ dangerouslyGetUserOption(path) { if (this.userPartialOptions) { const value = getPathSafe(this.userPartialOptions, path); if (value != null) return value; } return getPathSafe(this.userOptions, path); } hasThemeOverride(path) { if (this.overrides == null) return false; if (path[0] === "axes" && path.length > 1) { const axisType = this.getResolvedPath(["axes", path[1], "type"]); if (hasPathSafe(this.overrides, ["common", "axes", axisType, ...path.slice(2)])) { return true; } const seriesType = this.getResolvedPath(["series", "0", "type"]); return hasPathSafe(this.overrides, [seriesType, "axes", axisType, ...path.slice(2)]); } if (path[0] === "series" && path.length > 1) { const seriesType = this.getResolvedPath(["series", path[1], "type"]); return hasPathSafe(this.overrides, [seriesType, "series", ...path.slice(2)]); } return hasPathSafe(this.overrides, path); } getParamValue(path) { if (this.resolvedParams[path] != null) { return this.resolvedParams[path]; } const paramVertex = this.findVertexAlongEdge(this.params, [path], PATH_EDGE); if (!paramVertex) return; const defaultValueVertex = this.findNeighbour(paramVertex, DEFAULTS_EDGE); if (!defaultValueVertex) return; const resolved = this.resolveVertexValue(paramVertex, defaultValueVertex); this.resolvedParams[path] = resolved; return resolved; } getPathArray(vertex) { return this.findNeighbourValue(vertex, PATH_ARRAY_EDGE) ?? []; } getResolvedPath(path) { return getPathSafe(this.resolved, path); } getCachedValue(path, key) { const cacheKey = [...path, key].join("."); return _OptionsGraph.valueCache.get(cacheKey); } setCachedValue(path, key, value) { const cacheKey = [...path, key].join("."); _OptionsGraph.valueCache.set(cacheKey, value); } prune(vertex, edges) { this.addEdge(vertex, this.addVertex(edges), PRUNE_EDGE); } resolveVertexValue(vertex, valueVertex) { this.resolveVertexDependencies(valueVertex); const operation = this.findNeighbourValue(valueVertex, OPERATION_EDGE); if (operation && isOperation(operation)) { const operationValues = this.neighboursWithEdgeValue(valueVertex, OPERATION_VALUE_EDGE); const operator = operations[operation]; const operatorFn = typeof operator === "function" ? operator : operator.resolve; const resolved = operatorFn?.(this, vertex, operationValues ?? []); return resolved === RESOLVED_TO_BRANCH ? void 0 : resolved; } let value = this.getVertexValue(valueVertex); if (Array.isArray(value)) { const object2 = {}; this.resolveVertexChildren(valueVertex, object2); value = getPathSafe(object2, this.getPathArray(vertex)); } return this.resolveValueOrSymbol(value); } /** * Resolve the value currently referenced by `$1` by the nearest self-or-ancestor that has a defined value. */ resolveValue$1(pathArray) { for (let i = pathArray.length; i >= 0; i--) { const key = pathArray.slice(0, i).join("."); const resolvedValue = this.value$1.get(key); if (resolvedValue != void 0) { return resolvedValue; } } } /** * Graft a branch of the theme config onto the target vertex. */ graftConfig(target, configPathArray, ignorePaths) { const targetConfigObject = getPathSafe(this.config, configPathArray); const targetPathArrayVertex = this.findNeighbour(target, PATH_ARRAY_EDGE); if (isObject(targetConfigObject)) { this.buildGraphFromObject( target, DEFAULTS_EDGE, targetConfigObject, targetPathArrayVertex, void 0, ignorePaths ); } if (this.overrides) { const targetOverridesObject = getPathSafe(this.overrides, configPathArray); if (isObject(targetOverridesObject)) { this.buildGraphFromObject( target, OVERRIDES_EDGE, targetOverridesObject, targetPathArrayVertex, void 0, ignorePaths ); } const commonOverridesObject = getPathSafe(this.overrides, ["common", ...configPathArray.slice(1)]); if (isObject(commonOverridesObject)) { this.buildGraphFromObject( target, OVERRIDES_EDGE, commonOverridesObject, targetPathArrayVertex, void 0, ignorePaths ); } } this.buildDependencyGraph(); } /** * Graft a given object onto the target vertex. */ graftObject(target, object2, overridesPathArrays, edgeValue = this.graftEdge) { const pathArrayVertex = this.findNeighbour(target, PATH_ARRAY_EDGE); this.buildGraphFromObject(target, edgeValue, object2, pathArrayVertex); if (this.overrides && overridesPathArrays) { for (const overridePathArray of overridesPathArrays) { if (overridePathArray == null) continue; const overrides = getPathSafe(this.overrides, overridePathArray); if (overrides) { this.buildGraphFromObject(target, OVERRIDES_EDGE, overrides, pathArrayVertex); } } } this.buildDependencyGraph(); } /** * Graft a given operation and value onto `path` child of the target vertex. The `ontoObject` value is built onto * the graph each time this function is called, at the given path, while `value` is used for value$1 where * `ontoObject` is an operation that invokes value$1. */ graftValue(target, path, ontoObject, value, edgeValue = this.graftEdge) { const pathArray = [...this.getPathArray(target), path]; const pathVertex = this.findVertexAtPath(pathArray) ?? this.addVertex(path); this.value$1.set(pathArray.join("."), value); this.buildGraphFromValue(target, pathVertex, edgeValue, pathArray, ontoObject); this.buildDependencyGraph(); } /** * Resolve a branch as if it were a child of the context vertex, but without attaching it to the resolved root. */ graftAndResolveOrphan(context, branch) { const orphan = {}; const orphanVertex = this.addVertex(orphan); const contextPathArray = this.getPathArray(context); this.graftAndResolveChildren(branch, orphanVertex, contextPathArray, []); this.resolveVertex(orphanVertex, orphan); return getPathSafe(orphan, contextPathArray); } /** * Resolve a value as if it were a child of the context vertex, but without attaching it to the resolved root. */ graftAndResolveOrphanValue(context, path, ontoObject, value, edgeValue = this.graftEdge) { const orphan = {}; const orphanVertex = this.addVertex(orphan); const contextPathArray = this.getPathArray(context); const pathArray = [...contextPathArray, path]; const pathVertex = this.findVertexAtPath(pathArray) ?? this.addVertex(path); this.value$1.set(pathArray.join("."), value); this.buildGraphFromValue(orphanVertex, pathVertex, edgeValue, pathArray, ontoObject); this.resolveVertex(orphanVertex, orphan); return getPathSafe(orphan, pathArray); } buildGraphFromObject(parentVertex, edgeValue, object2, pathArrayVertex, shallowPaths = _OptionsGraph.SHALLOW_KEYS, ignorePaths) { const keys = Object.keys(object2); const operation = getOperation(object2, keys); if (operation) { const valueVertex = this.addVertex(object2); this.addEdge(parentVertex, valueVertex, edgeValue); this.buildGraphFromOperation(valueVertex, edgeValue, operation, pathArrayVertex); return; } if (keys.length === 0) { this.addEdge(parentVertex, this.addVertex(Array.isArray(object2) ? [] : {}), edgeValue); this.buildGraphAutoEnable(parentVertex, edgeValue, object2, void 0); return; } const pathVertices = this.getVertexChildrenByKey(parentVertex); const pathArray = pathArrayVertex ? this.getVertexValue(pathArrayVertex) : []; let enabledVertex; if (Array.isArray(object2)) { this.addEdge(parentVertex, this.addVertex(edgeValue), CHILDREN_SOURCE_EDGE); } const childPathArray = [...pathArray]; const pathArrayLength = pathArray.length; for (const key of keys) { if (ignorePaths?.has(key)) continue; const childPathVertex = pathVertices?.get(key) ?? this.addVertex(key); childPathArray[pathArrayLength] = key; if (shallowPaths?.has(key)) { this.buildShallowGraphFromValue(parentVertex, childPathVertex, edgeValue, childPathArray, object2[key]); } else { this.buildGraphFromValue( parentVertex, childPathVertex, edgeValue, childPathArray, object2[key], shallowPaths ); } if (key === "enabled") { enabledVertex = childPathVertex; } } this.buildGraphAutoEnable(parentVertex, edgeValue, object2, enabledVertex); } buildGraphAutoEnable(parentVertex, edgeValue, object2, enabledVertex) { if (edgeValue === DEFAULTS_EDGE && !enabledVertex) return; if (edgeValue === USER_OPTIONS_EDGE && enabledVertex) return; if (edgeValue !== DEFAULTS_EDGE && edgeValue !== USER_OPTIONS_EDGE && edgeValue !== USER_PARTIAL_OPTIONS_EDGE && edgeValue !== OVERRIDES_EDGE) return; let autoEnableVertex = this.findNeighbour(parentVertex, AUTO_ENABLE_EDGE); if (!autoEnableVertex) { autoEnableVertex = this.addVertex(AUTO_ENABLE_EDGE); this.addEdge(parentVertex, autoEnableVertex, AUTO_ENABLE_EDGE); } if (enabledVertex) { this.addEdge(enabledVertex, autoEnableVertex, AUTO_ENABLE_VALUE_EDGE); } const { enabled, _enabledFromTheme } = object2; this.addEdge( autoEnableVertex, this.addVertex({ enabled, _enabledFromTheme, keys: Object.keys(object2).length }), edgeValue ); } getVertexChildrenByKey(vertex) { const pathNeighbours = this.neighboursWithEdgeValue(vertex, PATH_EDGE); if (!pathNeighbours) return; const pathVertices = /* @__PURE__ */ new Map(); for (const neighbour of pathNeighbours) { pathVertices.set(this.getVertexValue(neighbour), neighbour); } return pathVertices; } buildGraphFromValue(parentVertex, pathVertex, edgeValue, pathArray, value, shallowPaths) { this.addEdge(parentVertex, pathVertex, PATH_EDGE); let pathArrayVertex = this.findNeighbour(pathVertex, PATH_ARRAY_EDGE); if (!pathArrayVertex) { pathArrayVertex = this.addVertex([...pathArray]); this.addEdge(pathVertex, pathArrayVertex, PATH_ARRAY_EDGE); } const operation = getOperation(value); if (operation) { const valueVertex = this.addVertex(value); this.addEdge(pathVertex, valueVertex, edgeValue); this.addEdge(valueVertex, pathArrayVertex, PATH_ARRAY_EDGE); this.buildGraphFromOperation(valueVertex, edgeValue, operation, pathArrayVertex); } else if (isObjectLike(value)) { this.buildGraphFromObject(pathVertex, edgeValue, value, pathArrayVertex, shallowPaths); } else { const neighbour = this.findNeighbour(pathVertex, edgeValue); if (neighbour && this.getVertexValue(neighbour) === value) { return; } const valueVertex = this.addVertex(value); this.addEdge(pathVertex, valueVertex, edgeValue); } } buildShallowGraphFromValue(parentVertex, pathVertex, edgeValue, pathArray, value) { this.addEdge(parentVertex, pathVertex, PATH_EDGE); let pathArrayVertex = this.findNeighbour(pathVertex, PATH_ARRAY_EDGE); if (!pathArrayVertex) { pathArrayVertex = this.addVertex([...pathArray]); this.addEdge(pathVertex, pathArrayVertex, PATH_ARRAY_EDGE); } const valueVertex = this.addVertex(value); this.addEdge(pathVertex, valueVertex, edgeValue); } buildGraphFromOperation(valueVertex, edgeValue, operation, pathArrayVertex) { const operationVertex = this.addVertex(operation.operation); this.addEdge(valueVertex, operationVertex, OPERATION_EDGE); for (const operationValue of operation.values) { this.buildGraphFromOperationValue(valueVertex, operationValue, edgeValue, pathArrayVertex); } } buildGraphFromOperationValue(valueVertex, operationValue, edgeValue, pathArrayVertex = this.EMPTY_PATH_ARRAY_VERTEX) { const operationValueVertex = this.addVertex(operationValue); this.addEdge(valueVertex, pathArrayVertex, PATH_ARRAY_EDGE); this.addEdge(valueVertex, operationValueVertex, OPERATION_VALUE_EDGE); const innerOperation = getOperation(operationValue); if (innerOperation) { this.buildGraphFromOperation(operationValueVertex, edgeValue, innerOperation, pathArrayVertex); } else if (isObjectLike(operationValue)) { this.buildGraphFromObject(operationValueVertex, edgeValue, operationValue, pathArrayVertex); } } /** * Add dependency edges on operations that require other vertices to be resolved before the operation can be * resolved. Then clear the list of pending edges. */ buildDependencyGraph() { for (let i = 0; i < this.pendingProcessingEdgesFrom.length; i++) { const valueVertex = this.pendingProcessingEdgesFrom[i]; const operationKeyVertex = this.pendingProcessingEdgesTo[i]; const operation = this.getVertexValue(operationKeyVertex); if (!isOperation(operation)) continue; const operationValues = this.neighboursWithEdgeValue(valueVertex, OPERATION_VALUE_EDGE); const operator = operations[operation]; const dependenciesFn = typeof operator === "function" ? void 0 : operator.dependencies; dependenciesFn?.(this, valueVertex, operationValues ?? []); } this.pendingProcessingEdgesFrom = []; this.pendingProcessingEdgesTo = []; } /** * Within the branch starting at the given vertex, reassign any value vertices and their operation key vertices to * the pending processing edges. These can then be built with `this.buildDependencyGraph()`. */ refreshPendingProcessingEdges(vertex) { const defaultValueVertex = this.findNeighbour(vertex, DEFAULTS_EDGE); const valueVertex = defaultValueVertex ?? vertex; const operationVertex = this.findNeighbour(valueVertex, OPERATION_EDGE); if (operationVertex) { this.pendingProcessingEdgesFrom.push(valueVertex); this.pendingProcessingEdgesTo.push(operationVertex); const neighbours2 = this.neighboursWithEdgeValue(valueVertex, OPERATION_VALUE_EDGE); if (neighbours2) { for (const neighbour of neighbours2) { this.refreshPendingProcessingEdges(neighbour); } } } const neighbours = this.neighboursWithEdgeValue(vertex, PATH_EDGE); if (neighbours) { for (const neighbour of neighbours) { this.refreshPendingProcessingEdges(neighbour); } } } resolveVertex(vertex, object2 = this.resolved, prune) { const pathArray = this.getPathArray(vertex); const rootAncestorPath = pathArray[0]; if (pathArray.length === 1) { this.resolvedRootAncestorsPaths.add(rootAncestorPath); } if (pathArray.length > 1 && !this.resolvedRootAncestorsPaths.has(rootAncestorPath)) { const rootAncestorVertex = this.findVertexAtPath([rootAncestorPath]); if (rootAncestorVertex) { this.resolveVertex(rootAncestorVertex, object2, prune); return; } } if (this.userPartialOptions == null && object2 === this.resolved && pathArray.length > 0) { const resolvedVertexValue = getPathSafe(object2, pathArray); if (resolvedVertexValue != null && !isPlainObject(resolvedVertexValue)) { return; } } this.resolveVertexInEdgePriority(vertex, object2, pathArray, prune); this.resolveVertexAutoEnable(vertex, object2, pathArray); this.resolveVertexChildren(vertex, object2, prune); } resolveVertexInEdgePriority(vertex, object2, pathArray, prune) { const children = this.neighboursWithEdgeValue(vertex, PATH_EDGE); const [highestPriority] = this.edgePriority; for (const edgeValue of this.edgePriority) { const valueVertex = this.findNeighbour(vertex, edgeValue); if (valueVertex == null) continue; const value = this.resolveVertexValueInternal(vertex, valueVertex); if (value == null && edgeValue !== highestPriority) continue; if (children && children.length > 0 && edgeValue !== highestPriority) continue; if (Array.isArray(prune) && prune.includes(edgeValue)) continue; this.hasUnsafeClearKeys || (this.hasUnsafeClearKeys = value != null && _OptionsGraph.UNSAFE_CLEAR_KEYS.has(pathArray.at(-1))); if (pathArray.length === 0) { if (value == null) continue; this.resolved = value; } else { setPathSafe(object2, pathArray, value); } break; } } resolveVertexValueInternal(vertex, valueVertex) { this.resolveVertexDependencies(valueVertex); const operation = this.findNeighbourValue(valueVertex, OPERATION_EDGE); if (operation && isOperation(operation)) { const operationValues = this.neighboursWithEdgeValue(valueVertex, OPERATION_VALUE_EDGE); const operator = operations[operation]; const operatorFn = typeof operator === "function" ? operator : operator.resolve; const resolved = operatorFn?.(this, vertex, operationValues ?? []); return resolved === RESOLVED_TO_BRANCH ? void 0 : resolved; } return this.resolveValueOrSymbol(this.getVertexValue(valueVertex)); } resolveVertexAutoEnable(vertex, object2, pathArray) { const autoEnableValueVertex = this.neighboursWithEdgeValue(vertex, AUTO_ENABLE_VALUE_EDGE)?.[0]; if (!autoEnableValueVertex) return; const pathVertex = this.findVertexAtPath(pathArray); const defaultsEnabled = this.findNeighbourValue(autoEnableValueVertex, DEFAULTS_EDGE); const overridesEnabled = this.findNeighbourValue(autoEnableValueVertex, OVERRIDES_EDGE); const userOptionsEnabled = this.findNeighbourValue(autoEnableValueVertex, USER_OPTIONS_EDGE); const hasUserOptionEnabled = pathVertex && this.findNeighbour(pathVertex, USER_OPTIONS_EDGE) != null; const userPartialOptionsEnabled = hasUserOptionEnabled ? void 0 : this.findNeighbourValue(autoEnableValueVertex, USER_PARTIAL_OPTIONS_EDGE); const isUserEnabled = userOptionsEnabled != null && userOptionsEnabled.enabled == null || userPartialOptionsEnabled != null && userPartialOptionsEnabled.enabled == null; if (isUserEnabled && !defaultsEnabled?._enabledFromTheme && !overridesEnabled?._enabledFromTheme) { setPathSafe(object2, pathArray, true); } } resolveVertexChildren(vertex, object2, prune) { const children = this.neighboursWithEdgeValue(vertex, PATH_EDGE); if (!children) return; prune ?? (prune = this.findNeighbourValue(vertex, PRUNE_EDGE)); for (const child of children) { const path = this.getVertexValue(child); if (children.length > 1 && isOperation(path)) continue; if (path === "_enabledFromTheme") continue; this.resolveVertex(child, object2, prune); } } resolveVertexDependencies(vertex) { const dependencies = this.neighboursWithEdgeValue(vertex, DEPENDENCY_EDGE); if (!dependencies) return; for (const dependency of dependencies) { this.resolveVertex(dependency); } } graftAndResolveChildren(remoteBranch, orphanBranch, contextPathArray, orphanPathArray) { const remoteChildren = this.neighboursWithEdgeValue(remoteBranch, PATH_EDGE); if (!remoteChildren) return; for (const remoteChild of remoteChildren) { const remoteChildPath = this.getVertexValue(remoteChild); const childContextPathArray = [...contextPathArray, remoteChildPath]; const childOrphanPathArray = [...orphanPathArray, remoteChildPath]; const orphanChildPathVertex = this.addVertex(remoteChildPath); const defaultValue = this.findNeighbourValue(remoteChild, DEFAULTS_EDGE); this.addEdge(orphanBranch, orphanChildPathVertex, PATH_EDGE); const orphanChildPathArrayVertex = this.addVertex(childContextPathArray); this.addEdge(orphanChildPathVertex, orphanChildPathArrayVertex, PATH_ARRAY_EDGE); if (isObject(defaultValue)) { this.buildGraphFromObject( orphanChildPathVertex, DEFAULTS_EDGE, defaultValue, orphanChildPathArrayVertex ); const orphanChildValueVertex = this.findNeighbour(orphanChildPathVertex, DEFAULTS_EDGE); this.addEdge(orphanChildValueVertex, this.addVertex(childContextPathArray), PATH_ARRAY_EDGE); const operation = this.findNeighbourValue(orphanChildValueVertex, OPERATION_EDGE); if (isOperation(operation)) { const operationValues = this.neighboursWithEdgeValue(orphanChildValueVertex, OPERATION_VALUE_EDGE); const operator = operations[operation]; const dependenciesFn = typeof operator === "function" ? void 0 : operator.dependencies; dependenciesFn?.(this, orphanChildValueVertex, operationValues ?? []); } } this.graftAndResolveChildren( remoteChild, orphanChildPathVertex, childContextPathArray, childOrphanPathArray ); } } resolveValueOrSymbol(value) { return typeof value === "symbol" && this.internalParams?.has(value) ? this.internalParams.get(value) : value; } snapshot() { debug2(`snapshot`); this.isRollingBack = true; } rollback() { debug2(`rollback ${this.rollbackEdgesFrom.length} edges and ${this.rollbackVertices.length} vertices`); for (let i = 0; i < this.rollbackEdgesFrom.length; i++) { const from3 = this.rollbackEdgesFrom[i]; const to = this.rollbackEdgesTo[i]; const edgeValue = this.rollbackEdgesValue[i]; this.removeEdge(from3, to, edgeValue); } for (const vertex of this.rollbackVertices) { this.removeVertex(vertex); } this.cachedPathVertices.clear(); this.rollbackVertices = []; this.rollbackEdgesFrom = []; this.rollbackEdgesTo = []; this.rollbackEdgesValue = []; this.isRollingBack = false; } /** * Console log a flowchart diagram of the graph at the given path. */ diagram(pathArray, maxDepth = 2) { this.diagramKeys = /* @__PURE__ */ new Map(); this.diagramEdges = /* @__PURE__ */ new Map(); const vertex = this.findVertexAtPath(pathArray); const diagram = [ "---", "config:", " layout: elk", " look: neo", " theme: redux", "---", "flowchart TB" ]; if (vertex) { this.diagramVertex(diagram, vertex, 1, maxDepth); } diagram.push("classDef UO fill: #e8f5e8, stroke: #4caf50"); diagram.push("classDef DE fill: #e3f2fd, stroke: #2196f3"); diagram.push("classDef DEP fill: #ffe0fd, stroke: #ff00f2"); diagram.push("classDef OP fill: #fff3e0, stroke: #ff9800"); diagram.push("classDef OPV fill: #fff3e0, stroke: #ff9800, stroke-width: 1px"); diagram.push("classDef OV fill: #e8f5ee, stroke: #4caf87"); console.log(diagram.join("\n")); } diagramKey(path) { let diagramKey = this.diagramKeys.get(path); if (!diagramKey) { diagramKey = `${this.diagramKeys.size}`; this.diagramKeys.set(path, diagramKey); } return diagramKey; } diagramLabel(path, vertex, edge) { let diagramKey = this.diagramKeys.get(path); if (diagramKey) return diagramKey; diagramKey = this.diagramKey(path); const classNames = { [USER_OPTIONS_EDGE]: "UO", [DEFAULTS_EDGE]: "DE", [DEPENDENCY_EDGE]: "DEP", [OPERATION_EDGE]: "OP", [OPERATION_VALUE_EDGE]: "OPV", [OVERRIDES_EDGE]: "OV" }; let className = edge ? classNames[edge] ?? void 0 : void 0; className = className ? `:::${className}` : ""; if (typeof vertex.value === "symbol") { return `${diagramKey}[/"[symbol]"\\]${className}`; } else if (Array.isArray(vertex.value)) { return `${diagramKey}[/"[array]"\\]${className}`; } else if (typeof vertex.value === "object") { return `${diagramKey}[/"[object]"\\]${className}`; } else if (edge === DEFAULTS_EDGE || edge === USER_OPTIONS_EDGE || edge === OVERRIDES_EDGE) { return `${diagramKey}("${vertex.value}")${className}`; } else { return `${diagramKey}["${vertex.value}"]${className}`; } } diagramVertex(diagram, vertex, depth, maxDepth) { const pathArray = this.getPathArray(vertex); const path = pathArray.length > 0 ? pathArray.join(".") : "root"; this.diagramNeighbours(diagram, path, vertex, depth + 1, maxDepth); let diagramKey = this.diagramKeys.get(path); if (!diagramKey) { diagramKey = this.diagramKey(path); diagram.push(` ${diagramKey}["${vertex.value}"]`); } } diagramNeighbours(diagram, path, vertex, depth, maxDepth) { for (const neighbour of this.neighboursWithEdgeValue(vertex, PATH_EDGE) ?? []) { const neighbourPathArray = this.getPathArray(neighbour); const neighbourPath = neighbourPathArray.length > 0 ? neighbourPathArray.join(".") : "root"; if (depth < maxDepth) { this.diagramVertex(diagram, neighbour, depth + 1, maxDepth); } this.diagramChild(diagram, PATH_EDGE, path, vertex, neighbourPath, vertex); } const userValues = this.neighboursWithEdgeValue(vertex, USER_OPTIONS_EDGE) ?? []; let index = 0; for (const userValue of userValues) { this.diagramChild( diagram, USER_OPTIONS_EDGE, path, vertex, `${path}.${USER_OPTIONS_EDGE}.${index}`, userValue ); index++; } const defaultValues = this.neighboursWithEdgeValue(vertex, DEFAULTS_EDGE) ?? []; index = 0; for (const defaultValue of defaultValues) { this.diagramChildWithNeighbours( diagram, DEFAULTS_EDGE, path, vertex, `${path}.${DEFAULTS_EDGE}.${index}`, defaultValue, depth + 1, maxDepth ); index++; } const operationVertices = this.neighboursWithEdgeValue(vertex, OPERATION_EDGE) ?? []; index = 0; const [operation] = operationVertices; if (operation) { this.diagramChildWithNeighbours( diagram, OPERATION_EDGE, path, vertex, `${path}.${OPERATION_EDGE}.${index}`, operation, depth + 1, maxDepth ); index++; } const operationValueVertices = this.neighboursWithEdgeValue(vertex, OPERATION_VALUE_EDGE) ?? []; index = 0; for (const operationValue of operationValueVertices) { this.diagramChildWithNeighbours( diagram, OPERATION_VALUE_EDGE, path, vertex, `${path}.${OPERATION_VALUE_EDGE}.${index}`, operationValue, depth + 1, maxDepth ); index++; } const dependencyVertices = this.neighboursWithEdgeValue(vertex, DEPENDENCY_EDGE) ?? []; index = 0; for (const dependency of dependencyVertices) { this.diagramChildWithNeighbours( diagram, DEPENDENCY_EDGE, path, vertex, this.getPathArray(dependency).join("."), dependency, depth + 1, maxDepth ); index++; } } diagramChild(diagram, edge, parentPath, parentVertex, childPath, childVertex) { let edges = this.diagramEdges.get(parentPath); if (edges?.has(childPath)) return; if (!edges) { edges = /* @__PURE__ */ new Set(); this.diagramEdges.set(parentPath, edges); } edges.add(childPath); const edgeString = edge === PATH_EDGE ? "" : `|${edge}|`; diagram.push( ` ${this.diagramLabel(parentPath, parentVertex)} -->${edgeString} ${this.diagramLabel(childPath, childVertex, edge)}` ); } diagramChildWithNeighbours(diagram, edge, parentPath, parentVertex, childPath, childVertex, depth, maxDepth) { this.diagramChild(diagram, edge, parentPath, parentVertex, childPath, childVertex); this.diagramNeighbours(diagram, childPath, childVertex, depth + 1, maxDepth); } }; // The default priority order in which to resolve options values. _OptionsGraph.EDGE_PRIORITY = [USER_OPTIONS_EDGE, OVERRIDES_EDGE, DEFAULTS_EDGE]; _OptionsGraph.GRAFT_EDGE = DEFAULTS_EDGE; // These keys must be stored as shallow objects in the graph and not manipulated. _OptionsGraph.SHALLOW_KEYS = /* @__PURE__ */ new Set(["context", "data", "topology"]); // These keys must be excluded when building the graph, they are instead resolved separately since they are objects // that must be applied to arrays. _OptionsGraph.COMPLEX_KEYS = ["annotations", "axes", "series"]; _OptionsGraph.ANNOTATIONS_OPTIONS_KEYS = [ "axesButtons", "data", "enabled", "optionsToolbar", "snap", "toolbar", "xKey", "volumeKey" ]; // If any of these keys are present in the resolved object then calling `clearSafe()` will not clear the graph. _OptionsGraph.UNSAFE_CLEAR_KEYS = /* @__PURE__ */ new Set(["itemStyler", "styler"]); // A cache of values that persists between chart updates, use sparingly. _OptionsGraph.valueCache = /* @__PURE__ */ new Map(); var OptionsGraph = _OptionsGraph; // packages/ag-charts-community/src/module/optionsModule.ts var stringFormat = (value) => `'${value}'`; var AXIS_ID_PREFIX = "__AXIS_ID_"; var POSITION_DIRECTIONS = { top: "x" /* X */, bottom: "x" /* X */, left: "y" /* Y */, right: "y" /* Y */ }; var _ChartOptions = class _ChartOptions { constructor(currentUserOptions, newUserOptions, processedOverrides, specialOverrides, metadata, deltaOptions, stripSymbols = false, apiStartTime) { this.themeParameters = {}; this.optionMetadata = metadata ?? {}; this.processedOverrides = processedOverrides ?? {}; let baseChartOptions = null; if (currentUserOptions instanceof _ChartOptions) { baseChartOptions = currentUserOptions; this.specialOverrides = baseChartOptions.specialOverrides; if (deltaOptions) { this.userDeltaKeys = new Set(Object.keys(deltaOptions)); } deltaOptions ?? (deltaOptions = jsonDiff( baseChartOptions.userOptions, newUserOptions, _ChartOptions.JSON_DIFF_OPTS )); this.userOptions = deepClone(merge(deltaOptions, baseChartOptions.userOptions), { ..._ChartOptions.OPTIONS_CLONE_OPTS_SLOW, seen: [] }); } else { this.userOptions = deepClone(currentUserOptions ?? newUserOptions, { ..._ChartOptions.OPTIONS_CLONE_OPTS_SLOW, seen: [] }); this.specialOverrides = this.specialOverridesDefaults({ ...specialOverrides }); } this.findSeriesWithUserVisiblity(newUserOptions, deltaOptions); if (stripSymbols) { this.removeLeftoverSymbols(this.userOptions); } const dataChangedLength = currentUserOptions instanceof _ChartOptions && deltaOptions?.data !== void 0 && deltaOptions?.data?.length !== currentUserOptions.userOptions.data?.length; let activeTheme, processedOptions, fastDelta, themeParameters, annotationThemes, googleFonts, optionsGraph; if (!stripSymbols && this.seriesWithUserVisibility == void 0 && deltaOptions !== void 0 && _ChartOptions.isFastPathDelta(deltaOptions) && baseChartOptions != null && !dataChangedLength) { ({ activeTheme, processedOptions, fastDelta } = this.fastSetup(deltaOptions, baseChartOptions)); themeParameters = baseChartOptions.themeParameters; annotationThemes = baseChartOptions.annotationThemes; } else { _ChartOptions.perfDebug(`ChartOptions.slowSetup()`); ({ activeTheme, processedOptions, themeParameters, annotationThemes, googleFonts, optionsGraph } = this.slowSetup(processedOverrides, deltaOptions, stripSymbols)); } this.activeTheme = activeTheme; this.processedOptions = processedOptions; this.fastDelta = fastDelta ?? void 0; this.themeParameters = themeParameters; this.annotationThemes = annotationThemes; this.googleFonts = googleFonts; this.optionsGraph = optionsGraph; if (apiStartTime !== void 0 && typeof apiStartTime === "number" && !Number.isNaN(apiStartTime)) { const endTime = performance.now(); this.optionsProcessingTime = endTime - apiStartTime; } debugLogger_exports.inDevelopmentMode(() => deepFreeze(this)); } static isFastPathDelta(deltaOptions) { for (const key of Object.keys(deltaOptions ?? {})) { if (!this.FAST_PATH_OPTIONS.has(key)) { _ChartOptions.perfDebug("ChartOptions.isFastPathDelta() - slow path required due to presence of: ", key); return false; } } _ChartOptions.perfDebug(`ChartOptions.isFastPathDelta() - fast path possible.`); return true; } findSeriesWithUserVisiblity(newUserOptions, deltaOptions) { for (const o of [newUserOptions, deltaOptions]) { const series = o?.series; if (!Array.isArray(series)) continue; for (let index = 0; index < series.length; index++) { const s = series[index]; if (!("visible" in s)) continue; this.seriesWithUserVisibility ?? (this.seriesWithUserVisibility = { identifiers: /* @__PURE__ */ new Set(), indices: /* @__PURE__ */ new Set() }); if (s.id) { this.seriesWithUserVisibility.identifiers.add(s.id); } else { this.seriesWithUserVisibility.indices.add(index); } } } } fastSetup(deltaOptions, baseChartOptions) { const { activeTheme, processedOptions: baseOptions } = baseChartOptions; const { presetType } = this.optionMetadata; if (presetType != null && deltaOptions?.data != null) { const presetDef = moduleRegistry_exports.getPresetModule(presetType); if (presetDef?.processData) { const { series, data } = presetDef.processData(deltaOptions.data); deltaOptions = mergeDefaults({ series, data }, deltaOptions); } } this.fastSeriesSetup(deltaOptions, baseOptions); const processedOptions = mergeDefaults(deltaOptions, baseOptions); _ChartOptions.debug("ChartOptions.fastSetup() - processed options", processedOptions); return { activeTheme, processedOptions, fastDelta: deltaOptions }; } fastSeriesSetup(deltaOptions, baseOptions) { if (!deltaOptions?.series) return; if (deltaOptions.series?.every((s, i) => jsonPropertyCompare(s, baseOptions.series?.[i] ?? {}))) { delete deltaOptions["series"]; } else { deltaOptions.series = deltaOptions.series.map((s, i) => { return merge(s, baseOptions.series?.[i] ?? {}); }); } } slowSetup(processedOverrides, deltaOptions, stripSymbols = false) { let options = deepClone(this.userOptions, _ChartOptions.OPTIONS_CLONE_OPTS_FAST); if (deltaOptions) { options = mergeDefaults(deltaOptions, options); if (stripSymbols) { this.removeLeftoverSymbols(options); } } let activeTheme = sanitizeThemeModules(getChartTheme(options.theme)); const { presetType } = this.optionMetadata; if (presetType != null) { const presetDef = moduleRegistry_exports.getPresetModule(presetType); if (presetDef) { const { validate: validatePreset = validate } = presetDef; const presetParams = options; const presetSubType = options.type; const presetTheme = presetSubType == null ? void 0 : activeTheme.presets[presetSubType]; const { cleared, invalid } = validatePreset(presetParams, presetDef.options, ""); for (const error2 of invalid) { logger_exports.warn(error2); } if (hasRequiredInPath(invalid, "")) { options = {}; } else { _ChartOptions.debug(">>> AgCharts.createOrUpdate() - applying preset", cleared); options = presetDef.create(cleared, presetTheme, () => this.activeTheme); activeTheme = sanitizeThemeModules(getChartTheme(options.theme)); } } } this.soloSeriesIntegrity(options); if (presetType != null) { activeTheme.templateTheme(options, false); } removeIncompatibleModuleOptions(void 0, options); const missingSeriesModules = this.validateSeriesOptions(options); const chartType = detectChartType(options); this.chartDef = moduleRegistry_exports.getChartModule(chartType); if (!this.chartDef.placeholder) { const { validate: validateChart = validate } = this.chartDef; const { cleared, invalid } = validateChart(options, this.chartDef.options, ""); for (const error2 of invalid) { logger_exports.warn(error2); } options = cleared; } this.validateAxesOptions(options); this.removeDisabledOptions(options); let googleFonts = this.processFonts(activeTheme.params); googleFonts = this.processFonts(options, googleFonts); this.processSeriesOptions(options); const unmappedAxisKeys = this.processAxesOptions(options, chartType); const optionsGraph = createOptionsGraph(activeTheme, options); const resolvedOptions = optionsGraph.resolve(); const themeParameters = optionsGraph.resolveParams(); const annotationThemes = optionsGraph.resolveAnnotationThemes(); optionsGraph.clearSafe(); const processedOptions = mergeDefaults(processedOverrides, resolvedOptions); removeIncompatibleModuleOptions(this.chartDef.name, processedOptions); processModuleOptions(this.chartDef.name, processedOptions, missingSeriesModules); this.validateSeriesOptions(processedOptions); this.validateAxesOptions(processedOptions, unmappedAxisKeys); this.validatePluginOptions(processedOptions); this.processMiniChartSeriesOptions(processedOptions); if (!processedOptions.loadGoogleFonts) { googleFonts.clear(); } _ChartOptions.debug(() => ["ChartOptions.slowSetup() - processed options", deepClone(processedOptions)]); return { activeTheme, processedOptions, themeParameters, annotationThemes, googleFonts, optionsGraph }; } validatePluginOptions(options) { for (const pluginDef of moduleRegistry_exports.listModulesByType("plugin" /* Plugin */)) { const pluginKey = pluginDef.name; if (pluginKey in options && pluginDef.options != null && (!pluginDef.chartType || pluginDef.chartType === this.chartDef?.name)) { const { cleared, invalid } = validate(options[pluginKey], pluginDef.options, pluginDef.name); for (const error2 of invalid) { logger_exports.warn(error2); } options[pluginKey] = cleared; } } } validateSeriesOptions(options) { const chartType = this.chartDef?.name; const validatedSeriesOptions = []; const seriesCount = options.series?.length ?? 0; const missingModules = []; let validSeriesTypes; for (let index = 0; index < seriesCount; index++) { const keyPath = `series[${index}]`; const seriesOptions = options.series[index]; const seriesDef = moduleRegistry_exports.getSeriesModule(seriesOptions.type); if (seriesDef == null) { const isEnterprise2 = moduleRegistry_exports.isEnterprise(); validSeriesTypes ?? (validSeriesTypes = joinFormatted( Array.from(ExpectedModules.values()).filter( (def) => def.type === "series" /* Series */ && (isEnterprise2 || !def.enterprise) && (!chartType || def.chartType === chartType) ).map((def) => def.name), "or", stringFormat )); const modulePlaceholder = ExpectedModules.get(seriesOptions.type); if (seriesOptions.type != null && modulePlaceholder?.type === "series" /* Series */) { missingModules.push(modulePlaceholder); continue; } logger_exports.warn( seriesOptions.type == null ? `Option \`${keyPath}.type\` is required and has not been provided; expecting ${validSeriesTypes}, ignoring.` : `Unknown type \`${seriesOptions.type}\` at \`${keyPath}.type\`; expecting ${validSeriesTypes}, ignoring.` ); continue; } else if (chartType && seriesDef.chartType !== chartType) { logger_exports.warn( `Series type \`${seriesDef.name}\` at \`${keyPath}.type\` is not supported by chart type \`${chartType}\`, ignoring.` ); continue; } if (seriesDef.options == null) { validatedSeriesOptions.push(seriesOptions); continue; } const { validate: validateSeries = validate } = seriesDef; const { cleared, invalid } = validateSeries(seriesOptions, seriesDef.options, keyPath); for (const error2 of invalid) { logger_exports.warn(error2); } if (!hasRequiredInPath(invalid, keyPath)) { validatedSeriesOptions.push(cleared); } } options.series = validatedSeriesOptions; return missingModules; } validateAxesOptions(options, unmappedAxisKeys) { if (!("axes" in options) || !options.axes) return; const chartType = this.chartDef?.name; const validatedAxesOptions = {}; let validAxesTypes; for (const [key, axisOptions] of entries(options.axes)) { if (!axisOptions) continue; if (axisOptions.type == null) { validatedAxesOptions[key] = axisOptions; continue; } const keyPath = `axes.${unmappedAxisKeys?.get(key) ?? key}`; const axisDef = moduleRegistry_exports.getAxisModule(axisOptions.type); if (axisDef == null) { const isEnterprise2 = moduleRegistry_exports.isEnterprise(); validAxesTypes ?? (validAxesTypes = joinFormatted( Array.from(ExpectedModules.values()).filter( (def) => def.type === "axis" /* Axis */ && (isEnterprise2 || !def.enterprise) && def.chartType === chartType ).map((def) => def.name), "or", stringFormat )); const modulePlaceholder = ExpectedModules.get(axisOptions.type); if (modulePlaceholder?.type !== "axis" /* Axis */) { logger_exports.warn( `Unknown type \`${axisOptions.type}\` at \`${keyPath}.type\`; expecting one of ${validAxesTypes}, ignoring.` ); } continue; } else if (axisDef.chartType !== chartType) { logger_exports.warn( `Axis type \`${axisDef.name}\` at \`${keyPath}.type\` is not supported by chart type \`${chartType}\`, ignoring.` ); break; } const { validate: validateAxis = validate } = axisDef; const { cleared, invalid } = validateAxis(axisOptions, axisDef.options, keyPath); for (const error2 of invalid) { logger_exports.warn(error2); } if (!hasRequiredInPath(invalid, keyPath)) { validatedAxesOptions[key] = cleared; } } options.axes = validatedAxesOptions; } diffOptions(other) { if (this === other) return {}; if (other == null) return this.processedOptions; return this.fastDelta ?? jsonDiff(other.processedOptions, this.processedOptions, _ChartOptions.JSON_DIFF_OPTS); } optionsType(options) { return options.series?.[0]?.type ?? "line"; } processSeriesOptions(options) { const displayNullData = options.displayNullData; const processedSeries = options.series?.map((series) => { const seriesDef = moduleRegistry_exports.getSeriesModule(series.type); const visibleDefined = Boolean(seriesDef?.options?.visible); const seriesDefaults = {}; if (visibleDefined) { seriesDefaults.visible = true; } if (displayNullData !== void 0 && series.allowNullKeys === void 0) { seriesDefaults.allowNullKeys = displayNullData; } return mergeDefaults(this.getSeriesGroupingOptions(series), series, seriesDefaults); }); options.series = this.setSeriesGroupingOptions(processedSeries ?? []); } /** * Collates axis keys from the axis and series options to determine the full set of axis keys, defaults series to * the primary axes and renames the primary axes to the internal direction-based names. */ processAxesOptions(options, chartType) { const directions2 = chartType === "polar" ? ["angle" /* Angle */, "radius" /* Radius */] : ["x" /* X */, "y" /* Y */]; const hasAxes = "axes" in options && Object.keys(options.axes ?? {}).length > 0; const nonDefaultSeriesAxisKeysCount = this.countNonDefaultSeriesAxisKeys(options, directions2); const hasNonDefaultSeriesAxisKeys = nonDefaultSeriesAxisKeysCount > 0; const hasExtraImplicitDefaultSeriesAxisKeys = hasNonDefaultSeriesAxisKeys && nonDefaultSeriesAxisKeysCount < (options?.series?.length ?? 0); const primarySeriesOptions = options.series?.[0]; const seriesType = this.optionsType(options); const defaultAxes = this.predictAxes(seriesType, directions2, primarySeriesOptions, options.data) ?? this.cloneDefaultAxes(seriesType); const isPrimarySeriesFlipped = isObject(primarySeriesOptions) && "direction" in primarySeriesOptions && primarySeriesOptions.direction === "horizontal" && moduleRegistry_exports.getSeriesModule(primarySeriesOptions.type)?.axisKeysFlipped != null; if (!hasAxes && !hasNonDefaultSeriesAxisKeys && !isPrimarySeriesFlipped) { options.axes = defaultAxes; return; } const axisKeys = "axes" in options ? new Set(Object.keys(options.axes ?? {})) : /* @__PURE__ */ new Set(); const primaryAxisKeys = this.getPrimaryAxisKeys(options, directions2, axisKeys, hasNonDefaultSeriesAxisKeys); const remappedAxisKeys = this.getRemappedAxisKeys( axisKeys, primaryAxisKeys, directions2, hasExtraImplicitDefaultSeriesAxisKeys ); const newAxes = {}; const unmappedAxisKeys = /* @__PURE__ */ new Map(); for (const [fromAxisKey, toAxisKey] of remappedAxisKeys) { newAxes[toAxisKey] = "axes" in options ? shallowClone(options.axes?.[fromAxisKey]) : void 0; unmappedAxisKeys.set(toAxisKey, fromAxisKey); } this.remapSeriesAxisKeys( options, directions2, newAxes, remappedAxisKeys, defaultAxes, hasExtraImplicitDefaultSeriesAxisKeys ); this.predictAxesMissingTypesAndPositions(options, directions2, newAxes, defaultAxes); this.alternateSecondaryAxisPositions(options, newAxes, unmappedAxisKeys); options.axes = newAxes; return unmappedAxisKeys; } /** * These keys are used to map a series to an axis for each direction. They are retrieved per-series to allow a * mixture of flipped and non-flipped series within the same chart. */ getSeriesDirectionAxisKey(seriesOptions, direction) { const seriesModule = moduleRegistry_exports.getSeriesModule(seriesOptions.type); if (!seriesModule) return; const isFlipped = "direction" in seriesOptions && seriesOptions.direction === "horizontal"; return isFlipped && seriesModule.axisKeysFlipped ? seriesModule.axisKeysFlipped[direction] : seriesModule.axisKeys?.[direction]; } /** * Check if any of the series' axis keys have values that do not match the expected value, i.e. the direction. */ countNonDefaultSeriesAxisKeys(options, directions2) { let count = 0; for (const seriesOptions of options.series ?? []) { for (const direction of directions2) { const directionAxisKey = this.getSeriesDirectionAxisKey(seriesOptions, direction); if (!directionAxisKey || !isKeyOf(directionAxisKey, seriesOptions)) continue; if (seriesOptions[directionAxisKey] === direction) continue; count++; } } return count; } /** * The primary axes are defined, for each direction, as those first in the `axes` object or referenced by a series. * This is irregardless of the specific key of the axis, e.g. an axis with the key `y` may have the position `top`, * and would therefore be classified as the primary `x` axis. */ getPrimaryAxisKeys(options, directions2, axisKeys, hasNonDefaultSeriesAxisKeys) { const primaryAxisKeys = /* @__PURE__ */ new Map(); for (const direction of directions2) { let foundPrimaryAxisKey = false; if ( // has axes "axes" in options && options.axes && // does not have standard-name primary axis without position !(direction in options.axes && isObject(options.axes[direction]) && !("position" in options.axes[direction])) ) { for (const [axisKey, axisOptions] of entries(options.axes)) { if ("position" in axisOptions && axisOptions.position && direction === POSITION_DIRECTIONS[axisOptions.position]) { primaryAxisKeys.set(direction, axisKey); foundPrimaryAxisKey = true; break; } } } if (foundPrimaryAxisKey) continue; if (!hasNonDefaultSeriesAxisKeys) continue; for (const seriesOptions of options.series ?? []) { const directionAxisKey = this.getSeriesDirectionAxisKey(seriesOptions, direction); if (!directionAxisKey) continue; const seriesAxisKey = seriesOptions[directionAxisKey]; if (axisKeys.has(seriesAxisKey)) continue; if (!seriesAxisKey) { primaryAxisKeys.set(direction, direction); break; } primaryAxisKeys.set(direction, seriesAxisKey); break; } } if (axisKeys.size === 0 || !("axes" in options) || !options.axes) return primaryAxisKeys; if (primaryAxisKeys.size === 0) { for (const direction of directions2) { if (direction in options.axes) { primaryAxisKeys.set(direction, direction); } } } if (primaryAxisKeys.size === 0) { for (const direction of directions2) { for (const [axisKey, axisOptions] of entries(options.axes)) { if (axisOptions.type?.startsWith(direction)) { primaryAxisKeys.set(direction, axisKey); break; } } } } if (primaryAxisKeys.size === 0 && (options.series?.length ?? 0) > 0) { for (const direction of directions2) { for (const seriesOptions of options.series) { const directionAxisKey = this.getSeriesDirectionAxisKey(seriesOptions, direction); if (!directionAxisKey) continue; const seriesAxisKey = seriesOptions[directionAxisKey]; if (!axisKeys.has(seriesAxisKey)) continue; primaryAxisKeys.set(direction, seriesAxisKey); break; } } } if (primaryAxisKeys.size < 2) { const primaryAxisIdsFound = new Set(primaryAxisKeys.values()); for (const [axisKey, axisOptions] of entries(options.axes)) { if (primaryAxisIdsFound.has(axisKey) || "position" in axisOptions) continue; for (const direction of directions2) { if (primaryAxisKeys.has(direction)) continue; primaryAxisKeys.set(direction, axisKey); primaryAxisIdsFound.add(axisKey); break; } if (primaryAxisKeys.size === 2) break; } } return primaryAxisKeys; } getRemappedAxisKeys(axisKeys, primaryAxisKeys, directions2, hasExtraImplicitDefaultSeriesAxisKeys) { const remappedAxisKeys = /* @__PURE__ */ new Map(); for (const [direction, axisKey] of primaryAxisKeys) { remappedAxisKeys.set(axisKey, direction); } for (const axisKey of axisKeys) { if (remappedAxisKeys.has(axisKey)) continue; remappedAxisKeys.set(axisKey, `${AXIS_ID_PREFIX}${remappedAxisKeys.size}`); } if (hasExtraImplicitDefaultSeriesAxisKeys) { for (const direction of directions2) { if (!remappedAxisKeys.has(direction)) { remappedAxisKeys.set(direction, `${AXIS_ID_PREFIX}${remappedAxisKeys.size}`); } } } return remappedAxisKeys; } /** * Update each series' axis keys to match the name used internally, such as the direction or a constant suffixed by * the index for secondary axes. */ remapSeriesAxisKeys(options, directions2, newAxes, remappedAxisKeys, defaultAxes, hasExtraImplicitDefaultSeriesAxisKeys) { for (const seriesOptions of options.series ?? []) { for (const direction of directions2) { const directionAxisKey = this.getSeriesDirectionAxisKey(seriesOptions, direction); if (!directionAxisKey) continue; newAxes[direction] ?? (newAxes[direction] = shallowClone(defaultAxes[direction])); let remappedSeriesAxisKey = direction; if (directionAxisKey in seriesOptions) { const seriesAxisKey = seriesOptions[directionAxisKey]; if (remappedAxisKeys.has(seriesAxisKey)) { remappedSeriesAxisKey = remappedAxisKeys.get(seriesAxisKey); } else { remappedSeriesAxisKey = `${AXIS_ID_PREFIX}${remappedAxisKeys.size}`; remappedAxisKeys.set(seriesAxisKey, remappedSeriesAxisKey); newAxes[remappedSeriesAxisKey] = shallowClone(defaultAxes[direction]); } } else if (remappedAxisKeys.has(direction) && hasExtraImplicitDefaultSeriesAxisKeys) { remappedSeriesAxisKey = remappedAxisKeys.get(direction); newAxes[remappedSeriesAxisKey] ?? (newAxes[remappedSeriesAxisKey] = shallowClone(defaultAxes[direction])); } seriesOptions[directionAxisKey] = remappedSeriesAxisKey; } } } /** * Attempt to predict the axes for each direction based on a subset of the data. Each series has its own prediction * algorithm. */ predictAxes(seriesType, directions2, userSeriesOptions, data) { if (!userSeriesOptions) return; const seriesData = userSeriesOptions?.data ?? data; if (!seriesData?.length) return; const predictAxis = moduleRegistry_exports.getSeriesModule(seriesType)?.predictAxis; if (!predictAxis) return; const axes = /* @__PURE__ */ new Map(); const indices = distribute(0, seriesData.length - 1, 5); for (const index of indices) { const datum = seriesData[index]; for (const direction of directions2) { const axis = predictAxis(direction, datum, userSeriesOptions); if (!axes.has(direction)) { axes.set(direction, axis); continue; } const prevAxis = axes.get(direction); if (!axis && !prevAxis) continue; if (!axis || !prevAxis) return; for (const key of Object.keys(prevAxis)) { if (prevAxis[key] !== axis[key]) return; } } } for (const [direction, axis] of axes) { if (!axis) axes.delete(direction); } if (axes.size === 0) return; if (axes.size === 1) { const [predictedAxis] = axes.values(); const defaultAxes = this.cloneDefaultAxes(seriesType); if (!("position" in predictedAxis)) return; return mapValues(defaultAxes, (axis) => { if (!("position" in axis)) return axis; return axis.position === predictedAxis.position ? predictedAxis : axis; }); } return Object.fromEntries(axes); } cloneDefaultAxes(seriesType) { const seriesModule = moduleRegistry_exports.getSeriesModule(seriesType); return seriesModule?.defaultAxes ? deepClone(seriesModule.defaultAxes) : {}; } predictAxesMissingTypesAndPositions(options, directions2, newAxes, defaultAxes) { for (const [key, axis] of entries(newAxes)) { if (!isPlainObject(axis)) continue; if ("type" in axis && "position" in axis) continue; if (key in defaultAxes) { axis.type ?? (axis.type = defaultAxes[key].type); axis.position ?? (axis.position = defaultAxes[key].position); continue; } const predictedType = this.predictAxisMissingTypeFromPosition(axis, defaultAxes); if (predictedType) continue; this.predictAxisMissingTypeAndPositionFromSeries(options, directions2, key, axis, defaultAxes); if (!("type" in axis)) { delete newAxes[key]; } } } predictAxisMissingTypeFromPosition(axis, defaultAxes) { if (!("position" in axis) || !isKeyOf(axis.position, POSITION_DIRECTIONS)) { return false; } for (const defaultAxis of Object.values(defaultAxes)) { if (isKeyOf(defaultAxis.position, POSITION_DIRECTIONS) && POSITION_DIRECTIONS[axis.position] === POSITION_DIRECTIONS[defaultAxis.position]) { axis.type = defaultAxis.type; return true; } } for (const [position, positionDirection] of entries(POSITION_DIRECTIONS)) { if (axis.position !== position && positionDirection === POSITION_DIRECTIONS[axis.position]) { axis.type = defaultAxes[positionDirection].type; return true; } } return false; } predictAxisMissingTypeAndPositionFromSeries(options, directions2, axisKey, axis, defaultAxes) { for (const seriesOptions of options.series ?? []) { for (const direction of directions2) { const directionAxisKey = this.getSeriesDirectionAxisKey(seriesOptions, direction); if (!directionAxisKey || !isKeyOf(directionAxisKey, seriesOptions)) continue; if (seriesOptions[directionAxisKey] !== axisKey) continue; axis.type ?? (axis.type = defaultAxes[direction].type); axis.position ?? (axis.position = defaultAxes[direction].position); return direction === "y" /* Y */; } } return false; } /** * If the first secondary axis in either direction does not have a specified position, it will be placed in the * alternate position to the primary axis (i.e. right or top). */ alternateSecondaryAxisPositions(options, newAxes, unmappedAxisKeys) { let xAxisCount = 0; let yAxisCount = 0; for (const [axisKey, axis] of entries(newAxes)) { if (!isPlainObject(axis) || !("position" in axis)) continue; const unmappedAxisKey = unmappedAxisKeys.get(axisKey); const unmappedAxis = "axes" in options && options.axes && unmappedAxisKey && unmappedAxisKey in options.axes ? options.axes[unmappedAxisKey] : void 0; const unmappedAxisPosition = unmappedAxis && "position" in unmappedAxis ? unmappedAxis.position : void 0; if (axis.position === "top" || axis.position === "bottom") { xAxisCount += 1; if (xAxisCount === 2 && unmappedAxisPosition == null) { axis.position = "top"; } } else if (axis.position === "left" || axis.position === "right") { yAxisCount += 1; if (yAxisCount === 2 && unmappedAxisPosition == null) { axis.position = "right"; } } if (xAxisCount > 1 && yAxisCount > 1) break; } } processMiniChartSeriesOptions(options) { const miniChartSeries = options.navigator?.miniChart?.series; if (miniChartSeries == null) return; options.navigator.miniChart.series = this.setSeriesGroupingOptions( miniChartSeries ); } getSeriesGroupingOptions(series) { const { groupable, stackable, stackedByDefault = false } = moduleRegistry_exports.getSeriesModule(series.type); if (series.grouped && !groupable) { logger_exports.warnOnce(`unsupported grouping of series type "${series.type}".`); } if ((series.stacked || series.stackGroup) && !stackable) { logger_exports.warnOnce(`unsupported stacking of series type "${series.type}".`); } let { grouped, stacked } = series; stacked ?? (stacked = (stackedByDefault || series.stackGroup != null) && !(groupable && grouped)); grouped ?? (grouped = true); return { stacked: stackable && stacked, grouped: groupable && grouped && !(stackable && stacked) }; } setSeriesGroupingOptions(allSeries) { const seriesGroups = this.getSeriesGrouping(allSeries); _ChartOptions.debug("ChartOptions.setSeriesGroupingOptions() - series grouping: ", seriesGroups); const groupIdx = {}; const groupCount2 = seriesGroups.reduce((countMap, seriesGroup) => { var _a; if (seriesGroup.groupType === "default" /* DEFAULT */) { return countMap; } countMap[_a = seriesGroup.seriesType] ?? (countMap[_a] = 0); countMap[seriesGroup.seriesType] += seriesGroup.groupType === "stack" /* STACK */ ? 1 : seriesGroup.series.length; return countMap; }, {}); return seriesGroups.flatMap((seriesGroup) => { var _a; groupIdx[_a = seriesGroup.seriesType] ?? (groupIdx[_a] = 0); switch (seriesGroup.groupType) { case "stack" /* STACK */: { const groupIndex = groupIdx[seriesGroup.seriesType]++; return seriesGroup.series.map( (series, stackIndex) => Object.assign(series, { seriesGrouping: { groupId: seriesGroup.groupId, groupIndex, groupCount: groupCount2[seriesGroup.seriesType], stackIndex, stackCount: seriesGroup.series.length } }) ); } case "group" /* GROUP */: return seriesGroup.series.map( (series) => Object.assign(series, { seriesGrouping: { groupId: seriesGroup.groupId, groupIndex: groupIdx[seriesGroup.seriesType]++, groupCount: groupCount2[seriesGroup.seriesType], stackIndex: 0, stackCount: 0 } }) ); } return seriesGroup.series; }).map(({ stacked: _, grouped: __, ...seriesOptions }) => seriesOptions); } getSeriesGroupId(series) { return [series.type, series.xKey, series.stacked ? series.stackGroup ?? "stacked" : "grouped"].filter(Boolean).join("-"); } getSeriesGrouping(allSeries) { const groupMap = /* @__PURE__ */ new Map(); return allSeries.reduce((result, series) => { const seriesType = series.type; if (!series.stacked && !series.grouped) { result.push({ groupType: "default" /* DEFAULT */, seriesType, series: [series], groupId: "__default__" }); } else { const groupId = this.getSeriesGroupId(series); if (!groupMap.has(groupId)) { const groupType = series.stacked ? "stack" /* STACK */ : "group" /* GROUP */; const record2 = { groupType, seriesType, series: [], groupId }; groupMap.set(groupId, record2); result.push(record2); } groupMap.get(groupId).series.push(series); } return result; }, []); } soloSeriesIntegrity(options) { if (!isArray(options.series)) return; const isSolo = (seriesType) => moduleRegistry_exports.getSeriesModule(seriesType)?.solo ?? false; const allSeries = options.series; if (allSeries && allSeries.length > 1 && allSeries.some((series) => isSolo(series.type))) { const mainSeriesType = this.optionsType(options); if (isSolo(mainSeriesType)) { logger_exports.warn( `series[0] of type '${mainSeriesType}' is incompatible with other series types. Only processing series[0]` ); options.series = allSeries.slice(0, 1); } else { const { solo, nonSolo } = groupBy(allSeries, (s) => isSolo(s.type) ? "solo" : "nonSolo"); const rejects = unique(solo.map((s) => s.type)).join(", "); logger_exports.warn(`Unable to mix these series types with the lead series type: ${rejects}`); options.series = nonSolo; } } } static processFontOptions(node, _, __, googleFonts = /* @__PURE__ */ new Set()) { if (typeof node === "object" && "fontFamily" in node) { if (Array.isArray(node.fontFamily)) { const fontFamily = []; for (const font of node.fontFamily) { if (typeof font === "object" && "googleFont" in font) { fontFamily.push(font.googleFont); googleFonts?.add(font.googleFont); } else { fontFamily.push(font); } } node.fontFamily = fontFamily.join(", "); } else if (typeof node.fontFamily === "object" && "googleFont" in node.fontFamily) { node.fontFamily = node.fontFamily.googleFont; googleFonts?.add(node.fontFamily); } } return googleFonts; } processFonts(options, googleFonts = /* @__PURE__ */ new Set()) { return jsonWalk( options, _ChartOptions.processFontOptions, /* @__PURE__ */ new Set(["data", "theme"]), void 0, void 0, googleFonts ); } static removeDisabledOptionJson(optionsNode) { if ("enabled" in optionsNode && optionsNode.enabled === false) { for (const key of Object.keys(optionsNode)) { if (key === "enabled") continue; delete optionsNode[key]; } } } removeDisabledOptions(options) { jsonWalk(options, _ChartOptions.removeDisabledOptionJson, /* @__PURE__ */ new Set(["data", "theme", "contextMenu"])); } static removeLeftoverSymbolsJson(optionsNode) { if (!optionsNode || !isObject(optionsNode)) return; for (const key of Object.keys(optionsNode)) { const value = optionsNode[key]; if (isSymbol(value)) { delete optionsNode[key]; } } } removeLeftoverSymbols(options) { jsonWalk(options, _ChartOptions.removeLeftoverSymbolsJson, /* @__PURE__ */ new Set(["data"])); } specialOverridesDefaults(options) { if (options.window == null) { options.window = getWindow(); } else { setWindow(options.window); } if (options.document == null) { options.document = getDocument(); } else { setDocument(options.document); } if (options.window == null) { throw new Error("AG Charts - unable to resolve global window"); } if (options.document == null) { throw new Error("AG Charts - unable to resolve global document"); } return options; } }; _ChartOptions.OPTIONS_CLONE_OPTS_SLOW = { shallow: /* @__PURE__ */ new Set(["data", "container"]), assign: /* @__PURE__ */ new Set(["context", "theme"]) }; _ChartOptions.OPTIONS_CLONE_OPTS_FAST = { shallow: /* @__PURE__ */ new Set(["container"]), assign: /* @__PURE__ */ new Set(["data", "context", "theme"]) }; _ChartOptions.JSON_DIFF_OPTS = /* @__PURE__ */ new Set(["data", "localeText"]); _ChartOptions.perfDebug = debugLogger_exports.create(true, "perf"); _ChartOptions.FAST_PATH_OPTIONS = /* @__PURE__ */ new Set(["data", "width", "height", "container"]); // AG-16389: Track keys the user passed in deltaOptions _ChartOptions.debug = debugLogger_exports.create(true, "opts"); var ChartOptions = _ChartOptions; // packages/ag-charts-community/src/chart/chartProxy.ts var debug3 = debugLogger_exports.create(true, "opts"); var DESTROYED_ERROR = "AG Charts - Chart was destroyed, cannot perform request."; var _AgChartInstanceProxy = class _AgChartInstanceProxy { constructor(chart, factoryApi, licenseManager) { this.factoryApi = factoryApi; this.licenseManager = licenseManager; this.chart = chart; } async update(options) { if (!this.chart) throw new Error(DESTROYED_ERROR); return debug3.group("AgChartInstance.update()", async () => { const apiStartTime = debugLogger_exports.check("scene:stats", "scene:stats:verbose") ? performance.now() : void 0; this.factoryApi.update(options, this, void 0, apiStartTime); await this.chart?.waitForUpdate(); }); } async updateDelta(deltaOptions) { if (!this.chart) throw new Error(DESTROYED_ERROR); return debug3.group("AgChartInstance.updateDelta()", async () => { const apiStartTime = debugLogger_exports.check("scene:stats", "scene:stats:verbose") ? performance.now() : void 0; this.factoryApi.updateUserDelta(this, deltaOptions, apiStartTime); await this.chart?.waitForUpdate(); }); } getOptions() { if (!this.chart) throw new Error(DESTROYED_ERROR); const options = deepClone(this.chart.getOptions(), ChartOptions.OPTIONS_CLONE_OPTS_FAST); for (const key of Object.keys(options)) { if (key.startsWith("_")) { delete options[key]; } } return options; } waitForUpdate() { if (!this.chart) throw new Error(DESTROYED_ERROR); return this.chart.waitForUpdate(); } applyTransaction(transaction) { const { chart } = this; if (!chart) throw new Error(DESTROYED_ERROR); if (transaction == null || typeof transaction !== "object") { throw new Error("AG Charts - applyTransaction expects a transaction object."); } const { add: add2, addIndex, remove, update } = transaction; if (add2 != null && !Array.isArray(add2)) { throw new Error('AG Charts - transaction "add" must be an array.'); } if (addIndex != null) { if (typeof addIndex !== "number" || !Number.isSafeInteger(addIndex) || addIndex < 0) { throw new Error( 'AG Charts - transaction "addIndex" must be a safe non-negative integer (0 to 9007199254740991).' ); } if (add2 == null || add2.length === 0) { throw new Error('AG Charts - transaction "addIndex" requires a non-empty "add" array.'); } } if (remove != null && !Array.isArray(remove)) { throw new Error('AG Charts - transaction "remove" must be an array.'); } if (update != null && !Array.isArray(update)) { throw new Error('AG Charts - transaction "update" must be an array.'); } return debug3.group("AgChartInstance.applyTransaction()", async () => { if (!chart.isDataTransactionSupported()) { const dataSet = chart.data.deepClone(); dataSet.addTransaction(transaction); dataSet.commitPendingTransactions(); return this.updateDelta({ data: dataSet.data }); } debug3("transaction", transaction); return await this.chart?.applyTransaction(transaction); }); } async download(opts) { if (!this.chart) throw new Error(DESTROYED_ERROR); const clone2 = await this.prepareResizedChart(this, this.chart, opts); try { clone2.chart?.download(opts?.fileName, opts?.fileFormat); } finally { clone2.destroy(); } } async __toSVG(opts) { if (!this.chart) throw new Error(DESTROYED_ERROR); const clone2 = await this.prepareResizedChart(this, this.chart, { width: 600, height: 300, ...opts }); try { return clone2?.chart?.toSVG(); } finally { clone2?.destroy(); } } async getImageDataURL(opts) { if (!this.chart) throw new Error(DESTROYED_ERROR); const clone2 = await this.prepareResizedChart(this, this.chart, opts); try { return clone2.chart.getCanvasDataURL(opts?.fileFormat); } finally { clone2.destroy(); } } getState() { return this.factoryApi.caretaker.save(...this.getEnabledOriginators()); } async setState(state) { const { chart } = this; if (!chart) return; const originators = this.getEnabledOriginators(); if (!originators.includes(chart.ctx.legendManager)) { await this.setStateOriginators(state, originators); return; } await this.setStateOriginators( state, originators.filter((originator) => originator !== chart.ctx.zoomManager) ); await this.setStateOriginators(state, [chart.ctx.zoomManager]); } resetAnimations() { this.chart?.resetAnimations(); } skipAnimations() { this.chart?.skipAnimations(); } destroy() { if (this.releaseChart) { this.releaseChart(); this.releaseChart = void 0; } else if (this.chart) { this.chart.publicApi = void 0; this.chart.destroy(); } this.chart = void 0; } async prepareResizedChart(proxy, chart, opts = {}) { const width2 = opts.width ?? chart.width ?? chart.ctx.scene.canvas.width; const height2 = opts.height ?? chart.height ?? chart.ctx.scene.canvas.height; const state = proxy.getState(); const processedOverrides = { ...chart.chartOptions.processedOverrides, container: getDocument().createElement("div"), width: width2, height: height2 }; if (opts.width != null && opts.height != null) { processedOverrides.overrideDevicePixelRatio = 1; } const userOptions = chart.getOptions(); if (moduleRegistry_exports.isEnterprise()) { processedOverrides.animation = { enabled: false }; const foreground = this.licenseManager?.getWatermarkForegroundConfigForBrowser(); if (foreground) { processedOverrides.foreground = foreground; } } const specialOverrides = { ...chart.chartOptions.specialOverrides }; const optionsMetadata = { ...chart.chartOptions.optionMetadata }; const data = await this.chart?.ctx.dataService.getData(); const cloneProxy = this.factoryApi.create( userOptions, processedOverrides, specialOverrides, optionsMetadata, data ); if (state.legend) { this.syncLegend(chart, cloneProxy, state); } cloneProxy.chart?.update(0 /* FULL */, { forceNodeDataRefresh: true }); await cloneProxy.waitForUpdate(); await cloneProxy.setState(state); const sourcing = { source: "chart-update", sourceDetail: "internal-prepareResizedChart" }; cloneProxy.chart?.ctx.zoomManager.updateZoom(sourcing, chart.ctx.zoomManager.getZoom()); cloneProxy.chart?.update(0 /* FULL */, { forceNodeDataRefresh: true }); await cloneProxy.waitForUpdate(); const legendPages = []; for (const legend of chart.modulesManager.legends()) { legendPages.push(legend.legend.pagination?.currentPage ?? 0); } for (const legend of cloneProxy.chart.modulesManager.legends()) { const page = legendPages.shift() ?? 0; if (!legend.legend.pagination) continue; legend.legend.pagination.setPage(page); } cloneProxy.chart?.update(0 /* FULL */, { forceNodeDataRefresh: true }); await cloneProxy.waitForUpdate(); return cloneProxy; } syncLegend(chart, cloneProxy, state) { const seriesIdMap = /* @__PURE__ */ new Map(); for (const [index, series] of chart.series.entries()) { const cloneSeries = cloneProxy.chart?.series[index]; if (!cloneSeries) continue; seriesIdMap.set(series.id, cloneSeries.id); } state.legend = state.legend?.map((legend) => { return { ...legend, seriesId: seriesIdMap.get(legend.seriesId ?? "") ?? legend.seriesId }; }); } getEnabledOriginators() { if (!this.chart) return []; const { chartOptions: { processedOptions, optionMetadata }, ctx: { annotationManager, chartTypeOriginator, zoomManager, legendManager }, modulesManager } = this.chart; const originators = []; if ("annotations" in processedOptions && processedOptions.annotations?.enabled) { originators.push(annotationManager); } const isFinancialChart = optionMetadata.presetType === "price-volume"; if (isFinancialChart) { originators.push(chartTypeOriginator); } if (processedOptions.navigator?.enabled || processedOptions.zoom?.enabled) { originators.push(zoomManager); } const legendEnabled = modulesManager.isEnabled("legend") && processedOptions.legend?.enabled !== false; if (legendEnabled) { originators.push(legendManager); } originators.push(this.chart.ctx.activeManager); return originators; } async setStateOriginators(state, originators) { this.factoryApi.caretaker.restore(state, ...originators); this.chart?.ctx.updateService.update(2 /* PROCESS_DATA */, { forceNodeDataRefresh: true }); await this.chart?.waitForUpdate(); } }; _AgChartInstanceProxy.chartInstances = /* @__PURE__ */ new WeakMap(); __decorateClass([ ActionOnSet({ oldValue(chart) { if (!chart.destroyed) { chart.publicApi = void 0; } _AgChartInstanceProxy.chartInstances.delete(chart); }, newValue(chart) { if (!chart) return; chart.publicApi = this; _AgChartInstanceProxy.chartInstances.set(chart, this); } }) ], _AgChartInstanceProxy.prototype, "chart", 2); var AgChartInstanceProxy = _AgChartInstanceProxy; // packages/ag-charts-community/src/util/pool.ts var CLEANUP_TIMEOUT_MS = 1e3; var _Pool = class _Pool { constructor(name, buildItem, releaseItem, destroyItem, maxPoolSize, cleanupTimeMs = CLEANUP_TIMEOUT_MS) { this.name = name; this.buildItem = buildItem; this.releaseItem = releaseItem; this.destroyItem = destroyItem; this.maxPoolSize = maxPoolSize; this.cleanupTimeMs = cleanupTimeMs; this.freePool = []; this.busyPool = /* @__PURE__ */ new Set(); } static getPool(name, buildItem, releaseItem, destroyItem, maxPoolSize) { if (!this.pools.has(name)) { this.pools.set(name, new _Pool(name, buildItem, releaseItem, destroyItem, maxPoolSize)); } return this.pools.get(name); } isFull() { return this.freePool.length + this.busyPool.size >= this.maxPoolSize; } hasFree() { return this.freePool.length > 0; } obtain(params) { if (!this.hasFree() && this.isFull()) { throw new Error("AG Charts - pool exhausted"); } let nextFree = this.freePool.pop(); if (nextFree == null) { nextFree = this.buildItem(params); _Pool.debug(() => [ `Pool[name=${this.name}]: Created instance (${this.freePool.length} / ${this.busyPool.size + 1} / ${this.maxPoolSize})`, nextFree ]); } else { _Pool.debug(() => [ `Pool[name=${this.name}]: Re-used instance (${this.freePool.length} / ${this.busyPool.size + 1} / ${this.maxPoolSize})`, nextFree ]); } this.busyPool.add(nextFree); return { item: nextFree, release: () => this.release(nextFree) }; } obtainFree() { const nextFree = this.freePool.pop(); if (nextFree == null) { throw new Error("AG Charts - pool has no free instances"); } _Pool.debug(() => [ `Pool[name=${this.name}]: Re-used instance (${this.freePool.length} / ${this.busyPool.size + 1} / ${this.maxPoolSize})`, nextFree ]); this.busyPool.add(nextFree); return { item: nextFree, release: () => this.release(nextFree) }; } release(item) { if (!this.busyPool.has(item)) { throw new Error("AG Charts - cannot free item from pool which is not tracked as busy."); } _Pool.debug(() => [ `Pool[name=${this.name}]: Releasing instance (${this.freePool.length} / ${this.busyPool.size} / ${this.maxPoolSize})`, item ]); this.releaseItem(item); this.busyPool.delete(item); this.freePool.push(item); _Pool.debug(() => [ `Pool[name=${this.name}]: Returned instance to free pool (${this.freePool.length} / ${this.busyPool.size} / ${this.maxPoolSize})`, item ]); const now = Date.now(); const earliestClean = now + this.cleanupTimeMs * 0.5; if (this.cleanPoolTimer && (this.cleanPoolDue ?? Infinity) < earliestClean) { clearTimeout(this.cleanPoolTimer); this.cleanPoolTimer = void 0; } if (!this.cleanPoolTimer) { this.cleanPoolDue = now + this.cleanupTimeMs; this.cleanPoolTimer = setTimeout(this.cleanPool.bind(this), this.cleanupTimeMs); } } cleanPool() { const itemsToFree = this.freePool.splice(0); for (const item of itemsToFree) { this.destroyItem(item); } _Pool.debug(() => [ `Pool[name=${this.name}]: Cleaned pool of ${itemsToFree.length} items (${this.freePool.length} / ${this.busyPool.size} / ${this.maxPoolSize})` ]); } destroy() { this.cleanPool(); for (const item of this.busyPool.values()) { this.destroyItem(item); } this.busyPool.clear(); } }; _Pool.pools = /* @__PURE__ */ new Map(); _Pool.debug = debugLogger_exports.create(true, "pool"); var Pool = _Pool; // packages/ag-charts-community/src/api/agCharts.ts var debug4 = debugLogger_exports.create(true, "opts"); var AgCharts = class { static licenseCheck(options) { if (this.licenseChecked) return; this.licenseManager = enterpriseRegistry.licenseManager?.(options); this.licenseManager?.validateLicense(); this.licenseChecked = true; } static getLicenseDetails(licenseKey) { return enterpriseRegistry.licenseManager?.({}).getLicenseDetails(licenseKey); } /** * Returns the `AgChartInstance` for a DOM node, if there is one. */ static getInstance(element2) { return AgChartsInternal.getInstance(element2); } /** * Create a new `AgChartInstance` based upon the given configuration options. */ static create(userOptions, optionsMetadata) { const apiStartTime = debugLogger_exports.check("scene:stats", "scene:stats:verbose") ? performance.now() : void 0; return debug4.group("AgCharts.create()", () => { userOptions = debugLogger_exports.inDevelopmentMode(() => deepFreeze(deepClone(userOptions))) ?? userOptions; this.licenseCheck(userOptions); const chart = AgChartsInternal.createOrUpdate({ userOptions, licenseManager: this.licenseManager, optionsMetadata, apiStartTime }); if (this.licenseManager?.isDisplayWatermark()) { enterpriseRegistry.injectWatermark?.( chart.chart.ctx.domManager, this.licenseManager.getWatermarkMessage() ); } return chart; }); } static createFinancialChart(options) { return debug4.group("AgCharts.createFinancialChart()", () => { return this.create(options, { presetType: "price-volume" }); }); } static createGauge(options) { return debug4.group("AgCharts.createGauge()", () => { return this.create(options, { presetType: "gauge-preset" }); }); } static __createSparkline(options) { return debug4.group("AgCharts.__createSparkline()", () => { const { pool, ...normalOptions } = options; return this.create(normalOptions, { presetType: "sparkline", pool: pool ?? true, domMode: "minimal", withDragInterpretation: false }); }); } }; AgCharts.licenseChecked = false; var _AgChartsInternal = class _AgChartsInternal { static getInstance(element2) { const chart = Chart.getInstance(element2); return chart ? AgChartInstanceProxy.chartInstances.get(chart) : void 0; } static createOrUpdate(opts) { let { proxy } = opts; const { userOptions, licenseManager, processedOverrides = proxy?.chart?.chartOptions.processedOverrides ?? {}, specialOverrides = proxy?.chart?.chartOptions.specialOverrides ?? {}, optionsMetadata = proxy?.chart?.chartOptions.optionMetadata ?? {}, deltaOptions, data, stripSymbols = false, apiStartTime } = opts; const styles = enterpriseRegistry.styles == null ? [] : [["ag-charts-enterprise", enterpriseRegistry.styles]]; if (moduleRegistry_exports.listModules().next().done) { throw new Error( [ "AG Charts - No modules have been registered.", "", "Call ModuleRegistry.registerModules(...) with the modules you need before using AgCharts.create().", "", "See https://www.ag-grid.com/charts/r/module-registry/ for more details." ].join("\n") ); } debug4(() => [">>> AgCharts.createOrUpdate() user options", deepClone(userOptions)]); const { presetType } = optionsMetadata; let mutableOptions = userOptions; if (AgCharts.optionsMutationFn && mutableOptions) { mutableOptions = AgCharts.optionsMutationFn( deepClone(mutableOptions, ChartOptions.OPTIONS_CLONE_OPTS_FAST), presetType ); debug4(() => [">>> AgCharts.createOrUpdate() MUTATED user options", deepClone(mutableOptions)]); } const pool = this.getPool(optionsMetadata); let create2 = false; let poolResult; let chart = proxy?.chart; if (chart == null && pool?.hasFree()) { poolResult = pool.obtainFree(); chart = poolResult.item; } const { document: document2, window: userWindow, styleContainer, skipCss, ...options } = mutableOptions ?? {}; const baseOptions = chart?.getChartOptions(); const chartOptions = new ChartOptions( baseOptions, options, processedOverrides, { ...specialOverrides, document: document2, window: userWindow, styleContainer, skipCss }, optionsMetadata, deltaOptions, stripSymbols, apiStartTime ); if (chart == null || detectChartType(chartOptions.processedOptions) !== detectChartType(chart.chartOptions.processedOptions)) { poolResult?.release(); poolResult = this.getPool(chartOptions.optionMetadata)?.obtain(chartOptions); if (poolResult) { chart = poolResult.item; } else { create2 = true; chart = _AgChartsInternal.createChartInstance(chartOptions, chart); } } if (chartOptions.optionsGraph) { chart.ctx.optionsGraphService.updateCallback((path, partialOptions, resolveOptions) => { return chartOptions.optionsGraph?.resolvePartial(path, partialOptions, resolveOptions); }); } for (const [id, css] of styles) { chart.ctx.domManager.addStyles(id, css); } chart.ctx.fontManager.updateFonts(chartOptions.googleFonts); if (data != null) { chart.ctx.dataService.restoreData(data); } if (proxy == null) { proxy = new AgChartInstanceProxy(chart, _AgChartsInternal.callbackApi, licenseManager); proxy.releaseChart = poolResult?.release; } else if (poolResult || create2) { proxy.releaseChart?.(); proxy.chart = chart; proxy.releaseChart = poolResult?.release; } if (debug4.check() && typeof globalThis.window !== "undefined") { globalThis.agChartInstances ?? (globalThis.agChartInstances = {}); globalThis.agChartInstances[chart.id] = chart; } chart.queuedUserOptions.push(chartOptions.userOptions); chart.queuedChartOptions.push(chartOptions); chart.requestFactoryUpdate((chartRef) => { debug4.group(">>>> Chart.applyOptions()", () => { chartRef.applyOptions(chartOptions); const queueIdx = chartRef.queuedUserOptions.indexOf(chartOptions.userOptions) + 1; chartRef.queuedUserOptions.splice(0, queueIdx); chartRef.queuedChartOptions.splice(0, queueIdx); }); }); return proxy; } static updateUserDelta(proxy, deltaOptions, apiStartTime) { deltaOptions = deepClone(deltaOptions, ChartOptions.OPTIONS_CLONE_OPTS_FAST); const stripSymbols = jsonWalk( deltaOptions, _AgChartsInternal.markRemovedProperties, /* @__PURE__ */ new Set(["data"]), void 0, void 0, false ); debug4(() => [">>> AgCharts.updateUserDelta() user delta", deepClone(deltaOptions)]); _AgChartsInternal.createOrUpdate({ proxy, deltaOptions, stripSymbols, apiStartTime }); } static createChartInstance(options, oldChart) { const transferableResource = oldChart?.destroy({ keepTransferableResources: true }); const chartType = detectChartType(options.processedOptions); const chartDef = moduleRegistry_exports.getChartModule(chartType); return chartDef.create(options, transferableResource); } static getPool(optionMetadata) { if (optionMetadata.pool !== true) return; return Pool.getPool( optionMetadata.presetType ?? "default", this.createChartInstance, this.detachAndClear, this.destroy, Infinity // AG-13480 - Prevent Grid exhausting pool during sorting. ); } }; _AgChartsInternal.caretaker = new MementoCaretaker(VERSION); _AgChartsInternal.callbackApi = { caretaker: _AgChartsInternal.caretaker, create(userOptions, processedOverrides, specialOverrides, optionsMetadata, data) { return _AgChartsInternal.createOrUpdate({ userOptions, processedOverrides, specialOverrides, optionsMetadata, data }); }, update(opts, chart, specialOverrides, apiStartTime) { return _AgChartsInternal.createOrUpdate({ userOptions: opts, proxy: chart, specialOverrides, apiStartTime }); }, updateUserDelta(chart, deltaOptions, apiStartTime) { return _AgChartsInternal.updateUserDelta(chart, deltaOptions, apiStartTime); } }; // CRT-1018 Use `Parameters` and `unknown` to strictly enforce type-safety _AgChartsInternal.markRemovedProperties = (node, _parallelNode, _ctx, previousModified) => { let modified = previousModified ?? false; if (typeof node !== "object" || node == null) return modified; for (const key of strictObjectKeys(node)) { const value = node[key]; if (value === void 0) { Object.assign(node, { [key]: Symbol("UNSET") }); modified || (modified = true); } } return modified; }; _AgChartsInternal.detachAndClear = (chart) => chart.detachAndClear(); _AgChartsInternal.destroy = (chart) => chart.destroy(); var AgChartsInternal = _AgChartsInternal; // packages/ag-charts-community/src/module-support.ts var module_support_exports = {}; __export(module_support_exports, { APPROXIMATE_THRESHOLD: () => APPROXIMATE_THRESHOLD, AbstractBarSeries: () => AbstractBarSeries, AbstractBarSeriesProperties: () => AbstractBarSeriesProperties, AggregationManager: () => AggregationManager, AnchoredPopover: () => AnchoredPopover, ApproximateOrdinalTimeScale: () => ApproximateOrdinalTimeScale, Arc: () => Arc2, Axis: () => Axis, AxisGroupZIndexMap: () => AxisGroupZIndexMap, AxisInterval: () => AxisInterval, AxisLabel: () => AxisLabel, AxisTick: () => AxisTick, BBox: () => BBox, Background: () => Background, BandScale: () => BandScale, BaseToolbar: () => BaseToolbar, Caption: () => Caption, CartesianAxis: () => CartesianAxis, CartesianCrossLine: () => CartesianCrossLine, CartesianSeries: () => CartesianSeries, CartesianSeriesNodeEvent: () => CartesianSeriesNodeEvent, CartesianSeriesProperties: () => CartesianSeriesProperties, CategoryAxis: () => CategoryAxis, CategoryScale: () => CategoryScale, Chart: () => Chart, ChartAxes: () => ChartAxes, ChartOptions: () => ChartOptions, CollapseMode: () => CollapseMode, ColorScale: () => ColorScale, ContextMenuRegistry: () => ContextMenuRegistry, ContinuousScale: () => ContinuousScale, DEFAULT_CARTESIAN_DIRECTION_KEYS: () => DEFAULT_CARTESIAN_DIRECTION_KEYS, DEFAULT_CARTESIAN_DIRECTION_NAMES: () => DEFAULT_CARTESIAN_DIRECTION_NAMES, DEFAULT_POLAR_DIRECTION_KEYS: () => DEFAULT_POLAR_DIRECTION_KEYS, DEFAULT_POLAR_DIRECTION_NAMES: () => DEFAULT_POLAR_DIRECTION_NAMES, DOMManager: () => DOMManager, DataController: () => DataController, DataModel: () => DataModel, DataModelSeries: () => DataModelSeries, DataSet: () => DataSet, DiscreteTimeAxis: () => DiscreteTimeAxis, DraggablePopover: () => DraggablePopover, DropShadow: () => DropShadow, ExtendedPath2D: () => ExtendedPath2D, FillGradientDefaults: () => FillGradientDefaults, FillImageDefaults: () => FillImageDefaults, FillPatternDefaults: () => FillPatternDefaults, FloatingToolbar: () => FloatingToolbar, FormatManager: () => FormatManager, Group: () => Group, GroupWidget: () => GroupWidget, HierarchyHighlightState: () => HierarchyHighlightState, HierarchyNode: () => HierarchyNode, HierarchySeries: () => HierarchySeries, HierarchySeriesProperties: () => HierarchySeriesProperties, HighlightManager: () => HighlightManager, HighlightProperties: () => HighlightProperties, HighlightState: () => HighlightState, Image: () => Image2, InteractionManager: () => InteractionManager, InteractionState: () => InteractionState, LARGEST_KEY_INTERVAL: () => LARGEST_KEY_INTERVAL, Label: () => Label, LabelStyle: () => LabelStyle, LayoutElement: () => LayoutElement, Line: () => Line, LinearScale: () => LinearScale, LogScale: () => LogScale, Marker: () => Marker, Menu: () => Menu, MercatorScale: () => MercatorScale, NODE_UPDATE_STATE_TO_PHASE_MAPPING: () => NODE_UPDATE_STATE_TO_PHASE_MAPPING, NativeWidget: () => NativeWidget, NiceMode: () => NiceMode, Node: () => Node, OrdinalTimeScale: () => OrdinalTimeScale, Path: () => Path, PointerEvents: () => PointerEvents, PolarAxis: () => PolarAxis, PolarSeries: () => PolarSeries, QuadtreeNearest: () => QuadtreeNearest, RadialColumnShape: () => RadialColumnShape, Range: () => Range, Rect: () => Rect, Rotatable: () => Rotatable, RotatableText: () => RotatableText, SMALLEST_KEY_INTERVAL: () => SMALLEST_KEY_INTERVAL, Scalable: () => Scalable, ScalableGroup: () => ScalableGroup, Scene: () => Scene, Sector: () => Sector, SectorBox: () => SectorBox, SegmentedPath: () => SegmentedPath, Selection: () => Selection, Series: () => Series, SeriesItemHighlightStyle: () => SeriesItemHighlightStyle, SeriesMarker: () => SeriesMarker, SeriesNodeEvent: () => SeriesNodeEvent, SeriesNodePickMode: () => SeriesNodePickMode, SeriesProperties: () => SeriesProperties, SeriesTooltip: () => SeriesTooltip, Shape: () => Shape, SliderWidget: () => SliderWidget, StopProperties: () => StopProperties, SvgPath: () => SvgPath, Text: () => Text, TimeAxisParentLevel: () => TimeAxisParentLevel, TimeScale: () => TimeScale, Toolbar: () => Toolbar, ToolbarButtonWidget: () => ToolbarButtonWidget, ToolbarWidget: () => ToolbarWidget, TooltipManager: () => TooltipManager, Transformable: () => Transformable, TransformableGroup: () => TransformableGroup, TransformableText: () => TransformableText, Translatable: () => Translatable, TranslatableGroup: () => TranslatableGroup, TranslatableSvgPath: () => TranslatableSvgPath, UnitTimeScale: () => UnitTimeScale, ZoomManager: () => ZoomManager, accumulativeValueProperty: () => accumulativeValueProperty, addHitTestersToQuadtree: () => addHitTestersToQuadtree, adjustLabelPlacement: () => adjustLabelPlacement, angleCategoryAxisOptionsDefs: () => angleCategoryAxisOptionsDefs, angleNumberAxisOptionsDefs: () => angleNumberAxisOptionsDefs, animationValidation: () => animationValidation, annotationCalloutStylesDefs: () => annotationCalloutStylesDefs, annotationChannelTextDefs: () => annotationChannelTextDefs, annotationCommentStylesDefs: () => annotationCommentStylesDefs, annotationCrossLineStyleDefs: () => annotationCrossLineStyleDefs, annotationDisjointChannelStyleDefs: () => annotationDisjointChannelStyleDefs, annotationFibonacciStylesDefs: () => annotationFibonacciStylesDefs, annotationLineStyleDefs: () => annotationLineStyleDefs, annotationLineTextDefs: () => annotationLineTextDefs, annotationMeasurerStylesDefs: () => annotationMeasurerStylesDefs, annotationNoteStylesDefs: () => annotationNoteStylesDefs, annotationOptionsDef: () => annotationOptionsDef, annotationParallelChannelStyleDefs: () => annotationParallelChannelStyleDefs, annotationQuickMeasurerStylesDefs: () => annotationQuickMeasurerStylesDefs, annotationShapeStylesDefs: () => annotationShapeStylesDefs, annotationTextStylesDef: () => annotationTextStylesDef, boxPlotSeriesThemeableOptionsDef: () => boxPlotSeriesThemeableOptionsDef, buildResetPathFn: () => buildResetPathFn, calculateDataDiff: () => calculateDataDiff, calculateLabelTranslation: () => calculateLabelTranslation, calculateSegments: () => calculateSegments, candlestickSeriesThemeableOptionsDef: () => candlestickSeriesThemeableOptionsDef, checkCrisp: () => checkCrisp, chordSeriesThemeableOptionsDef: () => chordSeriesThemeableOptionsDef, clippedRoundRect: () => clippedRoundRect, collapsedStartingBarPosition: () => collapsedStartingBarPosition, computeBarFocusBounds: () => computeBarFocusBounds, computeMarkerFocusBounds: () => computeMarkerFocusBounds, coneFunnelSeriesThemeableOptionsDef: () => coneFunnelSeriesThemeableOptionsDef, createDatumId: () => createDatumId, diff: () => diff, drawCorner: () => drawCorner, drawMarkerUnitPolygon: () => drawMarkerUnitPolygon, findNodeDatumInArray: () => findNodeDatumInArray, findQuadtreeMatch: () => findQuadtreeMatch, fixNumericExtent: () => fixNumericExtent, fromToMotion: () => fromToMotion, funnelSeriesThemeableOptionsDef: () => funnelSeriesThemeableOptionsDef, generateTicks: () => generateTicks, getColorStops: () => getColorStops, getCrossLineValue: () => getCrossLineValue, getItemStyles: () => getItemStyles, getItemStylesPerItemId: () => getItemStylesPerItemId, getLabelStyles: () => getLabelStyles, getMarkerStyles: () => getMarkerStyles, getMissCount: () => getMissCount, getRadialColumnWidth: () => getRadialColumnWidth, getShapeFill: () => getShapeFill, getShapeStyle: () => getShapeStyle, groupAccumulativeValueProperty: () => groupAccumulativeValueProperty, hasDimmedOpacity: () => hasDimmedOpacity, heatmapSeriesThemeableOptionsDef: () => heatmapSeriesThemeableOptionsDef, initialStatePickedOptionsDef: () => initialStatePickedOptionsDef, interpolatePoints: () => interpolatePoints, isTooltipValueMissing: () => isTooltipValueMissing, keyProperty: () => keyProperty, makeSeriesTooltip: () => makeSeriesTooltip, mapLineBackgroundSeriesThemeableOptionsDef: () => mapLineBackgroundSeriesThemeableOptionsDef, mapLineSeriesThemeableOptionsDef: () => mapLineSeriesThemeableOptionsDef, mapMarkerSeriesThemeableOptionsDef: () => mapMarkerSeriesThemeableOptionsDef, mapShapeBackgroundSeriesThemeableOptionsDef: () => mapShapeBackgroundSeriesThemeableOptionsDef, mapShapeSeriesThemeableOptionsDef: () => mapShapeSeriesThemeableOptionsDef, markerEnabled: () => markerEnabled, markerFadeInAnimation: () => markerFadeInAnimation, markerSwipeScaleInAnimation: () => markerSwipeScaleInAnimation, midpointStartingBarPosition: () => midpointStartingBarPosition, minimumTimeAxisDatumGranularity: () => minimumTimeAxisDatumGranularity, motion: () => motion, nightingaleSeriesThemeableOptionsDef: () => nightingaleSeriesThemeableOptionsDef, normaliseGroupTo: () => normaliseGroupTo, ohlcSeriesThemeableOptionsDef: () => ohlcSeriesThemeableOptionsDef, ordinalTimeAxisOptionsDefs: () => ordinalTimeAxisOptionsDefs, pairUpSpans: () => pairUpSpans, pathFadeInAnimation: () => pathFadeInAnimation, pathMotion: () => pathMotion, pathSwipeInAnimation: () => pathSwipeInAnimation, plotAreaPathFill: () => plotAreaPathFill, plotInterpolatedLinePathStroke: () => plotInterpolatedLinePathStroke, plotLinePathStroke: () => plotLinePathStroke, predictCartesianFinancialAxis: () => predictCartesianFinancialAxis, predictCartesianNonPrimitiveAxis: () => predictCartesianNonPrimitiveAxis, prepareAreaFillAnimationFns: () => prepareAreaFillAnimationFns, prepareBarAnimationFunctions: () => prepareBarAnimationFunctions, prepareLinePathPropertyAnimation: () => prepareLinePathPropertyAnimation, processedDataIsAnimatable: () => processedDataIsAnimatable, pyramidSeriesThemeableOptionsDef: () => pyramidSeriesThemeableOptionsDef, radarAreaSeriesThemeableOptionsDef: () => radarAreaSeriesThemeableOptionsDef, radarLineSeriesThemeableOptionsDef: () => radarLineSeriesThemeableOptionsDef, radialBarSeriesThemeableOptionsDef: () => radialBarSeriesThemeableOptionsDef, radialColumnSeriesThemeableOptionsDef: () => radialColumnSeriesThemeableOptionsDef, radiusCategoryAxisOptionsDefs: () => radiusCategoryAxisOptionsDefs, radiusNumberAxisOptionsDefs: () => radiusNumberAxisOptionsDefs, rangeAreaSeriesThemeableOptionsDef: () => rangeAreaSeriesThemeableOptionsDef, rangeBarSeriesThemeableOptionsDef: () => rangeBarSeriesThemeableOptionsDef, resetAxisLabelSelectionFn: () => resetAxisLabelSelectionFn, resetBarSelectionsDirect: () => resetBarSelectionsDirect, resetBarSelectionsFn: () => resetBarSelectionsFn, resetLabelFn: () => resetLabelFn, resetMarkerFn: () => resetMarkerFn, resetMarkerPositionFn: () => resetMarkerPositionFn, resetMarkerSelectionsDirect: () => resetMarkerSelectionsDirect, resetMotion: () => resetMotion, sankeySeriesThemeableOptionsDef: () => sankeySeriesThemeableOptionsDef, sectorBox: () => sectorBox, seriesLabelFadeInAnimation: () => seriesLabelFadeInAnimation, seriesLabelFadeOutAnimation: () => seriesLabelFadeOutAnimation, stackCartesianSeries: () => stackCartesianSeries, standaloneChartOptionsDefs: () => standaloneChartOptionsDefs, sunburstSeriesThemeableOptionsDef: () => sunburstSeriesThemeableOptionsDef, toHierarchyHighlightString: () => toHierarchyHighlightString, toHighlightString: () => toHighlightString, topologyChartOptionsDefs: () => topologyChartOptionsDefs, trailingAccumulatedValueProperty: () => trailingAccumulatedValueProperty, treemapSeriesThemeableOptionsDef: () => treemapSeriesThemeableOptionsDef, updateClipPath: () => updateClipPath, updateLabelNode: () => updateLabelNode, upsertNodeDatum: () => upsertNodeDatum, userInteraction: () => userInteraction, validateCrossLineValue: () => validateCrossLineValue, valueProperty: () => valueProperty, visibleRangeIndices: () => visibleRangeIndices, waterfallSeriesThemeableOptionsDef: () => waterfallSeriesThemeableOptionsDef }); // packages/ag-charts-community/src/chart/series/hierarchy/hierarchySeriesProperties.ts var HierarchyHighlightState = /* @__PURE__ */ ((HierarchyHighlightState2) => { HierarchyHighlightState2[HierarchyHighlightState2["None"] = 0] = "None"; HierarchyHighlightState2[HierarchyHighlightState2["Item"] = 1] = "Item"; HierarchyHighlightState2[HierarchyHighlightState2["OtherItem"] = 2] = "OtherItem"; HierarchyHighlightState2[HierarchyHighlightState2["Branch"] = 3] = "Branch"; HierarchyHighlightState2[HierarchyHighlightState2["OtherBranch"] = 4] = "OtherBranch"; return HierarchyHighlightState2; })(HierarchyHighlightState || {}); function toHierarchyHighlightString(state) { const unreachable = (a) => a; switch (state) { case 1 /* Item */: return "highlighted-item"; case 2 /* OtherItem */: return "unhighlighted-item"; case 3 /* Branch */: return "highlighted-branch"; case 4 /* OtherBranch */: return "unhighlighted-branch"; case 0 /* None */: return "none"; default: return unreachable(state); } } var HierarchySeriesProperties = class extends SeriesProperties { constructor() { super(...arguments); this.childrenKey = "children"; this.fills = Object.values(DEFAULT_FILLS); this.strokes = Object.values(DEFAULT_STROKES); } }; __decorateClass([ addFakeTransformToInstanceProperty ], HierarchySeriesProperties.prototype, "childrenKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HierarchySeriesProperties.prototype, "sizeKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HierarchySeriesProperties.prototype, "colorKey", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HierarchySeriesProperties.prototype, "colorName", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HierarchySeriesProperties.prototype, "fills", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HierarchySeriesProperties.prototype, "strokes", 2); __decorateClass([ addFakeTransformToInstanceProperty ], HierarchySeriesProperties.prototype, "colorRange", 2); // packages/ag-charts-community/src/chart/series/hierarchy/hierarchySeries.ts var _HierarchyNode = class _HierarchyNode { constructor(series, itemId, datumIndex, datum, sizeValue, colorValue, sumSize, depth, parent, children, style2) { this.series = series; this.itemId = itemId; this.datumIndex = datumIndex; this.datum = datum; this.sizeValue = sizeValue; this.colorValue = colorValue; this.sumSize = sumSize; this.depth = depth; this.parent = parent; this.children = children; this.style = style2; this.midPoint = { x: 0, y: 0 }; } get hasChildren() { return this.children.length > 0; } walk(callback2, order = _HierarchyNode.Walk.PreOrder) { if (order === _HierarchyNode.Walk.PreOrder) { callback2(this); } for (const child of this.children) { child.walk(callback2, order); } if (order === _HierarchyNode.Walk.PostOrder) { callback2(this); } } find(predicate) { if (predicate(this)) { return this; } for (const child of this.children) { const childResult = child.find(predicate); if (childResult != void 0) { return childResult; } } return void 0; } *[Symbol.iterator]() { yield this; for (const child of this.children) { yield* child; } } }; _HierarchyNode.Walk = { PreOrder: 0, PostOrder: 1 }; var HierarchyNode = _HierarchyNode; var HierarchySeries = class extends Series { constructor(moduleCtx) { super({ moduleCtx, pickModes: [1 /* NEAREST_NODE */, 0 /* EXACT_SHAPE_MATCH */] }); this.colorDomain = [0, 0]; this.maxDepth = 0; this.colorScale = new ColorScale(); this.animationState = new StateMachine( "empty", { empty: { update: { target: "ready", action: (data) => this.animateEmptyUpdateReady(data) }, reset: "empty", skip: "ready" }, ready: { updateData: "waiting", clear: "clearing", highlight: (data) => this.animateReadyHighlight(data), resize: (data) => this.animateReadyResize(data), reset: "empty", skip: "ready" }, waiting: { update: { target: "ready", action: (data) => this.animateWaitingUpdateReady(data) }, reset: "empty", skip: "ready" }, clearing: { update: { target: "empty", action: (data) => this.animateClearingUpdateEmpty(data) }, reset: "empty", skip: "ready" } }, () => this.checkProcessedDataAnimatable() ); } resetAnimation(phase) { if (phase === "initial") { this.animationState.transition("reset"); } else if (phase === "ready") { this.animationState.transition("skip"); } } processData() { const { NodeClass } = this; const { childrenKey, sizeKey, colorKey, colorRange } = this.properties; let maxDepth = 0; let minColor = Infinity; let maxColor = -Infinity; const createNode = (datum, indexPath, parent) => { const depth = parent.depth == null ? 0 : parent.depth + 1; const children = childrenKey == null ? void 0 : datum[childrenKey]; const isLeaf = children == null || children.length === 0; let sizeValue = sizeKey == null ? void 0 : datum[sizeKey]; if (Number.isFinite(sizeValue)) { sizeValue = Math.max(sizeValue, 0); } else { sizeValue = isLeaf ? 1 : 0; } const sumSize = sizeValue; maxDepth = Math.max(maxDepth, depth); const colorValue = colorKey == null ? void 0 : datum[colorKey]; if (typeof colorValue === "number") { minColor = Math.min(minColor, colorValue); maxColor = Math.max(maxColor, colorValue); } const style2 = this.getItemStyle({ datumIndex: indexPath, datum, depth, colorValue }, isLeaf, false); return appendChildren( new NodeClass( this, createDatumId(indexPath.join(";")), indexPath, datum, sizeValue, colorValue, sumSize, depth, parent, [], style2 ), children ); }; const appendChildren = (node, data) => { const { datumIndex } = node; if (data) { for (const [childIndex, datum] of data.entries()) { const child = createNode(datum, datumIndex.concat(childIndex), node); node.children.push(child); node.sumSize += child.sumSize; } } return node; }; const rootNode = appendChildren( new NodeClass(this, "root", [], void 0, 0, void 0, 0, void 0, void 0, [], {}), this.data?.data ); const colorDomain = [minColor, maxColor]; this.colorScale.domain = minColor < maxColor ? [minColor, maxColor] : [0, 1]; this.colorScale.range = colorRange ?? ["black"]; this.colorScale.update(); this.rootNode = rootNode; this.maxDepth = maxDepth; this.colorDomain = colorDomain; } update({ seriesRect }) { this.updateSelections(); this.updateNodes(); const animationData = this.getAnimationData(); const resize = this.checkResize(seriesRect); if (resize) { this.animationState.transition("resize", animationData); } this.animationState.transition("update", animationData); } resetAllAnimation(_data) { this.ctx.animationManager.stopByAnimationGroupId(this.id); } animateEmptyUpdateReady(data) { this.ctx.animationManager.skipCurrentBatch(); this.resetAllAnimation(data); } animateWaitingUpdateReady(data) { this.ctx.animationManager.skipCurrentBatch(); this.resetAllAnimation(data); } animateReadyHighlight(_data) { } animateReadyResize(data) { this.resetAllAnimation(data); } animateClearingUpdateEmpty(data) { this.ctx.animationManager.skipCurrentBatch(); this.resetAllAnimation(data); } getAnimationData() { return {}; } isProcessedDataAnimatable() { return true; } checkProcessedDataAnimatable() { if (!this.isProcessedDataAnimatable()) { this.ctx.animationManager.skipCurrentBatch(); } } findNodeDatum(itemId) { return this.rootNode?.find((n) => n.itemId === itemId); } dataCount() { return Number.NaN; } getSeriesDomain() { return { domain: [Number.NaN, Number.NaN] }; } getSeriesRange() { return [Number.NaN, Number.NaN]; } getLegendData(legendType) { const { colorKey, colorRange } = this.properties; const { id: seriesId, ctx: { legendManager }, visible } = this; return legendType === "gradient" && colorKey != null && colorRange != null ? [ { legendType: "gradient", enabled: visible && legendManager.getItemEnabled({ seriesId }), seriesId, series: this.getFormatterContext("color"), colorRange, colorDomain: this.colorDomain } ] : []; } getDatumIdFromData(node) { return node.datumIndex.join(":"); } getDatumId(node) { return this.getDatumIdFromData(node); } removeMeIndexPathForIndex(index) { return this.datumSelection.at(index + 1)?.datum.datumIndex ?? []; } removeMeIndexForIndexPath(indexPath) { for (const { index, datum } of this.datumSelection) { if (arraysEqual(datum.datumIndex, indexPath)) { return index - 1; } } return 0; } pickFocus(opts) { if (!this.rootNode?.children.length) return void 0; const index = clamp(0, opts.datumIndex - opts.datumIndexDelta, this.datumSelection.length - 1); const { datumIndexDelta: childDelta, otherIndexDelta: depthDelta } = opts; let path = this.removeMeIndexPathForIndex(index); const currentNode = path.reduce((n, childIndex) => n.children[childIndex], this.rootNode); if (depthDelta > 0 && currentNode.hasChildren) { path = [...path, 0]; } else if (depthDelta < 0 && path.length > 1) { path = path.slice(0, -1); } else if (depthDelta === 0 && childDelta !== 0) { const maxIndex = currentNode.parent.children.length - 1; path = path.slice(); path[path.length - 1] = clamp(0, path.at(-1) + childDelta, maxIndex); } const nextNode = path.reduce((n, childIndex) => n.children[childIndex], this.rootNode); const bounds = this.computeFocusBounds(this.datumSelection.at(index + 1)); if (bounds == null) return; return { datum: nextNode, datumIndex: this.removeMeIndexForIndexPath(path), otherIndex: nextNode.depth, bounds, clipFocusBox: true }; } getDatumAriaText(datum, description) { if (!(datum instanceof this.NodeClass)) { logger_exports.error(`datum is not HierarchyNode: ${JSON.stringify(datum)}`); return; } return this.ctx.localeManager.t("ariaAnnounceHierarchyDatum", { level: (datum.depth ?? -1) + 1, count: datum.children.length, description }); } getCategoryValue(_datumIndex) { return; } datumIndexForCategoryValue(_categoryValue) { return; } getActiveHighlightNode() { if (!this.properties.highlight.enabled) { return void 0; } const highlightedNode = this.ctx.highlightManager?.getActiveHighlight(); if (highlightedNode?.series !== this) { return void 0; } return highlightedNode; } getHierarchyHighlightState(isHighlight, highlightedNode, nodeDatum) { if (isHighlight) { return 1 /* Item */; } if (highlightedNode == null) { return 0 /* None */; } const nodeRoot = nodeDatum.datumIndex?.[0]; const highlightRoot = highlightedNode.datumIndex?.[0]; if (nodeRoot == null || highlightRoot == null) { return 0 /* None */; } return nodeRoot === highlightRoot ? 3 /* Branch */ : 4 /* OtherBranch */; } getHierarchyHighlightStyles(highlightState, highlight5) { switch (highlightState) { case 1 /* Item */: return mergeDefaults(highlight5.highlightedItem, highlight5.highlightedBranch); case 3 /* Branch */: return mergeDefaults(highlight5.unhighlightedItem, highlight5.highlightedBranch); case 4 /* OtherBranch */: return highlight5.unhighlightedBranch; default: return void 0; } } getHighlightStateString(_datum, isHighlight, datumIndex, _legendItemValues) { if (!this.properties.highlight.enabled) { return toHierarchyHighlightString(0 /* None */); } if (datumIndex == null) { return toHierarchyHighlightString(0 /* None */); } const nodeDatum = datumIndex.reduce((node, idx) => node?.children[idx], this.rootNode); const highlightedNode = this.getActiveHighlightNode(); if (nodeDatum == null) { return toHierarchyHighlightString(0 /* None */); } const state = this.getHierarchyHighlightState(isHighlight ?? false, highlightedNode, nodeDatum); return toHierarchyHighlightString(state); } }; // packages/ag-charts-community/src/chart/series/topology/mercatorScale.ts var radsInDeg = Math.PI / 180; var lonX = (lon) => lon * radsInDeg; var latY = (lat) => -Math.log(Math.tan(Math.PI * 0.25 + lat * radsInDeg * 0.5)); var xLon = (x) => x / radsInDeg; var yLat = (y) => (Math.atan(Math.exp(-y)) - Math.PI * 0.25) / (radsInDeg * 0.5); var MercatorScale = class _MercatorScale extends AbstractScale { constructor(domain, range3) { super(); this.domain = domain; this.range = range3; this.type = "mercator"; this.defaultTickCount = 0; this.bounds = _MercatorScale.bounds(domain); } static bounds(domain) { const [[lon0, lat0], [lon1, lat1]] = domain; const x0 = lonX(lon0); const y0 = latY(lat0); const x1 = lonX(lon1); const y1 = latY(lat1); return new BBox(Math.min(x0, x1), Math.min(y0, y1), Math.abs(x1 - x0), Math.abs(y1 - y0)); } static fixedScale() { return new _MercatorScale( [ [xLon(0), yLat(0)], [xLon(1), yLat(1)] ], [ [0, 0], [1, 1] ] ); } toDomain() { return; } normalizeDomains(...domains) { let x0 = -Infinity; let x1 = Infinity; let y0 = -Infinity; let y1 = Infinity; for (const input of domains) { const domain = input.domain; for (const [x, y] of domain) { x0 = Math.min(x, x0); x1 = Math.max(x, x1); y0 = Math.min(y, y0); y1 = Math.max(y, y1); } } return { domain: [ [x0, y0], [x1, y1] ], animatable: true }; } convert([lon, lat]) { const [[x0, y0], [x1, y1]] = this.range; const xScale = (x1 - x0) / this.bounds.width; const yScale = (y1 - y0) / this.bounds.height; return [(lonX(lon) - this.bounds.x) * xScale + x0, (latY(lat) - this.bounds.y) * yScale + y0]; } invert([x, y]) { const [[x0, y0], [x1, y1]] = this.range; const xScale = (x1 - x0) / this.bounds.width; const yScale = (y1 - y0) / this.bounds.height; return [xLon((x - x0) / xScale + this.bounds.x), yLat((y - y0) / yScale + this.bounds.y)]; } getDomainMinMax() { return unpackDomainMinMax(this.domain); } }; // packages/ag-charts-community/src/chart/axis/polarAxis.ts var PolarAxis = class extends Axis { constructor() { super(...arguments); this.shape = "polygon"; this.innerRadiusRatio = 0; this.defaultTickMinSpacing = 20; } update() { super.update(); this.tickLineGroup.visible = this.tick.enabled; this.tickLabelGroup.visible = this.label.enabled; } layoutCrossLines() { const sideFlag = this.label.getSideFlag(); const crosslinesVisible = this.hasDefinedDomain() || this.hasVisibleSeries(); const { rotation, parallelFlipRotation, regularFlipRotation } = this.calculateRotations(); for (const crossLine of this.crossLines) { crossLine.sideFlag = -sideFlag; crossLine.direction = rotation === -Math.PI / 2 ? "angle" /* Angle */ : "radius" /* Radius */; crossLine.parallelFlipRotation = parallelFlipRotation; crossLine.regularFlipRotation = regularFlipRotation; crossLine.calculateLayout?.(crosslinesVisible, this.reverse); } } updatePosition() { super.updatePosition(); const translationX = Math.floor(this.translation.x); const translationY = Math.floor(this.translation.y); this.tickLineGroup.translationX = translationX; this.tickLineGroup.translationY = translationY; this.tickLabelGroup.translationX = translationX; this.tickLabelGroup.translationY = translationY; this.crossLineRangeGroup.translationX = translationX; this.crossLineRangeGroup.translationY = translationY; this.crossLineLineGroup.translationX = translationX; this.crossLineLineGroup.translationY = translationY; this.crossLineLabelGroup.translationX = translationX; this.crossLineLabelGroup.translationY = translationY; this.tickLabelGroupSelection.each(resetAxisLabelSelectionFn()); } computeLabelsBBox(_options, _seriesRect) { return null; } computeRange() { } getAxisLinePoints() { return void 0; } }; __decorateClass([ addFakeTransformToInstanceProperty ], PolarAxis.prototype, "shape", 2); __decorateClass([ addFakeTransformToInstanceProperty ], PolarAxis.prototype, "innerRadiusRatio", 2); // packages/ag-charts-community/src/chart/crossline/crossLineLabelPosition.ts var horizontalCrosslineTranslationDirections = { top: { xTranslationDirection: 0, yTranslationDirection: -1 }, bottom: { xTranslationDirection: 0, yTranslationDirection: 1 }, left: { xTranslationDirection: -1, yTranslationDirection: 0 }, right: { xTranslationDirection: 1, yTranslationDirection: 0 }, "top-left": { xTranslationDirection: 1, yTranslationDirection: -1 }, "top-right": { xTranslationDirection: -1, yTranslationDirection: -1 }, "bottom-left": { xTranslationDirection: 1, yTranslationDirection: 1 }, "bottom-right": { xTranslationDirection: -1, yTranslationDirection: 1 }, inside: { xTranslationDirection: 0, yTranslationDirection: 0 }, "inside-left": { xTranslationDirection: 1, yTranslationDirection: 0 }, "inside-right": { xTranslationDirection: -1, yTranslationDirection: 0 }, "inside-top": { xTranslationDirection: 0, yTranslationDirection: 1 }, "inside-bottom": { xTranslationDirection: 0, yTranslationDirection: -1 }, "inside-top-left": { xTranslationDirection: 1, yTranslationDirection: 1 }, "inside-bottom-left": { xTranslationDirection: 1, yTranslationDirection: -1 }, "inside-top-right": { xTranslationDirection: -1, yTranslationDirection: 1 }, "inside-bottom-right": { xTranslationDirection: -1, yTranslationDirection: -1 } }; var verticalCrossLineTranslationDirections = { top: { xTranslationDirection: 1, yTranslationDirection: 0 }, bottom: { xTranslationDirection: -1, yTranslationDirection: 0 }, left: { xTranslationDirection: 0, yTranslationDirection: -1 }, right: { xTranslationDirection: 0, yTranslationDirection: 1 }, "top-left": { xTranslationDirection: -1, yTranslationDirection: -1 }, "top-right": { xTranslationDirection: -1, yTranslationDirection: 1 }, "bottom-left": { xTranslationDirection: 1, yTranslationDirection: -1 }, "bottom-right": { xTranslationDirection: 1, yTranslationDirection: 1 }, inside: { xTranslationDirection: 0, yTranslationDirection: 0 }, "inside-left": { xTranslationDirection: 0, yTranslationDirection: 1 }, "inside-right": { xTranslationDirection: 0, yTranslationDirection: -1 }, "inside-top": { xTranslationDirection: -1, yTranslationDirection: 0 }, "inside-bottom": { xTranslationDirection: 1, yTranslationDirection: 0 }, "inside-top-left": { xTranslationDirection: -1, yTranslationDirection: 1 }, "inside-bottom-left": { xTranslationDirection: 1, yTranslationDirection: 1 }, "inside-top-right": { xTranslationDirection: -1, yTranslationDirection: -1 }, "inside-bottom-right": { xTranslationDirection: 1, yTranslationDirection: -1 } }; function calculateLabelTranslation({ yDirection, padding: padding2 = 0, position = "top", bbox }) { const crossLineTranslationDirections = yDirection ? horizontalCrosslineTranslationDirections : verticalCrossLineTranslationDirections; const { xTranslationDirection, yTranslationDirection } = crossLineTranslationDirections[position]; const xTranslation = xTranslationDirection * (padding2 + bbox.width / 2); const yTranslation = yTranslationDirection * (padding2 + bbox.height / 2); return { xTranslation, yTranslation }; } // packages/ag-charts-community/src/scene/shape/svgPath.ts var SvgPath = class extends Path { constructor(d = "") { super(); this._d = ""; this.d = d; } get d() { return this._d; } set d(d) { if (d === this._d) return; this._d = d; this.path.clear(); this.path.appendSvg(d); this.checkPathDirty(); } }; var TranslatableSvgPath = class extends Translatable(SvgPath) { isPointInPath(x, y) { return super.isPointInPath(x - this.translationX, y - this.translationY); } }; // packages/ag-charts-community/src/scale/approximateOrdinalTimeScale.ts var ApproximateOrdinalTimeScale = class _ApproximateOrdinalTimeScale extends OrdinalTimeScale { static is(value) { return value instanceof _ApproximateOrdinalTimeScale; } /** * Set the source scale that this approximate scale delegates to. * All property access (domain, range, etc.) will be delegated to the source. */ setSourceScale(scale2) { this._sourceScale = scale2; const delegateProperty = (prop) => { Object.defineProperty(this, prop, { get: () => scale2[prop], set: (value) => { scale2[prop] = value; }, configurable: true }); }; delegateProperty("domain"); delegateProperty("range"); delegateProperty("paddingInner"); delegateProperty("paddingOuter"); delegateProperty("round"); const delegateReadOnly = (prop) => { Object.defineProperty(this, prop, { get: () => scale2[prop], configurable: true }); }; delegateReadOnly("bandwidth"); delegateReadOnly("step"); delegateReadOnly("inset"); delegateReadOnly("rawBandwidth"); } // Delegate bands to source scale (read-only) get bands() { return this._sourceScale?.bands ?? super.bands; } // Delegate refresh to ensure source scale is up-to-date refresh() { this._sourceScale?.["refresh"]?.(); } // Delegate ordinalRange to use source scale's computed values ordinalRange(i) { if (this._sourceScale) { return this._sourceScale.ordinalRange(i); } return super.ordinalRange(i); } // Delegate convert to source scale but use our findIndex convert(d, options) { this.refresh(); const i = this.findIndex(d, options?.alignment); if (i == null || i < 0 || i >= this.bands.length) { return Number.NaN; } return this.ordinalRange(i); } findIndex(value, alignment = 0 /* Leading */) { if (value == null) { return void 0; } const { bands, reversed } = this; const n = bands.length; if (n === 0) return void 0; if (n === 1) return 0; const firstBand = bands[0]; const lastBand = bands[n - 1]; if (firstBand == null || lastBand == null) { return this._sourceScale?.findIndex(value, alignment); } const target = value.valueOf(); const first2 = firstBand.valueOf(); const last = lastBand.valueOf(); const ratio2 = (target - first2) / (last - first2); const rawIndex = reversed ? (1 - ratio2) * (n - 1) : ratio2 * (n - 1); if (alignment === 0 /* Leading */) { return Math.max(0, Math.min(n - 1, Math.floor(rawIndex))); } else { return Math.max(0, Math.min(n - 1, Math.ceil(rawIndex))); } } }; // packages/ag-charts-community/src/scene/image.ts var Image2 = class extends Node { constructor(sourceImage) { super(); this.sourceImage = sourceImage; this.x = 0; this.y = 0; this.width = 0; this.height = 0; this.opacity = 1; } render(renderCtx) { const { ctx } = renderCtx; const image = this.sourceImage; if (image) { ctx.globalAlpha = this.opacity; ctx.drawImage(image, 0, 0, image.width, image.height, this.x, this.y, this.width, this.height); } super.render(renderCtx); } }; __decorateClass([ SceneChangeDetection() ], Image2.prototype, "x", 2); __decorateClass([ SceneChangeDetection() ], Image2.prototype, "y", 2); __decorateClass([ SceneChangeDetection() ], Image2.prototype, "width", 2); __decorateClass([ SceneChangeDetection() ], Image2.prototype, "height", 2); __decorateClass([ SceneChangeDetection() ], Image2.prototype, "opacity", 2); // packages/ag-charts-community/src/scene/shape/arc.ts var Arc2 = class extends Path { constructor() { super(...arguments); this.centerX = 0; this.centerY = 0; this.radius = 10; this.startAngle = 0; this.endAngle = Math.PI * 2; this.counterClockwise = false; this.type = 0 /* Open */; } get fullPie() { return isNumberEqual(normalizeAngle360(this.startAngle), normalizeAngle360(this.endAngle)); } updatePath() { const path = this.path; path.clear(); path.arc(this.centerX, this.centerY, this.radius, this.startAngle, this.endAngle, this.counterClockwise); if (this.type === 1 /* Chord */) { path.closePath(); } else if (this.type === 2 /* Round */ && !this.fullPie) { path.lineTo(this.centerX, this.centerY); path.closePath(); } } computeBBox() { return new BBox(this.centerX - this.radius, this.centerY - this.radius, this.radius * 2, this.radius * 2); } isPointInPath(x, y) { const bbox = this.getBBox(); return this.type !== 0 /* Open */ && bbox.containsPoint(x, y) && this.path.isPointInPath(x, y); } }; Arc2.className = "Arc"; __decorateClass([ SceneChangeDetection() ], Arc2.prototype, "centerX", 2); __decorateClass([ SceneChangeDetection() ], Arc2.prototype, "centerY", 2); __decorateClass([ SceneChangeDetection() ], Arc2.prototype, "radius", 2); __decorateClass([ SceneChangeDetection() ], Arc2.prototype, "startAngle", 2); __decorateClass([ SceneChangeDetection() ], Arc2.prototype, "endAngle", 2); __decorateClass([ SceneChangeDetection() ], Arc2.prototype, "counterClockwise", 2); __decorateClass([ SceneChangeDetection() ], Arc2.prototype, "type", 2); // packages/ag-charts-community/src/scene/shape/radialColumnShape.ts function rotatePoint2(x, y, rotation) { const radius = Math.hypot(x, y); const angle2 = Math.atan2(y, x); const rotated = angle2 + rotation; return { x: Math.cos(rotated) * radius, y: Math.sin(rotated) * radius }; } var RadialColumnShape = class extends Path { constructor() { super(...arguments); this.isBeveled = true; this.columnWidth = 0; this.startAngle = 0; this.endAngle = 0; this.outerRadius = 0; this.innerRadius = 0; this.axisInnerRadius = 0; this.axisOuterRadius = 0; } set cornerRadius(_value) { } computeBBox() { const { columnWidth } = this; const [innerRadius, outerRadius] = this.normalizeRadii(this.innerRadius, this.outerRadius); const rotation = this.getRotation(); const left = -columnWidth / 2; const right = columnWidth / 2; const top = -outerRadius; const bottom = -innerRadius; let x0 = Infinity; let y0 = Infinity; let x1 = -Infinity; let y1 = -Infinity; for (let i = 0; i < 4; i += 1) { const { x, y } = rotatePoint2(i % 2 === 0 ? left : right, i < 2 ? top : bottom, rotation); x0 = Math.min(x, x0); y0 = Math.min(y, y0); x1 = Math.max(x, x1); y1 = Math.max(y, y1); } return new BBox(x0, y0, x1 - x0, y1 - y0); } getRotation() { const { startAngle, endAngle } = this; const midAngle = angleBetween(startAngle, endAngle); return normalizeAngle360(startAngle + midAngle / 2 + Math.PI / 2); } normalizeRadii(innerRadius, outerRadius) { if (innerRadius > outerRadius) { return [outerRadius, innerRadius]; } return [innerRadius, outerRadius]; } updatePath() { const { isBeveled } = this; if (isBeveled) { this.updateBeveledPath(); } else { this.updateRectangularPath(); } this.checkPathDirty(); } updateRectangularPath() { const { columnWidth, path } = this; const [innerRadius, outerRadius] = this.normalizeRadii(this.innerRadius, this.outerRadius); const left = -columnWidth / 2; const right = columnWidth / 2; const top = -outerRadius; const bottom = -innerRadius; const rotation = this.getRotation(); const points = [ [left, bottom], [left, top], [right, top], [right, bottom] ].map(([x, y]) => rotatePoint2(x, y, rotation)); path.clear(true); path.moveTo(points[0].x, points[0].y); path.lineTo(points[1].x, points[1].y); path.lineTo(points[2].x, points[2].y); path.lineTo(points[3].x, points[3].y); path.closePath(); } calculateCircleIntersection(x, radiusSquared) { const xSquared = x * x; if (radiusSquared < xSquared) { return null; } const y = -Math.sqrt(radiusSquared - xSquared); const angle2 = Math.atan2(y, x); return { y, angle: angle2 }; } calculateBothIntersections(left, right, radius) { const radiusSquared = radius * radius; const leftInt = this.calculateCircleIntersection(left, radiusSquared); const rightInt = this.calculateCircleIntersection(right, radiusSquared); if (!leftInt || !rightInt) { return null; } return { left: leftInt, right: rightInt }; } calculateAxisOuterIntersections(left, right, axisOuterRadius) { const axisOuterRadiusSquared = axisOuterRadius * axisOuterRadius; const axisOuterLeft = this.calculateCircleIntersection(left, axisOuterRadiusSquared); const axisOuterRight = this.calculateCircleIntersection(right, axisOuterRadiusSquared); if (!axisOuterLeft || !axisOuterRight) { return null; } return { left: axisOuterLeft, right: axisOuterRight, radiusSquared: axisOuterRadiusSquared }; } moveToRotated(x, y, rotation) { const point = rotatePoint2(x, y, rotation); this.path.moveTo(point.x, point.y); } lineToRotated(x, y, rotation) { const point = rotatePoint2(x, y, rotation); this.path.lineTo(point.x, point.y); } renderTopWithCornerClipping(axisOuterRadius, axisOuter, geometry) { const { path } = this; const { right, top, rotation } = geometry; const topSquared = top * top; const topIntersectionSquared = axisOuter.radiusSquared - topSquared; if (topIntersectionSquared <= 0) { this.lineToRotated(right, axisOuter.right.y, rotation); path.arc(0, 0, axisOuterRadius, rotation + axisOuter.right.angle, rotation + axisOuter.left.angle, true); } else { const topIntersectionX = Math.sqrt(topIntersectionSquared); const topRightAngle = Math.atan2(top, topIntersectionX); const topLeftAngle = Math.atan2(top, -topIntersectionX); this.lineToRotated(right, axisOuter.right.y, rotation); path.arc(0, 0, axisOuterRadius, rotation + axisOuter.right.angle, rotation + topRightAngle, true); this.lineToRotated(-topIntersectionX, top, rotation); path.arc(0, 0, axisOuterRadius, rotation + topLeftAngle, rotation + axisOuter.left.angle, true); } } updateBeveledPath() { const { columnWidth, path, axisInnerRadius, axisOuterRadius } = this; const [innerRadius, outerRadius] = this.normalizeRadii(this.innerRadius, this.outerRadius); const left = -columnWidth / 2; const right = columnWidth / 2; const top = -outerRadius; const bottom = -innerRadius; const rotation = this.getRotation(); const isTouchingInner = isNumberEqual(innerRadius, axisInnerRadius); const isTouchingOuter = isNumberEqual(outerRadius, axisOuterRadius); const topCornersBreach = Math.hypot(left, top) > axisOuterRadius || Math.hypot(right, top) > axisOuterRadius; if (!isTouchingInner && !isTouchingOuter && !topCornersBreach) { this.updateRectangularPath(); return; } const inner = isTouchingInner ? this.calculateBothIntersections(left, right, innerRadius) : null; const outer = isTouchingOuter ? this.calculateBothIntersections(left, right, outerRadius) : null; const axisOuter = topCornersBreach ? this.calculateAxisOuterIntersections(left, right, axisOuterRadius) : null; if (isTouchingInner && !inner || isTouchingOuter && !outer || topCornersBreach && !axisOuter) { this.updateRectangularPath(); return; } path.clear(true); const geometry = { left, right, top, bottom, rotation }; if (inner) { this.moveToRotated(left, inner.left.y, rotation); } else { this.moveToRotated(left, bottom, rotation); } if (inner) { path.arc(0, 0, innerRadius, rotation + inner.left.angle, rotation + inner.right.angle, false); } else { this.lineToRotated(right, bottom, rotation); } if (outer) { this.lineToRotated(right, outer.right.y, rotation); path.arc(0, 0, outerRadius, rotation + outer.right.angle, rotation + outer.left.angle, true); } else if (axisOuter) { this.renderTopWithCornerClipping(axisOuterRadius, axisOuter, geometry); } else { this.lineToRotated(right, top, rotation); this.lineToRotated(left, top, rotation); } path.closePath(); } }; RadialColumnShape.className = "RadialColumnShape"; __decorateClass([ SceneChangeDetection() ], RadialColumnShape.prototype, "isBeveled", 2); __decorateClass([ SceneChangeDetection() ], RadialColumnShape.prototype, "columnWidth", 2); __decorateClass([ SceneChangeDetection() ], RadialColumnShape.prototype, "startAngle", 2); __decorateClass([ SceneChangeDetection() ], RadialColumnShape.prototype, "endAngle", 2); __decorateClass([ SceneChangeDetection() ], RadialColumnShape.prototype, "outerRadius", 2); __decorateClass([ SceneChangeDetection() ], RadialColumnShape.prototype, "innerRadius", 2); __decorateClass([ SceneChangeDetection() ], RadialColumnShape.prototype, "axisInnerRadius", 2); __decorateClass([ SceneChangeDetection() ], RadialColumnShape.prototype, "axisOuterRadius", 2); function getRadialColumnWidth(startAngle, endAngle, axisOuterRadius, columnWidthRatio, maxColumnWidthRatio) { const rotation = angleBetween(startAngle, endAngle); const pad2 = rotation * (1 - columnWidthRatio) / 2; startAngle += pad2; endAngle -= pad2; if (rotation < 1e-3) { return 2 * axisOuterRadius * maxColumnWidthRatio; } if (rotation >= 2 * Math.PI) { const midAngle = startAngle + rotation / 2; startAngle = midAngle - Math.PI; endAngle = midAngle + Math.PI; } const startX = axisOuterRadius * Math.cos(startAngle); const startY = axisOuterRadius * Math.sin(startAngle); const endX = axisOuterRadius * Math.cos(endAngle); const endY = axisOuterRadius * Math.sin(endAngle); const colWidth = Math.floor(Math.hypot(startX - endX, startY - endY)); const maxWidth = 2 * axisOuterRadius * maxColumnWidthRatio; return Math.max(1, Math.min(maxWidth, colWidth)); } // packages/ag-charts-community/src/widget/menuItemWidget.ts var MenuItemWidget = class extends AbstractButtonWidget { constructor() { super(createElement("div"), "menuitem"); } }; var MenuItemRadioWidget = class extends AbstractButtonWidget { constructor() { super(createElement("div"), "menuitemradio"); } setChecked(checked) { setAttribute(this.elem, "aria-checked", checked); } }; // packages/ag-charts-community/src/widget/menuWidget.ts var closeKeys = ["Escape", "ArrowLeft"]; var MenuWidget = class _MenuWidget extends RovingTabContainerWidget { constructor(orientation = "vertical") { super(orientation, "menu"); this.handleMouseEnter = (ev, current) => { if (!current.hasPopup()) { this.expandSubMenu(ev, void 0); } }; this.handleMouseMove = (_ev, current) => { current.focus({ preventScroll: true }); }; } destructor() { this.collapse({ mode: "2" /* DESTROY */ }); } addSeparator() { const sep = getDocument().createElement("div"); setAttribute(sep, "role", "separator"); this.elem.appendChild(sep); return sep; } onChildAdded(child) { super.onChildAdded(child); child.addListener("mouseenter", this.handleMouseEnter); child.addListener("mousemove", this.handleMouseMove); } onChildRemoved(child) { super.onChildRemoved(child); child.removeListener("mouseenter", this.handleMouseEnter); child.removeListener("mousemove", this.handleMouseMove); } addSubMenu() { const subMenuButton = new MenuItemWidget(); const subMenu = new _MenuWidget(this.orientation); subMenu.id = createElementId(); const expand = () => { this.collapseExpandedSubMenu(subMenu); subMenuButton.expandControlled(); }; const arrowRightOpener = (ev) => { if (hasNoModifiers(ev.sourceEvent) && ev.sourceEvent.code === "ArrowRight") { this.collapseExpandedSubMenu(subMenu); subMenuButton.expandControlled(); } }; subMenuButton.setControlled(subMenu); subMenuButton.setAriaHasPopup("menu"); subMenuButton.addListener("click", expand); subMenuButton.addListener("mouseenter", expand); subMenuButton.addListener("keydown", arrowRightOpener); this.addChild(subMenuButton); return { subMenuButton, subMenu }; } expandSubMenu(ev, subMenu) { const { expansionScope } = this; if (!expansionScope) return; this.collapseExpandedSubMenu(subMenu); subMenu?.expand(ev); } collapseExpandedSubMenu(newSubMenu) { const { expansionScope } = this; if (!expansionScope) return; expansionScope.expandedSubMenu?.collapse({ mode: "4" /* SIDLING_OPENED */ }); expansionScope.expandedSubMenu = newSubMenu; } expand(opts) { if (this.expansionScope != null) return; this.expansionScope = { lastFocus: getLastFocus(opts.sourceEvent), expandedSubMenu: void 0, abort: () => this.collapse({ mode: "1" /* ABORT */ }), close: () => this.collapse({ mode: "0" /* CLOSE */ }), removers: new CleanupRegistry() }; const scope = this.expansionScope; const buttons = this.children.map((value) => value.getElement()); setAttribute(scope.lastFocus, "aria-expanded", true); scope.removers.register( addMouseCloseListener(this.elem, scope.abort), addTouchCloseListener(this.elem, scope.abort), ...this.children.map((child) => addEscapeEventListener(child.getElement(), scope.close, closeKeys)), opts?.overrideFocusVisible && addOverrideFocusVisibleEventListener(this.elem, buttons, opts.overrideFocusVisible) ); this.internalListener?.dispatch("expand-widget", this, { type: "expand-widget" }); this.children[0]?.focus({ preventScroll: true }); } collapse(opts) { const { mode = "0" /* CLOSE */ } = opts ?? {}; if (this.expansionScope === void 0) return; const { lastFocus, removers, expandedSubMenu } = this.expansionScope; this.expansionScope = void 0; expandedSubMenu?.collapse({ mode: "3" /* PARENT_CLOSED */ }); setAttribute(lastFocus, "aria-expanded", false); if (mode === "0" /* CLOSE */) { lastFocus?.focus({ preventScroll: true }); } removers.flush(); this.internalListener?.dispatch("collapse-widget", this, { type: "collapse-widget", mode }); } }; // packages/ag-charts-community/src/components/popover/popover.ts var canvasOverlay = "canvas-overlay"; var Popover = class extends AbstractModuleInstance { constructor(ctx, id, options) { super(); this.ctx = ctx; this.hideFns = []; this.setOwnedWidget = /* @__PURE__ */ (() => { let ownedWidget; return (owns) => { ownedWidget?.destroy(); ownedWidget = owns; }; })(); this.moduleId = `popover-${id}`; if (options?.detached) { this.element = createElement("div"); } else { this.element = ctx.domManager.addChild(canvasOverlay, this.moduleId); } this.element.setAttribute("role", "presentation"); this.hideFns.push(() => this.setOwnedWidget(void 0)); this.cleanup.register(() => ctx.domManager.removeChild(canvasOverlay, this.moduleId)); } attachTo(popover) { if (this.element.parentElement) return; popover.element.append(this.element); } hide(opts) { const { lastFocus = this.lastFocus } = opts ?? {}; if (this.element.children.length === 0) return; for (const fn of this.hideFns) { fn(); } lastFocus?.focus(); this.lastFocus = void 0; } removeChildren() { this.element.replaceChildren(); } initPopoverElement(popover, options) { if (!this.element.parentElement) { throw new Error("Can not show popover that has not been attached to a parent."); } popover ?? (popover = createElement("div", "ag-charts-popover")); popover.classList.toggle("ag-charts-popover", true); if (options.ariaLabel != null) { popover.setAttribute("aria-label", options.ariaLabel); } if (options.class != null) { popover.classList.add(options.class); } this.element.replaceChildren(popover); return popover; } showWidget(controller, owns, options) { this.setOwnedWidget(owns); this.initPopoverElement(owns.getElement(), options); owns.addListener("collapse-widget", () => { controller.setControlled(void 0); this.setOwnedWidget(void 0); }); controller.setControlled(owns); controller.expandControlled(); } showWithChildren(children, options) { const popover = this.initPopoverElement(void 0, options); popover.replaceChildren(...children); this.hideFns.push(() => this.removeChildren()); if (options.onHide) { this.hideFns.push(options.onHide); } if (options.initialFocus && options.sourceEvent) { const lastFocus = getLastFocus(options.sourceEvent); if (lastFocus !== void 0) { this.lastFocus = lastFocus; this.initialFocus = options.initialFocus; } } return popover; } getPopoverElement() { return this.element.firstElementChild; } updatePosition(position) { const popover = this.getPopoverElement(); if (!popover) return; popover.style.setProperty("right", "unset"); popover.style.setProperty("bottom", "unset"); if (position.x != null) popover.style.setProperty("left", `${Math.floor(position.x)}px`); if (position.y != null) popover.style.setProperty("top", `${Math.floor(position.y)}px`); this.initialFocus?.focus(); this.initialFocus = void 0; } }; // packages/ag-charts-community/src/components/popover/anchoredPopover.ts var AnchoredPopover = class extends Popover { setAnchor(anchor, fallbackAnchor) { this.anchor = anchor; this.fallbackAnchor = fallbackAnchor; this.updatePosition(anchor); this.repositionWithinBounds(); } updateAnchor(options) { const anchor = options.anchor ?? this.anchor; const fallbackAnchor = options.fallbackAnchor ?? this.fallbackAnchor; if (anchor) { this.setAnchor(anchor, fallbackAnchor); } } showWidget(controller, owns, options) { super.showWidget(controller, owns, options); this.updateAnchor(options); } showWithChildren(children, options) { const popover = super.showWithChildren(children, options); this.updateAnchor(options); getWindow().requestAnimationFrame(() => { this.repositionWithinBounds(); }); return popover; } repositionWithinBounds() { const { anchor, ctx, fallbackAnchor } = this; const popover = this.getPopoverElement(); if (!anchor || !popover) return; const canvasRect = ctx.domManager.getBoundingClientRect(); const { offsetWidth: width2, offsetHeight: height2 } = popover; let x = clamp(0, anchor.x, canvasRect.width - width2); let y = clamp(0, anchor.y, canvasRect.height - height2); if (x !== anchor.x && fallbackAnchor?.x != null) { x = clamp(0, fallbackAnchor.x - width2, canvasRect.width - width2); } if (y !== anchor.y && fallbackAnchor?.y != null) { y = clamp(0, fallbackAnchor.y - height2, canvasRect.height - height2); } this.updatePosition({ x, y }); } }; // packages/ag-charts-community/src/components/menu/menu.ts var Menu = class extends AnchoredPopover { show(controller, options) { const menu = new MenuWidget("vertical"); for (const item of options.items) { menu.addChild(this.createRow(options, item, menu)); } menu.addClass("ag-charts-menu"); this.showWidget(controller, menu, options); } allocRow(options, item) { if (options.menuItemRole == null || options.menuItemRole === "menuitem") { return new MenuItemWidget(); } else { options.menuItemRole; const result = new MenuItemRadioWidget(); result.setChecked(options.value === item.value); return result; } } createRow(options, item, menu) { const active = item.value === options.value; const row = this.allocRow(options, item); row.addClass("ag-charts-menu__row"); row.toggleClass(`ag-charts-menu__row--active`, active); if (typeof item.value === "string") { row.getElement().dataset.popoverId = item.value; } if (item.icon != null) { const icon = createElement("span", `ag-charts-menu__icon ${getIconClassNames(item.icon)}`); row.getElement().appendChild(icon); } const strokeWidthVisible = item.strokeWidth != null; if (strokeWidthVisible) { row.toggleClass(`ag-charts-menu__row--stroke-width-visible`, strokeWidthVisible); row.setCSSVariable("--strokeWidth", strokeWidthVisible ? `${item.strokeWidth}px` : null); } if (item.label != null) { const label = createElement("span", "ag-charts-menu__label"); label.textContent = this.ctx.localeManager.t(item.label); row.getElement().appendChild(label); } if ("altText" in item) { row.setAriaLabel(this.ctx.localeManager.t(item.altText)); } row.addListener("click", ({ sourceEvent }) => { options.onPress?.(item); sourceEvent.preventDefault(); sourceEvent.stopPropagation(); menu.collapse(); }); return row; } }; // packages/ag-charts-community/src/components/popover/draggablePopover.ts var DraggablePopover = class extends Popover { constructor() { super(...arguments); this.dragged = false; } setDragHandle(dragHandle) { dragHandle.addListener("drag-start", (event) => { dragHandle.addClass(this.dragHandleDraggingClass); this.onDragStart(event); }); dragHandle.addListener("drag-move", this.onDragMove.bind(this)); dragHandle.addListener("drag-end", () => { dragHandle.removeClass(this.dragHandleDraggingClass); this.onDragEnd.bind(this); }); } onDragStart(event) { const popover = this.getPopoverElement(); if (!popover) return; event.sourceEvent.preventDefault(); this.dragged = true; this.dragStartState = { client: vector_exports.from(event.clientX, event.clientY), position: vector_exports.from( Number(popover.style.getPropertyValue("left").replace("px", "")), Number(popover.style.getPropertyValue("top").replace("px", "")) ) }; } onDragMove(event) { const { dragStartState } = this; const popover = this.getPopoverElement(); if (!dragStartState || !popover) return; const offset = vector_exports.sub(vector_exports.from(event.clientX, event.clientY), dragStartState.client); const position = vector_exports.add(dragStartState.position, offset); const bounds = this.ctx.domManager.getBoundingClientRect(); const partialPosition = {}; if (position.x >= 0 && position.x + popover.offsetWidth <= bounds.width) { partialPosition.x = position.x; } if (position.y >= 0 && position.y + popover.offsetHeight <= bounds.height) { partialPosition.y = position.y; } this.updatePosition(partialPosition); } onDragEnd() { this.dragStartState = void 0; } }; // packages/ag-charts-community/src/components/toolbar/toolbarButtonWidget.ts var ARIA_HASPOPUP = { "disjoint-channel": "false", "fibonacci-menu": "menu", "fibonacci-retracement": "false", "fibonacci-retracement-trend-based": "false", "fill-color": "dialog", "horizontal-line": "false", "line-color": "dialog", "line-menu": "menu", "line-stroke-width": "menu", "line-style-type": "menu", "measurer-menu": "menu", "pan-end": "false", "pan-left": "false", "pan-right": "false", "pan-start": "false", "parallel-channel": "false", "shape-menu": "menu", "text-color": "dialog", "text-menu": "menu", "text-size": "menu", "vertical-line": "false", "zoom-in": "false", "zoom-out": "false", callout: "false", clear: "false", comment: "false", delete: "false", line: "false", lock: "false", menu: "menu", note: "false", reset: "false", settings: "dialog", text: "false" }; function getAriaHasPopupOfValue(value) { if (typeof value !== "string") return "false"; return ARIA_HASPOPUP[value]; } var ToolbarButtonWidget = class extends ButtonWidget { constructor(localeManager) { super(); this.localeManager = localeManager; } update(options) { const { localeManager } = this; if (options.tooltip) { const tooltip = localeManager.t(options.tooltip); if (tooltip !== this.lastTooltip) { this.elem.title = tooltip; this.lastTooltip = tooltip; } } let innerHTML = ""; if (options.icon != null) { innerHTML = ``; } if (options.label != null) { const label = localeManager.t(options.label); innerHTML = `${innerHTML}${label}`; } const haspopup = getAriaHasPopupOfValue(options.value); if (haspopup == "false") { this.setAriaHasPopup(void 0); this.setAriaExpanded(void 0); } else { this.setAriaHasPopup(haspopup); this.setAriaExpanded(false); } if (innerHTML !== this.lastInnerHTML) { this.elem.innerHTML = innerHTML; this.lastInnerHTML = innerHTML; } } setChecked(checked) { setAttribute(this.elem, "aria-checked", checked); } }; // packages/ag-charts-community/src/components/toolbar/toolbar.ts var BUTTON_ACTIVE_CLASS = "ag-charts-toolbar__button--active"; var BaseToolbar = class extends ToolbarWidget { constructor({ eventsHub, localeManager }, ariaLabelId, orientation) { super(orientation); this.ariaLabelId = ariaLabelId; this.horizontalSpacing = 10; this.verticalSpacing = 10; this.events = new Listeners(); this.hasPrefix = false; this.buttonWidgets = []; this.updateAriaLabel = () => this.setAriaLabel(this.localeManager.t(this.ariaLabelId)); this.eventsHub = eventsHub; this.localeManager = localeManager; this.addClass("ag-charts-toolbar"); this.toggleClass("ag-charts-toolbar--horizontal", orientation === "horizontal"); this.toggleClass("ag-charts-toolbar--vertical", orientation === "vertical"); this.eventsHub.on("locale:change", this.updateAriaLabel); this.updateAriaLabel(); } setAriaLabelId(ariaLabelId) { this.ariaLabelId = ariaLabelId; this.updateAriaLabel(); } addToolbarListener(eventType, handler) { return this.events.addListener(eventType, handler); } clearButtons() { this.expanded?.collapse({ mode: "2" /* DESTROY */ }); for (const button of this.buttonWidgets) { button.destroy(); } this.buttonWidgets.splice(0); } updateButtons(buttons) { const { buttonWidgets } = this; for (const [index, button] of buttons.entries()) { const buttonWidget = this.buttonWidgets.at(index) ?? this.createButton(index, button); buttonWidget.update(button); } for (let index = buttons.length; index < buttonWidgets.length; index++) { const button = this.buttonWidgets.at(index); button?.destroy(); } this.buttonWidgets.splice(buttons.length); this.refreshButtonClasses(); } updateButtonByIndex(index, button) { this.buttonWidgets.at(index)?.update(button); } clearActiveButton() { for (const button of this.buttonWidgets) { button.toggleClass(BUTTON_ACTIVE_CLASS, false); } } toggleActiveButtonByIndex(index) { if (index === -1) return; for (const [buttonIndex, button] of this.buttonWidgets.entries()) { button.toggleClass(BUTTON_ACTIVE_CLASS, index != null && index === buttonIndex); } } toggleButtonEnabledByIndex(index, enabled) { if (index === -1) return; this.buttonWidgets.at(index)?.setEnabled(enabled); } toggleSwitchCheckedByIndex(index, checked) { if (index === -1) return; this.buttonWidgets.at(index)?.setChecked(checked); } getButtonBounds() { return this.buttonWidgets.map((buttonWidget) => this.getButtonWidgetBounds(buttonWidget)); } setButtonHiddenByIndex(index, hidden) { this.buttonWidgets.at(index)?.setHidden(hidden); } getButtonWidgetBounds(buttonWidget) { const parent = this.getBounds(); const bounds = buttonWidget.getBounds(); return new BBox(bounds.x + parent.x, bounds.y + parent.y, bounds.width, bounds.height); } refreshButtonClasses() { const { buttonWidgets, hasPrefix } = this; let first2; let last; let section; for (const [index, buttonWidget] of buttonWidgets.entries()) { first2 = !hasPrefix && index === 0 || section != buttonWidget.section; last = index === buttonWidgets.length - 1 || buttonWidget.section != buttonWidgets.at(index + 1)?.section; buttonWidget.toggleClass("ag-charts-toolbar__button--first", first2); buttonWidget.toggleClass("ag-charts-toolbar__button--last", last); buttonWidget.toggleClass("ag-charts-toolbar__button--gap", index > 0 && first2); section = buttonWidget.section; } } createButton(index, button) { const buttonWidget = this.createButtonWidget(); buttonWidget.addClass("ag-charts-toolbar__button"); buttonWidget.addListener("click", (event) => { const buttonOptions = { index, ...button instanceof BaseProperties ? button.toJson() : button }; const buttonBounds = this.getButtonWidgetBounds(buttonWidget); const params = { event, button: buttonOptions, buttonBounds, buttonWidget }; this.events.dispatch("button-pressed", params); }); buttonWidget.addListener("focus", () => { const params = { button: { index } }; this.events.dispatch("button-focused", params); }); buttonWidget.addListener("expand-controlled-widget", (e) => { this.expanded?.collapse({ mode: "4" /* SIDLING_OPENED */ }); this.expanded = e.controlled; const removeListener = this.expanded.addListener("collapse-widget", () => { this.expanded = void 0; removeListener(); }); }); if (button.section) { buttonWidget.section = button.section; } this.buttonWidgets.push(buttonWidget); this.addChild(buttonWidget); return buttonWidget; } }; var Toolbar = class extends BaseToolbar { createButtonWidget() { return new ToolbarButtonWidget(this.localeManager); } }; // packages/ag-charts-community/src/components/toolbar/floatingToolbar.ts var FloatingToolbarPopover = class extends DraggablePopover { constructor(ctx, id, onPopoverMoved) { super(ctx, id); this.onPopoverMoved = onPopoverMoved; this.dragHandleDraggingClass = "ag-charts-floating-toolbar__drag-handle--dragging"; } show(children, options = {}) { this.showWithChildren(children, { ...options, class: "ag-charts-floating-toolbar" }); } hide() { this.dragged = false; super.hide(); } getBounds() { const element2 = this.getPopoverElement(); return new BBox( element2?.offsetLeft ?? 0, element2?.offsetTop ?? 0, element2?.offsetWidth ?? 0, element2?.offsetHeight ?? 0 ); } hasBeenDragged() { return this.dragged; } setAnchor(anchor, horizontalSpacing, verticalSpacing) { const element2 = this.getPopoverElement(); if (!element2) return; const position = anchor.position ?? "above"; const { offsetWidth: width2, offsetHeight: height2 } = element2; let top = anchor.y - height2 - verticalSpacing; let left = anchor.x - width2 / 2; if (position === "below") { top = anchor.y + verticalSpacing; } else if (position === "right") { top = anchor.y - height2 / 2; left = anchor.x + horizontalSpacing; } else if (position === "above-left") { left = anchor.x; } this.updatePosition({ x: left, y: top }); } ignorePointerEvents() { const element2 = this.getPopoverElement(); if (element2) element2.style.pointerEvents = "none"; } capturePointerEvents() { const element2 = this.getPopoverElement(); if (element2) element2.style.pointerEvents = "unset"; } updatePosition(position) { const bounds = this.getBounds(); const canvasRect = this.ctx.domManager.getBoundingClientRect(); position.x = Math.floor(clamp(0, position.x, canvasRect.width - bounds.width)); position.y = Math.floor(clamp(0, position.y, canvasRect.height - bounds.height)); super.updatePosition(position); this.onPopoverMoved(); } }; var FloatingToolbar = class extends BaseToolbar { constructor(ctx, ariaLabelId, id) { super(ctx, ariaLabelId, "horizontal"); this.hasPrefix = true; this.popover = new FloatingToolbarPopover(ctx, id, this.onPopoverMoved.bind(this)); this.dragHandle = new DragHandleWidget(ctx.localeManager.t("toolbarAnnotationsDragHandle")); this.popover.setDragHandle(this.dragHandle); } destroy() { super.destroy(); this.popover.destroy(); } show(options = {}) { this.popover.show([this.dragHandle.getElement(), this.getElement()], options); } hide() { this.popover.hide(); } setAnchor(anchor) { this.popover.setAnchor(anchor, this.horizontalSpacing, this.verticalSpacing); } hasBeenDragged() { return this.popover.hasBeenDragged(); } ignorePointerEvents() { this.popover.ignorePointerEvents(); } capturePointerEvents() { this.popover.capturePointerEvents(); } onPopoverMoved() { const popoverBounds = this.popover.getBounds(); if (this.popoverBounds?.equals(popoverBounds)) return; this.popoverBounds = popoverBounds.clone(); const buttonBounds = this.getButtonBounds(); this.events.dispatch("toolbar-moved", { popoverBounds, buttonBounds }); } getButtonWidgetBounds(buttonWidget) { const popoverBounds = this.popover.getBounds(); const bounds = super.getButtonWidgetBounds(buttonWidget); const dragHandleBounds = this.dragHandle.getBounds(); return new BBox( bounds.x + popoverBounds.x - dragHandleBounds.width, bounds.y + popoverBounds.y, bounds.width, bounds.height ); } }; var DragHandleWidget = class extends NativeWidget { constructor(title) { super(createElement("div", "ag-charts-floating-toolbar__drag-handle")); const icon = new NativeWidget( createElement("span", `${getIconClassNames("drag-handle")} ag-charts-toolbar__icon`) ); icon.setAriaHidden(true); this.addChild(icon); this.elem.title = title; } }; // packages/ag-charts-community/src/module-support.ts var motion = { ...fromToMotion_exports, ...resetMotion_exports }; // packages/ag-charts-community/src/widget/exports.ts var exports_exports = {}; __export(exports_exports, { ButtonWidget: () => ButtonWidget, MenuItemRadioWidget: () => MenuItemRadioWidget, MenuItemWidget: () => MenuItemWidget, MenuWidget: () => MenuWidget, NativeWidget: () => NativeWidget, SliderWidget: () => SliderWidget, ToolbarWidget: () => ToolbarWidget, WIDGET_HTML_EVENTS: () => WIDGET_HTML_EVENTS, Widget: () => Widget, WidgetEventUtil: () => WidgetEventUtil }); // packages/ag-charts-community/src/integrated-charts-scene.ts var integrated_charts_scene_exports = {}; __export(integrated_charts_scene_exports, { Arc: () => Arc2, BBox: () => BBox, Caption: () => Caption, CategoryScale: () => CategoryScale, Group: () => Group, Line: () => Line, LinearScale: () => LinearScale, Marker: () => Marker, Path: () => Path, RadialColumnShape: () => RadialColumnShape, Rect: () => Rect, Scene: () => Scene, Sector: () => Sector, Shape: () => Shape, TranslatableGroup: () => TranslatableGroup, getRadialColumnWidth: () => getRadialColumnWidth, toRadians: () => toRadians }); // packages/ag-charts-community/src/integrated-charts-theme.ts var integrated_charts_theme_exports = {}; __export(integrated_charts_theme_exports, { AGGREGATION_INDEX_UNSET: () => AGGREGATION_INDEX_UNSET, AGGREGATION_INDEX_X_MAX: () => AGGREGATION_INDEX_X_MAX, AGGREGATION_INDEX_X_MIN: () => AGGREGATION_INDEX_X_MIN, AGGREGATION_INDEX_Y_MAX: () => AGGREGATION_INDEX_Y_MAX, AGGREGATION_INDEX_Y_MIN: () => AGGREGATION_INDEX_Y_MIN, AGGREGATION_MAX_POINTS: () => AGGREGATION_MAX_POINTS, AGGREGATION_MIN_RANGE: () => AGGREGATION_MIN_RANGE, AGGREGATION_SPAN: () => AGGREGATION_SPAN, AGGREGATION_THRESHOLD: () => AGGREGATION_THRESHOLD, AbstractModuleInstance: () => AbstractModuleInstance, ActionOnSet: () => ActionOnSet, AdjacencyListGraph: () => AdjacencyListGraph, AsyncAwaitQueue: () => AsyncAwaitQueue, BASE_FONT_SIZE: () => BASE_FONT_SIZE, BREAK_TRANSFORM_CHAIN: () => BREAK_TRANSFORM_CHAIN, BaseProperties: () => BaseProperties, Border: () => Border, CANVAS_HEIGHT: () => CANVAS_HEIGHT, CANVAS_TO_BUFFER_DEFAULTS: () => CANVAS_TO_BUFFER_DEFAULTS, CANVAS_WIDTH: () => CANVAS_WIDTH, CARTESIAN_AXIS_TYPE: () => CARTESIAN_AXIS_TYPE, CARTESIAN_POSITION: () => CARTESIAN_POSITION, CallbackCache: () => CallbackCache, ChangeDetectableProperties: () => ChangeDetectableProperties, ChartAxisDirection: () => ChartAxisDirection, ChartTheme: () => ChartTheme, ChartUpdateType: () => ChartUpdateType, CleanupRegistry: () => CleanupRegistry, Color: () => Color, ConfiguredCanvasMixin: () => ConfiguredCanvasMixin, DEFAULT_ANNOTATION_HANDLE_FILL: () => DEFAULT_ANNOTATION_HANDLE_FILL, DEFAULT_ANNOTATION_STATISTICS_COLOR: () => DEFAULT_ANNOTATION_STATISTICS_COLOR, DEFAULT_ANNOTATION_STATISTICS_DIVIDER_STROKE: () => DEFAULT_ANNOTATION_STATISTICS_DIVIDER_STROKE, DEFAULT_ANNOTATION_STATISTICS_DOWN_FILL: () => DEFAULT_ANNOTATION_STATISTICS_DOWN_FILL, DEFAULT_ANNOTATION_STATISTICS_DOWN_STROKE: () => DEFAULT_ANNOTATION_STATISTICS_DOWN_STROKE, DEFAULT_ANNOTATION_STATISTICS_FILL: () => DEFAULT_ANNOTATION_STATISTICS_FILL, DEFAULT_ANNOTATION_STATISTICS_STROKE: () => DEFAULT_ANNOTATION_STATISTICS_STROKE, DEFAULT_CAPTION_ALIGNMENT: () => DEFAULT_CAPTION_ALIGNMENT, DEFAULT_CAPTION_LAYOUT_STYLE: () => DEFAULT_CAPTION_LAYOUT_STYLE, DEFAULT_FIBONACCI_STROKES: () => DEFAULT_FIBONACCI_STROKES, DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL: () => DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL, DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR: () => DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR, DEFAULT_POLAR_SERIES_STROKE: () => DEFAULT_POLAR_SERIES_STROKE, DEFAULT_SHADOW_COLOUR: () => DEFAULT_SHADOW_COLOUR, DEFAULT_SPARKLINE_CROSSHAIR_STROKE: () => DEFAULT_SPARKLINE_CROSSHAIR_STROKE, DEFAULT_TEXTBOX_COLOR: () => DEFAULT_TEXTBOX_COLOR, DEFAULT_TEXTBOX_FILL: () => DEFAULT_TEXTBOX_FILL, DEFAULT_TEXTBOX_STROKE: () => DEFAULT_TEXTBOX_STROKE, DEFAULT_TEXT_ANNOTATION_COLOR: () => DEFAULT_TEXT_ANNOTATION_COLOR, DEFAULT_TOOLBAR_POSITION: () => DEFAULT_TOOLBAR_POSITION, DIRECTION_SWAP_AXES: () => DIRECTION_SWAP_AXES, Debug: () => debugLogger_exports, DebugMetrics: () => debugMetrics_exports, DeclaredSceneChangeDetection: () => DeclaredSceneChangeDetection, DeclaredSceneObjectChangeDetection: () => DeclaredSceneObjectChangeDetection, Deprecated: () => Deprecated, DeprecatedAndRenamedTo: () => DeprecatedAndRenamedTo, EllipsisChar: () => EllipsisChar, ErrorType: () => ErrorType, EventEmitter: () => EventEmitter, FILL_GRADIENT_BLANK_DEFAULTS: () => FILL_GRADIENT_BLANK_DEFAULTS, FILL_GRADIENT_CONIC_SERIES_DEFAULTS: () => FILL_GRADIENT_CONIC_SERIES_DEFAULTS, FILL_GRADIENT_LINEAR_DEFAULTS: () => FILL_GRADIENT_LINEAR_DEFAULTS, FILL_GRADIENT_LINEAR_HIERARCHY_DEFAULTS: () => FILL_GRADIENT_LINEAR_HIERARCHY_DEFAULTS, FILL_GRADIENT_LINEAR_KEYED_DEFAULTS: () => FILL_GRADIENT_LINEAR_KEYED_DEFAULTS, FILL_GRADIENT_LINEAR_SINGLE_DEFAULTS: () => FILL_GRADIENT_LINEAR_SINGLE_DEFAULTS, FILL_GRADIENT_RADIAL_DEFAULTS: () => FILL_GRADIENT_RADIAL_DEFAULTS, FILL_GRADIENT_RADIAL_REVERSED_DEFAULTS: () => FILL_GRADIENT_RADIAL_REVERSED_DEFAULTS, FILL_GRADIENT_RADIAL_REVERSED_SERIES_DEFAULTS: () => FILL_GRADIENT_RADIAL_REVERSED_SERIES_DEFAULTS, FILL_GRADIENT_RADIAL_SERIES_DEFAULTS: () => FILL_GRADIENT_RADIAL_SERIES_DEFAULTS, FILL_IMAGE_BLANK_DEFAULTS: () => FILL_IMAGE_BLANK_DEFAULTS, FILL_IMAGE_DEFAULTS: () => FILL_IMAGE_DEFAULTS, FILL_PATTERN_BLANK_DEFAULTS: () => FILL_PATTERN_BLANK_DEFAULTS, FILL_PATTERN_DEFAULTS: () => FILL_PATTERN_DEFAULTS, FILL_PATTERN_HIERARCHY_DEFAULTS: () => FILL_PATTERN_HIERARCHY_DEFAULTS, FILL_PATTERN_KEYED_DEFAULTS: () => FILL_PATTERN_KEYED_DEFAULTS, FILL_PATTERN_SINGLE_DEFAULTS: () => FILL_PATTERN_SINGLE_DEFAULTS, FONT_SIZE: () => FONT_SIZE, FONT_SIZE_RATIO: () => FONT_SIZE_RATIO, IS_DARK_THEME: () => IS_DARK_THEME, InterpolationProperties: () => InterpolationProperties, LABEL_BOXING_DEFAULTS: () => LABEL_BOXING_DEFAULTS, LEGEND_CONTAINER_THEME: () => LEGEND_CONTAINER_THEME, LRUCache: () => LRUCache, LineSplitter: () => LineSplitter, Logger: () => logger_exports, MARKER_SERIES_HIGHLIGHT_STYLE: () => MARKER_SERIES_HIGHLIGHT_STYLE, MULTI_SERIES_HIGHLIGHT_STYLE: () => MULTI_SERIES_HIGHLIGHT_STYLE, MementoCaretaker: () => MementoCaretaker, ModuleRegistry: () => moduleRegistry_exports, ModuleType: () => ModuleType, ObserveChanges: () => ObserveChanges, PALETTE_ALT_DOWN_FILL: () => PALETTE_ALT_DOWN_FILL, PALETTE_ALT_DOWN_STROKE: () => PALETTE_ALT_DOWN_STROKE, PALETTE_ALT_NEUTRAL_FILL: () => PALETTE_ALT_NEUTRAL_FILL, PALETTE_ALT_NEUTRAL_STROKE: () => PALETTE_ALT_NEUTRAL_STROKE, PALETTE_ALT_UP_FILL: () => PALETTE_ALT_UP_FILL, PALETTE_ALT_UP_STROKE: () => PALETTE_ALT_UP_STROKE, PALETTE_DOWN_FILL: () => PALETTE_DOWN_FILL, PALETTE_DOWN_STROKE: () => PALETTE_DOWN_STROKE, PALETTE_NEUTRAL_FILL: () => PALETTE_NEUTRAL_FILL, PALETTE_NEUTRAL_STROKE: () => PALETTE_NEUTRAL_STROKE, PALETTE_UP_FILL: () => PALETTE_UP_FILL, PALETTE_UP_STROKE: () => PALETTE_UP_STROKE, PART_WHOLE_HIGHLIGHT_STYLE: () => PART_WHOLE_HIGHLIGHT_STYLE, POLAR_AXIS_SHAPE: () => POLAR_AXIS_SHAPE, POLAR_AXIS_TYPE: () => POLAR_AXIS_TYPE, PREV_NEXT_KEYS: () => PREV_NEXT_KEYS, Padding: () => Padding, ParallelStateMachine: () => ParallelStateMachine, PolarZIndexMap: () => PolarZIndexMap, PropertiesArray: () => PropertiesArray, Property: () => addFakeTransformToInstanceProperty, ProxyOnWrite: () => ProxyOnWrite, ProxyProperty: () => ProxyProperty, ProxyPropertyOnWrite: () => ProxyPropertyOnWrite, SAFE_FILLS_OPERATION: () => SAFE_FILLS_OPERATION, SAFE_FILL_OPERATION: () => SAFE_FILL_OPERATION, SAFE_RANGE2_OPERATION: () => SAFE_RANGE2_OPERATION, SAFE_STROKE_FILL_OPERATION: () => SAFE_STROKE_FILL_OPERATION, SEGMENTATION_DEFAULTS: () => SEGMENTATION_DEFAULTS, SINGLE_SERIES_HIGHLIGHT_STYLE: () => SINGLE_SERIES_HIGHLIGHT_STYLE, SKIP_JS_BUILTINS: () => SKIP_JS_BUILTINS, ScaleAlignment: () => ScaleAlignment, SceneArrayChangeDetection: () => SceneArrayChangeDetection, SceneChangeDetection: () => SceneChangeDetection, SceneObjectChangeDetection: () => SceneObjectChangeDetection, SceneRefChangeDetection: () => SceneRefChangeDetection, SeriesContentZIndexMap: () => SeriesContentZIndexMap, SeriesZIndexMap: () => SeriesZIndexMap, SimpleCache: () => SimpleCache, SpanJoin: () => SpanJoin, StateMachine: () => StateMachine, StateMachineProperty: () => StateMachineProperty, TRIPLE_EQ: () => TRIPLE_EQ, TextMeasurer: () => TextMeasurer, TickIntervals: () => TickIntervals, TrimCharsRegex: () => TrimCharsRegex, TrimEdgeGuard: () => TrimEdgeGuard, UNIT_MAX: () => UNIT_MAX, UNIT_MIN: () => UNIT_MIN, UnknownError: () => UnknownError, ValidationError: () => ValidationError, Vec2: () => vector_exports, Vec4: () => vector4_exports, Vertex: () => Vertex, WeakCache: () => WeakCache, ZIndexMap: () => ZIndexMap, addEscapeEventListener: () => addEscapeEventListener, addFakeTransformToInstanceProperty: () => addFakeTransformToInstanceProperty, addMouseCloseListener: () => addMouseCloseListener, addObserverToInstanceProperty: () => addObserverToInstanceProperty, addOverrideFocusVisibleEventListener: () => addOverrideFocusVisibleEventListener, addTouchCloseListener: () => addTouchCloseListener, addTransformToInstanceProperty: () => addTransformToInstanceProperty, aggregationBucketForDatum: () => aggregationBucketForDatum, aggregationDatumMatchesIndex: () => aggregationDatumMatchesIndex, aggregationDomain: () => aggregationDomain, aggregationIndexForXRatio: () => aggregationIndexForXRatio, aggregationRangeFittingPoints: () => aggregationRangeFittingPoints, aggregationXRatioForDatumIndex: () => aggregationXRatioForDatumIndex, aggregationXRatioForXValue: () => aggregationXRatioForXValue, and: () => and, angleBetween: () => angleBetween, angularPadding: () => angularPadding, appendEllipsis: () => appendEllipsis, applySkiaPatches: () => applySkiaPatches, arcDistanceSquared: () => arcDistanceSquared, areScalingEqual: () => areScalingEqual, array: () => array, arrayLength: () => arrayLength, arrayOf: () => arrayOf, arrayOfDefs: () => arrayOfDefs, arraysEqual: () => arraysEqual, assignIfNotStrictlyEqual: () => assignIfNotStrictlyEqual, attachDescription: () => attachDescription, attachListener: () => attachListener, autoSizedLabelOptionsDefs: () => autoSizedLabelOptionsDefs, barHighlightOptionsDef: () => barHighlightOptionsDef, bezier2DDistance: () => bezier2DDistance, bezier2DExtrema: () => bezier2DExtrema, boolean: () => boolean, borderOptionsDef: () => borderOptionsDef, boxCollides: () => boxCollides, boxContains: () => boxContains, boxEmpty: () => boxEmpty, boxesEqual: () => boxesEqual, buildDateFormatter: () => buildDateFormatter, cachedTextMeasurer: () => cachedTextMeasurer, calcLineHeight: () => calcLineHeight, calculatePlacement: () => calculatePlacement, callWithContext: () => callWithContext, callback: () => callback, callbackDefs: () => callbackDefs, callbackOf: () => callbackOf, ceilTo: () => ceilTo, checkDatum: () => checkDatum, circularSliceArray: () => circularSliceArray, clamp: () => clamp, clampArray: () => clampArray, clipLines: () => clipLines, clipSpanX: () => clipSpanX, collapseSpanToPoint: () => collapseSpanToPoint, collectAggregationLevels: () => collectAggregationLevels, color: () => color, colorStopsOrderValidator: () => colorStopsOrderValidator, colorUnion: () => colorUnion, commonChartOptionsDefs: () => commonChartOptionsDefs, commonSeriesOptionsDefs: () => commonSeriesOptionsDefs, commonSeriesThemeableOptionsDefs: () => commonSeriesThemeableOptionsDefs, compactAggregationIndices: () => compactAggregationIndices, compareDates: () => compareDates, computeExtremesAggregation: () => computeExtremesAggregation, computeExtremesAggregationPartial: () => computeExtremesAggregationPartial, constant: () => constant, contextMenuItemsArray: () => contextMenuItemsArray, countFractionDigits: () => countFractionDigits, countLines: () => countLines, createAggregationIndices: () => createAggregationIndices, createButton: () => createButton, createCanvasContext: () => createCanvasContext, createCheckbox: () => createCheckbox, createDeprecationWarning: () => createDeprecationWarning, createElement: () => createElement, createElementId: () => createElementId, createIcon: () => createIcon, createId: () => createId, createIdsGenerator: () => createIdsGenerator, createNumberFormatter: () => createNumberFormatter, createSelect: () => createSelect, createSvgElement: () => createSvgElement, createTextArea: () => createTextArea, createTicks: () => createTicks, date: () => date, dateToNumber: () => dateToNumber, dateTruncationForDomain: () => dateTruncationForDomain, datesSortOrder: () => datesSortOrder, debounce: () => debounce, decodeIntervalValue: () => decodeIntervalValue, deepClone: () => deepClone, deepFreeze: () => deepFreeze, defaultEpoch: () => defaultEpoch, defined: () => defined, definedZoomState: () => definedZoomState, diffArrays: () => diffArrays, distribute: () => distribute, downloadUrl: () => downloadUrl, dropFirstWhile: () => dropFirstWhile, dropLastWhile: () => dropLastWhile, durationDay: () => durationDay, durationHour: () => durationHour, durationMinute: () => durationMinute, durationMonth: () => durationMonth, durationSecond: () => durationSecond, durationWeek: () => durationWeek, durationYear: () => durationYear, easeIn: () => easeIn, easeInOut: () => easeInOut, easeInOutQuad: () => easeInOutQuad, easeInQuad: () => easeInQuad, easeOut: () => easeOut, easeOutQuad: () => easeOutQuad, encodedToTimestamp: () => encodedToTimestamp, enterpriseRegistry: () => enterpriseRegistry, entries: () => entries, errorBarOptionsDefs: () => errorBarOptionsDefs, errorBarThemeableOptionsDefs: () => errorBarThemeableOptionsDefs, estimateTickCount: () => estimateTickCount, evaluateBezier: () => evaluateBezier, every: () => every, expandLegendPosition: () => expandLegendPosition, extent: () => extent, extractDecoratedProperties: () => extractDecoratedProperties, extractDomain: () => extractDomain, fillGradientDefaults: () => fillGradientDefaults, fillImageDefaults: () => fillImageDefaults, fillOptionsDef: () => fillOptionsDef, fillPatternDefaults: () => fillPatternDefaults, findMaxIndex: () => findMaxIndex, findMaxValue: () => findMaxValue, findMinIndex: () => findMinIndex, findMinMax: () => findMinMax, findMinValue: () => findMinValue, findRangeExtent: () => findRangeExtent, first: () => first, flush: () => flush, focusCursorAtEnd: () => focusCursorAtEnd, fontFamilyFull: () => fontFamilyFull, fontOptionsDef: () => fontOptionsDef, fontWeight: () => fontWeight, formatNumber: () => formatNumber, formatObjectValidator: () => formatObjectValidator, formatPercent: () => formatPercent, formatValue: () => formatValue, fromPairs: () => fromPairs, generateUUID: () => generateUUID, geoJson: () => geoJson, getAngleRatioRadians: () => getAngleRatioRadians, getAttribute: () => getAttribute, getChartTheme: () => getChartTheme, getDOMMatrix: () => getDOMMatrix, getDocument: () => getDocument, getElementBBox: () => getElementBBox, getIconClassNames: () => getIconClassNames, getImage: () => getImage, getLastFocus: () => getLastFocus, getMaxInnerRectSize: () => getMaxInnerRectSize, getMidpointsForIndices: () => getMidpointsForIndices, getMinOuterRectSize: () => getMinOuterRectSize, getOffscreenCanvas: () => getOffscreenCanvas, getPath: () => getPath, getPath2D: () => getPath2D, getResizeObserver: () => getResizeObserver, getSequentialColors: () => getSequentialColors, getTickTimeInterval: () => getTickTimeInterval, getWindow: () => getWindow, googleFont: () => googleFont, gradientColorStops: () => gradientColorStops, gradientStrict: () => gradientStrict, greaterThan: () => greaterThan, groupBy: () => groupBy, guardTextEdges: () => guardTextEdges, hasNoModifiers: () => hasNoModifiers, hasRequiredInPath: () => hasRequiredInPath, highlightOptionsDef: () => highlightOptionsDef, htmlElement: () => htmlElement, inRange: () => inRange, initRovingTabIndex: () => initRovingTabIndex, insertListItemsSorted: () => insertListItemsSorted, instanceOf: () => instanceOf, interpolationOptionsDefs: () => interpolationOptionsDefs, intervalCeil: () => intervalCeil, intervalEpoch: () => intervalEpoch, intervalExtent: () => intervalExtent, intervalFloor: () => intervalFloor, intervalHierarchy: () => intervalHierarchy, intervalMilliseconds: () => intervalMilliseconds, intervalNext: () => intervalNext, intervalPrevious: () => intervalPrevious, intervalRange: () => intervalRange, intervalRangeCount: () => intervalRangeCount, intervalRangeNumeric: () => intervalRangeNumeric, intervalRangeStartIndex: () => intervalRangeStartIndex, intervalStep: () => intervalStep, intervalUnit: () => intervalUnit, inverseEaseOut: () => inverseEaseOut, isArray: () => isArray, isBetweenAngles: () => isBetweenAngles, isBoolean: () => isBoolean, isButtonClickEvent: () => isButtonClickEvent, isColor: () => isColor, isContinuous: () => isContinuous, isDate: () => isDate, isDecoratedObject: () => isDecoratedObject, isDefined: () => isDefined, isDenseInterval: () => isDenseInterval, isDocumentFragment: () => isDocumentFragment, isElement: () => isElement, isEmptyObject: () => isEmptyObject, isEnumKey: () => isEnumKey, isEnumValue: () => isEnumValue, isFiniteNumber: () => isFiniteNumber, isFunction: () => isFunction, isGradientFill: () => isGradientFill, isGradientFillArray: () => isGradientFillArray, isGradientOrPatternFill: () => isGradientOrPatternFill, isHTMLElement: () => isHTMLElement, isHtmlElement: () => isHtmlElement, isImageFill: () => isImageFill, isInputPending: () => isInputPending, isInteger: () => isInteger, isKeyOf: () => isKeyOf, isNegative: () => isNegative, isNode: () => isNode, isNumber: () => isNumber, isNumberEqual: () => isNumberEqual, isNumberObject: () => isNumberObject, isObject: () => isObject, isObjectLike: () => isObjectLike, isObjectWithProperty: () => isObjectWithProperty, isObjectWithStringProperty: () => isObjectWithStringProperty, isPatternFill: () => isPatternFill, isPlainObject: () => isPlainObject, isPointLabelDatum: () => isPointLabelDatum, isProperties: () => isProperties, isRegExp: () => isRegExp, isScaleValid: () => isScaleValid, isSegmentTruncated: () => isSegmentTruncated, isString: () => isString, isStringFillArray: () => isStringFillArray, isStringObject: () => isStringObject, isSymbol: () => isSymbol, isTextTruncated: () => isTextTruncated, isTruncated: () => isTruncated, isUnitTimeCategoryScaling: () => isUnitTimeCategoryScaling, isValidDate: () => isValidDate, isValidNumberFormat: () => isValidNumberFormat, iterate: () => iterate, joinFormatted: () => joinFormatted, jsonApply: () => jsonApply, jsonDiff: () => jsonDiff, jsonPropertyCompare: () => jsonPropertyCompare, jsonWalk: () => jsonWalk, kebabCase: () => kebabCase, labelBoxOptionsDef: () => labelBoxOptionsDef, legendPositionValidator: () => legendPositionValidator, lessThan: () => lessThan, lessThanOrEqual: () => lessThanOrEqual, levenshteinDistance: () => levenshteinDistance, lineDashOptionsDef: () => lineDashOptionsDef, lineDistanceSquared: () => lineDistanceSquared, lineHighlightOptionsDef: () => lineHighlightOptionsDef, lineSegmentOptions: () => lineSegmentOptions, lineSegmentation: () => lineSegmentation, linear: () => linear, linearGaugeSeriesOptionsDef: () => linearGaugeSeriesOptionsDef, linearGaugeSeriesThemeableOptionsDef: () => linearGaugeSeriesThemeableOptionsDef, linearGaugeTargetOptionsDef: () => linearGaugeTargetOptionsDef, linearPoints: () => linearPoints, listDecoratedProperties: () => listDecoratedProperties, lowestGranularityForInterval: () => lowestGranularityForInterval, lowestGranularityUnitForTicks: () => lowestGranularityUnitForTicks, lowestGranularityUnitForValue: () => lowestGranularityUnitForValue, makeAccessibleClickListener: () => makeAccessibleClickListener, mapValues: () => mapValues, markerOptionsDefs: () => markerOptionsDefs, markerStyleOptionsDefs: () => markerStyleOptionsDefs, measureTextSegments: () => measureTextSegments, memo: () => memo, merge: () => merge, mergeArrayDefaults: () => mergeArrayDefaults, mergeDefaults: () => mergeDefaults, modulus: () => modulus, multiSeriesHighlightOptionsDef: () => multiSeriesHighlightOptionsDef, nearestSquared: () => nearestSquared, nearestSquaredInContainer: () => nearestSquaredInContainer, nextPowerOf2: () => nextPowerOf2, niceTicksDomain: () => niceTicksDomain, normalisedExtentWithMetadata: () => normalisedExtentWithMetadata, normalisedTimeExtentWithMetadata: () => normalisedTimeExtentWithMetadata, normalizeAngle180: () => normalizeAngle180, normalizeAngle360: () => normalizeAngle360, normalizeAngle360FromDegrees: () => normalizeAngle360FromDegrees, normalizeAngle360Inclusive: () => normalizeAngle360Inclusive, number: () => number, numberFormatValidator: () => numberFormatValidator, numberMin: () => numberMin, numberRange: () => numberRange, object: () => object, objectsEqual: () => objectsEqual, objectsEqualWith: () => objectsEqualWith, optionsDefs: () => optionsDefs, or: () => or, padding: () => padding, paddingOptions: () => paddingOptions, parseNumberFormat: () => parseNumberFormat, partialAssign: () => partialAssign, pause: () => pause, pick: () => pick, placeLabels: () => placeLabels, positiveNumber: () => positiveNumber, positiveNumberNonZero: () => positiveNumberNonZero, previousPowerOf2: () => previousPowerOf2, radialGaugeSeriesOptionsDef: () => radialGaugeSeriesOptionsDef, radialGaugeSeriesThemeableOptionsDef: () => radialGaugeSeriesThemeableOptionsDef, radialGaugeTargetOptionsDef: () => radialGaugeTargetOptionsDef, range: () => range, rangeValidator: () => rangeValidator, ratio: () => ratio, readIntegratedWrappedValue: () => readIntegratedWrappedValue, record: () => record, required: () => required, rescaleSpan: () => rescaleSpan, rescaleVisibleRange: () => rescaleVisibleRange, resetIds: () => resetIds, resolveOperation: () => resolveOperation, rotatePoint: () => rotatePoint, roundTo: () => roundTo, safeCall: () => safeCall, seriesLabelOptionsDefs: () => seriesLabelOptionsDefs, seriesTooltipRangeValidator: () => seriesTooltipRangeValidator, setAttribute: () => setAttribute, setAttributes: () => setAttributes, setDocument: () => setDocument, setElementBBox: () => setElementBBox, setElementStyle: () => setElementStyle, setElementStyles: () => setElementStyles, setPath: () => setPath, setWindow: () => setWindow, shadowOptionsDefs: () => shadowOptionsDefs, shallowClone: () => shallowClone, shapeHighlightOptionsDef: () => shapeHighlightOptionsDef, shapeSegmentOptions: () => shapeSegmentOptions, shapeSegmentation: () => shapeSegmentation, shapeValidator: () => shapeValidator, simpleMemorize: () => simpleMemorize, simpleMemorize2: () => simpleMemorize2, smoothPoints: () => smoothPoints, solveBezier: () => solveBezier, sortAndUniqueDates: () => sortAndUniqueDates, sortBasedOnArray: () => sortBasedOnArray, spanRange: () => spanRange, splitBezier2D: () => splitBezier2D, stepPoints: () => stepPoints, stopPageScrolling: () => stopPageScrolling, strictObjectKeys: () => strictObjectKeys, strictUnion: () => strictUnion, string: () => string, stringLength: () => stringLength, stringifyValue: () => stringifyValue, strokeOptionsDef: () => strokeOptionsDef, textOrSegments: () => textOrSegments, themeNames: () => themeNames, themeOperator: () => themeOperator, themeSymbols: () => themeSymbols, themes: () => themes, throttle: () => throttle, tickFormat: () => tickFormat, tickStep: () => tickStep, toArray: () => toArray, toDegrees: () => toDegrees, toFontString: () => toFontString, toIterable: () => toIterable, toPlainText: () => toPlainText, toRadians: () => toRadians, toTextString: () => toTextString, toolbarButtonOptionsDefs: () => toolbarButtonOptionsDefs, tooltipOptionsDefs: () => tooltipOptionsDefs, tooltipOptionsDefsWithArea: () => tooltipOptionsDefsWithArea, transformIntegratedCategoryValue: () => transformIntegratedCategoryValue, truncateLine: () => truncateLine, typeUnion: () => typeUnion, undocumented: () => undocumented, unguardTextEdges: () => unguardTextEdges, union: () => union, unionSymbol: () => unionSymbol, unique: () => unique, validate: () => validate, withTimeout: () => withTimeout, without: () => without, wrapLines: () => wrapLines, wrapText: () => wrapText, wrapTextOrSegments: () => wrapTextOrSegments, wrapTextSegments: () => wrapTextSegments }); var themeSymbols = { DEFAULT_ANNOTATION_HANDLE_FILL, DEFAULT_ANNOTATION_STATISTICS_COLOR, DEFAULT_ANNOTATION_STATISTICS_DIVIDER_STROKE, DEFAULT_ANNOTATION_STATISTICS_DOWN_FILL, DEFAULT_ANNOTATION_STATISTICS_DOWN_STROKE, DEFAULT_ANNOTATION_STATISTICS_FILL, DEFAULT_ANNOTATION_STATISTICS_STROKE, DEFAULT_CAPTION_ALIGNMENT, DEFAULT_CAPTION_LAYOUT_STYLE, DEFAULT_FIBONACCI_STROKES, DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL, DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR, DEFAULT_POLAR_SERIES_STROKE, DEFAULT_SHADOW_COLOUR, DEFAULT_SPARKLINE_CROSSHAIR_STROKE, DEFAULT_TEXTBOX_COLOR, DEFAULT_TEXTBOX_FILL, DEFAULT_TEXTBOX_STROKE, DEFAULT_TEXT_ANNOTATION_COLOR, DEFAULT_TOOLBAR_POSITION, IS_DARK_THEME, PALETTE_ALT_DOWN_FILL, PALETTE_ALT_DOWN_STROKE, PALETTE_ALT_NEUTRAL_FILL, PALETTE_ALT_NEUTRAL_STROKE, PALETTE_ALT_UP_FILL, PALETTE_ALT_UP_STROKE, PALETTE_DOWN_FILL, PALETTE_DOWN_STROKE, PALETTE_NEUTRAL_FILL, PALETTE_NEUTRAL_STROKE, PALETTE_UP_FILL, PALETTE_UP_STROKE }; var themeNames = Object.keys(themes); function resolveOperation(operation) { const params = ChartTheme.getDefaultPublicParameters(); const palette = ChartTheme.getDefaultColors(); const graph = new OptionsGraph({ line: { operation } }, { series: [{ type: "line" }] }, params, palette); const resolved = graph.resolve(); return resolved.operation; } // packages/ag-charts-community/src/integrated-charts-util.ts var integrated_charts_util_exports = {}; __export(integrated_charts_util_exports, { Color: () => Color, interpolateColor: () => interpolateColor }); // packages/ag-charts-community/src/module-bundles/integrated.ts var AgChartsCommunityModule = { VERSION, _Scene: integrated_charts_scene_exports, _Theme: integrated_charts_theme_exports, _Util: integrated_charts_util_exports, create: AgCharts.create.bind(AgCharts), createSparkline: AgCharts.__createSparkline.bind(AgCharts), setup: () => { moduleRegistry_exports.setRegistryMode(moduleRegistry_exports.RegistryMode.Integrated); moduleRegistry_exports.registerModules(AllCommunityModule); }, isEnterprise: false }; // packages/ag-charts-community/src/main-umd.ts moduleRegistry_exports.registerModules(AllCommunityModule); if (typeof module.exports == "object" && typeof exports == "object") { var __cp = (to, from, except, desc) => { if ((from && typeof from === "object") || typeof from === "function") { for (let key of Object.getOwnPropertyNames(from)) { if (!Object.prototype.hasOwnProperty.call(to, key) && key !== except) Object.defineProperty(to, key, { get: () => from[key], enumerable: !(desc = Object.getOwnPropertyDescriptor(from, key)) || desc.enumerable, }); } } return to; }; module.exports = __cp(module.exports, exports); } return module.exports; }))