@unovis/ts 1.6.0-bigip.1 → 1.6.0-bigip.3

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.
@@ -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 `data` and `leftNearestDatumIndex` will be provided.
26
+ * Default: `d => ''` */
27
+ template?: (datum: Datum, x: number | Date, data: Datum[], leftNearestDatumIndex: number) => 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,17 +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
- * It has to return an array of the CrosshairCircle objects: `{ y: number; color: string; opacity?: number }[]`.
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.
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
- /** X position in data space (not pixels) for synchronized crosshair. Default: `undefined` */
44
- xPosition?: number | Date;
45
- /** Force show the crosshair even when mouse is not over the chart. Default: `false` */
46
- forceShow?: boolean;
47
- /** Callback function called when crosshair position changes. Receives x position in data space. Default: `undefined` */
48
- onCrosshairMove?: (x: number | Date | undefined) => void;
44
+ getCircles?: (x: number | Date, data: Datum[], yScale: ContinuousScale, leftNearestDatumIndex: 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;
49
49
  }
50
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, xPosition: undefined, forceShow: false, onCrosshairMove: undefined });
3
+ const CrosshairDefaultConfig = Object.assign(Object.assign({}, XYComponentDefaultConfig), { yStacked: undefined, baseline: null, duration: 100, tooltip: undefined, template: (d, x, data, leftNearestDatumIndex) => '', 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 /** X position in data space (not pixels) for synchronized crosshair. Default: `undefined` */\n xPosition?: number | Date;\n /** Force show the crosshair even when mouse is not over the chart. Default: `false` */\n forceShow?: boolean;\n /** Callback function called when crosshair position changes. Receives x position in data space. Default: `undefined` */\n onCrosshairMove?: (x: number | Date | undefined) => void;\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 xPosition: undefined,\n forceShow: false,\n onCrosshairMove: 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,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,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,KAAK,EAChB,eAAe,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 // TODO: Change `datum` type to `Datum | undefined`. This may break the build for many people, so we might want to do it in version 2.0\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 `data` and `leftNearestDatumIndex` will be provided.\n * Default: `d => ''` */\n template?: (datum: Datum, x: number | Date, data: Datum[], leftNearestDatumIndex: number) => 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, leftNearestDatumIndex: 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, x: number | Date, data: Datum[], leftNearestDatumIndex: number): 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":";;AAuDO,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,CAAQ,EAAE,CAAgB,EAAE,IAAa,EAAE,qBAA6B,KAAa,EAAE,EACzG,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,13 +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 _dataX;
20
- private _isMouseOver;
21
18
  /** Tooltip component to be used by Crosshair if not provided by the config.
22
19
  * This property is supposed to be set externally by a container component like XYContainer. */
23
20
  tooltip: Tooltip;
@@ -27,17 +24,12 @@ export declare class Crosshair<Datum> extends XYComponentCore<Datum, CrosshairCo
27
24
  get accessors(): CrosshairAccessors<Datum>;
28
25
  constructor(config?: CrosshairConfigInterface<Datum>);
29
26
  setContainer(containerSvg: Selection<SVGSVGElement, unknown, SVGSVGElement, unknown>): void;
30
- setConfig(config: CrosshairConfigInterface<Datum>): void;
31
27
  _render(customDuration?: number): void;
32
28
  hide(): void;
33
- destroy(): void;
34
29
  _onMouseMove(event: MouseEvent): void;
35
- private _processMouseMove;
36
30
  _onMouseOut(): void;
37
- _showTooltip(event: MouseEvent): void;
31
+ _showTooltip(datum: Datum, xValue: number, pos: [number, number], nearestDatumIndex: number | undefined): void;
38
32
  _hideTooltip(): void;
39
33
  getYDataExtent(): number[];
40
34
  private getCircleData;
41
- _updateFromSync(xData: number | Date | undefined): void;
42
- private _getSyncState;
43
35
  }
@@ -1,10 +1,11 @@
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';
@@ -15,11 +16,10 @@ class Crosshair extends XYComponentCore {
15
16
  this.clippable = true; // Don't apply clipping path to this component. See XYContainer
16
17
  this._defaultConfig = CrosshairDefaultConfig;
17
18
  this.config = this._defaultConfig;
18
- this.x = 0;
19
- this.show = false;
19
+ this._xPx = undefined;
20
+ this._yPx = undefined;
21
+ this._mouseEvent = undefined;
20
22
  this._animFrameId = null;
21
- this._dataX = undefined;
22
- this._isMouseOver = false;
23
23
  /** Accessors passed externally (e.g. from XYContainer) */
24
24
  this._accessors = {
25
25
  x: undefined,
@@ -28,8 +28,8 @@ class Crosshair extends XYComponentCore {
28
28
  baseline: undefined,
29
29
  };
30
30
  if (config)
31
- this.config = Object.assign(Object.assign({}, this.config), config);
32
- this.g.style('opacity', this.show ? 1 : 0);
31
+ this.setConfig(config);
32
+ this.g.style('opacity', 0);
33
33
  this.line = this.g.append('line')
34
34
  .attr('class', line);
35
35
  }
@@ -37,44 +37,96 @@ class Crosshair extends XYComponentCore {
37
37
  get accessors() {
38
38
  var _a;
39
39
  const { config } = this;
40
- // If x or y are explicitly set in config, use those; otherwise use container accessors
41
- const x = config.x || this._accessors.x;
42
- 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;
43
43
  const y = yAcc ? (isArray(yAcc) ? yAcc : [yAcc]) : undefined;
44
- const yStacked = config.yStacked || this._accessors.yStacked;
44
+ const yStacked = hasConfig ? config.yStacked : this._accessors.yStacked;
45
45
  const baseline = (_a = config.baseline) !== null && _a !== void 0 ? _a : this._accessors.baseline;
46
46
  return { x, y, yStacked, baseline };
47
47
  }
48
48
  setContainer(containerSvg) {
49
+ // Set up mousemove event for Crosshair
49
50
  this.container = containerSvg;
50
51
  this.container.on('mousemove.crosshair', this._onMouseMove.bind(this));
51
52
  this.container.on('mouseout.crosshair', this._onMouseOut.bind(this));
52
- this._render();
53
- }
54
- setConfig(config) {
55
- super.setConfig(config);
56
- // For synchronized charts, update from sync when config changes
57
- const { isActive, enableSync } = this._getSyncState();
58
- if (enableSync && !isActive && (isNumber(config.xPosition) || config.xPosition instanceof Date)) {
59
- this._updateFromSync(config.xPosition);
60
- }
61
53
  }
62
54
  _render(customDuration) {
63
- const { config } = this;
55
+ var _a, _b, _c, _d;
56
+ const { config, datamodel } = this;
64
57
  const duration = isNumber(customDuration) ? customDuration : config.duration;
65
- const { isActive, enableSync } = this._getSyncState();
66
- // Handle synchronized charts with xPosition prop
67
- if (enableSync && !isActive && (isNumber(config.xPosition) || config.xPosition instanceof Date)) {
68
- this._updateFromSync(config.xPosition);
58
+ const isForceShowAtDefined = config.forceShowAt !== undefined;
59
+ const xPx = isForceShowAtDefined ? this.xScale(config.forceShowAt) : this._xPx;
60
+ const xValue = this.xScale.invert(xPx);
61
+ const leftNearestDatumIndex = (((_a = datamodel.data) === null || _a === void 0 ? void 0 : _a.length) && this.accessors.x)
62
+ ? datamodel.data.indexOf(getNearest(datamodel.data, xValue, this.accessors.x, FindNearestDirection.Left)) : undefined;
63
+ let nearestDatum;
64
+ let nearestDatumIndex;
65
+ if (config.snapToData) {
66
+ if (!this.accessors.y && !this.accessors.yStacked && ((_b = datamodel.data) === null || _b === void 0 ? void 0 : _b.length)) {
67
+ console.warn('Unovis | Crosshair: Y accessors have not been configured. Please check if they\'re present in the configuration object');
68
+ }
69
+ nearestDatum = getNearest(datamodel.data, xValue, this.accessors.x);
70
+ nearestDatumIndex = datamodel.data.indexOf(nearestDatum);
71
+ if (!nearestDatum)
72
+ return;
73
+ }
74
+ const xRange = this.xScale.range();
75
+ const yRange = this.yScale.range();
76
+ const xClamped = config.snapToData
77
+ ? clamp(Math.round(this.xScale(getNumber(nearestDatum, this.accessors.x, nearestDatumIndex))), 0, this._width)
78
+ : clamp(xPx, xRange[0], xRange[1]);
79
+ const isCrosshairWithinXRange = (xPx >= xRange[0]) && (xPx <= xRange[1]);
80
+ const isCrosshairWithinYRange = (this._yPx >= yRange[1]) && (this._yPx <= yRange[0]);
81
+ let shouldShow = this._xPx ? isCrosshairWithinXRange && isCrosshairWithinYRange : isCrosshairWithinXRange;
82
+ // If the crosshair is far from the mouse pointer (usually when `snapToData` is `true` and data resolution is low), hide it
83
+ if (config.hideWhenFarFromPointer && ((Math.abs(xClamped - (+xPx)) >= config.hideWhenFarFromPointerDistance))) {
84
+ shouldShow = false;
85
+ }
86
+ const tooltip = (_c = config.tooltip) !== null && _c !== void 0 ? _c : this.tooltip;
87
+ if (shouldShow && tooltip) {
88
+ const container = tooltip.getContainer() || this.container.node();
89
+ const isContainerBody = tooltip.isContainerBody();
90
+ if (isForceShowAtDefined) {
91
+ // Convert SVG coordinates to screen coordinates
92
+ const containerRect = this.container.node().getBoundingClientRect();
93
+ // Use the actual left margin from the container
94
+ const screenX = (isContainerBody ? xPx + containerRect.left : xPx) + this._containerMargin.left;
95
+ const screenY = this._height / 2 + (isContainerBody ? containerRect.top : 0);
96
+ const pos = [screenX, screenY];
97
+ this._showTooltip(nearestDatum, xValue, pos, leftNearestDatumIndex);
98
+ }
99
+ else if (this._mouseEvent) {
100
+ const pos = (isContainerBody ? [this._mouseEvent.clientX, this._mouseEvent.clientY] : pointer(this._mouseEvent, container));
101
+ this._showTooltip(nearestDatum, xValue, pos, leftNearestDatumIndex);
102
+ }
69
103
  }
70
- smartTransition(this.g, duration).style('opacity', this.show ? 1 : 0);
104
+ else
105
+ this._hideTooltip();
106
+ // Trigger `onCrosshairMove` if the render was triggered by a mouse move event
107
+ if (this._mouseEvent) {
108
+ (_d = config.onCrosshairMove) === null || _d === void 0 ? void 0 : _d.call(config, shouldShow ? this.xScale.invert(this._xPx) : undefined, nearestDatum, nearestDatumIndex);
109
+ this._mouseEvent = undefined;
110
+ }
111
+ // When `config.forceShowAt` becomes undefined, the component will "jump" to `this._xPx` which is set to the last mouse position.
112
+ // This looks off, so we set `this._xPx` to `xPx` to make it look like the crosshair is rendered at the forced position
113
+ if (isForceShowAtDefined) {
114
+ this._xPx = undefined;
115
+ }
116
+ smartTransition(this.g, duration)
117
+ .style('opacity', shouldShow ? 1 : 0);
71
118
  this.line
72
119
  .attr('y1', 0)
73
- .attr('y2', this._height);
120
+ .attr('y1', this._height);
121
+ // Don't render the crosshair if the xPx is not finite
122
+ if (!isFinite(xClamped))
123
+ return;
74
124
  smartTransition(this.line, duration, easeLinear)
75
- .attr('x1', this.x)
76
- .attr('x2', this.x);
77
- const circleData = this.getCircleData();
125
+ .attr('x1', xClamped)
126
+ .attr('x2', xClamped);
127
+ const circleData = isFunction(config.getCircles)
128
+ ? config.getCircles(xValue, datamodel.data, this.yScale, leftNearestDatumIndex)
129
+ : this.getCircleData(nearestDatum, nearestDatumIndex);
78
130
  const circles = this.g
79
131
  .selectAll('circle')
80
132
  .data(circleData, (d, i) => { var _a; return (_a = d.id) !== null && _a !== void 0 ? _a : i; });
@@ -82,13 +134,13 @@ class Crosshair extends XYComponentCore {
82
134
  .append('circle')
83
135
  .attr('class', circle)
84
136
  .attr('r', 0)
85
- .attr('cx', this.x)
137
+ .attr('cx', xClamped)
86
138
  .attr('cy', d => d.y)
87
139
  .style('fill', d => d.color)
88
140
  .style('stroke', d => d.strokeColor)
89
141
  .style('stroke-width', d => d.strokeWidth);
90
142
  smartTransition(circlesEnter.merge(circles), duration, easeLinear)
91
- .attr('cx', this.x)
143
+ .attr('cx', xClamped)
92
144
  .attr('cy', d => d.y)
93
145
  .attr('r', 4)
94
146
  .style('opacity', d => d.opacity)
@@ -100,108 +152,36 @@ class Crosshair extends XYComponentCore {
100
152
  hide() {
101
153
  this._onMouseOut();
102
154
  }
103
- destroy() {
104
- if (this._animFrameId) {
105
- window.cancelAnimationFrame(this._animFrameId);
106
- this._animFrameId = null;
107
- }
108
- this._hideTooltip();
109
- }
110
155
  _onMouseMove(event) {
111
- // Throttle the entire mouse move processing
112
- if (this._animFrameId)
113
- return;
114
- this._animFrameId = window.requestAnimationFrame(() => {
115
- this._processMouseMove(event);
116
- this._animFrameId = null;
117
- });
118
- }
119
- _processMouseMove(event) {
120
- var _a, _b;
121
- this._isMouseOver = true;
122
- const { config, datamodel, element } = this;
123
- const { isActive, enableSync } = this._getSyncState();
124
- // Early return for synchronized non-active charts, but allow tooltip display
125
- if (enableSync && !isActive) {
126
- if (this.show && this.datum) {
127
- this._showTooltip(event);
128
- }
129
- else if (this.show && this._dataX !== undefined && isNumber(this._dataX)) {
130
- // For synchronized charts, find the datum if it doesn't exist
131
- this.datum = getNearest(this.datamodel.data, this._dataX, this.accessors.x);
132
- if (this.datum) {
133
- this._showTooltip(event);
134
- }
135
- }
136
- return;
137
- }
138
- // Check if we have the necessary accessors
156
+ var _a;
157
+ const { datamodel, element } = this;
139
158
  if (!this.accessors.x && ((_a = datamodel.data) === null || _a === void 0 ? void 0 : _a.length)) {
140
159
  console.warn('Unovis | Crosshair: X accessor function has not been configured. Please check if it\'s present in the configuration object');
141
- return;
142
160
  }
143
- const [x] = pointer(event, element);
144
- const xRange = this.xScale.range();
145
- if (config.snapToData) {
146
- if (!this.accessors.y && !this.accessors.yStacked && ((_b = datamodel.data) === null || _b === void 0 ? void 0 : _b.length)) {
147
- console.warn('Unovis | Crosshair: Y accessors have not been configured. Please check if they\'re present in the configuration object');
148
- return;
149
- }
150
- const scaleX = this.xScale;
151
- const valueX = scaleX.invert(x);
152
- this.datum = getNearest(datamodel.data, valueX, this.accessors.x);
153
- this.datumIndex = datamodel.data.indexOf(this.datum);
154
- if (!this.datum)
155
- return;
156
- const dataX = getNumber(this.datum, this.accessors.x, this.datumIndex);
157
- this.x = clamp(Math.round(scaleX(dataX)), 0, this._width);
158
- this._dataX = dataX;
159
- // Show the crosshair only if it's in the chart range and not far from mouse pointer (if configured)
160
- this.show = (this.x >= 0) && (this.x <= this._width) && (!config.hideWhenFarFromPointer || (Math.abs(this.x - x) < config.hideWhenFarFromPointerDistance));
161
- }
162
- else {
163
- const tolerance = 2; // Show the crosshair when it is at least 2 pixels close to the chart area
164
- this.x = clamp(x, xRange[0], xRange[1]);
165
- this._dataX = this.xScale.invert(this.x);
166
- this.show = (x >= (xRange[0] - tolerance)) && (x <= (xRange[1] + tolerance));
167
- }
168
- // Call onCrosshairMove callback if provided
169
- if (this.show && config.onCrosshairMove && this._dataX !== undefined) {
170
- config.onCrosshairMove(this._dataX);
171
- }
172
- // Render immediately since we're already in a throttled context
173
- this._render();
174
- if (this.show)
175
- this._showTooltip(event);
176
- else
177
- this._hideTooltip();
161
+ const [x, y] = pointer(event, element);
162
+ this._xPx = x;
163
+ this._yPx = y;
164
+ this._mouseEvent = event;
165
+ window.cancelAnimationFrame(this._animFrameId);
166
+ this._animFrameId = window.requestAnimationFrame(() => {
167
+ this._render();
168
+ });
178
169
  }
179
170
  _onMouseOut() {
180
- this._isMouseOver = false;
181
- const { config } = this;
182
- // Always hide on mouse out
183
- this.show = false;
184
- this._hideTooltip();
185
- // Throttle rendering using requestAnimationFrame
186
- if (this._animFrameId)
187
- return;
171
+ window.cancelAnimationFrame(this._animFrameId);
188
172
  this._animFrameId = window.requestAnimationFrame(() => {
189
173
  this._render();
190
- this._animFrameId = null;
191
174
  });
192
- // If this is the active chart, notify parent to clear sync state
193
- if (config.onCrosshairMove) {
194
- config.onCrosshairMove(undefined);
195
- }
196
175
  }
197
- _showTooltip(event) {
176
+ _showTooltip(datum, xValue, pos, nearestDatumIndex) {
198
177
  var _a;
199
- const tooltip = (_a = this.config.tooltip) !== null && _a !== void 0 ? _a : this.tooltip;
200
- if (!tooltip || !this.datum)
178
+ const { config, datamodel } = this;
179
+ const tooltip = (_a = config.tooltip) !== null && _a !== void 0 ? _a : this.tooltip;
180
+ if (!tooltip || !pos)
201
181
  return;
202
- const container = tooltip.getContainer() || this.container.node();
203
- const [x, y] = tooltip.isContainerBody() ? [event.clientX, event.clientY] : pointer(event, container);
204
- const content = this.config.template(this.datum, this._dataX || this.xScale.invert(this.x));
182
+ const [x, y] = pos;
183
+ const content = config.template(datum, xValue, datamodel.data, nearestDatumIndex);
184
+ // Force set `followCursor` to `true` because we don't want Crosshair's tooltip to be hoverable
205
185
  tooltip.config.followCursor = true;
206
186
  // Set tooltip placement based on Crosshair's position (left / right)
207
187
  if (!tooltip.config.horizontalPlacement || tooltip.config.horizontalPlacement === Position.Auto) {
@@ -213,133 +193,44 @@ class Crosshair extends XYComponentCore {
213
193
  }
214
194
  _hideTooltip() {
215
195
  var _a;
216
- const tooltip = (_a = this.config.tooltip) !== null && _a !== void 0 ? _a : this.tooltip;
196
+ const { config } = this;
197
+ const tooltip = (_a = config.tooltip) !== null && _a !== void 0 ? _a : this.tooltip;
217
198
  tooltip === null || tooltip === void 0 ? void 0 : tooltip.hide();
218
199
  }
219
- // We don't want Crosshair to be taken into account in domain calculations
200
+ // We don't want Crosshair to be be taken in to account in domain calculations
220
201
  getYDataExtent() {
221
202
  return [undefined, undefined];
222
203
  }
223
- getCircleData() {
204
+ getCircleData(datum, datumIndex) {
224
205
  var _a, _b;
225
- const { datamodel: { data } } = this;
226
- const { isActive, enableSync } = this._getSyncState();
227
- if (isFunction(this.config.getCircles)) {
228
- return this.config.getCircles(this._dataX || this.xScale.invert(this.x), data, this.yScale);
229
- }
230
- // Get the datum - either from active mode or from synchronized mode
231
- let datum = this.datum;
232
- let datumIndex = this.datumIndex;
233
- if (!datum && enableSync && !isActive && this._dataX !== undefined) {
234
- if (isNumber(this._dataX)) {
235
- datum = getNearest(this.datamodel.data, this._dataX, this.accessors.x);
236
- datumIndex = this.datamodel.data.indexOf(datum);
237
- }
238
- }
239
- if (this.config.snapToData && datum) {
206
+ const { config } = this;
207
+ if (config.snapToData && datum) {
240
208
  const yAccessors = (_a = this.accessors.y) !== null && _a !== void 0 ? _a : [];
241
209
  const yStackedAccessors = (_b = this.accessors.yStacked) !== null && _b !== void 0 ? _b : [];
242
210
  const baselineValue = getNumber(datum, this.accessors.baseline, datumIndex) || 0;
243
211
  const stackedValues = getStackedValues(datum, datumIndex, ...yStackedAccessors)
244
- .map((value, index) => {
245
- const yValue = value + baselineValue;
246
- const yPixel = this.yScale(yValue);
247
- return {
248
- y: yPixel,
249
- opacity: isNumber(getNumber(datum, yStackedAccessors[index])) ? 1 : 1,
250
- color: getColor(datum, this.config.color, index),
251
- strokeColor: this.config.strokeColor ? getColor(datum, this.config.strokeColor, index) : undefined,
252
- strokeWidth: this.config.strokeWidth ? getNumber(datum, this.config.strokeWidth, index) : undefined,
253
- };
254
- });
212
+ .map((value, index) => ({
213
+ y: this.yScale(value + baselineValue),
214
+ opacity: isNumber(getNumber(datum, yStackedAccessors[index], index)) ? 1 : 0,
215
+ color: getColor(datum, config.color, index),
216
+ strokeColor: config.strokeColor ? getColor(datum, config.strokeColor, index) : undefined,
217
+ strokeWidth: config.strokeWidth ? getNumber(datum, config.strokeWidth, index) : undefined,
218
+ }));
255
219
  const regularValues = yAccessors
256
220
  .map((a, index) => {
257
- const value = getNumber(datum, a);
258
- const yPixel = isNumber(value) ? this.yScale(value) : 0;
221
+ const value = getNumber(datum, a, datumIndex);
259
222
  return {
260
- y: yPixel,
223
+ y: this.yScale(value),
261
224
  opacity: isNumber(value) ? 1 : 0,
262
- color: getColor(datum, this.config.color, stackedValues.length + index),
263
- strokeColor: this.config.strokeColor ? getColor(datum, this.config.strokeColor, index) : undefined,
264
- strokeWidth: this.config.strokeWidth ? getNumber(datum, this.config.strokeWidth, index) : undefined,
225
+ color: getColor(datum, config.color, stackedValues.length + index),
226
+ strokeColor: config.strokeColor ? getColor(datum, config.strokeColor, index) : undefined,
227
+ strokeWidth: config.strokeWidth ? getNumber(datum, config.strokeWidth, index) : undefined,
265
228
  };
266
229
  });
267
230
  return stackedValues.concat(regularValues);
268
231
  }
269
- // Return empty array if no data or no datum - crosshair line will still show
270
232
  return [];
271
233
  }
272
- _updateFromSync(xData) {
273
- var _a;
274
- const { isActive, enableSync } = this._getSyncState();
275
- if (!enableSync || isActive)
276
- return;
277
- // Handle mouse out signal
278
- if (xData === undefined) {
279
- this.show = false;
280
- this._hideTooltip();
281
- return;
282
- }
283
- if (!this.accessors.x || !((_a = this.datamodel.data) === null || _a === void 0 ? void 0 : _a.length))
284
- return;
285
- // Check if xPosition is within the chart's x domain
286
- const xDomain = this.xScale.domain();
287
- let xValue;
288
- if (typeof xData === 'number') {
289
- xValue = xData;
290
- }
291
- else if (xData instanceof Date) {
292
- xValue = xData.getTime();
293
- }
294
- else {
295
- this.show = false;
296
- this._hideTooltip();
297
- return;
298
- }
299
- // If xPosition is outside the chart's domain, hide the crosshair
300
- const domainMin = typeof xDomain[0] === 'number' ? xDomain[0] : (xDomain[0] instanceof Date ? xDomain[0].getTime() : 0);
301
- const domainMax = typeof xDomain[1] === 'number' ? xDomain[1] : (xDomain[1] instanceof Date ? xDomain[1].getTime() : 0);
302
- if (xValue < domainMin || xValue > domainMax) {
303
- this.show = false;
304
- this._hideTooltip();
305
- return;
306
- }
307
- // Find the datum at this x position
308
- if (typeof xData === 'number') {
309
- this.datum = getNearest(this.datamodel.data, xData, this.accessors.x);
310
- }
311
- else if (xData instanceof Date) {
312
- this.datum = getNearest(this.datamodel.data, xData.getTime(), this.accessors.x);
313
- }
314
- else {
315
- this.datum = undefined;
316
- }
317
- this.datumIndex = this.datamodel.data.indexOf(this.datum);
318
- // If no datum found, hide the crosshair
319
- if (!this.datum) {
320
- this.show = false;
321
- this._hideTooltip();
322
- return;
323
- }
324
- const dataX = getNumber(this.datum, this.accessors.x, this.datumIndex);
325
- this.x = clamp(Math.round(this.xScale(dataX)), 0, this._width);
326
- this._dataX = dataX;
327
- // Show the crosshair
328
- this.show = true;
329
- // Show tooltip for synchronized chart using the same logic as active charts
330
- const syntheticEvent = {
331
- clientX: this.container.node().getBoundingClientRect().left + this.x,
332
- clientY: this.container.node().getBoundingClientRect().top + this._height / 2,
333
- };
334
- this._showTooltip(syntheticEvent);
335
- }
336
- _getSyncState() {
337
- // A chart is active if it's being hovered and doesn't have external sync props
338
- const isActive = this._isMouseOver && !isNumber(this.config.xPosition) && !this.config.forceShow;
339
- // A chart is in sync mode if it has external sync props (regardless of mouse state)
340
- const enableSync = isNumber(this.config.xPosition) || !!this.config.forceShow;
341
- return { isActive, enableSync };
342
- }
343
234
  }
344
235
  Crosshair.selectors = style;
345
236