Files
sigpro-grid/node_modules/ag-charts-community/dist/package/main.esm.mjs
2026-03-17 08:44:54 +01:00

58387 lines
2.0 MiB
Executable File

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, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
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-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-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-community/src/util/time-interop.ts
import { Logger } from "ag-charts-core";
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.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 time = {
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/api/agCharts.ts
import {
Debug as Debug20,
MementoCaretaker as MementoCaretaker2,
ModuleRegistry as ModuleRegistry12,
deepClone as deepClone7,
deepFreeze as deepFreeze6,
enterpriseRegistry as enterpriseRegistry2,
jsonWalk as jsonWalk3,
strictObjectKeys as strictObjectKeys2
} from "ag-charts-core";
// packages/ag-charts-community/src/chart/chart.ts
import {
ActionOnSet as ActionOnSet3,
AsyncAwaitQueue,
ChartAxisDirection as ChartAxisDirection7,
ChartUpdateType as ChartUpdateType7,
CleanupRegistry as CleanupRegistry18,
Color as Color4,
Debug as Debug13,
Logger as Logger30,
ModuleRegistry as ModuleRegistry5,
ModuleType as ModuleType4,
Padding,
Property as Property24,
ProxyProperty,
ZIndexMap as ZIndexMap5,
callWithContext as callWithContext4,
createId as createId8,
enterpriseRegistry,
entries as entries4,
getWindow as getWindow16,
isFiniteNumber as isFiniteNumber4,
isInputPending,
jsonApply,
jsonDiff as jsonDiff4,
mergeDefaults as mergeDefaults5,
pause,
roundTo,
toPlainText as toPlainText7,
without as without2
} from "ag-charts-core";
// packages/ag-charts-community/src/scene/bbox.ts
import { boxContains, boxesEqual, clamp, nearestSquared } from "ag-charts-core";
// 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, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
static fromObject({ x, y, width, height }) {
return new _BBox(x, y, width, height);
}
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 (end(box.x, box.width) > right) {
right = end(box.x, box.width);
}
if (end(box.y, box.height) > bottom) {
bottom = end(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: end(this.x, this.width),
bottom: end(this.y, this.height),
toJSON() {
return {};
}
};
}
clone() {
const { x, y, width, height } = this;
return new _BBox(x, y, width, height);
}
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(end(this.x, this.width), end(other.x, other.width));
const y1 = Math.min(end(this.y, this.height), end(other.y, other.height));
if (x0 > x1 || y0 > y1)
return;
return new _BBox(x0, y0, x1 - x0, y1 - y0);
}
collidesBBox(other) {
return this.x < end(other.x, other.width) && end(this.x, this.width) > other.x && this.y < end(other.y, other.height) && end(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, end(this.x, this.width));
const dy = y - clamp(this.y, y, end(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 end(x, width) {
if (x === -Infinity && width === Infinity)
return Infinity;
return x + width;
}
// packages/ag-charts-community/src/scene/group.ts
import { clamp as clamp5, toIterable } from "ag-charts-core";
// packages/ag-charts-community/src/scene/canvas/hdpiOffscreenCanvas.ts
import { getOffscreenCanvas } from "ag-charts-core";
// packages/ag-charts-community/src/scene/canvas/canvasUtil.ts
import { Debug } from "ag-charts-core";
function clearContext({
context,
pixelRatio,
width,
height
}) {
context.save();
try {
context.resetTransform();
context.clearRect(0, 0, Math.ceil(width * pixelRatio), Math.ceil(height * pixelRatio));
} finally {
context.restore();
}
}
function debugContext(ctx) {
if (Debug.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(width, height, pixelRatio) {
return [Math.floor(width * pixelRatio), Math.floor(height * pixelRatio)];
}
var fallbackCanvas;
function getFallbackCanvas() {
const OffscreenCanvasCtor = getOffscreenCanvas();
fallbackCanvas ?? (fallbackCanvas = new OffscreenCanvasCtor(1, 1));
return fallbackCanvas;
}
var HdpiOffscreenCanvas = class {
constructor(options) {
const { width, height, pixelRatio, willReadFrequently = false } = options;
this.width = width;
this.height = height;
this.pixelRatio = pixelRatio;
const [canvasWidth, canvasHeight] = canvasDimensions(width, height, 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(width, height, pixelRatio) {
if (!(width > 0 && height > 0))
return;
const { canvas, context } = this;
if (width !== this.width || height !== this.height || pixelRatio !== this.pixelRatio) {
const [canvasWidth, canvasHeight] = canvasDimensions(width, height, pixelRatio);
canvas.width = canvasWidth;
canvas.height = canvasHeight;
}
context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
this.width = width;
this.height = height;
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/node.ts
import {
DeclaredSceneChangeDetection,
Logger as Logger2,
assignIfNotStrictlyEqual,
createId,
createSvgElement,
objectsEqual
} from "ag-charts-core";
// 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 length = Math.min(aArray.length, bArray.length);
for (let i = 0; i < length; 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
import { SceneChangeDetection } from "ag-charts-core";
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, width, height) {
const svg = node?.toSVG();
if (svg == null || !svg.elements.length && !svg.defs?.length)
return;
const root = createSvgElement("svg");
root.setAttribute("width", String(width));
root.setAttribute("height", String(height));
root.setAttribute("viewBox", `0 0 ${width} ${height}`);
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;
}
Logger2.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) {
Logger2.logGroup(
`Property changed multiple times before render: ${this.constructor.name}.${property} (${sources.length}x)`,
() => {
for (const source of sources) {
Logger2.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/scene/shape/shape.ts
import {
DeclaredSceneChangeDetection as DeclaredSceneChangeDetection2,
DeclaredSceneObjectChangeDetection,
SceneArrayChangeDetection,
SceneObjectChangeDetection,
TRIPLE_EQ,
boxesEqual as boxesEqual2,
clamp as clamp4,
generateUUID,
isGradientFill,
isImageFill,
isPatternFill,
isString,
objectsEqual as objectsEqual2
} from "ag-charts-core";
// packages/ag-charts-community/src/scene/gradient/conicGradient.ts
import { createSvgElement as createSvgElement3, normalizeAngle360FromDegrees } from "ag-charts-core";
// packages/ag-charts-community/src/scene/gradient/gradient.ts
import { createSvgElement as createSvgElement2 } from "ag-charts-core";
// packages/ag-charts-community/src/scale/colorScale.ts
import { Color, Logger as Logger3, clamp as clamp3 } from "ag-charts-core";
// 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
import { clamp as clamp2, readIntegratedWrappedValue } from "ag-charts-core";
function visibleTickRange(ticks, reversed, visibleRange) {
if (visibleRange == null || visibleRange[0] === 0 && visibleRange[1] === 1)
return;
const vt0 = clamp2(0, Math.floor(visibleRange[0] * ticks.length), ticks.length);
const vt1 = clamp2(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 color8 = Color.fromString(v);
const [l, c, h] = Color.RGBtoOKLCH(color8.r, color8.g, color8.b);
return { l, c, h, a: color8.a };
};
var delta = 1e-6;
var isAchromatic = (x) => x.c < delta || x.l < delta || x.l > 1 - delta;
var interpolateOklch = (x, y, d) => {
d = clamp3(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: range4 } = this;
if (domain.length < 2) {
Logger3.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) {
Logger3.warnOnce("`colorDomain` values should be supplied in ascending order.");
domain.sort((a2, b2) => a2 - b2);
break;
}
}
if (range4.length < domain.length) {
for (let i = range4.length; i < domain.length; i++) {
range4.push(range4.length > 0 ? range4[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: range4, parsedRange } = this;
const d0 = domain[0];
const d1 = domain.at(-1);
const r0 = range4[0];
const r1 = range4.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 / (range4.length - 1);
index = range4.length <= 2 ? 0 : Math.min(Math.floor(t * (range4.length - 1)), range4.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) {
Logger3.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 gradient = this.createCanvasGradient(ctx, bbox, params);
if (gradient == null)
return;
const isOkLch = colorSpace === "oklch";
const step = 0.05;
let c0 = stops[0];
gradient.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) {
gradient.addColorStop(stop, scale2.convert(stop) ?? "transparent");
}
}
gradient.addColorStop(c1.stop, c1.color);
c0 = c1;
}
if ("createPattern" in gradient) {
gradient = gradient.createPattern();
}
this._cache = { ctx, bbox, gradient };
return gradient;
}
toSvg(shapeBbox) {
const bbox = this.bbox ?? shapeBbox;
const gradient = this.createSvgGradient(bbox);
for (const { stop: offset, color: color8 } of this.stops) {
const stop = createSvgElement2("stop");
stop.setAttribute("offset", `${offset}`);
stop.setAttribute("stop-color", `${color8}`);
gradient.appendChild(stop);
}
return gradient;
}
};
// packages/ag-charts-community/src/scene/gradient/conicGradient.ts
var ConicGradient = class extends Gradient {
constructor(colorSpace, stops, angle = 0, bbox) {
super(colorSpace, stops, bbox);
this.angle = angle;
}
createCanvasGradient(ctx, bbox, params) {
const angleOffset = -90;
const { angle } = this;
const radians = normalizeAngle360FromDegrees(angle + 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 createSvgElement3("linearGradient");
}
};
// packages/ag-charts-community/src/scene/gradient/linearGradient.ts
import { createSvgElement as createSvgElement4, normalizeAngle360FromDegrees as normalizeAngle360FromDegrees2 } from "ag-charts-core";
var LinearGradient = class extends Gradient {
constructor(colorSpace, stops, angle = 0, bbox) {
super(colorSpace, stops, bbox);
this.angle = angle;
}
getGradientPoints(bbox) {
const angleOffset = 90;
const { angle } = this;
const radians = normalizeAngle360FromDegrees2(angle + 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 gradient = createSvgElement4("linearGradient");
gradient.setAttribute("x1", String(x0));
gradient.setAttribute("y1", String(y0));
gradient.setAttribute("x2", String(x1));
gradient.setAttribute("y2", String(y1));
gradient.setAttribute("gradientUnits", "userSpaceOnUse");
return gradient;
}
};
// packages/ag-charts-community/src/scene/gradient/radialGradient.ts
import { createSvgElement as createSvgElement5 } from "ag-charts-core";
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 gradient = createSvgElement5("radialGradient");
gradient.setAttribute("cx", String(cx));
gradient.setAttribute("cy", String(cy));
gradient.setAttribute("r", String(Math.hypot(bbox.width * 0.5, bbox.height * 0.5) / Math.SQRT2));
gradient.setAttribute("gradientUnits", "userSpaceOnUse");
return gradient;
}
};
// packages/ag-charts-community/src/scene/gradient/stops.ts
import { BaseProperties, Logger as Logger4, Property } from "ag-charts-core";
var StopProperties = class extends BaseProperties {
constructor() {
super(...arguments);
this.color = "black";
}
};
__decorateClass([
Property
], StopProperties.prototype, "stop", 2);
__decorateClass([
Property
], 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((colorStop, i) => {
const { stop } = colorStop;
const nextColor = colorStops.at(i + 1)?.color;
return nextColor == null ? [colorStop] : [colorStop, { stop, color: nextColor }];
});
}
function getDefaultColorStops(defaultColorStops, fillMode) {
const stopOffset = fillMode === "discrete" ? 1 : 0;
const colorStops = defaultColorStops.map(
(color8, index, { length }) => ({
stop: (index + stopOffset) / (length - 1 + stopOffset),
color: color8
})
);
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)) {
Logger4.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 colorStop = 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 = colorStop?.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 color8 = fill?.color;
const stop = stops[i];
if (color8 != null) {
lastDefinedColor = color8;
} else if (lastDefinedColor == null) {
if (colorScale == null) {
colorScale = new ColorScale();
colorScale.domain = [0, 1];
colorScale.range = defaultColorStops;
}
color8 = colorScale.convert(stop);
} else {
color8 = lastDefinedColor;
}
return { stop, color: color8 };
});
return fillMode === "discrete" ? discreteColorStops(colorStops) : colorStops;
}
// packages/ag-charts-community/src/scene/image/image.ts
import {
Logger as Logger5,
createSvgElement as createSvgElement6,
getDOMMatrix,
normalizeAngle360FromDegrees as normalizeAngle360FromDegrees3
} from "ag-charts-core";
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, width, height) {
if (!image)
return null;
const [renderedWidth, renderedHeight] = this.getSize(image.width, image.height, width, height);
if (renderedWidth < 1 || renderedHeight < 1) {
Logger5.warnOnce("Image fill is too small to render, ignoring.");
return null;
}
return ctx.createPattern(image, this.repeat);
}
getSize(imageWidth, imageHeight, width, height) {
const { fit } = this;
let dw = imageWidth;
let dh = imageHeight;
let scale2 = 1;
const shapeAspectRatio = width / height;
const imageAspectRatio = imageWidth / imageHeight;
if (fit === "stretch" || imageWidth === 0 || imageHeight === 0) {
dw = width;
dh = height;
} else if (fit === "contain") {
scale2 = imageAspectRatio > shapeAspectRatio ? width / imageWidth : height / imageHeight;
} else if (fit === "cover") {
scale2 = imageAspectRatio > shapeAspectRatio ? height / imageHeight : width / imageWidth;
}
return [Math.max(1, dw * scale2), Math.max(1, dh * scale2)];
}
setImageTransform(pattern, bbox) {
if (typeof pattern === "string")
return;
const { url, rotation, width, height } = this;
const image = this.imageLoader?.loadImage(url);
if (!image) {
return;
}
const angle = normalizeAngle360FromDegrees3(rotation);
const cos = Math.cos(angle);
const sin = Math.sin(angle);
const [renderedWidth, renderedHeight] = this.getSize(
image.width,
image.height,
width ?? bbox.width,
height ?? 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 width = this.width ?? shapeWidth;
const height = this.height ?? shapeHeight;
const cache = this._cache;
if (cache?.ctx === ctx && cache.width === width && cache.height === height) {
return cache.pattern;
}
const image = this.imageLoader?.loadImage(this.url, node);
const pattern = this.createCanvasImage(ctx, image, width, height);
if (pattern == null)
return;
this._cache = { ctx, pattern, width, height };
return pattern;
}
toSvg(bbox, pixelRatio) {
const { url, rotation, backgroundFill, backgroundFillOpacity } = this;
const { x, y, width, height } = bbox;
const pattern = createSvgElement6("pattern");
pattern.setAttribute("viewBox", `0 0 ${width} ${height}`);
pattern.setAttribute("x", String(x));
pattern.setAttribute("y", String(y));
pattern.setAttribute("width", String(width));
pattern.setAttribute("height", String(height));
pattern.setAttribute("patternUnits", "userSpaceOnUse");
const rect2 = createSvgElement6("rect");
rect2.setAttribute("x", "0");
rect2.setAttribute("y", "0");
rect2.setAttribute("width", String(width));
rect2.setAttribute("height", String(height));
rect2.setAttribute("fill", backgroundFill);
rect2.setAttribute("fill-opacity", String(backgroundFillOpacity));
pattern.appendChild(rect2);
const image = createSvgElement6("image");
image.setAttribute("href", url);
image.setAttribute("x", "0");
image.setAttribute("y", "0");
image.setAttribute("width", String(width));
image.setAttribute("height", String(height));
image.setAttribute("preserveAspectRatio", "none");
image.setAttribute("transform", `scale(${1 / pixelRatio}) rotate(${rotation}, ${width / 2}, ${height / 2})`);
pattern.appendChild(image);
return pattern;
}
};
// packages/ag-charts-community/src/scene/pattern/pattern.ts
import {
Logger as Logger7,
createSvgElement as createSvgElement7,
getDOMMatrix as getDOMMatrix2,
normalizeAngle360FromDegrees as normalizeAngle360FromDegrees4
} from "ag-charts-core";
// packages/ag-charts-community/src/scene/extendedPath2D.ts
import {
bezier2DDistance,
bezier2DExtrema,
evaluateBezier,
getPath2D,
lineDistanceSquared,
normalizeAngle360
} from "ag-charts-core";
// packages/ag-charts-community/src/util/svg.ts
import { Logger as Logger6 } from "ag-charts-core";
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) {
Logger6.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 {
Logger6.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, width, height) {
this.moveTo(x, y);
this.lineTo(x + width, y);
this.lineTo(x + width, y + height);
this.lineTo(x, y + height);
this.closePath();
}
roundRect(x, y, width, height, radii) {
radii = Math.min(radii, width / 2, height / 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 + width - radii, y);
this.arc(x + width - radii, y + radii, radii, 1.5 * Math.PI, 2 * Math.PI);
this.lineTo(x + width, y + radii);
this.lineTo(x + width, y + height - radii);
this.arc(x + width - radii, y + height - radii, radii, 0, Math.PI / 2);
this.lineTo(x + width - radii, y + height);
this.lineTo(x + radii, y + height);
this.arc(x + +radii, y + height - radii, radii, Math.PI / 2, Math.PI);
this.lineTo(x, y + height - 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 distanceSquared = (sx - this.cx) ** 2 + (sy - this.cy) ** 2;
if (!this.openedPath) {
this.moveTo(sx, sy);
} else if (distanceSquared > 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/pattern/patterns.ts
import { toRadians } from "ag-charts-core";
// packages/ag-charts-community/src/scene/util/pixel.ts
function align(pixelRatio, start, length) {
const alignedStart = Math.round(start * pixelRatio) / pixelRatio;
if (length == null) {
return alignedStart;
} else if (length === 0) {
return 0;
} else if (length < 1) {
return Math.ceil(length * pixelRatio) / pixelRatio;
}
return Math.round((length + start) * pixelRatio) / pixelRatio - alignedStart;
}
function alignBefore(pixelRatio, start) {
return Math.floor(start * pixelRatio) / pixelRatio;
}
// packages/ag-charts-community/src/scene/pattern/patterns.ts
function drawPatternUnitPolygon(path, params, moves) {
const { width, height, padding: padding2, strokeWidth } = params;
const x0 = width / 2;
const y0 = height / 2;
const w = Math.max(1, width - padding2 - strokeWidth / 2);
const h = Math.max(1, height - 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, strokeWidth, padding: padding2 }) {
const c = width / 2;
const r = Math.max(1, c - padding2 - strokeWidth / 2);
path.arc(c, c, r, 0, Math.PI * 2);
},
squares(path, { width, height, pixelRatio, padding: padding2, strokeWidth }) {
const offset = padding2 + strokeWidth / 2;
path.moveTo(align(pixelRatio, offset), align(pixelRatio, offset));
path.lineTo(align(pixelRatio, width - offset), align(pixelRatio, offset));
path.lineTo(align(pixelRatio, width - offset), align(pixelRatio, height - offset));
path.lineTo(align(pixelRatio, offset), align(pixelRatio, height - 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, height, padding: padding2 }) {
const spikes = 5;
const outerRadius = Math.max(1, (width - 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 angle = i * Math.PI / spikes - rotation;
const xCoordinate = width / 2 + Math.cos(angle) * radius;
const yCoordinate = height / 2 + Math.sin(angle) * radius;
path.lineTo(xCoordinate, yCoordinate);
}
path.closePath();
},
hearts(path, { width, height, padding: padding2 }) {
const r = Math.max(1, width / 4 - padding2 / 2);
const x = width / 2;
const y = height / 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, height, pixelRatio, strokeWidth }) {
const x = align(pixelRatio, width / 2) - strokeWidth % 2 / 2;
path.moveTo(x, 0);
path.lineTo(x, height);
},
"horizontal-lines"(path, { width, height, pixelRatio, strokeWidth }) {
const y = align(pixelRatio, height / 2) - strokeWidth % 2 / 2;
path.moveTo(0, y);
path.lineTo(width, y);
},
"forward-slanted-lines"(path, { width, height, strokeWidth }) {
const angle = Math.atan2(height, width);
const insetX = strokeWidth * Math.cos(angle);
const insetY = strokeWidth * Math.sin(angle);
path.moveTo(-insetX, insetY);
path.lineTo(insetX, -insetY);
path.moveTo(-insetX, height + insetY);
path.lineTo(width + insetX, -insetY);
path.moveTo(width - insetX, height + insetY);
path.lineTo(width + insetX, height - insetY);
},
"backward-slanted-lines"(path, { width, height, strokeWidth }) {
const angle = Math.atan2(height, width);
const insetX = strokeWidth * Math.cos(angle);
const insetY = strokeWidth * Math.sin(angle);
path.moveTo(width - insetX, -insetY);
path.lineTo(width + insetX, insetY);
path.moveTo(-insetX, -insetY);
path.lineTo(width + insetX, height + insetY);
path.moveTo(-insetX, height - insetY);
path.lineTo(insetX, height + 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, height, 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, height, 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, height, scale: scale2, backgroundFill, backgroundFillOpacity } = this;
if (width * scale2 < 1 || height * scale2 < 1) {
Logger7.warnOnce("Pattern fill is too small to render, ignoring.");
return null;
}
const offscreenPattern = new HdpiOffscreenCanvas({ width, height, pixelRatio: pixelRatio * scale2 });
const offscreenPatternCtx = offscreenPattern.context;
if (backgroundFill !== "none") {
offscreenPatternCtx.fillStyle = backgroundFill;
offscreenPatternCtx.globalAlpha = backgroundFillOpacity;
offscreenPatternCtx.fillRect(0, 0, width, height);
}
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 angle = normalizeAngle360FromDegrees4(this.rotation);
const scale2 = 1 / pixelRatio;
const cos = Math.cos(angle) * scale2;
const sin = Math.sin(angle) * scale2;
const DOMMatrixCtor = getDOMMatrix2();
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,
height,
fill,
fillOpacity,
backgroundFill,
backgroundFillOpacity,
stroke,
strokeWidth,
strokeOpacity,
rotation,
scale: scale2
} = this;
const pattern = createSvgElement7("pattern");
pattern.setAttribute("viewBox", `0 0 ${width} ${height}`);
pattern.setAttribute("width", String(width));
pattern.setAttribute("height", String(height));
pattern.setAttribute("patternUnits", "userSpaceOnUse");
const rect2 = createSvgElement7("rect");
rect2.setAttribute("x", "0");
rect2.setAttribute("y", "0");
rect2.setAttribute("width", String(width));
rect2.setAttribute("height", String(height));
rect2.setAttribute("fill", backgroundFill);
rect2.setAttribute("fill-opacity", String(backgroundFillOpacity));
pattern.appendChild(rect2);
const path = createSvgElement7("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 = "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 (gradient) {
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 (objectsEqual2(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(start, length) {
return align(this.layerManager?.canvas?.pixelRatio ?? 1, start, length);
}
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 gradient = this.fillGradient.toSvg(this.fillBBox ?? this.getBBox());
const id = generateUUID();
gradient.setAttribute("id", id);
defs.push(gradient);
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(style, fillBBox, fillParams) {
const opacity = style?.opacity ?? 1;
const fill = style?.fill;
const computedFillOpacity = (style?.fillOpacity ?? 1) * opacity;
const computedStrokeOpacity = (style?.strokeOpacity ?? 1) * opacity;
const computedStrokeWidth = style?.strokeWidth ?? 0;
const computedLineDashOffset = style?.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 !== style?.lineDash) {
this.__lineDash = style?.lineDash;
hasDirectChanges = true;
}
this.setFillProperties(fill, fillBBox, fillParams);
if (fill !== this.fill) {
this.fill = fill;
}
if (style?.stroke !== this.stroke) {
this.stroke = style?.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([
DeclaredSceneChangeDetection2()
], _Shape.prototype, "drawingMode", 2);
__decorateClass([
DeclaredSceneChangeDetection2()
], _Shape.prototype, "fillOpacity", 2);
__decorateClass([
DeclaredSceneChangeDetection2()
], _Shape.prototype, "strokeOpacity", 2);
__decorateClass([
DeclaredSceneObjectChangeDetection({
equals: objectsEqual2,
changeCb: _Shape.handleFillChange
})
], _Shape.prototype, "fill", 2);
__decorateClass([
SceneObjectChangeDetection({ equals: objectsEqual2, changeCb: _Shape.handleStrokeChange })
], _Shape.prototype, "stroke", 2);
__decorateClass([
DeclaredSceneChangeDetection2()
], _Shape.prototype, "strokeWidth", 2);
__decorateClass([
SceneArrayChangeDetection()
], _Shape.prototype, "lineDash", 2);
__decorateClass([
DeclaredSceneChangeDetection2()
], _Shape.prototype, "lineDashOffset", 2);
__decorateClass([
DeclaredSceneChangeDetection2()
], _Shape.prototype, "lineCap", 2);
__decorateClass([
DeclaredSceneChangeDetection2()
], _Shape.prototype, "lineJoin", 2);
__decorateClass([
DeclaredSceneChangeDetection2()
], _Shape.prototype, "miterLimit", 2);
__decorateClass([
DeclaredSceneChangeDetection2({ convertor: (v) => clamp4(0, v ?? 1, 1) })
], _Shape.prototype, "opacity", 2);
__decorateClass([
SceneObjectChangeDetection({ equals: TRIPLE_EQ, checkDirtyOnAssignment: true })
], _Shape.prototype, "fillShadow", 2);
__decorateClass([
DeclaredSceneObjectChangeDetection({ equals: boxesEqual2, changeCb: (s) => s.onFillChange() })
], _Shape.prototype, "fillBBox", 2);
__decorateClass([
DeclaredSceneObjectChangeDetection({ equals: objectsEqual2, changeCb: (s) => s.onFillChange() })
], _Shape.prototype, "fillParams", 2);
var Shape = _Shape;
// packages/ag-charts-community/src/scene/transformable.ts
import { createSvgElement as createSvgElement8 } from "ag-charts-core";
// packages/ag-charts-community/src/scene/matrix.ts
import { isNumberEqual } from "ag-charts-core";
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 = createSvgElement8("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 width = Math.ceil(bbox.x + bbox.width - x + padding2);
const height = Math.ceil(bbox.y + bbox.height - y + padding2);
return new BBox(x, y, width, height);
}
prepareSharedCanvas(width, height, pixelRatio) {
if (sharedOffscreenCanvas?.pixelRatio === pixelRatio) {
sharedOffscreenCanvas.resize(width, height, pixelRatio);
} else {
sharedOffscreenCanvas = new HdpiOffscreenCanvas({ width, height, 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, height, devicePixelRatio } = renderCtx;
const { dirty, layer } = this;
const layerResized = layer != null && (this._lastWidth !== width || this._lastHeight !== height);
const pixelRatioChanged = this._lastDevicePixelRatio !== devicePixelRatio;
this._lastWidth = width;
this._lastHeight = height;
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, height } = bbox;
const scaledWidth = Math.floor(width * pixelRatio);
const scaledHeight = Math.floor(height * pixelRatio);
if (scaledWidth > 0 && scaledHeight > 0) {
const canvas = this.prepareSharedCanvas(width, height, pixelRatio);
renderOffscreen(canvas, pixelRatio, 0, 0, pixelRatio, -x * pixelRatio, -y * pixelRatio);
image = { bitmap: canvas.transferToImageBitmap(), x, y, width, height };
}
}
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, height } = image;
ctx.drawImage(bitmap, 0, 0, width * pixelRatio, height * pixelRatio, x, y, width, height);
}
ctx.globalAlpha = globalAlpha;
super.render(childRenderCtx);
}
applyClip(ctx, clipRect) {
const { x, y, width, height } = clipRect;
ctx.beginPath();
ctx.rect(x, y, width, height);
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) => clamp5(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/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 (error) {
reject?.(error);
}
[next, done, reject] = this.acquireQueue.shift() ?? [];
}
this.available = true;
}
};
// 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/util/render.ts
import { getWindow } from "ag-charts-core";
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/widget.ts
import {
attachListener as attachListener4,
getAttribute,
getElementBBox,
getWindow as getWindow4,
setAttribute,
setElementBBox,
setElementStyle,
setElementStyles
} from "ag-charts-core";
// 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
import { entries } from "ag-charts-core";
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/widgetListenerInternal.ts
import { CleanupRegistry as CleanupRegistry3, attachListener as attachListener3, boxContains as boxContains2, partialAssign } from "ag-charts-core";
// packages/ag-charts-community/src/widget/mouseDragger.ts
import { CleanupRegistry, attachListener, getWindow as getWindow2 } from "ag-charts-core";
var MouseDragger = class {
constructor(glob, self, myCallbacks, downEvent) {
this.glob = glob;
this.self = self;
this.window = getWindow2();
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
import { CleanupRegistry as CleanupRegistry2, attachListener as attachListener2, getWindow as getWindow3 } from "ag-charts-core";
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 CleanupRegistry2();
this.longTapInterrupted = false;
this.longtap = () => {
const { target, initialTouch } = this;
if (!this.longTapInterrupted) {
const cleanup = new CleanupRegistry2();
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(
attachListener2(target, "touchmove", longTapMove, { passive: false }),
attachListener2(target, "touchend", longTapEnd, { passive: false }),
attachListener2(target, "touchcancel", longTapEnd, { passive: false })
);
const { clientX, clientY } = initialTouch;
const contextMenuEvent = new PointerEvent("contextmenu", {
bubbles: true,
cancelable: true,
view: getWindow3(),
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(
attachListener2(target, "touchmove", touchmove, { passive: false }),
attachListener2(target, "touchstart", touchend, { passive: false }),
attachListener2(target, "touchend", touchend, { passive: false }),
attachListener2(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, origin, sourceEvent) {
const { currentX, currentY } = WidgetEventUtil.calcCurrentXY(current.getElement(), sourceEvent);
const originDeltaX = sourceEvent.pageX - origin.pageX;
const originDeltaY = sourceEvent.pageY - origin.pageY;
return {
type,
device: "mouse",
offsetX: origin.offsetX + originDeltaX,
offsetY: origin.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, origin, sourceEvent, touch) {
const { currentX, currentY } = WidgetEventUtil.calcCurrentXY(current.getElement(), touch);
const originDeltaX = touch.pageX - origin.pageX;
const originDeltaY = touch.pageY - origin.pageY;
return {
type,
device: "touch",
offsetX: origin.offsetX + originDeltaX,
offsetY: origin.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 CleanupRegistry3();
cleanup.register(
attachListener3(element2, "mousedown", (event) => this.triggerMouseDrag(target, event)),
attachListener3(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 origin = { pageX: Number.NaN, pageY: Number.NaN, offsetX: Number.NaN, offsetY: Number.NaN };
partialAssign(["pageX", "pageY", "offsetX", "offsetY"], origin, initialDownEvent);
const dragCallbacks = {
mousedown: (downEvent) => {
const dragStartEvent = makeMouseDrag(current, "drag-start", origin, downEvent);
this.dispatch("drag-start", current, dragStartEvent);
},
mousemove: (moveEvent) => {
const dragMoveEvent = makeMouseDrag(current, "drag-move", origin, moveEvent);
this.dispatch("drag-move", current, dragMoveEvent);
},
mouseup: (upEvent) => {
const dragEndEvent = makeMouseDrag(current, "drag-end", origin, 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 (!boxContains2(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 origin = { pageX: Number.NaN, pageY: Number.NaN, ...getTouchOffsets(current, initialTouch) };
partialAssign(["pageX", "pageY"], origin, initialTouch);
const dragCallbacks = {
touchmove: (moveEvent, touch) => {
const dragMoveEvent = makeTouchDrag(current, "drag-move", origin, moveEvent, touch);
this.dispatch("drag-move", current, dragMoveEvent);
},
touchend: (cancelEvent, touch) => {
const dragMoveEvent = makeTouchDrag(current, "drag-end", origin, 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", origin, 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 getWindow4()?.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 attachListener4(getWindow4(), "pagehide", pagehideHandler);
}
};
// packages/ag-charts-community/src/chart/background/background.ts
import { AbstractModuleInstance, Property as Property2, ProxyPropertyOnWrite, ZIndexMap } from "ag-charts-core";
// packages/ag-charts-community/src/scene/shape/rect.ts
import { DeclaredSceneChangeDetection as DeclaredSceneChangeDetection3, boxesEqual as boxesEqual3, isNumberEqual as isNumberEqual2 } from "ag-charts-core";
// 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
import { SceneChangeDetection as SceneChangeDetection2, createSvgElement as createSvgElement9 } from "ag-charts-core";
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 = createSvgElement9("path");
element2.setAttribute("d", this.svgPathData());
const defs = this.applySvgFillAttributes(element2, []);
this.applySvgStrokeAttributes(element2);
return {
elements: [element2],
defs
};
}
};
Path.className = "Path";
__decorateClass([
SceneChangeDetection2()
], Path.prototype, "clip", 2);
__decorateClass([
SceneChangeDetection2()
], Path.prototype, "clipX", 1);
__decorateClass([
SceneChangeDetection2()
], 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 (isNumberEqual2(leading0, 0)) {
leading0 = 0;
}
if (trailing1 > trailingEdge) {
trailingClipped = true;
trailing0 = trailingInset - Math.sqrt(Math.max(cornerRadius ** 2 - (leadingInset - trailingEdge) ** 2));
trailing1 = trailingEdge;
} else if (isNumberEqual2(trailing1, 0)) {
trailing1 = 0;
}
return { leading0, leading1, trailing0, trailing1, leadingClipped, trailingClipped };
}
function clippedRoundRect(path, x, y, width, height, 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, width, height);
} else {
const x0 = Math.max(x, clipBBox.x);
const x1 = Math.min(x + width, clipBBox.x + clipBBox.width);
const y0 = Math.max(y, clipBBox.y);
const y1 = Math.min(y + height, 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, width, height, topLeftCornerRadius);
return;
}
if (width < 0) {
x += width;
width = Math.abs(width);
}
if (height < 0) {
y += height;
height = Math.abs(height);
}
if (width <= 0 || height <= 0)
return;
if (clipBBox == null) {
clipBBox = new BBox(x, y, width, height);
} else {
const x0 = Math.max(x, clipBBox.x);
const x1 = Math.min(x + width, clipBBox.x + clipBBox.width);
const y0 = Math.max(y, clipBBox.y);
const y1 = Math.min(y + height, clipBBox.y + clipBBox.height);
clipBBox = new BBox(x0, y0, x1 - x0, y1 - y0);
}
const borderScale = Math.max(maxVerticalCornerRadius / height, maxHorizontalCornerRadius / width, 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 + width - 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 + width - 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 + width - bottomRightCornerRadius), 0),
Math.max(clipBBox.y + clipBBox.height - (y + height - 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 + width - bottomRightCornerRadius;
const cy = y + height - bottomRightCornerRadius;
bottomRightCorner = { x0, y0, x1, y1, cx, cy };
}
if (drawBottomLeftCorner) {
const nodes = cornerEdges(
clipBBox.width,
clipBBox.height,
Math.max(clipBBox.y + clipBBox.height - (y + height - 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 + height - 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: width, __height: height, __clipBBox: clipBBox } = this;
return clipBBox?.clone() ?? new BBox(x, y, width, height);
}
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, width, height, opacity, clipBBox) {
this.__x = x;
this.__y = y;
this.__width = width;
this.__height = height;
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([
DeclaredSceneChangeDetection3()
], Rect.prototype, "x", 2);
__decorateClass([
DeclaredSceneChangeDetection3()
], Rect.prototype, "y", 2);
__decorateClass([
DeclaredSceneChangeDetection3()
], Rect.prototype, "width", 2);
__decorateClass([
DeclaredSceneChangeDetection3()
], Rect.prototype, "height", 2);
__decorateClass([
DeclaredSceneChangeDetection3()
], Rect.prototype, "topLeftCornerRadius", 2);
__decorateClass([
DeclaredSceneChangeDetection3()
], Rect.prototype, "topRightCornerRadius", 2);
__decorateClass([
DeclaredSceneChangeDetection3()
], Rect.prototype, "bottomRightCornerRadius", 2);
__decorateClass([
DeclaredSceneChangeDetection3()
], Rect.prototype, "bottomLeftCornerRadius", 2);
__decorateClass([
DeclaredSceneChangeDetection3({ equals: boxesEqual3 })
], Rect.prototype, "clipBBox", 2);
__decorateClass([
DeclaredSceneChangeDetection3()
], Rect.prototype, "crisp", 2);
function areCornersZero(cornerRadius) {
return cornerRadius === 0;
}
// packages/ag-charts-community/src/scene/shape/text.ts
import {
Debug as Debug3,
LineSplitter,
SceneRefChangeDetection,
cachedTextMeasurer,
createSvgElement as createSvgElement10,
isArray,
measureTextSegments,
toFontString,
toPlainText,
toTextString
} from "ag-charts-core";
// packages/ag-charts-community/src/scene/sceneDebug.ts
import { Debug as Debug2, DebugMetrics, Logger as Logger8, TextMeasurer, getWindow as getWindow5, isString as isString2, toArray } from "ag-charts-core";
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 (!Debug2.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;
Logger8.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 (!Debug2.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 end2 = performance.now();
const { start, ...durations } = debugSplitTimes;
const totalTime = end2 - start;
const statsAccumulator = getStatsAccumulator();
statsAccumulator.recordTimings(durations);
statsAccumulator.recordTiming("\u23F1\uFE0F", totalTime);
const splits = Object.entries(durations).map(([n, t]) => time2(n, t)).filter((v) => v != null).join(" + ");
const extras = Object.entries(extraDebugStats).map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join(" ; ");
const detailedStats = Debug2.check("scene:stats:verbose" /* SCENE_STATS_VERBOSE */);
const memUsage = detailedStats ? memoryUsage() : null;
const metrics = detailedStats ? DebugMetrics.flush() : {};
const metricsEntries = Object.entries(metrics);
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 = [
`${time2("\u23F1\uFE0F", start, end2)} (${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(isString2);
const measurer = new TextMeasurer(ctx);
const statsSize = new Map(stats.map((t) => [t, measurer.measureText(t)]));
const width = Math.max(...Array.from(statsSize.values(), (s) => s.width));
const height = accumulate(statsSize.values(), (s) => s.height);
const x = 2 + seriesRect.x;
ctx.save();
try {
ctx.fillStyle = colors?.background ?? "white";
ctx.fillRect(x, 0, width, height);
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) {
Logger8.warnOnce("Error during debug stats rendering", e);
} finally {
ctx.restore();
}
}
function prepareSceneNodeHighlight(ctx) {
const config = toArray(getWindow5("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) {
Logger8.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) {
Logger8.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 (!Debug2.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 time2(name, start, end2) {
const duration = end2 == null ? start : end2 - start;
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, height, lineMetrics } = measureTextSegments(text, font);
const totalHeight = lineHeight ? lineHeight * lineMetrics.length : height;
const offsetTop = _Text.calcTopOffset(totalHeight, lineMetrics[0], textBaseline);
const offsetLeft = _Text.calcLeftOffset(width, textAlign);
return new BBox(x - offsetLeft, y - offsetTop, width, 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, height, lineMetrics } = cachedTextMeasurer(font).measureLines(lines);
const totalHeight = lineHeight ? lineHeight * lineMetrics.length : height;
const offsetTop = _Text.calcTopOffset(totalHeight, lineMetrics[0], textBaseline);
const offsetLeft = _Text.calcLeftOffset(width, textAlign);
return new BBox(x - offsetLeft, y - offsetTop, width, totalHeight);
}
static calcTopOffset(height, textMetrics, textBaseline) {
switch (textBaseline) {
case "alphabetic":
return textMetrics?.ascent ?? 0;
case "middle":
return height / 2;
case "bottom":
return height;
default:
return 0;
}
}
static calcSegmentedTopOffset(height, 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
) : height / 2;
case "bottom":
return height;
default:
return 0;
}
}
static calcLeftOffset(width, textAlign) {
let offset = 0;
switch (textAlign) {
case "center":
offset = 0.5;
break;
case "right":
case "end":
offset = 1;
}
return width * offset;
}
getBBox() {
const bbox = super.getBBox();
if (!this.textMap?.size || !isArray(this.text))
return bbox;
const { height, lineMetrics } = measureTextSegments(this.text, this);
const offsetTop = _Text.calcSegmentedTopOffset(height, 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, height, ascent, segments } of measureTextSegments(this.text, this).lineMetrics) {
let offsetX = 0;
for (const { color: color8, textMetrics, ...segment } of segments) {
const textNode = textNodes.next().value;
textNode.x = this.x - width / 2 + offsetX;
textNode.y = ascent + offsetY;
textNode.setProperties({ ...segment, fill: color8 ?? this.fill });
const textBBox = textNode.getBBox();
this.textMap.set(textNode, textBBox);
offsetX += textMetrics.width;
}
offsetY += height;
}
}
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, height, lineMetrics } = measureTextSegments(this.text, this);
let translateX = 0;
switch (this.textAlign) {
case "left":
case "start":
translateX = width / 2;
break;
case "right":
case "end":
translateX = width / -2;
}
const translateY = this.y - _Text.calcSegmentedTopOffset(height, 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, height } = textBBox.grow(this.boxPadding);
this.boxing.opacity = this.opacity;
this.boxing.x = x;
this.boxing.y = y;
this.boxing.width = width;
this.boxing.height = height;
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 = createSvgElement10("text");
if (isArray(text)) {
for (const segment of text) {
const segmentElement = createSvgElement10("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 = Debug3.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/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: ZIndexMap.CHART_BACKGROUND });
}
onLayoutComplete(e) {
const { width, height } = e.chart;
this.rectNode.width = width;
this.rectNode.height = height;
}
};
__decorateClass([
Property2,
ProxyPropertyOnWrite("node", "visible")
], Background.prototype, "visible", 2);
__decorateClass([
Property2,
ProxyPropertyOnWrite("rectNode", "fill")
], Background.prototype, "fill", 2);
__decorateClass([
Property2
], Background.prototype, "image", 2);
__decorateClass([
Property2,
ProxyPropertyOnWrite("textNode")
], Background.prototype, "text", 2);
// packages/ag-charts-community/src/chart/caption.ts
import {
BaseProperties as BaseProperties2,
FONT_SIZE,
Property as Property3,
ProxyPropertyOnWrite as ProxyPropertyOnWrite2,
createId as createId2,
isArray as isArray2,
isSegmentTruncated,
isTextTruncated,
toPlainText as toPlainText2,
toTextString as toTextString2,
wrapText,
wrapTextSegments
} from "ag-charts-core";
var Caption = class extends BaseProperties2 {
constructor() {
super(...arguments);
this.id = createId2(this);
this.node = new RotatableText({ zIndex: 1 }).setProperties({
textAlign: "center",
pointerEvents: 1 /* None */
});
this.enabled = false;
this.textAlign = "center";
this.fontSize = FONT_SIZE.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 (isArray2(text)) {
wrappedText = wrapTextSegments(text, options);
this.truncated = wrappedText.some(isSegmentTruncated);
} else {
wrappedText = wrapText(toTextString2(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 = toPlainText2(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: toPlainText2(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([
Property3,
ProxyPropertyOnWrite2("node", "visible")
], Caption.prototype, "enabled", 2);
__decorateClass([
Property3,
ProxyPropertyOnWrite2("node")
], Caption.prototype, "text", 2);
__decorateClass([
Property3,
ProxyPropertyOnWrite2("node")
], Caption.prototype, "textAlign", 2);
__decorateClass([
Property3,
ProxyPropertyOnWrite2("node")
], Caption.prototype, "fontStyle", 2);
__decorateClass([
Property3,
ProxyPropertyOnWrite2("node")
], Caption.prototype, "fontWeight", 2);
__decorateClass([
Property3,
ProxyPropertyOnWrite2("node")
], Caption.prototype, "fontSize", 2);
__decorateClass([
Property3,
ProxyPropertyOnWrite2("node")
], Caption.prototype, "fontFamily", 2);
__decorateClass([
Property3,
ProxyPropertyOnWrite2("node", "fill")
], Caption.prototype, "color", 2);
__decorateClass([
Property3
], Caption.prototype, "spacing", 2);
__decorateClass([
Property3
], Caption.prototype, "maxWidth", 2);
__decorateClass([
Property3
], Caption.prototype, "maxHeight", 2);
__decorateClass([
Property3
], Caption.prototype, "wrapping", 2);
__decorateClass([
Property3
], Caption.prototype, "padding", 2);
__decorateClass([
Property3
], Caption.prototype, "layoutStyle", 2);
// packages/ag-charts-community/src/chart/chartAxes.ts
import { ChartAxisDirection, every, isObject } from "ag-charts-core";
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, object5) => isObject(object5) && "type" in object5 && this.findById(id)?.type === object5.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 [ChartAxisDirection.X]() {
return this.getById(ChartAxisDirection.X);
}
get [ChartAxisDirection.Y]() {
return this.getById(ChartAxisDirection.Y);
}
perpendicular(to) {
const direction = to.direction === ChartAxisDirection.X ? ChartAxisDirection.Y : ChartAxisDirection.X;
return this[direction];
}
};
var PolarChartAxes = class extends ChartAxes {
get [ChartAxisDirection.Angle]() {
return this.getById(ChartAxisDirection.Angle);
}
get [ChartAxisDirection.Radius]() {
return this.getById(ChartAxisDirection.Radius);
}
};
// packages/ag-charts-community/src/chart/chartCaptions.ts
import { Property as Property4, cachedTextMeasurer as cachedTextMeasurer2, isArray as isArray3, measureTextSegments as measureTextSegments2, toTextString as toTextString3 } from "ag-charts-core";
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 } = isArray3(caption.text) ? measureTextSegments2(caption.text, caption) : cachedTextMeasurer2(caption).measureLines(toTextString3(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" && isArray3(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([
Property4
], ChartCaptions.prototype, "title", 2);
__decorateClass([
Property4
], ChartCaptions.prototype, "subtitle", 2);
__decorateClass([
Property4
], ChartCaptions.prototype, "footnote", 2);
// packages/ag-charts-community/src/chart/chartContext.ts
import {
CallbackCache,
ChartUpdateType as ChartUpdateType3,
CleanupRegistry as CleanupRegistry11,
EventEmitter as EventEmitter6,
ModuleRegistry,
ModuleType
} from "ag-charts-core";
// packages/ag-charts-community/src/api/preset/chartTypeOriginator.ts
import { Logger as Logger9 } from "ag-charts-core";
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) => Logger9.error("error restoring state", e));
}
};
// packages/ag-charts-community/src/api/state/historyManager.ts
import { CleanupRegistry as CleanupRegistry4, Debug as Debug4 } from "ag-charts-core";
// packages/ag-charts-community/src/version.ts
var VERSION = "13.1.0";
// 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 = Debug4.create(true, "history");
this.cleanup = new CleanupRegistry4();
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
import { MementoCaretaker, objectsEqual as objectsEqual3 } from "ag-charts-core";
var StateManager = class {
constructor() {
this.caretaker = new MementoCaretaker(VERSION);
this.state = /* @__PURE__ */ new Map();
}
setState(originator, value) {
if (objectsEqual3(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/dom/domManager.ts
import {
attachListener as attachListener6,
createElement,
createId as createId3,
entries as entries2,
getDocument as getDocument3,
getWindow as getWindow9,
isDocumentFragment,
kebabCase,
setAttribute as setAttribute3,
stopPageScrolling
} from "ag-charts-core";
// 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
import { CleanupRegistry as CleanupRegistry5 } from "ag-charts-core";
var BaseManager = class {
constructor() {
this.cleanup = new CleanupRegistry5();
this.destroyed = false;
}
destroy() {
this.cleanup.flush();
this.destroyed = true;
}
};
// packages/ag-charts-community/src/util/guardedElement.ts
import { CleanupRegistry as CleanupRegistry6, attachListener as attachListener5, getDocument, getWindow as getWindow6, isHTMLElement, setAttribute as setAttribute2 } from "ag-charts-core";
var GuardedElement = class _GuardedElement {
constructor(element2, topTabGuard, bottomTabGuard) {
this.element = element2;
this.topTabGuard = topTabGuard;
this.bottomTabGuard = bottomTabGuard;
this.cleanup = new CleanupRegistry6();
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(attachListener5(guard, "focus", () => this.onTab(guard, reverse)));
}
setGuardIndices(index) {
const tabindex = index;
setAttribute2(this.topTabGuard, "tabindex", tabindex);
setAttribute2(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 = getWindow6();
return Array.from(element2.querySelectorAll(selectors)).filter((e) => {
if (isHTMLElement(e)) {
const style = myWindow.getComputedStyle(e);
return style.display !== "none" && style.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/sizeMonitor.ts
import { getDocument as getDocument2, getResizeObserver, getWindow as getWindow8 } from "ag-charts-core";
// packages/ag-charts-community/src/util/pixelRatioObserver.ts
import { getWindow as getWindow7 } from "ag-charts-core";
var PixelRatioObserver = class {
constructor(callback4) {
this.callback = callback4;
this.devicePixelRatio = getWindow7("devicePixelRatio") ?? 1;
this.devicePixelRatioMediaQuery = void 0;
this.devicePixelRatioListener = (e) => {
if (e.matches)
return;
this.devicePixelRatio = getWindow7("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 = getWindow7("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((entries7) => {
for (const {
target,
contentRect: { width, height }
} of entries7) {
const entry = this.elements.get(target);
this.checkSize(entry, target, width, height);
}
});
}
let animationFrame;
this.pixelRatioObserver = new PixelRatioObserver(() => {
clearTimeout(animationFrame);
animationFrame = setTimeout(() => this.checkPixelRatio(), 0);
});
this.documentReady = getDocument2("readyState") === "complete";
if (this.documentReady) {
this.observeWindow();
} else {
getWindow8()?.addEventListener("load", this.onLoad);
}
}
destroy() {
getWindow8()?.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, height } = entry.size;
entry.size = { width, height, pixelRatio };
entry.cb(entry.size, element2);
}
}
}
checkSize(entry, element2, width, height) {
if (!entry)
return;
if (width !== entry.size?.width || height !== entry.size?.height) {
const pixelRatio = this.pixelRatioObserver?.pixelRatio ?? 1;
entry.size = { width, height, 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/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 = `--${createId3(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) {
getWindow9().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 } = this.element;
style.width = `${optionsWidth ?? minWidth}px`;
style.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 entries2(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) {
setAttribute3(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 attachListener6(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 = getWindow9();
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 } = getWindow9();
return new BBox(0, 0, innerWidth, innerHeight);
}
getShadowDocumentRoot(current = this.container) {
const docRoot = current?.ownerDocument?.body ?? getDocument3("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(getDocument3("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, style) {
this.cursorState.set(callerId, style);
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/dom/proxyInteractionService.ts
import { CleanupRegistry as CleanupRegistry7, createElement as createElement8, setElementStyle as setElementStyle3 } from "ag-charts-core";
// packages/ag-charts-community/src/widget/boundedTextWidget.ts
import { createElement as createElement2, createSvgElement as createSvgElement11 } from "ag-charts-core";
var BoundedTextWidget = class extends Widget {
constructor() {
super(createElement2("div"));
this.textElement = createSvgElement11("text");
this.textElement.role = "presentation";
this.svgElement = createSvgElement11("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/buttonWidget.ts
import { createElement as createElement3 } from "ag-charts-core";
// packages/ag-charts-community/src/widget/abstractButtonWidget.ts
import { isButtonClickEvent, setAttribute as setAttribute4 } from "ag-charts-core";
// 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);
setAttribute4(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) {
setAttribute4(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(createElement3("button"));
}
};
// packages/ag-charts-community/src/widget/groupWidget.ts
import { createElement as createElement4, setAttribute as setAttribute5 } from "ag-charts-core";
var GroupWidget = class extends Widget {
constructor() {
super(createElement4("div"));
setAttribute5(this.elem, "role", "group");
}
destructor() {
}
};
// packages/ag-charts-community/src/widget/listWidget.ts
import { createElement as createElement6, setAttribute as setAttribute7, setElementStyle as setElementStyle2 } from "ag-charts-core";
// packages/ag-charts-community/src/widget/rovingTabContainerWidget.ts
import { PREV_NEXT_KEYS, createElement as createElement5, getAttribute as getAttribute2, hasNoModifiers, setAttribute as setAttribute6 } from "ag-charts-core";
var RovingTabContainerWidget = class extends Widget {
constructor(initialOrientation, role) {
super(createElement5("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();
};
setAttribute6(this.elem, "role", role);
this.orientation = initialOrientation;
}
get orientation() {
return getAttribute2(this.elem, "aria-orientation") ?? "both";
}
set orientation(orientation) {
setAttribute6(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 = createElement6("div");
setAttribute7(listItem, "role", "listitem");
setElementStyle2(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
import { clamp as clamp6, createElement as createElement7, formatPercent, getAttribute as getAttribute3, setAttribute as setAttribute8 } from "ag-charts-core";
var _SliderWidget = class _SliderWidget extends Widget {
constructor() {
super(createElement7("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 getAttribute3(this.elem, "aria-orientation") ?? "both";
}
set orientation(orientation) {
setAttribute8(this.elem, "aria-orientation", orientation === "both" ? void 0 : orientation);
_SliderWidget.registerDefaultPreventers(this, orientation);
}
destructor() {
}
clampValueRatio(clampMin, clampMax) {
const ratio10 = this.getValueRatio();
const clampedRatio = clamp6(clampMin, ratio10, clampMax);
if (clampedRatio !== ratio10) {
this.setValueRatio(clampedRatio);
}
return clampedRatio;
}
setValueRatio(ratio10, opts) {
const { divider } = this.step;
const value = Math.round(ratio10 * 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
import { setAttribute as setAttribute9 } from "ag-charts-core";
var SwitchWidget = class extends ButtonWidget {
constructor() {
super();
setAttribute9(this.elem, "role", "switch");
this.setChecked(false);
}
setChecked(checked) {
setAttribute9(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(createElement8("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 CleanupRegistry7();
}
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();
setElementStyle3(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-community/src/locale/defaultMessageFormatter.ts
import { Logger as Logger10 } from "ag-charts-core";
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 formatter = format == null ? null : formatters[format];
if (format != null && formatter == null) {
Logger10.warnOnce(`Format style [${format}] is not supported`);
}
if (formatter != null) {
return formatter.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/scene.ts
import { CleanupRegistry as CleanupRegistry8, Debug as Debug6, EventEmitter as EventEmitter2, Logger as Logger11, createId as createId4, downloadUrl } from "ag-charts-core";
// packages/ag-charts-community/src/scene/canvas/hdpiCanvas.ts
import { ObserveChanges, createElement as createElement9, getWindow as getWindow10 } from "ag-charts-core";
var HdpiCanvas = class {
constructor(options) {
this.enabled = true;
this.width = 600;
this.height = 300;
const { width, height, canvasElement, willReadFrequently = false } = options;
this.pixelRatio = options.pixelRatio ?? getWindow10("devicePixelRatio") ?? 1;
this.element = canvasElement ?? createElement9("canvas");
this.element.style.display = "block";
this.element.style.width = (width ?? this.width) + "px";
this.element.style.height = (height ?? this.height) + "px";
this.element.width = Math.round((width ?? this.width) * this.pixelRatio);
this.element.height = Math.round((height ?? this.height) * this.pixelRatio);
this.context = this.element.getContext("2d", { willReadFrequently });
this.onEnabledChange();
this.resize(width ?? 0, height ?? 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(width, height, pixelRatio) {
if (!(width > 0 && height > 0))
return;
const { element: element2, context } = this;
element2.width = Math.round(width * pixelRatio);
element2.height = Math.round(height * pixelRatio);
context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
element2.style.width = width + "px";
element2.style.height = height + "px";
this.width = width;
this.height = height;
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
import { EventEmitter, getImage } from "ag-charts-core";
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
import { Debug as Debug5 } from "ag-charts-core";
var LayersManager = class {
constructor(canvas) {
this.canvas = canvas;
this.debug = Debug5.create(true, "scene");
this.layersMap = /* @__PURE__ */ new Map();
this.nextLayerId = 0;
}
get size() {
return this.layersMap.size;
}
resize(width, height, pixelRatio) {
this.canvas.resize(width, height, pixelRatio);
for (const { canvas } of this.layersMap.values()) {
canvas.resize(width, height, pixelRatio);
}
}
addLayer(opts) {
const { width, height, pixelRatio } = this.canvas;
const { name } = opts;
const canvas = new HdpiOffscreenCanvas({ width, height, 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 EventEmitter2 {
constructor(canvasOptions) {
super();
this.debug = Debug6.create(true, "scene" /* SCENE */);
this.id = createId4(this);
this.imageLoader = new ImageLoader();
this.root = null;
this.pendingSize = null;
this.isDirty = false;
this.cleanup = new CleanupRegistry8();
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 }) => {
Logger11.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() {
Debug6.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(width, height, pixelRatio) {
width = Math.round(width);
height = Math.round(height);
pixelRatio ?? (pixelRatio = this.pixelRatio);
if (width > 0 && height > 0 && (width !== this.width || height !== this.height || pixelRatio !== this.pixelRatio)) {
this.pendingSize = [width, height, 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, height, pixelRatio: devicePixelRatio } = this;
if (!ctx) {
return;
}
const statsEnabled = Debug6.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,
height,
devicePixelRatio,
debugNodes: {}
};
if (Debug6.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 && Debug6.check("scene:dirtyTree" /* SCENE_DIRTY_TREE */)) {
const { dirtyTree, paths } = buildDirtyTree(root);
Debug6.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, height } = this;
if (root == null)
return;
return Node.toSVG(root, width, height);
}
/** 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
import { deepClone, isArray as isArray4, mergeDefaults } from "ag-charts-core";
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 || isArray4(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
import { ZIndexMap as ZIndexMap2 } from "ag-charts-core";
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: ZIndexMap2.AXIS_GRID });
this.axisGroup = new Group({ name: "Axes", zIndex: ZIndexMap2.AXIS });
this.axisLabelGroup = new Group({ name: "Axes-Labels", zIndex: ZIndexMap2.SERIES_LABEL });
this.axisCrosslineRangeGroup = new Group({
name: "Axes-Crosslines-Range",
zIndex: ZIndexMap2.SERIES_CROSSLINE_RANGE
});
this.axisCrosslineLineGroup = new Group({
name: "Axes-Crosslines-Line",
zIndex: ZIndexMap2.SERIES_CROSSLINE_LINE
});
this.axisCrosslineLabelGroup = new Group({
name: "Axes-Crosslines-Label",
zIndex: ZIndexMap2.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
import { ActionOnSet, Debug as Debug7, Logger as Logger12, throttle } from "ag-charts-core";
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 = Debug7.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) => Logger12.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 start = 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() - start}ms | ${id}`);
} catch (error) {
this.debug(`DataService - request failed | ${id}`);
Logger12.errorOnce(`DataService - request failed | [${error}]`);
}
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
import { ChartUpdateType, cachedTextMeasurer as cachedTextMeasurer3, getDocument as getDocument4, getResizeObserver as getResizeObserver2 } from "ag-charts-core";
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 = getResizeObserver2();
if (ResizeObserverCtor === void 0)
return;
const doc = getDocument4();
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((entries7) => {
const width = entries7?.at(0)?.contentBoxSize.at(0)?.inlineSize;
if (width != null && width > 0) {
cachedTextMeasurer3.clear();
this.updateService.update(ChartUpdateType.PERFORM_LAYOUT);
}
});
fontCheckObserver.observe(fontCheckElement);
this.observers.push(fontCheckObserver);
}
};
// packages/ag-charts-community/src/chart/formatter/formatManager.ts
import {
Logger as Logger14,
buildDateFormatter,
createNumberFormatter,
formatValue,
isPlainObject,
parseNumberFormat,
simpleMemorize2,
toTextString as toTextString4
} from "ag-charts-core";
// packages/ag-charts-community/src/util/listeners.ts
import { Logger as Logger13 } from "ag-charts-core";
var Listeners = class {
constructor() {
this.registeredListeners = /* @__PURE__ */ new Map();
}
addListener(eventType, handler) {
const record = { symbol: Symbol(eventType), handler };
if (this.registeredListeners.has(eventType)) {
this.registeredListeners.get(eventType).push(record);
} else {
this.registeredListeners.set(eventType, [record]);
}
return () => this.removeListener(record.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) {
Logger13.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, style, truncateDate) => {
const mergedFormatter = _FormatManager.mergeSpecifiers(propertyFormatter, specifier) ?? defaultTimeFormats;
return _FormatManager.getFormatter("date", mergedFormatter, unit, style, { 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, style = "long", { truncateDate } = {}) {
if (isPlainObject(specifier)) {
if (type !== "date") {
Logger14.warn("Date formatter configuration is not supported for non-date types.");
return;
}
unit ?? (unit = "millisecond");
const fullFormat = style === "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(formatter) {
if (this.formatter !== formatter) {
this.formatter = formatter;
this.formats.clear();
this.dateFormatter.reset();
this.dispatch("format-changed");
}
}
format(formatInContext, params, { specifier, truncateDate, allowNull } = {}) {
if (params.value == null && !allowNull)
return;
const { formatter } = this;
if (formatter == null)
return;
if (typeof formatter === "function") {
const value = formatInContext(formatter, params);
return value == null ? void 0 : String(value);
}
const propertyFormatter = formatter[params.property];
if (propertyFormatter == null)
return;
if (typeof propertyFormatter === "function") {
const value = formatInContext(propertyFormatter, params);
return value == null ? value : toTextString4(value);
} else if (params.type === "date") {
const { unit, style } = params;
const dateFormatter = this.dateFormatter(propertyFormatter, specifier, unit, style, 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 } = this;
const propertyFormatter = typeof formatter === "function" ? void 0 : formatter?.[params.property];
switch (params.type) {
case "date": {
const { unit, style } = params;
const propertySpecifier = propertyFormatter != null && typeof propertyFormatter !== "function" ? propertyFormatter : void 0;
const dateFormatter = this.dateFormatter(propertySpecifier, specifier, unit, style, 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/interaction/activeManager.ts
import { objectsEqual as objectsEqual4, validate } from "ag-charts-core";
// packages/ag-charts-community/src/chart/chartOptionsDefs.ts
import {
array,
arrayOfDefs,
boolean,
commonChartOptionsDefs,
defined,
geoJson,
htmlElement,
object,
or,
positiveNumber,
required,
strictUnion,
string,
undocumented,
union
} from "ag-charts-core";
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/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/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 || !objectsEqual4(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/animationManager.ts
import { Debug as Debug10, EventEmitter as EventEmitter3, Logger as Logger20, getWindow as getWindow11 } from "ag-charts-core";
// packages/ag-charts-community/src/motion/animation.ts
import { clamp as clamp7, isPlainObject as isPlainObject2, linear, objectsEqualWith } from "ag-charts-core";
// packages/ag-charts-community/src/util/interpolate.ts
import { Color as Color2 } from "ag-charts-core";
function interpolateNumber(a, b) {
return (d) => Number(a) * (1 - d) + Number(b) * d;
}
function interpolateColor(a, b) {
if (typeof a === "string") {
try {
a = Color2.fromString(a);
} catch {
a = Color2.fromArray([0, 0, 0]);
}
}
if (typeof b === "string") {
try {
b = Color2.fromString(b);
} catch {
b = Color2.fromArray([0, 0, 0]);
}
}
return (d) => Color2.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(array4) {
return array4.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 (isPlainObject2(a) && isPlainObject2(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(clamp7(0, (this.elapsed - this.delay) / this.duration, 1));
}
createInterpolator(from, to) {
if (typeof to !== "object" || isInterpolating(to)) {
return this.interpolateValue(from, to);
} else if (Array.isArray(to)) {
const interpolatorValues = [];
for (let i = 0; i < to.length; i++) {
const interpolator = this.createInterpolator(from[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(from[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/chart/data/processors.ts
import {
clamp as clamp8,
isContinuous,
isFiniteNumber as isFiniteNumber2,
isNegative as isNegative2,
memo,
transformIntegratedCategoryValue
} from "ag-charts-core";
// packages/ag-charts-community/src/chart/data/aggregateFunctions.ts
import { isFiniteNumber } from "ag-charts-core";
// 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 range(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/dataModel.ts
import { Debug as Debug8, Logger as Logger18, first as first7 } from "ag-charts-core";
// packages/ag-charts-community/src/chart/data/data-model/aggregation/aggregator.ts
import { first } from "ag-charts-core";
// packages/ag-charts-community/src/chart/data/data-model/utils/helpers.ts
import { isObject as isObject2 } from "ag-charts-core";
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 isObject2(key) ? JSON.stringify(key) : String(key);
}
function toKeyString(keys) {
return keys.map(keyToString).join("-");
}
function fixNumericExtent(extent6) {
const numberExtent = extent6?.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(length, value) {
const out = [];
for (let i = 0; i < length; 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/data-model/domain/domainManager.ts
import { first as first2, iterate } from "ag-charts-core";
// 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
import { Logger as Logger15, isNegative } from "ag-charts-core";
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") {
Logger15.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) => [first2(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
import { Logger as Logger16, first as first3, iterate as iterate2 } from "ag-charts-core";
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 = first3(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 iterate2(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}`;
Logger16.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
import { first as first4 } from "ag-charts-core";
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[first4(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/data-model/incremental/incrementalProcessor.ts
import { first as first6 } from "ag-charts-core";
// 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(callback4) {
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;
callback4(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(array4, 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 = array4.splice(op.index, op.deleteCount, ...insertElements);
if (onRemove && removed.length > 0) {
onRemove(removed, op);
}
}
if (array4.length !== finalLength) {
array4.length = finalLength;
}
}
};
// packages/ag-charts-community/src/chart/data/data-model/reducers/reducerManager.ts
import { first as first5 } from "ag-charts-core";
// 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] : first5(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 = first6(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 start = 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 end2 = performance.now();
processedData.time = end2 - start;
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 array4 = getArray(defIndex, scope);
if (!array4)
continue;
const insertionCache = insertionCaches.get(scope);
this.applyChangeDescWithCache(
changeDesc,
array4,
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) => [first6(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 array4 = invalidityMap.get(scope);
if (!array4) {
const insertionCache2 = insertionCaches.get(scope);
const hasAnyInvalid = insertionCache2 && Array.from(insertionCache2.values()).some(extractValue);
if (hasAnyInvalid) {
array4 = createArray(changeDesc.indexMap.originalLength, false);
invalidityMap.set(scope, array4);
} else {
continue;
}
}
if (processedArrays.has(array4))
continue;
processedArrays.add(array4);
const insertionCache = insertionCaches.get(scope);
this.applyChangeDescWithCache(
changeDesc,
array4,
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 = first6(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 = first6(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 = first6(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 start = size - 1;
const childStart = start + size << 1;
let nodeOffset = start << 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, start, end2, bufferIndex, currentStart, step, into) {
const currentEnd = currentStart + step - 1;
if (currentEnd < start || currentStart >= end2)
return into;
if (currentStart >= start && currentEnd < end2) {
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, start, end2, Math.trunc(bufferIndex + 1), currentStart, step, into);
this.computeRangeInto(buffer, start, end2, Math.trunc(bufferIndex + 2), currentStart + step, step, into);
}
return into;
}
rangeBetween(start, end2, into) {
const result = into ?? [0, 0];
if (start > end2) {
result[0] = Number.NaN;
result[1] = Number.NaN;
return result;
}
const { maxLevelSize, buffer } = this;
result[0] = Infinity;
result[1] = -Infinity;
this.computeRangeInto(buffer, start, end2, 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
import { Logger as Logger17, iterate as iterate3 } from "ag-charts-core";
var ScopeCacheManager = class {
constructor(ctx) {
this.ctx = ctx;
}
processScopeCache() {
this.ctx.scopeCache.clear();
for (const def of iterate3(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) {
Logger17.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 = Debug8.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 start = 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 end2 = performance.now();
processedData.time = end2 - start;
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] : first7(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 = first7(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) {
Logger18.log(`DataModel.processData() - ${name}`);
Logger18.table(data);
}
};
Logger18.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) {
Logger18.log("DataModel.processData() - Optimization Summary");
const opt = processedData.optimizations;
if (opt.performance) {
Logger18.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})` : "";
Logger18.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) {
Logger18.log(` Domain Banding: \u2713 (${totalApplied}/${totalDefs} definitions)`);
for (const def of [...keyStats, ...valueStats]) {
if (def.stats) {
const pct2 = (def.stats.scanRatio * 100).toFixed(1);
Logger18.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(", ");
Logger18.log(` Domain Banding: \u2717 (${uniqueReasons})`);
}
}
if (opt.sharedDatumIndices) {
const symbol = opt.sharedDatumIndices.applied ? "\u2713" : "\u2717";
const ratio10 = `${opt.sharedDatumIndices.sharedGroupCount}/${opt.sharedDatumIndices.totalGroupCount}`;
Logger18.log(` Shared DatumIndices: ${symbol} (${ratio10} groups)`);
}
if (opt.batchMerging) {
const pct2 = (opt.batchMerging.mergeRatio * 100).toFixed(0);
const reduction = `${opt.batchMerging.originalBatchCount} \u2192 ${opt.batchMerging.mergedBatchCount}`;
Logger18.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 isFiniteNumber2(datum) ? clamp8(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 ? [] : [range(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 normalise = (val, extent6) => {
if (extent6 === 0)
return 0;
const result = (val ?? 0) * normaliseTo / extent6;
if (result >= 0) {
return Math.min(normaliseTo, result);
}
return Math.max(-normaliseTo, result);
};
return () => () => (columns, valueIndexes, dataGroup, groupIndex) => {
const extent6 = 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] = normalise(value, extent6);
}
}
};
}
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 normalise = function normaliseFn(val, start, span) {
const result = normaliseTo[0] + (val - start) / 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 [start, end2] = pData.domain.values[pIdx];
if (rangeMin != null)
start = rangeMin;
if (rangeMax != null)
end2 = rangeMax;
const span = end2 - start;
pData.domain.values[pIdx] = [normaliseTo[0], normaliseTo[1]];
const column = pData.columns[pIdx];
for (let datumIndex = 0; datumIndex < column.length; datumIndex += 1) {
column[datumIndex] = normalise(column[datumIndex], start, 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 (!isFiniteNumber2(currentVal))
continue;
const useNegative = separateNegative && isNegative2(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 length = Math.max(previousData.input.count, processedData.input.count);
const allowNull = processedData.defs.keys.some((def) => def.allowNullKey === true);
for (let i = 0; i < length; 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/interaction/animationBatch.ts
import { Debug as Debug9, Logger as Logger19 } from "ag-charts-core";
var AnimationBatch = class {
constructor(maxAnimationTime) {
this.maxAnimationTime = maxAnimationTime;
this.debug = Debug9.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 (error) {
Logger19.error("Error during animation stop", error);
}
}
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 = Debug10.create(true, "animation");
this.events = new EventEmitter3();
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 (error) {
this.failsafeOnError(error);
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 (error) {
this.failsafeOnError(error);
}
this.requestAnimation();
}
stop() {
this.isPlaying = false;
this.cancelAnimation();
this.debug("AnimationManager.stop()");
this.batch.stop();
}
stopByAnimationId(id) {
try {
this.batch.stopByAnimationId(id);
} catch (error) {
this.failsafeOnError(error);
}
}
stopByAnimationGroupId(id) {
try {
this.batch.stopByAnimationGroupId(id);
} catch (error) {
this.failsafeOnError(error);
}
}
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 = getWindow11().requestAnimationFrame((t) => {
cb(t).catch((e) => Logger20.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 (error) {
this.failsafeOnError(error);
}
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(error, cancelAnimation = true) {
Logger20.error("Error during animation, skipping animations", error);
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
import { objectsEqual as objectsEqual5 } from "ag-charts-core";
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 && objectsEqual5(a.datumIndex, b.datumIndex);
}
};
// milliseconds
_HighlightManager.HIGHLIGHT_CHANGE_EVENT = "highlight:change";
var HighlightManager = _HighlightManager;
// packages/ag-charts-community/src/chart/interaction/tooltipManager.ts
import { CleanupRegistry as CleanupRegistry9, objectsEqual as objectsEqual6 } from "ag-charts-core";
// packages/ag-charts-community/src/chart/series/util.ts
import { Color as Color3, findMaxIndex, findMinIndex, isString as isString3 } from "ag-charts-core";
// packages/ag-charts-community/src/chart/series/seriesProperties.ts
import { BaseProperties as BaseProperties3, PropertiesArray, Property as Property5, mergeDefaults as mergeDefaults2 } from "ag-charts-core";
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 BaseProperties3 {
};
__decorateClass([
Property5
], SeriesItemHighlightStyle.prototype, "fill", 2);
__decorateClass([
Property5
], SeriesItemHighlightStyle.prototype, "fillOpacity", 2);
__decorateClass([
Property5
], SeriesItemHighlightStyle.prototype, "stroke", 2);
__decorateClass([
Property5
], SeriesItemHighlightStyle.prototype, "strokeWidth", 2);
__decorateClass([
Property5
], SeriesItemHighlightStyle.prototype, "strokeOpacity", 2);
__decorateClass([
Property5
], SeriesItemHighlightStyle.prototype, "lineDash", 2);
__decorateClass([
Property5
], SeriesItemHighlightStyle.prototype, "lineDashOffset", 2);
var HighlightProperties = class extends BaseProperties3 {
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 mergeDefaults2(...keys.map((key) => this[key]));
}
};
__decorateClass([
Property5
], HighlightProperties.prototype, "enabled", 2);
__decorateClass([
Property5
], HighlightProperties.prototype, "range", 2);
__decorateClass([
Property5
], HighlightProperties.prototype, "bringToFront", 2);
__decorateClass([
Property5
], HighlightProperties.prototype, "highlightedItem", 2);
__decorateClass([
Property5
], HighlightProperties.prototype, "unhighlightedItem", 2);
__decorateClass([
Property5
], HighlightProperties.prototype, "highlightedSeries", 2);
__decorateClass([
Property5
], HighlightProperties.prototype, "unhighlightedSeries", 2);
var SegmentOptions = class extends BaseProperties3 {
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([
Property5
], SegmentOptions.prototype, "start", 2);
__decorateClass([
Property5
], SegmentOptions.prototype, "stop", 2);
__decorateClass([
Property5
], SegmentOptions.prototype, "fill", 2);
__decorateClass([
Property5
], SegmentOptions.prototype, "fillOpacity", 2);
__decorateClass([
Property5
], SegmentOptions.prototype, "stroke", 2);
__decorateClass([
Property5
], SegmentOptions.prototype, "strokeWidth", 2);
__decorateClass([
Property5
], SegmentOptions.prototype, "strokeOpacity", 2);
__decorateClass([
Property5
], SegmentOptions.prototype, "lineDash", 2);
__decorateClass([
Property5
], SegmentOptions.prototype, "lineDashOffset", 2);
var Segmentation = class {
constructor() {
this.key = "x";
this.segments = new PropertiesArray(SegmentOptions);
}
};
__decorateClass([
Property5
], Segmentation.prototype, "enabled", 2);
__decorateClass([
Property5
], Segmentation.prototype, "key", 2);
__decorateClass([
Property5
], Segmentation.prototype, "segments", 2);
var FillGradientDefaults = class extends BaseProperties3 {
constructor() {
super(...arguments);
this.type = "gradient";
this.colorStops = [];
this.bounds = "item";
this.gradient = "linear";
this.rotation = 0;
this.reverse = false;
this.colorSpace = "rgb";
}
};
__decorateClass([
Property5
], FillGradientDefaults.prototype, "type", 2);
__decorateClass([
Property5
], FillGradientDefaults.prototype, "colorStops", 2);
__decorateClass([
Property5
], FillGradientDefaults.prototype, "bounds", 2);
__decorateClass([
Property5
], FillGradientDefaults.prototype, "gradient", 2);
__decorateClass([
Property5
], FillGradientDefaults.prototype, "rotation", 2);
__decorateClass([
Property5
], FillGradientDefaults.prototype, "reverse", 2);
__decorateClass([
Property5
], FillGradientDefaults.prototype, "colorSpace", 2);
var FillPatternDefaults = class extends BaseProperties3 {
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([
Property5
], FillPatternDefaults.prototype, "type", 2);
__decorateClass([
Property5
], FillPatternDefaults.prototype, "colorStops", 2);
__decorateClass([
Property5
], FillPatternDefaults.prototype, "bounds", 2);
__decorateClass([
Property5
], FillPatternDefaults.prototype, "gradient", 2);
__decorateClass([
Property5
], FillPatternDefaults.prototype, "rotation", 2);
__decorateClass([
Property5
], FillPatternDefaults.prototype, "scale", 2);
__decorateClass([
Property5
], FillPatternDefaults.prototype, "reverse", 2);
__decorateClass([
Property5
], FillPatternDefaults.prototype, "path", 2);
__decorateClass([
Property5
], FillPatternDefaults.prototype, "pattern", 2);
__decorateClass([
Property5
], FillPatternDefaults.prototype, "width", 2);
__decorateClass([
Property5
], FillPatternDefaults.prototype, "height", 2);
__decorateClass([
Property5
], FillPatternDefaults.prototype, "padding", 2);
__decorateClass([
Property5
], FillPatternDefaults.prototype, "fill", 2);
__decorateClass([
Property5
], FillPatternDefaults.prototype, "fillOpacity", 2);
__decorateClass([
Property5
], FillPatternDefaults.prototype, "backgroundFill", 2);
__decorateClass([
Property5
], FillPatternDefaults.prototype, "backgroundFillOpacity", 2);
__decorateClass([
Property5
], FillPatternDefaults.prototype, "stroke", 2);
__decorateClass([
Property5
], FillPatternDefaults.prototype, "strokeOpacity", 2);
__decorateClass([
Property5
], FillPatternDefaults.prototype, "strokeWidth", 2);
var FillImageDefaults = class extends BaseProperties3 {
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([
Property5
], FillImageDefaults.prototype, "type", 2);
__decorateClass([
Property5
], FillImageDefaults.prototype, "url", 2);
__decorateClass([
Property5
], FillImageDefaults.prototype, "rotation", 2);
__decorateClass([
Property5
], FillImageDefaults.prototype, "scale", 2);
__decorateClass([
Property5
], FillImageDefaults.prototype, "backgroundFill", 2);
__decorateClass([
Property5
], FillImageDefaults.prototype, "backgroundFillOpacity", 2);
__decorateClass([
Property5
], FillImageDefaults.prototype, "repeat", 2);
__decorateClass([
Property5
], FillImageDefaults.prototype, "fit", 2);
var SeriesProperties = class extends BaseProperties3 {
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([
Property5
], SeriesProperties.prototype, "id", 2);
__decorateClass([
Property5
], SeriesProperties.prototype, "visible", 2);
__decorateClass([
Property5
], SeriesProperties.prototype, "focusPriority", 2);
__decorateClass([
Property5
], SeriesProperties.prototype, "showInLegend", 2);
__decorateClass([
Property5
], SeriesProperties.prototype, "cursor", 2);
__decorateClass([
Property5
], SeriesProperties.prototype, "nodeClickRange", 2);
__decorateClass([
Property5
], 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 [first8, last] = datumBoundaryPoints(xValue, xDomain);
return {
xKey,
yKey,
xValue,
yValue,
first: first8,
last,
min,
max
};
}
function visibleRangeIndices(sortOrder, length, [range0, range1], xRange) {
let xMinIndex = findMinIndex(0, length - 1, (i) => {
const index = sortOrder === 1 ? i : length - i;
const x1 = xRange(index)?.[1] ?? Number.NaN;
return !Number.isFinite(x1) || x1 >= range0;
}) ?? 0;
let xMaxIndex = findMaxIndex(0, length - 1, (i) => {
const index = sortOrder === 1 ? i : length - i;
const x0 = xRange(index)?.[0] ?? Number.NaN;
return !Number.isFinite(x0) || x0 <= range1;
}) ?? length - 1;
if (sortOrder === -1) {
[xMinIndex, xMaxIndex] = [length - xMaxIndex, length - xMinIndex];
}
xMinIndex = Math.max(xMinIndex, 0);
xMaxIndex = Math.min(xMaxIndex + 1, length);
return [xMinIndex, xMaxIndex];
}
function getDatumRefPoint(series, datum, movedBounds) {
if (movedBounds) {
const { x, y, width, height } = movedBounds;
return { canvasX: x + width / 2, canvasY: y + height / 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, start, countUntil, iteratee) {
let i = -1;
let count = 0;
let shift = 0;
let reachedAnEnd = false;
while (count < countUntil && i <= max - min) {
i += 1;
const index = start + 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(style) {
return (style?.opacity ?? 1) < 1 || (style?.fillOpacity ?? 1) < 1 || (style?.strokeOpacity ?? 1) < 1;
}
var opaqueMarkerFillCache = /* @__PURE__ */ new Map();
function isOpaqueMarkerFillStyle(style) {
if (style == null)
return false;
const fill = style.fill;
if (!isString3(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 = Color3.fromString(fillString).a === 1;
} catch {
cached = false;
}
opaqueMarkerFillCache.set(fillString, cached);
}
return cached;
}
function resolveMarkerDrawingMode(baseDrawingMode, style) {
if (baseDrawingMode !== "cutout")
return baseDrawingMode;
return isOpaqueMarkerFillStyle(style) ? "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/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 CleanupRegistry9();
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 (objectsEqual6(this.appliedState?.content, state?.content) && objectsEqual6(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
import { CleanupRegistry as CleanupRegistry10, EventEmitter as EventEmitter4 } from "ag-charts-core";
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 distanceSquared = dx * dx + dy * dy;
const thresholdSquared = DRAG_THRESHOLD_PX * DRAG_THRESHOLD_PX;
return distanceSquared >= thresholdSquared;
}
function checkDoubleTapDistance(t1, t2) {
const dx = t1.clientX - t2.clientX;
const dy = t1.clientY - t2.clientY;
const distanceSquared = dx * dx + dy * dy;
const thresholdSquared = DOUBLE_TAP_THRESHOLD_PX * DOUBLE_TAP_THRESHOLD_PX;
return distanceSquared < thresholdSquared;
}
var DragInterpreter = class {
constructor(widget) {
this.cleanup = new CleanupRegistry10();
this.events = new EventEmitter4();
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/chart/interaction/zoomManager.ts
import {
ChartAxisDirection as ChartAxisDirection2,
Logger as Logger22,
ScaleAlignment as ScaleAlignment3,
attachDescription,
clamp as clamp11,
deepClone as deepClone2,
deepFreeze,
defined as defined2,
definedZoomState,
isFiniteNumber as isFiniteNumber3,
isObject as isObject3,
strictObjectKeys,
validate as validate2
} from "ag-charts-core";
// packages/ag-charts-community/src/scale/continuousScale.ts
import { findMinMax } from "ag-charts-core";
var _ContinuousScale = class _ContinuousScale extends AbstractScale {
constructor(domain = [], range4 = []) {
super();
this.range = range4;
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: range4 } = this;
const clamp25 = 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 (clamp25) {
const [start, stop] = findMinMax([d0, d1]);
if (x < start) {
return range4[0];
} else if (x > stop) {
return range4[1];
}
}
if (d0 === d1) {
return (range4[0] + range4[1]) / 2;
} else if (x === d0) {
return range4[0];
} else if (x === d1) {
return range4[1];
}
const r0 = range4[0];
return r0 + (x - d0) / (d1 - d0) * (range4[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: range4 } = this;
const [r0, r1] = range4;
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
import { ScaleAlignment, findMaxIndex as findMaxIndex2, findMinIndex as findMinIndex2 } from "ag-charts-core";
// packages/ag-charts-community/src/scale/bandScale.ts
import { Logger as Logger21, clamp as clamp9 } from "ag-charts-core";
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 = clamp9(0, value, 1);
this._paddingInner = value;
this._paddingOuter = value;
}
get padding() {
return this._paddingInner;
}
set paddingInner(value) {
this.invalid = true;
this._paddingInner = clamp9(0, value, 1);
}
get paddingInner() {
return this._paddingInner;
}
set paddingOuter(value) {
this.invalid = true;
this._paddingOuter = clamp9(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) {
Logger21.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 distance = Math.abs(p - position);
if (distance === 0)
return mid;
if (distance < closestDistance) {
closestDistance = distance;
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 round = this.round && Math.floor(rawStep) > 0;
const step = round ? Math.floor(rawStep) : rawStep;
let inset = r0 + (rangeDistance - step * (bandCount - paddingInner)) / 2;
let bandwidth = step * (1 - paddingInner);
if (round) {
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: range4 } = this;
const min = Math.min(range4[0], range4[1]);
const max = Math.max(range4[0], range4[1]);
return clamp9(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/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: range4 } = this;
if (value.valueOf() < numericBands[0])
return range4[0];
if (value.valueOf() > numericBands.at(-1))
return range4[1];
}
const alignment = options?.alignment ?? ScaleAlignment.Leading;
if (alignment !== ScaleAlignment.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 ratio10 = (v - v0) / (v1 - v0);
const r = ratio10 * (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 = findMinIndex2(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 = ScaleAlignment.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 === ScaleAlignment.Trailing) {
return findMinIndex2(0, n - 1, (index) => numericBands[index] >= target);
}
return findMaxIndex2(0, n - 1, (index) => numericBands[index] <= target);
}
};
// packages/ag-charts-community/src/util/panToBBox.ts
import { Vec4, clamp as clamp10 } from "ag-charts-core";
function normalize(screenMin, min, screenMax, max, target) {
return min + (max - min) * ((target - screenMin) / (screenMax - screenMin));
}
function unnormalize(screenMin, min, screenMax, max, ratio10) {
return screenMin + (ratio10 - min) * ((screenMax - screenMin) / (max - min));
}
function calcWorldAxis(viewportMin, viewportMax, ratio10) {
return [
unnormalize(viewportMin, ratio10.min, viewportMax, ratio10.max, 0),
unnormalize(viewportMin, ratio10.min, viewportMax, ratio10.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 clamp10(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 = Vec4.from(targetBBox);
const viewport = Vec4.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 = clamp10(0, result.x.min, 1 - diffX);
result.x.max = result.x.min + diffX;
result.y.min = clamp10(0, result.y.min, 1 - diffY);
result.y.max = result.y.min + diffY;
return result;
}
// packages/ag-charts-community/src/chart/rangeAlignment.ts
import { ScaleAlignment as ScaleAlignment2 } from "ag-charts-core";
function rangeAlignment(start, end2) {
const startValue = start?.valueOf();
const endValue = end2?.valueOf();
if (typeof startValue !== "number" || typeof endValue !== "number")
return [void 0, void 0];
return startValue < endValue ? [ScaleAlignment2.Leading, ScaleAlignment2.Trailing] : [ScaleAlignment2.Trailing, ScaleAlignment2.Leading];
}
// packages/ag-charts-community/src/chart/interaction/zoomManager.ts
var rangeValidator = (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) {
Logger22.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(ChartAxisDirection2.X);
const yId = this.getPrimaryAxisId(ChartAxisDirection2.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 === ChartAxisDirection2.X) {
x ?? (x = { min, max });
} else if (direction === ChartAxisDirection2.Y) {
y ?? (y = { min, max });
}
}
if (x || y) {
return { x, y };
}
}
createMemento() {
return this.getMementoRanges();
}
guardMemento(blob, messages) {
if (blob == null)
return true;
if (!isObject3(blob))
return false;
const primaryX = this.getPrimaryAxis(ChartAxisDirection2.X);
const primaryY = this.getPrimaryAxis(ChartAxisDirection2.Y);
const zoomMementoDefs = {
rangeX: { start: rangeValidator(primaryX), end: defined2 },
rangeY: { start: rangeValidator(primaryY), end: defined2 },
ratioX: { start: defined2, end: defined2 },
ratioY: { start: defined2, end: defined2 },
autoScaledAxes: defined2
};
const { invalid } = validate2(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(ChartAxisDirection2.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(deepClone2(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 = deepClone2(this.state);
const newState = deepClone2(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) {
Logger22.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, extent6) {
return this.extendWith(sourcing, direction, (end2) => Number(end2) - extent6);
}
extendWith({ source, sourceDetail }, direction, fn) {
const axis = this.getPrimaryAxis(direction);
if (!axis)
return;
const extents = this.getDomainExtents(axis);
if (!extents)
return;
const [, end2] = extents;
const start = fn(end2);
const ratio10 = this.rangeToRatioAxis(axis, { start });
if (!ratio10)
return;
this.updateChanges({ source, sourceDetail, changes: { [direction]: ratio10 }, isReset: false });
}
updateWith({ source, sourceDetail }, direction, fn) {
const axis = this.getPrimaryAxis(direction);
if (!axis)
return;
const extents = this.getDomainExtents(axis);
if (!extents)
return;
let [start, end2] = extents;
[start, end2] = fn(start, end2);
const ratio10 = this.rangeToRatioAxis(axis, { start, end: end2 });
if (!ratio10)
return;
this.updateChanges({ source, sourceDetail, changes: { [direction]: ratio10 }, 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(ChartAxisDirection2.X);
const yAxis = this.getPrimaryAxis(ChartAxisDirection2.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(ChartAxisDirection2.X, zoom.x),
rangeY: this.getRangeDirection(ChartAxisDirection2.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 !== ChartAxisDirection2.X && requiredRangeDirection !== ChartAxisDirection2.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 === ChartAxisDirection2.X) {
min = clamp11(0, 1 - requiredZoom, crossAxisZoom.min);
max = clamp11(0, min + requiredZoom, 1);
} else {
max = Math.min(1, crossAxisZoom.max);
min = max - requiredZoom;
if (min < 0) {
max -= min;
min = 0;
}
min = clamp11(0, min, 1);
max = clamp11(0, max, 1);
}
this.lastRestoredRequiredRange = requiredRangeRatio;
this.lastRestoredRequiredRangeDirection = requiredRangeDirection;
const zoom = { [requiredRangeDirection]: { min, max } };
const changes = this.toCoreZoomState(zoom);
this.lastRestoredState = deepFreeze(deepClone2(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 = deepClone2(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, ratio10) {
return this.getRangeAxis(this.findAxis(axisId), ratio10);
}
getRangeDirection(direction, ratio10) {
return this.getRangeAxis(this.getPrimaryAxis(direction), ratio10);
}
getRangeAxis(axis, ratio10) {
if (!axis || !ratio10 || !ContinuousScale.is(axis.scale) && !DiscreteTimeScale.is(axis.scale))
return;
const extents = this.getDomainPixelExtents(axis);
if (!extents)
return;
const [d0, d1] = extents;
let start;
let end2;
if (d0 <= d1) {
start = axis.scale.invert(0);
end2 = axis.scale.invert(d0 + (d1 - d0) * ratio10.max);
} else {
start = axis.scale.invert(d0 - (d0 - d1) * ratio10.min);
end2 = axis.scale.invert(0);
}
return { start, end: end2 };
}
rangeToRatio(axisId, range4) {
return this.rangeToRatioAxis(this.findAxis(axisId), range4);
}
rangeToRatioDirection(direction, range4) {
return this.rangeToRatioAxis(this.getPrimaryAxis(direction), range4);
}
rangeToRatioAxis(axis, range4) {
if (!axis)
return;
const extents = this.getDomainPixelExtents(axis);
if (!extents)
return;
const [d0, d1] = extents;
const { scale: scale2 } = axis;
const { start, end: end2 } = range4;
const [startAlignment = ScaleAlignment3.Leading, endAlignment = ScaleAlignment3.Trailing] = rangeAlignment(
start,
end2
);
const r0 = start == null ? d0 : scale2.convert(start, { alignment: startAlignment });
const r1 = end2 == null ? d1 : scale2.convert(end2, { alignment: endAlignment }) + (scale2.bandwidth ?? 0);
if (!isFiniteNumber3(r0) || !isFiniteNumber3(r1))
return;
const [dMin, dMax] = [Math.min(d0, d1), Math.max(d0, d1)];
if (r0 < dMin || r0 > dMax) {
Logger22.warnOnce(
`Invalid range start [${start}], expecting a value between [${scale2.invert(d0)}] and [${scale2.invert(d1)}], ignoring.`
);
return;
}
if (r1 < dMin || r1 > dMax) {
Logger22.warnOnce(
`Invalid range end [${end2}], 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 (!isFiniteNumber3(d0) || !isFiniteNumber3(d1))
return;
return [d0, d1];
}
};
// 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(width, height) {
const context = { width, height, layoutBox: new BBox(0, 0, width, height), 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, height }, options) {
this.eventsHub.emit("layout:complete", {
axes: options.axes ?? {},
chart: { width, height },
clipSeries: options.clipSeries ?? false,
series: options.series
});
}
};
// packages/ag-charts-community/src/chart/layout/seriesLabelLayoutManager.ts
import { isPointLabelDatum, placeLabels } from "ag-charts-core";
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
import { Logger as Logger23, isArray as isArray5 } from "ag-charts-core";
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 || isArray5(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) {
Logger23.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
import "ag-charts-core";
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 width of stacks.values()) {
maxStackWidth = Math.max(maxStackWidth, width == null ? _bandwidth : width);
}
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 width of stacks.values()) {
if (width == null) {
hasUnfixed = true;
continue;
}
maxStackWidth = Math.max(maxStackWidth, width);
}
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 round = this.round && Math.floor(bandwidth) > 0;
if (round) {
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, 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 });
}
updateSeries({ internalId, seriesGrouping, visible, width, type }) {
if (!seriesGrouping)
return;
const entry = this.groups.get(type)?.get(internalId);
if (entry) {
entry.grouping = seriesGrouping;
entry.width = width;
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
import { ChartUpdateType as ChartUpdateType2, EventEmitter as EventEmitter5 } from "ag-charts-core";
var UpdateService = class {
constructor(updateCallback) {
this.updateCallback = updateCallback;
this.events = new EventEmitter5();
}
addListener(eventName, listener) {
return this.events.on(eventName, listener);
}
destroy() {
this.events.clear();
}
update(type = ChartUpdateType2.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 EventEmitter6();
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 CleanupRegistry11();
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(ChartUpdateType3.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 module of ModuleRegistry.listModulesByType(ModuleType.Plugin)) {
if (!module.chartType || module.chartType === chartType) {
module.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
import { BaseProperties as BaseProperties4, Property as Property6 } from "ag-charts-core";
var ChartHighlight = class extends BaseProperties4 {
constructor() {
super(...arguments);
this.range = "tooltip";
this.drawingMode = "cutout";
}
};
__decorateClass([
Property6
], ChartHighlight.prototype, "range", 2);
__decorateClass([
Property6
], ChartHighlight.prototype, "drawingMode", 2);
// packages/ag-charts-community/src/chart/data/caching.ts
import { arraysEqual, objectsEqual as objectsEqual7 } from "ag-charts-core";
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 (!objectsEqual7(propA, propB) || !idsMapEqual(idsMapA, idsMapB))
return false;
}
return true;
}
function optsEqual(a, b) {
const { props: propsA, ...restA } = a;
const { props: propsB, ...restB } = b;
return objectsEqual7(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
import { Debug as Debug11, Logger as Logger24, getWindow as getWindow12, jsonDiff } from "ag-charts-core";
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 = Debug11.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()) {
getWindow12().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()) {
getWindow12("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 (error) {
for (const cb of rejects) {
cb(error);
}
}
};
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 (Debug11.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) {
Logger24.log("\u26A0\uFE0F DATA-MODEL REPROCESS DIFF DETECTED \u26A0\uFE0F");
Logger24.log("Difference between incremental update and full reprocess:");
Logger24.log("");
Logger24.log("BASELINE (full reprocess):");
Logger24.log(JSON.stringify(baselineJson, null, 2));
Logger24.log("");
Logger24.log("REPROCESSED (incremental update):");
Logger24.log(JSON.stringify(reprocessedJson, null, 2));
Logger24.log("");
Logger24.log("DIFF (what changed):");
Logger24.log(JSON.stringify(diff2, null, 2));
} else {
Logger24.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 clone = { ...prop, scopes: [id], data: dataSet.data };
_DataController.createIdsMap(id, clone);
let dataId;
if (_DataController.crossScopeMergableTypes.has(clone.type)) {
dataId = -1;
} else if (dataIds.has(dataSet.data)) {
dataId = dataIds.get(dataSet.data);
} else {
dataId = nextDataId++;
dataIds.set(dataSet.data, dataId);
}
const matchKey = `${clone.type}-${dataId}-${clone.groupId}`;
const matches = optsByTypeAndDataId.get(matchKey);
const match = matches?.find((existing) => _DataController.deepEqual(existing, clone));
if (matches == null) {
result.opts.props.push(clone);
optsByTypeAndDataId.set(matchKey, [clone]);
continue;
} else if (match == null) {
result.opts.props.push(clone);
matches.push(clone);
continue;
}
if (clone.scopes != null) {
match.scopes ?? (match.scopes = []);
match.scopes.push(...clone.scopes);
}
if ((match.type === "key" || match.type === "value") && clone.idsMap?.size) {
match.idsMap ?? (match.idsMap = /* @__PURE__ */ new Map());
_DataController.mergeIdsMap(clone.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, length;
if (Array.isArray(a)) {
length = a.length;
if (length !== b.length) {
return false;
}
for (i = length - 1; i >= 0; i--) {
if (!_DataController.deepEqual(a[i], b[i])) {
return false;
}
}
return true;
}
const keys = Object.keys(a);
length = keys.length;
if (length !== Object.keys(b).length) {
return false;
}
for (i = length - 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/data/dataSet.ts
import { Logger as Logger25 } from "ag-charts-core";
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 normalized = this.normalizeTransaction(transaction);
this.pendingTransactions.push(normalized);
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, addIndex, prepend, append, remove, update } = transaction;
if (add === void 0) {
return transaction;
}
const result = { remove, update };
if (prepend)
result.prepend = prepend;
if (append)
result.append = append;
if (add && add.length > 0) {
const currentSize = this.netSize();
if (addIndex === void 0 || addIndex >= currentSize) {
result.append = append ? [...append, ...add] : add;
} else if (addIndex === 0) {
result.prepend = prepend ? [...add, ...prepend] : add;
} else {
result.insertions = [{ index: addIndex, items: add }];
}
}
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) {
Logger25.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) {
Logger25.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/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
import { BaseProperties as BaseProperties5, Property as Property7 } from "ag-charts-core";
var Keyboard = class extends BaseProperties5 {
constructor() {
super(...arguments);
this.enabled = false;
}
};
__decorateClass([
Property7
], Keyboard.prototype, "enabled", 2);
__decorateClass([
Property7
], Keyboard.prototype, "tabIndex", 2);
// packages/ag-charts-community/src/chart/axis/cartesianAxis.ts
import {
ChartAxisDirection as ChartAxisDirection5,
Property as Property18,
StateMachine,
arraysEqual as arraysEqual2,
calcLineHeight,
countLines,
diffArrays,
findMinMax as findMinMax7,
isPlainObject as isPlainObject6
} from "ag-charts-core";
// 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
});
import { easeOut } from "ag-charts-core";
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, finish, delay, duration, ...from } = 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,
to,
ease: easeOut,
collapsable,
onPlay: () => {
const startProps = { ...start, ...toStart, ...from };
applyFn(node, startProps, "start");
},
onUpdate(props) {
applyFn(node, props, "update");
},
onStop: () => {
const endProps = {
...start,
...toStart,
...from,
...to,
...finish,
...toFinish
};
applyFn(node, endProps, "end");
}
});
if (isLive) {
liveNodeIndex++;
}
nodeIndex++;
prevFromProps = from;
}
};
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, from, to, extraOpts) {
const { nodes, selections } = deconstructSelectionsOrNodes(selectionsOrNodes);
const { start, finish, phase } = extraOpts;
animationManager.animate({
id: `${groupId}_${subId}`,
groupId,
phase: phase ?? "update",
from,
to,
ease: easeOut,
onPlay: () => {
if (!start)
return;
for (const node of nodes) {
node.setProperties(start);
}
for (const selection of selections) {
const selectionNodes = selection.nodes();
selection.batchedUpdate(function staticMotionPlay() {
for (const node of selectionNodes) {
node.setProperties(start);
}
});
}
},
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 from = propsFn(node, node.datum);
node.setProperties(from);
}
selection.cleanup();
});
}
for (const node of nodes) {
const from = propsFn(node, node.datum);
node.setProperties(from);
}
}
// packages/ag-charts-community/src/scene/selection.ts
import { Debug as Debug12 } from "ag-charts-core";
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 = Debug12.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(iterate5) {
const nodes = this._nodes;
this.parentNode.batchedUpdate(function selectionEach() {
for (const entry of nodes.entries()) {
iterate5(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/shape/line.ts
import { createSvgElement as createSvgElement12, lineDistanceSquared as lineDistanceSquared2 } from "ag-charts-core";
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 lineDistanceSquared2(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 = createSvgElement12("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/label.ts
import { BaseProperties as BaseProperties6, Property as Property8, isArray as isArray6 } from "ag-charts-core";
var LabelBorder = class {
constructor() {
this.enabled = true;
}
};
__decorateClass([
Property8
], LabelBorder.prototype, "enabled", 2);
__decorateClass([
Property8
], LabelBorder.prototype, "stroke", 2);
__decorateClass([
Property8
], LabelBorder.prototype, "strokeWidth", 2);
__decorateClass([
Property8
], LabelBorder.prototype, "strokeOpacity", 2);
var LabelStyle = class extends BaseProperties6 {
constructor() {
super(...arguments);
this.border = new LabelBorder();
}
};
__decorateClass([
Property8
], LabelStyle.prototype, "border", 2);
__decorateClass([
Property8
], LabelStyle.prototype, "color", 2);
__decorateClass([
Property8
], LabelStyle.prototype, "cornerRadius", 2);
__decorateClass([
Property8
], LabelStyle.prototype, "fill", 2);
__decorateClass([
Property8
], LabelStyle.prototype, "fillOpacity", 2);
__decorateClass([
Property8
], LabelStyle.prototype, "fontStyle", 2);
__decorateClass([
Property8
], LabelStyle.prototype, "fontWeight", 2);
__decorateClass([
Property8
], LabelStyle.prototype, "fontSize", 2);
__decorateClass([
Property8
], LabelStyle.prototype, "fontFamily", 2);
__decorateClass([
Property8
], 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, format } = this;
let result;
if (formatter != null) {
result ?? (result = formatWithContext(formatter, 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 || isArray6(result) ? result : String(result);
}
};
__decorateClass([
Property8
], Label.prototype, "enabled", 2);
__decorateClass([
Property8
], Label.prototype, "formatter", 2);
__decorateClass([
Property8
], Label.prototype, "format", 2);
__decorateClass([
Property8
], 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/chart/axis/axis.ts
import {
ChartAxisDirection as ChartAxisDirection4,
ChartUpdateType as ChartUpdateType4,
CleanupRegistry as CleanupRegistry12,
ObserveChanges as ObserveChanges2,
Property as Property16,
WeakCache,
ZIndexMap as ZIndexMap3,
callWithContext,
clampArray as clampArray2,
deepFreeze as deepFreeze2,
findMinMax as findMinMax4,
findRangeExtent,
isArray as isArray8,
mergeDefaults as mergeDefaults3
} from "ag-charts-core";
// 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(callback4) {
return Array.from(this.moduleMap.values(), callback4);
}
destroy() {
for (const moduleInstance of this.moduleMap.values()) {
moduleInstance?.destroy?.();
}
this.moduleMap.clear();
}
};
// packages/ag-charts-community/src/chart/crossline/cartesianCrossLine.ts
import { BaseProperties as BaseProperties7, Property as Property9, clampArray, createId as createId5, findMinMax as findMinMax2, toRadians as toRadians2 } from "ag-charts-core";
// 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/crossline/crossLine.ts
import { checkDatum } from "ag-charts-core";
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 isContinuous3 = ContinuousScale.is(scale2) || DiscreteTimeScale.is(scale2);
const validValue = (val) => checkDatum(val, isContinuous3) && !Number.isNaN(scale2.convert(val, { clamp: true }));
if (crossLine.type === "range") {
const [start, end2] = value;
return validValue(start) && validValue(end2);
} 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([
Property9
], CartesianCrossLineLabel.prototype, "enabled", 2);
__decorateClass([
Property9
], CartesianCrossLineLabel.prototype, "padding", 2);
__decorateClass([
Property9
], CartesianCrossLineLabel.prototype, "text", 2);
__decorateClass([
Property9
], CartesianCrossLineLabel.prototype, "position", 2);
__decorateClass([
Property9
], CartesianCrossLineLabel.prototype, "rotation", 2);
__decorateClass([
Property9
], CartesianCrossLineLabel.prototype, "parallel", 2);
var CartesianCrossLine = class extends BaseProperties7 {
constructor() {
super();
this.id = createId5(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: range4, 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] = findMinMax2(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 (range4) {
const [r0, r1] = range4;
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 range4 = this.type === "range";
const { position = this.defaultLabelPosition } = this.label;
if (range4) {
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 = toRadians2(label.rotation ?? 0);
const bbox = crossLineLabel.getBBox();
if (!bbox)
return;
const { width, height } = bbox;
const xOffset = label.padding + width / 2;
const yOffset = label.padding + height / 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 = toRadians2(label.rotation ?? 0);
tempText.textBaseline = "middle";
tempText.textAlign = "center";
const bbox = tempText.getBBox();
if (!bbox)
return;
const { width, height } = bbox;
return { width, height };
}
calculatePadding(into) {
const { label, anchor } = this;
const size = this.computeLabelSize();
if (!size)
return;
const { width, height } = size;
const xOffset = label.padding + width;
const yOffset = label.padding + height;
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([
Property9
], CartesianCrossLine.prototype, "enabled", 2);
__decorateClass([
Property9
], CartesianCrossLine.prototype, "type", 2);
__decorateClass([
Property9
], CartesianCrossLine.prototype, "range", 2);
__decorateClass([
Property9
], CartesianCrossLine.prototype, "value", 2);
__decorateClass([
Property9
], CartesianCrossLine.prototype, "defaultColorRange", 2);
__decorateClass([
Property9
], CartesianCrossLine.prototype, "fill", 2);
__decorateClass([
Property9
], CartesianCrossLine.prototype, "fillOpacity", 2);
__decorateClass([
Property9
], CartesianCrossLine.prototype, "stroke", 2);
__decorateClass([
Property9
], CartesianCrossLine.prototype, "strokeWidth", 2);
__decorateClass([
Property9
], CartesianCrossLine.prototype, "strokeOpacity", 2);
__decorateClass([
Property9
], CartesianCrossLine.prototype, "lineDash", 2);
__decorateClass([
Property9
], CartesianCrossLine.prototype, "label", 2);
// packages/ag-charts-community/src/chart/axis/axisGridLine.ts
import { Property as Property10 } from "ag-charts-core";
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([
Property10
], AxisGridLine.prototype, "enabled", 2);
__decorateClass([
Property10
], AxisGridLine.prototype, "width", 2);
__decorateClass([
Property10
], AxisGridLine.prototype, "style", 2);
// packages/ag-charts-community/src/chart/axis/axisInterval.ts
import { BaseProperties as BaseProperties8, Property as Property11 } from "ag-charts-core";
var AxisInterval = class extends BaseProperties8 {
};
__decorateClass([
Property11
], AxisInterval.prototype, "placement", 2);
__decorateClass([
Property11
], AxisInterval.prototype, "step", 2);
__decorateClass([
Property11
], AxisInterval.prototype, "values", 2);
__decorateClass([
Property11
], AxisInterval.prototype, "minSpacing", 2);
__decorateClass([
Property11
], AxisInterval.prototype, "maxSpacing", 2);
// packages/ag-charts-community/src/chart/axis/axisLabel.ts
import { BaseProperties as BaseProperties9, Property as Property12, isArray as isArray7, objectsEqual as objectsEqual8 } from "ag-charts-core";
var AxisLabel = class extends BaseProperties9 {
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(callWithContext7, params, index, options) {
const { formatter, 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 (formatter != null) {
const step = params.type === "date" ? params.step : void 0;
const visibleDomain = params.type === "number" ? params.visibleDomain : void 0;
result = callWithContext7(formatter, {
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 || !objectsEqual8(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 || isArray7(result) ? result : String(result);
}
};
__decorateClass([
Property12
], AxisLabel.prototype, "enabled", 2);
__decorateClass([
Property12
], AxisLabel.prototype, "border", 2);
__decorateClass([
Property12
], AxisLabel.prototype, "cornerRadius", 2);
__decorateClass([
Property12
], AxisLabel.prototype, "fill", 2);
__decorateClass([
Property12
], AxisLabel.prototype, "fillOpacity", 2);
__decorateClass([
Property12
], AxisLabel.prototype, "fontStyle", 2);
__decorateClass([
Property12
], AxisLabel.prototype, "fontWeight", 2);
__decorateClass([
Property12
], AxisLabel.prototype, "fontSize", 2);
__decorateClass([
Property12
], AxisLabel.prototype, "fontFamily", 2);
__decorateClass([
Property12
], AxisLabel.prototype, "wrapping", 2);
__decorateClass([
Property12
], AxisLabel.prototype, "truncate", 2);
__decorateClass([
Property12
], AxisLabel.prototype, "spacing", 2);
__decorateClass([
Property12
], AxisLabel.prototype, "minSpacing", 2);
__decorateClass([
Property12
], AxisLabel.prototype, "color", 2);
__decorateClass([
Property12
], AxisLabel.prototype, "rotation", 2);
__decorateClass([
Property12
], AxisLabel.prototype, "avoidCollisions", 2);
__decorateClass([
Property12
], AxisLabel.prototype, "mirrored", 2);
__decorateClass([
Property12
], AxisLabel.prototype, "padding", 2);
__decorateClass([
Property12
], AxisLabel.prototype, "parallel", 2);
__decorateClass([
Property12
], AxisLabel.prototype, "itemStyler", 2);
__decorateClass([
Property12
], AxisLabel.prototype, "formatter", 2);
__decorateClass([
Property12
], AxisLabel.prototype, "format", 2);
// packages/ag-charts-community/src/chart/axis/axisLine.ts
import { Property as Property13 } from "ag-charts-core";
var AxisLine = class {
constructor() {
this.enabled = true;
this.width = 1;
this.stroke = void 0;
}
};
__decorateClass([
Property13
], AxisLine.prototype, "enabled", 2);
__decorateClass([
Property13
], AxisLine.prototype, "width", 2);
__decorateClass([
Property13
], AxisLine.prototype, "stroke", 2);
// packages/ag-charts-community/src/chart/axis/axisTick.ts
import { BaseProperties as BaseProperties10, Property as Property14 } from "ag-charts-core";
var AxisTick = class extends BaseProperties10 {
constructor() {
super(...arguments);
this.enabled = true;
this.width = 1;
this.size = 6;
}
};
__decorateClass([
Property14
], AxisTick.prototype, "enabled", 2);
__decorateClass([
Property14
], AxisTick.prototype, "width", 2);
__decorateClass([
Property14
], AxisTick.prototype, "size", 2);
__decorateClass([
Property14
], AxisTick.prototype, "stroke", 2);
// packages/ag-charts-community/src/chart/axis/axisTitle.ts
import { BaseProperties as BaseProperties11, FONT_SIZE as FONT_SIZE2, Property as Property15 } from "ag-charts-core";
var AxisTitle = class extends BaseProperties11 {
constructor() {
super(...arguments);
this.caption = new Caption();
this.enabled = false;
this.spacing = Caption.SMALL_PADDING;
this.fontSize = FONT_SIZE2.SMALLER;
this.fontFamily = "sans-serif";
this.wrapping = "always";
}
};
__decorateClass([
Property15
], AxisTitle.prototype, "enabled", 2);
__decorateClass([
Property15
], AxisTitle.prototype, "text", 2);
__decorateClass([
Property15
], AxisTitle.prototype, "spacing", 2);
__decorateClass([
Property15
], AxisTitle.prototype, "fontStyle", 2);
__decorateClass([
Property15
], AxisTitle.prototype, "fontWeight", 2);
__decorateClass([
Property15
], AxisTitle.prototype, "fontSize", 2);
__decorateClass([
Property15
], AxisTitle.prototype, "fontFamily", 2);
__decorateClass([
Property15
], AxisTitle.prototype, "color", 2);
__decorateClass([
Property15
], AxisTitle.prototype, "wrapping", 2);
__decorateClass([
Property15
], AxisTitle.prototype, "formatter", 2);
// packages/ag-charts-community/src/chart/axis/axisUtil.ts
import { findMinMax as findMinMax3 } from "ag-charts-core";
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] = findMinMax3(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(start, end2) {
const directDistance = Math.abs(end2 - start);
if (directDistance < halfCircle) {
return end2;
} else if (start > end2) {
return end2 + fullCircle;
}
return end2 - 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, range4, value) {
const bandwidth = scale2.bandwidth ?? 0;
const step = scale2.step ?? 0;
const offset = (step - bandwidth) / 2;
const position = scale2.convert(value);
const start = position - offset;
const end2 = position + bandwidth + offset;
return [position, clampArray2(start, range4), clampArray2(end2, range4)];
}
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: ZIndexMap3.SERIES_ANNOTATION
});
this.gridGroup = new TranslatableGroup({ name: `${this.id}-Axis-grid`, zIndex: ZIndexMap3.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: ZIndexMap3.SERIES_CROSSLINE_RANGE
});
this.crossLineLineGroup = new TransformableGroup({
name: `${this.id}-CrossLines-Line`,
zIndex: ZIndexMap3.SERIES_CROSSLINE_LINE
});
this.crossLineLabelGroup = new TransformableGroup({
name: `${this.id}-CrossLines-Label`,
zIndex: ZIndexMap3.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 CleanupRegistry12();
// 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 deepFreeze2(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 start = rr[0] - shift;
scale2.range = [start, start + 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] = findMinMax4(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] = findMinMax4([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 = mergeDefaults3(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: range4,
scale: scale2,
gridLength
} = this;
const rangeExtent = findRangeExtent(range4);
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: range4, gridLength } = this;
const rangeExtent = findRangeExtent(range4);
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 timeInterval2;
let truncateDate;
if (tickFormatParams.type === "number") {
fractionDigits = tickFormatParams.fractionDigits;
} else if (tickFormatParams.type === "date") {
const { unit, step, epoch } = tickFormatParams;
timeInterval2 = { 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, timeInterval2, 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 isArray8(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, width, height) {
this.tickLineGroup.setClipRect(new BBox(x, y, width, height));
}
clipGrid(x, y, width, height) {
this.gridGroup.setClipRect(new BBox(x, y, width, height));
}
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 ChartUpdateType4.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: range4, id } = this;
const value = scale2.invert(this.isVertical() ? point.y : point.x, true);
const [position, start, end2] = computeBand(scale2, range4, value);
return { id, value, band: [start, end2], position };
}
measureBand(value) {
if (!BandScale.is(this.scale)) {
return;
}
const [, start, end2] = computeBand(this.scale, this.range, value);
return { band: [start, end2] };
}
isVertical() {
return this.direction === ChartAxisDirection4.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([
Property16
], _Axis.prototype, "nice", 2);
__decorateClass([
Property16
], _Axis.prototype, "reverse", 2);
__decorateClass([
Property16
], _Axis.prototype, "interval", 2);
__decorateClass([
Property16
], _Axis.prototype, "title", 2);
__decorateClass([
ObserveChanges2((target, value, oldValue) => target.onGridLengthChange(value, oldValue))
], _Axis.prototype, "gridLength", 2);
var Axis = _Axis;
// packages/ag-charts-community/src/chart/axis/cartesianAxisLabel.ts
import { Property as Property17 } from "ag-charts-core";
var CartesianAxisLabel = class extends AxisLabel {
constructor() {
super(...arguments);
this.autoRotateAngle = 335;
}
};
__decorateClass([
Property17
], CartesianAxisLabel.prototype, "autoRotate", 2);
__decorateClass([
Property17
], CartesianAxisLabel.prototype, "autoRotateAngle", 2);
// packages/ag-charts-community/src/chart/axis/generateTicks.ts
import {
ScaleAlignment as ScaleAlignment7,
cachedTextMeasurer as cachedTextMeasurer5,
countFractionDigits as countFractionDigits2,
estimateTickCount,
findMinMax as findMinMax6,
findRangeExtent as findRangeExtent2,
getTickTimeInterval as getTickTimeInterval2,
intervalMilliseconds as intervalMilliseconds3,
lowestGranularityForInterval,
normalizeAngle360FromDegrees as normalizeAngle360FromDegrees6,
rotatePoint
} from "ag-charts-core";
// packages/ag-charts-community/src/scale/categoryScale.ts
import { clamp as clamp12, dateToNumber, previousPowerOf2 } from "ag-charts-core";
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 = clamp12(0, Math.floor((visibleRange?.[0] ?? 0) * bands.length), bands.length);
const vt1 = clamp12(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/scale/ordinalTimeScale.ts
import {
ScaleAlignment as ScaleAlignment4,
datesSortOrder,
sortAndUniqueDates
} from "ag-charts-core";
// packages/ag-charts-community/src/scale/timeScale.ts
import {
TickIntervals,
dateToNumber as dateToNumber2,
defaultEpoch,
getTickTimeInterval,
intervalRange,
intervalRangeStartIndex,
intervalStep,
isDenseInterval,
isPlainObject as isPlainObject3
} from "ag-charts-core";
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 (dateToNumber2(d0) === dateToNumber2(n0) && dateToNumber2(d1) === dateToNumber2(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(dateToNumber2);
const start = timestamps[0];
const stop = timestamps.at(-1);
if (interval != null) {
const availableRange = this.getPixelRange();
return {
ticks: getDateTicksForInterval({ start, stop, interval, availableRange, visibleRange, extend }) ?? getDefaultDateTicks({ start, 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 timeInterval2 = getTickTimeInterval(start, stop, tickCount, minTickCount, maxTickCount, {
weekStart: sunday
});
if (timeInterval2 == null)
return;
const ticks = intervalRange(timeInterval2, new Date(start), new Date(stop), { visibleRange, extend });
const firstTickIndex = intervalRangeStartIndex(timeInterval2, new Date(start), new Date(stop), {
visibleRange,
extend
});
return {
ticks,
count: void 0,
firstTickIndex,
timeInterval: timeInterval2
};
}
};
function getDefaultDateTicks({
start,
stop,
tickCount,
minTickCount,
maxTickCount,
visibleRange,
extend
}) {
const t = getTickTimeInterval(start, stop, tickCount, minTickCount, maxTickCount, { weekStart: sunday });
return t ? intervalRange(t, new Date(start), new Date(stop), { visibleRange, extend }) : [];
}
function getDateTicksForInterval({
start,
stop,
interval,
availableRange,
visibleRange,
extend
}) {
if (!interval) {
return [];
}
if (isPlainObject3(interval) || typeof interval === "string") {
const ticks2 = intervalRange(interval, new Date(start), new Date(stop), { visibleRange, extend });
if (isDenseInterval(ticks2.length, availableRange)) {
return;
}
return ticks2;
}
const absInterval = Math.abs(interval);
if (isDenseInterval(Math.abs(stop - start) / absInterval, availableRange))
return;
const tickInterval = TickIntervals.findLast((t) => absInterval % t.duration === 0);
if (tickInterval) {
const { timeInterval: timeInterval2, step, duration } = tickInterval;
const alignedInterval = {
...timeInterval2,
step: step * intervalStep(timeInterval2) * Math.round(absInterval / duration),
epoch: defaultEpoch(timeInterval2, { weekStart: sunday })
};
return intervalRange(alignedInterval, new Date(start), new Date(stop), { visibleRange, extend });
}
let date3 = new Date(Math.min(start, stop));
const stopDate = new Date(Math.max(start, stop));
const ticks = [];
while (date3 <= stopDate) {
ticks.push(date3);
date3 = new Date(date3);
date3.setMilliseconds(date3.getMilliseconds() + absInterval);
}
return ticks;
}
function updateNiceDomainIteration(d0, d1, ticks, availableRange) {
const { interval } = ticks;
const start = Math.min(dateToNumber2(d0), dateToNumber2(d1));
const stop = Math.max(dateToNumber2(d0), dateToNumber2(d1));
let i;
if (isPlainObject3(interval) || typeof interval === "string") {
i = interval;
} else {
let tickCount;
if (typeof interval === "number") {
tickCount = (stop - start) / Math.max(interval, 1);
if (isDenseInterval(tickCount, availableRange)) {
tickCount = void 0;
}
}
tickCount ?? (tickCount = ticks.tickCount ?? ContinuousScale.defaultTickCount);
i = getTickTimeInterval(start, stop, tickCount, ticks.minTickCount, ticks.maxTickCount, { weekStart: sunday });
}
if (i == null)
return [d0, d1];
const domain = intervalRange(i, new Date(start), 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 start;
let stop;
if (domain && domain.length >= 2) {
start = domain[0].valueOf();
stop = domain.at(-1).valueOf();
} else {
start = bands[0].valueOf();
stop = bands.at(-1).valueOf();
}
const [r0, r1] = this.range;
const availableRange = Math.abs(r1 - r0);
const dateTicks = getDateTicksForInterval({ start, 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, ScaleAlignment4.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], ScaleAlignment4.Trailing) ?? 0;
const i1 = this.findIndex(domain[isReversed ? 0 : domain.length - 1], ScaleAlignment4.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
import {
Logger as Logger26,
ScaleAlignment as ScaleAlignment5,
decodeIntervalValue,
encodedToTimestamp,
findMaxIndex as findMaxIndex3,
findMinIndex as findMinIndex3,
intervalFloor,
intervalMilliseconds,
intervalNext,
intervalRange as intervalRange2,
intervalRangeCount,
intervalRangeNumeric,
intervalRangeStartIndex as intervalRangeStartIndex2,
isPlainObject as isPlainObject4
} from "ag-charts-core";
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 [start, stop] = bandRange;
const rangeParams = { visibleRange: [0, 1], extend: false };
if (intervalRangeCount(interval, start, stop, rangeParams) > MAX_BANDS) {
Logger26.warnOnce(`the configured unit results in too many bands, ignoring. Supply a larger unit.`);
this._encodedBands = [];
return;
}
const { encodedValues, encodingParams } = intervalRangeNumeric(interval, start, 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 [start, stop] = bandRange;
const d0 = Math.min(start.valueOf(), stop.valueOf());
const d1 = Math.max(start.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 = ScaleAlignment5.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 === ScaleAlignment5.Trailing ? Math.ceil(rawIndex) : Math.floor(rawIndex);
index = Math.max(0, Math.min(index, n - 1));
if (this.isLinearUnit()) {
if (alignment === ScaleAlignment5.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 === ScaleAlignment5.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 [start, stop] = bandRange;
if (intervalRangeCount(interval, start, stop, rangeParams) > MAX_BANDS) {
Logger26.warnOnce(`the configured unit results in too many bands, ignoring. Supply a larger unit.`);
return { bands: [], firstBandIndex: void 0 };
}
const bands = intervalRange2(interval, start, stop, rangeParams);
const firstBandIndex = intervalRangeStartIndex2(interval, start, 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 (isPlainObject4(interval) || typeof interval === "string") {
intervalTicks = intervalRange2(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 = findMaxIndex3(i0, i1, (index) => bands[index].valueOf() <= d0) ?? i0;
intervalEndIndex = findMaxIndex3(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 = findMaxIndex3(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 = findMinIndex3(0, ticks.length - 1, (i) => ticks[i].valueOf() >= bandStart) ?? 0;
let lastTickIndex = findMaxIndex3(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 [start, stop] = calculateBandRange(domain, interval);
return intervalRangeCount(interval, start, stop, rangeParams) <= MAX_BANDS;
}
function calculateBandRange(domain, interval) {
const start = intervalFloor(interval, domain[0]);
const stop = intervalFloor(interval, domain[1]);
return [start, stop];
}
// packages/ag-charts-community/src/util/secondaryAxisTicks.ts
import { countFractionDigits, createTicks, findMinMax as findMinMax5, niceTicksDomain } from "ag-charts-core";
function calculateNiceSecondaryAxis(scale2, domain, primaryTickCount, reverse, visibleRange) {
let [d0, d1] = findMinMax5(domain.map(Number));
const unzoomedTickCount = Math.floor(primaryTickCount.unzoomed);
if (unzoomedTickCount <= 1) {
const [start2, stop2] = domainWithOddTickCount(d0, d1);
const tickCount = 5 * Math.pow(2, -Math.ceil(Math.log2(visibleRange[1] - visibleRange[0])));
const { ticks: ticks2 } = createTicks(start2, stop2, tickCount, void 0, void 0, visibleRange);
const d2 = [scale2.toDomain(start2), 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 start = d0;
let stop = d1;
start = calculateNiceStart(start, stop, unzoomedTickCount);
const baseStep = getTickStep(start, stop, unzoomedTickCount);
const segments = unzoomedTickCount - 1;
stop = start + segments * baseStep;
const stepAlignedStart = Math.floor(start / baseStep) * baseStep;
const stepAlignedStop = Math.floor(stop / baseStep) * baseStep;
if (stepAlignedStart <= d0 && stepAlignedStop >= d1) {
start = stepAlignedStart;
stop = stepAlignedStop;
}
const d = [scale2.toDomain(start), scale2.toDomain(stop)];
if (reverse)
d.reverse();
const step = baseStep * ((primaryTickCount.unzoomed - 1) / (primaryTickCount.zoomed - 1));
const ticks = getTicks(start, step, Math.floor(primaryTickCount.zoomed));
return { domain: d, ticks };
}
function domainWithOddTickCount(d0, d1) {
let start = d0;
let stop = d1;
let iterations = 0;
do {
[start, stop] = niceTicksDomain(start, stop);
const { ticks } = createTicks(start, stop, 5);
if (ticks.length % 2 === 1)
return [start, stop];
start -= 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(start, step, count) {
const fractionDigits = countFractionDigits(step);
const f = Math.pow(10, fractionDigits);
const ticks = [];
for (let i = 0; i < count; i++) {
const tick = start + step * i;
ticks.push(Math.round(tick * f) / f);
}
return ticks;
}
function getTickStep(start, stop, count) {
const segments = count - 1;
const rawStep = (stop - start) / 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
import {
ScaleAlignment as ScaleAlignment6,
boxCollides,
buildDateFormatter as buildDateFormatter2,
cachedTextMeasurer as cachedTextMeasurer4,
compareDates,
createIdsGenerator,
dropFirstWhile,
dropLastWhile,
getMaxInnerRectSize,
intervalCeil,
intervalExtent,
intervalFloor as intervalFloor2,
intervalHierarchy,
intervalMilliseconds as intervalMilliseconds2,
intervalNext as intervalNext2,
intervalPrevious,
intervalRange as intervalRange3,
intervalUnit,
isArray as isArray9,
isPlainObject as isPlainObject5,
isSegmentTruncated as isSegmentTruncated2,
isTextTruncated as isTextTruncated2,
measureTextSegments as measureTextSegments3,
normalizeAngle360FromDegrees as normalizeAngle360FromDegrees5,
toPlainText as toPlainText3,
toTextString as toTextString5,
wrapTextOrSegments
} from "ag-charts-core";
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, height } = datum;
if (result.some((l) => boxCollides(l, x, y, width + padding2, height + 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 intervalRange3(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: timeInterval2
}) {
const { scale: scale2, label, tickFormatter, inRange: inRange2, isVertical, sizeLimit = Infinity } = options;
const isContinuous3 = TimeScale.is(scale2) || DiscreteTimeScale.is(scale2);
const measurer = cachedTextMeasurer4(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,
timeInterval2,
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 = isArray9(tickLabel);
const isTruncated2 = tickLabel !== inputText && (isSegmented ? isSegmentTruncated2(tickLabel.at(-1)) : isTextTruncated2(toTextString5(tickLabel)));
let tickId;
if (isContinuous3) {
const tickValue = tick?.valueOf();
if (Number.isFinite(tickValue)) {
tickId = idGenerator(`v:${tickValue}`);
}
}
tickId ?? (tickId = idGenerator(`l:${isSegmented ? toPlainText3(tickLabel.flat()) : tickLabel}`));
ticks.push({
tick,
tickId,
tickLabel,
isPrimary,
index: i + rawFirstTickIndex,
textUntruncated: isTruncated2 ? toPlainText3(inputText) : void 0,
textMetrics: isSegmented ? measureTextSegments3(tickLabel, label) : measurer.measureLines(toTextString5(tickLabel)),
translation: Math.floor(translation)
});
}
});
return ticks;
}
function withTemporaryDomain(scale2, temporaryDomain, callback4) {
const originalDomain = scale2.domain;
try {
scale2.domain = temporaryDomain;
callback4();
} finally {
scale2.domain = originalDomain;
}
}
function axisTickFormatter(labelEnabled, generatePrimaryTicks, niceDomain, rawTicks, fractionDigits, timeInterval2, tickFormatter) {
const dateStyle = generatePrimaryTicks ? "component" : "long";
const parentInterval = generatePrimaryTicks && timeInterval2 ? intervalHierarchy(timeInterval2) : void 0;
const primaryFormatter = generatePrimaryTicks ? tickFormatter(niceDomain, rawTicks, true, fractionDigits, parentInterval, dateStyle) : null;
const defaultFormatter = labelEnabled ? tickFormatter(niceDomain, rawTicks, false, fractionDigits, timeInterval2, dateStyle) : null;
return (isPrimary, tick, index) => {
const formatter = isPrimary ? primaryFormatter : defaultFormatter;
return formatter?.(tick, index) ?? String(tick);
};
}
function getTimeIntervalTicks(scale2, visibleRange, tickCount, maxTickCount, tickParams, timeInterval2, reverse, minimumTimeGranularity) {
if (!TimeScale.is(scale2) && !DiscreteTimeScale.is(scale2))
return;
const parentInterval = intervalHierarchy(timeInterval2);
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 = intervalFloor2(parentInterval, dp0);
if (dp0.valueOf() >= dv0) {
dp0 = intervalPrevious(parentInterval, dp0);
}
dp1 = intervalCeil(parentInterval, dp1);
if (dp1.valueOf() <= dv1) {
dp1 = intervalNext2(parentInterval, dp1);
}
const primaryTicks = intervalRange3(parentInterval, dp0, dp1);
const milliseconds = intervalMilliseconds2(timeInterval2);
const skipFirstPrimaryTick = OrdinalTimeScale.is(scale2);
const intervalTickParams = { ...tickParams, interval: timeInterval2 };
const ticks = [];
let primaryTicksIndices;
let parentLevelMode;
let alignment;
let ordinalTickStep = 0;
if (OrdinalTimeScale.is(scale2)) {
const timeIntervalGranularity = intervalUnit(timeInterval2);
parentLevelMode = minimumTimeGranularity != null && intervalMilliseconds2(minimumTimeGranularity) >= intervalMilliseconds2(timeIntervalGranularity) ? 2 /* OrdinalTimeStepTicks */ : 3 /* OrdinalTimeScaleTicks */;
alignment = ScaleAlignment6.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 || intervalMilliseconds2(scale2.interval) >= milliseconds)) {
parentLevelMode = 1 /* UnitTimeScaleTicks */;
} else {
parentLevelMode = 0 /* ContinuousTimeScaleTicks */;
alignment = ScaleAlignment6.Interpolate;
}
for (let i = 0; i < primaryTicks.length - 1; i++) {
const p0 = primaryTicks[i];
const p1 = primaryTicks[i + 1];
const first8 = 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 || !first8)) {
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, timeInterval2, textMeasurer) {
const specifier = labelSpecifier(label.format, timeInterval2);
if (specifier == null) {
return { width: 0, height: 0 };
}
const labelFormatter = buildDateFormatter2(specifier);
const hierarchy = timeInterval2 ? intervalHierarchy(timeInterval2) : void 0;
const primarySpecifier = labelSpecifier(primaryLabel?.format, hierarchy);
const primaryLabelFormatter = primarySpecifier ? buildDateFormatter2(primarySpecifier) : labelFormatter;
const d0 = new Date(domain[0]);
const d1 = new Date(domain.at(-1));
const hierarchyRange = hierarchy ? intervalRange3(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 = intervalRange3(timeInterval2, l0, l1, { limit: 50 });
for (const date3 of labelRange) {
const text = labelFormatter(date3);
const { width, height } = textMeasurer.measureLines(text);
maxWidth = Math.max(maxWidth, width + xPadding);
maxHeight = Math.max(maxHeight, height + yPadding);
}
}
if (primaryLabelFormatter != null && hierarchyRange != null) {
const padding2 = expandLabelPadding(primaryLabel);
const xPadding = padding2.left + padding2.right;
const yPadding = padding2.top + padding2.bottom;
for (const date3 of hierarchyRange) {
const text = primaryLabelFormatter(date3);
const { width, height } = textMeasurer.measureLines(text);
maxWidth = Math.max(maxWidth, width + xPadding);
maxHeight = Math.max(maxHeight, height + 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, timeInterval2) {
if (format == null)
return;
if (typeof format === "string") {
return format;
} else if (isPlainObject5(format) && timeInterval2 != null) {
return format[intervalUnit(timeInterval2)];
}
}
function calculateLabelRotation(rotation, parallel, axisRotation = 0) {
const configuredRotation = normalizeAngle360FromDegrees5(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) ? normalizeAngle360FromDegrees6(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: range4,
visibleRange,
label,
defaultTickMinSpacing,
interval: { minSpacing, maxSpacing }
}) {
const { defaultTickCount } = scale2;
const rangeExtent = findRangeExtent2(range4);
const zoomExtent = findRangeExtent2(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 countTicks = (i) => Math.max(tickCount - i, minTickCount);
const regenerateTicks = step == null && values == null && countTicks(index) > minTickCount;
const previousTicks = previousTickData.rawTicks;
const maxIterations = tickCount - minTickCount;
const countParams = { minTickCount, maxTickCount, tickCount: countTicks(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 = countTicks(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: timeInterval2
} = nextTicks;
return {
tickData: {
tickDomain,
niceDomain,
rawTicks,
rawTickCount,
fractionDigits,
timeInterval: timeInterval2,
ticks: formatTicks(options, {
niceDomain,
rawTicks,
rawFirstTickIndex,
generatePrimaryTicks,
primaryTicksIndices,
alignment,
fractionDigits,
timeInterval: timeInterval2
})
},
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 timeInterval2;
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 = ScaleAlignment7.Trailing;
} else if (UnitTimeScale.is(scale2)) {
alignment = ScaleAlignment7.Interpolate;
}
if (ContinuousScale.is(scale2)) {
const [d0, d1] = findMinMax6(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 start = Math.min(dates[0].valueOf(), dates.at(-1).valueOf());
const end2 = Math.max(dates[0].valueOf(), dates.at(-1).valueOf());
timeInterval2 = getTickTimeInterval2(start, end2, 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 && timeInterval2 != null && // Prefer UnitTimeAxis.unit over this interval, because the user may have defined an epoch
intervalMilliseconds3(minTimeInterval) >= intervalMilliseconds3(timeInterval2)) {
timeInterval2 = minTimeInterval;
}
const intervalTicks = timeInterval2 ? getTimeIntervalTicks(
scale2,
visibleRange,
tickCount,
maxTickCount,
tickParams,
timeInterval2,
reverse,
minimumTimeGranularity
) : void 0;
if (intervalTicks) {
({ ticks: rawTicks, primaryTicksIndices, alignment } = intervalTicks);
} else {
const intervalTickParams = UnitTimeScale.is(scale2) && tickParams.interval == null && timeInterval2 != null ? { ...tickParams, interval: timeInterval2 } : 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;
timeInterval2 ?? (timeInterval2 = paramsInterval ?? tickGeneration?.timeInterval);
}
}
}
}
});
rawTicks ?? (rawTicks = []);
let fractionDigits = 0;
for (const tick of rawTicks) {
if (typeof tick !== "number")
continue;
const value = countFractionDigits2(tick);
if (value > fractionDigits) {
fractionDigits = value;
}
}
if (!generatePrimaryTicks) {
primaryTicksIndices = void 0;
}
return {
tickDomain,
niceDomain,
rawTicks,
rawTickCount,
rawFirstTickIndex,
generatePrimaryTicks,
primaryTicksIndices,
alignment,
fractionDigits,
timeInterval: timeInterval2
};
}
function createTimeLabelData(options, tickData, labelRotation) {
const { niceDomain, ticks, timeInterval: timeInterval2 } = tickData;
if (timeInterval2 == null)
return [];
const spacing = ticksSpacing(ticks);
const { label, labelOffset, primaryLabel, domain } = options;
const { width, height } = timeIntervalMaxLabelSize(
label,
primaryLabel,
niceDomain ?? domain,
timeInterval2,
cachedTextMeasurer5(label)
);
const labelData = [];
for (const translation of [0, spacing]) {
const { x, y } = rotatePoint(labelOffset, translation, labelRotation);
labelData.push({ x, y, width, height });
}
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 width = textMetrics.width + xPadding;
const height = textMetrics.height + yPadding;
labelData.push({ x, y, width, height });
}
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 && !arraysEqual2(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" ? ChartAxisDirection5.X : ChartAxisDirection5.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: range4, interval, reverse, defaultTickMinSpacing, minimumTimeGranularity } = this;
const tickGenerationResult = generateTicks({
label,
scale: scale2,
interval,
primaryLabel,
domain,
range: range4,
reverse,
niceMode,
visibleRange,
defaultTickMinSpacing,
minimumTimeGranularity,
sideFlag,
labelOffset: labelX,
primaryTickCount: initialPrimaryTickCount,
axisRotation: this.horizontal ? Math.PI / -2 : 0,
isVertical: this.direction === ChartAxisDirection5.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 > range4[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: timeInterval2, 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: timeInterval2, 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 } = gridLine;
const { stroke, strokeWidth = 0, lineDash } = style[tickIndex % style.length] ?? {};
return { tickId, offset, x1, y1, x2, y2, stroke, strokeWidth, lineDash };
}
calculateGridFills(ticks, p1, p2) {
const { horizontal, range: range4, 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 < range4[0] || !isVerticalUnitTime && ticks[0].translation > range4[0];
if (firstFillOffCanvas) {
const injectedTick = { tickId: `before:${ticks[0].tickId}`, translation: range4[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: range4 } = this;
const nextTick = ticks[index + 1];
const startOffset = translation;
const endOffset = nextTick ? nextTick.translation : range4[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 } = this.line;
this.lineNode.setProperties({ stroke, strokeWidth: enabled ? width : 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] = findMinMax7(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 start = tickOffset;
const end2 = tickOffset - direction * (tickSize + tickSpacing);
const min = Math.min(start, end2);
const max = Math.max(start, end2);
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 = isPlainObject6(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: range4 } = this;
const midOffset = (range4[0] + range4[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 = (p) => p.defaultValue } = title;
const text = this.cachedCallWithContext(formatter, 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: range4 } = 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: range4
};
}
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([
Property18
], _CartesianAxis.prototype, "thickness", 2);
__decorateClass([
Property18
], _CartesianAxis.prototype, "maxThicknessRatio", 2);
__decorateClass([
Property18
], _CartesianAxis.prototype, "position", 2);
__decorateClass([
Property18
], _CartesianAxis.prototype, "crossAt", 2);
var CartesianAxis = _CartesianAxis;
// 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
import { ModuleRegistry as ModuleRegistry2, jsonDiff as jsonDiff2 } from "ag-charts-core";
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 = ModuleRegistry2.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 = jsonDiff2(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/mapping/types.ts
import { ModuleRegistry as ModuleRegistry3 } from "ag-charts-core";
// packages/ag-charts-community/src/chart/factory/expectedModules.ts
import { ModuleType as ModuleType2 } from "ag-charts-core";
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 === ModuleType2.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 ModuleRegistry3.getSeriesModule(mainSeriesType)?.chartType ?? getSeriesExpectedChartType(mainSeriesType) ?? "unknown";
}
function isAgCartesianChartOptions(input) {
return detectChartType(input) === "cartesian";
}
// packages/ag-charts-community/src/chart/modulesManager.ts
import { ModuleRegistry as ModuleRegistry4, ModuleType as ModuleType3 } from "ag-charts-core";
var ModulesManager = class extends ModuleMap {
*legends() {
for (const module of ModuleRegistry4.listModulesByType(ModuleType3.Plugin)) {
if (module.name === "legend" || module.name === "gradientLegend") {
yield {
legendType: module.name === "legend" ? "category" : "gradient",
legend: this.getModule(module.name)
};
}
}
}
};
// packages/ag-charts-community/src/chart/overlay/chartOverlays.ts
import { BaseProperties as BaseProperties13, Property as Property20 } from "ag-charts-core";
// packages/ag-charts-community/src/chart/overlay/overlay.ts
import {
BaseProperties as BaseProperties12,
Property as Property19,
callWithContext as callWithContext2,
createElement as createElement10,
isArray as isArray10,
isHTMLElement as isHTMLElement2,
toPlainText as toPlainText4,
toTextString as toTextString6
} from "ag-charts-core";
var DEFAULT_OVERLAY_CLASS = "ag-charts-overlay";
var DEFAULT_OVERLAY_DARK_CLASS = "ag-charts-dark-overlay";
var Overlay = class extends BaseProperties12 {
constructor(className, defaultMessageId) {
super();
this.className = className;
this.defaultMessageId = defaultMessageId;
this.enabled = true;
}
getText(localeManager) {
if (isArray10(this.text)) {
return toPlainText4(this.text);
}
if (this.rendererAsText) {
return this.rendererAsText;
}
return localeManager.t(toTextString6(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 = callWithContext2(callers, this.renderer, params);
if (isHTMLElement2(htmlContent)) {
this.content = htmlContent;
} else {
const tempDiv = createElement10("div");
tempDiv.innerHTML = htmlContent;
const { firstElementChild } = tempDiv;
if (isHTMLElement2(firstElementChild) && tempDiv.childElementCount === 1) {
this.content = firstElementChild;
} else {
this.content = tempDiv;
}
}
this.rendererAsText = this.content?.textContent?.trim() ?? void 0;
} else {
const content = createElement10("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 (isArray10(this.text)) {
const container = createElement10("div");
for (const segment of this.text) {
const el = createElement10("span", {
color: segment.color,
fontSize: `${segment.fontSize}px`,
fontFamily: segment.fontFamily ?? "inherit",
fontWeight: String(segment.fontWeight),
fontStyle: segment.fontStyle
});
el.innerText = toTextString6(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([
Property19
], Overlay.prototype, "enabled", 2);
__decorateClass([
Property19
], Overlay.prototype, "text", 2);
__decorateClass([
Property19
], Overlay.prototype, "renderer", 2);
// packages/ag-charts-community/src/chart/overlay/chartOverlays.ts
var ChartOverlays = class extends BaseProperties13 {
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([
Property20
], ChartOverlays.prototype, "darkTheme", 2);
__decorateClass([
Property20
], ChartOverlays.prototype, "loading", 2);
__decorateClass([
Property20
], ChartOverlays.prototype, "noData", 2);
__decorateClass([
Property20
], ChartOverlays.prototype, "noVisibleSeries", 2);
__decorateClass([
Property20
], ChartOverlays.prototype, "unsupportedBrowser", 2);
// packages/ag-charts-community/src/chart/overlay/loadingSpinner.ts
import { createElement as createElement11 } from "ag-charts-core";
function getLoadingSpinner(text, defaultDuration) {
const { animationDuration } = PHASE_METADATA["add"];
const duration = animationDuration * defaultDuration;
const container = createElement11("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 = createElement11("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 = createElement11("p", { marginTop: "1em" });
label.innerText = text;
const background = createElement11("div", `${DEFAULT_OVERLAY_CLASS}__loading-background`, {
position: "absolute",
inset: "0",
opacity: "0.5",
zIndex: "-1"
});
const animationStyles = createElement11("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
import { BaseProperties as BaseProperties14, Border, CleanupRegistry as CleanupRegistry13, Property as Property21, ProxyPropertyOnWrite as ProxyPropertyOnWrite3, ZIndexMap as ZIndexMap4 } from "ag-charts-core";
var SeriesArea = class extends BaseProperties14 {
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 CleanupRegistry13();
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: ZIndexMap4.SERIES_AREA_CONTAINER });
}
onLayoutComplete(event) {
const { x, y, width, height } = event.series.paddedRect;
this.rectNode.x = x;
this.rectNode.y = y;
this.rectNode.width = width;
this.rectNode.height = height;
}
};
__decorateClass([
Property21
], SeriesArea.prototype, "border", 2);
__decorateClass([
Property21
], SeriesArea.prototype, "clip", 2);
__decorateClass([
ProxyPropertyOnWrite3("rectNode", "cornerRadius"),
Property21
], SeriesArea.prototype, "cornerRadius", 2);
__decorateClass([
Property21
], SeriesArea.prototype, "padding", 2);
// packages/ag-charts-community/src/chart/series/series.ts
import {
ActionOnSet as ActionOnSet2,
ChartAxisDirection as ChartAxisDirection6,
CleanupRegistry as CleanupRegistry14,
EventEmitter as EventEmitter7,
LRUCache,
Logger as Logger27,
SeriesContentZIndexMap,
SeriesZIndexMap,
callWithContext as callWithContext3,
createId as createId6,
isEmptyObject,
isGradientFill as isGradientFill3,
isPatternFill as isPatternFill3,
jsonDiff as jsonDiff3,
mergeDefaults as mergeDefaults4,
nearestSquared as nearestSquared2,
without
} from "ag-charts-core";
// packages/ag-charts-community/src/chart/series/shapeUtil.ts
import {
isGradientFill as isGradientFill2,
isImageFill as isImageFill2,
isPatternFill as isPatternFill2
} from "ag-charts-core";
function getShapeFill(fill, defaultGradient, defaultPattern, defaultImage) {
if (isGradientFill2(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 (isPatternFill2(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 width = fill.width ?? fill.height ?? defaultPattern.width;
const height = fill.height ?? fill.width ?? defaultPattern.height;
return {
type: "pattern",
pattern,
width,
height,
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 (isImageFill2(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(style, defaultGradient, defaultPattern, defaultImage) {
if (!isGradientFill2(style?.fill) && !isPatternFill2(style?.fill) && !isImageFill2(style?.fill))
return style;
return {
...style,
fill: getShapeFill(style.fill, defaultGradient, defaultPattern, defaultImage)
};
}
// 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 ChartAxisDirection6.X;
case "y":
return ChartAxisDirection6.Y;
case "angle":
return ChartAxisDirection6.Angle;
case "radius":
return ChartAxisDirection6.Radius;
}
}
function axisDirectionProperty(direction) {
switch (direction) {
case ChartAxisDirection6.X:
return "x";
case ChartAxisDirection6.Y:
return "y";
case ChartAxisDirection6.Angle:
return "angle";
case ChartAxisDirection6.Radius:
return "radius";
default:
return "x";
}
}
var Series = class extends Observable {
constructor(seriesOpts) {
super();
this.cleanup = new CleanupRegistry14();
this.usesPlacedLabels = false;
this.alwaysClip = false;
this.hasChangesOnHighlight = false;
this.seriesGrouping = void 0;
this.NodeEvent = SeriesNodeEvent;
this.internalId = createId6(this);
// The group node that contains the series rendering in its default (non-highlighted) state.
this.contentGroup = new TranslatableGroup({
name: `${this.internalId}-content`,
zIndex: SeriesZIndexMap.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: SeriesZIndexMap.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: SeriesContentZIndexMap.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 = [ChartAxisDirection6.X, ChartAxisDirection6.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 EventEmitter7();
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 = [SeriesZIndexMap.ANY_CONTENT, zIndex, SeriesContentZIndexMap.FOREGROUND];
this.highlightGroup.zIndex = [SeriesZIndexMap.ANY_CONTENT, zIndex, SeriesContentZIndexMap.HIGHLIGHT];
this.labelGroup.zIndex = [SeriesZIndexMap.ANY_CONTENT, zIndex, SeriesContentZIndexMap.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((module) => module.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, style) {
return resolveMarkerDrawingMode(drawingMode, style);
}
filterItemStylerFillParams(fill) {
if (isGradientFill3(fill)) {
return without(fill, ["bounds", "colorSpace", "gradient", "reverse"]);
} else if (isPatternFill3(fill)) {
return without(fill, ["padding"]);
}
return fill;
}
getModuleTooltipParams() {
return this.moduleMap.mapModules((module) => module.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 = nearestSquared2(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) {
callWithContext3([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 = mergeDefaults4(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 style = this.cachedCallWithContext(itemStyler, {
seriesId: this.id,
...markerStyle,
fill,
...params,
highlightState: highlightStateString,
datum
});
const resolved = this.ctx.optionsGraphService.resolvePartial(resolvePath2, style);
markerStyle = mergeDefaults4(resolved, markerStyle);
}
return markerStyle;
}
applyMarkerStyle(style, markerNode, point, fillBBox, { applyTranslation = true, selected = true } = {}) {
const { shape, size = 0 } = style;
const visible = this.visible && size > 0 && point && !Number.isNaN(point.x) && !Number.isNaN(point.y);
markerNode.setStyleProperties(style, 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 center = bb.computeCenter();
const [dx, dy] = ["x", "y"].map(
(key) => (style.strokeWidth ?? 0) + Math.abs(center[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 = jsonDiff3(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 (error) {
Logger27.error(String(error));
}
}
cachedCallWithContext(fn, params) {
return this.ctx.callbackCache.call([this.properties, this.ctx.chartService], fn, params);
}
callWithContext(fn, params) {
return callWithContext3([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([
ActionOnSet2({
changeValue: function(newVal, oldVal) {
this.onSeriesGroupingChange(oldVal, newVal);
}
})
], Series.prototype, "seriesGrouping", 2);
// packages/ag-charts-community/src/chart/series/seriesAreaManager.ts
import { ChartUpdateType as ChartUpdateType5, Logger as Logger28, Vec4 as Vec42, clamp as clamp14, createId as createId7 } from "ag-charts-core";
// packages/ag-charts-community/src/dom/focusIndicator.ts
import { createElement as createElement12, createSvgElement as createSvgElement13, getWindow as getWindow13, setElementBBox as setElementBBox2 } from "ag-charts-core";
var FocusIndicator = class {
constructor(swapChain) {
this.swapChain = swapChain;
this.hasBeenActivated = false;
this.div = createElement12("div");
this.svg = createSvgElement13("svg");
this.outerPath = createSvgElement13("path");
this.innerPath = createSvgElement13("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 = createElement12("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);
}
setElementBBox2(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 && getWindow13().getComputedStyle(parent).opacity === "1";
}
};
// packages/ag-charts-community/src/dom/focusSwapChain.ts
import {
createElement as createElement13,
createElementId,
setAttribute as setAttribute10,
setAttributes,
setElementStyle as setElementStyle4
} from "ag-charts-core";
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) => {
setElementStyle4(e.target, "pointer-events", void 0);
return !this.skipDispatch && this.dispatch("blur", e);
};
this.onFocus = (e) => {
setElementStyle4(e.target, "pointer-events", "auto");
return !this.skipDispatch && this.dispatch("focus", e);
};
setAttribute10(this.label1, "id", createElementId());
setAttribute10(this.label2, "id", createElementId());
setElementStyle4(this.label1, "display", "none");
setElementStyle4(this.label2, "display", "none");
this.label1.textContent = initialAltText;
this.label2.textContent = initialAltText;
this.activeAnnouncer = this.createAnnouncer(announcerRole);
this.inactiveAnnouncer = this.createAnnouncer(announcerRole);
setAttribute10(this.activeAnnouncer, "tabindex", 0);
this.label2.insertAdjacentElement("afterend", this.activeAnnouncer);
this.label2.insertAdjacentElement("afterend", this.inactiveAnnouncer);
this.swap(initialAltText);
}
createAnnouncer(role) {
const announcer = createElement13("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
import { entries as entries3 } from "ag-charts-core";
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 entries3(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/tooltip/tooltip.ts
import {
BaseProperties as BaseProperties15,
CleanupRegistry as CleanupRegistry15,
Property as Property22,
calculatePlacement,
clamp as clamp13,
getWindow as getWindow14,
isNode
} from "ag-charts-core";
// packages/ag-charts-community/src/chart/tooltip/springAnimation.ts
import { EventEmitter as EventEmitter8 } from "ag-charts-core";
var M = 0.1;
var K = 200;
var C = 12;
var DELTA = 0.5;
var SpringAnimation = class {
constructor() {
this.events = new EventEmitter8();
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/chart/tooltip/tooltipContent.ts
import { toPlainText as toPlainText6, toTextString as toTextString7 } from "ag-charts-core";
// packages/ag-charts-community/src/util/sanitize.ts
import { createElement as createElement14, toPlainText as toPlainText5 } from "ag-charts-core";
var element = null;
function sanitizeHtml(text) {
const plainText = toPlainText5(text);
if (plainText === "")
return "";
element ?? (element = createElement14("div"));
element.textContent = plainText;
return element.innerHTML.replaceAll("\n", "<br>");
}
// packages/ag-charts-community/src/chart/marker/marker.ts
import { DeclaredSceneChangeDetection as DeclaredSceneChangeDetection4, DeclaredSceneObjectChangeDetection as DeclaredSceneObjectChangeDetection2, TRIPLE_EQ as TRIPLE_EQ2 } from "ag-charts-core";
// packages/ag-charts-community/src/chart/marker/shapes.ts
import { toRadians as toRadians3 } from "ag-charts-core";
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, toRadians3(130), toRadians3(330));
path.arc(x + r, y - r, r, toRadians3(220), toRadians3(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 angle = i * Math.PI / spikes - rotation;
const xCoordinate = x + Math.cos(angle) * radius;
const yCoordinate = y + Math.sin(angle) * 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([
DeclaredSceneObjectChangeDetection2({ equals: TRIPLE_EQ2 })
], InternalMarker.prototype, "shape", 2);
__decorateClass([
DeclaredSceneChangeDetection4()
], InternalMarker.prototype, "x", 2);
__decorateClass([
DeclaredSceneChangeDetection4()
], InternalMarker.prototype, "y", 2);
__decorateClass([
DeclaredSceneChangeDetection4({ 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/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 width = Math.max(symbol.marker.enabled === false ? 0 : size, symbol.line == null ? 0 : lineSize);
const height = 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 = height / 2;
line.x2 = width;
line.y2 = height / 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 = width / 2 + (anchor.x - 0.5) * size;
const y = height / 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, width, height);
}
// 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 toTextString7(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(toPlainText6(c.heading));
}
for (const i of c.items) {
if (textOrSegmentsIsDefined(i.title)) {
ariaLabel.push(toPlainText6(i.title));
}
if (i.data) {
for (const datum of i.data) {
if (datum.missing === true)
continue;
ariaLabel.push(datum.label ?? datum.fallbackLabel, toPlainText6(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, toPlainText6(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 = toPlainText6(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 = toPlainText6(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, toPlainText6(datum.value), false);
html += " ";
}
}
} else {
if (textOrSegmentsIsDefined(content.heading)) {
html += `<span class="${DEFAULT_TOOLTIP_CLASS}-heading">${sanitizeHtml(toPlainText6(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 BaseProperties15 {
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([
Property22
], TooltipPosition.prototype, "xOffset", 2);
__decorateClass([
Property22
], TooltipPosition.prototype, "yOffset", 2);
__decorateClass([
Property22
], TooltipPosition.prototype, "anchorTo", 2);
__decorateClass([
Property22
], TooltipPosition.prototype, "placement", 2);
var Tooltip = class extends BaseProperties15 {
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 CleanupRegistry15();
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 getWindow14().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 = clamp13(minX, position.x, maxX);
const top = clamp13(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 }, callback4) {
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: callback4,
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([
Property22
], Tooltip.prototype, "enabled", 2);
__decorateClass([
Property22
], Tooltip.prototype, "mode", 2);
__decorateClass([
Property22
], Tooltip.prototype, "showArrow", 2);
__decorateClass([
Property22
], Tooltip.prototype, "delay", 2);
__decorateClass([
Property22
], Tooltip.prototype, "range", 2);
__decorateClass([
Property22
], Tooltip.prototype, "wrapping", 2);
__decorateClass([
Property22
], Tooltip.prototype, "position", 2);
__decorateClass([
Property22
], Tooltip.prototype, "pagination", 2);
__decorateClass([
Property22
], Tooltip.prototype, "darkTheme", 2);
__decorateClass([
Property22
], Tooltip.prototype, "bounds", 2);
// packages/ag-charts-community/src/chart/series/pickManager.ts
import { objectsEqual as objectsEqual9 } from "ag-charts-core";
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 && objectsEqual9(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 = createId7(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() <= ChartUpdateType5.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(ChartUpdateType5.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 distance = 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 = distance === 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 = clamp14(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 } = Vec42.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 = Vec42.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: range4 } = this.chart.highlight;
const intent = range4 === "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(ChartUpdateType5.SERIES_UPDATE, { clearCallbackCache: true });
} else {
this.update(ChartUpdateType5.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 } = pick2;
if (pick2.datums.length === 0)
continue;
if (distance === 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 > distance) {
const [datum] = datums;
const { datumIndex } = datum;
result = { matches: [{ series, datum, datumIndex }], distance };
}
}
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) {
Logger28.warn(`Cannot find seriesId: "${desiredSeriesId}"`);
return void 0;
}
const desiredDatum = desiredSeries.findNodeDatum(desiredItemId);
if (desiredDatum == void 0) {
Logger28.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
import { SeriesZIndexMap as SeriesZIndexMap2, clamp as clamp15 } from "ag-charts-core";
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(
clamp15(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 ?? SeriesZIndexMap2.ANY_CONTENT;
if (lowestSeriesZIndex == null || zIndex == null) {
lowestSeriesZIndex = zIndex;
continue;
}
lowestSeriesZIndex = compareZIndex(lowestSeriesZIndex, zIndex) <= 0 ? lowestSeriesZIndex : zIndex;
}
return lowestSeriesZIndex ?? SeriesZIndexMap2.ANY_CONTENT;
}
};
// packages/ag-charts-community/src/chart/touch.ts
import { BaseProperties as BaseProperties16, Property as Property23 } from "ag-charts-core";
var Touch = class extends BaseProperties16 {
constructor() {
super(...arguments);
this.dragAction = "drag";
}
};
__decorateClass([
Property23
], Touch.prototype, "dragAction", 2);
// packages/ag-charts-community/src/chart/update/dataWindowProcessor.ts
import { ChartUpdateType as ChartUpdateType6, CleanupRegistry as CleanupRegistry16 } from "ag-charts-core";
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 CleanupRegistry16();
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(ChartUpdateType6.UPDATE_DATA);
}
onDataError() {
this.updateService.update(ChartUpdateType6.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/chart/update/overlaysProcessor.ts
import { CleanupRegistry as CleanupRegistry17, setAttribute as setAttribute11 } from "ag-charts-core";
// packages/ag-charts-community/src/util/browser.ts
import { Logger as Logger29, getWindow as getWindow15 } from "ag-charts-core";
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 } = getWindow15("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) {
Logger29.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) {
Logger29.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 CleanupRegistry17();
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;
setAttribute11(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 = Debug13.create(true, "opts");
var _Chart = class _Chart extends Observable {
constructor(options, resources) {
var _a;
super();
this.id = createId8(this);
this.seriesRoot = new TranslatableGroup({
name: `${this.id}-series-root`,
zIndex: ZIndexMap5.SERIES_LAYER
});
this.annotationRoot = new TranslatableGroup({
name: `${this.id}-annotation-root`,
zIndex: ZIndexMap5.SERIES_ANNOTATION
});
this.titleGroup = new Group({
name: "titles",
zIndex: ZIndexMap5.SERIES_LABEL
});
this.debug = Debug13.create(true, "chart");
this.extraDebugStats = {};
this.data = DataSet.empty();
this._firstAutoSize = true;
this._autoSizeNotify = new AsyncAwaitQueue();
this._requiredRange = 0;
this._requiredRangeDirection = ChartAxisDirection7.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 CleanupRegistry18();
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 = ChartUpdateType7.NONE;
this.runningUpdateType = ChartUpdateType7.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) => Logger30.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(ChartUpdateType7.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(ChartUpdateType7.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) {
callWithContext4(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 = ChartUpdateType7.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) => Logger30.errorOnce(e));
}
clearCallbackCache() {
this.ctx.callbackCache.invalidateCache();
for (const series of this.series) {
series.resetDatumCallbackCache();
}
}
update(type = ChartUpdateType7.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 === ChartUpdateType7.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 (error) {
Logger30.error("update error", error, error.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 = ChartUpdateType7.NONE;
this.seriesToUpdate.clear();
this.runningUpdateType = performUpdateType;
if (this.updateShortcutCount === 0 && performUpdateType < ChartUpdateType7.SCENE_RENDER) {
ctx.animationManager.startBatch(this._performUpdateSkipAnimations);
ctx.animationManager.onBatchStop(() => this.chartAnimationPhase = "ready");
}
this.ctx.scene.updateDebugFlags();
this.debug("Chart.performUpdate() - start", ChartUpdateType7[performUpdateType]);
this._previousSplit = performance.now();
splits.start ?? (splits.start = this._previousSplit);
switch (performUpdateType) {
case ChartUpdateType7.FULL:
if (this.checkUpdateShortcut(ChartUpdateType7.FULL))
break;
this.ctx.updateService.dispatchPreDomUpdate();
this.updateDOM();
case ChartUpdateType7.UPDATE_DATA:
if (this.checkUpdateShortcut(ChartUpdateType7.UPDATE_DATA))
break;
this.updateData();
this.updateSplits("\u2B07\uFE0F");
case ChartUpdateType7.PROCESS_DATA:
if (this.checkUpdateShortcut(ChartUpdateType7.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 ChartUpdateType7.PROCESS_DOMAIN:
if (this.checkUpdateShortcut(ChartUpdateType7.PROCESS_DOMAIN))
break;
await this.processDomains();
this.updateSplits("\u26F0\uFE0F");
case ChartUpdateType7.PROCESS_RANGE:
if (this.checkUpdateShortcut(ChartUpdateType7.PROCESS_RANGE))
break;
this.processRanges();
this.updateSplits("\u{1F4D0}");
case ChartUpdateType7.PERFORM_LAYOUT:
await this.checkFirstAutoSize();
if (this.checkUpdateShortcut(ChartUpdateType7.PERFORM_LAYOUT))
break;
await this.processLayout();
this.updateSplits("\u2316");
case ChartUpdateType7.PRE_SERIES_UPDATE:
if (this.checkUpdateShortcut(ChartUpdateType7.PRE_SERIES_UPDATE))
break;
this.preSeriesUpdate();
this.updateSplits("\u2753");
case ChartUpdateType7.SERIES_UPDATE: {
if (this.checkUpdateShortcut(ChartUpdateType7.SERIES_UPDATE))
break;
this.seriesRoot.renderToOffscreenCanvas = this.highlight.drawingMode === "cutout";
await this.updateSeries(seriesToUpdate);
this.updateAriaLabels();
this.seriesLayerManager.updateLayerCompositing();
this.updateSplits("\u{1F914}");
}
case ChartUpdateType7.PRE_SCENE_RENDER:
if (this.checkUpdateShortcut(ChartUpdateType7.PRE_SCENE_RENDER))
break;
ctx.updateService.dispatchPreSceneRender();
this.updateSplits("\u2196");
case ChartUpdateType7.SCENE_RENDER:
if (this.checkUpdateShortcut(ChartUpdateType7.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 ChartUpdateType7.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 = ChartUpdateType7.NONE;
this.syncStatus = "ready";
}
this._performUpdateNotify.notify();
const end2 = performance.now();
this.debug("Chart.performUpdate() - end", {
chart: this,
durationMs: roundTo(end2 - splits["start"]),
count,
performUpdateType: ChartUpdateType7[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) {
Logger30.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: ", ChartUpdateType7[checkUpdateType]);
this.updateShortcutCount++;
return true;
}
this.debug("Chart.checkUpdateShortcut() - PROCEEDING TO: ", ChartUpdateType7[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) {
Logger30.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, height } = size;
const { pixelRatio } = size;
width = Math.floor(width);
height = Math.floor(height);
if (width === 0 && height === 0)
return;
const [autoWidth = 0, autoHeight = 0, autoPixelRatio = 1] = this._lastAutoSize ?? [];
if (autoWidth === width && autoHeight === height && autoPixelRatio === pixelRatio)
return;
this._lastAutoSize = [width, height, 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 width = inWidth ?? this.width ?? this._lastAutoSize?.[0];
const height = inHeight ?? this.height ?? this._lastAutoSize?.[1];
const pixelRatio = inOverrideDevicePixelRatio ?? this.overrideDevicePixelRatio ?? this._lastAutoSize?.[2];
this.debug(`Chart.resize() from ${source}`, {
width,
height,
pixelRatio,
stack: new Error("Stack trace for resize tracking").stack
});
if (width == null || height == null || !isFiniteNumber4(width) || !isFiniteNumber4(height))
return;
if (scene.resize(width, height, pixelRatio)) {
animationManager.reset();
let skipAnimations = true;
if ((this.width == null || this.height == null) && this._firstAutoSize) {
skipAnimations = false;
this._firstAutoSize = false;
}
let updateType = ChartUpdateType7.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 module of this.modulesManager.modules()) {
if (module?.processData) {
promises.push(module.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 = ChartAxisDirection7.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(ChartAxisDirection7.X) === ChartAxisDirection7.Y) {
this._requiredRangeDirection = ChartAxisDirection7.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 module of ModuleRegistry5.listModulesByType(ModuleType4.Plugin)) {
switch (module.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) {
Logger30.warnOnce(
`legend item '${toPlainText7(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, height } = this.ctx.scene;
const ctx = this.ctx.layoutManager.createContext(width, height);
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 color8 = Color4.fromString(bg);
const [lightness] = Color4.RGBtoOKLCH(color8.r, color8.g, color8.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 === ChartAxisDirection7.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 = getWindow16("agChartsDebugTimeout");
if (agChartsDebugTimeout == null) {
timeoutMs ?? (timeoutMs = 1e4);
failOnTimeout ?? (failOnTimeout = false);
} else {
timeoutMs = agChartsDebugTimeout;
failOnTimeout ?? (failOnTimeout = true);
}
const start = performance.now();
while (this._pendingFactoryUpdatesCount > 0 || this.performUpdateType !== ChartUpdateType7.NONE || this.runningUpdateType !== ChartUpdateType7.NONE || this.ctx.scene.waitingForUpdate() || this.data.hasPendingTransactions()) {
if (this.destroyed)
break;
if (this._pendingFactoryUpdatesCount > 0) {
await this.updateMutex.waitForClearAcquireQueue();
}
if (this.performUpdateType !== ChartUpdateType7.NONE || this.runningUpdateType !== ChartUpdateType7.NONE || this.data.hasPendingTransactions()) {
await this._performUpdateNotify.waitForCompletion();
}
if (performance.now() - start > timeoutMs) {
const message = `Chart.waitForUpdate() timeout of ${timeoutMs} reached - first chart update taking too long.`;
if (failOnTimeout) {
throw new Error(message);
} else {
Logger30.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 = ChartUpdateType7.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 ? ChartUpdateType7.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", ChartUpdateType7[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 mergeDefaults5(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 === ChartAxisDirection7.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(
without2(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 module of ModuleRegistry5.listModulesByType(ModuleType4.Plugin)) {
const shouldBeEnabled = !module.chartType || module.chartType === chartType;
if (shouldBeEnabled === this.modulesManager.isEnabled(module.name))
continue;
if (shouldBeEnabled) {
const moduleInstance = module.create(this.getModuleContext());
this.modulesManager.addModule(module.name, moduleInstance);
this[module.name] = moduleInstance;
} else {
this.modulesManager.removeModule(module.name);
delete this[module.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 = jsonDiff4(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 = ModuleRegistry5.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 module of ModuleRegistry5.listModulesByType(ModuleType4.SeriesPlugin)) {
if (module.name in options && (module.seriesTypes?.includes(series.type) ?? true)) {
moduleMap.addModule(module.name, module.create(moduleContext));
}
}
}
applySeriesValues(target, options) {
const moduleMap = target.getModuleMap();
const { type, data, listeners, seriesGrouping, showInMiniChart, ...seriesOptions } = options;
for (const module of ModuleRegistry5.listModulesByType(ModuleType4.SeriesPlugin)) {
if (module.name in seriesOptions) {
const moduleInstance = moduleMap.getModule(module.name);
if (moduleInstance) {
const moduleOptions = seriesOptions[module.name];
moduleInstance.properties.set(moduleOptions);
delete seriesOptions[module.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 entries4(options)) {
const axis = ModuleRegistry5.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 module of ModuleRegistry5.listModulesByType(ModuleType4.AxisPlugin)) {
const shouldBeEnabled = options[module.name] != null;
if (shouldBeEnabled === moduleMap.isEnabled(module.name))
continue;
if (shouldBeEnabled) {
moduleMap.addModule(module.name, module.create(moduleContext));
axis[module.name] = moduleMap.getModule(module.name);
} else {
moduleMap.removeModule(module.name);
delete axis[module.name];
}
}
}
registerListeners(source, listeners) {
source.clearEventListeners();
if (listeners && typeof listeners === "object") {
for (const [property, listener] of entries4(listeners)) {
if (listener == null) {
continue;
}
source.addEventListener(property, listener);
}
}
}
async applyTransaction(transaction) {
await this.updateMutex.acquire(() => {
this.data.addTransaction(transaction);
this.update(ChartUpdateType7.UPDATE_DATA, {
apiUpdate: true,
skipAnimations: true
});
});
await this.waitForUpdate();
}
onSyncActiveClear() {
this.seriesAreaManager.onActiveClear();
}
};
_Chart.className = "Chart";
_Chart.chartsInstances = /* @__PURE__ */ new WeakMap();
__decorateClass([
ActionOnSet3({
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([
ActionOnSet3({
newValue(value) {
this.resize("width option", { inWidth: value });
}
})
], _Chart.prototype, "width", 2);
__decorateClass([
ActionOnSet3({
newValue(value) {
this.resize("height option", { inHeight: value });
}
})
], _Chart.prototype, "height", 2);
__decorateClass([
ActionOnSet3({
newValue(value) {
this.resize("minWidth option", { inMinWidth: value });
}
})
], _Chart.prototype, "minWidth", 2);
__decorateClass([
ActionOnSet3({
newValue(value) {
this.resize("minHeight option", { inMinHeight: value });
}
})
], _Chart.prototype, "minHeight", 2);
__decorateClass([
ActionOnSet3({
newValue(value) {
this.resize("overrideDevicePixelRatio option", { inOverrideDevicePixelRatio: value });
}
})
], _Chart.prototype, "overrideDevicePixelRatio", 2);
__decorateClass([
Property24
], _Chart.prototype, "padding", 2);
__decorateClass([
Property24
], _Chart.prototype, "keyboard", 2);
__decorateClass([
Property24
], _Chart.prototype, "touch", 2);
__decorateClass([
Property24
], _Chart.prototype, "mode", 2);
__decorateClass([
Property24
], _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([
Property24
], _Chart.prototype, "formatter", 2);
__decorateClass([
Property24
], _Chart.prototype, "suppressFieldDotNotation", 2);
__decorateClass([
Property24
], _Chart.prototype, "loadGoogleFonts", 2);
__decorateClass([
ActionOnSet3({
changeValue(newValue, oldValue) {
this.onAxisChange(newValue, oldValue);
}
})
], _Chart.prototype, "axes", 2);
__decorateClass([
ActionOnSet3({
changeValue(newValue, oldValue) {
this.onSeriesChange(newValue, oldValue);
}
})
], _Chart.prototype, "series", 2);
var Chart = _Chart;
// packages/ag-charts-community/src/chart/chartProxy.ts
import {
ActionOnSet as ActionOnSet4,
ChartUpdateType as ChartUpdateType8,
Debug as Debug18,
ModuleRegistry as ModuleRegistry11,
deepClone as deepClone6,
getDocument as getDocument6
} from "ag-charts-core";
// packages/ag-charts-community/src/module/optionsModule.ts
import {
ChartAxisDirection as ChartAxisDirection8,
Debug as Debug17,
Logger as Logger34,
ModuleRegistry as ModuleRegistry10,
ModuleType as ModuleType7,
deepClone as deepClone5,
deepFreeze as deepFreeze5,
distribute,
entries as entries5,
getDocument as getDocument5,
getWindow as getWindow17,
groupBy as groupBy3,
hasRequiredInPath,
isArray as isArray14,
isKeyOf,
isObject as isObject8,
isPlainObject as isPlainObject9,
isSymbol as isSymbol2,
joinFormatted,
jsonDiff as jsonDiff5,
jsonPropertyCompare,
jsonWalk as jsonWalk2,
mapValues as mapValues2,
merge,
mergeDefaults as mergeDefaults8,
setDocument,
setWindow,
shallowClone,
unique as unique2,
validate as validate4
} from "ag-charts-core";
// packages/ag-charts-community/src/chart/factory/processModuleOptions.ts
import {
Logger as Logger31,
ModuleRegistry as ModuleRegistry7,
ModuleType as ModuleType6,
deepClone as deepClone4,
deepFreeze as deepFreeze4,
groupBy as groupBy2,
isArray as isArray12,
isDefined,
isObject as isObject4,
unique
} from "ag-charts-core";
// packages/ag-charts-community/src/chart/themes/chartTheme.ts
import {
BASE_FONT_SIZE,
CARTESIAN_AXIS_TYPE,
Color as Color5,
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,
FONT_SIZE_RATIO,
IS_DARK_THEME,
ModuleRegistry as ModuleRegistry6,
ModuleType as ModuleType5,
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,
POLAR_AXIS_TYPE,
deepClone as deepClone3,
deepFreeze as deepFreeze3,
getSequentialColors,
groupBy,
isArray as isArray11,
jsonWalk,
mergeDefaults as mergeDefaults6
} from "ag-charts-core";
// 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/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/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: Color5.interpolate(
[
Color5.fromHexString(DEFAULT_FILLS.BLUE),
Color5.fromHexString("#cbdef5")
// TODO: Color.lighten(DEFAULT_FILLS.BLUE, ?)
],
8
).map((color8) => color8.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 mergeDefaults6(
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 } = deepClone3(options);
const defaults = this.createChartConfigPerChartType(this.getDefaults());
const presets = {};
if (overrides) {
this.processOverrides(presets, overrides);
}
const { fills, strokes, sequentialColors, ...otherColors } = this.getDefaultColors();
this.palette = deepFreeze3(
mergeDefaults6(palette, {
fills: Object.values(fills),
strokes: Object.values(strokes),
sequentialColors: Object.values(sequentialColors),
...otherColors
})
);
this.paletteType = paletteType(palette);
this.params = mergeDefaults6(params, this.getPublicParameters());
this.config = deepFreeze3(deepClone3(defaults));
this.overrides = deepFreeze3(overrides);
this.presets = deepFreeze3(presets);
}
processOverrides(presets, overrides) {
for (const s of ModuleRegistry6.listModulesByType(ModuleType5.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 ModuleRegistry6.listModulesByType(ModuleType5.Chart)) {
for (const seriesModule of ModuleRegistry6.listModulesByType(ModuleType5.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 = mergeDefaults6(
{ axes: {} },
...Array.from(ModuleRegistry6.listModulesByType(ModuleType5.Plugin), (p) => ({
[p.name]: p.themeTemplate
})),
ModuleRegistry6.getChartModule(chartType)?.themeTemplate,
this.getChartDefaults()
);
for (const seriesType of seriesTypes) {
result[seriesType] = mergeDefaults6(
getSeriesThemeTemplate(seriesType),
result[seriesType] ?? chartTypeDefaults
);
const { axes } = result[seriesType];
for (const axisModule of ModuleRegistry6.listModulesByType(ModuleType5.Axis)) {
axes[axisModule.name] = mergeDefaults6(
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 = [...ModuleRegistry6.listModulesByType(ModuleType5.Series)];
const seriesByChartType = groupBy(seriesModules, (s) => s.chartType || "unknown");
return mergeDefaults6(
...Object.keys(seriesByChartType).map(
(chartType) => getOverridesByType(chartType, seriesByChartType[chartType]?.map((s) => s.name) ?? [])
)
);
}
static applyTemplateTheme(node, _other, params) {
if (isArray11(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, clone = true) {
const themeInstance = clone ? deepClone3(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 = {
[CARTESIAN_AXIS_TYPE.NUMBER]: _ChartTheme.getAxisDefaults({ title: true, time: false }),
[CARTESIAN_AXIS_TYPE.LOG]: _ChartTheme.getAxisDefaults({ title: true, time: false }),
[CARTESIAN_AXIS_TYPE.CATEGORY]: _ChartTheme.getAxisDefaults({ title: true, time: false }),
[CARTESIAN_AXIS_TYPE.GROUPED_CATEGORY]: _ChartTheme.getAxisDefaults({ title: true, time: false }),
[CARTESIAN_AXIS_TYPE.TIME]: _ChartTheme.getAxisDefaults({ title: true, time: true }),
[CARTESIAN_AXIS_TYPE.UNIT_TIME]: _ChartTheme.getAxisDefaults({ title: true, time: true }),
[CARTESIAN_AXIS_TYPE.ORDINAL_TIME]: _ChartTheme.getAxisDefaults({ title: true, time: true }),
[POLAR_AXIS_TYPE.ANGLE_CATEGORY]: _ChartTheme.getAxisDefaults({ title: false, time: false }),
[POLAR_AXIS_TYPE.ANGLE_NUMBER]: _ChartTheme.getAxisDefaults({ title: false, time: false }),
[POLAR_AXIS_TYPE.RADIUS_CATEGORY]: _ChartTheme.getAxisDefaults({ title: true, time: false }),
[POLAR_AXIS_TYPE.RADIUS_NUMBER]: _ChartTheme.getAxisDefaults({ title: true, time: false })
};
var ChartTheme = _ChartTheme;
function getAxisThemeTemplate(axisType) {
let themeTemplate7 = ModuleRegistry6.getAxisModule(axisType)?.themeTemplate ?? {};
for (const module of ModuleRegistry6.listModulesByType(ModuleType5.AxisPlugin)) {
if (module.axisTypes?.includes(axisType) ?? true) {
themeTemplate7 = mergeDefaults6({ [module.name]: module.themeTemplate }, themeTemplate7);
}
}
return themeTemplate7;
}
function getSeriesThemeTemplate(seriesType) {
let themeTemplate7 = ModuleRegistry6.getSeriesModule(seriesType)?.themeTemplate ?? {};
for (const module of ModuleRegistry6.listModulesByType(ModuleType5.SeriesPlugin)) {
if (module.seriesTypes?.includes(seriesType) ?? true) {
themeTemplate7 = mergeDefaults6({ series: { [module.name]: module.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 (ModuleRegistry7.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(ModuleType6.Plugin);
if (!isObject4(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(ModuleType6.SeriesPlugin);
if (!isObject4(target) || !missingSeriesPlugins)
return;
for (const pluginName of missingSeriesPlugins) {
if (pluginName in target) {
delete target[pluginName];
}
}
}
function pruneAxisPlugins(target) {
const missingAxisPlugins = missingModules.get(ModuleType6.AxisPlugin);
if (!isObject4(target) || !missingAxisPlugins)
return;
for (const pluginName of missingAxisPlugins) {
if (pluginName in target && target[pluginName].enabled !== true) {
delete target[pluginName];
}
}
}
function pruneAxes(axes) {
if (!isObject4(axes))
return;
for (const axisName of Object.keys(axes)) {
if (missingModules.get(ModuleType6.Axis)?.has(axisName)) {
delete axes[axisName];
continue;
}
pruneAxisPlugins(axes[axisName]);
}
}
function pruneSeriesEntry(entry) {
if (!isObject4(entry))
return;
pruneAxes(entry.axes);
prunePlugins(entry);
pruneSeriesPlugins(entry.series);
}
const config = deepClone4(theme.config);
const overrides = deepClone4(theme.overrides);
const presets = deepClone4(theme.presets);
for (const seriesType of Object.keys(config)) {
if (missingModules.get(ModuleType6.Series)?.has(seriesType)) {
delete config[seriesType];
continue;
}
pruneSeriesEntry(config[seriesType]);
}
if (isObject4(overrides)) {
const overridesObj = overrides;
if (isObject4(overridesObj.common)) {
pruneAxes(overridesObj.common.axes);
prunePlugins(overridesObj.common);
}
for (const seriesType of Object.keys(overridesObj)) {
if (seriesType === "common")
continue;
if (missingModules.get(ModuleType6.Series)?.has(seriesType)) {
delete overridesObj[seriesType];
continue;
}
pruneSeriesEntry(overridesObj[seriesType]);
}
}
if (isObject4(presets)) {
const presetsObj = presets;
for (const presetName of Object.keys(presetsObj)) {
if (missingModules.get(ModuleType6.Preset)?.has(presetName) || missingModules.get(ModuleType6.Series)?.has(presetName)) {
delete presetsObj[presetName];
continue;
}
prunePlugins(presetsObj[presetName]);
pruneAxes(presetsObj[presetName]?.axes);
}
}
return Object.create(theme, {
config: { value: deepFreeze4(config), enumerable: true },
overrides: { value: isObject4(overrides) ? deepFreeze4(overrides) : overrides, enumerable: true },
presets: { value: isObject4(presets) ? deepFreeze4(presets) : presets, enumerable: true }
});
}
function processModuleOptions(chartType, options, additionalMissingModules) {
const missingModules = unique(removeUnregisteredModuleOptions(chartType, options).concat(additionalMissingModules));
if (!missingModules.length)
return;
const installationReferenceUrl = ModuleRegistry7.isIntegrated() ? "https://www.ag-grid.com/data-grid/integrated-charts-installation/" : "https://www.ag-grid.com/charts/r/module-registry/";
const missingOptions = groupBy2(missingModules, (module) => module.enterprise ? "enterprise" : "community");
if (missingModules.length) {
const packageName = ModuleRegistry7.isEnterprise() || missingOptions.enterprise?.length ? "enterprise" : "community";
Logger31.errorOnce(
[
"required modules are not registered. Check if you have registered the modules:",
"",
ModuleRegistry7.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(module) {
return module.moduleId ?? module.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 && isObject4(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(module) {
missingModules.set(module.name, module);
}
for (const module of ExpectedModules.values()) {
if (ModuleRegistry7.hasModule(module.name))
continue;
if (SkippedModules.has(module.name))
continue;
if (chartType && module.chartType && chartType !== module.chartType)
continue;
switch (module.type) {
case "chart":
break;
case "axis":
if (axisTypesInOptions.has(module.name)) {
for (const key of Object.keys(optionsAxes)) {
if (optionsAxes?.[key].type === module.name) {
delete optionsAxes[key];
}
}
addMissingModule(module);
}
break;
case "series":
if (seriesTypesInOptions.has(module.name)) {
options.series = options.series.filter((series) => series.type !== module.name);
addMissingModule(module);
}
break;
case "plugin":
const optionsKey = module.name;
const pluginValue = options[optionsKey];
if (isObject4(pluginValue)) {
if (pluginValue.enabled !== false) {
addMissingModule(module);
}
delete options[optionsKey];
}
break;
case "axis:plugin":
for (const axis of Object.values(optionsAxes)) {
const axisModuleKey = module.name;
if (axis?.[axisModuleKey]) {
if (axis[axisModuleKey].enabled !== false) {
addMissingModule(module);
}
delete axis[axisModuleKey];
}
}
break;
case "series:plugin":
for (const series of options.series ?? []) {
if (series[module.name]) {
delete series[module.name];
addMissingModule(module);
}
}
break;
}
}
for (const seriesType of seriesTypesInOptions) {
const expectedSeriesModule = ExpectedModules.get(seriesType);
if (expectedSeriesModule?.type === ModuleType6.Series && !ModuleRegistry7.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 && isObject4(options.axes);
const hasSeriesOptions = "series" in options && isArray12(options.series);
const matchChartType = (module) => chartType == null || !module.chartType || module.chartType === chartType;
const incompatibleModules = [];
for (const module of ModuleRegistry7.listModules()) {
if (ModuleRegistry7.isModuleType(ModuleType6.Plugin, module)) {
if (!matchChartType(module)) {
delete options[module.name];
incompatibleModules.push(module.name);
}
} else if (ModuleRegistry7.isModuleType(ModuleType6.AxisPlugin, module)) {
if (hasAxesOptions && !matchChartType(module)) {
for (const axis of Object.values(options.axes)) {
delete axis[module.name];
}
incompatibleModules.push(module.name);
}
} else if (ModuleRegistry7.isModuleType(ModuleType6.SeriesPlugin, module)) {
if (hasSeriesOptions && !matchChartType(module)) {
for (const series of options.series) {
delete series[module.name];
}
incompatibleModules.push(module.name);
}
}
}
return incompatibleModules;
}
// packages/ag-charts-community/src/chart/mapping/themes.ts
import {
Debug as Debug14,
Logger as Logger32,
arrayOf as arrayOf8,
boolean as boolean14,
color as color6,
colorUnion as colorUnion3,
fontFamilyFull,
fontWeight,
gradientStrict,
isObject as isObject6,
mergeDefaults as mergeDefaults7,
number as number12,
object as object3,
or as or5,
simpleMemorize,
string as string14,
union as union11,
validate as validate3
} from "ag-charts-core";
// packages/ag-charts-community/src/chart/themes/darkTheme.ts
import {
DEFAULT_ANNOTATION_HANDLE_FILL as DEFAULT_ANNOTATION_HANDLE_FILL2,
DEFAULT_ANNOTATION_STATISTICS_COLOR as DEFAULT_ANNOTATION_STATISTICS_COLOR2,
DEFAULT_ANNOTATION_STATISTICS_DIVIDER_STROKE as DEFAULT_ANNOTATION_STATISTICS_DIVIDER_STROKE2,
DEFAULT_ANNOTATION_STATISTICS_FILL as DEFAULT_ANNOTATION_STATISTICS_FILL2,
DEFAULT_ANNOTATION_STATISTICS_STROKE as DEFAULT_ANNOTATION_STATISTICS_STROKE2,
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL as DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL2,
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR as DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR2,
DEFAULT_POLAR_SERIES_STROKE as DEFAULT_POLAR_SERIES_STROKE2,
DEFAULT_TEXTBOX_COLOR as DEFAULT_TEXTBOX_COLOR2,
DEFAULT_TEXTBOX_FILL as DEFAULT_TEXTBOX_FILL2,
DEFAULT_TEXTBOX_STROKE as DEFAULT_TEXTBOX_STROKE2,
DEFAULT_TEXT_ANNOTATION_COLOR as DEFAULT_TEXT_ANNOTATION_COLOR2,
IS_DARK_THEME as IS_DARK_THEME2,
getSequentialColors as getSequentialColors2
} from "ag-charts-core";
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: getSequentialColors2(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_THEME2, true);
params.set(DEFAULT_POLAR_SERIES_STROKE2, DEFAULT_DARK_BACKGROUND_FILL);
params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR2, DEFAULT_DARK_FILLS.BLUE);
params.set(DEFAULT_TEXT_ANNOTATION_COLOR2, "#fff");
params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL2, DEFAULT_DARK_FILLS.BLUE);
params.set(DEFAULT_ANNOTATION_HANDLE_FILL2, DEFAULT_DARK_BACKGROUND_FILL);
params.set(DEFAULT_ANNOTATION_STATISTICS_FILL2, "#28313e");
params.set(DEFAULT_ANNOTATION_STATISTICS_STROKE2, "#4b525d");
params.set(DEFAULT_ANNOTATION_STATISTICS_COLOR2, "#fff");
params.set(DEFAULT_ANNOTATION_STATISTICS_DIVIDER_STROKE2, "#fff");
params.set(DEFAULT_TEXTBOX_FILL2, "#28313e");
params.set(DEFAULT_TEXTBOX_STROKE2, "#4b525d");
params.set(DEFAULT_TEXTBOX_COLOR2, "#fff");
return params;
}
constructor(options) {
super(options);
}
};
// packages/ag-charts-community/src/chart/themes/financialDark.ts
import {
DEFAULT_CAPTION_ALIGNMENT as DEFAULT_CAPTION_ALIGNMENT2,
DEFAULT_CAPTION_LAYOUT_STYLE as DEFAULT_CAPTION_LAYOUT_STYLE2,
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL as DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL3,
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR as DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR3,
DEFAULT_TOOLBAR_POSITION as DEFAULT_TOOLBAR_POSITION2,
getSequentialColors as getSequentialColors3
} from "ag-charts-core";
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: getSequentialColors3(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_COLOR3, FINANCIAL_DARK_FILLS.BLUE);
params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL3, FINANCIAL_DARK_FILLS.BLUE);
params.set(DEFAULT_CAPTION_LAYOUT_STYLE2, "overlay");
params.set(DEFAULT_CAPTION_ALIGNMENT2, "left");
params.set(DEFAULT_TOOLBAR_POSITION2, "bottom");
return params;
}
};
// packages/ag-charts-community/src/chart/themes/financialLight.ts
import {
DEFAULT_CAPTION_ALIGNMENT as DEFAULT_CAPTION_ALIGNMENT3,
DEFAULT_CAPTION_LAYOUT_STYLE as DEFAULT_CAPTION_LAYOUT_STYLE3,
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL as DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL4,
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR as DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR4,
DEFAULT_TOOLBAR_POSITION as DEFAULT_TOOLBAR_POSITION3,
getSequentialColors as getSequentialColors4
} from "ag-charts-core";
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: getSequentialColors4(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_COLOR4, FINANCIAL_LIGHT_FILLS.BLUE);
params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL4, FINANCIAL_LIGHT_FILLS.BLUE);
params.set(DEFAULT_CAPTION_LAYOUT_STYLE3, "overlay");
params.set(DEFAULT_CAPTION_ALIGNMENT3, "left");
params.set(DEFAULT_TOOLBAR_POSITION3, "bottom");
return params;
}
};
// packages/ag-charts-community/src/chart/themes/materialDark.ts
import {
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL as DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL5,
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR as DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR5,
getSequentialColors as getSequentialColors5
} from "ag-charts-core";
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: getSequentialColors5(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_COLOR5, MATERIAL_DARK_FILLS.BLUE);
params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL5, MATERIAL_DARK_FILLS.BLUE);
return params;
}
};
// packages/ag-charts-community/src/chart/themes/materialLight.ts
import {
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL as DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL6,
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR as DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR6,
getSequentialColors as getSequentialColors6
} from "ag-charts-core";
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: getSequentialColors6(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_COLOR6, MATERIAL_LIGHT_FILLS.BLUE);
params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL6, MATERIAL_LIGHT_FILLS.BLUE);
return params;
}
};
// packages/ag-charts-community/src/chart/themes/polychromaDark.ts
import {
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL as DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL7,
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR as DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR7,
getSequentialColors as getSequentialColors7
} from "ag-charts-core";
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: getSequentialColors7(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_COLOR7, POLYCHROMA_DARK_FILLS.BLUE);
params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL7, POLYCHROMA_DARK_FILLS.BLUE);
return params;
}
};
// packages/ag-charts-community/src/chart/themes/polychromaLight.ts
import {
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL as DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL8,
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR as DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR8,
getSequentialColors as getSequentialColors8
} from "ag-charts-core";
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: getSequentialColors8(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_COLOR8, POLYCHROMA_LIGHT_FILLS.BLUE);
params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL8, POLYCHROMA_LIGHT_FILLS.BLUE);
return params;
}
};
// packages/ag-charts-community/src/chart/themes/sheetsDark.ts
import {
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL as DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL9,
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR as DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR9,
getSequentialColors as getSequentialColors9
} from "ag-charts-core";
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: getSequentialColors9({ ...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_COLOR9, SHEETS_DARK_FILLS.BLUE);
params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL9, SHEETS_DARK_FILLS.BLUE);
return params;
}
};
// packages/ag-charts-community/src/chart/themes/sheetsLight.ts
import {
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL as DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL10,
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR as DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR10,
getSequentialColors as getSequentialColors10
} from "ag-charts-core";
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: getSequentialColors10({ ...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_COLOR10, SHEETS_LIGHT_FILLS.BLUE);
params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL10, SHEETS_LIGHT_FILLS.BLUE);
return params;
}
};
// packages/ag-charts-community/src/chart/themes/themeOptionsDef.ts
import {
array as array3,
arrayOf as arrayOf7,
arrayOfDefs as arrayOfDefs6,
boolean as boolean13,
callbackOf as callbackOf4,
color as color5,
commonChartOptionsDefs as commonChartOptionsDefs2,
constant as constant11,
defined as defined4,
fillOptionsDef as fillOptionsDef8,
fontOptionsDef as fontOptionsDef6,
isFunction,
isObject as isObject5,
isSymbol,
lineDashOptionsDef as lineDashOptionsDef9,
linearGaugeSeriesThemeableOptionsDef,
linearGaugeTargetOptionsDef,
number as number11,
numberFormatValidator as numberFormatValidator4,
optionsDefs as optionsDefs3,
or as or4,
positiveNumber as positiveNumber11,
radialGaugeSeriesThemeableOptionsDef,
radialGaugeTargetOptionsDef,
ratio as ratio7,
required as required12,
string as string13,
strokeOptionsDef as strokeOptionsDef9,
textOrSegments as textOrSegments4,
themeOperator as themeOperator2,
undocumented as undocumented11,
union as union10,
unionSymbol,
without as without7
} from "ag-charts-core";
// packages/ag-charts-community/src/chart/axesOptionsDefs.ts
import {
and,
arrayLength,
arrayOf,
arrayOfDefs as arrayOfDefs2,
attachDescription as attachDescription2,
boolean as boolean2,
borderOptionsDef,
callback,
callbackDefs,
callbackOf,
color,
constant,
date,
defined as defined3,
fillOptionsDef,
fontOptionsDef,
greaterThan,
labelBoxOptionsDef,
lessThan,
lineDashOptionsDef,
number,
numberFormatValidator,
object as object2,
optionsDefs,
or as or2,
positiveNumber as positiveNumber2,
positiveNumberNonZero,
ratio,
required as required2,
string as string2,
strokeOptionsDef,
textOrSegments,
themeOperator,
undocumented as undocumented2,
union as union2
} from "ag-charts-core";
var timeIntervalUnit = union2("millisecond", "second", "minute", "hour", "day", "month", "year");
var timeIntervalDefs = {
unit: required2(timeIntervalUnit),
step: positiveNumberNonZero,
epoch: date,
utc: boolean2
};
timeIntervalDefs.every = callback;
var timeInterval = optionsDefs(timeIntervalDefs, "a time interval object");
var commonCrossLineLabelOptionsDefs = {
enabled: boolean2,
text: string2,
padding: number,
border: borderOptionsDef,
cornerRadius: number,
...fontOptionsDef,
...fillOptionsDef
};
var commonCrossLineOptionsDefs = attachDescription2(
{
enabled: boolean2,
type: required2(union2("line", "range")),
range: and(
attachDescription2((_, { options }) => options.type === "range", "crossLine type to be 'range'"),
arrayOf(defined3),
arrayLength(2, 2)
),
value: and(
attachDescription2((_, { options }) => options.type === "line", "crossLine type to be 'line'"),
defined3
),
label: commonCrossLineLabelOptionsDefs,
fill: color,
fillOpacity: ratio,
...strokeOptionsDef,
...lineDashOptionsDef
},
"cross-line options"
);
var cartesianCrossLineOptionsDefs = {
...commonCrossLineOptionsDefs,
label: {
...commonCrossLineLabelOptionsDefs,
position: union2(
"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: boolean2,
rotation: number,
avoidCollisions: boolean2,
minSpacing: positiveNumber2,
spacing: positiveNumber2,
formatter: callbackOf(textOrSegments),
itemStyler: callbackDefs({
...fontOptionsDef,
...labelBoxOptionsDef,
spacing: number
}),
...fontOptionsDef,
...labelBoxOptionsDef
};
var cartesianAxisLabelOptionsDefs = {
autoRotate: boolean2,
autoRotateAngle: number,
wrapping: union2("never", "always", "hyphenate", "on-space"),
truncate: boolean2,
...commonAxisLabelOptionsDefs
};
var cartesianNumericAxisLabel = {
format: numberFormatValidator,
...cartesianAxisLabelOptionsDefs
};
var cartesianTimeAxisLabel = {
format: or2(string2, object2),
...cartesianAxisLabelOptionsDefs
};
var cartesianAxisTick = {
enabled: boolean2,
width: positiveNumber2,
size: positiveNumber2,
stroke: color
};
var cartesianTimeAxisParentLevel = {
enabled: boolean2,
label: cartesianTimeAxisLabel,
tick: cartesianAxisTick
};
var commonAxisIntervalOptionsDefs = {
values: arrayOf(defined3),
minSpacing: positiveNumber2
};
var commonAxisOptionsDefs = {
reverse: boolean2,
gridLine: {
enabled: boolean2,
width: positiveNumber2,
style: arrayOfDefs2(
{
fill: color,
fillOpacity: positiveNumber2,
stroke: or2(color, themeOperator),
strokeWidth: positiveNumber2,
lineDash: arrayOf(positiveNumber2)
},
"a grid-line style object array"
)
},
interval: commonAxisIntervalOptionsDefs,
label: commonAxisLabelOptionsDefs,
line: {
enabled: boolean2,
width: positiveNumber2,
stroke: color
},
tick: cartesianAxisTick,
context: () => true
};
commonAxisOptionsDefs.layoutConstraints = undocumented2({
stacked: required2(boolean2),
align: required2(union2("start", "end")),
unit: required2(union2("percent", "px")),
width: required2(positiveNumber2)
});
var cartesianAxisOptionsDefs = {
...commonAxisOptionsDefs,
crossAt: {
value: required2(or2(number, date, string2, arrayOf(string2))),
sticky: boolean2
},
crossLines: arrayOfDefs2(cartesianCrossLineOptionsDefs, "a cross-line options array"),
position: union2("top", "right", "bottom", "left"),
thickness: positiveNumber2,
maxThicknessRatio: ratio,
title: {
enabled: boolean2,
text: textOrSegments,
spacing: positiveNumber2,
formatter: callbackOf(textOrSegments),
...fontOptionsDef
}
};
cartesianAxisOptionsDefs.title._enabledFromTheme = undocumented2(boolean2);
var cartesianAxisBandHighlightOptions = {
enabled: boolean2,
...fillOptionsDef,
...strokeOptionsDef,
...lineDashOptionsDef
};
function cartesianAxisCrosshairOptions(canFormat, timeFormat) {
const baseCrosshairLabel = {
enabled: boolean2,
xOffset: number,
yOffset: number,
formatter: callbackOf(string2),
renderer: callbackOf(
or2(
string2,
optionsDefs(
{
text: string2,
color,
backgroundColor: color,
opacity: ratio
},
"crosshair label renderer result object"
)
)
)
};
let crosshairLabel;
if (canFormat) {
crosshairLabel = {
...baseCrosshairLabel,
format: timeFormat ? or2(
string2,
optionsDefs({
millisecond: string2,
second: string2,
hour: string2,
day: string2,
month: string2,
year: string2
})
) : string2
};
}
return {
enabled: boolean2,
snap: boolean2,
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: boolean2,
interval: {
step: supportTimeInterval ? or2(positiveNumberNonZero, timeIntervalUnit, timeInterval) : positiveNumberNonZero,
values: arrayOf(validDatum),
minSpacing: and(positiveNumber2, lessThan("maxSpacing")),
maxSpacing: and(positiveNumber2, greaterThan("minSpacing"))
}
};
}
var discreteTimeAxisIntervalOptionsDefs = {
step: or2(positiveNumberNonZero, timeIntervalUnit, timeInterval),
values: arrayOf(or2(number, date)),
minSpacing: and(positiveNumber2, lessThan("maxSpacing")),
maxSpacing: and(positiveNumber2, greaterThan("minSpacing")),
placement: union2("on", "between")
};
var categoryAxisOptionsDefs = {
...cartesianAxisOptionsDefs,
type: constant("category"),
label: cartesianAxisLabelOptionsDefs,
paddingInner: ratio,
paddingOuter: ratio,
groupPaddingInner: ratio,
crosshair: cartesianAxisCrosshairOptions(),
bandAlignment: union2("justify", "start", "center", "end"),
bandHighlight: cartesianAxisBandHighlightOptions,
interval: {
...commonAxisIntervalOptionsDefs,
placement: union2("on", "between")
}
};
var groupedCategoryAxisOptionsDefs = {
...cartesianAxisOptionsDefs,
type: constant("grouped-category"),
label: cartesianAxisLabelOptionsDefs,
crosshair: cartesianAxisCrosshairOptions(),
bandHighlight: cartesianAxisBandHighlightOptions,
paddingInner: ratio,
groupPaddingInner: ratio,
depthOptions: arrayOfDefs2(
{
label: {
enabled: boolean2,
avoidCollisions: boolean2,
wrapping: union2("never", "always", "hyphenate", "on-space"),
truncate: boolean2,
rotation: number,
spacing: number,
...fontOptionsDef,
...labelBoxOptionsDef
},
tick: {
enabled: boolean2,
stroke: color,
width: positiveNumber2
}
},
"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,
attachDescription2((value) => value !== 1, "not equal to 1")
),
label: cartesianNumericAxisLabel,
crosshair: cartesianAxisCrosshairOptions(true)
};
var timeAxisOptionsDefs = {
...cartesianAxisOptionsDefs,
...continuousAxisOptions(or2(number, date), true),
type: constant("time"),
label: cartesianTimeAxisLabel,
parentLevel: cartesianTimeAxisParentLevel,
crosshair: cartesianAxisCrosshairOptions(true, true)
};
var unitTimeAxisOptionsDefs = {
...cartesianAxisOptionsDefs,
type: constant("unit-time"),
unit: or2(timeInterval, timeIntervalUnit),
label: cartesianTimeAxisLabel,
parentLevel: cartesianTimeAxisParentLevel,
paddingInner: ratio,
paddingOuter: ratio,
groupPaddingInner: ratio,
crosshair: cartesianAxisCrosshairOptions(true, true),
bandAlignment: union2("justify", "start", "center", "end"),
bandHighlight: cartesianAxisBandHighlightOptions,
min: and(or2(number, date), lessThan("max")),
max: and(or2(number, date), greaterThan("min")),
preferredMin: and(or2(number, date), lessThan("preferredMax"), lessThan("max")),
preferredMax: and(or2(number, date), greaterThan("preferredMin"), greaterThan("min")),
interval: discreteTimeAxisIntervalOptionsDefs
};
// packages/ag-charts-community/src/chart/axesOptionsEnterpriseDefs.ts
import {
arrayOfDefs as arrayOfDefs3,
boolean as boolean3,
callbackOf as callbackOf2,
constant as constant2,
fontOptionsDef as fontOptionsDef2,
number as number2,
numberFormatValidator as numberFormatValidator2,
positiveNumber as positiveNumber3,
ratio as ratio2,
textOrSegments as textOrSegments2,
union as union3
} from "ag-charts-core";
var ordinalTimeAxisOptionsDefs = {
...cartesianAxisOptionsDefs,
type: constant2("ordinal-time"),
paddingInner: ratio2,
paddingOuter: ratio2,
groupPaddingInner: ratio2,
label: cartesianTimeAxisLabel,
parentLevel: cartesianTimeAxisParentLevel,
interval: discreteTimeAxisIntervalOptionsDefs,
crosshair: cartesianAxisCrosshairOptions(true, true),
bandHighlight: cartesianAxisBandHighlightOptions,
bandAlignment: union3("justify", "start", "center", "end")
};
var angleNumberAxisOptionsDefs = {
...commonAxisOptionsDefs,
...continuousAxisOptions(number2),
type: constant2("angle-number"),
crossLines: arrayOfDefs3(commonCrossLineOptionsDefs),
startAngle: number2,
endAngle: number2,
label: {
...commonAxisLabelOptionsDefs,
orientation: union3("fixed", "parallel", "perpendicular"),
format: numberFormatValidator2
}
};
var angleCategoryAxisOptionsDefs = {
...commonAxisOptionsDefs,
type: constant2("angle-category"),
shape: union3("polygon", "circle"),
crossLines: arrayOfDefs3(commonCrossLineOptionsDefs),
startAngle: number2,
endAngle: number2,
paddingInner: ratio2,
groupPaddingInner: ratio2,
label: {
...commonAxisLabelOptionsDefs,
orientation: union3("fixed", "parallel", "perpendicular")
}
};
angleCategoryAxisOptionsDefs.innerRadiusRatio = ratio2;
var radiusNumberAxisOptionsDefs = {
...commonAxisOptionsDefs,
...continuousAxisOptions(number2),
type: constant2("radius-number"),
shape: union3("polygon", "circle"),
positionAngle: number2,
innerRadiusRatio: ratio2,
crossLines: arrayOfDefs3(
{
...commonCrossLineOptionsDefs,
label: {
...commonCrossLineLabelOptionsDefs,
positionAngle: number2
}
},
"cross-line options"
),
title: {
enabled: boolean3,
text: textOrSegments2,
spacing: positiveNumber3,
formatter: callbackOf2(textOrSegments2),
...fontOptionsDef2
},
label: {
...commonAxisLabelOptionsDefs,
format: numberFormatValidator2
}
};
var radiusCategoryAxisOptionsDefs = {
...commonAxisOptionsDefs,
type: constant2("radius-category"),
positionAngle: number2,
innerRadiusRatio: ratio2,
paddingInner: ratio2,
paddingOuter: ratio2,
groupPaddingInner: ratio2,
label: commonAxisLabelOptionsDefs,
crossLines: arrayOfDefs3(
{
...commonCrossLineOptionsDefs,
label: {
...commonCrossLineLabelOptionsDefs,
positionAngle: number2
}
},
"cross-line options"
),
title: {
enabled: boolean3,
text: textOrSegments2,
spacing: positiveNumber3,
formatter: callbackOf2(textOrSegments2),
...fontOptionsDef2
}
};
// packages/ag-charts-community/src/chart/series/cartesian/areaSeriesOptionsDef.ts
import {
boolean as boolean4,
callbackDefs as callbackDefs2,
commonSeriesOptionsDefs,
commonSeriesThemeableOptionsDefs,
constant as constant3,
fillOptionsDef as fillOptionsDef2,
interpolationOptionsDefs,
lineDashOptionsDef as lineDashOptionsDef2,
markerOptionsDefs,
markerStyleOptionsDefs,
multiSeriesHighlightOptionsDef,
number as number3,
required as required3,
seriesLabelOptionsDefs,
shadowOptionsDefs,
shapeHighlightOptionsDef,
shapeSegmentation,
string as string3,
strokeOptionsDef as strokeOptionsDef2,
tooltipOptionsDefsWithArea,
undocumented as undocumented3
} from "ag-charts-core";
var highlight = multiSeriesHighlightOptionsDef(shapeHighlightOptionsDef, shapeHighlightOptionsDef);
var areaStyler = callbackDefs2({
...strokeOptionsDef2,
...fillOptionsDef2,
...lineDashOptionsDef2,
marker: markerStyleOptionsDefs
});
var areaSeriesThemeableOptionsDef = {
showInMiniChart: boolean4,
connectMissingData: boolean4,
interpolation: interpolationOptionsDefs,
label: seriesLabelOptionsDefs,
styler: areaStyler,
marker: markerOptionsDefs,
tooltip: tooltipOptionsDefsWithArea,
shadow: shadowOptionsDefs,
...commonSeriesThemeableOptionsDefs,
...fillOptionsDef2,
...strokeOptionsDef2,
...lineDashOptionsDef2,
highlight,
segmentation: shapeSegmentation
};
var areaSeriesOptionsDef = {
...areaSeriesThemeableOptionsDef,
...commonSeriesOptionsDefs,
highlight,
type: required3(constant3("area")),
xKey: required3(string3),
yKey: required3(string3),
xKeyAxis: string3,
yKeyAxis: string3,
xName: string3,
yName: string3,
legendItemName: string3,
stacked: boolean4,
stackGroup: string3,
normalizedTo: number3
};
areaSeriesOptionsDef.yFilterKey = undocumented3(string3);
// packages/ag-charts-community/src/chart/series/cartesian/barSeriesOptionsDef.ts
import {
barHighlightOptionsDef,
boolean as boolean5,
callback as callback2,
callbackDefs as callbackDefs3,
commonSeriesOptionsDefs as commonSeriesOptionsDefs2,
commonSeriesThemeableOptionsDefs as commonSeriesThemeableOptionsDefs2,
constant as constant4,
errorBarOptionsDefs,
errorBarThemeableOptionsDefs,
fillOptionsDef as fillOptionsDef3,
lineDashOptionsDef as lineDashOptionsDef3,
multiSeriesHighlightOptionsDef as multiSeriesHighlightOptionsDef2,
number as number4,
positiveNumber as positiveNumber4,
positiveNumberNonZero as positiveNumberNonZero2,
ratio as ratio3,
required as required4,
seriesLabelOptionsDefs as seriesLabelOptionsDefs2,
shadowOptionsDefs as shadowOptionsDefs2,
shapeSegmentation as shapeSegmentation2,
string as string4,
strokeOptionsDef as strokeOptionsDef3,
tooltipOptionsDefs,
undocumented as undocumented4,
union as union4
} from "ag-charts-core";
var highlight2 = multiSeriesHighlightOptionsDef2(barHighlightOptionsDef, barHighlightOptionsDef);
var barStyler = callbackDefs3({
...fillOptionsDef3,
...strokeOptionsDef3,
...lineDashOptionsDef3,
cornerRadius: positiveNumber4
});
var barSeriesThemeableOptionsDef = {
direction: union4("horizontal", "vertical"),
showInMiniChart: boolean5,
cornerRadius: positiveNumber4,
styler: barStyler,
itemStyler: barStyler,
crisp: boolean5,
label: {
...seriesLabelOptionsDefs2,
placement: union4("inside-center", "inside-start", "inside-end", "outside-start", "outside-end"),
spacing: positiveNumber4
},
errorBar: errorBarThemeableOptionsDefs,
shadow: shadowOptionsDefs2,
tooltip: tooltipOptionsDefs,
...commonSeriesThemeableOptionsDefs2,
highlight: highlight2,
...fillOptionsDef3,
...strokeOptionsDef3,
...lineDashOptionsDef3,
segmentation: shapeSegmentation2,
width: positiveNumberNonZero2,
widthRatio: ratio3
};
barSeriesThemeableOptionsDef.sparklineMode = undocumented4(boolean5);
var barSeriesOptionsDef = {
...barSeriesThemeableOptionsDef,
...commonSeriesOptionsDefs2,
highlight: highlight2,
type: required4(constant4("bar")),
xKey: required4(string4),
yKey: required4(string4),
xKeyAxis: string4,
yKeyAxis: string4,
xName: string4,
yName: string4,
direction: union4("horizontal", "vertical"),
grouped: boolean5,
stacked: boolean5,
stackGroup: string4,
normalizedTo: number4,
legendItemName: string4,
errorBar: errorBarOptionsDefs
};
barSeriesOptionsDef.yFilterKey = undocumented4(string4);
barSeriesOptionsDef.pickOutsideVisibleMinorAxis = undocumented4(boolean5);
barSeriesOptionsDef.focusPriority = undocumented4(number4);
barSeriesOptionsDef.simpleItemStyler = undocumented4(callback2);
// packages/ag-charts-community/src/chart/series/cartesian/bubbleSeriesOptionsDef.ts
import {
arrayOf as arrayOf2,
boolean as boolean6,
callbackDefs as callbackDefs4,
commonSeriesOptionsDefs as commonSeriesOptionsDefs3,
commonSeriesThemeableOptionsDefs as commonSeriesThemeableOptionsDefs3,
constant as constant5,
markerOptionsDefs as markerOptionsDefs2,
multiSeriesHighlightOptionsDef as multiSeriesHighlightOptionsDef3,
number as number5,
positiveNumber as positiveNumber5,
required as required5,
seriesLabelOptionsDefs as seriesLabelOptionsDefs3,
shapeHighlightOptionsDef as shapeHighlightOptionsDef2,
string as string5,
tooltipOptionsDefs as tooltipOptionsDefs2,
undocumented as undocumented5,
union as union5,
without as without3
} from "ag-charts-core";
var bubbleSeriesThemeableOptionsDef = {
title: string5,
domain: arrayOf2(number5),
maxSize: positiveNumber5,
showInMiniChart: boolean6,
label: {
placement: union5("top", "right", "bottom", "left"),
...seriesLabelOptionsDefs3
},
tooltip: tooltipOptionsDefs2,
styler: callbackDefs4({
...markerOptionsDefs2,
maxSize: positiveNumber5
}),
maxRenderedItems: number5,
...commonSeriesThemeableOptionsDefs3,
...without3(markerOptionsDefs2, ["enabled"]),
highlight: multiSeriesHighlightOptionsDef3(shapeHighlightOptionsDef2, shapeHighlightOptionsDef2)
};
var bubbleSeriesOptionsDef = {
...bubbleSeriesThemeableOptionsDef,
...commonSeriesOptionsDefs3,
type: required5(constant5("bubble")),
xKey: required5(string5),
yKey: required5(string5),
sizeKey: required5(string5),
labelKey: string5,
xName: string5,
yName: string5,
sizeName: string5,
labelName: string5,
legendItemName: string5,
xKeyAxis: string5,
yKeyAxis: string5,
highlight: multiSeriesHighlightOptionsDef3(shapeHighlightOptionsDef2, shapeHighlightOptionsDef2)
};
bubbleSeriesOptionsDef.xFilterKey = undocumented5(string5);
bubbleSeriesOptionsDef.yFilterKey = undocumented5(string5);
bubbleSeriesOptionsDef.sizeFilterKey = undocumented5(string5);
// packages/ag-charts-community/src/chart/series/cartesian/histogramSeriesOptionsDef.ts
import {
arrayOf as arrayOf3,
boolean as boolean7,
commonSeriesOptionsDefs as commonSeriesOptionsDefs4,
commonSeriesThemeableOptionsDefs as commonSeriesThemeableOptionsDefs4,
constant as constant6,
fillOptionsDef as fillOptionsDef4,
lineDashOptionsDef as lineDashOptionsDef4,
multiSeriesHighlightOptionsDef as multiSeriesHighlightOptionsDef4,
number as number6,
positiveNumber as positiveNumber6,
required as required6,
seriesLabelOptionsDefs as seriesLabelOptionsDefs4,
shadowOptionsDefs as shadowOptionsDefs3,
shapeHighlightOptionsDef as shapeHighlightOptionsDef3,
string as string6,
strokeOptionsDef as strokeOptionsDef4,
tooltipOptionsDefs as tooltipOptionsDefs3,
union as union6
} from "ag-charts-core";
var histogramSeriesThemeableOptionsDef = {
showInMiniChart: boolean7,
cornerRadius: positiveNumber6,
label: seriesLabelOptionsDefs4,
tooltip: tooltipOptionsDefs3,
shadow: shadowOptionsDefs3,
...commonSeriesThemeableOptionsDefs4,
...fillOptionsDef4,
...strokeOptionsDef4,
...lineDashOptionsDef4,
highlight: multiSeriesHighlightOptionsDef4(shapeHighlightOptionsDef3, shapeHighlightOptionsDef3),
areaPlot: boolean7,
aggregation: union6("count", "sum", "mean"),
bins: arrayOf3(arrayOf3(number6)),
binCount: positiveNumber6
};
var histogramSeriesOptionsDef = {
...commonSeriesOptionsDefs4,
...histogramSeriesThemeableOptionsDef,
type: required6(constant6("histogram")),
xKey: required6(string6),
yKey: string6,
xKeyAxis: string6,
yKeyAxis: string6,
xName: string6,
yName: string6
};
// packages/ag-charts-community/src/chart/series/cartesian/lineSeriesOptionsDef.ts
import {
boolean as boolean8,
callbackDefs as callbackDefs5,
commonSeriesOptionsDefs as commonSeriesOptionsDefs5,
commonSeriesThemeableOptionsDefs as commonSeriesThemeableOptionsDefs5,
constant as constant7,
errorBarOptionsDefs as errorBarOptionsDefs2,
errorBarThemeableOptionsDefs as errorBarThemeableOptionsDefs2,
interpolationOptionsDefs as interpolationOptionsDefs2,
lineDashOptionsDef as lineDashOptionsDef5,
lineHighlightOptionsDef,
lineSegmentation,
markerOptionsDefs as markerOptionsDefs3,
markerStyleOptionsDefs as markerStyleOptionsDefs2,
multiSeriesHighlightOptionsDef as multiSeriesHighlightOptionsDef5,
number as number7,
required as required7,
seriesLabelOptionsDefs as seriesLabelOptionsDefs5,
shapeHighlightOptionsDef as shapeHighlightOptionsDef4,
string as string7,
strokeOptionsDef as strokeOptionsDef5,
tooltipOptionsDefs as tooltipOptionsDefs4,
undocumented as undocumented6
} from "ag-charts-core";
var highlight3 = multiSeriesHighlightOptionsDef5(shapeHighlightOptionsDef4, lineHighlightOptionsDef);
var lineStyler = callbackDefs5({
...strokeOptionsDef5,
...lineDashOptionsDef5,
marker: markerStyleOptionsDefs2
});
var lineSeriesThemeableOptionsDef = {
title: string7,
showInMiniChart: boolean8,
connectMissingData: boolean8,
interpolation: interpolationOptionsDefs2,
label: seriesLabelOptionsDefs5,
styler: lineStyler,
marker: markerOptionsDefs3,
tooltip: tooltipOptionsDefs4,
errorBar: errorBarThemeableOptionsDefs2,
...commonSeriesThemeableOptionsDefs5,
...strokeOptionsDef5,
...lineDashOptionsDef5,
highlight: highlight3,
segmentation: lineSegmentation
};
lineSeriesThemeableOptionsDef.sparklineMode = undocumented6(boolean8);
var lineSeriesOptionsDef = {
...lineSeriesThemeableOptionsDef,
...commonSeriesOptionsDefs5,
highlight: highlight3,
type: required7(constant7("line")),
xKey: required7(string7),
yKey: required7(string7),
xKeyAxis: string7,
yKeyAxis: string7,
xName: string7,
yName: string7,
stacked: boolean8,
stackGroup: string7,
normalizedTo: number7,
legendItemName: string7,
errorBar: errorBarOptionsDefs2
};
lineSeriesOptionsDef.yFilterKey = undocumented6(string7);
lineSeriesOptionsDef.pickOutsideVisibleMinorAxis = undocumented6(boolean8);
lineSeriesOptionsDef.focusPriority = undocumented6(number7);
// packages/ag-charts-community/src/chart/series/cartesian/scatterSeriesOptionsDef.ts
import {
boolean as boolean9,
callbackDefs as callbackDefs6,
commonSeriesOptionsDefs as commonSeriesOptionsDefs6,
commonSeriesThemeableOptionsDefs as commonSeriesThemeableOptionsDefs6,
constant as constant8,
errorBarOptionsDefs as errorBarOptionsDefs3,
errorBarThemeableOptionsDefs as errorBarThemeableOptionsDefs3,
markerOptionsDefs as markerOptionsDefs4,
multiSeriesHighlightOptionsDef as multiSeriesHighlightOptionsDef6,
number as number8,
required as required8,
seriesLabelOptionsDefs as seriesLabelOptionsDefs6,
shapeHighlightOptionsDef as shapeHighlightOptionsDef5,
string as string8,
tooltipOptionsDefs as tooltipOptionsDefs5,
undocumented as undocumented7,
union as union7,
without as without4
} from "ag-charts-core";
var scatterSeriesThemeableOptionsDef = {
title: string8,
showInMiniChart: boolean9,
label: {
placement: union7("top", "right", "bottom", "left"),
...seriesLabelOptionsDefs6
},
tooltip: tooltipOptionsDefs5,
errorBar: errorBarThemeableOptionsDefs3,
styler: callbackDefs6(markerOptionsDefs4),
maxRenderedItems: number8,
...commonSeriesThemeableOptionsDefs6,
...without4(markerOptionsDefs4, ["enabled"]),
highlight: multiSeriesHighlightOptionsDef6(shapeHighlightOptionsDef5, shapeHighlightOptionsDef5)
};
var scatterSeriesOptionsDef = {
...scatterSeriesThemeableOptionsDef,
...commonSeriesOptionsDefs6,
type: required8(constant8("scatter")),
xKey: required8(string8),
yKey: required8(string8),
labelKey: string8,
xName: string8,
yName: string8,
labelName: string8,
legendItemName: string8,
xKeyAxis: string8,
yKeyAxis: string8,
errorBar: errorBarOptionsDefs3,
highlight: multiSeriesHighlightOptionsDef6(shapeHighlightOptionsDef5, shapeHighlightOptionsDef5)
};
scatterSeriesOptionsDef.xFilterKey = undocumented7(string8);
scatterSeriesOptionsDef.yFilterKey = undocumented7(string8);
scatterSeriesOptionsDef.sizeFilterKey = undocumented7(string8);
// packages/ag-charts-community/src/chart/series/polar/donutSeriesOptionsDef.ts
import {
arrayOfDefs as arrayOfDefs4,
constant as constant10,
fontOptionsDef as fontOptionsDef4,
labelBoxOptionsDef as labelBoxOptionsDef3,
number as number10,
positiveNumber as positiveNumber8,
ratio as ratio5,
required as required10,
string as string10,
undocumented as undocumented9
} from "ag-charts-core";
// packages/ag-charts-community/src/chart/series/polar/pieSeriesOptionsDef.ts
import {
arrayOf as arrayOf4,
boolean as boolean10,
callbackDefs as callbackDefs7,
callbackOf as callbackOf3,
color as color2,
colorUnion,
commonSeriesOptionsDefs as commonSeriesOptionsDefs7,
commonSeriesThemeableOptionsDefs as commonSeriesThemeableOptionsDefs7,
constant as constant9,
fillOptionsDef as fillOptionsDef5,
fontOptionsDef as fontOptionsDef3,
labelBoxOptionsDef as labelBoxOptionsDef2,
lineDashOptionsDef as lineDashOptionsDef6,
multiSeriesHighlightOptionsDef as multiSeriesHighlightOptionsDef7,
number as number9,
positiveNumber as positiveNumber7,
ratio as ratio4,
required as required9,
shadowOptionsDefs as shadowOptionsDefs4,
shapeHighlightOptionsDef as shapeHighlightOptionsDef6,
string as string9,
strokeOptionsDef as strokeOptionsDef6,
textOrSegments as textOrSegments3,
tooltipOptionsDefs as tooltipOptionsDefs6,
undocumented as undocumented8,
without as without5
} from "ag-charts-core";
var highlight4 = multiSeriesHighlightOptionsDef7(shapeHighlightOptionsDef6, shapeHighlightOptionsDef6);
var pieSeriesThemeableOptionsDef = {
...commonSeriesThemeableOptionsDefs7,
radiusMin: positiveNumber7,
radiusMax: positiveNumber7,
rotation: number9,
outerRadiusOffset: number9,
outerRadiusRatio: ratio4,
hideZeroValueSectorsInLegend: boolean10,
sectorSpacing: positiveNumber7,
cornerRadius: positiveNumber7,
itemStyler: callbackDefs7({
...fillOptionsDef5,
...strokeOptionsDef6,
...lineDashOptionsDef6,
cornerRadius: positiveNumber7
}),
title: {
enabled: boolean10,
text: string9,
showInLegend: boolean10,
spacing: positiveNumber7,
...fontOptionsDef3
},
calloutLabel: {
enabled: boolean10,
offset: number9,
minAngle: positiveNumber7,
avoidCollisions: boolean10,
formatter: callbackOf3(textOrSegments3),
format: string9,
itemStyler: callbackDefs7({
enabled: boolean10,
...labelBoxOptionsDef2,
...fontOptionsDef3
}),
...labelBoxOptionsDef2,
...fontOptionsDef3
},
sectorLabel: {
enabled: boolean10,
positionOffset: number9,
positionRatio: ratio4,
formatter: callbackOf3(textOrSegments3),
format: string9,
itemStyler: callbackDefs7({
enabled: boolean10,
...labelBoxOptionsDef2,
...fontOptionsDef3
}),
...labelBoxOptionsDef2,
...fontOptionsDef3
},
calloutLine: {
colors: arrayOf4(color2),
length: positiveNumber7,
strokeWidth: positiveNumber7,
itemStyler: callbackDefs7({
color: color2,
length: positiveNumber7,
strokeWidth: positiveNumber7
})
},
fills: arrayOf4(colorUnion),
strokes: arrayOf4(color2),
tooltip: tooltipOptionsDefs6,
shadow: shadowOptionsDefs4,
highlight: highlight4,
...lineDashOptionsDef6,
...without5(fillOptionsDef5, ["fill"]),
...without5(strokeOptionsDef6, ["stroke"])
};
var pieSeriesOptionsDef = {
...pieSeriesThemeableOptionsDef,
...commonSeriesOptionsDefs7,
type: required9(constant9("pie")),
angleKey: required9(string9),
radiusKey: string9,
calloutLabelKey: string9,
sectorLabelKey: string9,
legendItemKey: string9,
angleName: string9,
radiusName: string9,
calloutLabelName: string9,
sectorLabelName: string9,
highlight: highlight4
};
pieSeriesOptionsDef.angleFilterKey = undocumented8(string9);
pieSeriesOptionsDef.defaultColorRange = undocumented8(arrayOf4(arrayOf4(color2)));
pieSeriesOptionsDef.defaultPatternFills = undocumented8(arrayOf4(color2));
pieSeriesOptionsDef.title._enabledFromTheme = undocumented8(boolean10);
pieSeriesOptionsDef.calloutLabel._enabledFromTheme = undocumented8(boolean10);
pieSeriesOptionsDef.sectorLabel._enabledFromTheme = undocumented8(boolean10);
pieSeriesOptionsDef.angleKeyAxis = undocumented8(string9);
pieSeriesOptionsDef.radiusKeyAxis = undocumented8(string9);
// packages/ag-charts-community/src/chart/series/polar/donutSeriesOptionsDef.ts
var donutSeriesThemeableOptionsDef = {
...pieSeriesThemeableOptionsDef,
innerRadiusOffset: number10,
innerRadiusRatio: ratio5,
innerCircle: {
fill: string10,
fillOpacity: ratio5
},
innerLabels: {
spacing: positiveNumber8,
...fontOptionsDef4,
...labelBoxOptionsDef3
}
};
var donutSeriesOptionsDef = {
...donutSeriesThemeableOptionsDef,
...pieSeriesOptionsDef,
type: required10(constant10("donut")),
innerLabels: arrayOfDefs4(
{
text: required10(string10),
spacing: positiveNumber8,
...fontOptionsDef4,
...labelBoxOptionsDef3
},
"inner label options array"
)
};
donutSeriesOptionsDef.angleFilterKey = undocumented9(string10);
// packages/ag-charts-community/src/chart/themes/annotationOptionsDef.ts
import {
array as array2,
arrayOf as arrayOf5,
arrayOfDefs as arrayOfDefs5,
boolean as boolean11,
color as color3,
fillOptionsDef as fillOptionsDef6,
fontOptionsDef as fontOptionsDef5,
lineDashOptionsDef as lineDashOptionsDef7,
optionsDefs as optionsDefs2,
or as or3,
positiveNumber as positiveNumber9,
required as required11,
string as string11,
strokeOptionsDef as strokeOptionsDef7,
toolbarButtonOptionsDefs,
undocumented as undocumented10,
union as union8
} from "ag-charts-core";
var annotationLineOptionsDef = {
lineStyle: union8("solid", "dashed", "dotted"),
...lineDashOptionsDef7
};
var annotationHandleStylesDefs = {
...fillOptionsDef6,
...strokeOptionsDef7,
...lineDashOptionsDef7
};
var annotationTextStylesDef = {
visible: boolean11,
locked: boolean11,
readOnly: boolean11,
handle: annotationHandleStylesDefs,
...fontOptionsDef5
};
var annotationLineTextDefs = {
position: union8("top", "center", "bottom"),
alignment: union8("left", "center", "right"),
...fontOptionsDef5
};
var annotationChannelTextDefs = {
position: union8("top", "inside", "bottom"),
alignment: union8("left", "center", "right"),
...fontOptionsDef5
};
var annotationAxisLabelOptionsDef = {
enabled: boolean11,
cornerRadius: positiveNumber9,
...fontOptionsDef5,
...fillOptionsDef6,
...strokeOptionsDef7,
...lineDashOptionsDef7
};
var annotationChannelMiddleDefs = {
visible: boolean11,
...annotationLineOptionsDef,
...strokeOptionsDef7
};
var annotationMeasurerStatisticsOptionsDefs = {
divider: strokeOptionsDef7,
...fillOptionsDef6,
...strokeOptionsDef7,
...fontOptionsDef5
};
var annotationQuickMeasurerDirectionStylesDefs = {
handle: annotationHandleStylesDefs,
statistics: annotationMeasurerStatisticsOptionsDefs,
...annotationLineOptionsDef,
...fillOptionsDef6,
...strokeOptionsDef7
};
var annotationLineStyleDefs = {
visible: boolean11,
locked: boolean11,
readOnly: boolean11,
extendStart: boolean11,
extendEnd: boolean11,
handle: annotationHandleStylesDefs,
text: annotationLineTextDefs,
...annotationLineOptionsDef,
...strokeOptionsDef7
};
var annotationCrossLineStyleDefs = {
visible: boolean11,
locked: boolean11,
readOnly: boolean11,
axisLabel: annotationAxisLabelOptionsDef,
handle: annotationHandleStylesDefs,
text: annotationLineTextDefs,
...annotationLineOptionsDef,
...strokeOptionsDef7
};
var annotationChannelStyleDefs = {
visible: boolean11,
locked: boolean11,
readOnly: boolean11,
extendStart: boolean11,
extendEnd: boolean11,
handle: annotationHandleStylesDefs,
text: annotationChannelTextDefs,
background: fillOptionsDef6,
...annotationLineOptionsDef,
...strokeOptionsDef7
};
var annotationDisjointChannelStyleDefs = {
...annotationChannelStyleDefs
};
var annotationParallelChannelStyleDefs = {
...annotationChannelStyleDefs,
middle: annotationChannelMiddleDefs
};
var annotationFibonacciStylesDefs = {
label: fontOptionsDef5,
showFill: boolean11,
isMultiColor: boolean11,
strokes: arrayOf5(color3),
rangeStroke: color3,
bands: union8(4, 6, 10),
...annotationLineStyleDefs
};
var annotationCalloutStylesDefs = {
...fillOptionsDef6,
...strokeOptionsDef7,
...annotationTextStylesDef
};
var annotationCommentStylesDefs = {
...fillOptionsDef6,
...strokeOptionsDef7,
...annotationTextStylesDef
};
var annotationNoteStylesDefs = {
...fillOptionsDef6,
...strokeOptionsDef7,
...annotationTextStylesDef,
background: {
...fillOptionsDef6,
...strokeOptionsDef7
}
};
var annotationShapeStylesDefs = {
visible: boolean11,
locked: boolean11,
readOnly: boolean11,
handle: annotationHandleStylesDefs,
...fillOptionsDef6
};
var annotationMeasurerStylesDefs = {
visible: boolean11,
locked: boolean11,
readOnly: boolean11,
extendStart: boolean11,
extendEnd: boolean11,
handle: annotationHandleStylesDefs,
text: annotationLineTextDefs,
background: fillOptionsDef6,
statistics: annotationMeasurerStatisticsOptionsDefs,
...annotationLineOptionsDef,
...strokeOptionsDef7
};
var annotationQuickMeasurerStylesDefs = {
visible: boolean11,
up: annotationQuickMeasurerDirectionStylesDefs,
down: annotationQuickMeasurerDirectionStylesDefs
};
var annotationOptionsDef = {
enabled: boolean11,
axesButtons: {
enabled: boolean11,
axes: union8("x", "y", "xy")
},
toolbar: {
enabled: boolean11,
padding: positiveNumber9,
buttons: arrayOfDefs5(
{
...toolbarButtonOptionsDefs,
value: union8(
"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: boolean11,
buttons: arrayOf5(
or3(
optionsDefs2({
...toolbarButtonOptionsDefs,
value: required11(
union8(
"line-stroke-width",
"line-style-type",
"line-color",
"fill-color",
"text-color",
"text-size",
"delete",
"settings"
)
)
}),
optionsDefs2({
...toolbarButtonOptionsDefs,
value: required11(union8("lock")),
checkedOverrides: toolbarButtonOptionsDefs
})
)
)
}
};
annotationOptionsDef.data = undocumented10(array2);
annotationOptionsDef.xKey = undocumented10(string11);
annotationOptionsDef.volumeKey = undocumented10(string11);
annotationOptionsDef.snap = undocumented10(boolean11);
// packages/ag-charts-community/src/chart/themes/enterpriseThemeableOptionsDef.ts
import {
and as and2,
arrayOf as arrayOf6,
autoSizedLabelOptionsDefs,
barHighlightOptionsDef as barHighlightOptionsDef2,
boolean as boolean12,
callbackDefs as callbackDefs8,
color as color4,
colorUnion as colorUnion2,
commonSeriesThemeableOptionsDefs as commonSeriesThemeableOptionsDefs8,
fillOptionsDef as fillOptionsDef7,
highlightOptionsDef,
interpolationOptionsDefs as interpolationOptionsDefs3,
lessThanOrEqual,
lineDashOptionsDef as lineDashOptionsDef8,
lineHighlightOptionsDef as lineHighlightOptionsDef2,
markerOptionsDefs as markerOptionsDefs5,
markerStyleOptionsDefs as markerStyleOptionsDefs3,
multiSeriesHighlightOptionsDef as multiSeriesHighlightOptionsDef8,
numberFormatValidator as numberFormatValidator3,
positiveNumber as positiveNumber10,
positiveNumberNonZero as positiveNumberNonZero3,
ratio as ratio6,
seriesLabelOptionsDefs as seriesLabelOptionsDefs7,
shadowOptionsDefs as shadowOptionsDefs5,
shapeHighlightOptionsDef as shapeHighlightOptionsDef7,
shapeSegmentation as shapeSegmentation3,
string as string12,
strokeOptionsDef as strokeOptionsDef8,
tooltipOptionsDefs as tooltipOptionsDefs7,
union as union9,
without as without6
} from "ag-charts-core";
var hierarchyHighlightStyleOptionsDef = {
...fillOptionsDef7,
...strokeOptionsDef8,
opacity: ratio6
};
var boxPlotStyleOptionsDef = {
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8,
cornerRadius: positiveNumber10,
whisker: {
...strokeOptionsDef8,
...lineDashOptionsDef8
},
cap: {
lengthRatio: ratio6
}
};
var boxPlotHighlightStyleOptionsDef = {
...boxPlotStyleOptionsDef,
opacity: ratio6
};
var boxPlotStyler = callbackDefs8({
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8,
cornerRadius: positiveNumber10,
whisker: {
...strokeOptionsDef8,
...lineDashOptionsDef8
},
cap: {
lengthRatio: ratio6
}
});
var boxPlotSeriesThemeableOptionsDef = {
direction: union9("horizontal", "vertical"),
showInMiniChart: boolean12,
styler: boxPlotStyler,
itemStyler: boxPlotStyler,
tooltip: tooltipOptionsDefs7,
...commonSeriesThemeableOptionsDefs8,
...boxPlotStyleOptionsDef,
highlight: multiSeriesHighlightOptionsDef8(boxPlotHighlightStyleOptionsDef, boxPlotHighlightStyleOptionsDef),
segmentation: shapeSegmentation3,
width: positiveNumberNonZero3,
widthRatio: ratio6
};
var candlestickSeriesItemOptionsDef = {
cornerRadius: positiveNumber10,
wick: {
...strokeOptionsDef8,
...lineDashOptionsDef8
},
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8
};
var candlestickHighlightStyleOptionsDef = {
...candlestickSeriesItemOptionsDef,
opacity: ratio6
};
var candlestickSeriesThemeableOptionsDef = {
item: {
up: candlestickSeriesItemOptionsDef,
down: candlestickSeriesItemOptionsDef
},
itemStyler: callbackDefs8({
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8,
cornerRadius: positiveNumber10,
wick: {
...strokeOptionsDef8,
...lineDashOptionsDef8
}
}),
showInMiniChart: boolean12,
tooltip: tooltipOptionsDefs7,
...commonSeriesThemeableOptionsDefs8,
highlight: multiSeriesHighlightOptionsDef8(candlestickHighlightStyleOptionsDef, candlestickHighlightStyleOptionsDef)
};
var chordSeriesThemeableOptionsDef = {
fills: arrayOf6(colorUnion2),
strokes: arrayOf6(color4),
label: {
spacing: positiveNumber10,
maxWidth: positiveNumber10,
...seriesLabelOptionsDefs7
},
link: {
tension: ratio6,
itemStyler: callbackDefs8({
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8,
tension: ratio6
}),
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8
},
node: {
width: positiveNumber10,
spacing: positiveNumber10,
itemStyler: callbackDefs8({
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8
}),
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8
},
tooltip: tooltipOptionsDefs7,
...commonSeriesThemeableOptionsDefs8
};
var coneFunnelSeriesThemeableOptionsDef = {
direction: union9("horizontal", "vertical"),
fills: arrayOf6(colorUnion2),
strokes: arrayOf6(color4),
label: {
spacing: positiveNumber10,
placement: union9("before", "middle", "after"),
...seriesLabelOptionsDefs7
},
stageLabel: {
placement: union9("before", "after"),
format: numberFormatValidator3,
...commonAxisLabelOptionsDefs
},
tooltip: tooltipOptionsDefs7,
...without6(commonSeriesThemeableOptionsDefs8, ["showInLegend"]),
...without6(fillOptionsDef7, ["fill"]),
...without6(strokeOptionsDef8, ["stroke"]),
...lineDashOptionsDef8,
highlight: highlightOptionsDef(lineHighlightOptionsDef2)
};
var funnelSeriesThemeableOptionsDef = {
direction: union9("horizontal", "vertical"),
fills: arrayOf6(colorUnion2),
strokes: arrayOf6(color4),
itemStyler: callbackDefs8({
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8
}),
spacingRatio: ratio6,
crisp: boolean12,
dropOff: {
enabled: boolean12,
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8
},
stageLabel: {
placement: union9("before", "after"),
format: numberFormatValidator3,
...commonAxisLabelOptionsDefs
},
label: seriesLabelOptionsDefs7,
tooltip: tooltipOptionsDefs7,
shadow: shadowOptionsDefs5,
...without6(commonSeriesThemeableOptionsDefs8, ["showInLegend"]),
...without6(fillOptionsDef7, ["fill"]),
...without6(strokeOptionsDef8, ["stroke"]),
...lineDashOptionsDef8
};
var heatmapSeriesThemeableOptionsDef = {
title: string12,
textAlign: union9("left", "center", "right"),
verticalAlign: union9("top", "middle", "bottom"),
itemPadding: positiveNumber10,
itemStyler: callbackDefs8({
...fillOptionsDef7,
...strokeOptionsDef8
}),
showInMiniChart: boolean12,
label: autoSizedLabelOptionsDefs,
tooltip: tooltipOptionsDefs7,
...commonSeriesThemeableOptionsDefs8,
...strokeOptionsDef8
};
var ohlcSeriesThemeableOptionsDef = {
showInMiniChart: boolean12,
itemStyler: callbackDefs8({
...strokeOptionsDef8,
...lineDashOptionsDef8
}),
item: {
up: {
...strokeOptionsDef8,
...lineDashOptionsDef8
},
down: {
...strokeOptionsDef8,
...lineDashOptionsDef8
}
},
tooltip: tooltipOptionsDefs7,
...commonSeriesThemeableOptionsDefs8,
highlight: multiSeriesHighlightOptionsDef8(lineHighlightOptionsDef2, lineHighlightOptionsDef2)
};
var mapLineSeriesThemeableOptionsDef = {
maxStrokeWidth: positiveNumber10,
itemStyler: callbackDefs8({
...strokeOptionsDef8,
...lineDashOptionsDef8
}),
sizeDomain: arrayOf6(positiveNumber10),
label: seriesLabelOptionsDefs7,
tooltip: tooltipOptionsDefs7,
...commonSeriesThemeableOptionsDefs8,
...strokeOptionsDef8,
...lineDashOptionsDef8,
highlight: multiSeriesHighlightOptionsDef8(lineHighlightOptionsDef2, lineHighlightOptionsDef2)
};
var mapLineBackgroundSeriesThemeableOptionsDef = {
...strokeOptionsDef8,
...lineDashOptionsDef8
};
var mapMarkerSeriesThemeableOptionsDef = {
colorRange: arrayOf6(color4),
maxSize: positiveNumber10,
sizeDomain: arrayOf6(positiveNumber10),
label: {
placement: union9("top", "bottom", "left", "right"),
...seriesLabelOptionsDefs7
},
tooltip: tooltipOptionsDefs7,
...commonSeriesThemeableOptionsDefs8,
...without6(markerOptionsDefs5, ["enabled"]),
highlight: multiSeriesHighlightOptionsDef8(shapeHighlightOptionsDef7, shapeHighlightOptionsDef7)
};
var mapShapeSeriesThemeableOptionsDef = {
colorRange: arrayOf6(color4),
padding: positiveNumber10,
itemStyler: callbackDefs8({
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8
}),
label: autoSizedLabelOptionsDefs,
tooltip: tooltipOptionsDefs7,
...commonSeriesThemeableOptionsDefs8,
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8,
highlight: multiSeriesHighlightOptionsDef8(shapeHighlightOptionsDef7, shapeHighlightOptionsDef7)
};
var mapShapeBackgroundSeriesThemeableOptionsDef = {
...commonSeriesThemeableOptionsDefs8,
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8
};
var radialSeriesStylerDef = callbackDefs8({
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8,
cornerRadius: positiveNumber10
});
var nightingaleSeriesThemeableOptionsDef = {
cornerRadius: positiveNumber10,
styler: radialSeriesStylerDef,
itemStyler: radialSeriesStylerDef,
label: seriesLabelOptionsDefs7,
tooltip: tooltipOptionsDefs7,
...commonSeriesThemeableOptionsDefs8,
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8,
highlight: multiSeriesHighlightOptionsDef8(barHighlightOptionsDef2, barHighlightOptionsDef2)
};
var pyramidSeriesThemeableOptionsDef = {
direction: union9("horizontal", "vertical"),
aspectRatio: positiveNumber10,
spacing: positiveNumber10,
reverse: boolean12,
itemStyler: callbackDefs8({
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8
}),
fills: arrayOf6(colorUnion2),
strokes: arrayOf6(color4),
label: seriesLabelOptionsDefs7,
stageLabel: {
spacing: positiveNumber10,
placement: union9("before", "after"),
...seriesLabelOptionsDefs7
},
tooltip: tooltipOptionsDefs7,
shadow: shadowOptionsDefs5,
...commonSeriesThemeableOptionsDefs8,
...without6(fillOptionsDef7, ["fill"]),
...without6(strokeOptionsDef8, ["stroke"]),
...lineDashOptionsDef8
};
var radarAreaSeriesThemeableOptionsDef = {
connectMissingData: boolean12,
marker: markerOptionsDefs5,
styler: callbackDefs8({
marker: markerStyleOptionsDefs3,
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8
}),
label: seriesLabelOptionsDefs7,
tooltip: tooltipOptionsDefs7,
...commonSeriesThemeableOptionsDefs8,
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8,
highlight: multiSeriesHighlightOptionsDef8(shapeHighlightOptionsDef7, shapeHighlightOptionsDef7)
};
var radarLineSeriesThemeableOptionsDef = {
connectMissingData: boolean12,
marker: markerOptionsDefs5,
styler: callbackDefs8({
marker: markerStyleOptionsDefs3,
...strokeOptionsDef8,
...lineDashOptionsDef8
}),
label: seriesLabelOptionsDefs7,
tooltip: tooltipOptionsDefs7,
...commonSeriesThemeableOptionsDefs8,
...strokeOptionsDef8,
...lineDashOptionsDef8,
highlight: multiSeriesHighlightOptionsDef8(shapeHighlightOptionsDef7, lineHighlightOptionsDef2)
};
var radialBarSeriesThemeableOptionsDef = {
cornerRadius: positiveNumber10,
styler: radialSeriesStylerDef,
itemStyler: radialSeriesStylerDef,
label: seriesLabelOptionsDefs7,
tooltip: tooltipOptionsDefs7,
...commonSeriesThemeableOptionsDefs8,
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8,
highlight: multiSeriesHighlightOptionsDef8(barHighlightOptionsDef2, barHighlightOptionsDef2)
};
var radialColumnSeriesThemeableOptionsDef = {
cornerRadius: positiveNumber10,
columnWidthRatio: ratio6,
maxColumnWidthRatio: ratio6,
styler: radialSeriesStylerDef,
itemStyler: radialSeriesStylerDef,
label: seriesLabelOptionsDefs7,
tooltip: tooltipOptionsDefs7,
...commonSeriesThemeableOptionsDefs8,
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8,
highlight: multiSeriesHighlightOptionsDef8(barHighlightOptionsDef2, barHighlightOptionsDef2)
};
var rangeAreaSeriesLineThemeableOptionsDef = {
marker: markerOptionsDefs5,
...strokeOptionsDef8,
...lineDashOptionsDef8
};
var rangeAreaSeriesItemLineThemeableOptionsDef = {
marker: {
enabled: boolean12,
...markerStyleOptionsDefs3
},
...strokeOptionsDef8,
...lineDashOptionsDef8
};
var rangeAreaSeriesLineStyleDef = {
marker: markerStyleOptionsDefs3,
...strokeOptionsDef8,
...lineDashOptionsDef8
};
var rangeAreaSeriesThemeableOptionsDef = {
showInMiniChart: boolean12,
connectMissingData: boolean12,
interpolation: interpolationOptionsDefs3,
label: {
...seriesLabelOptionsDefs7,
placement: union9("inside", "outside"),
spacing: positiveNumber10
},
tooltip: tooltipOptionsDefs7,
shadow: shadowOptionsDefs5,
...commonSeriesThemeableOptionsDefs8,
...fillOptionsDef7,
...rangeAreaSeriesLineThemeableOptionsDef,
item: {
low: { ...rangeAreaSeriesItemLineThemeableOptionsDef },
high: { ...rangeAreaSeriesItemLineThemeableOptionsDef }
},
styler: callbackDefs8({
...fillOptionsDef7,
item: {
low: { ...rangeAreaSeriesLineStyleDef },
high: { ...rangeAreaSeriesLineStyleDef }
}
}),
highlight: multiSeriesHighlightOptionsDef8(shapeHighlightOptionsDef7, shapeHighlightOptionsDef7),
segmentation: shapeSegmentation3,
invertedStyle: {
enabled: boolean12,
...fillOptionsDef7
}
};
var rangeBarStyleCallback = callbackDefs8({
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8,
cornerRadius: positiveNumber10
});
var rangeBarSeriesThemeableOptionsDef = {
direction: union9("horizontal", "vertical"),
grouped: boolean12,
showInMiniChart: boolean12,
cornerRadius: positiveNumber10,
styler: rangeBarStyleCallback,
itemStyler: rangeBarStyleCallback,
label: {
...seriesLabelOptionsDefs7,
placement: union9("inside", "outside"),
spacing: positiveNumber10
},
tooltip: tooltipOptionsDefs7,
shadow: shadowOptionsDefs5,
...commonSeriesThemeableOptionsDefs8,
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8,
highlight: multiSeriesHighlightOptionsDef8(barHighlightOptionsDef2, barHighlightOptionsDef2),
segmentation: shapeSegmentation3,
width: positiveNumberNonZero3,
widthRatio: ratio6
};
var sankeySeriesThemeableOptionsDef = {
fills: arrayOf6(colorUnion2),
strokes: arrayOf6(color4),
label: {
...seriesLabelOptionsDefs7,
spacing: positiveNumber10,
placement: union9("left", "right", "center"),
edgePlacement: union9("inside", "outside")
},
link: {
itemStyler: callbackDefs8({
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8
}),
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8
},
node: {
width: positiveNumber10,
spacing: positiveNumber10,
minSpacing: and2(positiveNumber10, lessThanOrEqual("spacing")),
alignment: union9("left", "center", "right", "justify"),
verticalAlignment: union9("top", "bottom", "center"),
sort: union9("data", "ascending", "descending", "auto"),
itemStyler: callbackDefs8({
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8
}),
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8
},
tooltip: tooltipOptionsDefs7,
...commonSeriesThemeableOptionsDefs8
};
var sunburstSeriesThemeableOptionsDef = {
fills: arrayOf6(colorUnion2),
strokes: arrayOf6(color4),
colorRange: arrayOf6(color4),
sectorSpacing: positiveNumber10,
cornerRadius: positiveNumber10,
padding: positiveNumber10,
itemStyler: callbackDefs8({
...fillOptionsDef7,
...strokeOptionsDef8
}),
label: {
spacing: positiveNumber10,
...autoSizedLabelOptionsDefs
},
secondaryLabel: autoSizedLabelOptionsDefs,
tooltip: tooltipOptionsDefs7,
...without6(commonSeriesThemeableOptionsDefs8, ["highlight", "showInLegend"]),
...without6(fillOptionsDef7, ["fill"]),
...without6(strokeOptionsDef8, ["stroke"]),
highlight: {
highlightedItem: hierarchyHighlightStyleOptionsDef,
highlightedBranch: hierarchyHighlightStyleOptionsDef,
unhighlightedItem: hierarchyHighlightStyleOptionsDef,
unhighlightedBranch: hierarchyHighlightStyleOptionsDef
}
};
var treemapSeriesThemeableOptionsDef = {
fills: arrayOf6(colorUnion2),
strokes: arrayOf6(color4),
colorRange: arrayOf6(color4),
itemStyler: callbackDefs8({
...fillOptionsDef7,
...strokeOptionsDef8
}),
group: {
gap: positiveNumber10,
padding: positiveNumber10,
cornerRadius: positiveNumber10,
textAlign: union9("left", "center", "right"),
interactive: boolean12,
highlight: {
highlightedItem: hierarchyHighlightStyleOptionsDef,
unhighlightedItem: hierarchyHighlightStyleOptionsDef
},
label: {
...seriesLabelOptionsDefs7,
spacing: positiveNumber10
},
...fillOptionsDef7,
...strokeOptionsDef8
},
tile: {
gap: positiveNumber10,
padding: positiveNumber10,
cornerRadius: positiveNumber10,
textAlign: union9("left", "center", "right"),
verticalAlign: union9("top", "middle", "bottom"),
label: {
...seriesLabelOptionsDefs7,
spacing: positiveNumber10,
lineHeight: positiveNumber10,
minimumFontSize: positiveNumber10,
wrapping: union9("never", "always", "hyphenate", "on-space"),
overflowStrategy: union9("ellipsis", "hide")
},
secondaryLabel: {
...seriesLabelOptionsDefs7,
lineHeight: positiveNumber10,
minimumFontSize: positiveNumber10,
wrapping: union9("never", "always", "hyphenate", "on-space"),
overflowStrategy: union9("ellipsis", "hide")
},
highlight: {
highlightedItem: hierarchyHighlightStyleOptionsDef,
highlightedBranch: hierarchyHighlightStyleOptionsDef,
unhighlightedItem: hierarchyHighlightStyleOptionsDef,
unhighlightedBranch: hierarchyHighlightStyleOptionsDef
},
...fillOptionsDef7,
...strokeOptionsDef8
},
tooltip: tooltipOptionsDefs7,
...without6(commonSeriesThemeableOptionsDefs8, ["highlight", "showInLegend"])
};
var waterfallSeriesItemOptionsDef = {
name: string12,
cornerRadius: positiveNumber10,
itemStyler: callbackDefs8({
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8,
cornerRadius: positiveNumber10
}),
label: {
...seriesLabelOptionsDefs7,
placement: union9("inside-start", "inside-center", "inside-end", "outside-start", "outside-end"),
spacing: positiveNumber10
},
tooltip: tooltipOptionsDefs7,
shadow: shadowOptionsDefs5,
...fillOptionsDef7,
...strokeOptionsDef8,
...lineDashOptionsDef8
};
var waterfallSeriesThemeableOptionsDef = {
direction: union9("horizontal", "vertical"),
showInMiniChart: boolean12,
item: {
positive: waterfallSeriesItemOptionsDef,
negative: waterfallSeriesItemOptionsDef,
total: waterfallSeriesItemOptionsDef
},
line: {
enabled: boolean12,
...strokeOptionsDef8,
...lineDashOptionsDef8
},
tooltip: tooltipOptionsDefs7,
width: positiveNumberNonZero3,
widthRatio: ratio6,
...commonSeriesThemeableOptionsDefs8
};
// packages/ag-charts-community/src/chart/themes/themeOptionsDef.ts
var serializableDate = optionsDefs3(
{
__type: required12(constant11("date")),
value: or4(string13, number11)
},
"a serializable date object"
);
var navigatorHandleOptionsDef = {
width: positiveNumber11,
height: positiveNumber11,
grip: boolean13,
fill: color5,
stroke: color5,
strokeWidth: positiveNumber11,
cornerRadius: positiveNumber11
};
var navigatorOptionsDef = {
enabled: boolean13,
height: positiveNumber11,
spacing: positiveNumber11,
cornerRadius: number11,
mask: {
fill: color5,
fillOpacity: ratio7,
stroke: color5,
strokeWidth: positiveNumber11
},
minHandle: navigatorHandleOptionsDef,
maxHandle: navigatorHandleOptionsDef,
miniChart: {
enabled: boolean13,
padding: {
top: positiveNumber11,
bottom: positiveNumber11
},
label: {
enabled: boolean13,
avoidCollisions: boolean13,
spacing: positiveNumber11,
format: numberFormatValidator4,
formatter: callbackOf4(textOrSegments4),
interval: {
minSpacing: positiveNumber11,
maxSpacing: positiveNumber11,
values: array3,
step: number11
},
...fontOptionsDef6
},
series: defined4
}
};
var scrollbarTrackOptionsDef = {
...fillOptionsDef8,
...strokeOptionsDef9,
...lineDashOptionsDef9,
cornerRadius: positiveNumber11,
opacity: ratio7
};
var scrollbarThumbOptionsDef = {
...scrollbarTrackOptionsDef,
minSize: positiveNumber11,
hoverStyle: {
fill: fillOptionsDef8.fill,
stroke: strokeOptionsDef9.stroke
}
};
var scrollbarBaseOptionsDef = {
enabled: boolean13,
thickness: positiveNumber11,
spacing: positiveNumber11,
tickSpacing: positiveNumber11,
visible: union10("auto", "always", "never"),
placement: union10("outer", "inner"),
track: scrollbarTrackOptionsDef,
thumb: scrollbarThumbOptionsDef
};
var scrollbarHorizontalOrientationOptionsDef = {
...scrollbarBaseOptionsDef,
position: union10("top", "bottom")
};
var scrollbarVerticalOrientationOptionsDef = {
...scrollbarBaseOptionsDef,
position: union10("left", "right")
};
var scrollbarOptionsDef = {
enabled: boolean13,
thickness: positiveNumber11,
spacing: positiveNumber11,
tickSpacing: positiveNumber11,
visible: union10("auto", "always", "never"),
placement: union10("outer", "inner"),
track: scrollbarTrackOptionsDef,
thumb: scrollbarThumbOptionsDef,
horizontal: scrollbarHorizontalOrientationOptionsDef,
vertical: scrollbarVerticalOrientationOptionsDef
};
var cartesianCrossLineThemeableOptionsDefs = without7(cartesianCrossLineOptionsDefs, ["type", "value", "range"]);
var cartesianAxesThemeDef = {
number: {
...without7(numberAxisOptionsDefs, ["type", "crossLines"]),
top: without7(numberAxisOptionsDefs, ["type", "crossLines", "position"]),
right: without7(numberAxisOptionsDefs, ["type", "crossLines", "position"]),
bottom: without7(numberAxisOptionsDefs, ["type", "crossLines", "position"]),
left: without7(numberAxisOptionsDefs, ["type", "crossLines", "position"]),
crossLines: cartesianCrossLineThemeableOptionsDefs
},
log: {
...without7(logAxisOptionsDefs, ["type", "crossLines"]),
top: without7(logAxisOptionsDefs, ["type", "crossLines", "position"]),
right: without7(logAxisOptionsDefs, ["type", "crossLines", "position"]),
bottom: without7(logAxisOptionsDefs, ["type", "crossLines", "position"]),
left: without7(logAxisOptionsDefs, ["type", "crossLines", "position"]),
crossLines: cartesianCrossLineThemeableOptionsDefs
},
category: {
...without7(categoryAxisOptionsDefs, ["type", "crossLines"]),
top: without7(categoryAxisOptionsDefs, ["type", "crossLines", "position"]),
right: without7(categoryAxisOptionsDefs, ["type", "crossLines", "position"]),
bottom: without7(categoryAxisOptionsDefs, ["type", "crossLines", "position"]),
left: without7(categoryAxisOptionsDefs, ["type", "crossLines", "position"]),
crossLines: cartesianCrossLineThemeableOptionsDefs
},
time: {
...without7(timeAxisOptionsDefs, ["type", "crossLines"]),
top: without7(timeAxisOptionsDefs, ["type", "crossLines", "position"]),
right: without7(timeAxisOptionsDefs, ["type", "crossLines", "position"]),
bottom: without7(timeAxisOptionsDefs, ["type", "crossLines", "position"]),
left: without7(timeAxisOptionsDefs, ["type", "crossLines", "position"]),
crossLines: cartesianCrossLineThemeableOptionsDefs
},
"unit-time": {
...without7(unitTimeAxisOptionsDefs, ["type", "crossLines"]),
top: without7(unitTimeAxisOptionsDefs, ["type", "crossLines", "position"]),
right: without7(unitTimeAxisOptionsDefs, ["type", "crossLines", "position"]),
bottom: without7(unitTimeAxisOptionsDefs, ["type", "crossLines", "position"]),
left: without7(unitTimeAxisOptionsDefs, ["type", "crossLines", "position"]),
crossLines: cartesianCrossLineThemeableOptionsDefs
},
"grouped-category": {
...without7(groupedCategoryAxisOptionsDefs, ["type"]),
top: without7(groupedCategoryAxisOptionsDefs, ["type", "position"]),
right: without7(groupedCategoryAxisOptionsDefs, ["type", "position"]),
bottom: without7(groupedCategoryAxisOptionsDefs, ["type", "position"]),
left: without7(groupedCategoryAxisOptionsDefs, ["type", "position"]),
crossLines: cartesianCrossLineThemeableOptionsDefs
},
"ordinal-time": {
...without7(ordinalTimeAxisOptionsDefs, ["type", "crossLines"]),
top: without7(ordinalTimeAxisOptionsDefs, ["type", "crossLines", "position"]),
right: without7(ordinalTimeAxisOptionsDefs, ["type", "crossLines", "position"]),
bottom: without7(ordinalTimeAxisOptionsDefs, ["type", "crossLines", "position"]),
left: without7(ordinalTimeAxisOptionsDefs, ["type", "crossLines", "position"]),
crossLines: cartesianCrossLineThemeableOptionsDefs
}
};
var polarAxesThemeDef = {
"angle-category": {
...without7(angleCategoryAxisOptionsDefs, ["type", "crossLines"]),
crossLines: without7(commonCrossLineOptionsDefs, ["type"])
},
"angle-number": {
...without7(angleNumberAxisOptionsDefs, ["type", "crossLines"]),
crossLines: without7(commonCrossLineOptionsDefs, ["type"])
},
"radius-category": {
...without7(radiusCategoryAxisOptionsDefs, ["type", "crossLines"]),
crossLines: {
...without7(commonCrossLineOptionsDefs, ["type"]),
label: {
...commonCrossLineLabelOptionsDefs,
positionAngle: number11
}
}
},
"radius-number": {
...without7(radiusNumberAxisOptionsDefs, ["type", "crossLines"]),
crossLines: {
...without7(commonCrossLineOptionsDefs, ["type"]),
label: {
...commonCrossLineLabelOptionsDefs,
positionAngle: number11
}
}
}
};
var undocumentedSeriesOptionsDef = {
visible: undocumented11(boolean13)
};
var themeOverridesOptionsDef = {
common: {
...commonChartOptionsDefs2,
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: boolean13
},
initialState: {
legend: arrayOfDefs6(
{
visible: boolean13,
seriesId: string13,
itemId: string13,
legendItemName: string13
},
"legend state array"
),
zoom: {
rangeX: {
start: or4(number11, serializableDate),
end: or4(number11, serializableDate)
},
rangeY: {
start: or4(number11, serializableDate),
end: or4(number11, serializableDate)
},
ratioX: {
start: ratio7,
end: ratio7
},
ratioY: {
start: ratio7,
end: ratio7
},
autoScaledAxes: arrayOf7(constant11("y"))
}
}
},
line: {
...commonChartOptionsDefs2,
axes: cartesianAxesThemeDef,
series: lineSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
scrollbar: scrollbarOptionsDef,
...undocumentedSeriesOptionsDef
},
scatter: {
...commonChartOptionsDefs2,
axes: cartesianAxesThemeDef,
series: scatterSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
scrollbar: scrollbarOptionsDef,
...undocumentedSeriesOptionsDef,
// @ts-expect-error undocumented option - required by grid
paired: undocumented11(boolean13)
},
bubble: {
...commonChartOptionsDefs2,
axes: cartesianAxesThemeDef,
series: bubbleSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
scrollbar: scrollbarOptionsDef,
...undocumentedSeriesOptionsDef
},
area: {
...commonChartOptionsDefs2,
axes: cartesianAxesThemeDef,
series: areaSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
scrollbar: scrollbarOptionsDef,
...undocumentedSeriesOptionsDef
},
bar: {
...commonChartOptionsDefs2,
axes: cartesianAxesThemeDef,
series: barSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
scrollbar: scrollbarOptionsDef,
...undocumentedSeriesOptionsDef
},
"box-plot": {
...commonChartOptionsDefs2,
axes: cartesianAxesThemeDef,
series: boxPlotSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
scrollbar: scrollbarOptionsDef,
...undocumentedSeriesOptionsDef
},
candlestick: {
...commonChartOptionsDefs2,
axes: cartesianAxesThemeDef,
series: candlestickSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
scrollbar: scrollbarOptionsDef,
...undocumentedSeriesOptionsDef
},
"cone-funnel": {
...commonChartOptionsDefs2,
axes: cartesianAxesThemeDef,
series: coneFunnelSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
scrollbar: scrollbarOptionsDef,
...undocumentedSeriesOptionsDef
},
funnel: {
...commonChartOptionsDefs2,
axes: cartesianAxesThemeDef,
series: funnelSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
scrollbar: scrollbarOptionsDef,
...undocumentedSeriesOptionsDef
},
ohlc: {
...commonChartOptionsDefs2,
axes: cartesianAxesThemeDef,
series: ohlcSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
scrollbar: scrollbarOptionsDef,
...undocumentedSeriesOptionsDef
},
histogram: {
...commonChartOptionsDefs2,
axes: without7(cartesianAxesThemeDef, ["category", "grouped-category", "unit-time", "ordinal-time"]),
series: histogramSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
scrollbar: scrollbarOptionsDef,
...undocumentedSeriesOptionsDef
},
heatmap: {
...commonChartOptionsDefs2,
axes: cartesianAxesThemeDef,
series: heatmapSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
scrollbar: scrollbarOptionsDef,
...undocumentedSeriesOptionsDef
},
waterfall: {
...commonChartOptionsDefs2,
axes: cartesianAxesThemeDef,
series: waterfallSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
scrollbar: scrollbarOptionsDef,
...undocumentedSeriesOptionsDef
},
"range-bar": {
...commonChartOptionsDefs2,
axes: cartesianAxesThemeDef,
series: rangeBarSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
scrollbar: scrollbarOptionsDef,
...undocumentedSeriesOptionsDef
},
"range-area": {
...commonChartOptionsDefs2,
axes: cartesianAxesThemeDef,
series: rangeAreaSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
scrollbar: scrollbarOptionsDef,
...undocumentedSeriesOptionsDef
},
donut: {
...commonChartOptionsDefs2,
series: donutSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
...undocumentedSeriesOptionsDef
},
pie: {
...commonChartOptionsDefs2,
series: pieSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
...undocumentedSeriesOptionsDef
},
"radar-line": {
...commonChartOptionsDefs2,
axes: polarAxesThemeDef,
series: radarLineSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
...undocumentedSeriesOptionsDef
},
"radar-area": {
...commonChartOptionsDefs2,
axes: polarAxesThemeDef,
series: radarAreaSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
...undocumentedSeriesOptionsDef
},
"radial-bar": {
...commonChartOptionsDefs2,
axes: polarAxesThemeDef,
series: radialBarSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
...undocumentedSeriesOptionsDef
},
"radial-column": {
...commonChartOptionsDefs2,
axes: polarAxesThemeDef,
series: radialColumnSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
...undocumentedSeriesOptionsDef
},
nightingale: {
...commonChartOptionsDefs2,
axes: polarAxesThemeDef,
series: nightingaleSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
...undocumentedSeriesOptionsDef
},
sunburst: {
...commonChartOptionsDefs2,
series: sunburstSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
...undocumentedSeriesOptionsDef
},
treemap: {
...commonChartOptionsDefs2,
series: treemapSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
...undocumentedSeriesOptionsDef
},
"map-shape": {
...commonChartOptionsDefs2,
series: mapShapeSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
...undocumentedSeriesOptionsDef
},
"map-line": {
...commonChartOptionsDefs2,
series: mapLineSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
...undocumentedSeriesOptionsDef
},
"map-marker": {
...commonChartOptionsDefs2,
series: mapMarkerSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
...undocumentedSeriesOptionsDef
},
"map-shape-background": {
...commonChartOptionsDefs2,
series: mapShapeBackgroundSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
...undocumentedSeriesOptionsDef
},
"map-line-background": {
...commonChartOptionsDefs2,
series: mapLineBackgroundSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
...undocumentedSeriesOptionsDef
},
sankey: {
...commonChartOptionsDefs2,
series: sankeySeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
...undocumentedSeriesOptionsDef
},
chord: {
...commonChartOptionsDefs2,
series: chordSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
...undocumentedSeriesOptionsDef
},
pyramid: {
...commonChartOptionsDefs2,
series: pyramidSeriesThemeableOptionsDef,
navigator: navigatorOptionsDef,
...undocumentedSeriesOptionsDef
},
"radial-gauge": {
...commonChartOptionsDefs2,
...radialGaugeSeriesThemeableOptionsDef,
targets: without7(radialGaugeTargetOptionsDef, ["value"]),
tooltip: {
...radialGaugeSeriesThemeableOptionsDef.tooltip,
...commonChartOptionsDefs2.tooltip
}
},
"linear-gauge": {
...commonChartOptionsDefs2,
...linearGaugeSeriesThemeableOptionsDef,
targets: without7(linearGaugeTargetOptionsDef, ["value"]),
tooltip: {
...linearGaugeSeriesThemeableOptionsDef.tooltip,
...commonChartOptionsDefs2.tooltip
}
}
};
function mapValues(object5, mapper) {
const result = {};
for (const key of Reflect.ownKeys(object5)) {
result[key] = mapper(object5[key], key, object5);
}
return result;
}
var themeOverridesOptionsWithOperatorsDef = mapValues(
themeOverridesOptionsDef,
function themeOperatorMapper(value, key) {
if (isSymbol(key))
return value;
if (isFunction(value)) {
return or4(value, themeOperator2, isSymbol);
} else if (isObject5(value)) {
return or4(
optionsDefs3(
unionSymbol in value ? mapValues(value, (val) => isObject5(val) ? mapValues(val, themeOperatorMapper) : val) : mapValues(value, themeOperatorMapper)
),
themeOperator2,
isSymbol
);
}
throw new Error(`Invalid theme override value: ${String(value)}`);
}
);
// packages/ag-charts-community/src/chart/themes/vividDark.ts
import {
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL as DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL11,
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR as DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR11,
getSequentialColors as getSequentialColors11
} from "ag-charts-core";
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: getSequentialColors11(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_COLOR11, VIVID_DARK_FILLS.BLUE);
params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL11, VIVID_DARK_FILLS.BLUE);
return params;
}
};
// packages/ag-charts-community/src/chart/themes/vividLight.ts
import {
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL as DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL12,
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR as DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR12,
getSequentialColors as getSequentialColors12
} from "ag-charts-core";
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: getSequentialColors12(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_COLOR12, VIVID_FILLS.BLUE);
params.set(DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL12, VIVID_FILLS.BLUE);
return params;
}
};
// packages/ag-charts-community/src/chart/mapping/themes.ts
var lightTheme = simpleMemorize(() => new ChartTheme());
var themeCacheDebug = Debug14.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 } = validate3(reduceThemeOptions(value), themeOptionsDef, "theme");
for (const error of invalid) {
Logger32.warnOnce(String(error));
}
const baseTheme = cleared?.baseTheme ? getChartTheme(cleared.baseTheme) : lightTheme();
return cleared ? new baseTheme.constructor(cleared) : baseTheme;
}
function reduceThemeOptions(options) {
if (!isObject6(options) || !isObject6(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: mergeDefaults7(...overrides),
params,
palette
};
}
var themeOptionsDef = {
baseTheme: or5(string14, object3),
overrides: themeOverridesOptionsWithOperatorsDef,
params: {
accentColor: color6,
axisColor: color6,
backgroundColor: color6,
borderColor: color6,
borderRadius: number12,
chartBackgroundColor: color6,
chartPadding: number12,
focusShadow: string14,
foregroundColor: color6,
fontFamily: fontFamilyFull,
fontSize: number12,
fontWeight,
gridLineColor: color6,
popupShadow: string14,
subtleTextColor: color6,
textColor: color6,
separationLinesColor: color6,
chromeBackgroundColor: color6,
chromeFontFamily: fontFamilyFull,
chromeFontSize: number12,
chromeFontWeight: fontWeight,
chromeSubtleTextColor: color6,
chromeTextColor: color6,
buttonBackgroundColor: color6,
buttonBorder: boolean14,
buttonFontWeight: fontWeight,
buttonTextColor: color6,
inputBackgroundColor: color6,
inputBorder: boolean14,
inputTextColor: color6,
menuBackgroundColor: color6,
menuBorder: boolean14,
menuTextColor: color6,
panelBackgroundColor: color6,
panelSubtleTextColor: color6,
tooltipBackgroundColor: color6,
tooltipBorder: boolean14,
tooltipTextColor: color6,
tooltipSubtleTextColor: color6,
crosshairLabelBackgroundColor: color6,
crosshairLabelTextColor: color6
},
palette: {
fills: arrayOf8(colorUnion3),
strokes: arrayOf8(color6),
up: { fill: or5(color6, gradientStrict), stroke: color6 },
down: { fill: or5(color6, gradientStrict), stroke: color6 },
neutral: { fill: or5(color6, gradientStrict), stroke: color6 }
}
};
var themeNameValidator = union11(
"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 } = validate3(
{ theme: value },
{ theme: or5(themeNameValidator, object3) }
);
for (const error of invalid) {
Logger32.warnOnce(String(error));
}
return invalid.length === 0;
}
// packages/ag-charts-community/src/module/optionsGraph.ts
import {
AdjacencyListGraph,
Debug as Debug16,
ModuleRegistry as ModuleRegistry9,
isObject as isObject7,
isObjectLike as isObjectLike3,
isPlainObject as isPlainObject8,
pick,
simpleMemorize as simpleMemorize3,
without as without9
} from "ag-charts-core";
// packages/ag-charts-community/src/module/optionsGraphOperations.ts
import {
Color as Color6,
Debug as Debug15,
Logger as Logger33,
ModuleRegistry as ModuleRegistry8,
circularSliceArray,
isArray as isArray13,
isGradientFill as isGradientFill4,
isImageFill as isImageFill3,
isNumber as isNumber2,
isObjectLike as isObjectLike2,
isPatternFill as isPatternFill4,
isPlainObject as isPlainObject7,
isString as isString4,
without as without8
} from "ag-charts-core";
// packages/ag-charts-community/src/module/optionsGraphUtils.ts
import { isNumber, isObjectLike } from "ag-charts-core";
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(object5, path) {
let result = object5;
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(object5, path) {
let result = object5;
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(object5, path, value) {
const pathLength = path.length;
if (pathLength === 0)
return;
let result = object5;
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 = ModuleRegistry8.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 Color6.mix(
Color6.fromString(foregroundColor),
Color6.fromString(backgroundColor),
1 - foregroundRatio
).toString();
}
Debug15.inDevelopmentMode(
() => Logger33.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 color8 = Color6.fromString(foregroundColor);
return new Color6(color8.r, color8.g, color8.b, opacity).toString();
}
Debug15.inDevelopmentMode(
() => Logger33.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 (!isArray13(colors) || !isNumber2(count))
return;
return Color6.interpolate(
colors.map((color8) => Color6.fromString(color8)),
count
).map((color8) => color8.toString());
}
function isGradientOperation(graph, vertex, values) {
const [valueVertex] = values;
const value = graph.resolveVertexValue(vertex, valueVertex);
return isGradientFill4(value);
}
function isImageOperation(graph, vertex, values) {
const [valueVertex] = values;
const value = graph.resolveVertexValue(vertex, valueVertex);
return isImageFill3(value);
}
function isPatternOperation(graph, vertex, values) {
const [valueVertex] = values;
const value = graph.resolveVertexValue(vertex, valueVertex);
return isPatternFill4(value);
}
function mixOperation(graph, vertex, values) {
const [colorAVertex, colorBVertex, ratioVertex] = values;
const colorA = graph.resolveVertexValue(vertex, colorAVertex);
const colorB = graph.resolveVertexValue(vertex, colorBVertex);
const ratio10 = graph.resolveVertexValue(vertex, ratioVertex);
const pathArray = graph.getPathArray(vertex);
const warningPrefix = `\`$mix\` json operation failed on [${String(colorA)}, ${String(colorB)}, ${String(ratio10)}] at [${pathArray.join(".")}], expecting`;
const warningMessage = `${warningPrefix} two colors and a number between 0 and 1.`;
if (typeof colorB !== "string" || !isRatio(ratio10)) {
Debug15.inDevelopmentMode(() => Logger33.warnOnce(warningMessage));
return;
}
if (typeof colorA === "string") {
try {
return Color6.mix(Color6.fromString(colorA), Color6.fromString(colorB), ratio10).toString();
} catch {
Debug15.inDevelopmentMode(() => Logger33.warnOnce(warningMessage));
return;
}
}
if (!isGradientFill4(colorA)) {
Debug15.inDevelopmentMode(() => Logger33.warnOnce(warningMessage));
return;
}
let colorStops = colorA.colorStops;
try {
colorStops = colorStops?.map((value) => {
let color8;
if (typeof value.color === "string") {
color8 = Color6.mix(Color6.fromString(value.color), Color6.fromString(colorB), ratio10).toString();
}
return { ...value, color: color8 };
});
} catch {
Debug15.inDevelopmentMode(
() => Logger33.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);
}
Debug15.inDevelopmentMode(
() => Logger33.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 first8 = true;
for (const valueVertex of values) {
const value = graph.resolveVertexValue(vertex, valueVertex);
if (first8) {
compare = value;
first8 = 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 (!isString4(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 (!isString4(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 (!isString4(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 (isString4(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 (!isString4(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 (!isString4(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 object5 = graph.getVertexValue(objectVertex);
if (!isPlainObject7(object5))
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 (isPlainObject7(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, object5, [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 (!isPlainObject7(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 (!isPlainObject7(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 object5 = graph.resolveVertexValue(vertex, objectVertex);
if (!Array.isArray(keys) || !isPlainObject7(object5))
return;
return without8(object5, keys);
}
function sizeOperation(graph, vertex, values) {
const [valueVertex] = values;
const value = graph.resolveVertexValue(vertex, valueVertex);
if (!isObjectLike2(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 = Debug16.create("opts", "options-graph");
var createOptionsGraph = simpleMemorize3(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 = isObject7(userOptions?.theme) ? paletteType(userOptions.theme?.palette) : "inbuilt";
const seriesType = userOptions.series?.[0]?.type ?? "line";
debug2("build user");
this.buildGraphFromObject(this.root, USER_OPTIONS_EDGE, without9(userOptions, ["theme"]));
debug2("build defaults");
this.buildGraphFromObject(this.root, DEFAULTS_EDGE, without9(config[seriesType], _OptionsGraph.COMPLEX_KEYS));
const seriesOverrides = overrides ? without9(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 ? without9(overrides.common, _OptionsGraph.COMPLEX_KEYS) : {};
if (Object.keys(commonOverrides).length > 0) {
debug2("build common overrides");
this.buildGraphFromObject(
this.root,
OVERRIDES_EDGE,
ModuleRegistry9.getSeriesModule(seriesType)?.chartType === "cartesian" ? commonOverrides : without9(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 = without9(
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 = without9(
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(from, to, edge) {
const hasEdge = (this.neighboursWithEdgeValue(from, edge)?.indexOf(to) ?? -1) !== -1;
if (this.isRollingBack && !hasEdge) {
this.rollbackEdgesFrom.push(from);
this.rollbackEdgesTo.push(to);
this.rollbackEdgesValue.push(edge);
}
super.addEdge(from, 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 object5 = {};
this.resolveVertexChildren(valueVertex, object5);
value = getPathSafe(object5, 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 (isObject7(targetConfigObject)) {
this.buildGraphFromObject(
target,
DEFAULTS_EDGE,
targetConfigObject,
targetPathArrayVertex,
void 0,
ignorePaths
);
}
if (this.overrides) {
const targetOverridesObject = getPathSafe(this.overrides, configPathArray);
if (isObject7(targetOverridesObject)) {
this.buildGraphFromObject(
target,
OVERRIDES_EDGE,
targetOverridesObject,
targetPathArrayVertex,
void 0,
ignorePaths
);
}
const commonOverridesObject = getPathSafe(this.overrides, ["common", ...configPathArray.slice(1)]);
if (isObject7(commonOverridesObject)) {
this.buildGraphFromObject(
target,
OVERRIDES_EDGE,
commonOverridesObject,
targetPathArrayVertex,
void 0,
ignorePaths
);
}
}
this.buildDependencyGraph();
}
/**
* Graft a given object onto the target vertex.
*/
graftObject(target, object5, overridesPathArrays, edgeValue = this.graftEdge) {
const pathArrayVertex = this.findNeighbour(target, PATH_ARRAY_EDGE);
this.buildGraphFromObject(target, edgeValue, object5, 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, object5, pathArrayVertex, shallowPaths = _OptionsGraph.SHALLOW_KEYS, ignorePaths) {
const keys = Object.keys(object5);
const operation = getOperation(object5, keys);
if (operation) {
const valueVertex = this.addVertex(object5);
this.addEdge(parentVertex, valueVertex, edgeValue);
this.buildGraphFromOperation(valueVertex, edgeValue, operation, pathArrayVertex);
return;
}
if (keys.length === 0) {
this.addEdge(parentVertex, this.addVertex(Array.isArray(object5) ? [] : {}), edgeValue);
this.buildGraphAutoEnable(parentVertex, edgeValue, object5, void 0);
return;
}
const pathVertices = this.getVertexChildrenByKey(parentVertex);
const pathArray = pathArrayVertex ? this.getVertexValue(pathArrayVertex) : [];
let enabledVertex;
if (Array.isArray(object5)) {
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, object5[key]);
} else {
this.buildGraphFromValue(
parentVertex,
childPathVertex,
edgeValue,
childPathArray,
object5[key],
shallowPaths
);
}
if (key === "enabled") {
enabledVertex = childPathVertex;
}
}
this.buildGraphAutoEnable(parentVertex, edgeValue, object5, enabledVertex);
}
buildGraphAutoEnable(parentVertex, edgeValue, object5, 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 } = object5;
this.addEdge(
autoEnableVertex,
this.addVertex({ enabled, _enabledFromTheme, keys: Object.keys(object5).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 (isObjectLike3(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 (isObjectLike3(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, object5 = 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, object5, prune);
return;
}
}
if (this.userPartialOptions == null && object5 === this.resolved && pathArray.length > 0) {
const resolvedVertexValue = getPathSafe(object5, pathArray);
if (resolvedVertexValue != null && !isPlainObject8(resolvedVertexValue)) {
return;
}
}
this.resolveVertexInEdgePriority(vertex, object5, pathArray, prune);
this.resolveVertexAutoEnable(vertex, object5, pathArray);
this.resolveVertexChildren(vertex, object5, prune);
}
resolveVertexInEdgePriority(vertex, object5, 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(object5, 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, object5, 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(object5, pathArray, true);
}
}
resolveVertexChildren(vertex, object5, 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, object5, 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 (isObject7(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 from = this.rollbackEdgesFrom[i];
const to = this.rollbackEdgesTo[i];
const edgeValue = this.rollbackEdgesValue[i];
this.removeEdge(from, 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: ChartAxisDirection8.X,
bottom: ChartAxisDirection8.X,
left: ChartAxisDirection8.Y,
right: ChartAxisDirection8.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 = jsonDiff5(
baseChartOptions.userOptions,
newUserOptions,
_ChartOptions.JSON_DIFF_OPTS
));
this.userOptions = deepClone5(merge(deltaOptions, baseChartOptions.userOptions), {
..._ChartOptions.OPTIONS_CLONE_OPTS_SLOW,
seen: []
});
} else {
this.userOptions = deepClone5(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;
}
Debug17.inDevelopmentMode(() => deepFreeze5(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 = ModuleRegistry10.getPresetModule(presetType);
if (presetDef?.processData) {
const { series, data } = presetDef.processData(deltaOptions.data);
deltaOptions = mergeDefaults8({ series, data }, deltaOptions);
}
}
this.fastSeriesSetup(deltaOptions, baseOptions);
const processedOptions = mergeDefaults8(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 = deepClone5(this.userOptions, _ChartOptions.OPTIONS_CLONE_OPTS_FAST);
if (deltaOptions) {
options = mergeDefaults8(deltaOptions, options);
if (stripSymbols) {
this.removeLeftoverSymbols(options);
}
}
let activeTheme = sanitizeThemeModules(getChartTheme(options.theme));
const { presetType } = this.optionMetadata;
if (presetType != null) {
const presetDef = ModuleRegistry10.getPresetModule(presetType);
if (presetDef) {
const { validate: validatePreset = validate4 } = 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 error of invalid) {
Logger34.warn(error);
}
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 = ModuleRegistry10.getChartModule(chartType);
if (!this.chartDef.placeholder) {
const { validate: validateChart = validate4 } = this.chartDef;
const { cleared, invalid } = validateChart(options, this.chartDef.options, "");
for (const error of invalid) {
Logger34.warn(error);
}
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 = mergeDefaults8(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", deepClone5(processedOptions)]);
return { activeTheme, processedOptions, themeParameters, annotationThemes, googleFonts, optionsGraph };
}
validatePluginOptions(options) {
for (const pluginDef of ModuleRegistry10.listModulesByType(ModuleType7.Plugin)) {
const pluginKey = pluginDef.name;
if (pluginKey in options && pluginDef.options != null && (!pluginDef.chartType || pluginDef.chartType === this.chartDef?.name)) {
const { cleared, invalid } = validate4(options[pluginKey], pluginDef.options, pluginDef.name);
for (const error of invalid) {
Logger34.warn(error);
}
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 = ModuleRegistry10.getSeriesModule(seriesOptions.type);
if (seriesDef == null) {
const isEnterprise = ModuleRegistry10.isEnterprise();
validSeriesTypes ?? (validSeriesTypes = joinFormatted(
Array.from(ExpectedModules.values()).filter(
(def) => def.type === ModuleType7.Series && (isEnterprise || !def.enterprise) && (!chartType || def.chartType === chartType)
).map((def) => def.name),
"or",
stringFormat
));
const modulePlaceholder = ExpectedModules.get(seriesOptions.type);
if (seriesOptions.type != null && modulePlaceholder?.type === ModuleType7.Series) {
missingModules.push(modulePlaceholder);
continue;
}
Logger34.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) {
Logger34.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 = validate4 } = seriesDef;
const { cleared, invalid } = validateSeries(seriesOptions, seriesDef.options, keyPath);
for (const error of invalid) {
Logger34.warn(error);
}
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 entries5(options.axes)) {
if (!axisOptions)
continue;
if (axisOptions.type == null) {
validatedAxesOptions[key] = axisOptions;
continue;
}
const keyPath = `axes.${unmappedAxisKeys?.get(key) ?? key}`;
const axisDef = ModuleRegistry10.getAxisModule(axisOptions.type);
if (axisDef == null) {
const isEnterprise = ModuleRegistry10.isEnterprise();
validAxesTypes ?? (validAxesTypes = joinFormatted(
Array.from(ExpectedModules.values()).filter(
(def) => def.type === ModuleType7.Axis && (isEnterprise || !def.enterprise) && def.chartType === chartType
).map((def) => def.name),
"or",
stringFormat
));
const modulePlaceholder = ExpectedModules.get(axisOptions.type);
if (modulePlaceholder?.type !== ModuleType7.Axis) {
Logger34.warn(
`Unknown type \`${axisOptions.type}\` at \`${keyPath}.type\`; expecting one of ${validAxesTypes}, ignoring.`
);
}
continue;
} else if (axisDef.chartType !== chartType) {
Logger34.warn(
`Axis type \`${axisDef.name}\` at \`${keyPath}.type\` is not supported by chart type \`${chartType}\`, ignoring.`
);
break;
}
const { validate: validateAxis = validate4 } = axisDef;
const { cleared, invalid } = validateAxis(axisOptions, axisDef.options, keyPath);
for (const error of invalid) {
Logger34.warn(error);
}
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 ?? jsonDiff5(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 = ModuleRegistry10.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 mergeDefaults8(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" ? [ChartAxisDirection8.Angle, ChartAxisDirection8.Radius] : [ChartAxisDirection8.X, ChartAxisDirection8.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 = isObject8(primarySeriesOptions) && "direction" in primarySeriesOptions && primarySeriesOptions.direction === "horizontal" && ModuleRegistry10.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 = ModuleRegistry10.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 && isObject8(options.axes[direction]) && !("position" in options.axes[direction]))
) {
for (const [axisKey, axisOptions] of entries5(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 entries5(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 entries5(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 = ModuleRegistry10.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 mapValues2(defaultAxes, (axis) => {
if (!("position" in axis))
return axis;
return axis.position === predictedAxis.position ? predictedAxis : axis;
});
}
return Object.fromEntries(axes);
}
cloneDefaultAxes(seriesType) {
const seriesModule = ModuleRegistry10.getSeriesModule(seriesType);
return seriesModule?.defaultAxes ? deepClone5(seriesModule.defaultAxes) : {};
}
predictAxesMissingTypesAndPositions(options, directions2, newAxes, defaultAxes) {
for (const [key, axis] of entries5(newAxes)) {
if (!isPlainObject9(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 entries5(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 === ChartAxisDirection8.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 entries5(newAxes)) {
if (!isPlainObject9(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 } = ModuleRegistry10.getSeriesModule(series.type);
if (series.grouped && !groupable) {
Logger34.warnOnce(`unsupported grouping of series type "${series.type}".`);
}
if ((series.stacked || series.stackGroup) && !stackable) {
Logger34.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 record = { groupType, seriesType, series: [], groupId };
groupMap.set(groupId, record);
result.push(record);
}
groupMap.get(groupId).series.push(series);
}
return result;
}, []);
}
soloSeriesIntegrity(options) {
if (!isArray14(options.series))
return;
const isSolo = (seriesType) => ModuleRegistry10.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)) {
Logger34.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 } = groupBy3(allSeries, (s) => isSolo(s.type) ? "solo" : "nonSolo");
const rejects = unique2(solo.map((s) => s.type)).join(", ");
Logger34.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 jsonWalk2(
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) {
jsonWalk2(options, _ChartOptions.removeDisabledOptionJson, /* @__PURE__ */ new Set(["data", "theme", "contextMenu"]));
}
static removeLeftoverSymbolsJson(optionsNode) {
if (!optionsNode || !isObject8(optionsNode))
return;
for (const key of Object.keys(optionsNode)) {
const value = optionsNode[key];
if (isSymbol2(value)) {
delete optionsNode[key];
}
}
}
removeLeftoverSymbols(options) {
jsonWalk2(options, _ChartOptions.removeLeftoverSymbolsJson, /* @__PURE__ */ new Set(["data"]));
}
specialOverridesDefaults(options) {
if (options.window == null) {
options.window = getWindow17();
} else {
setWindow(options.window);
}
if (options.document == null) {
options.document = getDocument5();
} 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 = Debug17.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 = Debug17.create(true, "opts");
var ChartOptions = _ChartOptions;
// packages/ag-charts-community/src/chart/chartProxy.ts
var debug3 = Debug18.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 = Debug18.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 = Debug18.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 = deepClone6(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, addIndex, remove, update } = transaction;
if (add != null && !Array.isArray(add)) {
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 (add == null || add.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 clone = await this.prepareResizedChart(this, this.chart, opts);
try {
clone.chart?.download(opts?.fileName, opts?.fileFormat);
} finally {
clone.destroy();
}
}
async __toSVG(opts) {
if (!this.chart)
throw new Error(DESTROYED_ERROR);
const clone = await this.prepareResizedChart(this, this.chart, { width: 600, height: 300, ...opts });
try {
return clone?.chart?.toSVG();
} finally {
clone?.destroy();
}
}
async getImageDataURL(opts) {
if (!this.chart)
throw new Error(DESTROYED_ERROR);
const clone = await this.prepareResizedChart(this, this.chart, opts);
try {
return clone.chart.getCanvasDataURL(opts?.fileFormat);
} finally {
clone.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 width = opts.width ?? chart.width ?? chart.ctx.scene.canvas.width;
const height = opts.height ?? chart.height ?? chart.ctx.scene.canvas.height;
const state = proxy.getState();
const processedOverrides = {
...chart.chartOptions.processedOverrides,
container: getDocument6().createElement("div"),
width,
height
};
if (opts.width != null && opts.height != null) {
processedOverrides.overrideDevicePixelRatio = 1;
}
const userOptions = chart.getOptions();
if (ModuleRegistry11.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(ChartUpdateType8.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(ChartUpdateType8.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(ChartUpdateType8.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(ChartUpdateType8.PROCESS_DATA, { forceNodeDataRefresh: true });
await this.chart?.waitForUpdate();
}
};
_AgChartInstanceProxy.chartInstances = /* @__PURE__ */ new WeakMap();
__decorateClass([
ActionOnSet4({
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
import { Debug as Debug19 } from "ag-charts-core";
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 = Debug19.create(true, "pool");
var Pool = _Pool;
// packages/ag-charts-community/src/api/agCharts.ts
var debug4 = Debug20.create(true, "opts");
var AgCharts = class {
static licenseCheck(options) {
if (this.licenseChecked)
return;
this.licenseManager = enterpriseRegistry2.licenseManager?.(options);
this.licenseManager?.validateLicense();
this.licenseChecked = true;
}
static getLicenseDetails(licenseKey) {
return enterpriseRegistry2.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 = Debug20.check("scene:stats", "scene:stats:verbose") ? performance.now() : void 0;
return debug4.group("AgCharts.create()", () => {
userOptions = Debug20.inDevelopmentMode(() => deepFreeze6(deepClone7(userOptions))) ?? userOptions;
this.licenseCheck(userOptions);
const chart = AgChartsInternal.createOrUpdate({
userOptions,
licenseManager: this.licenseManager,
optionsMetadata,
apiStartTime
});
if (this.licenseManager?.isDisplayWatermark()) {
enterpriseRegistry2.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 = enterpriseRegistry2.styles == null ? [] : [["ag-charts-enterprise", enterpriseRegistry2.styles]];
if (ModuleRegistry12.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", deepClone7(userOptions)]);
const { presetType } = optionsMetadata;
let mutableOptions = userOptions;
if (AgCharts.optionsMutationFn && mutableOptions) {
mutableOptions = AgCharts.optionsMutationFn(
deepClone7(mutableOptions, ChartOptions.OPTIONS_CLONE_OPTS_FAST),
presetType
);
debug4(() => [">>> AgCharts.createOrUpdate() MUTATED user options", deepClone7(mutableOptions)]);
}
const pool = this.getPool(optionsMetadata);
let create = false;
let poolResult;
let chart = proxy?.chart;
if (chart == null && pool?.hasFree()) {
poolResult = pool.obtainFree();
chart = poolResult.item;
}
const { document, window: userWindow, styleContainer, skipCss, ...options } = mutableOptions ?? {};
const baseOptions = chart?.getChartOptions();
const chartOptions = new ChartOptions(
baseOptions,
options,
processedOverrides,
{
...specialOverrides,
document,
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 {
create = 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 || create) {
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 = deepClone7(deltaOptions, ChartOptions.OPTIONS_CLONE_OPTS_FAST);
const stripSymbols = jsonWalk3(
deltaOptions,
_AgChartsInternal.markRemovedProperties,
/* @__PURE__ */ new Set(["data"]),
void 0,
void 0,
false
);
debug4(() => [">>> AgCharts.updateUserDelta() user delta", deepClone7(deltaOptions)]);
_AgChartsInternal.createOrUpdate({
proxy,
deltaOptions,
stripSymbols,
apiStartTime
});
}
static createChartInstance(options, oldChart) {
const transferableResource = oldChart?.destroy({ keepTransferableResources: true });
const chartType = detectChartType(options.processedOptions);
const chartDef = ModuleRegistry12.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 MementoCaretaker2(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 strictObjectKeys2(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/main.ts
import { ModuleRegistry as ModuleRegistry14 } from "ag-charts-core";
// 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: () => Arc,
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/cartesian/util.ts
import {
CARTESIAN_AXIS_TYPE as CARTESIAN_AXIS_TYPE2,
CARTESIAN_POSITION,
ChartAxisDirection as ChartAxisDirection9,
isArray as isArray15,
isDate,
isEmptyObject as isEmptyObject2,
isNumber as isNumber3,
isObject as isObject9,
isString as isString5
} from "ag-charts-core";
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 === ChartAxisDirection9.X ? xAxis : yAxis;
const { scale: scale2, direction } = axis;
const isXDirection = direction === ChartAxisDirection9.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 (isEmptyObject2(segment)) {
continue;
}
const { start, 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(start ?? startFallback) - offset;
let stopPosition = scale2.convert(stop ?? stopFallback) + 2 * offset;
const invalidStart = start != 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, ...style }) => {
const x0 = isXDirection ? start : -horizontalMargin;
const y0 = isXDirection ? -verticalMargin : start;
const x1 = isXDirection ? stop + bandwidth : seriesRect.width + horizontalMargin;
const y1 = isXDirection ? seriesRect.height + verticalMargin : stop + bandwidth;
return { clipRect: { x0, y0, x1, y1 }, ...style };
});
}
var TIME_AXIS_KEYS = /* @__PURE__ */ new Set(["time", "timestamp", "date", "datetime"]);
function predictCartesianAxis(direction, datum, seriesOptions, { allowPrimitiveTypes = true } = {}) {
if (direction !== ChartAxisDirection9.X && direction !== ChartAxisDirection9.Y)
return;
if (!isObject9(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: CARTESIAN_AXIS_TYPE2.NUMBER,
position
};
}
return {
type: CARTESIAN_AXIS_TYPE2.CATEGORY,
position
};
}
function predictCartesianNonPrimitiveAxis(direction, datum, seriesOptions) {
return predictCartesianAxis(direction, datum, seriesOptions, { allowPrimitiveTypes: false });
}
function predictCartesianFinancialAxis(direction, datum, seriesOptions) {
if (direction !== ChartAxisDirection9.X)
return;
if (!isObject9(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 (isString5(value)) {
return { type: "category", position };
}
}
function predictGroupedCategoryAxisType(value) {
if (isArray15(value) && value.every((v) => isString5(v) || v === null)) {
return CARTESIAN_AXIS_TYPE2.GROUPED_CATEGORY;
}
}
function predictTimeAxisType(key, value) {
if (isDate(value) || TIME_AXIS_KEYS.has(key) && isNumber3(value)) {
return CARTESIAN_AXIS_TYPE2.TIME;
}
}
function predictOrdinalTimeAxisType(key, value) {
if (isDate(value) || TIME_AXIS_KEYS.has(key) && isNumber3(value)) {
return CARTESIAN_AXIS_TYPE2.ORDINAL_TIME;
}
}
function getSeriesOptionsKey(direction, seriesOptions) {
if (direction === ChartAxisDirection9.X && "xKey" in seriesOptions) {
return seriesOptions.xKey;
} else if (direction === ChartAxisDirection9.Y && "yKey" in seriesOptions) {
return seriesOptions.yKey;
}
}
function getAxisPosition(direction, seriesOptions) {
if ("direction" in seriesOptions && seriesOptions.direction === "horizontal") {
return direction === ChartAxisDirection9.X ? CARTESIAN_POSITION.LEFT : CARTESIAN_POSITION.BOTTOM;
}
return direction === ChartAxisDirection9.X ? CARTESIAN_POSITION.BOTTOM : CARTESIAN_POSITION.LEFT;
}
// packages/ag-charts-community/src/chart/series/cartesian/cartesianSeries.ts
import {
ChartAxisDirection as ChartAxisDirection11,
Debug as Debug21,
DebugMetrics as DebugMetrics2,
Property as Property27,
StateMachine as StateMachine2,
extractDomain,
findMaxIndex as findMaxIndex4,
findMinIndex as findMinIndex4,
findMinMax as findMinMax9,
isFiniteNumber as isFiniteNumber5
} from "ag-charts-core";
// packages/ag-charts-community/src/scale/logScale.ts
import { createTicks as createTicks2, findMinMax as findMinMax8, findRangeExtent as findRangeExtent3, isDenseInterval as isDenseInterval2, isInteger, range as range2 } from "ag-charts-core";
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 log(base, domain, x) {
const start = Math.min(...domain);
const fn = logFunctions[base] ?? DEFAULT_LOG;
return start >= 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 start = Math.min(...domain);
const fn = powFunctions[base] ?? DEFAULT_POW;
return start >= 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) => log(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] = findMinMax8(this.domain);
if (min >= 0 !== max >= 0)
return Number.NaN;
return min >= 0 ? Math.log(x) : -Math.log(-x);
}
transformInvert(x) {
const [min, max] = findMinMax8(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(log(base, domain, d0)));
const n1 = pow(base, domain, roundStop(log(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 start = Math.min(d0, d1);
const stop = Math.max(d0, d1);
let p0 = this.log(start);
let p1 = this.log(stop);
if (interval) {
const inBounds = (tick) => tick >= start && tick <= stop;
const step = Math.min(Math.abs(interval), Math.abs(p1 - p0));
const { ticks: rangeTicks, count, firstTickIndex } = range2(p0, p1, step, visibleRange);
const ticks2 = rangeTicks.map(this.pow).filter(inBounds);
if (!isDenseInterval2(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 } = createTicks2(p0, p1, step, void 0, void 0, visibleRange);
return {
ticks: ticks2.map(this.pow),
count,
firstTickIndex
};
}
const ticks = [];
const isPositive = start > 0;
p0 = Math.floor(p0) - 1;
p1 = Math.round(p1) + 1;
const availableSpacing = findRangeExtent3(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 >= start && t <= stop && (k === 1 || fits || ticks.length === 0)) {
ticks.push(t);
lastTickPosition = tickPosition;
}
}
}
return filterVisibleTicks(ticks, isPositive, visibleRange);
}
};
// packages/ag-charts-community/src/scene/segmentedGroup.ts
import { SceneRefChangeDetection as SceneRefChangeDetection3, getPath2D as getPath2D3 } from "ag-charts-core";
// packages/ag-charts-community/src/scene/shape/segmentedPath.ts
import { SceneRefChangeDetection as SceneRefChangeDetection2, getPath2D as getPath2D2 } from "ag-charts-core";
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 = getPath2D2();
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([
SceneRefChangeDetection2()
], 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 = getPath2D3();
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([
SceneRefChangeDetection3()
], SegmentedGroup.prototype, "segments", 2);
// packages/ag-charts-community/src/scene/util/quadtree.ts
import { nearestSquared as nearestSquared3 } from "ag-charts-core";
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, height } = this.boundary;
const { capacity } = this;
const depth = this.maxdepth - 1;
const halfWidth = width / 2;
const halfHeight = height / 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 = nearestSquared3(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/numberAxis.ts
import { Property as Property25, normalisedExtentWithMetadata } from "ag-charts-core";
// packages/ag-charts-community/src/scale/linearScale.ts
import { createTicks as createTicks3, isDenseInterval as isDenseInterval3, niceTicksDomain as niceTicksDomain2, range as range3, tickStep } from "ag-charts-core";
var LinearScale = class _LinearScale extends ContinuousScale {
constructor() {
super([0, 1], [0, 1]);
this.type = "number";
}
static is(value) {
return value instanceof _LinearScale;
}
static getTickStep(start, stop, ticks) {
const { interval, tickCount = ContinuousScale.defaultTickCount, minTickCount, maxTickCount } = ticks;
return interval ?? tickStep(start, 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 (!isDenseInterval3((d1 - d0) / step, this.getPixelRange())) {
return range3(d0, d1, step, visibleRange);
}
}
return createTicks3(d0, d1, tickCount, minTickCount, maxTickCount, visibleRange);
}
niceDomain(ticks, domain = this.domain) {
if (domain.length < 2)
return [];
const { tickCount = ContinuousScale.defaultTickCount } = ticks;
let [start, stop] = domain;
if (tickCount === 1) {
[start, stop] = niceTicksDomain2(start, stop);
} else if (tickCount > 1) {
const roundStart = start > stop ? Math.ceil : Math.floor;
const roundStop = start > stop ? Math.floor : Math.ceil;
const maxAttempts = 4;
for (let i = 0; i < maxAttempts; i++) {
const prev0 = start;
const prev1 = stop;
const step = _LinearScale.getTickStep(start, stop, ticks);
const [d0, d1] = domain;
start = roundStart(d0 / step) * step;
stop = roundStop(d1 / step) * step;
if (start === prev0 && stop === prev1)
break;
}
}
return [ticks.nice[0] ? start : 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: extent6, clipped } = normalisedExtentWithMetadata(
d.domain,
min,
max,
preferredMin,
preferredMax,
void 0,
d.sortMetadata?.sortOrder
);
return { domain: extent6, 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 length = d1 - d0;
return [d0 + r0 * length, d1 - (1 - r1) * length];
}
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([
Property25
], NumberAxis.prototype, "min", 2);
__decorateClass([
Property25
], NumberAxis.prototype, "max", 2);
__decorateClass([
Property25
], NumberAxis.prototype, "preferredMin", 2);
__decorateClass([
Property25
], NumberAxis.prototype, "preferredMax", 2);
// packages/ag-charts-community/src/chart/axis/timeAxis.ts
import {
BaseProperties as BaseProperties17,
Logger as Logger35,
Property as Property26,
ProxyPropertyOnWrite as ProxyPropertyOnWrite4,
dateTruncationForDomain,
intervalEpoch,
intervalFloor as intervalFloor3,
intervalMilliseconds as intervalMilliseconds4,
intervalStep as intervalStep2,
intervalUnit as intervalUnit2,
lowestGranularityForInterval as lowestGranularityForInterval2,
lowestGranularityUnitForTicks,
lowestGranularityUnitForValue,
normalisedTimeExtentWithMetadata
} from "ag-charts-core";
var TimeAxisParentLevel = class extends BaseProperties17 {
constructor() {
super(...arguments);
this.enabled = false;
this.label = new AxisLabel();
this.tick = new AxisTick();
}
};
__decorateClass([
Property26
], TimeAxisParentLevel.prototype, "enabled", 2);
__decorateClass([
Property26
], TimeAxisParentLevel.prototype, "label", 2);
__decorateClass([
Property26
], 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) {
Logger35.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: extent6, clipped } = normalisedTimeExtentWithMetadata(
d,
this.min,
this.max,
this.preferredMin,
this.preferredMax
);
return { domain: extent6, clipped };
}
processData() {
super.processData();
const { boundSeries, direction, min, max } = this;
this.minimumTimeGranularity = minimumTimeAxisDatumGranularity(boundSeries, direction, min, max);
}
tickFormatParams(domain, ticks, _fractionDigits, timeInterval2) {
timeInterval2 ?? (timeInterval2 = lowestGranularityUnitForTicks(ticks));
const truncateDate = dateTruncationForDomain(domain);
const unit = intervalUnit2(timeInterval2);
const step = intervalStep2(timeInterval2);
const epoch = intervalEpoch(timeInterval2);
return { type: "date", unit, step, epoch, truncateDate };
}
datumFormatParams(value, params, _fractionDigits, timeInterval2, style) {
if (typeof value === "number") {
value = new Date(value);
}
if (timeInterval2 == null) {
const { minimumTimeGranularity } = this;
const datumGranularity = lowestGranularityUnitForValue(value);
if (minimumTimeGranularity != null && intervalMilliseconds4(minimumTimeGranularity) < intervalMilliseconds4(datumGranularity)) {
timeInterval2 = minimumTimeGranularity;
} else {
timeInterval2 = datumGranularity;
}
}
const { datum, seriesId, legendItemName, key, source, property, domain, boundSeries } = params;
const unit = intervalUnit2(timeInterval2);
const step = intervalStep2(timeInterval2);
const epoch = intervalEpoch(timeInterval2);
return {
type: "date",
value,
datum,
seriesId,
legendItemName,
key,
source,
property,
domain,
boundSeries,
unit,
step,
epoch,
style
};
}
};
TimeAxis.className = "TimeAxis";
TimeAxis.type = "time";
__decorateClass([
Property26
], TimeAxis.prototype, "parentLevel", 2);
__decorateClass([
Property26
], TimeAxis.prototype, "min", 2);
__decorateClass([
Property26
], TimeAxis.prototype, "max", 2);
__decorateClass([
Property26
], TimeAxis.prototype, "preferredMin", 2);
__decorateClass([
Property26
], TimeAxis.prototype, "preferredMax", 2);
__decorateClass([
Property26,
ProxyPropertyOnWrite4("_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 lowestGranularityForInterval2(minTimeInterval);
} else {
return calculateDefaultUnit(boundSeries, direction, min, max)?.unit;
}
}
function calculateDefaultUnit(boundSeries, direction, min, max) {
let start = Infinity;
let end2 = -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);
start = Math.min(start ?? Infinity, d0, d1);
end2 = Math.max(end2 ?? -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);
}
start = Math.min(start, min?.valueOf() ?? Infinity, max?.valueOf() ?? Infinity);
end2 = Math.max(end2, min?.valueOf() ?? -Infinity, max?.valueOf() ?? -Infinity);
if (!Number.isFinite(start) || !Number.isFinite(end2))
return;
interval ?? (interval = Math.abs(end2 - start));
interval = Math.min(interval, minNonZeroDifference(domainValues));
const unit = lowestGranularityForInterval2(interval);
let step = interval / intervalMilliseconds4(unit);
if (maxDataCount <= 2) {
step = Math.floor(step);
} else {
step = Math.round(step);
}
step = Math.max(step, 1);
const epoch = step === 1 ? void 0 : intervalFloor3(unit, start);
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/series/dataModelSeries.ts
import { ChartAxisDirection as ChartAxisDirection10, clamp as clamp16, objectsEqual as objectsEqual10 } from "ag-charts-core";
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[ChartAxisDirection10.X]?.scale;
const yScale = this.axes[ChartAxisDirection10.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 = clamp16(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 (objectsEqual10(categoryValue, xValue))
return datumIndex;
}
}
};
// packages/ag-charts-community/src/chart/series/cartesian/cartesianSeries.ts
var DEFAULT_CARTESIAN_DIRECTION_KEYS = {
[ChartAxisDirection11.X]: ["xKey"],
[ChartAxisDirection11.Y]: ["yKey"]
};
var DEFAULT_CARTESIAN_DIRECTION_NAMES = {
[ChartAxisDirection11.X]: ["xName"],
[ChartAxisDirection11.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([
Property27
], CartesianSeriesProperties.prototype, "xKeyAxis", 2);
__decorateClass([
Property27
], CartesianSeriesProperties.prototype, "yKeyAxis", 2);
__decorateClass([
Property27
], CartesianSeriesProperties.prototype, "legendItemName", 2);
__decorateClass([
Property27
], CartesianSeriesProperties.prototype, "pickOutsideVisibleMinorAxis", 2);
__decorateClass([
Property27
], 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 = Debug21.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 StateMachine2(
"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 === ChartAxisDirection11.X)
return this.properties.yKeyAxis;
if (direction === ChartAxisDirection11.Y)
return this.properties.xKeyAxis;
}
if (direction === ChartAxisDirection11.X)
return this.properties.xKeyAxis;
if (direction === ChartAxisDirection11.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[ChartAxisDirection11.X];
const yAxis = this.axes[ChartAxisDirection11.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) {
DebugMetrics2.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[ChartAxisDirection11.X];
const yAxis = axes[ChartAxisDirection11.Y];
const [axisX1, axisX2] = findMinMax9(xAxis?.range ?? [0, 1]);
const [axisY1, axisY2] = findMinMax9(yAxis?.range ?? [0, 1]);
const xSeriesDomain = extractDomain(this.getSeriesDomain(ChartAxisDirection11.X));
const xSeriesRange = [xAxis?.scale.convert(xSeriesDomain.at(0)), xAxis?.scale.convert(xSeriesDomain.at(-1))];
const ySeriesDomain = extractDomain(this.getSeriesDomain(ChartAxisDirection11.Y));
const ySeriesRange = [yAxis?.scale.convert(ySeriesDomain.at(0)), yAxis?.scale.convert(ySeriesDomain.at(-1))];
const [seriesX1, seriesX2] = findMinMax9(xSeriesRange);
const [seriesY1, seriesY2] = findMinMax9(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[ChartAxisDirection11.X];
const yAxis = axes[ChartAxisDirection11.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 distanceSquared = Math.max((hitPoint.x - datumX) ** 2 + (hitPoint.y - datumY) ** 2, 0);
if (distanceSquared < minDistanceSquared) {
minDistanceSquared = distanceSquared;
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 distance = Math.max(minDistance - (closestDatum.point?.size ?? 0) / 2, 0);
return { datum: closestDatum, distance };
}
}
pickNodeMainAxisFirst(point, requireCategoryAxis) {
const { x, y } = point;
const { axes, _contextNodeData: contextNodeData } = this;
const { pickOutsideVisibleMinorAxis } = this.properties;
if (!contextNodeData)
return;
const xAxis = axes[ChartAxisDirection11.X];
const yAxis = axes[ChartAxisDirection11.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 = ChartAxisDirection11.X] = directions2;
const hitPointCoords = [x, y];
if (majorDirection !== ChartAxisDirection11.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 !== ChartAxisDirection11.X) {
visible.reverse();
}
if (!visible[0] || !pickOutsideVisibleMinorAxis && !visible[1])
continue;
const datumPoint = [datumX, datumY];
if (majorDirection !== ChartAxisDirection11.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 [start, end2] = visibleRangeIndices(
sortOrder,
indices?.length ?? xValues.length,
visibleRange,
(topIndex) => {
const datumIndex = indices?.[topIndex] ?? topIndex;
return this.xCoordinateRange(xValues[datumIndex], pixelSize, datumIndex);
}
);
return start < end2 ? [start, end2] : [end2, start];
}
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 === ChartAxisDirection11.X ? ChartAxisDirection11.Y : ChartAxisDirection11.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[ChartAxisDirection11.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[ChartAxisDirection11.Y] : this.axes[ChartAxisDirection11.X];
const axis = shouldFlipXY ? this.axes[ChartAxisDirection11.X] : this.axes[ChartAxisDirection11.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 (!isFiniteNumber5(cross0) || !isFiniteNumber5(cross1) || !isFiniteNumber5(axis0) || !isFiniteNumber5(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: range4, domain } = scale2;
return {
type: "log",
convert: (d) => scale2.convert(d),
domain: [domain[0], domain[1]],
range: [range4[0], range4[1]]
};
} else if (scale2 instanceof ContinuousScale) {
const { range: range4, domain } = scale2;
return {
type: "continuous",
domain: [domain[0], domain[1]],
range: [range4[0], range4[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(ChartAxisDirection11)) {
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, length, range4, xValue) {
const range0 = range4[0].valueOf();
const range1 = range4[1].valueOf();
let xMinIndex = findMinIndex4(0, length - 1, (i) => {
const index = sortOrder === 1 ? i : length - i;
const x = xValue(index)?.valueOf();
return !Number.isFinite(x) || x >= range0;
});
let xMaxIndex = findMaxIndex4(0, length - 1, (i) => {
const index = sortOrder === 1 ? i : length - 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] = [length - xMaxIndex, length - xMinIndex];
}
xMinIndex = Math.max(xMinIndex, 0);
xMaxIndex = Math.min(xMaxIndex + 1, length);
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/chart/labelUtil.ts
import { mergeDefaults as mergeDefaults9 } from "ag-charts-core";
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 mergeDefaults9(stylerResult, styleParams);
}
return label;
}
function updateLabelNode(series, textNode, params, label, labelDatum, isHighlight, activeHighlight) {
if (series.visible && label.enabled && labelDatum) {
const style = 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 = style.color;
textNode.setAlign(labelDatum);
textNode.setFont(style);
textNode.setBoxing(style);
} 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 height = rect2.height * barDirection;
y = y0 + height * displacementRatio + spacing * textAlignment * barDirection;
textBaseline = textAlignment === barDirection ? "top" : "bottom";
} else {
const x0 = isUpward ? rect2.x : rect2.x + rect2.width;
const width = rect2.width * barDirection;
x = x0 + width * displacementRatio + spacing * textAlignment * barDirection;
textAlign = textAlignment === barDirection ? "left" : "right";
}
}
return { x, y, textAlign, textBaseline };
}
// 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/seriesMarker.ts
import {
ChangeDetectableProperties,
Property as Property28,
SceneChangeDetection as SceneChangeDetection3,
SceneObjectChangeDetection as SceneObjectChangeDetection2,
TRIPLE_EQ as TRIPLE_EQ3,
objectsEqual as objectsEqual11
} from "ag-charts-core";
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([
Property28,
SceneChangeDetection3()
], SeriesMarker.prototype, "enabled", 2);
__decorateClass([
Property28,
SceneObjectChangeDetection2({ equals: TRIPLE_EQ3 })
], SeriesMarker.prototype, "shape", 2);
__decorateClass([
Property28,
SceneChangeDetection3()
], SeriesMarker.prototype, "size", 2);
__decorateClass([
Property28,
SceneObjectChangeDetection2({ equals: objectsEqual11 })
], SeriesMarker.prototype, "fill", 2);
__decorateClass([
Property28,
SceneChangeDetection3()
], SeriesMarker.prototype, "fillOpacity", 2);
__decorateClass([
Property28,
SceneChangeDetection3()
], SeriesMarker.prototype, "stroke", 2);
__decorateClass([
Property28,
SceneChangeDetection3()
], SeriesMarker.prototype, "strokeWidth", 2);
__decorateClass([
Property28,
SceneChangeDetection3()
], SeriesMarker.prototype, "strokeOpacity", 2);
__decorateClass([
Property28
], SeriesMarker.prototype, "lineDash", 2);
__decorateClass([
Property28
], SeriesMarker.prototype, "lineDashOffset", 2);
__decorateClass([
Property28,
SceneObjectChangeDetection2({ equals: TRIPLE_EQ3 })
], SeriesMarker.prototype, "itemStyler", 2);
// packages/ag-charts-community/src/chart/series/seriesTooltip.ts
import {
BaseProperties as BaseProperties18,
Property as Property29,
callWithContext as callWithContext5,
isDate as isDate2,
isNumber as isNumber4,
isString as isString6,
mergeDefaults as mergeDefaults10,
toTextString as toTextString8
} from "ag-charts-core";
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 BaseProperties18 {
constructor() {
super(...arguments);
this.enabled = false;
}
};
__decorateClass([
Property29
], SeriesTooltipInteraction.prototype, "enabled", 2);
var SeriesTooltip = class extends BaseProperties18 {
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 : callWithContext5(callers, this.renderer, params);
if (isString6(overrides) || isNumber4(overrides) || isDate2(overrides)) {
return { type: "raw", rawHtmlString: toTextString8(overrides) };
}
if (overrides != null) {
const mergedMarker = mergeDefaults10(overrides.symbol?.marker, content.symbol?.marker);
const mergedLineInput = overrides.symbol?.line ?? content.symbol?.line ? mergeDefaults10(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([
Property29
], SeriesTooltip.prototype, "enabled", 2);
__decorateClass([
Property29
], SeriesTooltip.prototype, "showArrow", 2);
__decorateClass([
Property29
], SeriesTooltip.prototype, "renderer", 2);
__decorateClass([
Property29
], SeriesTooltip.prototype, "interaction", 2);
__decorateClass([
Property29
], SeriesTooltip.prototype, "position", 2);
__decorateClass([
Property29
], SeriesTooltip.prototype, "range", 2);
__decorateClass([
Property29
], SeriesTooltip.prototype, "class", 2);
function makeSeriesTooltip() {
return new SeriesTooltip();
}
// packages/ag-charts-community/src/chart/series/cartesian/abstractBarSeries.ts
import { ChartAxisDirection as ChartAxisDirection12, Property as Property32, extent as extent2, isFiniteNumber as isFiniteNumber7 } from "ag-charts-core";
// packages/ag-charts-community/src/chart/axis/categoryAxis.ts
import { ActionOnSet as ActionOnSet5, ChartUpdateType as ChartUpdateType9, Property as Property30, ProxyPropertyOnWrite as ProxyPropertyOnWrite5, isFiniteNumber as isFiniteNumber6 } from "ag-charts-core";
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 ChartUpdateType9.PROCESS_DOMAIN;
}
updateScale() {
super.updateScale();
let { paddingInner, paddingOuter } = this;
if (!isFiniteNumber6(paddingInner) || !isFiniteNumber6(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 } = gridLine;
const { stroke, strokeWidth = 0, lineDash } = style[tickIndex % style.length] ?? {};
return { tickId, offset, x1, y1, x2, y2, stroke, strokeWidth, lineDash };
}
calculateGridFills(ticks, p1, p2) {
const { horizontal, range: range4, 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 > range4[0] + scale2.step / 2;
const lastTick = ticks.at(-1);
const lastFillOffCanvas = horizontal && lastTick.translation < range4[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([
Property30
], _CategoryAxis.prototype, "groupPaddingInner", 2);
__decorateClass([
Property30
], _CategoryAxis.prototype, "paddingInner", 2);
__decorateClass([
Property30
], _CategoryAxis.prototype, "paddingOuter", 2);
__decorateClass([
ProxyPropertyOnWrite5("layoutConstraints", "align")
], _CategoryAxis.prototype, "bandAlignment", 2);
__decorateClass([
ActionOnSet5({
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/chart/axis/groupedCategoryAxis.ts
import {
BaseProperties as BaseProperties19,
PropertiesArray as PropertiesArray2,
Property as Property31,
angularPadding,
createIdsGenerator as createIdsGenerator2,
extent,
extractDomain as extractDomain2,
getMaxInnerRectSize as getMaxInnerRectSize2,
inRange,
isArray as isArray16,
isObject as isObject10,
isTruncated,
normalizeAngle360FromDegrees as normalizeAngle360FromDegrees7,
sortBasedOnArray,
toArray as toArray2,
toPlainText as toPlainText8,
wrapTextOrSegments as wrapTextOrSegments2
} from "ag-charts-core";
// 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 ratio10 = shift / subtrees;
wp.change -= ratio10;
wp.shift += shift;
wm.change += ratio10;
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(extent6, flip) {
let scaling = 1;
if (extent6 > 0) {
const { left, right } = this.dimensions;
if (right !== left) {
scaling = extent6 / (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 BaseProperties19 {
constructor() {
super(...arguments);
this.enabled = true;
this.border = new LabelBorder();
}
};
__decorateClass([
Property31
], DepthLabelProperties.prototype, "enabled", 2);
__decorateClass([
Property31
], DepthLabelProperties.prototype, "avoidCollisions", 2);
__decorateClass([
Property31
], DepthLabelProperties.prototype, "border", 2);
__decorateClass([
Property31
], DepthLabelProperties.prototype, "color", 2);
__decorateClass([
Property31
], DepthLabelProperties.prototype, "cornerRadius", 2);
__decorateClass([
Property31
], DepthLabelProperties.prototype, "spacing", 2);
__decorateClass([
Property31
], DepthLabelProperties.prototype, "rotation", 2);
__decorateClass([
Property31
], DepthLabelProperties.prototype, "wrapping", 2);
__decorateClass([
Property31
], DepthLabelProperties.prototype, "truncate", 2);
__decorateClass([
Property31
], DepthLabelProperties.prototype, "fill", 2);
__decorateClass([
Property31
], DepthLabelProperties.prototype, "fontStyle", 2);
__decorateClass([
Property31
], DepthLabelProperties.prototype, "fontWeight", 2);
__decorateClass([
Property31
], DepthLabelProperties.prototype, "fontSize", 2);
__decorateClass([
Property31
], DepthLabelProperties.prototype, "fontFamily", 2);
__decorateClass([
Property31
], DepthLabelProperties.prototype, "padding", 2);
var DepthTickProperties = class extends BaseProperties19 {
constructor() {
super(...arguments);
this.enabled = true;
}
};
__decorateClass([
Property31
], DepthTickProperties.prototype, "enabled", 2);
__decorateClass([
Property31
], DepthTickProperties.prototype, "width", 2);
__decorateClass([
Property31
], DepthTickProperties.prototype, "stroke", 2);
var DepthProperties = class extends BaseProperties19 {
constructor() {
super(...arguments);
this.label = new DepthLabelProperties();
this.tick = new DepthTickProperties();
}
};
__decorateClass([
Property31
], DepthProperties.prototype, "label", 2);
__decorateClass([
Property31
], 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 PropertiesArray2(DepthProperties);
this.includeInvisibleDomains = true;
this.tickScale.paddingInner = 1;
this.tickScale.paddingOuter = 0;
}
resizeTickTree() {
if (!this.tickTreeLayout)
return;
const { nodes } = this.tickTreeLayout;
const { range: range4, step, inset, bandwidth } = this.scale;
const width = Math.abs(range4[1] - range4[0]) - step;
const scaling = this.tickTreeLayout.scaling(width, range4[0] > range4[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: range4, depthOptions, horizontal, line } = this;
const scrollbar = this.chartLayout?.scrollbars?.[this.id];
const scrollbarThickness = this.getScrollbarThickness(scrollbar);
this.lineNode.datum = horizontal ? { x1: range4[0], x2: range4[1], y1: 0, y2: 0 } : { x1: 0, x2: 0, y1: range4[0], y2: range4[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, range4))
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 = getMaxInnerRectSize2(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 = wrapTextOrSegments2(text, wrapOptions) || text;
}
if (text !== inputText && isTruncated(text)) {
truncatedLabelText.set(index, toPlainText8(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 = normalizeAngle360FromDegrees7(optionsMap[depth]?.rotation);
const { width, height } = tempText.getBBox();
const labelSize = horizontal ? height : width;
if (depthLabelMaxSize[depth] < labelSize) {
depthLabelMaxSize[depth] = labelSize;
}
}
const idGenerator = createIdsGenerator2();
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 = normalizeAngle360FromDegrees7(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, height } = tempText.getBBox();
const labelSize = horizontal ? width : height;
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) => extractDomain2(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(array4) {
const seen = /* @__PURE__ */ new Set();
return array4.filter((item) => {
const key = isArray16(item) ? JSON.stringify(item) : item;
if (seen.has(key))
return false;
seen.add(key);
return true;
});
}
};
GroupedCategoryAxis.className = "GroupedCategoryAxis";
GroupedCategoryAxis.type = "grouped-category";
__decorateClass([
Property31
], 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 tickStep3 = 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 % tickStep3 === 0) {
visibleTickInfos.push(info);
}
removedIndex++;
}
return visibleTickInfos;
}
function convertIntegratedCategoryValue(datum) {
return toArray2(isObject10(datum) && "value" in datum ? datum.value : datum);
}
// packages/ag-charts-community/src/chart/series/cartesian/quadtreeUtil.ts
import { Logger as Logger36 } from "ag-charts-core";
function addHitTestersToQuadtree(quadtree, hitTesters) {
for (const node of hitTesters) {
const datum = node.datum;
if (datum === void 0) {
Logger36.error("undefined datum");
} else {
quadtree.addValue(node, datum);
}
}
}
function findQuadtreeMatch(series, point) {
const { x, y } = point;
const { nearest, distanceSquared } = series.getQuadTree().find(x, y);
if (nearest !== void 0) {
return { datum: nearest.value, distance: Math.sqrt(distanceSquared) };
}
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([
Property32
], AbstractBarSeriesProperties.prototype, "direction", 2);
__decorateClass([
Property32
], AbstractBarSeriesProperties.prototype, "width", 2);
__decorateClass([
Property32
], AbstractBarSeriesProperties.prototype, "widthRatio", 2);
var AbstractBarSeries = class extends CartesianSeries {
constructor() {
super(...arguments);
this.smallestDataInterval = void 0;
this.largestDataInterval = void 0;
}
padBandExtent(keys, alignStart) {
const ratio10 = typeof alignStart === "boolean" ? 1 : 0.5;
const scalePadding = isFiniteNumber7(this.smallestDataInterval) ? this.smallestDataInterval * ratio10 : 0;
const keysExtent = extent2(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() ? ChartAxisDirection12.X : ChartAxisDirection12.Y;
}
getCategoryDirection() {
return this.shouldFlipXY() ? ChartAxisDirection12.Y : ChartAxisDirection12.X;
}
getValueAxis() {
const direction = this.getBarDirection();
return this.axes[direction];
}
getCategoryAxis() {
const direction = this.getCategoryDirection();
return this.axes[direction];
}
getMinimumRangeSeries(ranges) {
const { width } = this.properties;
if (width == null)
return;
const axis = this.getCategoryAxis();
if (!axis)
return;
const { index } = this.ctx.seriesStateManager.getVisiblePeerGroupIndex(this);
ranges[index] = Math.max(ranges[index] ?? 0, width);
}
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 width = ranges.reduce((sum, range4) => sum + range4, 0);
const averageWidth = width / ranges.length;
const { visibleGroupCount } = this.ctx.seriesStateManager.getVisiblePeerGroupIndex(this);
const bandWidth = width + 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 } = 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 = width;
if (seriesGrouping == null && relativeWidth == null && groupScale) {
relativeWidth = this.getGroupScaleRangeWidth(groupScale);
}
if (relativeWidth == null && bandwidth < 1 && groupScale) {
return groupScale.rawBandwidth;
}
return (relativeWidth ?? bandwidth) * widthRatio;
}
if (width != null) {
return width;
}
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() === ChartAxisDirection12.X) {
if (direction === ChartAxisDirection12.X) {
return ChartAxisDirection12.Y;
}
return ChartAxisDirection12.X;
}
return direction;
}
initQuadTree(quadtree) {
addHitTestersToQuadtree(quadtree, this.datumNodesIter());
}
pickNodeClosestDatum(point) {
return findQuadtreeMatch(this, point);
}
};
// packages/ag-charts-community/src/chart/series/cartesian/lineUtil.ts
import {
SpanJoin as SpanJoin2,
areScalingEqual,
isScaleValid,
linearPoints,
smoothPoints,
spanRange as spanRange3,
stepPoints
} from "ag-charts-core";
// packages/ag-charts-community/src/chart/series/cartesian/lineInterpolationPlotting.ts
import {
SpanJoin,
solveBezier,
spanRange,
splitBezier2D
} from "ag-charts-core";
function lerp(a, b, ratio10) {
return (b - a) * ratio10 + 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 SpanJoin.MoveTo:
if (reversed) {
path.moveTo(x1, y1);
} else {
path.moveTo(x0, y0);
}
break;
case SpanJoin.LineTo:
if (reversed) {
path.lineTo(x1, y1);
} else {
path.lineTo(x0, y0);
}
break;
case SpanJoin.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 [start, end2] = spanRange(span);
plotStart(path, moveTo, start.x, start.y, end2.x, end2.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, ratio10) {
const [aStart, aEnd] = spanRange(a);
const [bStart, bEnd] = spanRange(b);
const x0 = lerp(aStart.x, bStart.x, ratio10);
const y0 = lerp(aStart.y, bStart.y, ratio10);
const x1 = lerp(aEnd.x, bEnd.x, ratio10);
const y1 = lerp(aEnd.y, bEnd.y, ratio10);
return [
{ x: x0, y: y0 },
{ x: x1, y: y1 }
];
}
function plotInterpolatedSpans(path, a, b, ratio10, moveTo, reversed) {
const [{ x: x0, y: y0 }, { x: x1, y: y1 }] = interpolatedSpanRange(a, b, ratio10);
plotStart(path, moveTo, x0, y0, x1, y1, reversed);
if (a.type === "cubic" && b.type === "cubic") {
const cp1x = lerp(a.cp1x, b.cp1x, ratio10);
const cp1y = lerp(a.cp1y, b.cp1y, ratio10);
const cp2x = lerp(a.cp2x, b.cp2x, ratio10);
const cp2y = lerp(a.cp2y, b.cp2y, ratio10);
plotCubic(path, x0, y0, cp1x, cp1y, cp2x, cp2y, x1, y1, reversed);
} else if (a.type === "step" && b.type === "step") {
const stepX = lerp(a.stepX, b.stepX, ratio10);
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 = lerp(as.leftCp1x, bs.leftCp1x, ratio10);
const leftCp1y = lerp(as.leftCp1y, bs.leftCp1y, ratio10);
const leftCp2x = lerp(as.leftCp2x, bs.leftCp2x, ratio10);
const leftCp2y = lerp(as.leftCp2y, bs.leftCp2y, ratio10);
const stepX = lerp(as.stepX, bs.stepX, ratio10);
const stepY0 = lerp(as.stepY0, bs.stepY0, ratio10);
const stepY1 = lerp(as.stepY1, bs.stepY1, ratio10);
const rightCp1x = lerp(as.rightCp1x, bs.rightCp1x, ratio10);
const rightCp1y = lerp(as.rightCp1y, bs.rightCp1y, ratio10);
const rightCp2x = lerp(as.rightCp2x, bs.rightCp2x, ratio10);
const rightCp2y = lerp(as.rightCp2y, bs.rightCp2y, ratio10);
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
import {
clipSpanX,
collapseSpanToPoint,
isUnitTimeCategoryScaling,
rescaleSpan,
spanRange as spanRange2,
transformIntegratedCategoryValue as transformIntegratedCategoryValue2
} from "ag-charts-core";
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 transformIntegratedCategoryValue2(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 range4 = spanRange2(span);
let start;
let end2;
if (data.scales.x?.type === "category") {
const step = (range4[1].x - range4[0].x) / (xIndices.xValue1Index - xIndices.xValue0Index);
start = range4[0].x + (xValue0Index - xIndices.xValue0Index) * step;
end2 = start + step;
} else {
const xValue0 = axisValues[xValue0Index];
const xValue1 = axisValues[xValue0Index + 1];
start = scale(xValue0, data.scales.x);
end2 = scale(xValue1, data.scales.x);
}
return clipSpanX(span, start, end2);
}
function axisZeroSpan(span, data) {
const [r0, r1] = spanRange2(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] = spanRange2(span);
return collapseSpanToPoint(span, {
x: (r0.x + r1.x) / 2,
y: (r0.y + r1.y) / 2
});
}
function collapseSpan(span, collapseMode, data, axisIndices, indices, range4) {
let xValue;
let yValue;
if (indices.xValue0Index >= range4.xValue1Index) {
const datumIndex = axisIndices.findLast((i) => i.xValue1Index <= range4.xValue1Index)?.datumIndex;
const datum = datumIndex == null ? void 0 : data.data[datumIndex];
xValue = datum?.xValue1;
yValue = datum?.yValue1;
} else if (indices.xValue0Index <= range4.xValue0Index) {
const datumIndex = axisIndices.find((i) => i.xValue0Index >= range4.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, range4, 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, range4);
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, range4, 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, range4);
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 }] = spanRange2(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, range4, 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, range4, out);
return;
} else if (oldIndices != null && newIndices == null) {
removeSpan(oldData, collapseMode, oldAxisIndices, oldIndices, newZeroData, range4, 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, range4, 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, range4, 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 range4 = {
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,
range4,
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 [start, end2] = spanRange3(span);
const join = lastPoint != null && pointsEq(lastPoint, start) ? SpanJoin2.Skip : SpanJoin2.MoveTo;
plotSpan(path, span, join, false);
lastPoint = end2;
}
}
function plotInterpolatedLinePathStroke(ratio10, path, spans) {
let lastPoint;
for (const span of spans) {
const [start, end2] = interpolatedSpanRange(span.from, span.to, ratio10);
const join = lastPoint != null && pointsEq(lastPoint, start) ? SpanJoin2.Skip : SpanJoin2.MoveTo;
plotInterpolatedSpans(path.path, span.from, span.to, ratio10, join, false);
lastPoint = end2;
}
}
function prepareLinePathStrokeAnimationFns(status, spans, visibleToggleMode, targetOpacity = 1) {
const removePhaseFn = (ratio10, path) => plotInterpolatedLinePathStroke(ratio10, path, spans.removed);
const updatePhaseFn = (ratio10, path) => plotInterpolatedLinePathStroke(ratio10, path, spans.moved);
const addPhaseFn = (ratio10, path) => plotInterpolatedLinePathStroke(ratio10, 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/barUtil.ts
import { ChartAxisDirection as ChartAxisDirection13, isNegative as isNegative3 } from "ag-charts-core";
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 isNegative3(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 width = isVertical ? datum.width : 0;
let height = isVertical ? 0 : datum.height;
const { opacity = 1 } = datum;
if (prevDatum && (Number.isNaN(x) || Number.isNaN(y))) {
({ x, y } = prevDatum);
width = isVertical ? prevDatum.width : 0;
height = 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 - height, width, height) : new BBox(x - width, y, width, height);
} else {
clipBBox = new BBox(x, y, width, height);
}
return { x, y, width, height, 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 ? ChartAxisDirection13.Y : ChartAxisDirection13.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, height, clipBBox, opacity = 1 }) {
return { x, y, width, height, 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, height } = datum;
return Transformable.toCanvas(series.contentGroup, new BBox(x, y, width, height));
}
// 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/areaUtil.ts
import { SpanJoin as SpanJoin3, isScaleValid as isScaleValid2, spanRange as spanRange4 } from "ag-charts-core";
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 } = spanRange4(span);
const { 0: pp0, 1: pp1 } = spanRange4(phantomSpan);
if (pointsEq(sp, sp0) && pointsEq(pp, pp0)) {
plotSpan(path, span, SpanJoin3.Skip, false);
} else {
for (let j = i - 1; j >= phantomSpanIndex; j -= 1) {
plotSpan(path, phantomSpans[j].span, SpanJoin3.LineTo, true);
}
path.closePath();
plotSpan(path, span, SpanJoin3.MoveTo, false);
phantomSpanIndex = i;
}
sp = sp1;
pp = pp1;
}
for (let j = spans.length - 1; j >= phantomSpanIndex; j -= 1) {
plotSpan(path, phantomSpans[j].span, SpanJoin3.LineTo, true);
}
path.closePath();
}
function plotInterpolatedAreaSeriesFillSpans(ratio10, { 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, ratio10, SpanJoin3.MoveTo, false);
plotInterpolatedSpans(path, reversedPhantomSpan.from, reversedPhantomSpan.to, ratio10, SpanJoin3.LineTo, true);
path.closePath();
}
}
function prepareAreaFillAnimationFns(status, spans, fillPhantomSpans, visibleToggleMode) {
const removePhaseFn = (ratio10, path) => plotInterpolatedAreaSeriesFillSpans(ratio10, path, spans.removed, fillPhantomSpans.removed);
const updatePhaseFn = (ratio10, path) => plotInterpolatedAreaSeriesFillSpans(ratio10, path, spans.moved, fillPhantomSpans.moved);
const addPhaseFn = (ratio10, path) => plotInterpolatedAreaSeriesFillSpans(ratio10, 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 || !isScaleValid2(newData.scales.x) || !isScaleValid2(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/diffUtil.ts
import { areScalingEqual as areScalingEqual2 } from "ag-charts-core";
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 (!areScalingEqual2(scales.x, prevScales.x))
return true;
if (!areScalingEqual2(scales.y, prevScales.y))
return true;
if (!isGroupScaleContext(contextNodeData) || !isGroupScaleContext(previousContextNodeData))
return false;
const groupScale = contextNodeData.groupScale;
const prevGroupScale = previousContextNodeData.groupScale;
return !areScalingEqual2(groupScale, prevGroupScale);
}
// packages/ag-charts-community/src/chart/series/cartesian/markerUtil.ts
import { clamp as clamp17, findRangeExtent as findRangeExtent4, inverseEaseOut } from "ag-charts-core";
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 = clamp17(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 style = series.getFormattedMarkerStyle(datum);
const anchor = Marker.anchor(style.shape);
const size = point.focusSize ?? style.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 ?? findRangeExtent4(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: width, seriesRectHeight: height } = nodeDataDependencies;
staticFromToMotion(
id,
"path_properties",
animationManager,
paths,
{ clipX: 0 },
{ clipX: width },
{
phase: "initial",
start: { clip: true, clipY: height, 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/polar/polarSeries.ts
import { ChartAxisDirection as ChartAxisDirection14, PolarZIndexMap, StateMachine as StateMachine3 } from "ag-charts-core";
var DEFAULT_POLAR_DIRECTION_KEYS = {
[ChartAxisDirection14.Angle]: ["angleKey"],
[ChartAxisDirection14.Radius]: ["radiusKey"]
};
var DEFAULT_POLAR_DIRECTION_NAMES = {
[ChartAxisDirection14.Angle]: ["angleName"],
[ChartAxisDirection14.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 = [ChartAxisDirection14.Angle, ChartAxisDirection14.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 StateMachine3(
"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 === ChartAxisDirection14.Angle)
return this.properties.angleKeyAxis;
if (direction === ChartAxisDirection14.Radius)
return this.properties.radiusKeyAxis;
}
setZIndex(zIndex) {
super.setZIndex(zIndex);
this.contentGroup.zIndex = [zIndex, PolarZIndexMap.FOREGROUND];
this.highlightGroup.zIndex = [zIndex, PolarZIndexMap.HIGHLIGHT];
this.labelGroup.zIndex = [zIndex, PolarZIndexMap.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/series/hierarchy/hierarchySeries.ts
import { Logger as Logger37, StateMachine as StateMachine4, arraysEqual as arraysEqual3, clamp as clamp18, mergeDefaults as mergeDefaults11 } from "ag-charts-core";
// packages/ag-charts-community/src/chart/series/hierarchy/hierarchySeriesProperties.ts
import { Property as Property33 } from "ag-charts-core";
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([
Property33
], HierarchySeriesProperties.prototype, "childrenKey", 2);
__decorateClass([
Property33
], HierarchySeriesProperties.prototype, "sizeKey", 2);
__decorateClass([
Property33
], HierarchySeriesProperties.prototype, "colorKey", 2);
__decorateClass([
Property33
], HierarchySeriesProperties.prototype, "colorName", 2);
__decorateClass([
Property33
], HierarchySeriesProperties.prototype, "fills", 2);
__decorateClass([
Property33
], HierarchySeriesProperties.prototype, "strokes", 2);
__decorateClass([
Property33
], 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, style) {
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 = style;
this.midPoint = { x: 0, y: 0 };
}
get hasChildren() {
return this.children.length > 0;
}
walk(callback4, order = _HierarchyNode.Walk.PreOrder) {
if (order === _HierarchyNode.Walk.PreOrder) {
callback4(this);
}
for (const child of this.children) {
child.walk(callback4, order);
}
if (order === _HierarchyNode.Walk.PostOrder) {
callback4(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 StateMachine4(
"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 style = this.getItemStyle({ datumIndex: indexPath, datum, depth, colorValue }, isLeaf, false);
return appendChildren(
new NodeClass(
this,
createDatumId(indexPath.join(";")),
indexPath,
datum,
sizeValue,
colorValue,
sumSize,
depth,
parent,
[],
style
),
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 (arraysEqual3(datum.datumIndex, indexPath)) {
return index - 1;
}
}
return 0;
}
pickFocus(opts) {
if (!this.rootNode?.children.length)
return void 0;
const index = clamp18(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] = clamp18(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)) {
Logger37.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 mergeDefaults11(highlight5.highlightedItem, highlight5.highlightedBranch);
case 3 /* Branch */:
return mergeDefaults11(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, range4) {
super();
this.domain = domain;
this.range = range4;
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/util/deferredExecutor.ts
import { getWindow as getWindow18 } from "ag-charts-core";
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 = getWindow18();
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 = getWindow18();
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(range4) {
const hasLevel = this._filters?.some((f) => f.maxRange > range4);
if (!hasLevel && this.executor.isPending()) {
const remaining = this.executor.demand();
if (remaining) {
this.mergeFilters(remaining);
}
}
}
/**
* Get the best filter for a given range.
*/
getFilterForRange(range4) {
return this._filters?.find((f) => f.maxRange > range4);
}
/**
* 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 ratio10 = this._dataLength > 0 ? dataLength / this._dataLength : 0;
if (ratio10 > 10 || ratio10 < 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/axis/polarAxis.ts
import { ChartAxisDirection as ChartAxisDirection15, Property as Property34 } from "ag-charts-core";
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 ? ChartAxisDirection15.Angle : ChartAxisDirection15.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([
Property34
], PolarAxis.prototype, "shape", 2);
__decorateClass([
Property34
], PolarAxis.prototype, "innerRadiusRatio", 2);
// 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: range4 } = 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 : range4[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 } = gridLine;
const { stroke, strokeWidth = 0, lineDash } = style[tickIndex % style.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: range4 } = 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 : range4[0];
const endOffset = nextTick ? translation + (nextTick.translation - translation) / 2 : range4[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: range4, 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 : range4[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/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/motion/pathMotion.ts
import { easeOut as easeOut2 } from "ag-charts-core";
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: easeOut2,
collapsable,
onUpdate(ratio10, preInit) {
if (preInit && phase !== "removed")
return;
path.path.clear(true);
updateFn(ratio10, 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/scene/dropShadow.ts
import { ChangeDetectableProperties as ChangeDetectableProperties2, Property as Property35 } from "ag-charts-core";
var DropShadow = class extends ChangeDetectableProperties2 {
constructor() {
super(...arguments);
this.enabled = true;
this.color = "rgba(0, 0, 0, 0.5)";
this.xOffset = 0;
this.yOffset = 0;
this.blur = 5;
}
};
__decorateClass([
Property35,
SceneChangeDetection()
], DropShadow.prototype, "enabled", 2);
__decorateClass([
Property35,
SceneChangeDetection()
], DropShadow.prototype, "color", 2);
__decorateClass([
Property35,
SceneChangeDetection()
], DropShadow.prototype, "xOffset", 2);
__decorateClass([
Property35,
SceneChangeDetection()
], DropShadow.prototype, "yOffset", 2);
__decorateClass([
Property35,
SceneChangeDetection()
], DropShadow.prototype, "blur", 2);
// packages/ag-charts-community/src/scene/util/sector.ts
import { angleBetween, isBetweenAngles, normalizeAngle180, normalizeAngle360 as normalizeAngle3602 } from "ag-charts-core";
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 angle = Math.atan2(y, x);
return startAngle < endAngle ? angle <= endAngle && angle >= startAngle : angle <= endAngle && angle >= -Math.PI || angle >= startAngle && angle <= 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 angle = Math.atan2(opposite, adjacent);
if (isBetweenAngles(angle, 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 start = 0;
let end2 = 1;
for (let i = 0; i < 8; i += 1) {
const s = (start + end2) / 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) {
start = s;
} else {
end2 = s;
}
}
return start;
}
var delta2 = 1e-6;
function clockwiseAngle(angle, relativeToStartAngle) {
if (angleBetween(angle, relativeToStartAngle) < delta2) {
return relativeToStartAngle;
} else {
return normalizeAngle3602(angle - relativeToStartAngle) + relativeToStartAngle;
}
}
function clockwiseAngles(startAngle, endAngle, relativeToStartAngle = 0) {
const fullPie = Math.abs(endAngle - startAngle) >= 2 * Math.PI;
const sweepAngle = fullPie ? 2 * Math.PI : normalizeAngle3602(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/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
import { ScaleAlignment as ScaleAlignment8 } from "ag-charts-core";
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 = ScaleAlignment8.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 first8 = firstBand.valueOf();
const last = lastBand.valueOf();
const ratio10 = (target - first8) / (last - first8);
const rawIndex = reversed ? (1 - ratio10) * (n - 1) : ratio10 * (n - 1);
if (alignment === ScaleAlignment8.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/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/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
import { SceneChangeDetection as SceneChangeDetection4, isNumberEqual as isNumberEqual3, normalizeAngle360 as normalizeAngle3603 } from "ag-charts-core";
var Arc = 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 isNumberEqual3(normalizeAngle3603(this.startAngle), normalizeAngle3603(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);
}
};
Arc.className = "Arc";
__decorateClass([
SceneChangeDetection4()
], Arc.prototype, "centerX", 2);
__decorateClass([
SceneChangeDetection4()
], Arc.prototype, "centerY", 2);
__decorateClass([
SceneChangeDetection4()
], Arc.prototype, "radius", 2);
__decorateClass([
SceneChangeDetection4()
], Arc.prototype, "startAngle", 2);
__decorateClass([
SceneChangeDetection4()
], Arc.prototype, "endAngle", 2);
__decorateClass([
SceneChangeDetection4()
], Arc.prototype, "counterClockwise", 2);
__decorateClass([
SceneChangeDetection4()
], Arc.prototype, "type", 2);
// packages/ag-charts-community/src/scene/shape/radialColumnShape.ts
import { SceneChangeDetection as SceneChangeDetection5, angleBetween as angleBetween2, isNumberEqual as isNumberEqual4, normalizeAngle360 as normalizeAngle3604 } from "ag-charts-core";
function rotatePoint2(x, y, rotation) {
const radius = Math.hypot(x, y);
const angle = Math.atan2(y, x);
const rotated = angle + 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 = angleBetween2(startAngle, endAngle);
return normalizeAngle3604(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 angle = Math.atan2(y, x);
return { y, angle };
}
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 = isNumberEqual4(innerRadius, axisInnerRadius);
const isTouchingOuter = isNumberEqual4(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([
SceneChangeDetection5()
], RadialColumnShape.prototype, "isBeveled", 2);
__decorateClass([
SceneChangeDetection5()
], RadialColumnShape.prototype, "columnWidth", 2);
__decorateClass([
SceneChangeDetection5()
], RadialColumnShape.prototype, "startAngle", 2);
__decorateClass([
SceneChangeDetection5()
], RadialColumnShape.prototype, "endAngle", 2);
__decorateClass([
SceneChangeDetection5()
], RadialColumnShape.prototype, "outerRadius", 2);
__decorateClass([
SceneChangeDetection5()
], RadialColumnShape.prototype, "innerRadius", 2);
__decorateClass([
SceneChangeDetection5()
], RadialColumnShape.prototype, "axisInnerRadius", 2);
__decorateClass([
SceneChangeDetection5()
], RadialColumnShape.prototype, "axisOuterRadius", 2);
function getRadialColumnWidth(startAngle, endAngle, axisOuterRadius, columnWidthRatio, maxColumnWidthRatio) {
const rotation = angleBetween2(startAngle, endAngle);
const pad = rotation * (1 - columnWidthRatio) / 2;
startAngle += pad;
endAngle -= pad;
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/scene/shape/sector.ts
import { SceneChangeDetection as SceneChangeDetection6, SceneObjectChangeDetection as SceneObjectChangeDetection3 } from "ag-charts-core";
var Arc2 = 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, start, 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 angle = start ? startAngle + angleOffset + angleSweep : endAngle - angleOffset - angleSweep;
const radius = inner ? innerRadius + r : outerRadius - r;
const cx = radius * Math.cos(angle);
const cy = radius * Math.sin(angle);
if (clipSector != null) {
const delta3 = 1e-6;
if (!start && !(angle >= startAngle - delta3 && angle <= clipSector.endAngle - delta3))
return;
if (start && !(angle >= clipSector.startAngle + delta3 && angle <= endAngle - delta3))
return;
if (inner && radius < clipSector.innerRadius - delta3)
return;
if (!inner && radius > clipSector.outerRadius + delta3)
return;
}
const arc = new Arc2(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 (start) {
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 (start) {
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 (start) {
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(start === inner ? arc.a0 : arc.a1);
const theta = clockwiseAngle(Math.atan2(y, x), startAngle);
const radialArc = inner ? innerArc : outerArc;
if (start) {
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 Arc2(
0,
0,
outerArcRadius,
startAngle + outerArcRadiusOffset,
endAngle - outerArcRadiusOffset
);
const innerArcRadius = clipSector?.innerRadius ?? innerRadius;
const innerArcRadiusOffset = this.getAngleOffset(innerArcRadius);
const innerArc = hasInnerSweep ? new Arc2(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([
SceneChangeDetection6()
], Sector.prototype, "centerX", 2);
__decorateClass([
SceneChangeDetection6()
], Sector.prototype, "centerY", 2);
__decorateClass([
SceneChangeDetection6()
], Sector.prototype, "innerRadius", 2);
__decorateClass([
SceneChangeDetection6()
], Sector.prototype, "outerRadius", 2);
__decorateClass([
SceneChangeDetection6()
], Sector.prototype, "startAngle", 2);
__decorateClass([
SceneChangeDetection6()
], Sector.prototype, "endAngle", 2);
__decorateClass([
SceneObjectChangeDetection3({ equals: (lhs, rhs) => lhs.equals(rhs) })
], Sector.prototype, "clipSector", 2);
__decorateClass([
SceneChangeDetection6()
], Sector.prototype, "concentricEdgeInset", 2);
__decorateClass([
SceneChangeDetection6()
], Sector.prototype, "radialEdgeInset", 2);
__decorateClass([
SceneChangeDetection6()
], Sector.prototype, "startOuterCornerRadius", 2);
__decorateClass([
SceneChangeDetection6()
], Sector.prototype, "endOuterCornerRadius", 2);
__decorateClass([
SceneChangeDetection6()
], Sector.prototype, "startInnerCornerRadius", 2);
__decorateClass([
SceneChangeDetection6()
], Sector.prototype, "endInnerCornerRadius", 2);
// packages/ag-charts-community/src/components/menu/menu.ts
import { createElement as createElement17, getIconClassNames } from "ag-charts-core";
// packages/ag-charts-community/src/widget/menuItemWidget.ts
import { createElement as createElement15, setAttribute as setAttribute12 } from "ag-charts-core";
var MenuItemWidget = class extends AbstractButtonWidget {
constructor() {
super(createElement15("div"), "menuitem");
}
};
var MenuItemRadioWidget = class extends AbstractButtonWidget {
constructor() {
super(createElement15("div"), "menuitemradio");
}
setChecked(checked) {
setAttribute12(this.elem, "aria-checked", checked);
}
};
// packages/ag-charts-community/src/widget/menuWidget.ts
import {
CleanupRegistry as CleanupRegistry19,
addEscapeEventListener,
addMouseCloseListener,
addOverrideFocusVisibleEventListener,
addTouchCloseListener,
createElementId as createElementId2,
getDocument as getDocument7,
getLastFocus,
hasNoModifiers as hasNoModifiers2,
setAttribute as setAttribute13
} from "ag-charts-core";
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 = getDocument7().createElement("div");
setAttribute13(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 = createElementId2();
const expand = () => {
this.collapseExpandedSubMenu(subMenu);
subMenuButton.expandControlled();
};
const arrowRightOpener = (ev) => {
if (hasNoModifiers2(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 CleanupRegistry19()
};
const scope = this.expansionScope;
const buttons = this.children.map((value) => value.getElement());
setAttribute13(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 */ });
setAttribute13(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/anchoredPopover.ts
import { clamp as clamp19, getWindow as getWindow19 } from "ag-charts-core";
// packages/ag-charts-community/src/components/popover/popover.ts
import { AbstractModuleInstance as AbstractModuleInstance2, createElement as createElement16, getLastFocus as getLastFocus2 } from "ag-charts-core";
var canvasOverlay = "canvas-overlay";
var Popover = class extends AbstractModuleInstance2 {
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 = createElement16("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 = createElement16("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 = getLastFocus2(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);
getWindow19().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: width, offsetHeight: height } = popover;
let x = clamp19(0, anchor.x, canvasRect.width - width);
let y = clamp19(0, anchor.y, canvasRect.height - height);
if (x !== anchor.x && fallbackAnchor?.x != null) {
x = clamp19(0, fallbackAnchor.x - width, canvasRect.width - width);
}
if (y !== anchor.y && fallbackAnchor?.y != null) {
y = clamp19(0, fallbackAnchor.y - height, canvasRect.height - height);
}
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 = createElement17("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 = createElement17("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
import { Vec2 } from "ag-charts-core";
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: Vec2.from(event.clientX, event.clientY),
position: Vec2.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 = Vec2.sub(Vec2.from(event.clientX, event.clientY), dragStartState.client);
const position = Vec2.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
import { getIconClassNames as getIconClassNames2, setAttribute as setAttribute14 } from "ag-charts-core";
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="${getIconClassNames2(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) {
setAttribute14(this.elem, "aria-checked", checked);
}
};
// packages/ag-charts-community/src/components/toolbar/toolbar.ts
import { BaseProperties as BaseProperties20 } from "ag-charts-core";
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 first8;
let last;
let section;
for (const [index, buttonWidget] of buttonWidgets.entries()) {
first8 = !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", first8);
buttonWidget.toggleClass("ag-charts-toolbar__button--last", last);
buttonWidget.toggleClass("ag-charts-toolbar__button--gap", index > 0 && first8);
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 BaseProperties20 ? 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
import { clamp as clamp20, createElement as createElement18, getIconClassNames as getIconClassNames3 } from "ag-charts-core";
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: width, offsetHeight: height } = element2;
let top = anchor.y - height - verticalSpacing;
let left = anchor.x - width / 2;
if (position === "below") {
top = anchor.y + verticalSpacing;
} else if (position === "right") {
top = anchor.y - height / 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(clamp20(0, position.x, canvasRect.width - bounds.width));
position.y = Math.floor(clamp20(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(createElement18("div", "ag-charts-floating-toolbar__drag-handle"));
const icon = new NativeWidget(
createElement18("span", `${getIconClassNames3("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/api/preset/presetModules.ts
import {
and as and3,
boolean as boolean15,
color as color7,
date as date2,
defined as defined5,
greaterThan as greaterThan2,
lessThan as lessThan2,
lineDashOptionsDef as lineDashOptionsDef10,
number as number13,
or as or6,
positiveNumber as positiveNumber12,
ratio as ratio8,
string as string15,
strokeOptionsDef as strokeOptionsDef10,
typeUnion,
undocumented as undocumented12,
without as without10
} from "ag-charts-core";
// packages/ag-charts-community/src/api/preset/sparkline.ts
import {
DEFAULT_SPARKLINE_CROSSHAIR_STROKE as DEFAULT_SPARKLINE_CROSSHAIR_STROKE2,
isDate as isDate3,
isNumber as isNumber5,
isString as isString7,
simpleMemorize as simpleMemorize4,
toTextString as toTextString9
} from "ag-charts-core";
var commonAxisProperties = {
title: {
enabled: false
},
label: {
enabled: false
},
line: {
enabled: false
},
gridLine: {
enabled: false
},
crosshair: {
enabled: false,
stroke: DEFAULT_SPARKLINE_CROSSHAIR_STROKE2,
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 = simpleMemorize4(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 = simpleMemorize4((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 (isString7(userContent) || isNumber5(userContent) || isDate3(userContent)) {
return toTextString9(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,
listeners,
locale,
minHeight,
minWidth,
overrideDevicePixelRatio,
padding: padding2,
width,
theme: baseTheme,
data: baseData,
crosshair,
axis,
min,
max,
tooltip,
context,
styleNonce,
...seriesOptions
} = opts;
const chartOpts = {
background,
container,
foreground,
height,
listeners,
locale,
minHeight,
minWidth,
overrideDevicePixelRatio,
padding: padding2,
width,
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: boolean15,
reverse: boolean15,
stroke: color7,
strokeWidth: positiveNumber12
};
var commonSparklineOptionsDef = {
context: () => true,
tooltip: defined5,
theme: defined5,
background: defined5,
container: defined5,
width: defined5,
height: defined5,
minWidth: defined5,
minHeight: defined5,
padding: defined5,
listeners: defined5,
locale: defined5,
data: defined5,
styleNonce: string15,
axis: typeUnion(
{
number: {
...commonSparklineAxisOptionsDef,
min: and3(number13, lessThan2("max")),
max: and3(number13, greaterThan2("min"))
},
category: {
...commonSparklineAxisOptionsDef,
paddingInner: ratio8,
paddingOuter: ratio8
},
time: {
...commonSparklineAxisOptionsDef,
min: and3(or6(number13, date2), lessThan2("max")),
max: and3(or6(number13, date2), greaterThan2("min"))
}
},
"axis options",
"category"
),
min: and3(number13, lessThan2("max")),
max: and3(number13, greaterThan2("min")),
crosshair: {
enabled: boolean15,
snap: boolean15,
...strokeOptionsDef10,
...lineDashOptionsDef10
},
xKey: string15,
yKey: string15
};
commonSparklineOptionsDef.overrideDevicePixelRatio = undocumented12(number13);
commonSparklineOptionsDef.foreground = undocumented12(defined5);
var SparklinePresetModule = {
type: "preset",
name: "sparkline",
version: VERSION,
options: typeUnion(
{
area: {
...commonSparklineOptionsDef,
...without10(areaSeriesOptionsDef, commonSparklineOmit)
},
bar: {
...commonSparklineOptionsDef,
...without10(barSeriesOptionsDef, commonSparklineOmit)
},
line: {
...commonSparklineOptionsDef,
...without10(lineSeriesOptionsDef, commonSparklineOmit)
}
},
"sparkline options"
),
create: sparkline,
processData: sparklineDataPreset
};
// packages/ag-charts-community/src/chart/cartesianChartModule.ts
import { ValidationError, isObject as isObject11, validate as validate5, without as without11 } from "ag-charts-core";
// packages/ag-charts-community/src/chart/cartesianChart.ts
import {
ActionOnSet as ActionOnSet6,
ChartAxisDirection as ChartAxisDirection16,
Logger as Logger38,
clampArray as clampArray3,
entries as entries6,
fromPairs,
groupBy as groupBy4
} from "ag-charts-core";
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) {
Logger38.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 === ChartAxisDirection16.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 = groupBy4(this.axes, (axis) => axis.position ?? "left");
const newAxisAreaWidths = /* @__PURE__ */ new Map();
const axisOffsets = /* @__PURE__ */ new Map();
for (const [position, axes] of entries6(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 entries6(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: range4
} = 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) ? range4[domain[0]] : clampArray3(crossPosition, range4);
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 === ChartAxisDirection16.X;
case "horizontal-line":
return axis.direction === ChartAxisDirection16.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 === ChartAxisDirection16.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 entries6(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, height } = seriesRect;
const maxEnd = isLeftRight ? height : width;
let start = 0;
let end2 = maxEnd;
let { min, max } = this.ctx.zoomManager.getAxisZoom(axis.id);
const { width: axisWidth, unit, align: align2 } = axis.layoutConstraints;
if (unit === "px") {
end2 = start + axisWidth;
} else {
end2 = end2 * axisWidth / 100;
}
const size = end2 - start;
if (align2 === "end") {
start = maxEnd - size;
end2 = maxEnd;
} else if (align2 === "center") {
const center = start + (maxEnd - start) / 2;
start = center - size / 2;
end2 = center + size / 2;
} else if (align2 === "justify") {
end2 = maxEnd;
}
if (isLeftRight) {
if (isNumberAxis) {
[start, end2] = [end2, start];
} else {
[min, max] = [1 - max, 1 - min];
}
}
axis.range = [start, end2];
axis.visibleRange = [min, max];
axis.gridLength = isLeftRight ? width : height;
axis.lineRange = isLeftRight ? [height, 0] : [0, width];
}
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, height } = seriesRect;
width += axis.direction === ChartAxisDirection16.X ? gridLinePadding : axisLinePadding;
height += axis.direction === ChartAxisDirection16.Y ? gridLinePadding : axisLinePadding;
axis.clipGrid(seriesRect.x, seriesRect.y, width, height);
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([
ActionOnSet6({
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) => isObject11(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, optionsDefs4, 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 = without11(options, ["axes"]);
}
}
const result = validate5(options, optionsDefs4, path);
result.invalid.push(...additionalErrors);
return result;
}
};
// packages/ag-charts-community/src/chart/polarChartModule.ts
import { UnknownError, validate as validate6, without as without12 } from "ag-charts-core";
// packages/ag-charts-community/src/chart/polarChart.ts
import { ChartAxisDirection as ChartAxisDirection17, Padding as Padding2, ZIndexMap as ZIndexMap6, iterate as iterate4 } from "ag-charts-core";
var PolarChart = class extends Chart {
constructor(options, resources) {
super(options, resources);
this.axes = this.createChartAxes();
this.padding = new Padding2(40);
this.ctx.axisManager.axisGroup.zIndex = ZIndexMap6.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[ChartAxisDirection17.Angle];
const radiusAxis = this.axes[ChartAxisDirection17.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 iterate4(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 iterate4(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, optionsDefs4, 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 = without12(options, ["axes"]);
}
}
const result = validate6(options, optionsDefs4, path);
result.invalid.push(...additionalErrors);
return result;
}
};
// packages/ag-charts-community/src/chart/legend/legendModule.ts
import {
CARTESIAN_POSITION as CARTESIAN_POSITION2,
FONT_SIZE_RATIO as FONT_SIZE_RATIO2,
LEGEND_CONTAINER_THEME,
boolean as boolean16,
borderOptionsDef as borderOptionsDef2,
callback as callback3,
colorUnion as colorUnion4,
fillOptionsDef as fillOptionsDef9,
fontOptionsDef as fontOptionsDef7,
legendPositionValidator,
number as number14,
padding,
positiveNumber as positiveNumber13,
ratio as ratio9,
shapeValidator,
strokeOptionsDef as strokeOptionsDef11,
union as union12
} from "ag-charts-core";
// packages/ag-charts-community/src/chart/legend/legend.ts
import {
BaseProperties as BaseProperties22,
Border as Border2,
ChartUpdateType as ChartUpdateType11,
CleanupRegistry as CleanupRegistry20,
FONT_SIZE as FONT_SIZE4,
LineSplitter as LineSplitter2,
Logger as Logger39,
ObserveChanges as ObserveChanges4,
Property as Property37,
ZIndexMap as ZIndexMap7,
cachedTextMeasurer as cachedTextMeasurer6,
callWithContext as callWithContext6,
clamp as clamp22,
createId as createId10,
deepClone as deepClone8,
expandLegendPosition,
isImageFill as isImageFill4,
isPatternFill as isPatternFill5,
isTextTruncated as isTextTruncated3,
objectsEqual as objectsEqual12,
toPlainText as toPlainText11,
truncateLine
} from "ag-charts-core";
// 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 ratio10 = n / bboxIndex;
if (ratio10 < 2) {
return Math.ceil(n / 2);
}
return bboxIndex;
}
}
return n;
}
// packages/ag-charts-community/src/chart/pagination/pagination.ts
import { ActionOnSet as ActionOnSet7, BaseProperties as BaseProperties21, ChartUpdateType as ChartUpdateType10, FONT_SIZE as FONT_SIZE3, Property as Property36, clamp as clamp21, createId as createId9 } from "ag-charts-core";
var PaginationLabel = class extends BaseProperties21 {
constructor() {
super(...arguments);
this.color = "black";
this.fontStyle = void 0;
this.fontWeight = void 0;
this.fontSize = FONT_SIZE3.SMALL;
this.fontFamily = "Verdana, sans-serif";
}
};
__decorateClass([
Property36
], PaginationLabel.prototype, "color", 2);
__decorateClass([
Property36
], PaginationLabel.prototype, "fontStyle", 2);
__decorateClass([
Property36
], PaginationLabel.prototype, "fontWeight", 2);
__decorateClass([
Property36
], PaginationLabel.prototype, "fontSize", 2);
__decorateClass([
Property36
], PaginationLabel.prototype, "fontFamily", 2);
var PaginationMarkerStyle = class extends BaseProperties21 {
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([
Property36
], PaginationMarkerStyle.prototype, "size", 2);
__decorateClass([
Property36
], PaginationMarkerStyle.prototype, "fill", 2);
__decorateClass([
Property36
], PaginationMarkerStyle.prototype, "fillOpacity", 2);
__decorateClass([
Property36
], PaginationMarkerStyle.prototype, "stroke", 2);
__decorateClass([
Property36
], PaginationMarkerStyle.prototype, "strokeWidth", 2);
__decorateClass([
Property36
], PaginationMarkerStyle.prototype, "strokeOpacity", 2);
var PaginationMarker = class extends BaseProperties21 {
constructor(parent) {
super();
this.parent = parent;
this.shape = "triangle";
this.size = 15;
this.padding = 8;
}
};
__decorateClass([
ActionOnSet7({
changeValue() {
if (this.parent.marker === this) {
this.parent.onMarkerShapeChange();
}
}
})
], PaginationMarker.prototype, "shape", 2);
__decorateClass([
Property36
], PaginationMarker.prototype, "size", 2);
__decorateClass([
Property36
], PaginationMarker.prototype, "padding", 2);
var Pagination = class extends BaseProperties21 {
constructor(chartUpdateCallback, pageUpdateCallback) {
super();
this.chartUpdateCallback = chartUpdateCallback;
this.pageUpdateCallback = pageUpdateCallback;
this.id = createId9(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: FONT_SIZE3.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: color8, fontStyle, fontWeight: fontWeight2, fontSize, fontFamily }
} = this;
labelNode.text = `${currentPage + 1} / ${pages}`;
labelNode.fill = color8;
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, style) {
const { shape, size } = this.marker;
marker.shape = shape;
marker.size = size;
marker.fill = style.fill;
marker.fillOpacity = style.fillOpacity ?? 1;
marker.stroke = style.stroke;
marker.strokeWidth = style.strokeWidth;
marker.strokeOpacity = style.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 = clamp21(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(ChartUpdateType10.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(ChartUpdateType10.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([
Property36
], Pagination.prototype, "marker", 2);
__decorateClass([
Property36
], Pagination.prototype, "activeStyle", 2);
__decorateClass([
Property36
], Pagination.prototype, "inactiveStyle", 2);
__decorateClass([
Property36
], Pagination.prototype, "highlightStyle", 2);
__decorateClass([
Property36
], Pagination.prototype, "label", 2);
// packages/ag-charts-community/src/chart/legend/legendDOMProxy.ts
import { createElement as createElement19, createElementId as createElementId3, toPlainText as toPlainText9 } from "ag-charts-core";
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 = createElement19("p");
this.itemDescription.style.display = "none";
this.itemDescription.id = createElementId3();
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, toPlainText9(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, width } = Transformable.toCanvas(l, l.getTextMeasureBBox());
const margin = (maxHeight - height) / 2;
const bbox = { x: x - groupBBox.x, y: y - margin - groupBBox.y, height: maxHeight, width };
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 = toPlainText9(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
import { toPlainText as toPlainText10 } from "ag-charts-core";
function makeLegendItemEvent(type, { itemId, seriesId, label: { text } }, event) {
const result = {
defaultPrevented: false,
apiEvent: {
type,
itemId,
// FIXME: AG-16068
seriesId,
event,
text: toPlainText10(text),
preventDefault: () => result.defaultPrevented = true
}
};
return result;
}
// packages/ag-charts-community/src/chart/legend/legendMarkerLabel.ts
import { ObserveChanges as ObserveChanges3, ProxyPropertyOnWrite as ProxyPropertyOnWrite6, SceneChangeDetection as SceneChangeDetection7 } from "ag-charts-core";
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, 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 + length / 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 = length;
line.y1 = 0;
line.y2 = 0;
}
}
preRender(renderCtx) {
const out = super.preRender(renderCtx);
this.layout();
return out;
}
layoutLabel() {
const { length, spacing } = this;
this.label.x = length + spacing;
}
computeBBox() {
this.layout();
return super.computeBBox();
}
};
LegendMarkerLabel.className = "MarkerLabel";
__decorateClass([
ProxyPropertyOnWrite6("label")
], LegendMarkerLabel.prototype, "text", 2);
__decorateClass([
ProxyPropertyOnWrite6("label")
], LegendMarkerLabel.prototype, "fontStyle", 2);
__decorateClass([
ProxyPropertyOnWrite6("label")
], LegendMarkerLabel.prototype, "fontWeight", 2);
__decorateClass([
ProxyPropertyOnWrite6("label")
], LegendMarkerLabel.prototype, "fontSize", 2);
__decorateClass([
ProxyPropertyOnWrite6("label")
], LegendMarkerLabel.prototype, "fontFamily", 2);
__decorateClass([
ProxyPropertyOnWrite6("label", "fill")
], LegendMarkerLabel.prototype, "color", 2);
__decorateClass([
ObserveChanges3((target) => target.layoutLabel())
], LegendMarkerLabel.prototype, "spacing", 2);
__decorateClass([
ObserveChanges3((target) => target.layoutLabel())
], LegendMarkerLabel.prototype, "length", 2);
__decorateClass([
SceneChangeDetection7()
], 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 BaseProperties22 {
constructor() {
super(...arguments);
this.maxLength = void 0;
this.color = "black";
this.fontStyle = void 0;
this.fontWeight = void 0;
this.fontSize = FONT_SIZE4.SMALL;
this.fontFamily = "Verdana, sans-serif";
}
};
__decorateClass([
Property37
], LegendLabel.prototype, "maxLength", 2);
__decorateClass([
Property37
], LegendLabel.prototype, "color", 2);
__decorateClass([
Property37
], LegendLabel.prototype, "fontStyle", 2);
__decorateClass([
Property37
], LegendLabel.prototype, "fontWeight", 2);
__decorateClass([
Property37
], LegendLabel.prototype, "fontSize", 2);
__decorateClass([
Property37
], LegendLabel.prototype, "fontFamily", 2);
__decorateClass([
Property37
], LegendLabel.prototype, "formatter", 2);
var LegendMarker = class extends BaseProperties22 {
constructor() {
super(...arguments);
this.shape = void 0;
this.size = 15;
this.padding = 8;
}
};
__decorateClass([
Property37
], LegendMarker.prototype, "shape", 2);
__decorateClass([
Property37
], LegendMarker.prototype, "size", 2);
__decorateClass([
Property37
], LegendMarker.prototype, "padding", 2);
__decorateClass([
Property37
], LegendMarker.prototype, "strokeWidth", 2);
__decorateClass([
Property37
], LegendMarker.prototype, "enabled", 2);
var LegendLine = class extends BaseProperties22 {
};
__decorateClass([
Property37
], LegendLine.prototype, "strokeWidth", 2);
__decorateClass([
Property37
], LegendLine.prototype, "length", 2);
var LegendItem = class extends BaseProperties22 {
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([
Property37
], LegendItem.prototype, "maxWidth", 2);
__decorateClass([
Property37
], LegendItem.prototype, "paddingX", 2);
__decorateClass([
Property37
], LegendItem.prototype, "paddingY", 2);
__decorateClass([
Property37
], LegendItem.prototype, "showSeriesStroke", 2);
__decorateClass([
Property37
], LegendItem.prototype, "marker", 2);
__decorateClass([
Property37
], LegendItem.prototype, "label", 2);
__decorateClass([
Property37
], LegendItem.prototype, "line", 2);
var LegendListeners = class extends BaseProperties22 {
};
__decorateClass([
Property37
], LegendListeners.prototype, "legendItemClick", 2);
__decorateClass([
Property37
], LegendListeners.prototype, "legendItemDoubleClick", 2);
var fillGradientDefaults = {
type: "gradient",
bounds: "item",
gradient: "linear",
colorStops: [{ color: "black" }],
rotation: 0,
reverse: false,
colorSpace: "rgb"
};
var fillPatternDefaults = {
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 fillImageDefaults = {
type: "image",
backgroundFill: "black",
backgroundFillOpacity: 1,
rotation: 0,
repeat: "no-repeat",
fit: "contain",
width: 8,
height: 8
};
var Legend = class extends BaseProperties22 {
constructor(ctx) {
super();
this.ctx = ctx;
this.id = createId10(this);
this.group = new TranslatableGroup({ name: "legend", zIndex: ZIndexMap7.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 Border2(this.containerNode);
this.cornerRadius = 0;
this.fillOpacity = 1;
this.padding = 4;
this.spacing = 0;
this.cleanup = new CleanupRegistry20();
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 (objectsEqual12(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 } = this.item.label;
if (formatter) {
const seriesDatum = datum.datum;
return this.cachedCallWithContext(formatter, {
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(width, height) {
const {
paddingX,
paddingY,
label,
maxWidth,
label: { maxLength = Infinity, fontStyle, fontWeight: fontWeight2, fontSize, fontFamily }
} = this.item;
this.updateItemSelection();
const bboxes = [];
const measurer = cachedTextMeasurer6(label);
const itemMaxWidthPercentage = 0.8;
const maxItemWidth = maxWidth ?? width * 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 = toPlainText11(labelText, "<unknown>").replace(LineSplitter2, " ");
markerLabel.text = this.truncate(text, maxLength, maxItemWidth, paddedSymbolWidth, measurer, id);
bboxes.push(markerLabel.getTextMeasureBBox());
});
width = Math.max(1, width);
height = Math.max(1, height);
if (!Number.isFinite(width)) {
return {};
}
[width, height] = this.updateContainer(width, height);
const size = this.size;
const oldSize = this.oldSize;
size[0] = width;
size[1] = height;
if (size[0] !== oldSize[0] || size[1] !== oldSize[1]) {
oldSize[0] = size[0];
oldSize[1] = size[1];
}
const { pages, maxPageHeight, maxPageWidth } = this.updatePagination(bboxes, width, height);
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(deepClone8(symbol)));
}
line.visible = anyLineEnabled;
if (line.visible) {
line.setStyleProperties(this.getLineStyles(symbol));
}
markerLabel.length = markerWidth;
markerLabel.spacing = spacing;
markerLabel.isCustomMarker = isCustomMarker;
return paddedSymbolWidth;
}
updateContainer(width, height) {
const containerStyles = this.getContainerStyles();
this.containerNode.width = 0;
this.containerNode.height = 0;
this.containerNode.setStyleProperties(containerStyles);
this.containerNode.cornerRadius = containerStyles.cornerRadius;
width -= containerStyles.strokeWidth * 2 + containerStyles.padding.left + containerStyles.padding.right;
height -= containerStyles.strokeWidth * 2 + containerStyles.padding.top + containerStyles.padding.bottom;
return [width, height];
}
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 (isTextTruncated3(result)) {
this.truncatedItems.add(id);
} else {
this.truncatedItems.delete(id);
}
return result;
}
updatePagination(bboxes, width, height) {
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,
width,
height
);
const newCurrentPage = pages.findIndex((p) => p.endIndex >= trackingIndex);
this.pagination.currentPage = clamp22(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, width, height) {
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) {
Logger39.warn("unable to find stable legend layout.");
break;
}
paginationBBox = lastPassPaginationBBox;
const maxWidth = width - (paginationVertical ? 0 : paginationBBox.width);
const maxHeight = height - (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(ChartUpdateType11.SCENE_RENDER);
}
update() {
const {
label: { color: color8 }
} = this.item;
this.itemSelection.each((markerLabel, datum) => {
markerLabel.setEnabled(datum.enabled);
markerLabel.color = color8;
});
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 (isPatternFill5(fill)) {
fill.width = 8;
fill.height = 8;
fill.padding = 1;
fill.strokeWidth = Math.min(2, fill.strokeWidth ?? 2);
}
if (isImageFill4(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
},
fillGradientDefaults,
fillPatternDefaults,
fillImageDefaults
);
}
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
},
fillGradientDefaults,
fillPatternDefaults,
fillImageDefaults
);
}
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) {
callWithContext6([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(ChartUpdateType11.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) {
callWithContext6(
[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(ChartUpdateType11.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) {
Logger39.warn(`Cannot find seriesId: "${activeItem.seriesId}"`);
event.reject();
} else if (datum === void 0) {
const json = JSON.stringify({ seriesId: activeItem.seriesId, itemId: activeItem.itemId });
Logger39.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, height } = 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 = (width - legendBBox.width) / 2;
translationY = 0;
break;
case "bottom":
translationX = (width - legendBBox.width) / 2;
translationY = height - legendBBox.height;
break;
case "right":
translationX = width - legendBBox.width;
translationY = (height - legendBBox.height) / 2;
break;
case "left":
translationX = 0;
translationY = (height - legendBBox.height) / 2;
break;
case "top-right":
case "right-top":
translationX = width - legendBBox.width;
translationY = 0;
break;
case "top-left":
case "left-top":
translationX = 0;
translationY = 0;
break;
case "bottom-right":
case "right-bottom":
translationX = width - legendBBox.width;
translationY = height - legendBBox.height;
break;
case "bottom-left":
case "left-bottom":
translationX = 0;
translationY = height - 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, height } = shrinkRect;
const { placement } = expandLegendPosition(this.position);
const aspectRatio = width / height;
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, width) : width;
legendHeight = this.maxHeight ? Math.min(this.maxHeight, height) : Math.round(height * 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, width) : Math.round(width * widthCoefficient);
legendHeight = this.maxHeight ? Math.min(this.maxHeight, height) : height;
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([
Property37
], Legend.prototype, "toggleSeries", 2);
__decorateClass([
Property37
], Legend.prototype, "pagination", 2);
__decorateClass([
Property37
], Legend.prototype, "item", 2);
__decorateClass([
Property37
], Legend.prototype, "listeners", 2);
__decorateClass([
ObserveChanges4((target, newValue, oldValue) => {
target.updateGroupVisibility();
if (newValue === oldValue) {
return;
}
const {
ctx: { legendManager, stateManager }
} = target;
if (oldValue === false && newValue === true) {
stateManager.restoreState(legendManager);
}
}),
Property37
], Legend.prototype, "enabled", 2);
__decorateClass([
Property37
], Legend.prototype, "position", 2);
__decorateClass([
Property37
], Legend.prototype, "maxWidth", 2);
__decorateClass([
Property37
], Legend.prototype, "maxHeight", 2);
__decorateClass([
Property37
], Legend.prototype, "reverseOrder", 2);
__decorateClass([
Property37
], Legend.prototype, "orientation", 2);
__decorateClass([
Property37
], Legend.prototype, "preventHidingAll", 2);
__decorateClass([
Property37
], Legend.prototype, "border", 2);
__decorateClass([
Property37
], Legend.prototype, "cornerRadius", 2);
__decorateClass([
Property37
], Legend.prototype, "fill", 2);
__decorateClass([
Property37
], Legend.prototype, "fillOpacity", 2);
__decorateClass([
Property37
], Legend.prototype, "padding", 2);
__decorateClass([
Property37
], Legend.prototype, "spacing", 2);
__decorateClass([
Property37
], Legend.prototype, "xOffset", 2);
__decorateClass([
Property37
], 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: boolean16,
position: legendPositionValidator,
orientation: union12("horizontal", "vertical"),
maxWidth: positiveNumber13,
maxHeight: positiveNumber13,
spacing: positiveNumber13,
border: borderOptionsDef2,
cornerRadius: number14,
padding,
fill: colorUnion4,
fillOpacity: ratio9,
preventHidingAll: boolean16,
reverseOrder: boolean16,
toggleSeries: boolean16,
item: {
marker: {
size: positiveNumber13,
shape: shapeValidator,
padding: positiveNumber13,
strokeWidth: positiveNumber13
},
line: {
length: positiveNumber13,
strokeWidth: positiveNumber13
},
label: {
maxLength: positiveNumber13,
formatter: callback3,
...fontOptionsDef7
},
maxWidth: positiveNumber13,
paddingX: positiveNumber13,
paddingY: positiveNumber13,
showSeriesStroke: boolean16
},
pagination: {
marker: {
size: positiveNumber13,
shape: shapeValidator,
padding: positiveNumber13
},
activeStyle: {
...fillOptionsDef9,
...strokeOptionsDef11
},
inactiveStyle: {
...fillOptionsDef9,
...strokeOptionsDef11
},
highlightStyle: {
...fillOptionsDef9,
...strokeOptionsDef11
},
label: fontOptionsDef7
},
listeners: {
legendItemClick: callback3,
legendItemDoubleClick: callback3
}
},
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: CARTESIAN_POSITION2.BOTTOM,
orientation: {
$if: [
{
$or: [
{ $eq: [{ $path: "./position" }, CARTESIAN_POSITION2.LEFT] },
{ $eq: [{ $path: "./position" }, CARTESIAN_POSITION2.LEFT_TOP] },
{ $eq: [{ $path: "./position" }, CARTESIAN_POSITION2.LEFT_BOTTOM] },
{ $eq: [{ $path: "./position" }, CARTESIAN_POSITION2.RIGHT] },
{ $eq: [{ $path: "./position" }, CARTESIAN_POSITION2.RIGHT_TOP] },
{ $eq: [{ $path: "./position" }, CARTESIAN_POSITION2.RIGHT_BOTTOM] },
{ $eq: [{ $path: "./position/placement" }, CARTESIAN_POSITION2.LEFT] },
{ $eq: [{ $path: "./position/placement" }, CARTESIAN_POSITION2.LEFT_TOP] },
{ $eq: [{ $path: "./position/placement" }, CARTESIAN_POSITION2.LEFT_BOTTOM] },
{ $eq: [{ $path: "./position/placement" }, CARTESIAN_POSITION2.RIGHT] },
{ $eq: [{ $path: "./position/placement" }, CARTESIAN_POSITION2.RIGHT_TOP] },
{ $eq: [{ $path: "./position/placement" }, CARTESIAN_POSITION2.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_RATIO2.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/chart/series/cartesian/areaSeriesModule.ts
import {
CARTESIAN_AXIS_TYPE as CARTESIAN_AXIS_TYPE3,
CARTESIAN_POSITION as CARTESIAN_POSITION3,
ChartAxisDirection as ChartAxisDirection19,
DEFAULT_SHADOW_COLOUR as DEFAULT_SHADOW_COLOUR2,
FILL_GRADIENT_LINEAR_DEFAULTS,
FILL_GRADIENT_RADIAL_REVERSED_DEFAULTS,
FILL_IMAGE_DEFAULTS,
FILL_PATTERN_DEFAULTS,
LABEL_BOXING_DEFAULTS,
MARKER_SERIES_HIGHLIGHT_STYLE,
SEGMENTATION_DEFAULTS
} from "ag-charts-core";
// packages/ag-charts-community/src/chart/series/cartesian/areaSeries.ts
import {
ChartAxisDirection as ChartAxisDirection18,
DebugMetrics as DebugMetrics3,
SeriesContentZIndexMap as SeriesContentZIndexMap2,
SeriesZIndexMap as SeriesZIndexMap3,
extent as extent3,
isContinuous as isContinuous2,
isDefined as isDefined2,
mergeDefaults as mergeDefaults12
} from "ag-charts-core";
// packages/ag-charts-community/src/chart/axis/logAxis.ts
import { Logger as Logger40, normalisedExtentWithMetadata as normalisedExtentWithMetadata2 } from "ag-charts-core";
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: extent6, clipped } = normalisedExtentWithMetadata2(
d.domain,
min,
max,
preferredMin,
preferredMax,
void 0,
d.sortMetadata?.sortOrder
);
if (extent6[0] < 0 && extent6[1] > 0 || d.domain[0] < 0 && d.domain[1] > 0) {
Logger40.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 (extent6[0] === 0 || extent6[1] === 0 || d.domain[0] === 0 || d.domain[1] === 0) {
Logger40.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: extent6, 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/chart/series/cartesian/areaAggregation.ts
import {
AGGREGATION_INDEX_X_MAX,
AGGREGATION_INDEX_X_MIN,
AGGREGATION_INDEX_Y_MAX,
AGGREGATION_INDEX_Y_MIN,
AGGREGATION_MIN_RANGE,
AGGREGATION_THRESHOLD,
aggregationDomain,
aggregationIndexForXRatio,
aggregationRangeFittingPoints,
aggregationXRatioForDatumIndex,
aggregationXRatioForXValue,
compactAggregationIndices,
createAggregationIndices,
nextPowerOf2,
simpleMemorize2 as simpleMemorize22
} from "ag-charts-core";
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 = simpleMemorize22(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/chart/series/cartesian/areaSeriesProperties.ts
import { InterpolationProperties, Property as Property38 } from "ag-charts-core";
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([
Property38
], AreaSeriesProperties.prototype, "xKey", 2);
__decorateClass([
Property38
], AreaSeriesProperties.prototype, "xName", 2);
__decorateClass([
Property38
], AreaSeriesProperties.prototype, "yKey", 2);
__decorateClass([
Property38
], AreaSeriesProperties.prototype, "yName", 2);
__decorateClass([
Property38
], AreaSeriesProperties.prototype, "yFilterKey", 2);
__decorateClass([
Property38
], AreaSeriesProperties.prototype, "stackGroup", 2);
__decorateClass([
Property38
], AreaSeriesProperties.prototype, "normalizedTo", 2);
__decorateClass([
Property38
], AreaSeriesProperties.prototype, "fill", 2);
__decorateClass([
Property38
], AreaSeriesProperties.prototype, "fillOpacity", 2);
__decorateClass([
Property38
], AreaSeriesProperties.prototype, "stroke", 2);
__decorateClass([
Property38
], AreaSeriesProperties.prototype, "strokeWidth", 2);
__decorateClass([
Property38
], AreaSeriesProperties.prototype, "strokeOpacity", 2);
__decorateClass([
Property38
], AreaSeriesProperties.prototype, "lineDash", 2);
__decorateClass([
Property38
], AreaSeriesProperties.prototype, "lineDashOffset", 2);
__decorateClass([
Property38
], AreaSeriesProperties.prototype, "interpolation", 2);
__decorateClass([
Property38
], AreaSeriesProperties.prototype, "styler", 2);
__decorateClass([
Property38
], AreaSeriesProperties.prototype, "shadow", 2);
__decorateClass([
Property38
], AreaSeriesProperties.prototype, "marker", 2);
__decorateClass([
Property38
], AreaSeriesProperties.prototype, "label", 2);
__decorateClass([
Property38
], AreaSeriesProperties.prototype, "tooltip", 2);
__decorateClass([
Property38
], AreaSeriesProperties.prototype, "connectMissingData", 2);
// 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: SeriesZIndexMap3.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 = [SeriesZIndexMap3.BACKGROUND, zIndex];
this.contentGroup.zIndex = [SeriesZIndexMap3.ANY_CONTENT, zIndex, SeriesContentZIndexMap2.FOREGROUND];
} else {
this.backgroundGroup.zIndex = [SeriesZIndexMap3.ANY_CONTENT, zIndex, SeriesContentZIndexMap2.FOREGROUND, 0];
this.contentGroup.zIndex = [SeriesZIndexMap3.ANY_CONTENT, zIndex, SeriesContentZIndexMap2.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[ChartAxisDirection18.X]?.scale;
const yScale = this.axes[ChartAxisDirection18.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 ((isDefined2(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 (isDefined2(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[ChartAxisDirection18.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[ChartAxisDirection18.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[ChartAxisDirection18.Y];
if (direction === ChartAxisDirection18.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(extent3(keys.domain)) };
}
const yExtent = this.domainForClippedRange(
ChartAxisDirection18.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(
ChartAxisDirection18.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[ChartAxisDirection18.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) {
DebugMetrics3.record(
`${this.type}:aggregation`,
filters.map((f) => f.maxRange)
);
}
}
estimateTargetRange() {
const xAxis = this.axes[ChartAxisDirection18.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[ChartAxisDirection18.X];
const yAxis = axes[ChartAxisDirection18.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[ChartAxisDirection18.X];
const yAxis = axes[ChartAxisDirection18.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[ChartAxisDirection18.X];
if (xAxis == null)
return;
const { scale: xScale } = xAxis;
const [r0, r1] = xScale.range;
const range4 = Math.abs(r1 - r0);
this.aggregationManager.ensureLevelForRange(range4);
const dataAggregationFilter = this.aggregationManager.getFilterForRange(range4);
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 range4 = Math.abs(r1 - r0);
this.aggregationManager.ensureLevelForRange(range4);
const dataAggregationFilter = this.aggregationManager.getFilterForRange(range4);
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(ChartAxisDirection18.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 (isDefined2(ctx.normalizedTo) ? ctx.isContinuousY && isContinuous2(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 = mergeDefaults12(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[ChartAxisDirection18.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 style = datum.style ?? contextNodeData.styles[state];
this.applyMarkerStyle(style, node, datum.point, fillBBox, { selected: datum.selected });
node.drawingMode = this.resolveMarkerDrawingModeForState(drawingMode, style);
});
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 style = getLabelStyles(this, datum, params, this.properties.label, isHighlight, activeHighlight);
const { enabled, fontStyle, fontWeight: fontWeight2, fontSize, fontFamily, color: color8 } = style;
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 = color8;
text.visible = true;
text.fillOpacity = this.getHighlightStyle(isHighlight, datum.datumIndex).opacity ?? 1;
text.setBoxing(style);
} 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, style) {
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(style.fill) ?? style.fill;
return {
...datumStylerProperties(xValue, yValue, xKey, yKey, xDomain, yDomain),
xValue,
yValue,
...style,
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[ChartAxisDirection18.X];
const yAxis = axes[ChartAxisDirection18.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_COLOUR2,
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: CARTESIAN_AXIS_TYPE3.NUMBER,
position: CARTESIAN_POSITION3.LEFT
},
x: {
type: CARTESIAN_AXIS_TYPE3.CATEGORY,
position: CARTESIAN_POSITION3.BOTTOM
}
},
axisKeys: { [ChartAxisDirection19.X]: "xKeyAxis", [ChartAxisDirection19.Y]: "yKeyAxis" },
themeTemplate,
create: (ctx) => new AreaSeries(ctx)
};
// packages/ag-charts-community/src/chart/series/cartesian/barSeriesModule.ts
import {
ChartAxisDirection as ChartAxisDirection21,
DEFAULT_SHADOW_COLOUR as DEFAULT_SHADOW_COLOUR3,
DIRECTION_SWAP_AXES,
FILL_GRADIENT_LINEAR_DEFAULTS as FILL_GRADIENT_LINEAR_DEFAULTS2,
FILL_IMAGE_DEFAULTS as FILL_IMAGE_DEFAULTS2,
FILL_PATTERN_DEFAULTS as FILL_PATTERN_DEFAULTS2,
LABEL_BOXING_DEFAULTS as LABEL_BOXING_DEFAULTS2,
MULTI_SERIES_HIGHLIGHT_STYLE,
SEGMENTATION_DEFAULTS as SEGMENTATION_DEFAULTS2
} from "ag-charts-core";
// packages/ag-charts-community/src/chart/series/cartesian/barSeries.ts
import {
AGGREGATION_INDEX_UNSET,
AGGREGATION_INDEX_X_MAX as AGGREGATION_INDEX_X_MAX2,
AGGREGATION_INDEX_X_MIN as AGGREGATION_INDEX_X_MIN2,
AGGREGATION_INDEX_Y_MAX as AGGREGATION_INDEX_Y_MAX2,
AGGREGATION_INDEX_Y_MIN as AGGREGATION_INDEX_Y_MIN2,
AGGREGATION_SPAN,
ChartAxisDirection as ChartAxisDirection20,
DebugMetrics as DebugMetrics4,
areScalingEqual as areScalingEqual3,
isFiniteNumber as isFiniteNumber8,
mergeDefaults as mergeDefaults13
} from "ag-charts-core";
// packages/ag-charts-community/src/scene/shape/barShape.ts
import { DeclaredSceneChangeDetection as DeclaredSceneChangeDetection5 } from "ag-charts-core";
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: width,
__height: height
} = this;
path.clear();
borderPath.clear();
if (direction === "x") {
const featherInsetX = Math.abs(featherRatio) * width;
if (featherRatio > 0) {
path.moveTo(x, y);
path.lineTo(x + width - featherInsetX, y);
path.lineTo(x + width, y + height / 2);
path.lineTo(x + width - featherInsetX, y + height);
path.lineTo(x, y + height);
path.closePath();
} else {
path.moveTo(x + featherInsetX, y);
path.lineTo(x + width, y);
path.lineTo(x + width, y + height);
path.lineTo(x + featherInsetX, y + height);
path.lineTo(x, y + height / 2);
path.closePath();
}
} else {
const featherInsetY = Math.abs(featherRatio) * height;
if (featherRatio > 0) {
path.moveTo(x, y + featherInsetY);
path.lineTo(x + width / 2, y);
path.lineTo(x + width, y + featherInsetY);
path.lineTo(x + width, y + height);
path.lineTo(x, y + height);
path.closePath();
} else {
path.moveTo(x, y);
path.lineTo(x + width, y);
path.lineTo(x + width, y + height - featherInsetY);
path.lineTo(x + width / 2, y + height);
path.lineTo(x, y + height - 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([
DeclaredSceneChangeDetection5()
], BarShape.prototype, "direction", 2);
__decorateClass([
DeclaredSceneChangeDetection5()
], BarShape.prototype, "featherRatio", 2);
// packages/ag-charts-community/src/chart/series/cartesian/barAggregation.ts
import {
AGGREGATION_MIN_RANGE as AGGREGATION_MIN_RANGE2,
AGGREGATION_THRESHOLD as AGGREGATION_THRESHOLD2,
aggregationDomain as aggregationDomain2,
aggregationRangeFittingPoints as aggregationRangeFittingPoints2,
compactAggregationIndices as compactAggregationIndices2,
createAggregationIndices as createAggregationIndices2,
getMidpointsForIndices,
nextPowerOf2 as nextPowerOf22,
simpleMemorize2 as simpleMemorize23
} from "ag-charts-core";
function computeBarAggregation(domain, xValues, yStartValues, yEndValues, options) {
if (xValues.length < AGGREGATION_THRESHOLD2)
return;
const [d0, d1] = domain;
const { smallestKeyInterval, xNeedsValueOf, yNeedsValueOf, existingFilters } = options;
let maxRange = aggregationRangeFittingPoints2(xValues, d0, d1, { smallestKeyInterval, xNeedsValueOf });
const existingFilter = existingFilters?.find((f) => f.maxRange === maxRange);
let {
indexData: positiveIndexData,
valueData: positiveValueData,
negativeIndexData,
negativeValueData
} = createAggregationIndices2(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 = compactAggregationIndices2(positiveIndexData, positiveValueData, currentMaxRange, {
reuseIndexData: nextExistingFilter?.positiveIndexData,
reuseValueData: nextExistingFilter?.positiveValueData
});
const negativeCompacted = compactAggregationIndices2(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_THRESHOLD2)
return;
const [d0, d1] = domain;
const { smallestKeyInterval, xNeedsValueOf, yNeedsValueOf, targetRange, existingFilters } = options;
const finestMaxRange = aggregationRangeFittingPoints2(xValues, d0, d1, { smallestKeyInterval, xNeedsValueOf });
const targetMaxRange = Math.min(finestMaxRange, nextPowerOf22(Math.max(targetRange, AGGREGATION_MIN_RANGE2)));
const existingFilter = existingFilters?.find((f) => f.maxRange === targetMaxRange);
const {
indexData: positiveIndexData,
valueData: positiveValueData,
negativeIndexData,
negativeValueData
} = createAggregationIndices2(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] = aggregationDomain2(scale2, domainInput);
return computeBarAggregation([d0, d1], xValues, yStartValues, yEndValues, {
smallestKeyInterval,
xNeedsValueOf,
yNeedsValueOf
});
}
var memoizedAggregateBarData = simpleMemorize23(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] = aggregationDomain2(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] = aggregationDomain2(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
import { Property as Property39 } from "ag-charts-core";
var BarSeriesLabel = class extends Label {
constructor() {
super(...arguments);
this.placement = "inside-center";
this.spacing = 0;
}
};
__decorateClass([
Property39
], BarSeriesLabel.prototype, "placement", 2);
__decorateClass([
Property39
], 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([
Property39
], BarSeriesProperties.prototype, "xKey", 2);
__decorateClass([
Property39
], BarSeriesProperties.prototype, "xName", 2);
__decorateClass([
Property39
], BarSeriesProperties.prototype, "yKey", 2);
__decorateClass([
Property39
], BarSeriesProperties.prototype, "yName", 2);
__decorateClass([
Property39
], BarSeriesProperties.prototype, "yFilterKey", 2);
__decorateClass([
Property39
], BarSeriesProperties.prototype, "stackGroup", 2);
__decorateClass([
Property39
], BarSeriesProperties.prototype, "normalizedTo", 2);
__decorateClass([
Property39
], BarSeriesProperties.prototype, "fill", 2);
__decorateClass([
Property39
], BarSeriesProperties.prototype, "fillOpacity", 2);
__decorateClass([
Property39
], BarSeriesProperties.prototype, "stroke", 2);
__decorateClass([
Property39
], BarSeriesProperties.prototype, "strokeWidth", 2);
__decorateClass([
Property39
], BarSeriesProperties.prototype, "strokeOpacity", 2);
__decorateClass([
Property39
], BarSeriesProperties.prototype, "lineDash", 2);
__decorateClass([
Property39
], BarSeriesProperties.prototype, "lineDashOffset", 2);
__decorateClass([
Property39
], BarSeriesProperties.prototype, "cornerRadius", 2);
__decorateClass([
Property39
], BarSeriesProperties.prototype, "crisp", 2);
__decorateClass([
Property39
], BarSeriesProperties.prototype, "styler", 2);
__decorateClass([
Property39
], BarSeriesProperties.prototype, "itemStyler", 2);
__decorateClass([
Property39
], BarSeriesProperties.prototype, "simpleItemStyler", 2);
__decorateClass([
Property39
], BarSeriesProperties.prototype, "shadow", 2);
__decorateClass([
Property39
], BarSeriesProperties.prototype, "label", 2);
__decorateClass([
Property39
], BarSeriesProperties.prototype, "tooltip", 2);
__decorateClass([
Property39
], BarSeriesProperties.prototype, "sparklineMode", 2);
// 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 (isFiniteNumber8(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" ? ChartAxisDirection20.X : ChartAxisDirection20.Y;
if (selfDirection !== direction)
return [];
const yKey = this.yCumulativeKey(this.dataModel);
const [y0, y1] = this.domainForVisibleRange(ChartAxisDirection20.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[ChartAxisDirection20.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) {
DebugMetrics4.record(
`${this.type}:aggregation`,
filters.map((f) => f.maxRange)
);
}
}
estimateTargetRange() {
const xAxis = this.axes[ChartAxisDirection20.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 range4 = Math.abs(xScale.range[1] - xScale.range[0]);
this.aggregationManager.ensureLevelForRange(range4);
const dataAggregationFilter = this.aggregationManager.getFilterForRange(range4);
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: range4,
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() === ChartAxisDirection20.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(ChartAxisDirection20.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_MIN2 : AGGREGATION_INDEX_Y_MAX2;
const Y_MAX = positive ? AGGREGATION_INDEX_Y_MAX2 : AGGREGATION_INDEX_Y_MIN2;
const visibleRange = this.visibleRangeIndices("xValue", ctx.xAxis.range, indices);
const start = visibleRange[0];
const end2 = visibleRange[1];
for (let i = start; i < end2; i += 1) {
const aggIndex = i * AGGREGATION_SPAN;
const xMinIndex = indexData[aggIndex + AGGREGATION_INDEX_X_MIN2];
const xMaxIndex = indexData[aggIndex + AGGREGATION_INDEX_X_MAX2];
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 width = Math.abs(xPosition(xMaxIndex) - xPosition(xMinIndex)) + ctx.barWidth;
if (x - width < 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 = width;
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 width = 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 start = visibleRange[0];
const end2 = visibleRange[1];
for (let groupIndex = start; groupIndex < end2; 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 = width;
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 width = ctx.barWidth;
const visibleRange = this.visibleRangeIndices("xValue", ctx.xAxis.range);
let start = visibleRange[0];
let end2 = visibleRange[1];
if (this.processedData.input.count < 1e3) {
start = 0;
end2 = this.processedData.input.count;
}
for (let datumIndex = start; datumIndex < end2; 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 = width;
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, style) {
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(style.fill) ?? style.fill;
return {
seriesId,
...datumStylerProperties(xValue, yValue, xKey, yKey, xDomain, yDomain),
datum,
xValue,
yValue,
stackGroup,
highlightState: highlightStateString,
...style,
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 mergeDefaults13(
overrides,
highlightStyle,
this.getStyle(false, highlightState)
);
}
let style = mergeDefaults13(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,
style
);
return this.ctx.optionsGraphService.resolvePartial(
["series", `${this.declarationOrder}`],
this.callWithContext(itemStyler, params)
);
}
);
if (overrides) {
style = mergeDefaults13(overrides, style);
}
}
return style;
}
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() === ChartAxisDirection20.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 style = datum.style ?? contextStyles[series.getHighlightState(highlightedDatum, isHighlight, datum.datumIndex)];
rect2.setStyleProperties(style, fillBBox);
const cornerRadius = style.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 || !areScalingEqual3(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_DEFAULTS2],
["image", FILL_IMAGE_DEFAULTS2],
["pattern", FILL_PATTERN_DEFAULTS2]
]
},
stroke: { $palette: "stroke" },
fillOpacity: 1,
strokeWidth: { $isUserOption: ["./stroke", 2, 0] },
lineDash: [0],
lineDashOffset: 0,
label: {
...LABEL_BOXING_DEFAULTS2,
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_COLOUR3,
xOffset: 3,
yOffset: 3,
blur: 5
},
highlight: MULTI_SERIES_HIGHLIGHT_STYLE,
segmentation: SEGMENTATION_DEFAULTS2
}
};
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: { [ChartAxisDirection21.X]: "xKeyAxis", [ChartAxisDirection21.Y]: "yKeyAxis" },
axisKeysFlipped: { [ChartAxisDirection21.X]: "yKeyAxis", [ChartAxisDirection21.Y]: "xKeyAxis" },
themeTemplate: themeTemplate2,
create: (ctx) => new BarSeries(ctx)
};
// packages/ag-charts-community/src/chart/series/cartesian/bubbleSeriesModule.ts
import {
CARTESIAN_AXIS_TYPE as CARTESIAN_AXIS_TYPE4,
CARTESIAN_POSITION as CARTESIAN_POSITION4,
ChartAxisDirection as ChartAxisDirection23,
FILL_GRADIENT_RADIAL_REVERSED_DEFAULTS as FILL_GRADIENT_RADIAL_REVERSED_DEFAULTS2,
FILL_IMAGE_DEFAULTS as FILL_IMAGE_DEFAULTS3,
FILL_PATTERN_DEFAULTS as FILL_PATTERN_DEFAULTS3,
LABEL_BOXING_DEFAULTS as LABEL_BOXING_DEFAULTS3,
MULTI_SERIES_HIGHLIGHT_STYLE as MULTI_SERIES_HIGHLIGHT_STYLE2
} from "ag-charts-core";
// packages/ag-charts-community/src/chart/series/cartesian/bubbleSeries.ts
import {
ChartAxisDirection as ChartAxisDirection22,
cachedTextMeasurer as cachedTextMeasurer7,
clamp as clamp24,
dateToNumber as dateToNumber3,
extent as extent4,
formatValue as formatValue2,
isArray as isArray17,
measureTextSegments as measureTextSegments4,
rescaleVisibleRange,
toPlainText as toPlainText12
} from "ag-charts-core";
// packages/ag-charts-community/src/chart/series/cartesian/bubbleAggregation.ts
import { aggregationDomain as aggregationDomain3, aggregationXRatioForXValue as aggregationXRatioForXValue2, clamp as clamp23 } from "ag-charts-core";
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 = aggregationXRatioForXValue2(xValue, xDomain.min, xDomain.max, xNeedsValueOf);
const yRatio = aggregationXRatioForXValue2(yValue, yDomain.min, yDomain.max, yNeedsValueOf);
const distanceSquared = (xRatio - midX) ** 2 + (yRatio - midY) ** 2;
if (distanceSquared < currentDistanceSquared) {
currentDistanceSquared = distanceSquared;
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 = aggregationXRatioForXValue2(xValue, xDomain.min, xDomain.max, xNeedsValueOf);
const yRatio = aggregationXRatioForXValue2(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 = aggregationXRatioForXValue2(xValue, xDomain.min, xDomain.max, xNeedsValueOf);
const yRatio = aggregationXRatioForXValue2(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] = aggregationDomain3(xScale, xDomainInput);
const [yd0, yd1] = aggregationDomain3(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: clamp23(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
import { Property as Property40, ProxyProperty as ProxyProperty2, SceneArrayChangeDetection as SceneArrayChangeDetection2, SceneChangeDetection as SceneChangeDetection8 } from "ag-charts-core";
var BubbleSeriesMarker = class extends SeriesMarker {
constructor() {
super(...arguments);
this.maxSize = 30;
}
};
__decorateClass([
Property40,
SceneChangeDetection8()
], BubbleSeriesMarker.prototype, "maxSize", 2);
__decorateClass([
Property40,
SceneArrayChangeDetection2()
], BubbleSeriesMarker.prototype, "domain", 2);
var BubbleSeriesLabel = class extends Label {
constructor() {
super(...arguments);
this.placement = "top";
}
};
__decorateClass([
Property40
], 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([
Property40
], BubbleSeriesProperties.prototype, "xKey", 2);
__decorateClass([
Property40
], BubbleSeriesProperties.prototype, "yKey", 2);
__decorateClass([
Property40
], BubbleSeriesProperties.prototype, "sizeKey", 2);
__decorateClass([
Property40
], BubbleSeriesProperties.prototype, "labelKey", 2);
__decorateClass([
Property40
], BubbleSeriesProperties.prototype, "xFilterKey", 2);
__decorateClass([
Property40
], BubbleSeriesProperties.prototype, "yFilterKey", 2);
__decorateClass([
Property40
], BubbleSeriesProperties.prototype, "sizeFilterKey", 2);
__decorateClass([
Property40
], BubbleSeriesProperties.prototype, "xName", 2);
__decorateClass([
Property40
], BubbleSeriesProperties.prototype, "yName", 2);
__decorateClass([
Property40
], BubbleSeriesProperties.prototype, "sizeName", 2);
__decorateClass([
Property40
], BubbleSeriesProperties.prototype, "labelName", 2);
__decorateClass([
Property40
], BubbleSeriesProperties.prototype, "title", 2);
__decorateClass([
ProxyProperty2("marker.shape")
], BubbleSeriesProperties.prototype, "shape", 2);
__decorateClass([
ProxyProperty2("marker.size")
], BubbleSeriesProperties.prototype, "size", 2);
__decorateClass([
ProxyProperty2("marker.maxSize")
], BubbleSeriesProperties.prototype, "maxSize", 2);
__decorateClass([
ProxyProperty2("marker.domain")
], BubbleSeriesProperties.prototype, "domain", 2);
__decorateClass([
ProxyProperty2("marker.fill")
], BubbleSeriesProperties.prototype, "fill", 2);
__decorateClass([
ProxyProperty2("marker.fillOpacity")
], BubbleSeriesProperties.prototype, "fillOpacity", 2);
__decorateClass([
ProxyProperty2("marker.stroke")
], BubbleSeriesProperties.prototype, "stroke", 2);
__decorateClass([
ProxyProperty2("marker.strokeWidth")
], BubbleSeriesProperties.prototype, "strokeWidth", 2);
__decorateClass([
ProxyProperty2("marker.strokeOpacity")
], BubbleSeriesProperties.prototype, "strokeOpacity", 2);
__decorateClass([
ProxyProperty2("marker.lineDash")
], BubbleSeriesProperties.prototype, "lineDash", 2);
__decorateClass([
ProxyProperty2("marker.lineDashOffset")
], BubbleSeriesProperties.prototype, "lineDashOffset", 2);
__decorateClass([
ProxyProperty2("marker.itemStyler")
], BubbleSeriesProperties.prototype, "itemStyler", 2);
__decorateClass([
Property40
], BubbleSeriesProperties.prototype, "styler", 2);
__decorateClass([
Property40
], BubbleSeriesProperties.prototype, "label", 2);
__decorateClass([
Property40
], BubbleSeriesProperties.prototype, "tooltip", 2);
__decorateClass([
Property40
], BubbleSeriesProperties.prototype, "maxRenderedItems", 2);
// 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[ChartAxisDirection22.X]?.scale;
const yScale = this.axes[ChartAxisDirection22.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[ChartAxisDirection22.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[ChartAxisDirection22.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 = {
[ChartAxisDirection22.X]: "xValue",
[ChartAxisDirection22.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 === ChartAxisDirection22.X ? ChartAxisDirection22.Y : ChartAxisDirection22.X;
const crossId = dataValues[crossDirection];
const ext = this.domainForClippedRange(direction, [id], crossId);
return { domain: fixNumericExtent(extent4(ext)) };
}
getSeriesRange(_direction, visibleRange) {
return this.domainForVisibleRange(ChartAxisDirection22.Y, ["yValue"], "xValue", visibleRange);
}
getVisibleItems(xVisibleRange, yVisibleRange, minVisibleItems) {
const { dataAggregation, axes } = this;
const xAxis = axes[ChartAxisDirection22.X];
const yAxis = axes[ChartAxisDirection22.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[ChartAxisDirection22.X];
const yAxis = this.axes[ChartAxisDirection22.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(dateToNumber3),
dataModel.getDomain(this, `xValue`, "value", processedData).domain.map(dateToNumber3)
);
}
if (ContinuousScale.is(yScale)) {
yVisibleRange = rescaleVisibleRange(
yVisibleRange,
yScale.domain.map(dateToNumber3),
dataModel.getDomain(this, `yValue`, "value", processedData).domain.map(dateToNumber3)
);
}
}
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: cachedTextMeasurer7(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, height } = isArray17(labelText) ? measureTextSegments4(labelText, ctx.label) : ctx.labelTextMeasurer.measureLines(String(labelText));
width += ctx.labelPadding.left + ctx.labelPadding.right;
height += ctx.labelPadding.bottom + ctx.labelPadding.top;
return { text: labelText, width, height };
}
/**
* 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, toPlainText12(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 style = { ...datum.style ?? contextNodeData.styles[state] };
style.size = size;
if (dilation > 1) {
const fillOpacity = style.fillOpacity ?? 0;
const opacityScale = 0.269669 + 683e-6 * count + -37.534348 * area2 + 4449e-6 * count * area2 + -0 * count ** 2 + 44.428603 * area2 ** 2;
style.fillOpacity = clamp24(fillOpacity / dilation, fillOpacity / 0.1 * opacityScale, 1);
}
this.applyMarkerStyle(style, node, datum.point, fillBBox, { selected: datum.selected });
node.drawingMode = this.resolveMarkerDrawingModeForState(drawingMode, style);
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 style = getLabelStyles(this, datum, params, this.properties.label, isHighlight, activeHighlight);
text.text = datum.label.text;
text.fill = style.color;
text.x = datum.point?.x ?? 0;
text.y = datum.point?.y ?? 0;
text.fontStyle = style.fontStyle;
text.fontWeight = style.fontWeight;
text.fontSize = style.fontSize;
text.fontFamily = style.fontFamily;
text.textBaseline = "top";
text.fillOpacity = this.getHighlightStyle(isHighlight, datum.datumIndex).opacity ?? 1;
text.setBoxing(style);
});
}
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[ChartAxisDirection22.X];
const yAxis = axes[ChartAxisDirection22.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 ?? formatValue2(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 ?? formatValue2(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 style = this.getStyle();
const marker = this.getMarkerStyle(
this.properties.marker,
{},
void 0,
{
isHighlight: false,
checkForHighlight: false,
resolveMarkerSubPath: []
},
style
);
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/bubbleSeriesModule.ts
var themeTemplate3 = {
series: {
shape: "circle",
size: 7,
maxSize: 30,
fill: {
$applySwitch: [
{ $path: "type" },
{ $palette: "fill" },
["gradient", FILL_GRADIENT_RADIAL_REVERSED_DEFAULTS2],
["image", FILL_IMAGE_DEFAULTS3],
["pattern", FILL_PATTERN_DEFAULTS3]
]
},
stroke: { $palette: "stroke" },
fillOpacity: 0.8,
maxRenderedItems: 2e3,
label: {
...LABEL_BOXING_DEFAULTS3,
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_STYLE2
}
};
var BubbleSeriesModule = {
type: "series",
name: "bubble",
chartType: "cartesian",
version: VERSION,
dependencies: [CartesianChartModule],
options: bubbleSeriesOptionsDef,
predictAxis: predictCartesianAxis,
defaultAxes: {
x: {
type: CARTESIAN_AXIS_TYPE4.NUMBER,
position: CARTESIAN_POSITION4.BOTTOM
},
y: {
type: CARTESIAN_AXIS_TYPE4.NUMBER,
position: CARTESIAN_POSITION4.LEFT
}
},
axisKeys: { [ChartAxisDirection23.X]: "xKeyAxis", [ChartAxisDirection23.Y]: "yKeyAxis" },
themeTemplate: themeTemplate3,
create: (ctx) => new BubbleSeries(ctx)
};
// packages/ag-charts-community/src/chart/series/cartesian/histogramSeriesModule.ts
import {
CARTESIAN_AXIS_TYPE as CARTESIAN_AXIS_TYPE5,
CARTESIAN_POSITION as CARTESIAN_POSITION5,
ChartAxisDirection as ChartAxisDirection25,
DEFAULT_SHADOW_COLOUR as DEFAULT_SHADOW_COLOUR4,
FILL_GRADIENT_LINEAR_DEFAULTS as FILL_GRADIENT_LINEAR_DEFAULTS3,
FILL_IMAGE_DEFAULTS as FILL_IMAGE_DEFAULTS4,
FILL_PATTERN_DEFAULTS as FILL_PATTERN_DEFAULTS4,
LABEL_BOXING_DEFAULTS as LABEL_BOXING_DEFAULTS4,
MULTI_SERIES_HIGHLIGHT_STYLE as MULTI_SERIES_HIGHLIGHT_STYLE3
} from "ag-charts-core";
// packages/ag-charts-community/src/chart/series/cartesian/histogramSeries.ts
import {
ChartAxisDirection as ChartAxisDirection24,
createTicks as createTicks4,
deepClone as deepClone9,
findMinMax as findMinMax10,
isDate as isDate4,
isNumber as isNumber6,
mergeDefaults as mergeDefaults14,
tickStep as tickStep2
} from "ag-charts-core";
// packages/ag-charts-community/src/chart/series/cartesian/histogramSeriesProperties.ts
import { Property as Property41 } from "ag-charts-core";
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([
Property41
], HistogramSeriesProperties.prototype, "xKey", 2);
__decorateClass([
Property41
], HistogramSeriesProperties.prototype, "yKey", 2);
__decorateClass([
Property41
], HistogramSeriesProperties.prototype, "xName", 2);
__decorateClass([
Property41
], HistogramSeriesProperties.prototype, "yName", 2);
__decorateClass([
Property41
], HistogramSeriesProperties.prototype, "fill", 2);
__decorateClass([
Property41
], HistogramSeriesProperties.prototype, "fillOpacity", 2);
__decorateClass([
Property41
], HistogramSeriesProperties.prototype, "stroke", 2);
__decorateClass([
Property41
], HistogramSeriesProperties.prototype, "strokeWidth", 2);
__decorateClass([
Property41
], HistogramSeriesProperties.prototype, "strokeOpacity", 2);
__decorateClass([
Property41
], HistogramSeriesProperties.prototype, "lineDash", 2);
__decorateClass([
Property41
], HistogramSeriesProperties.prototype, "lineDashOffset", 2);
__decorateClass([
Property41
], HistogramSeriesProperties.prototype, "cornerRadius", 2);
__decorateClass([
Property41
], HistogramSeriesProperties.prototype, "areaPlot", 2);
__decorateClass([
Property41
], HistogramSeriesProperties.prototype, "bins", 2);
__decorateClass([
Property41
], HistogramSeriesProperties.prototype, "aggregation", 2);
__decorateClass([
Property41
], HistogramSeriesProperties.prototype, "binCount", 2);
__decorateClass([
Property41
], HistogramSeriesProperties.prototype, "shadow", 2);
__decorateClass([
Property41
], HistogramSeriesProperties.prototype, "label", 2);
__decorateClass([
Property41
], 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 = createTicks4(xDomain[0], xDomain[1], defaultBinCount).ticks;
const binSize = tickStep2(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, binSize } = this.calculateNiceStart(startGuess, stop, segments);
return this.getBins(start, stop, binSize, segments);
}
getBins(start, stop, step, count) {
const bins = [];
const precision = this.calculatePrecision(step);
for (let i = 0; i < count; i++) {
const a = Math.round((start + i * step) * precision) / precision;
let b = Math.round((start + (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 start = Math.floor(a / magnitude) * magnitude;
return {
start,
binSize
};
}
async processData(dataController) {
const { visible } = this;
const { xKey, yKey, areaPlot, aggregation } = this.properties;
const xScale = this.axes[ChartAxisDirection24.X]?.scale;
const yScale = this.axes[ChartAxisDirection24.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 = isNumber6(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 (isDate4(xValue)) {
xValue = xValue.getTime();
}
if (!isNumber6(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 === ChartAxisDirection24.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[ChartAxisDirection24.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] = findMinMax10([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 mergeDefaults14(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 style = datum.style ?? contextNodeData.styles[this.getHighlightState(highlightedDatum, opts.isHighlight, datum.datumIndex)];
const { cornerRadius = 0 } = style;
const { topLeftCornerRadius, topRightCornerRadius, bottomRightCornerRadius, bottomLeftCornerRadius } = datum;
rect2.setStyleProperties(style, 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 style = getLabelStyles(
this,
datum,
this.properties,
this.properties.label,
isHighlight,
activeHighlight
);
const { enabled, fontStyle, fontWeight: fontWeight2, fontSize, fontFamily, color: color8 } = style;
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 = color8;
text.visible = true;
text.fillOpacity = this.getHighlightStyle(isHighlight, datum.datumIndex).opacity ?? 1;
text.setBoxing(style);
} 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[ChartAxisDirection24.X];
const yAxis = axes[ChartAxisDirection24.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: deepClone9(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/histogramSeriesModule.ts
var themeTemplate4 = {
series: {
fill: {
$applySwitch: [
{ $path: "type" },
{ $palette: "fill" },
["gradient", FILL_GRADIENT_LINEAR_DEFAULTS3],
["image", FILL_IMAGE_DEFAULTS4],
["pattern", FILL_PATTERN_DEFAULTS4]
]
},
stroke: { $palette: "stroke" },
strokeWidth: 1,
fillOpacity: 1,
strokeOpacity: 1,
lineDash: [0],
lineDashOffset: 0,
label: {
...LABEL_BOXING_DEFAULTS4,
enabled: false,
fontSize: { $ref: "fontSize" },
fontFamily: { $ref: "fontFamily" },
fontWeight: { $ref: "fontWeight" },
color: { $ref: "chartBackgroundColor" }
},
shadow: {
enabled: false,
color: DEFAULT_SHADOW_COLOUR4,
xOffset: 3,
yOffset: 3,
blur: 5
},
highlight: MULTI_SERIES_HIGHLIGHT_STYLE3
}
};
var HistogramSeriesModule = {
type: "series",
name: "histogram",
chartType: "cartesian",
// enterprise: true,
version: VERSION,
dependencies: [CartesianChartModule],
options: histogramSeriesOptionsDef,
predictAxis: predictCartesianNonPrimitiveAxis,
defaultAxes: {
x: {
type: CARTESIAN_AXIS_TYPE5.NUMBER,
position: CARTESIAN_POSITION5.BOTTOM
},
y: {
type: CARTESIAN_AXIS_TYPE5.NUMBER,
position: CARTESIAN_POSITION5.LEFT
}
},
axisKeys: { [ChartAxisDirection25.X]: "xKeyAxis", [ChartAxisDirection25.Y]: "yKeyAxis" },
themeTemplate: themeTemplate4,
create: (ctx) => new HistogramSeries(ctx)
};
// packages/ag-charts-community/src/chart/series/cartesian/lineSeriesModule.ts
import {
CARTESIAN_AXIS_TYPE as CARTESIAN_AXIS_TYPE6,
CARTESIAN_POSITION as CARTESIAN_POSITION6,
ChartAxisDirection as ChartAxisDirection27,
FILL_GRADIENT_RADIAL_REVERSED_DEFAULTS as FILL_GRADIENT_RADIAL_REVERSED_DEFAULTS3,
FILL_IMAGE_DEFAULTS as FILL_IMAGE_DEFAULTS5,
FILL_PATTERN_DEFAULTS as FILL_PATTERN_DEFAULTS5,
LABEL_BOXING_DEFAULTS as LABEL_BOXING_DEFAULTS5,
MARKER_SERIES_HIGHLIGHT_STYLE as MARKER_SERIES_HIGHLIGHT_STYLE2,
SAFE_STROKE_FILL_OPERATION,
SEGMENTATION_DEFAULTS as SEGMENTATION_DEFAULTS3
} from "ag-charts-core";
// packages/ag-charts-community/src/chart/series/cartesian/lineSeries.ts
import { ChartAxisDirection as ChartAxisDirection26, DebugMetrics as DebugMetrics5, extent as extent5, isDefined as isDefined3, mergeDefaults as mergeDefaults15 } from "ag-charts-core";
// packages/ag-charts-community/src/chart/series/cartesian/lineAggregation.ts
import {
AGGREGATION_INDEX_X_MAX as AGGREGATION_INDEX_X_MAX3,
AGGREGATION_INDEX_X_MIN as AGGREGATION_INDEX_X_MIN3,
AGGREGATION_INDEX_Y_MAX as AGGREGATION_INDEX_Y_MAX3,
AGGREGATION_INDEX_Y_MIN as AGGREGATION_INDEX_Y_MIN3,
AGGREGATION_MIN_RANGE as AGGREGATION_MIN_RANGE3,
AGGREGATION_THRESHOLD as AGGREGATION_THRESHOLD3,
aggregationDomain as aggregationDomain4,
aggregationIndexForXRatio as aggregationIndexForXRatio2,
aggregationRangeFittingPoints as aggregationRangeFittingPoints3,
aggregationXRatioForDatumIndex as aggregationXRatioForDatumIndex2,
aggregationXRatioForXValue as aggregationXRatioForXValue3,
compactAggregationIndices as compactAggregationIndices3,
createAggregationIndices as createAggregationIndices3,
nextPowerOf2 as nextPowerOf23,
simpleMemorize2 as simpleMemorize24
} from "ag-charts-core";
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) ? aggregationXRatioForXValue3(xValue, d0, d1, xNeedsValueOf) : aggregationXRatioForDatumIndex2(datumIndex, xValuesLength);
const aggIndex = aggregationIndexForXRatio2(xRatio, maxRange);
return datumIndex === indexData[aggIndex + AGGREGATION_INDEX_X_MIN3] || datumIndex === indexData[aggIndex + AGGREGATION_INDEX_X_MAX3] || datumIndex === indexData[aggIndex + AGGREGATION_INDEX_Y_MIN3] || datumIndex === indexData[aggIndex + AGGREGATION_INDEX_Y_MAX3];
}
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_THRESHOLD3)
return;
const [d0, d1] = domain;
const { xNeedsValueOf, yNeedsValueOf, existingFilters } = options;
let maxRange = aggregationRangeFittingPoints3(xValues, d0, d1, { xNeedsValueOf });
const existingFilter = existingFilters?.find((f) => f.maxRange === maxRange);
let { indexData, valueData } = createAggregationIndices3(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 = compactAggregationIndices3(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_THRESHOLD3)
return;
const [d0, d1] = domain;
const { xNeedsValueOf, yNeedsValueOf, targetRange, existingFilters } = options;
const finestMaxRange = aggregationRangeFittingPoints3(xValues, d0, d1, { xNeedsValueOf });
const targetMaxRange = Math.min(finestMaxRange, nextPowerOf23(Math.max(targetRange, AGGREGATION_MIN_RANGE3)));
const existingFilter = existingFilters?.find((f) => f.maxRange === targetMaxRange);
const { indexData, valueData } = createAggregationIndices3(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] = aggregationDomain4(scale2, domainInput);
return computeLineAggregation([d0, d1], xValues, yValues, { xNeedsValueOf, yNeedsValueOf });
}
var memoizedAggregateLineData = simpleMemorize24(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] = aggregationDomain4(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] = aggregationDomain4(scale2, domainInput);
return computeLineAggregationPartial([d0, d1], xValues, yValues, {
xNeedsValueOf,
yNeedsValueOf,
targetRange,
existingFilters
});
}
// packages/ag-charts-community/src/chart/series/cartesian/lineSeriesProperties.ts
import { InterpolationProperties as InterpolationProperties2, Property as Property42 } from "ag-charts-core";
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 InterpolationProperties2();
this.marker = new SeriesMarker();
this.label = new Label();
this.tooltip = makeSeriesTooltip();
this.connectMissingData = false;
this.sparklineMode = false;
}
};
__decorateClass([
Property42
], LineSeriesProperties.prototype, "xKey", 2);
__decorateClass([
Property42
], LineSeriesProperties.prototype, "yKey", 2);
__decorateClass([
Property42
], LineSeriesProperties.prototype, "xName", 2);
__decorateClass([
Property42
], LineSeriesProperties.prototype, "yName", 2);
__decorateClass([
Property42
], LineSeriesProperties.prototype, "yFilterKey", 2);
__decorateClass([
Property42
], LineSeriesProperties.prototype, "stackGroup", 2);
__decorateClass([
Property42
], LineSeriesProperties.prototype, "normalizedTo", 2);
__decorateClass([
Property42
], LineSeriesProperties.prototype, "title", 2);
__decorateClass([
Property42
], LineSeriesProperties.prototype, "stroke", 2);
__decorateClass([
Property42
], LineSeriesProperties.prototype, "strokeWidth", 2);
__decorateClass([
Property42
], LineSeriesProperties.prototype, "strokeOpacity", 2);
__decorateClass([
Property42
], LineSeriesProperties.prototype, "lineDash", 2);
__decorateClass([
Property42
], LineSeriesProperties.prototype, "lineDashOffset", 2);
__decorateClass([
Property42
], LineSeriesProperties.prototype, "interpolation", 2);
__decorateClass([
Property42
], LineSeriesProperties.prototype, "styler", 2);
__decorateClass([
Property42
], LineSeriesProperties.prototype, "marker", 2);
__decorateClass([
Property42
], LineSeriesProperties.prototype, "label", 2);
__decorateClass([
Property42
], LineSeriesProperties.prototype, "tooltip", 2);
__decorateClass([
Property42
], LineSeriesProperties.prototype, "connectMissingData", 2);
__decorateClass([
Property42
], 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[ChartAxisDirection26.X]?.scale;
const yScale = this.axes[ChartAxisDirection26.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 (isDefined3(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[ChartAxisDirection26.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[ChartAxisDirection26.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[ChartAxisDirection26.Y];
if (direction === ChartAxisDirection26.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(extent5(xDomain.domain)) };
}
const yExtent = this.domainForClippedRange(
ChartAxisDirection26.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(
ChartAxisDirection26.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[ChartAxisDirection26.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) {
DebugMetrics5.record(
`${this.type}:aggregation`,
filters.map((f) => f.maxRange)
);
}
}
estimateTargetRange() {
const xAxis = this.axes[ChartAxisDirection26.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 range4 = Math.abs(r1 - r0);
this.aggregationManager.ensureLevelForRange(range4);
const dataAggregationFilter = this.aggregationManager.getFilterForRange(range4);
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(ChartAxisDirection26.Y).domain,
labelsEnabled: this.properties.label.enabled,
animationEnabled: !this.ctx.animationManager.isSkipped(),
canIncrementallyUpdate,
dataAggregationFilter,
range: range4,
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 [start, end2] = this.visibleRangeIndices("xValue", ctx.xAxis.range, indices);
start = Math.max(start - 1, 0);
end2 = Math.min(end2 + 1, indices?.length ?? ctx.xValues.length);
if (this.processedData.input.count < 1e3) {
start = 0;
end2 = this.processedData.input.count;
}
for (let i = start; i < end2; 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 = mergeDefaults15(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[ChartAxisDirection26.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 style = datum.style ?? contextNodeData.styles[state];
this.applyMarkerStyle(style, node, datum.point, fillBBox, {
applyTranslation,
selected: datum.selected
});
node.drawingMode = this.resolveMarkerDrawingModeForState(drawingMode, style);
});
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 style = getLabelStyles(this, datum, params, this.properties.label, isHighlight, activeHighlight);
const { enabled, fontStyle, fontWeight: fontWeight2, fontSize, fontFamily, color: color8 } = style;
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 = color8;
text.visible = true;
text.fillOpacity = this.getHighlightStyle(isHighlight, datum.datumIndex).opacity ?? 1;
text.setBoxing(style);
} 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, style) {
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(style.fill) ?? style.fill;
return {
...datumStylerProperties(xValue, yValue, xKey, yKey, xDomain, yDomain),
xValue,
yValue,
...style,
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[ChartAxisDirection26.X];
const yAxis = axes[ChartAxisDirection26.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_DEFAULTS3],
["image", FILL_IMAGE_DEFAULTS5],
["pattern", FILL_PATTERN_DEFAULTS5]
]
},
stroke: { $palette: "stroke" }
},
label: {
...LABEL_BOXING_DEFAULTS5,
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_STYLE2,
segmentation: SEGMENTATION_DEFAULTS3
}
};
var LineSeriesModule = {
type: "series",
name: "line",
chartType: "cartesian",
stackable: true,
version: VERSION,
dependencies: [CartesianChartModule],
options: lineSeriesOptionsDef,
predictAxis: predictCartesianNonPrimitiveAxis,
defaultAxes: {
y: {
type: CARTESIAN_AXIS_TYPE6.NUMBER,
position: CARTESIAN_POSITION6.LEFT
},
x: {
type: CARTESIAN_AXIS_TYPE6.CATEGORY,
position: CARTESIAN_POSITION6.BOTTOM
}
},
axisKeys: { [ChartAxisDirection27.X]: "xKeyAxis", [ChartAxisDirection27.Y]: "yKeyAxis" },
themeTemplate: themeTemplate5,
create: (ctx) => new LineSeries(ctx)
};
// packages/ag-charts-community/src/chart/series/cartesian/scatterSeriesModule.ts
import {
CARTESIAN_AXIS_TYPE as CARTESIAN_AXIS_TYPE7,
CARTESIAN_POSITION as CARTESIAN_POSITION7,
ChartAxisDirection as ChartAxisDirection28,
FILL_GRADIENT_RADIAL_REVERSED_DEFAULTS as FILL_GRADIENT_RADIAL_REVERSED_DEFAULTS4,
FILL_IMAGE_DEFAULTS as FILL_IMAGE_DEFAULTS6,
FILL_PATTERN_DEFAULTS as FILL_PATTERN_DEFAULTS6,
LABEL_BOXING_DEFAULTS as LABEL_BOXING_DEFAULTS6,
MULTI_SERIES_HIGHLIGHT_STYLE as MULTI_SERIES_HIGHLIGHT_STYLE4
} from "ag-charts-core";
// 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/scatterSeriesModule.ts
var themeTemplate6 = {
series: {
shape: "circle",
size: 7,
fill: {
$applySwitch: [
{ $path: "type" },
{ $palette: "fill" },
["gradient", FILL_GRADIENT_RADIAL_REVERSED_DEFAULTS4],
["image", FILL_IMAGE_DEFAULTS6],
["pattern", FILL_PATTERN_DEFAULTS6]
]
},
stroke: { $palette: "stroke" },
fillOpacity: 0.8,
maxRenderedItems: 2e3,
label: {
...LABEL_BOXING_DEFAULTS6,
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_STYLE4
}
};
var ScatterSeriesModule = {
type: "series",
name: "scatter",
chartType: "cartesian",
version: VERSION,
dependencies: [CartesianChartModule],
options: scatterSeriesOptionsDef,
predictAxis: predictCartesianAxis,
defaultAxes: {
x: {
type: CARTESIAN_AXIS_TYPE7.NUMBER,
position: CARTESIAN_POSITION7.BOTTOM
},
y: {
type: CARTESIAN_AXIS_TYPE7.NUMBER,
position: CARTESIAN_POSITION7.LEFT
}
},
axisKeys: { [ChartAxisDirection28.X]: "xKeyAxis", [ChartAxisDirection28.Y]: "yKeyAxis" },
themeTemplate: themeTemplate6,
create: (ctx) => new ScatterSeries(ctx)
};
// packages/ag-charts-community/src/chart/series/polar/donutSeries.ts
import {
ChartAxisDirection as ChartAxisDirection29,
ChartUpdateType as ChartUpdateType12,
DebugMetrics as DebugMetrics6,
Logger as Logger41,
PolarZIndexMap as PolarZIndexMap2,
extractDomain as extractDomain3,
formatValue as formatValue3,
isGradientFill as isGradientFill5,
isStringFillArray,
jsonDiff as jsonDiff6,
mergeDefaults as mergeDefaults16,
modulus,
normalizeAngle180 as normalizeAngle1802,
toPlainText as toPlainText13,
toRadians as toRadians5,
wrapTextOrSegments as wrapTextOrSegments3
} from "ag-charts-core";
// packages/ag-charts-community/src/chart/series/polar/donutSeriesProperties.ts
import { BaseProperties as BaseProperties23, PropertiesArray as PropertiesArray3, Property as Property43 } from "ag-charts-core";
var DonutTitle = class extends Caption {
constructor() {
super(...arguments);
this.showInLegend = false;
}
};
__decorateClass([
Property43
], DonutTitle.prototype, "showInLegend", 2);
var DonutInnerLabel = class extends Label {
constructor() {
super(...arguments);
this.spacing = 2;
}
set(properties, _reset) {
return super.set(properties);
}
};
__decorateClass([
Property43
], DonutInnerLabel.prototype, "text", 2);
__decorateClass([
Property43
], DonutInnerLabel.prototype, "spacing", 2);
var DonutInnerCircle = class extends BaseProperties23 {
constructor() {
super(...arguments);
this.fill = "transparent";
this.fillOpacity = 1;
}
};
__decorateClass([
Property43
], DonutInnerCircle.prototype, "fill", 2);
__decorateClass([
Property43
], 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([
Property43
], DonutSeriesCalloutLabel.prototype, "offset", 2);
__decorateClass([
Property43
], DonutSeriesCalloutLabel.prototype, "minAngle", 2);
__decorateClass([
Property43
], DonutSeriesCalloutLabel.prototype, "minSpacing", 2);
__decorateClass([
Property43
], DonutSeriesCalloutLabel.prototype, "maxCollisionOffset", 2);
__decorateClass([
Property43
], DonutSeriesCalloutLabel.prototype, "avoidCollisions", 2);
var DonutSeriesSectorLabel = class extends Label {
constructor() {
super(...arguments);
this.positionOffset = 0;
this.positionRatio = 0.5;
}
};
__decorateClass([
Property43
], DonutSeriesSectorLabel.prototype, "positionOffset", 2);
__decorateClass([
Property43
], DonutSeriesSectorLabel.prototype, "positionRatio", 2);
var DonutSeriesCalloutLine = class extends BaseProperties23 {
constructor() {
super(...arguments);
this.length = 10;
this.strokeWidth = 1;
}
};
__decorateClass([
Property43
], DonutSeriesCalloutLine.prototype, "colors", 2);
__decorateClass([
Property43
], DonutSeriesCalloutLine.prototype, "length", 2);
__decorateClass([
Property43
], DonutSeriesCalloutLine.prototype, "strokeWidth", 2);
__decorateClass([
Property43
], 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 PropertiesArray3(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([
Property43
], DonutSeriesProperties.prototype, "angleKey", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "angleName", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "angleFilterKey", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "radiusKey", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "radiusName", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "radiusMin", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "radiusMax", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "calloutLabelKey", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "calloutLabelName", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "sectorLabelKey", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "sectorLabelName", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "legendItemKey", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "defaultColorRange", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "defaultPatternFills", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "fills", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "strokes", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "fillOpacity", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "strokeOpacity", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "lineDash", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "lineDashOffset", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "cornerRadius", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "itemStyler", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "rotation", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "outerRadiusOffset", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "outerRadiusRatio", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "innerRadiusOffset", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "innerRadiusRatio", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "strokeWidth", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "sectorSpacing", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "hideZeroValueSectorsInLegend", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "innerLabels", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "title", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "innerCircle", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "shadow", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "calloutLabel", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "sectorLabel", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "calloutLine", 2);
__decorateClass([
Property43
], DonutSeriesProperties.prototype, "tooltip", 2);
// packages/ag-charts-community/src/chart/series/polar/pieUtil.ts
import { isBetweenAngles as isBetweenAngles2, toRadians as toRadians4 } from "ag-charts-core";
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 + toRadians4(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 angle = Math.atan2(dy, dx);
const sectors = series.getItemNodes();
for (const sector of sectors) {
if (sector.datum.missing === true)
continue;
if (isBetweenAngles2(angle, sector.startAngle, sector.endAngle)) {
const radius = Math.hypot(dx, dy);
let distance = 0;
if (radius < sector.innerRadius) {
distance = sector.innerRadius - radius;
} else if (radius > sector.outerRadius) {
distance = radius - sector.outerRadius;
}
return { datum: sector.datum, distance };
}
}
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: PolarZIndexMap2.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((angle) => angle + 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 = [PolarZIndexMap2.BACKGROUND, zIndex];
}
nodeFactory() {
const sector = new Sector();
sector.miterLimit = 1e9;
return sector;
}
getSeriesDomain(direction) {
if (direction === ChartAxisDirection29.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) {
Logger41.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) {
DebugMetrics6.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) + toRadians5(rotation);
currentStart = currentValue;
sum += currentValue;
const endAngle = angleScale.convert(currentStart) + toRadians5(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 >= toRadians5(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 = normalizeAngle1802(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 (!isGradientFill5(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
} = mergeDefaults16(
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, style) {
const { angleKey, radiusKey, calloutLabelKey, sectorLabelKey, legendItemKey } = this.properties;
const fill = this.filterItemStylerFillParams(style.fill) ?? style.fill;
return {
datum,
angleKey,
radiusKey,
calloutLabelKey,
sectorLabelKey,
legendItemKey,
...style,
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 = jsonDiff6(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: color8, 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 = color8 ?? 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 length = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
const paddedLength = length - offset;
if (paddedLength > 0) {
x2 = x2 + dx * paddedLength / length;
y2 = y2 + dy * paddedLength / length;
}
}
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 style = this.getLabelStyle(datum, calloutLabel, "calloutLabel");
const padding2 = expandLabelPadding(style);
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 style = 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(style);
tempTextNode.setAlign(align2);
tempTextNode.setBoxing(style);
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 = wrapTextOrSegments3(label.text, options);
}
visible = !hasVerticalOverflow;
}
text.text = displayText;
text.x = x;
text.y = y;
text.setFont(style);
text.setAlign(align2);
text.setBoxing(style);
text.fill = style.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 style = 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(style);
text.setAlign({
textAlign: label.collisionTextAlign ?? label.textAlign,
textBaseline: label.textBaseline
});
text.setBoxing(style);
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 style = this.getLabelStyle(datum, properties.sectorLabel, "sectorLabel", isDatumHighlighted);
const labelRadius = innerRadius * (1 - positionRatio) + outerRadius * positionRatio + positionOffset;
text.fill = style.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(style);
text.setAlign(align2);
text.setBoxing(style);
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: color8 } = 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 = color8;
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 = extractDomain3(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
}) ?? formatValue3(angleRawValue, 3);
return this.formatTooltipWithContext(
tooltip,
{
title,
symbol: this.legendItemSymbol(datumIndex),
data: [{ label: toPlainText13(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 (isGradientFill5(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) => toPlainText13(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(ChartUpdateType12.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/donutTheme.ts
import { LABEL_BOXING_DEFAULTS as LABEL_BOXING_DEFAULTS8, PART_WHOLE_HIGHLIGHT_STYLE as PART_WHOLE_HIGHLIGHT_STYLE2 } from "ag-charts-core";
// packages/ag-charts-community/src/chart/series/polar/pieTheme.ts
import {
DEFAULT_SHADOW_COLOUR as DEFAULT_SHADOW_COLOUR5,
FILL_GRADIENT_RADIAL_REVERSED_SERIES_DEFAULTS,
FILL_IMAGE_DEFAULTS as FILL_IMAGE_DEFAULTS7,
FILL_PATTERN_DEFAULTS as FILL_PATTERN_DEFAULTS7,
FONT_SIZE_RATIO as FONT_SIZE_RATIO3,
LABEL_BOXING_DEFAULTS as LABEL_BOXING_DEFAULTS7,
PART_WHOLE_HIGHLIGHT_STYLE
} from "ag-charts-core";
var pieTheme = {
series: {
title: {
enabled: true,
fontWeight: { $ref: "fontWeight" },
fontSize: { $rem: FONT_SIZE_RATIO3.LARGE },
fontFamily: { $ref: "fontFamily" },
color: { $ref: "subtleTextColor" },
spacing: 5
},
calloutLabel: {
...LABEL_BOXING_DEFAULTS7,
enabled: true,
fontSize: { $ref: "fontSize" },
fontFamily: { $ref: "fontFamily" },
fontWeight: { $ref: "fontWeight" },
color: { $ref: "textColor" },
offset: 3,
minAngle: 1e-3
},
sectorLabel: {
...LABEL_BOXING_DEFAULTS7,
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_DEFAULTS7],
["image", FILL_IMAGE_DEFAULTS7]
]
}
]
},
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_COLOUR5,
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_DEFAULTS8,
fontSize: { $ref: "fontSize" },
fontFamily: { $ref: "fontFamily" },
fontWeight: { $ref: "fontWeight" },
color: { $ref: "textColor" },
spacing: 2
}
},
highlight: PART_WHOLE_HIGHLIGHT_STYLE2
}
};
// 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/locale/localeModule.ts
import { callbackOf as callbackOf5, object as object4, string as string16 } from "ag-charts-core";
// packages/ag-charts-community/src/locale/locale.ts
import { AbstractModuleInstance as AbstractModuleInstance3, ObserveChanges as ObserveChanges5, Property as Property44 } from "ag-charts-core";
var Locale = class extends AbstractModuleInstance3 {
constructor(ctx) {
super();
this.ctx = ctx;
this.localeText = void 0;
}
};
__decorateClass([
ObserveChanges5((target) => {
target.ctx.localeManager.setLocaleText(target.localeText);
}),
Property44
], Locale.prototype, "localeText", 2);
__decorateClass([
ObserveChanges5((target) => {
target.ctx.localeManager.setLocaleTextFormatter(target.getLocaleText);
}),
Property44
], Locale.prototype, "getLocaleText", 2);
// packages/ag-charts-community/src/locale/localeModule.ts
var LocaleModule = {
type: "plugin",
name: "locale",
version: VERSION,
options: {
localeText: object4,
getLocaleText: callbackOf5(string16)
},
create: (ctx) => new Locale(ctx)
};
// packages/ag-charts-community/src/module/axis-modules/numberAxisModule.ts
import "ag-charts-core";
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/logAxisModule.ts
import "ag-charts-core";
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/timeAxisModule.ts
import "ag-charts-core";
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/module/axis-modules/categoryAxisModule.ts
import "ag-charts-core";
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/module/axis-modules/groupedCategoryAxisModule.ts
import "ag-charts-core";
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/module/axis-modules/unitTimeAxisModule.ts
import "ag-charts-core";
// packages/ag-charts-community/src/chart/axis/unitTimeAxis.ts
import {
Property as Property45,
dateTruncationForDomain as dateTruncationForDomain2,
intervalEpoch as intervalEpoch2,
intervalFloor as intervalFloor4,
intervalStep as intervalStep3,
intervalUnit as intervalUnit3,
lowestGranularityUnitForTicks as lowestGranularityUnitForTicks2,
lowestGranularityUnitForValue as lowestGranularityUnitForValue2,
normalisedTimeExtentWithMetadata as normalisedTimeExtentWithMetadata2,
objectsEqual as objectsEqual13
} from "ag-charts-core";
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 = lowestGranularityUnitForValue2(domain[0]);
} else {
const { boundSeries, direction, min, max } = this;
defaultUnit = calculateDefaultUnit(boundSeries, direction, min, max);
}
if (!objectsEqual13(this.defaultUnit, defaultUnit)) {
this.defaultUnit = defaultUnit;
}
}
updateScale() {
super.updateScale();
this.scale.interval = this.unit ?? this.defaultUnit;
}
normaliseDataDomain(d) {
const { extent: extent6, clipped } = normalisedTimeExtentWithMetadata2(
d,
this.min,
this.max,
this.preferredMin,
this.preferredMax
);
return { domain: extent6, clipped };
}
tickFormatParams(domain, ticks, _fractionDigits, timeInterval2) {
timeInterval2 ?? (timeInterval2 = lowestGranularityUnitForTicks2(ticks));
const truncateDate = dateTruncationForDomain2(domain);
const unit = intervalUnit3(timeInterval2);
const step = intervalStep3(timeInterval2);
const epoch = intervalEpoch2(timeInterval2);
return { type: "date", unit, step, epoch, truncateDate };
}
datumFormatParams(value, params, _fractionDigits, timeInterval2, style) {
const interval = this.unit ?? this.defaultUnit ?? "millisecond";
timeInterval2 ?? (timeInterval2 = interval);
const { datum, seriesId, legendItemName, key, source, property, domain, boundSeries } = params;
const unit = intervalUnit3(timeInterval2);
const step = intervalStep3(timeInterval2);
const epoch = intervalEpoch2(timeInterval2);
return {
type: "date",
value: intervalFloor4(interval, value),
datum,
seriesId,
legendItemName,
key,
source,
property,
domain,
boundSeries,
unit,
step,
epoch,
style
};
}
};
UnitTimeAxis.className = "UnitTimeAxis";
UnitTimeAxis.type = "unit-time";
__decorateClass([
Property45
], UnitTimeAxis.prototype, "parentLevel", 2);
__decorateClass([
Property45
], UnitTimeAxis.prototype, "min", 2);
__decorateClass([
Property45
], UnitTimeAxis.prototype, "max", 2);
__decorateClass([
Property45
], UnitTimeAxis.prototype, "preferredMin", 2);
__decorateClass([
Property45
], UnitTimeAxis.prototype, "preferredMax", 2);
__decorateClass([
Property45
], 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/all.ts
import "ag-charts-core";
// packages/ag-charts-community/src/module-bundles/cartesian-axes.ts
var AllCartesianAxesModule = [
NumberAxisModule,
LogAxisModule,
TimeAxisModule,
CategoryAxisModule,
GroupedCategoryAxisModule,
UnitTimeAxisModule
];
// 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/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/module-bundles/integrated.ts
import { ModuleRegistry as ModuleRegistry13 } from "ag-charts-core";
// packages/ag-charts-community/src/integrated-charts-scene.ts
var integrated_charts_scene_exports = {};
__export(integrated_charts_scene_exports, {
Arc: () => Arc,
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: () => toRadians6
});
import { toRadians as toRadians6 } from "ag-charts-core";
// packages/ag-charts-community/src/integrated-charts-theme.ts
var integrated_charts_theme_exports = {};
__export(integrated_charts_theme_exports, {
ChartTheme: () => ChartTheme,
getChartTheme: () => getChartTheme,
resolveOperation: () => resolveOperation,
themeNames: () => themeNames,
themeSymbols: () => themeSymbols,
themes: () => themes
});
import {
DEFAULT_ANNOTATION_HANDLE_FILL as DEFAULT_ANNOTATION_HANDLE_FILL3,
DEFAULT_ANNOTATION_STATISTICS_COLOR as DEFAULT_ANNOTATION_STATISTICS_COLOR3,
DEFAULT_ANNOTATION_STATISTICS_DIVIDER_STROKE as DEFAULT_ANNOTATION_STATISTICS_DIVIDER_STROKE3,
DEFAULT_ANNOTATION_STATISTICS_DOWN_FILL as DEFAULT_ANNOTATION_STATISTICS_DOWN_FILL2,
DEFAULT_ANNOTATION_STATISTICS_DOWN_STROKE as DEFAULT_ANNOTATION_STATISTICS_DOWN_STROKE2,
DEFAULT_ANNOTATION_STATISTICS_FILL as DEFAULT_ANNOTATION_STATISTICS_FILL3,
DEFAULT_ANNOTATION_STATISTICS_STROKE as DEFAULT_ANNOTATION_STATISTICS_STROKE3,
DEFAULT_CAPTION_ALIGNMENT as DEFAULT_CAPTION_ALIGNMENT4,
DEFAULT_CAPTION_LAYOUT_STYLE as DEFAULT_CAPTION_LAYOUT_STYLE4,
DEFAULT_FIBONACCI_STROKES as DEFAULT_FIBONACCI_STROKES2,
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL as DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL13,
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR as DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR13,
DEFAULT_POLAR_SERIES_STROKE as DEFAULT_POLAR_SERIES_STROKE3,
DEFAULT_SHADOW_COLOUR as DEFAULT_SHADOW_COLOUR6,
DEFAULT_SPARKLINE_CROSSHAIR_STROKE as DEFAULT_SPARKLINE_CROSSHAIR_STROKE3,
DEFAULT_TEXTBOX_COLOR as DEFAULT_TEXTBOX_COLOR3,
DEFAULT_TEXTBOX_FILL as DEFAULT_TEXTBOX_FILL3,
DEFAULT_TEXTBOX_STROKE as DEFAULT_TEXTBOX_STROKE3,
DEFAULT_TEXT_ANNOTATION_COLOR as DEFAULT_TEXT_ANNOTATION_COLOR3,
DEFAULT_TOOLBAR_POSITION as DEFAULT_TOOLBAR_POSITION4,
IS_DARK_THEME as IS_DARK_THEME3,
PALETTE_ALT_DOWN_FILL as PALETTE_ALT_DOWN_FILL2,
PALETTE_ALT_DOWN_STROKE as PALETTE_ALT_DOWN_STROKE2,
PALETTE_ALT_NEUTRAL_FILL as PALETTE_ALT_NEUTRAL_FILL2,
PALETTE_ALT_NEUTRAL_STROKE as PALETTE_ALT_NEUTRAL_STROKE2,
PALETTE_ALT_UP_FILL as PALETTE_ALT_UP_FILL2,
PALETTE_ALT_UP_STROKE as PALETTE_ALT_UP_STROKE2,
PALETTE_DOWN_FILL as PALETTE_DOWN_FILL2,
PALETTE_DOWN_STROKE as PALETTE_DOWN_STROKE2,
PALETTE_NEUTRAL_FILL as PALETTE_NEUTRAL_FILL2,
PALETTE_NEUTRAL_STROKE as PALETTE_NEUTRAL_STROKE2,
PALETTE_UP_FILL as PALETTE_UP_FILL2,
PALETTE_UP_STROKE as PALETTE_UP_STROKE2
} from "ag-charts-core";
__reExport(integrated_charts_theme_exports, ag_charts_core_star);
import * as ag_charts_core_star from "ag-charts-core";
var themeSymbols = {
DEFAULT_ANNOTATION_HANDLE_FILL: DEFAULT_ANNOTATION_HANDLE_FILL3,
DEFAULT_ANNOTATION_STATISTICS_COLOR: DEFAULT_ANNOTATION_STATISTICS_COLOR3,
DEFAULT_ANNOTATION_STATISTICS_DIVIDER_STROKE: DEFAULT_ANNOTATION_STATISTICS_DIVIDER_STROKE3,
DEFAULT_ANNOTATION_STATISTICS_DOWN_FILL: DEFAULT_ANNOTATION_STATISTICS_DOWN_FILL2,
DEFAULT_ANNOTATION_STATISTICS_DOWN_STROKE: DEFAULT_ANNOTATION_STATISTICS_DOWN_STROKE2,
DEFAULT_ANNOTATION_STATISTICS_FILL: DEFAULT_ANNOTATION_STATISTICS_FILL3,
DEFAULT_ANNOTATION_STATISTICS_STROKE: DEFAULT_ANNOTATION_STATISTICS_STROKE3,
DEFAULT_CAPTION_ALIGNMENT: DEFAULT_CAPTION_ALIGNMENT4,
DEFAULT_CAPTION_LAYOUT_STYLE: DEFAULT_CAPTION_LAYOUT_STYLE4,
DEFAULT_FIBONACCI_STROKES: DEFAULT_FIBONACCI_STROKES2,
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL: DEFAULT_FINANCIAL_CHARTS_ANNOTATION_BACKGROUND_FILL13,
DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR: DEFAULT_FINANCIAL_CHARTS_ANNOTATION_COLOR13,
DEFAULT_POLAR_SERIES_STROKE: DEFAULT_POLAR_SERIES_STROKE3,
DEFAULT_SHADOW_COLOUR: DEFAULT_SHADOW_COLOUR6,
DEFAULT_SPARKLINE_CROSSHAIR_STROKE: DEFAULT_SPARKLINE_CROSSHAIR_STROKE3,
DEFAULT_TEXTBOX_COLOR: DEFAULT_TEXTBOX_COLOR3,
DEFAULT_TEXTBOX_FILL: DEFAULT_TEXTBOX_FILL3,
DEFAULT_TEXTBOX_STROKE: DEFAULT_TEXTBOX_STROKE3,
DEFAULT_TEXT_ANNOTATION_COLOR: DEFAULT_TEXT_ANNOTATION_COLOR3,
DEFAULT_TOOLBAR_POSITION: DEFAULT_TOOLBAR_POSITION4,
IS_DARK_THEME: IS_DARK_THEME3,
PALETTE_ALT_DOWN_FILL: PALETTE_ALT_DOWN_FILL2,
PALETTE_ALT_DOWN_STROKE: PALETTE_ALT_DOWN_STROKE2,
PALETTE_ALT_NEUTRAL_FILL: PALETTE_ALT_NEUTRAL_FILL2,
PALETTE_ALT_NEUTRAL_STROKE: PALETTE_ALT_NEUTRAL_STROKE2,
PALETTE_ALT_UP_FILL: PALETTE_ALT_UP_FILL2,
PALETTE_ALT_UP_STROKE: PALETTE_ALT_UP_STROKE2,
PALETTE_DOWN_FILL: PALETTE_DOWN_FILL2,
PALETTE_DOWN_STROKE: PALETTE_DOWN_STROKE2,
PALETTE_NEUTRAL_FILL: PALETTE_NEUTRAL_FILL2,
PALETTE_NEUTRAL_STROKE: PALETTE_NEUTRAL_STROKE2,
PALETTE_UP_FILL: PALETTE_UP_FILL2,
PALETTE_UP_STROKE: PALETTE_UP_STROKE2
};
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: () => Color7,
interpolateColor: () => interpolateColor
});
import { Color as Color7 } from "ag-charts-core";
// 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: () => {
ModuleRegistry13.setRegistryMode(ModuleRegistry13.RegistryMode.Integrated);
ModuleRegistry13.registerModules(AllCommunityModule);
},
isEnterprise: false
};
export {
AG_CHARTS_LOCALE_EN_US,
AgCharts,
AgChartsCommunityModule,
AgTooltipAnchorToType,
AgTooltipPlacementType,
AllCartesianAxesModule,
AllCartesianModule,
AllCartesianSeriesModule,
AllCommunityModule,
AllPolarModule,
AreaSeriesModule,
BarSeriesModule,
BubbleSeriesModule,
CartesianChartModule,
CategoryAxisModule,
DonutSeriesModule,
GroupedCategoryAxisModule,
HistogramSeriesModule,
LegendModule,
LineSeriesModule,
LocaleModule,
LogAxisModule,
ModuleRegistry14 as ModuleRegistry,
NumberAxisModule,
PieSeriesModule,
PolarChartModule,
ScatterSeriesModule,
SparklinePresetModule,
TimeAxisModule,
UnitTimeAxisModule,
VERSION,
module_support_exports as _ModuleSupport,
integrated_charts_scene_exports as _Scene,
integrated_charts_theme_exports as _Theme,
integrated_charts_util_exports as _Util,
exports_exports as _Widget,
time
};