@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.
- package/components/area/index.js +2 -1
- package/components/area/index.js.map +1 -1
- package/components/crosshair/config.d.ts +12 -11
- package/components/crosshair/config.js +1 -1
- package/components/crosshair/config.js.map +1 -1
- package/components/crosshair/index.d.ts +4 -10
- package/components/crosshair/index.js +110 -216
- package/components/crosshair/index.js.map +1 -1
- package/components/timeline/index.js +3 -4
- package/components/timeline/index.js.map +1 -1
- package/containers/single-container/index.js +3 -1
- package/containers/single-container/index.js.map +1 -1
- package/containers/xy-container/index.js +3 -1
- package/containers/xy-container/index.js.map +1 -1
- package/core/component/index.d.ts +4 -0
- package/core/component/index.js +6 -0
- package/core/component/index.js.map +1 -1
- package/core/xy-component/index.d.ts +1 -0
- package/core/xy-component/index.js +3 -1
- package/core/xy-component/index.js.map +1 -1
- package/index.js +1 -0
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/styles/index.js +1 -0
- package/styles/index.js.map +1 -1
- package/types/data.d.ts +5 -0
- package/types/data.js +7 -0
- package/types/data.js.map +1 -1
- package/types/text.d.ts +1 -1
- package/types/text.js.map +1 -1
- package/types.js +1 -1
- package/utils/data.d.ts +3 -3
- package/utils/data.js +38 -8
- package/utils/data.js.map +1 -1
- package/utils/text.d.ts +1 -1
- package/utils/text.js +11 -7
- package/utils/text.js.map +1 -1
- package/components/crosshair/sync-bus.d.ts +0 -9
- package/components/crosshair/sync-bus.js +0 -27
- package/components/crosshair/sync-bus.js.map +0 -1
package/components/area/index.js
CHANGED
|
@@ -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
|
|
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":";;;;;;;;;;;;AA0BM,MAAO,IAAY,SAAQ,eAAkD,CAAA;AAYjF,IAAA,WAAA,CAAa,MAAmC,EAAA;AAC9C,QAAA,KAAK,EAAE,CAAA;QAXC,IAAc,CAAA,cAAA,GAAG,iBAA+C,CAAA;AACnE,QAAA,IAAA,CAAA,MAAM,GAA+B,IAAI,CAAC,cAAc,CAAA;QACxD,IAAO,CAAA,OAAA,GAAG,IAAI,CAAA;AAIrB,QAAA,IAAA,CAAA,MAAM,GAAG;AACP,YAAA,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,GAAG,EAAE;SAC1B,CAAA;AAIC,QAAA,IAAI,MAAM;AAAE,YAAA,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;;AAGlC,QAAA,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;KAC5C;AAED,IAAA,OAAO,CAAE,cAAuB,EAAA;AAC9B,QAAA,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,CAAA;QAC7B,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,EAAE,GAAG,IAAI,CAAA;AAC5C,QAAA,MAAM,QAAQ,GAAG,QAAQ,CAAC,cAAc,CAAC,GAAG,cAAc,GAAG,MAAM,CAAC,QAAQ,CAAA;QAE5E,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,SAAsB,CAAC,CAAA;AACrD,QAAA,IAAI,CAAC,QAAQ,GAAG,IAAI,EAAa;aAC9B,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;aACX,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;aACb,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;;;aAGb,KAAK,CAAC,QAAQ,CAAC,CAAA;QAElB,MAAM,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAA6B,CAAA;AAC1F,QAAA,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;AAE5E,QAAA,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,EAAE,UAAU,EAAE,IAAI,CAAC,aAAa,CAAC,CAAA;AACrF,QAAA,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAA;QAC3D,MAAM,wBAAwB,GAAa,EAAE,CAAA;AAC7C,QAAA,MAAM,WAAW,GAAkB,OAAO,CAAC,GAAG,CAC5C,GAAG,IAAI,GAAG,CAAC,GAAG,CACZ,CAAC,CAAC,EAAE,CAAC,KAAI;;AACP,YAAA,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAA;YACtB,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;YAC5B,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AAC5B,YAAA,MAAM,cAAc,GAAG,EAAE,GAAG,EAAE,CAAA;;YAG9B,MAAM,UAAU,GAAG,wBAAwB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;AACnD,YAAA,MAAM,UAAU,GAAG,cAAc,GAAG,EAAE,GAAG,UAAU,GAAG,EAAE,GAAG,UAAU,CAAA;AACrE,YAAA,MAAM,UAAU,GAAG,cAAc,GAAG,EAAE,GAAG,UAAU,GAAG,EAAE,GAAG,UAAU,CAAA;;YAGrE,IAAI,gBAAgB,GAAG,CAAC,CAAA;YACxB,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,YAAY;AACxC,gBAAA,IAAI,CAAC,GAAG,CAAC,UAAU,GAAG,UAAU,CAAC,IAAI,CAAA,EAAA,GAAA,MAAM,CAAC,SAAS,MAAI,IAAA,IAAA,EAAA,KAAA,KAAA,CAAA,GAAA,EAAA,GAAA,CAAC,CAAC,EAAE;AAC/D,gBAAA,gBAAgB,GAAG,CAAC,CAAA,EAAA,GAAA,MAAM,CAAC,SAAS,mCAAI,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,GAAG,UAAU,CAAC,CAAA;AAC9E,gBAAA,wBAAwB,CAAC,CAAC,CAAC,GAAG,UAAU,GAAG,gBAAgB,CAAA;AAC5D,aAAA;YAED,OAAO;gBACL,CAAC;AACD,gBAAA,EAAE,EAAE,UAAU;AACd,gBAAA,EAAE,EAAE,cAAc,GAAG,UAAU,GAAG,gBAAgB,GAAG,UAAU,GAAG,gBAAgB;aACnF,CAAA;SACF,CACF,CACF,CAAA;;;AAID,QAAA,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,GAAG,CAAC,CAAA;AACzC,QAAA,MAAM,mBAAmB,GAAG,WAAW,CAAC,OAAO,EAAE,CAAA;AACjD,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC;AACjB,aAAA,SAAS,CAA4B,CAAI,CAAA,EAAAA,MAAM,EAAE,CAAC;aAClD,IAAI,CAAC,mBAAmB,CAAC,CAAA;QAE5B,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC;AAC5C,aAAA,IAAI,CAAC,OAAO,EAAEA,MAAM,CAAC;AACrB,aAAA,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;AACrD,aAAA,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC;aACnB,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,CAAC,CAAC,CAAC,CAAA;AAExE,QAAA,MAAM,WAAW,GAAG,eAAe,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC;aACnE,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,KAAI;YACzB,MAAM,SAAS,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAA;YAClD,OAAO,SAAS,GAAG,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,CAAA;AACxE,SAAC,CAAC;aACD,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,CAAC,CAAC,CAAC;aACrE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC,CAAC,CAAA;AAE5E,QAAA,IAAI,QAAQ,EAAE;YACZ,MAAM,UAAU,GAAG,WAAgF,CAAA;AACnG,YAAA,UAAU,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,KAAI;AACrC,gBAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACxC,gBAAA,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAA;AAClD,gBAAA,OAAO,eAAe,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;AACxC,aAAC,CAAC,CAAA;AACH,SAAA;AAAM,aAAA;YACL,WAAW,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC,CAAA;AAClE,SAAA;AAED,QAAA,eAAe,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC;AACpC,aAAA,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC;AACnB,aAAA,MAAM,EAAE,CAAA;KACZ;AAED,IAAA,cAAc,CAAE,kBAA2B,EAAA;AACzC,QAAA,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAA;QAClC,MAAM,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAA6B,CAAA;AAE1F,QAAA,MAAM,IAAI,GAAG,kBAAkB,GAAG,iBAAiB,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAsB,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,IAAI,CAAA;QACxI,OAAO,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,EAAE,GAAG,UAAU,CAAC,CAAA;KAC9D;IAED,UAAU,GAAA;QACR,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAA;QAClC,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAc,CAAA;QAEhD,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;QACrD,MAAM,EAAE,GAAG,EAAE,CAAA;QAEb,OAAO,IAAI,CAAC,QAAQ,CAAC;YACnB,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE;YACxB,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE;AACzB,SAAA,CAAC,CAAA;KACH;;AA9HM,IAAS,CAAA,SAAA,GAAGC,KAAC;;;;"}
|
|
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.
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
*
|
|
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
|
|
43
|
-
/**
|
|
44
|
-
|
|
45
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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(
|
|
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,
|
|
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.
|
|
29
|
-
this.
|
|
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.
|
|
43
|
-
this.g.style('opacity',
|
|
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
|
-
|
|
52
|
-
const x = config.x
|
|
53
|
-
const yAcc = config.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
|
|
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
|
-
|
|
55
|
+
var _a, _b, _c, _d;
|
|
56
|
+
const { config, datamodel } = this;
|
|
77
57
|
const duration = isNumber(customDuration) ? customDuration : config.duration;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
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('
|
|
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',
|
|
88
|
-
.attr('x2',
|
|
89
|
-
const circleData =
|
|
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',
|
|
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',
|
|
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
|
|
129
|
-
const {
|
|
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(
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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:
|
|
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
|
|