@unovis/ts 1.6.0-bigip.0 → 1.6.0-bigip.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/components/area/index.js +2 -1
  2. package/components/area/index.js.map +1 -1
  3. package/components/crosshair/config.d.ts +12 -11
  4. package/components/crosshair/config.js +1 -1
  5. package/components/crosshair/config.js.map +1 -1
  6. package/components/crosshair/index.d.ts +4 -10
  7. package/components/crosshair/index.js +110 -216
  8. package/components/crosshair/index.js.map +1 -1
  9. package/components/timeline/index.js +3 -4
  10. package/components/timeline/index.js.map +1 -1
  11. package/containers/single-container/index.js +3 -1
  12. package/containers/single-container/index.js.map +1 -1
  13. package/containers/xy-container/index.js +3 -1
  14. package/containers/xy-container/index.js.map +1 -1
  15. package/core/component/index.d.ts +4 -0
  16. package/core/component/index.js +6 -0
  17. package/core/component/index.js.map +1 -1
  18. package/core/xy-component/index.d.ts +1 -0
  19. package/core/xy-component/index.js +3 -1
  20. package/core/xy-component/index.js.map +1 -1
  21. package/index.js +1 -0
  22. package/index.js.map +1 -1
  23. package/package.json +1 -1
  24. package/styles/index.js +1 -0
  25. package/styles/index.js.map +1 -1
  26. package/types/data.d.ts +5 -0
  27. package/types/data.js +7 -0
  28. package/types/data.js.map +1 -1
  29. package/types/text.d.ts +1 -1
  30. package/types/text.js.map +1 -1
  31. package/types.js +1 -1
  32. package/utils/data.d.ts +3 -3
  33. package/utils/data.js +38 -8
  34. package/utils/data.js.map +1 -1
  35. package/utils/text.d.ts +1 -1
  36. package/utils/text.js +11 -7
  37. package/utils/text.js.map +1 -1
  38. package/components/crosshair/sync-bus.d.ts +0 -9
  39. package/components/crosshair/sync-bus.js +0 -27
  40. package/components/crosshair/sync-bus.js.map +0 -1
