Files
sigpro-grid/node_modules/ag-charts-community/dist/umd/ag-charts-community.js
2026-03-17 08:44:54 +01:00

66281 lines
2.2 MiB
Executable File

(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 ?? "<root>"}`);
}
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 ?? "<unknown>"}`,
`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 ?? "<unknown>"] = 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", "<br>");
}
// 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 += `<span class="${DEFAULT_TOOLTIP_CLASS}-label">${sanitizeHtml(label)}</span>`;
rowHtml += " ";
rowHtml += `<span class="${DEFAULT_TOOLTIP_CLASS}-value">${sanitizeHtml(value)}</span>`;
} else {
rowHtml += `<span class="${DEFAULT_TOOLTIP_CLASS}-label">${sanitizeHtml(value)}</span>`;
}
const rowClassNames = [`${DEFAULT_TOOLTIP_CLASS}-row`];
if (inline)
rowClassNames.push(`${DEFAULT_TOOLTIP_CLASS}-row--inline`);
rowHtml = `<div class="${rowClassNames.join(" ")}">${rowHtml}</div>`;
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 += `<span class="${DEFAULT_TOOLTIP_CLASS}-symbol">${symbol}</span>`;
}
if (titleDefined) {
html += `<span class="${DEFAULT_TOOLTIP_CLASS}-title">${sanitizeHtml(content.title)}</span>`;
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 `<div class="${DEFAULT_TOOLTIP_CLASS}-footer">${text}</div>`;
}
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 += `<span class="${DEFAULT_TOOLTIP_CLASS}-title">${sanitizeHtml(compactTitle)}</span>`;
}
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 += `<span class="${DEFAULT_TOOLTIP_CLASS}-heading">${sanitizeHtml(toPlainText(content.heading))}</span>`;
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 = `<div class="${DEFAULT_TOOLTIP_CLASS}-content">${html.trimEnd()}</div>`;
return html;
}
function tooltipPaginationHtml(localeManager, pagination) {
const paginationContent = pagination == null ? void 0 : tooltipPaginationContentHtml(localeManager, pagination);
if (paginationContent == null)
return "";
return `<div class="${DEFAULT_TOOLTIP_CLASS}-content">${paginationContent}</div>`;
}
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, "<unknown>").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*(?:'(?:[^']|(?<!\\)\\')*'|"(?:[^"]|(?<!\\)\\")*"|-?\d+)\s*\])\s*)/g;
while (matchGroup = regExp.exec(path)) {
if (matchGroup.index !== matchIndex) {
return;
}
matchIndex = matchGroup.index + matchGroup[0].length;
const match = matchGroup[1].trim();
if (match.startsWith(".")) {
components.push(match.slice(1).trim());
} else if (match.startsWith("[")) {
const accessor = match.slice(1, -1).trim();
if (accessor.startsWith(`'`)) {
components.push(accessor.slice(1, -1).replaceAll(/(?<!\\)\\'/g, `'`));
} else if (accessor.startsWith(`"`)) {
components.push(accessor.slice(1, -1).replaceAll(/(?<!\\)\\"/g, `"`));
} else {
components.push(accessor);
}
} else {
components.push(match);
}
}
if (matchIndex !== path.length)
return;
return components;
}
function createPathAccessor(components) {
return (datum) => {
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<number>();
* 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<DataRow>();
* 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<DataRow>();
* 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<DataRow>();
* 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 = '<div role="presentation" class="ag-charts-wrapper ag-charts-styles" data-ag-charts><div role="presentation" class="ag-charts-canvas-center"><div role="presentation" class="ag-charts-canvas-container"><div role="presentation" class="ag-charts-canvas-background" aria-hidden="true"></div><div role="presentation" class="ag-charts-canvas" aria-hidden="true"></div><div role="figure" class="ag-charts-canvas-proxy"><div role="presentation" class="ag-charts-series-area"></div></div><div role="presentation" class="ag-charts-canvas-overlay ag-charts-tooltip-container"></div></div></div></div>';
// 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 ?? "<unknown>";
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 = `<span class="${getIconClassNames(options.icon)} ag-charts-toolbar__icon"></span>`;
}
if (options.label != null) {
const label = localeManager.t(options.label);
innerHTML = `${innerHTML}<span class="ag-charts-toolbar__label">${label}</span>`;
}
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;
}))