@@ -101,7 +101,8 @@ class Area extends XYComponentCore {
101
101
  getYDataExtent(scaleByVisibleData) {
102
102
  const { config, datamodel } = this;
103
103
  const yAccessors = (isArray(config.y) ? config.y : [config.y]);
104
- const data = scaleByVisibleData ? filterDataByRange(datamodel.data, this.xScale.domain(), config.x) : datamodel.data;
104
+ const xDomain = this.xScale.domain();
105
+ const data = scaleByVisibleData ? filterDataByRange(datamodel.data, xDomain, config.x, true) : datamodel.data;
105
106
  return getStackedExtent(data, config.baseline, ...yAccessors);
106
107
  }
107
108
  _emptyPath() {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/components/area/index.ts"],"sourcesContent":["import { select } from 'd3-selection'\nimport { Transition } from 'd3-transition'\nimport { area, Area as AreaInterface } from 'd3-shape'\nimport { interpolatePath } from 'd3-interpolate-path'\n\n// Core\nimport { XYComponentCore } from 'core/xy-component'\n\n// Utils\nimport { getNumber, getString, isArray, isNumber, getStackedExtent, getStackedData, filterDataByRange } from 'utils/data'\nimport { smartTransition } from 'utils/d3'\nimport { getColor } from 'utils/color'\n\n// Types\nimport { Curve, CurveType } from 'types/curve'\nimport { NumericAccessor } from 'types/accessor'\n\n// Local Types\nimport { AreaDatum } from './types'\n\n// Config\nimport { AreaDefaultConfig, AreaConfigInterface } from './config'\n\n// Styles\nimport * as s from './style'\n\nexport class Area<Datum> extends XYComponentCore<Datum, AreaConfigInterface<Datum>> {\n static selectors = s\n protected _defaultConfig = AreaDefaultConfig as AreaConfigInterface<Datum>\n public config: AreaConfigInterface<Datum> = this._defaultConfig\n public stacked = true\n private _areaGen: AreaInterface<AreaDatum>\n private _prevNegative: boolean[] | undefined // To help guessing the stack direction when an accessor was set to null or 0\n\n events = {\n [Area.selectors.area]: {},\n }\n\n constructor (config?: AreaConfigInterface<Datum>) {\n super()\n if (config) this.setConfig(config)\n\n // Determine if the provided chart should be stacked\n this.stacked = Array.isArray(this.config.y)\n }\n\n _render (customDuration?: number): void {\n super._render(customDuration)\n const { config, datamodel: { data } } = this\n const duration = isNumber(customDuration) ? customDuration : config.duration\n\n const curveGen = Curve[config.curveType as CurveType]\n this._areaGen = area<AreaDatum>()\n .x(d => d.x)\n .y0(d => d.y0)\n .y1(d => d.y1)\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n .curve(curveGen)\n\n const yAccessors = (isArray(config.y) ? config.y : [config.y]) as NumericAccessor<Datum>[]\n const areaDataX = data.map((d, i) => this.xScale(getNumber(d, config.x, i)))\n\n const stacked = getStackedData(data, config.baseline, yAccessors, this._prevNegative)\n this._prevNegative = stacked.map(s => !!s.isMostlyNegative)\n const minHeightCumulativeArray: number[] = []\n const stackedData: AreaDatum[][] = stacked.map(\n arr => arr.map(\n (d, j) => {\n const x = areaDataX[j]\n const y0 = this.yScale(d[0])\n const y1 = this.yScale(d[1])\n const isNegativeArea = y1 > y0\n\n // Get cumulative adjustment and apply in the correct direction\n const cumulative = minHeightCumulativeArray[j] || 0\n const adjustedY0 = isNegativeArea ? y0 + cumulative : y0 - cumulative\n const adjustedY1 = isNegativeArea ? y1 + cumulative : y1 - cumulative\n\n // Calculate height adjustment if needed\n let heightAdjustment = 0\n if ((config.minHeight || config.minHeight1Px) &&\n Math.abs(adjustedY1 - adjustedY0) < (config.minHeight ?? 1)) {\n heightAdjustment = (config.minHeight ?? 1) - Math.abs(adjustedY1 - adjustedY0)\n minHeightCumulativeArray[j] = cumulative + heightAdjustment\n }\n\n return {\n x,\n y0: adjustedY0,\n y1: isNegativeArea ? adjustedY1 + heightAdjustment : adjustedY1 - heightAdjustment,\n }\n }\n )\n )\n\n // We reverse the data in order to have the first areas to be displayed on top\n // for better visibility when they're close to zero\n const areaMaxIdx = stackedData.length - 1\n const stackedDataReversed = stackedData.reverse()\n const areas = this.g\n .selectAll<SVGPathElement, AreaDatum>(`.${s.area}`)\n .data(stackedDataReversed)\n\n const areasEnter = areas.enter().append('path')\n .attr('class', s.area)\n .attr('d', d => this._areaGen(d) || this._emptyPath())\n .style('opacity', 0)\n .style('fill', (d, i) => getColor(data, config.color, areaMaxIdx - i))\n\n const areasMerged = smartTransition(areasEnter.merge(areas), duration)\n .style('opacity', (d, i) => {\n const isDefined = d.some(p => (p.y0 - p.y1) !== 0)\n return isDefined ? getNumber(data, config.opacity, areaMaxIdx - i) : 0\n })\n .style('fill', (d, i) => getColor(data, config.color, areaMaxIdx - i))\n .style('cursor', (d, i) => getString(data, config.cursor, areaMaxIdx - i))\n\n if (duration) {\n const transition = areasMerged as Transition<SVGPathElement, AreaDatum[], SVGGElement, AreaDatum[]>\n transition.attrTween('d', (d, i, el) => {\n const previous = select(el[i]).attr('d')\n const next = this._areaGen(d) || this._emptyPath()\n return interpolatePath(previous, next)\n })\n } else {\n areasMerged.attr('d', d => this._areaGen(d) || this._emptyPath())\n }\n\n smartTransition(areas.exit(), duration)\n .style('opacity', 0)\n .remove()\n }\n\n getYDataExtent (scaleByVisibleData: boolean): number[] {\n const { config, datamodel } = this\n const yAccessors = (isArray(config.y) ? config.y : [config.y]) as NumericAccessor<Datum>[]\n\n const data = scaleByVisibleData ? filterDataByRange(datamodel.data, this.xScale.domain() as [number, number], config.x) : datamodel.data\n return getStackedExtent(data, config.baseline, ...yAccessors)\n }\n\n _emptyPath (): string {\n const xRange = this.xScale.range()\n const yDomain = this.yScale.domain() as number[]\n\n const y0 = this.yScale((yDomain[0] + yDomain[1]) / 2)\n const y1 = y0\n\n return this._areaGen([\n { y0, y1, x: xRange[0] },\n { y0, y1, x: xRange[1] },\n ])\n }\n}\n"],"names":["s.area","s"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/components/area/index.ts"],"sourcesContent":["import { select } from 'd3-selection'\nimport { Transition } from 'd3-transition'\nimport { area, Area as AreaInterface } from 'd3-shape'\nimport { interpolatePath } from 'd3-interpolate-path'\n\n// Core\nimport { XYComponentCore } from 'core/xy-component'\n\n// Utils\nimport { getNumber, getString, isArray, isNumber, getStackedExtent, getStackedData, filterDataByRange } from 'utils/data'\nimport { smartTransition } from 'utils/d3'\nimport { getColor } from 'utils/color'\n\n// Types\nimport { Curve, CurveType } from 'types/curve'\nimport { NumericAccessor } from 'types/accessor'\n\n// Local Types\nimport { AreaDatum } from './types'\n\n// Config\nimport { AreaDefaultConfig, AreaConfigInterface } from './config'\n\n// Styles\nimport * as s from './style'\n\nexport class Area<Datum> extends XYComponentCore<Datum, AreaConfigInterface<Datum>> {\n static selectors = s\n protected _defaultConfig = AreaDefaultConfig as AreaConfigInterface<Datum>\n public config: AreaConfigInterface<Datum> = this._defaultConfig\n public stacked = true\n private _areaGen: AreaInterface<AreaDatum>\n private _prevNegative: boolean[] | undefined // To help guessing the stack direction when an accessor was set to null or 0\n\n events = {\n [Area.selectors.area]: {},\n }\n\n constructor (config?: AreaConfigInterface<Datum>) {\n super()\n if (config) this.setConfig(config)\n\n // Determine if the provided chart should be stacked\n this.stacked = Array.isArray(this.config.y)\n }\n\n _render (customDuration?: number): void {\n super._render(customDuration)\n const { config, datamodel: { data } } = this\n const duration = isNumber(customDuration) ? customDuration : config.duration\n\n const curveGen = Curve[config.curveType as CurveType]\n this._areaGen = area<AreaDatum>()\n .x(d => d.x)\n .y0(d => d.y0)\n .y1(d => d.y1)\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n .curve(curveGen)\n\n const yAccessors = (isArray(config.y) ? config.y : [config.y]) as NumericAccessor<Datum>[]\n const areaDataX = data.map((d, i) => this.xScale(getNumber(d, config.x, i)))\n\n const stacked = getStackedData(data, config.baseline, yAccessors, this._prevNegative)\n this._prevNegative = stacked.map(s => !!s.isMostlyNegative)\n const minHeightCumulativeArray: number[] = []\n const stackedData: AreaDatum[][] = stacked.map(\n arr => arr.map(\n (d, j) => {\n const x = areaDataX[j]\n const y0 = this.yScale(d[0])\n const y1 = this.yScale(d[1])\n const isNegativeArea = y1 > y0\n\n // Get cumulative adjustment and apply in the correct direction\n const cumulative = minHeightCumulativeArray[j] || 0\n const adjustedY0 = isNegativeArea ? y0 + cumulative : y0 - cumulative\n const adjustedY1 = isNegativeArea ? y1 + cumulative : y1 - cumulative\n\n // Calculate height adjustment if needed\n let heightAdjustment = 0\n if ((config.minHeight || config.minHeight1Px) &&\n Math.abs(adjustedY1 - adjustedY0) < (config.minHeight ?? 1)) {\n heightAdjustment = (config.minHeight ?? 1) - Math.abs(adjustedY1 - adjustedY0)\n minHeightCumulativeArray[j] = cumulative + heightAdjustment\n }\n\n return {\n x,\n y0: adjustedY0,\n y1: isNegativeArea ? adjustedY1 + heightAdjustment : adjustedY1 - heightAdjustment,\n }\n }\n )\n )\n\n // We reverse the data in order to have the first areas to be displayed on top\n // for better visibility when they're close to zero\n const areaMaxIdx = stackedData.length - 1\n const stackedDataReversed = stackedData.reverse()\n const areas = this.g\n .selectAll<SVGPathElement, AreaDatum>(`.${s.area}`)\n .data(stackedDataReversed)\n\n const areasEnter = areas.enter().append('path')\n .attr('class', s.area)\n .attr('d', d => this._areaGen(d) || this._emptyPath())\n .style('opacity', 0)\n .style('fill', (d, i) => getColor(data, config.color, areaMaxIdx - i))\n\n const areasMerged = smartTransition(areasEnter.merge(areas), duration)\n .style('opacity', (d, i) => {\n const isDefined = d.some(p => (p.y0 - p.y1) !== 0)\n return isDefined ? getNumber(data, config.opacity, areaMaxIdx - i) : 0\n })\n .style('fill', (d, i) => getColor(data, config.color, areaMaxIdx - i))\n .style('cursor', (d, i) => getString(data, config.cursor, areaMaxIdx - i))\n\n if (duration) {\n const transition = areasMerged as Transition<SVGPathElement, AreaDatum[], SVGGElement, AreaDatum[]>\n transition.attrTween('d', (d, i, el) => {\n const previous = select(el[i]).attr('d')\n const next = this._areaGen(d) || this._emptyPath()\n return interpolatePath(previous, next)\n })\n } else {\n areasMerged.attr('d', d => this._areaGen(d) || this._emptyPath())\n }\n\n smartTransition(areas.exit(), duration)\n .style('opacity', 0)\n .remove()\n }\n\n getYDataExtent (scaleByVisibleData: boolean): number[] {\n const { config, datamodel } = this\n const yAccessors = (isArray(config.y) ? config.y : [config.y]) as NumericAccessor<Datum>[]\n\n const xDomain = this.xScale.domain() as [number, number]\n const data = scaleByVisibleData ? filterDataByRange(datamodel.data, xDomain, config.x, true) : datamodel.data\n return getStackedExtent(data, config.baseline, ...yAccessors)\n }\n\n _emptyPath (): string {\n const xRange = this.xScale.range()\n const yDomain = this.yScale.domain() as number[]\n\n const y0 = this.yScale((yDomain[0] + yDomain[1]) / 2)\n const y1 = y0\n\n return this._areaGen([\n { y0, y1, x: xRange[0] },\n { y0, y1, x: xRange[1] },\n ])\n }\n}\n"],"names":["s.area","s"],"mappings":"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"}
@@ -21,9 +21,11 @@ export interface CrosshairConfigInterface<Datum> extends WithOptional<XYComponen
21
21
  baseline?: NumericAccessor<Datum>;
22
22
  /** An instance of the Tooltip component to be used with Crosshair. Default: `undefined` */
23
23
  tooltip?: Tooltip | undefined;
24
- /** Tooltip template accessor. The function is supposed to return either a valid HTML string or an HTMLElement. Default: `d => ''` */
25
- template?: (data: Datum, x: number | Date) => string | HTMLElement;
26
- /** Hide Crosshair when the corresponding element is far from mouse pointer. Default: `true` */
24
+ /** Tooltip template accessor. The function is supposed to return either a valid HTML string or an HTMLElement.
25
+ * When snapToData is false, datum will be undefined but nearestDatumIndex and data will be provided.
26
+ * Default: `d => ''` */
27
+ template?: (datum: Datum, x: number | Date, nearestDatumIndex?: number, data?: Datum[]) => string | HTMLElement;
28
+ /** Hide Crosshair when the corresponding datum element is far from mouse pointer. Default: `true` */
27
29
  hideWhenFarFromPointer?: boolean;
28
30
  /** Distance in pixels to check in the hideWhenFarFromPointer condition. Default: `100` */
29
31
  hideWhenFarFromPointerDistance?: number;
@@ -34,16 +36,15 @@ export interface CrosshairConfigInterface<Datum> extends WithOptional<XYComponen
34
36
  */
35
37
  snapToData?: boolean;
36
38
  /** Custom function for setting up the crosshair circles, usually needed when `snapToData` is set to `false`.
37
- * The function receives the horizontal position of the crosshair (in the data space, not in pixels), the data array
38
- * and the `yScale` instance to help you calculate the correct vertical position of the circles.
39
+ * The function receives the horizontal position of the crosshair (in the data space, not in pixels), the data array,
40
+ * the `yScale` instance to help you calculate the correct vertical position of the circles, and the nearest datum index.
39
41
  * It has to return an array of the CrosshairCircle objects: `{ y: number; color: string; opacity?: number }[]`.
40
42
  * Default: `undefined`
41
43
  */
42
- getCircles?: (x: number | Date | Date, data: Datum[], yScale: ContinuousScale) => CrosshairCircle[];
43
- /**
44
- * If set, enables crosshair synchronization between charts with the same syncId.
45
- * All charts with the same syncId will share crosshair position and events.
46
- */
47
- syncId?: string;
44
+ getCircles?: (x: number | Date, data: Datum[], yScale: ContinuousScale, nearestDatumIndex?: number) => CrosshairCircle[];
45
+ /** Callback function that is called when the crosshair is moved. Default: `undefined` */
46
+ onCrosshairMove?: (x: number | Date, datum: Datum | undefined, datumIndex: number | undefined) => void;
47
+ /** Force the crosshair to show at a specific position. Default: `undefined` */
48
+ forceShowAt?: number | Date;
48
49
  }
49
50
  export declare const CrosshairDefaultConfig: CrosshairConfigInterface<unknown>;
@@ -1,6 +1,6 @@
1
1
  import { XYComponentDefaultConfig } from '../../core/xy-component/config.js';
2
2
 
3
- const CrosshairDefaultConfig = Object.assign(Object.assign({}, XYComponentDefaultConfig), { yStacked: undefined, baseline: null, duration: 100, tooltip: undefined, template: (d, x) => '', hideWhenFarFromPointer: true, hideWhenFarFromPointerDistance: 100, snapToData: true, getCircles: undefined, color: undefined, strokeColor: undefined, strokeWidth: undefined, syncId: undefined });
3
+ const CrosshairDefaultConfig = Object.assign(Object.assign({}, XYComponentDefaultConfig), { yStacked: undefined, baseline: null, duration: 100, tooltip: undefined, template: (d, x, nearestDatumIndex, data) => '', hideWhenFarFromPointer: true, hideWhenFarFromPointerDistance: 100, snapToData: true, getCircles: undefined, color: undefined, strokeColor: undefined, strokeWidth: undefined, onCrosshairMove: undefined, forceShowAt: undefined });
4
4
 
5
5
  export { CrosshairDefaultConfig };
6
6
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sources":["../../../src/components/crosshair/config.ts"],"sourcesContent":["import { XYComponentConfigInterface, XYComponentDefaultConfig } from 'core/xy-component/config'\nimport { Tooltip } from 'components/tooltip'\n\n// Types\nimport { NumericAccessor, ColorAccessor } from 'types/accessor'\nimport { ContinuousScale } from 'types/scale'\nimport { WithOptional } from 'types/misc'\nimport { CrosshairCircle } from './types'\n\n// We extend partial XY config interface because x and y properties are optional for Crosshair\nexport interface CrosshairConfigInterface<Datum> extends WithOptional<XYComponentConfigInterface<Datum>, 'x' | 'y'> {\n /** Optional accessor function for getting the values along the X axis. Default: `undefined` */\n x?: NumericAccessor<Datum>;\n /** Optional single of multiple accessor functions for getting the values along the Y axis. Default: `undefined` */\n y?: NumericAccessor<Datum> | NumericAccessor<Datum>[];\n /** Optional color array or color accessor function for crosshair circles. Default: `d => d.color` */\n color?: ColorAccessor<Datum>;\n /** Optional stroke color accessor function for crosshair circles. Default: `undefined` */\n strokeColor?: ColorAccessor<Datum>;\n /** Optional stroke width for crosshair circles. Default: `undefined` */\n strokeWidth?: NumericAccessor<Datum>;\n /** Separate array of accessors for stacked components (eg StackedBar, Area). Default: `undefined` */\n yStacked?: NumericAccessor<Datum>[];\n /** Baseline accessor function for stacked values, useful with stacked areas. Default: `null` */\n baseline?: NumericAccessor<Datum>;\n /** An instance of the Tooltip component to be used with Crosshair. Default: `undefined` */\n tooltip?: Tooltip | undefined;\n /** Tooltip template accessor. The function is supposed to return either a valid HTML string or an HTMLElement. Default: `d => ''` */\n template?: (data: Datum, x: number | Date) => string | HTMLElement;\n /** Hide Crosshair when the corresponding element is far from mouse pointer. Default: `true` */\n hideWhenFarFromPointer?: boolean;\n /** Distance in pixels to check in the hideWhenFarFromPointer condition. Default: `100` */\n hideWhenFarFromPointerDistance?: number;\n /** Snap to the nearest data point.\n * If disabled, the tooltip template will receive only the horizontal position of the crosshair and you'll be responsible\n * for getting the underlying data records and crosshair circles (see the `getCircles` configuration option).\n * Default: `true`\n */\n snapToData?: boolean;\n /** Custom function for setting up the crosshair circles, usually needed when `snapToData` is set to `false`.\n * The function receives the horizontal position of the crosshair (in the data space, not in pixels), the data array\n * and the `yScale` instance to help you calculate the correct vertical position of the circles.\n * It has to return an array of the CrosshairCircle objects: `{ y: number; color: string; opacity?: number }[]`.\n * Default: `undefined`\n */\n getCircles?: (x: number | Date | Date, data: Datum[], yScale: ContinuousScale) => CrosshairCircle[];\n /**\n * If set, enables crosshair synchronization between charts with the same syncId.\n * All charts with the same syncId will share crosshair position and events.\n */\n syncId?: string;\n}\n\nexport const CrosshairDefaultConfig: CrosshairConfigInterface<unknown> = {\n ...XYComponentDefaultConfig,\n yStacked: undefined,\n baseline: null,\n duration: 100,\n tooltip: undefined,\n template: <Datum>(d: Datum, x: number | Date): string => '',\n hideWhenFarFromPointer: true,\n hideWhenFarFromPointerDistance: 100,\n snapToData: true,\n getCircles: undefined,\n color: undefined,\n strokeColor: undefined,\n strokeWidth: undefined,\n syncId: undefined,\n}\n\n"],"names":[],"mappings":";;AAqDO,MAAM,sBAAsB,GAAA,MAAA,CAAA,MAAA,CAAA,MAAA,CAAA,MAAA,CAAA,EAAA,EAC9B,wBAAwB,CAAA,EAAA,EAC3B,QAAQ,EAAE,SAAS,EACnB,QAAQ,EAAE,IAAI,EACd,QAAQ,EAAE,GAAG,EACb,OAAO,EAAE,SAAS,EAClB,QAAQ,EAAE,CAAQ,CAAQ,EAAE,CAAgB,KAAa,EAAE,EAC3D,sBAAsB,EAAE,IAAI,EAC5B,8BAA8B,EAAE,GAAG,EACnC,UAAU,EAAE,IAAI,EAChB,UAAU,EAAE,SAAS,EACrB,KAAK,EAAE,SAAS,EAChB,WAAW,EAAE,SAAS,EACtB,WAAW,EAAE,SAAS,EACtB,MAAM,EAAE,SAAS;;;;"}
1
+ {"version":3,"file":"config.js","sources":["../../../src/components/crosshair/config.ts"],"sourcesContent":["import { XYComponentConfigInterface, XYComponentDefaultConfig } from 'core/xy-component/config'\nimport { Tooltip } from 'components/tooltip'\n\n// Types\nimport { NumericAccessor, ColorAccessor } from 'types/accessor'\nimport { ContinuousScale } from 'types/scale'\nimport { WithOptional } from 'types/misc'\nimport { CrosshairCircle } from './types'\n\n// We extend partial XY config interface because x and y properties are optional for Crosshair\nexport interface CrosshairConfigInterface<Datum> extends WithOptional<XYComponentConfigInterface<Datum>, 'x' | 'y'> {\n /** Optional accessor function for getting the values along the X axis. Default: `undefined` */\n x?: NumericAccessor<Datum>;\n /** Optional single of multiple accessor functions for getting the values along the Y axis. Default: `undefined` */\n y?: NumericAccessor<Datum> | NumericAccessor<Datum>[];\n /** Optional color array or color accessor function for crosshair circles. Default: `d => d.color` */\n color?: ColorAccessor<Datum>;\n /** Optional stroke color accessor function for crosshair circles. Default: `undefined` */\n strokeColor?: ColorAccessor<Datum>;\n /** Optional stroke width for crosshair circles. Default: `undefined` */\n strokeWidth?: NumericAccessor<Datum>;\n /** Separate array of accessors for stacked components (eg StackedBar, Area). Default: `undefined` */\n yStacked?: NumericAccessor<Datum>[];\n /** Baseline accessor function for stacked values, useful with stacked areas. Default: `null` */\n baseline?: NumericAccessor<Datum>;\n /** An instance of the Tooltip component to be used with Crosshair. Default: `undefined` */\n tooltip?: Tooltip | undefined;\n /** Tooltip template accessor. The function is supposed to return either a valid HTML string or an HTMLElement.\n * When snapToData is false, datum will be undefined but nearestDatumIndex and data will be provided.\n * Default: `d => ''` */\n template?: (datum: Datum, x: number | Date, nearestDatumIndex?: number, data?: Datum[]) => string | HTMLElement;\n /** Hide Crosshair when the corresponding datum element is far from mouse pointer. Default: `true` */\n hideWhenFarFromPointer?: boolean;\n /** Distance in pixels to check in the hideWhenFarFromPointer condition. Default: `100` */\n hideWhenFarFromPointerDistance?: number;\n /** Snap to the nearest data point.\n * If disabled, the tooltip template will receive only the horizontal position of the crosshair and you'll be responsible\n * for getting the underlying data records and crosshair circles (see the `getCircles` configuration option).\n * Default: `true`\n */\n snapToData?: boolean;\n /** Custom function for setting up the crosshair circles, usually needed when `snapToData` is set to `false`.\n * The function receives the horizontal position of the crosshair (in the data space, not in pixels), the data array,\n * the `yScale` instance to help you calculate the correct vertical position of the circles, and the nearest datum index.\n * It has to return an array of the CrosshairCircle objects: `{ y: number; color: string; opacity?: number }[]`.\n * Default: `undefined`\n */\n getCircles?: (x: number | Date, data: Datum[], yScale: ContinuousScale, nearestDatumIndex?: number) => CrosshairCircle[];\n /** Callback function that is called when the crosshair is moved. Default: `undefined` */\n onCrosshairMove?: (x: number | Date, datum: Datum | undefined, datumIndex: number | undefined) => void;\n /** Force the crosshair to show at a specific position. Default: `undefined` */\n forceShowAt?: number | Date;\n}\n\nexport const CrosshairDefaultConfig: CrosshairConfigInterface<unknown> = {\n ...XYComponentDefaultConfig,\n yStacked: undefined,\n baseline: null,\n duration: 100,\n tooltip: undefined,\n template: <Datum>(d: Datum | undefined, x: number | Date, nearestDatumIndex?: number, data?: Datum[]): string => '',\n hideWhenFarFromPointer: true,\n hideWhenFarFromPointerDistance: 100,\n snapToData: true,\n getCircles: undefined,\n color: undefined,\n strokeColor: undefined,\n strokeWidth: undefined,\n onCrosshairMove: undefined,\n forceShowAt: undefined,\n}\n\n"],"names":[],"mappings":";;AAsDO,MAAM,sBAAsB,GAAA,MAAA,CAAA,MAAA,CAAA,MAAA,CAAA,MAAA,CAAA,EAAA,EAC9B,wBAAwB,CAC3B,EAAA,EAAA,QAAQ,EAAE,SAAS,EACnB,QAAQ,EAAE,IAAI,EACd,QAAQ,EAAE,GAAG,EACb,OAAO,EAAE,SAAS,EAClB,QAAQ,EAAE,CAAQ,CAAoB,EAAE,CAAgB,EAAE,iBAA0B,EAAE,IAAc,KAAa,EAAE,EACnH,sBAAsB,EAAE,IAAI,EAC5B,8BAA8B,EAAE,GAAG,EACnC,UAAU,EAAE,IAAI,EAChB,UAAU,EAAE,SAAS,EACrB,KAAK,EAAE,SAAS,EAChB,WAAW,EAAE,SAAS,EACtB,WAAW,EAAE,SAAS,EACtB,eAAe,EAAE,SAAS,EAC1B,WAAW,EAAE,SAAS;;;;"}
@@ -11,14 +11,10 @@ export declare class Crosshair<Datum> extends XYComponentCore<Datum, CrosshairCo
11
11
  config: CrosshairConfigInterface<Datum>;
12
12
  container: Selection<SVGSVGElement, any, SVGSVGElement, any>;
13
13
  line: Selection<SVGLineElement, any, SVGElement, any>;
14
- x: number;
15
- datum: Datum;
16
- datumIndex: number;
17
- show: boolean;
14
+ private _xPx;
15
+ private _yPx;
16
+ private _mouseEvent;
18
17
  private _animFrameId;
19
- private _currentXData;
20
- private _syncListener;
21
- private _isSyncActive;
22
18
  /** Tooltip component to be used by Crosshair if not provided by the config.
23
19
  * This property is supposed to be set externally by a container component like XYContainer. */
24
20
  tooltip: Tooltip;
@@ -30,12 +26,10 @@ export declare class Crosshair<Datum> extends XYComponentCore<Datum, CrosshairCo
30
26
  setContainer(containerSvg: Selection<SVGSVGElement, unknown, SVGSVGElement, unknown>): void;
31
27
  _render(customDuration?: number): void;
32
28
  hide(): void;
33
- destroy(): void;
34
29
  _onMouseMove(event: MouseEvent): void;
35
30
  _onMouseOut(): void;
36
- _showTooltip(event?: MouseEvent): void;
31
+ _showTooltip(datum: Datum, xValue: number, pos: [number, number], nearestDatumIndex?: number): void;
37
32
  _hideTooltip(): void;
38
33
  getYDataExtent(): number[];
39
34
  private getCircleData;
40
- _updateFromSync(xData: number | Date | undefined): void;
41
35
  }
@@ -1,36 +1,25 @@
1
1
  import { pointer } from 'd3-selection';
2
2
  import { easeLinear } from 'd3-ease';
3
3
  import { XYComponentCore } from '../../core/xy-component/index.js';
4
- import { isArray, isNumber, getNearest, getNumber, clamp, isFunction, getStackedValues } from '../../utils/data.js';
4
+ import { isArray, isNumber, getNearest, clamp, getNumber, isFunction, getStackedValues } from '../../utils/data.js';
5
5
  import { smartTransition } from '../../utils/d3.js';
6
6
  import { getColor } from '../../utils/color.js';
7
7
  import { Position } from '../../types/position.js';
8
+ import { FindNearestDirection } from '../../types/data.js';
8
9
  import { CrosshairDefaultConfig } from './config.js';
9
10
  import * as style from './style.js';
10
11
  import { line, circle } from './style.js';
11
- import { crosshairSyncBus } from './sync-bus.js';
12
12
 
13
- function isInDomain(x, xDomain) {
14
- if (!Array.isArray(xDomain) || xDomain.length < 2)
15
- return false;
16
- // Convert everything to numbers for comparison
17
- const xVal = typeof x === 'number' ? x : (x instanceof Date ? x.getTime() : new Date(x).getTime());
18
- const d0 = +xDomain[0];
19
- const d1 = +xDomain[xDomain.length - 1];
20
- return xVal >= d0 && xVal <= d1;
21
- }
22
13
  class Crosshair extends XYComponentCore {
23
14
  constructor(config) {
24
15
  super();
25
16
  this.clippable = true; // Don't apply clipping path to this component. See XYContainer
26
17
  this._defaultConfig = CrosshairDefaultConfig;
27
18
  this.config = this._defaultConfig;
28
- this.x = 0;
29
- this.show = false;
19
+ this._xPx = undefined;
20
+ this._yPx = undefined;
21
+ this._mouseEvent = undefined;
30
22
  this._animFrameId = null;
31
- this._currentXData = undefined;
32
- this._syncListener = null;
33
- this._isSyncActive = false; // true if this chart is the one broadcasting
34
23
  /** Accessors passed externally (e.g. from XYContainer) */
35
24
  this._accessors = {
36
25
  x: undefined,
@@ -39,8 +28,8 @@ class Crosshair extends XYComponentCore {
39
28
  baseline: undefined,
40
29
  };
41
30
  if (config)
42
- this.config = Object.assign(Object.assign({}, this.config), config);
43
- this.g.style('opacity', this.show ? 1 : 0);
31
+ this.setConfig(config);
32
+ this.g.style('opacity', 0);
44
33
  this.line = this.g.append('line')
45
34
  .attr('class', line);
46
35
  }
@@ -48,45 +37,100 @@ class Crosshair extends XYComponentCore {
48
37
  get accessors() {
49
38
  var _a;
50
39
  const { config } = this;
51
- // If x or y are explicitly set in config, use those; otherwise use container accessors
52
- const x = config.x || this._accessors.x;
53
- const yAcc = config.y || this._accessors.y;
40
+ const hasConfig = !!(config.x || config.y || config.yStacked);
41
+ const x = hasConfig ? config.x : this._accessors.x;
42
+ const yAcc = hasConfig ? config.y : this._accessors.y;
54
43
  const y = yAcc ? (isArray(yAcc) ? yAcc : [yAcc]) : undefined;
55
- const yStacked = config.yStacked || this._accessors.yStacked;
44
+ const yStacked = hasConfig ? config.yStacked : this._accessors.yStacked;
56
45
  const baseline = (_a = config.baseline) !== null && _a !== void 0 ? _a : this._accessors.baseline;
57
46
  return { x, y, yStacked, baseline };
58
47
  }
59
48
  setContainer(containerSvg) {
49
+ // Set up mousemove event for Crosshair
60
50
  this.container = containerSvg;
61
51
  this.container.on('mousemove.crosshair', this._onMouseMove.bind(this));
62
52
  this.container.on('mouseout.crosshair', this._onMouseOut.bind(this));
63
- this._render();
64
- // --- SYNC: subscribe if needed ---
65
- if (this.config.syncId) {
66
- this._syncListener = (x) => {
67
- if (!this._isSyncActive) {
68
- this._updateFromSync(x);
69
- window.requestAnimationFrame(() => this._render());
70
- }
71
- };
72
- crosshairSyncBus.subscribe(this.config.syncId, this._syncListener);
73
- }
74
53
  }
75
54
  _render(customDuration) {
76
- const { config } = this;
55
+ var _a, _b, _c, _d;
56
+ const { config, datamodel } = this;
77
57
  const duration = isNumber(customDuration) ? customDuration : config.duration;
78
- // --- SYNC: update from sync bus if not active ---
79
- if (config.syncId && !this._isSyncActive) {
80
- // _updateFromSync is called by the bus
58
+ const isForceShowAtDefined = config.forceShowAt !== undefined;
59
+ const xPx = isForceShowAtDefined ? this.xScale(config.forceShowAt) : this._xPx;
60
+ const xValue = this.xScale.invert(xPx);
61
+ let datum;
62
+ let datumIndex;
63
+ let nearestDatumIndex;
64
+ // Always calculate the nearest datum index for template and getCircles functions
65
+ if (((_a = datamodel.data) === null || _a === void 0 ? void 0 : _a.length) && this.accessors.x) {
66
+ const nearestDatum = getNearest(datamodel.data, xValue, this.accessors.x, FindNearestDirection.Left);
67
+ nearestDatumIndex = datamodel.data.indexOf(nearestDatum);
68
+ }
69
+ if (config.snapToData) {
70
+ if (!this.accessors.y && !this.accessors.yStacked && ((_b = datamodel.data) === null || _b === void 0 ? void 0 : _b.length)) {
71
+ console.warn('Unovis | Crosshair: Y accessors have not been configured. Please check if they\'re present in the configuration object');
72
+ }
73
+ datum = getNearest(datamodel.data, xValue, this.accessors.x);
74
+ datumIndex = datamodel.data.indexOf(datum);
75
+ if (!datum)
76
+ return;
81
77
  }
82
- smartTransition(this.g, duration).style('opacity', this.show ? 1 : 0);
78
+ const xRange = this.xScale.range();
79
+ const yRange = this.yScale.range();
80
+ const xClamped = config.snapToData
81
+ ? clamp(Math.round(this.xScale(getNumber(datum, this.accessors.x, datumIndex))), 0, this._width)
82
+ : clamp(xPx, xRange[0], xRange[1]);
83
+ const isCrosshairWithinXRange = (xPx >= xRange[0]) && (xPx <= xRange[1]);
84
+ const isCrosshairWithinYRange = (this._yPx >= yRange[1]) && (this._yPx <= yRange[0]);
85
+ let shouldShow = this._xPx ? isCrosshairWithinXRange && isCrosshairWithinYRange : isCrosshairWithinXRange;
86
+ // If the crosshair is far from the mouse pointer (usually when `snapToData` is `true` and data resolution is low), hide it
87
+ if (config.hideWhenFarFromPointer && ((Math.abs(xClamped - (+xPx)) >= config.hideWhenFarFromPointerDistance))) {
88
+ shouldShow = false;
89
+ }
90
+ const tooltip = (_c = config.tooltip) !== null && _c !== void 0 ? _c : this.tooltip;
91
+ if (shouldShow && tooltip) {
92
+ const container = tooltip.getContainer() || this.container.node();
93
+ const isContainerBody = tooltip.isContainerBody();
94
+ if (isForceShowAtDefined) {
95
+ // Convert SVG coordinates to screen coordinates
96
+ const containerRect = this.container.node().getBoundingClientRect();
97
+ // Use the actual left margin from the container
98
+ const screenX = (isContainerBody ? xPx + containerRect.left : xPx) + this._containerMargin.left;
99
+ const screenY = this._height / 2 + (isContainerBody ? containerRect.top : 0);
100
+ const pos = [screenX, screenY];
101
+ this._showTooltip(datum, xValue, pos, nearestDatumIndex);
102
+ }
103
+ else if (this._mouseEvent) {
104
+ const pos = (isContainerBody ? [this._mouseEvent.clientX, this._mouseEvent.clientY] : pointer(this._mouseEvent, container));
105
+ this._showTooltip(datum, xValue, pos, nearestDatumIndex);
106
+ }
107
+ }
108
+ else
109
+ this._hideTooltip();
110
+ // Trigger `onCrosshairMove` if the render was triggered by a mouse move event
111
+ if (this._mouseEvent) {
112
+ (_d = config.onCrosshairMove) === null || _d === void 0 ? void 0 : _d.call(config, shouldShow ? this.xScale.invert(this._xPx) : undefined, datum, datumIndex);
113
+ this._mouseEvent = undefined;
114
+ }
115
+ // When `config.forceShowAt` becomes undefined, the component will "jump" to `this._xPx` which is set to the last mouse position.
116
+ // This looks off, so we set `this._xPx` to `xPx` to make it look like the crosshair is rendered at the forced position
117
+ if (isForceShowAtDefined) {
118
+ this._xPx = undefined;
119
+ }
120
+ smartTransition(this.g, duration)
121
+ .style('opacity', shouldShow ? 1 : 0);
83
122
  this.line
84
123
  .attr('y1', 0)
85
- .attr('y2', this._height);
124
+ .attr('y1', this._height);
125
+ // Don't render the crosshair if the xPx is not finite
126
+ if (!isFinite(xClamped))
127
+ return;
86
128
  smartTransition(this.line, duration, easeLinear)
87
- .attr('x1', this.x)
88
- .attr('x2', this.x);
89
- const circleData = this.getCircleData();
129
+ .attr('x1', xClamped)
130
+ .attr('x2', xClamped);
131
+ const circleData = isFunction(config.getCircles)
132
+ ? config.getCircles(xValue, datamodel.data, this.yScale, nearestDatumIndex)
133
+ : this.getCircleData(datum, datumIndex);
90
134
  const circles = this.g
91
135
  .selectAll('circle')
92
136
  .data(circleData, (d, i) => { var _a; return (_a = d.id) !== null && _a !== void 0 ? _a : i; });
@@ -94,13 +138,13 @@ class Crosshair extends XYComponentCore {
94
138
  .append('circle')
95
139
  .attr('class', circle)
96
140
  .attr('r', 0)
97
- .attr('cx', this.x)
141
+ .attr('cx', xClamped)
98
142
  .attr('cy', d => d.y)
99
143
  .style('fill', d => d.color)
100
144
  .style('stroke', d => d.strokeColor)
101
145
  .style('stroke-width', d => d.strokeWidth);
102
146
  smartTransition(circlesEnter.merge(circles), duration, easeLinear)
103
- .attr('cx', this.x)
147
+ .attr('cx', xClamped)
104
148
  .attr('cy', d => d.y)
105
149
  .attr('r', 4)
106
150
  .style('opacity', d => d.opacity)
@@ -112,118 +156,35 @@ class Crosshair extends XYComponentCore {
112
156
  hide() {
113
157
  this._onMouseOut();
114
158
  }
115
- destroy() {
116
- if (this._animFrameId) {
117
- window.cancelAnimationFrame(this._animFrameId);
118
- this._animFrameId = null;
119
- }
120
- this._hideTooltip();
121
- // --- SYNC: unsubscribe if needed ---
122
- if (this.config.syncId && this._syncListener) {
123
- crosshairSyncBus.unsubscribe(this.config.syncId, this._syncListener);
124
- this._syncListener = null;
125
- }
126
- }
127
159
  _onMouseMove(event) {
128
- var _a, _b;
129
- const { config, datamodel, element } = this;
130
- // Set sync active state
131
- this._isSyncActive = !!config.syncId;
132
- // Check if we have the necessary accessors
160
+ var _a;
161
+ const { datamodel, element } = this;
133
162
  if (!this.accessors.x && ((_a = datamodel.data) === null || _a === void 0 ? void 0 : _a.length)) {
134
163
  console.warn('Unovis | Crosshair: X accessor function has not been configured. Please check if it\'s present in the configuration object');
135
- return;
136
- }
137
- const [x] = pointer(event, element);
138
- const xRange = this.xScale.range();
139
- if (config.snapToData) {
140
- if (!this.accessors.y && !this.accessors.yStacked && ((_b = datamodel.data) === null || _b === void 0 ? void 0 : _b.length)) {
141
- console.warn('Unovis | Crosshair: Y accessors have not been configured. Please check if they\'re present in the configuration object');
142
- return;
143
- }
144
- const scaleX = this.xScale;
145
- const valueX = scaleX.invert(x);
146
- this.datum = getNearest(datamodel.data, valueX, this.accessors.x);
147
- this.datumIndex = datamodel.data.indexOf(this.datum);
148
- if (!this.datum)
149
- return;
150
- const dataX = getNumber(this.datum, this.accessors.x, this.datumIndex);
151
- this.x = clamp(Math.round(scaleX(dataX)), 0, this._width);
152
- this._currentXData = dataX;
153
- // Show the crosshair only if it's in the chart range and not far from mouse pointer (if configured)
154
- this.show = (this.x >= 0) && (this.x <= this._width) && (!config.hideWhenFarFromPointer || (Math.abs(this.x - x) < config.hideWhenFarFromPointerDistance));
155
- // Emit to sync bus if configured
156
- if (config.syncId) {
157
- const xDomain = this.xScale.domain();
158
- if (isInDomain(dataX, xDomain)) {
159
- crosshairSyncBus.emit(config.syncId, dataX);
160
- }
161
- else {
162
- crosshairSyncBus.emit(config.syncId, undefined);
163
- }
164
- }
165
- }
166
- else {
167
- const tolerance = 2; // Show the crosshair when it is at least 2 pixels close to the chart area
168
- this.x = clamp(x, xRange[0], xRange[1]);
169
- this._currentXData = this.xScale.invert(this.x);
170
- this.show = (x >= (xRange[0] - tolerance)) && (x <= (xRange[1] + tolerance));
171
164
  }
165
+ const [x, y] = pointer(event, element);
166
+ this._xPx = x;
167
+ this._yPx = y;
168
+ this._mouseEvent = event;
172
169
  window.cancelAnimationFrame(this._animFrameId);
173
170
  this._animFrameId = window.requestAnimationFrame(() => {
174
171
  this._render();
175
172
  });
176
- if (this.show)
177
- this._showTooltip(event);
178
- else
179
- this._hideTooltip();
180
173
  }
181
174
  _onMouseOut() {
182
- const { config } = this;
183
- // If this chart is the sync broadcaster, emit undefined to the sync bus
184
- if (config.syncId && this._isSyncActive) {
185
- crosshairSyncBus.emit(config.syncId, undefined);
186
- }
187
- this._isSyncActive = false;
188
- // Hide crosshair and tooltip
189
- this.show = false;
190
- this._hideTooltip();
191
175
  window.cancelAnimationFrame(this._animFrameId);
192
176
  this._animFrameId = window.requestAnimationFrame(() => {
193
177
  this._render();
194
178
  });
195
179
  }
196
- _showTooltip(event) {
180
+ _showTooltip(datum, xValue, pos, nearestDatumIndex) {
197
181
  var _a;
198
- const { config } = this;
182
+ const { config, datamodel } = this;
199
183
  const tooltip = (_a = config.tooltip) !== null && _a !== void 0 ? _a : this.tooltip;
200
- if (!tooltip) {
201
- return;
202
- }
203
- // For synchronized charts, we need to find the datum if it doesn't exist
204
- let datum = this.datum;
205
- if (!datum && config.syncId && !this._isSyncActive && this._currentXData !== undefined) {
206
- // Find the datum at the current x position (only for number values)
207
- if (typeof this._currentXData === 'number') {
208
- datum = getNearest(this.datamodel.data, this._currentXData, this.accessors.x);
209
- }
210
- }
211
- if (!datum) {
184
+ if (!tooltip || !pos)
212
185
  return;
213
- }
214
- let x, y;
215
- if (event) {
216
- // Use actual mouse event
217
- const container = tooltip.getContainer() || this.container.node();
218
- [x, y] = tooltip.isContainerBody() ? [event.clientX, event.clientY] : pointer(event, container);
219
- }
220
- else {
221
- // Use synthetic event for sync
222
- const containerRect = this.container.node().getBoundingClientRect();
223
- x = tooltip.isContainerBody() ? containerRect.left + this.x : this.x;
224
- y = tooltip.isContainerBody() ? containerRect.top + this._height / 2 : this._height / 2;
225
- }
226
- const content = config.template(datum, this._currentXData || this.xScale.invert(this.x));
186
+ const [x, y] = pos;
187
+ const content = config.template(datum, xValue, nearestDatumIndex, datamodel.data);
227
188
  // Force set `followCursor` to `true` because we don't want Crosshair's tooltip to be hoverable
228
189
  tooltip.config.followCursor = true;
229
190
  // Set tooltip placement based on Crosshair's position (left / right)
@@ -244,43 +205,26 @@ class Crosshair extends XYComponentCore {
244
205
  getYDataExtent() {
245
206
  return [undefined, undefined];
246
207
  }
247
- getCircleData() {
208
+ getCircleData(datum, datumIndex) {
248
209
  var _a, _b;
249
- const { config, datamodel: { data } } = this;
250
- if (isFunction(config.getCircles))
251
- return config.getCircles(this._currentXData || this.xScale.invert(this.x), data, this.yScale);
252
- // Get the datum - either from active mode or from synchronized mode
253
- let datum = this.datum;
254
- let datumIndex = this.datumIndex;
255
- if (!datum && config.syncId && !this._isSyncActive && this._currentXData !== undefined) {
256
- // Find the datum at the current x position (only for number values)
257
- if (typeof this._currentXData === 'number') {
258
- datum = getNearest(this.datamodel.data, this._currentXData, this.accessors.x);
259
- datumIndex = this.datamodel.data.indexOf(datum);
260
- }
261
- }
210
+ const { config } = this;
262
211
  if (config.snapToData && datum) {
263
212
  const yAccessors = (_a = this.accessors.y) !== null && _a !== void 0 ? _a : [];
264
213
  const yStackedAccessors = (_b = this.accessors.yStacked) !== null && _b !== void 0 ? _b : [];
265
214
  const baselineValue = getNumber(datum, this.accessors.baseline, datumIndex) || 0;
266
215
  const stackedValues = getStackedValues(datum, datumIndex, ...yStackedAccessors)
267
- .map((value, index) => {
268
- const yValue = value + baselineValue;
269
- const yPixel = this.yScale(yValue);
270
- return {
271
- y: yPixel,
272
- opacity: 1,
273
- color: getColor(datum, config.color, index),
274
- strokeColor: config.strokeColor ? getColor(datum, config.strokeColor, index) : undefined,
275
- strokeWidth: config.strokeWidth ? getNumber(datum, config.strokeWidth, index) : undefined,
276
- };
277
- });
216
+ .map((value, index) => ({
217
+ y: this.yScale(value + baselineValue),
218
+ opacity: isNumber(getNumber(datum, yStackedAccessors[index], index)) ? 1 : 0,
219
+ color: getColor(datum, config.color, index),
220
+ strokeColor: config.strokeColor ? getColor(datum, config.strokeColor, index) : undefined,
221
+ strokeWidth: config.strokeWidth ? getNumber(datum, config.strokeWidth, index) : undefined,
222
+ }));
278
223
  const regularValues = yAccessors
279
224
  .map((a, index) => {
280
- const value = getNumber(datum, a);
281
- const yPixel = isNumber(value) ? this.yScale(value) : 0;
225
+ const value = getNumber(datum, a, datumIndex);
282
226
  return {
283
- y: yPixel,
227
+ y: this.yScale(value),
284
228
  opacity: isNumber(value) ? 1 : 0,
285
229
  color: getColor(datum, config.color, stackedValues.length + index),
286
230
  strokeColor: config.strokeColor ? getColor(datum, config.strokeColor, index) : undefined,
@@ -289,58 +233,8 @@ class Crosshair extends XYComponentCore {
289
233
  });
290
234
  return stackedValues.concat(regularValues);
291
235
  }
292
- // Return empty array if no data or no datum - crosshair line will still show
293
236
  return [];
294
237
  }
295
- _updateFromSync(xData) {
296
- var _a;
297
- const { config, datamodel } = this;
298
- if (!config.syncId || this._isSyncActive) {
299
- return;
300
- }
301
- // Handle mouse out signal
302
- if (xData === undefined) {
303
- this.show = false;
304
- this._hideTooltip();
305
- return;
306
- }
307
- if (!this.accessors.x || !((_a = datamodel.data) === null || _a === void 0 ? void 0 : _a.length)) {
308
- return;
309
- }
310
- // --- DOMAIN CHECK (on passive chart, use xData directly) ---
311
- const xDomain = this.xScale.domain();
312
- if (!isInDomain(xData, xDomain)) {
313
- this.show = false;
314
- this._hideTooltip();
315
- return;
316
- }
317
- // --- END DOMAIN CHECK ---
318
- // Find the datum at this x position
319
- if (typeof xData === 'number') {
320
- this.datum = getNearest(datamodel.data, xData, this.accessors.x);
321
- }
322
- else if (xData instanceof Date || typeof xData === 'string') {
323
- const xVal = xData instanceof Date ? xData.getTime() : new Date(xData).getTime();
324
- this.datum = getNearest(datamodel.data, xVal, this.accessors.x);
325
- }
326
- else {
327
- this.datum = undefined;
328
- }
329
- this.datumIndex = datamodel.data.indexOf(this.datum);
330
- // If no datum found, hide the crosshair
331
- if (!this.datum) {
332
- this.show = false;
333
- this._hideTooltip();
334
- return;
335
- }
336
- const dataX = getNumber(this.datum, this.accessors.x, this.datumIndex);
337
- this.x = clamp(Math.round(this.xScale(dataX)), 0, this._width);
338
- this._currentXData = dataX;
339
- // Show the crosshair
340
- this.show = true;
341
- // Show tooltip for synchronized chart
342
- this._showTooltip();
343
- }
344
238
  }
345
239
  Crosshair.selectors = style;
346
240