• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

naver / billboard.js / 21126911356

19 Jan 2026 05:55AM UTC coverage: 94.082% (-0.08%) from 94.157%
21126911356

push

github

web-flow
chore(deps-dev): update dependency (#4083)

update dependencies to the latest

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

6624 of 7321 branches covered (90.48%)

Branch coverage included in aggregate %.

8225 of 8462 relevant lines covered (97.2%)

25510.97 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.24
/src/ChartInternal/shape/shape.ts
1
/**
2
 * Copyright (c) 2017 ~ present NAVER Corp.
3
 * billboard.js project is licensed under the MIT license
4
 */
5
import {select as d3Select} from "d3-selection";
6
import {
7
        curveBasis as d3CurveBasis,
8
        curveBasisClosed as d3CurveBasisClosed,
9
        curveBasisOpen as d3CurveBasisOpen,
10
        curveBundle as d3CurveBundle,
11
        curveCardinal as d3CurveCardinal,
12
        curveCardinalClosed as d3CurveCardinalClosed,
13
        curveCardinalOpen as d3CurveCardinalOpen,
14
        curveCatmullRom as d3CurveCatmullRom,
15
        curveCatmullRomClosed as d3CurveCatmullRomClosed,
16
        curveCatmullRomOpen as d3CurveCatmullRomOpen,
17
        curveLinear as d3CurveLinear,
18
        curveLinearClosed as d3CurveLinearClosed,
19
        curveMonotoneX as d3CurveMonotoneX,
20
        curveMonotoneY as d3CurveMonotoneY,
21
        curveNatural as d3CurveNatural,
22
        curveStep as d3CurveStep,
23
        curveStepAfter as d3CurveStepAfter,
24
        curveStepBefore as d3CurveStepBefore
25
} from "d3-shape";
26
import type {d3Selection} from "../../../types/types";
27
import CLASS from "../../config/classes";
28
import {KEY} from "../../module/Cache";
29
import {
30
        capitalize,
31
        getBBox,
32
        getPointer,
33
        getRectSegList,
34
        getUnique,
35
        isFunction,
36
        isNumber,
37
        isObjectType,
38
        isUndefined,
39
        isValue,
40
        notEmpty,
41
        parseDate
42
} from "../../module/util";
43
import type {IDataIndice, IDataRow, TIndices} from "../data/IData";
44

45
/**
46
 * Get grouped data point function for y coordinate
47
 * - Note: Grouped(stacking) works only for line and bar types
48
 * @param {object} d data vlaue
49
 * @returns {function|undefined}
50
 * @private
51
 */
52
function _getGroupedDataPointsFn(d) {
53
        const $$ = this;
1,479✔
54
        let fn;
55

56
        if ($$.isLineType(d)) {
1,479!
57
                fn = $$.generateGetLinePoints($$.getShapeIndices($$.isLineType));
1,479✔
58
        } else if ($$.isBarType(d)) {
×
59
                fn = $$.generateGetBarPoints($$.getShapeIndices($$.isBarType));
×
60
        }
61

62
        return fn;
1,479✔
63
}
64

65
export interface IOffset {
66
        _$width: number;
67
        _$total: number[];
68
}
69

70
export default {
71
        /**
72
         * Get the shape draw function
73
         * @returns {object}
74
         * @private
75
         */
76
        getDrawShape() {
77
                type TShape = {area?: any, bar?: any, line?: any};
78

79
                const $$ = this;
7,589✔
80
                const isRotated = $$.config.axis_rotated;
7,589✔
81
                const {hasRadar, hasTreemap} = $$.state;
7,589✔
82
                const shape = {type: <TShape>{}, indices: <TShape>{}, pos: {}};
7,589✔
83

84
                !hasTreemap && ["bar", "candlestick", "line", "area"].forEach(v => {
7,589✔
85
                        const name = capitalize(/^(bubble|scatter)$/.test(v) ? "line" : v);
29,912!
86

87
                        if (
29,912✔
88
                                $$.hasType(v) || $$.hasTypeOf(name) || (
82,007✔
89
                                        v === "line" &&
90
                                        ($$.hasType("bubble") || $$.hasType("scatter"))
91
                                )
92
                        ) {
93
                                const indices = $$.getShapeIndices($$[`is${name}Type`]);
7,175✔
94
                                const drawFn = $$[`generateDraw${name}`];
7,175✔
95

96
                                shape.indices[v] = indices;
7,175✔
97
                                shape.type[v] = drawFn ? drawFn.bind($$)(indices, false) : undefined;
7,175!
98
                        }
99
                });
100

101
                if (!$$.hasArcType() || hasRadar || hasTreemap) {
7,589✔
102
                        let cx;
103
                        let cy;
104

105
                        // generate circle x/y functions depending on updated params
106
                        if (!hasTreemap) {
6,803✔
107
                                cx = hasRadar ? $$.radarCircleX : (isRotated ? $$.circleY : $$.circleX);
6,692✔
108
                                cy = hasRadar ? $$.radarCircleY : (isRotated ? $$.circleX : $$.circleY);
6,692✔
109
                        }
110

111
                        shape.pos = {
6,803✔
112
                                xForText: $$.generateXYForText(shape.indices, true),
113
                                yForText: $$.generateXYForText(shape.indices, false),
114
                                cx: (cx || function() {}).bind($$),
6,914✔
115
                                cy: (cy || function() {}).bind($$)
6,914✔
116
                        };
117
                }
118

119
                return shape;
7,589✔
120
        },
121

122
        /**
123
         * Get shape's indices according it's position within each axis tick.
124
         *
125
         * From the below example, indices will be:
126
         * ==> {data1: 0, data2: 0, data3: 1, data4: 1, __max__: 1}
127
         *
128
         *         data1 data3   data1 data3
129
         *         data2 data4   data2 data4
130
         *         -------------------------
131
         *                  0             1
132
         * @param {function} typeFilter Chart type filter function
133
         * @returns {object} Indices object with its position
134
         */
135
        getShapeIndices(typeFilter): TIndices {
136
                const $$ = this;
8,654✔
137
                const {config} = $$;
8,654✔
138
                const xs = config.data_xs;
8,654✔
139
                const hasXs = notEmpty(xs);
8,654✔
140
                const indices: TIndices = {};
8,654✔
141
                let i: any = hasXs ? {} : 0;
8,654✔
142

143
                if (hasXs) {
8,654✔
144
                        getUnique(Object.keys(xs).map(v => xs[v]))
228✔
145
                                .forEach(v => {
146
                                        i[v] = 0;
213✔
147
                                        indices[v] = {};
213✔
148
                                });
149
                }
150

151
                $$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$))
8,654✔
152
                        .forEach(d => {
153
                                const xKey = d.id in xs ? xs[d.id] : "";
18,044✔
154
                                const ind = xKey ? indices[xKey] : indices;
18,044✔
155

156
                                for (let j = 0, groups; (groups = config.data_groups[j]); j++) {
18,044✔
157
                                        if (groups.indexOf(d.id) < 0) {
10,134✔
158
                                                continue;
2,598✔
159
                                        }
160

161
                                        for (let k = 0, key; (key = groups[k]); k++) {
7,536✔
162
                                                if (key in ind) {
12,378✔
163
                                                        ind[d.id] = ind[key];
4,809✔
164
                                                        break;
4,809✔
165
                                                }
166

167
                                                // for same grouped data, add other data to same indices
168
                                                if (d.id !== key && xKey) {
7,569✔
169
                                                        ind[key] = ind[d.id] ?? i[xKey];
18✔
170
                                                }
171
                                        }
172
                                }
173

174
                                if (isUndefined(ind[d.id])) {
18,044✔
175
                                        ind[d.id] = xKey ? i[xKey]++ : i++;
13,235✔
176
                                        ind.__max__ = (xKey ? i[xKey] : i) - 1;
13,235✔
177
                                }
178
                        });
179

180
                return indices;
8,654✔
181
        },
182

183
        /**
184
         * Get indices value based on data ID value
185
         * @param {object} indices Indices object
186
         * @param {object} d Data row
187
         * @param {string} caller Caller function name (Used only for 'sparkline' plugin)
188
         * @returns {object} Indices object
189
         * @private
190
         */
191
        getIndices(indices: TIndices, d: IDataRow, caller?: string): IDataIndice { // eslint-disable-line
192
                const $$ = this;
58,752✔
193
                const {data_xs: xs, bar_indices_removeNull: removeNull} = $$.config;
58,752✔
194
                const {id, index} = d;
58,752✔
195

196
                if ($$.isBarType(id) && removeNull) {
58,752✔
197
                        const ind = {} as IDataIndice;
54✔
198

199
                        // redefine bar indices order
200
                        $$.getAllValuesOnIndex(index, true)
54✔
201
                                .forEach((v, i) => {
202
                                        ind[v.id] = i;
108✔
203
                                        ind.__max__ = i;
108✔
204
                                });
205

206
                        return ind;
54✔
207
                }
208

209
                return notEmpty(xs) ? indices[xs[id]] : indices as IDataIndice;
58,698✔
210
        },
211

212
        /**
213
         * Get indices max number
214
         * @param {object} indices Indices object
215
         * @returns {number} Max number
216
         * @private
217
         */
218
        getIndicesMax(indices: TIndices | IDataIndice): number {
219
                return notEmpty(this.config.data_xs) ?
4,989✔
220
                        // if is multiple xs, return total sum of xs' __max__ value
221
                        Object.keys(indices)
222
                                .map(v => indices[v].__max__ || 0)
198✔
223
                                .reduce((acc, curr) => acc + curr) :
90✔
224
                        (indices as IDataIndice).__max__;
225
        },
226

227
        getShapeX(offset: IOffset, indices, isSub?: boolean): (d) => number {
228
                const $$ = this;
23,332✔
229
                const {config, scale} = $$;
23,332✔
230
                const currScale = isSub ? scale.subX : (scale.zoom || scale.x);
23,332✔
231
                const barOverlap = config.bar_overlap;
23,332✔
232
                const barPadding = config.bar_padding;
23,332✔
233
                const sum = (p, c) => p + c;
23,332✔
234

235
                // total shapes half width
236
                const halfWidth = isObjectType(offset) && (
23,332✔
237
                        offset._$total.length ? offset._$total.reduce(sum) / 2 : 0
270✔
238
                );
239

240
                return d => {
23,332✔
241
                        const ind = $$.getIndices(indices, d, "getShapeX");
29,379✔
242
                        const index = d.id in ind ? ind[d.id] : 0;
29,379✔
243
                        const targetsNum = (ind.__max__ || 0) + 1;
29,379✔
244
                        let x = 0;
29,379✔
245

246
                        if (notEmpty(d.x)) {
29,379!
247
                                const xPos = currScale(d.x, true);
29,379✔
248

249
                                if (halfWidth) {
29,379✔
250
                                        const offsetWidth = offset[d.id] || offset._$width;
171!
251

252
                                        x = barOverlap ? xPos - offsetWidth / 2 : xPos - offsetWidth +
171✔
253
                                                offset._$total.slice(0, index + 1).reduce(sum) -
254
                                                halfWidth;
255
                                } else {
256
                                        x = xPos - (isNumber(offset) ? offset : offset._$width) *
29,208✔
257
                                                        (targetsNum / 2 - (
258
                                                                barOverlap ? 1 : index
29,208!
259
                                                        ));
260
                                }
261
                        }
262

263
                        // adjust x position for bar.padding option
264
                        if (offset && x && targetsNum > 1 && barPadding) {
29,379✔
265
                                if (index) {
432✔
266
                                        x += barPadding * index;
216✔
267
                                }
268

269
                                if (targetsNum > 2) {
432!
270
                                        x -= (targetsNum - 1) * barPadding / 2;
×
271
                                } else if (targetsNum === 2) {
432!
272
                                        x -= barPadding / 2;
432✔
273
                                }
274
                        }
275

276
                        return x;
29,379✔
277
                };
278
        },
279

280
        getShapeY(isSub?: boolean): Function {
281
                const $$ = this;
23,332✔
282
                const isStackNormalized = $$.isStackNormalized();
23,332✔
283

284
                return d => {
23,332✔
285
                        let {value} = d;
30,153✔
286

287
                        if (isNumber(d)) {
30,153✔
288
                                value = d;
1,032✔
289
                        } else if ($$.isAreaRangeType(d)) {
29,121✔
290
                                value = $$.getBaseValue(d, "mid");
108✔
291
                        } else if (isStackNormalized) {
29,013✔
292
                                value = $$.getRatio("index", d, true);
420✔
293
                        } else if ($$.isBubbleZType(d)) {
28,593!
294
                                value = $$.getBubbleZData(d.value, "y");
×
295
                        } else if ($$.isBarRangeType(d)) {
28,593✔
296
                                // TODO use range.getEnd() like method
297
                                value = value[1];
177✔
298
                        }
299

300
                        return $$.getYScaleById(d.id, isSub)(value);
30,153✔
301
                };
302
        },
303

304
        /**
305
         * Get shape based y Axis min value
306
         * @param {string} id Data id
307
         * @returns {number}
308
         * @private
309
         */
310
        getShapeYMin(id: string): number {
311
                const $$ = this;
65,349✔
312
                const axisId = $$.axis.getId(id);
65,349✔
313
                const scale = $$.scale[axisId];
65,349✔
314
                const [yMin] = scale.domain();
65,349✔
315
                const inverted = $$.config[`axis_${axisId}_inverted`];
65,349✔
316

317
                return !$$.isGrouped(id) && !inverted && yMin > 0 ? yMin : 0;
65,349✔
318
        },
319

320
        /**
321
         * Get Shape's offset data
322
         * @param {function} typeFilter Type filter function
323
         * @returns {object}
324
         * @private
325
         */
326
        getShapeOffsetData(typeFilter) {
327
                const $$ = this;
23,332✔
328
                const targets = $$.orderTargets(
23,332✔
329
                        $$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$))
330
                );
331

332
                // Create cache key based on target IDs
333
                const targetIds = targets.map(t => t.id).join("_");
45,232✔
334
                const cacheKey = `${KEY.shapeOffset}_${targetIds}`;
23,332✔
335

336
                // Check if result is already cached
337
                const cachedData = $$.cache.get(cacheKey);
23,332✔
338

339
                if (cachedData) {
23,332✔
340
                        return cachedData;
17,617✔
341
                }
342

343
                const isStackNormalized = $$.isStackNormalized();
5,715✔
344

345
                const shapeOffsetTargets = targets.map(target => {
5,715✔
346
                        let rowValues = target.values;
11,046✔
347
                        const values = {};
11,046✔
348

349
                        if ($$.isStepType(target)) {
11,046✔
350
                                rowValues = $$.convertValuesToStep(rowValues);
216✔
351
                        }
352

353
                        const rowValueMapByXValue = rowValues.reduce((out, d) => {
11,046✔
354
                                const key = Number(d.x);
57,654✔
355

356
                                out[key] = d;
57,654✔
357
                                values[key] = isStackNormalized ? $$.getRatio("index", d, true) : d.value;
57,654✔
358

359
                                return out;
57,654✔
360
                        }, {});
361

362
                        return {
11,046✔
363
                                id: target.id,
364
                                rowValues,
365
                                rowValueMapByXValue,
366
                                values
367
                        };
368
                });
369
                const indexMapByTargetId = targets.reduce((out, {id}, index) => {
5,715✔
370
                        out[id] = index;
11,046✔
371
                        return out;
11,046✔
372
                }, {});
373

374
                const result = {indexMapByTargetId, shapeOffsetTargets};
5,715✔
375

376
                // Cache the result
377
                $$.cache.add(cacheKey, result);
5,715✔
378

379
                return result;
5,715✔
380
        },
381

382
        getShapeOffset(typeFilter, indices, isSub?: boolean): Function {
383
                const $$ = this;
23,332✔
384
                const {shapeOffsetTargets, indexMapByTargetId} = $$.getShapeOffsetData(
23,332✔
385
                        typeFilter
386
                );
387
                const groupsZeroAs = $$.config.data_groupsZeroAs;
23,332✔
388

389
                return (d, idx) => {
23,332✔
390
                        const {id, value, x} = d;
29,382✔
391
                        const ind = $$.getIndices(indices, d);
29,382✔
392
                        const scale = $$.getYScaleById(id, isSub);
29,382✔
393

394
                        if ($$.isBarRangeType(d)) {
29,382✔
395
                                // TODO use range.getStart()
396
                                return scale(value[0]);
177✔
397
                        }
398

399
                        const dataXAsNumber = Number(x);
29,205✔
400
                        const y0 = scale(groupsZeroAs === "zero" ? 0 : $$.getShapeYMin(id));
29,205✔
401
                        let offset = y0;
29,205✔
402

403
                        shapeOffsetTargets
29,205✔
404
                                .filter(t => t.id !== id && ind[t.id] === ind[id])
99,729✔
405
                                .forEach(t => {
406
                                        const {
407
                                                id: tid,
408
                                                rowValueMapByXValue,
409
                                                rowValues,
410
                                                values: tvalues
411
                                        } = t;
48,798✔
412

413
                                        // for same stacked group (ind[tid] === ind[id])
414
                                        if (indexMapByTargetId[tid] < indexMapByTargetId[id]) {
48,798✔
415
                                                const rValue = tvalues[dataXAsNumber];
25,131✔
416
                                                let row = rowValues[idx];
25,131✔
417

418
                                                // check if the x values line up
419
                                                if (!row || Number(row.x) !== dataXAsNumber) {
25,131✔
420
                                                        row = rowValueMapByXValue[dataXAsNumber];
927✔
421
                                                }
422

423
                                                if (row?.value * value >= 0 && isNumber(rValue)) {
25,131✔
424
                                                        const addOffset = value === 0 ?
12,981✔
425
                                                                (
426
                                                                        (groupsZeroAs === "positive" &&
840✔
427
                                                                                rValue > 0) ||
428
                                                                        (groupsZeroAs === "negative" && rValue < 0)
429
                                                                ) :
430
                                                                true;
431

432
                                                        if (addOffset) {
12,981✔
433
                                                                offset += scale(rValue) - y0;
12,819✔
434
                                                        }
435
                                                }
436
                                        }
437
                                });
438

439
                        return offset;
29,205✔
440
                };
441
        },
442

443
        /**
444
         * Get data's y coordinate
445
         * @param {object} d Target data
446
         * @param {number} i Index number
447
         * @returns {number} y coordinate
448
         * @private
449
         */
450
        circleY(d: IDataRow, i: number): number {
451
                const $$ = this;
50,328✔
452
                const id = d.id;
50,328✔
453
                let points;
454

455
                if ($$.isGrouped(id)) {
50,328✔
456
                        points = _getGroupedDataPointsFn.bind($$)(d);
1,479✔
457
                }
458

459
                return points ? points(d, i)[0][1] : $$.getYScaleById(id)($$.getBaseValue(d));
50,328✔
460
        },
461

462
        getBarW(type, axis, targetsNum: number): number | IOffset {
463
                const $$ = this;
4,989✔
464
                const {config, org, scale, state} = $$;
4,989✔
465
                const maxDataCount = $$.getMaxDataCount();
4,989✔
466
                const isGrouped = type === "bar" && config.data_groups?.length;
4,989✔
467
                const configName = `${type}_width`;
4,989✔
468
                const {k} = $$.getZoomTransform?.() ?? {k: 1};
4,989✔
469
                const xMinMax = <[number, number]>[
4,989✔
470
                        config.axis_x_min ?? org.xDomain[0],
9,888✔
471
                        config.axis_x_max ?? org.xDomain[1]
9,915✔
472
                ].map($$.axis.isTimeSeries() ? parseDate.bind($$) : Number);
4,989✔
473

474
                let tickInterval = axis.tickInterval(maxDataCount);
4,989✔
475

476
                if (scale.zoom && !$$.axis.isCategorized() && k > 1) {
4,989✔
477
                        const isSameMinMax = xMinMax.every((v, i) => v === org.xDomain[i]);
162✔
478

479
                        tickInterval = org.xDomain.map((v, i) => {
90✔
480
                                const value = isSameMinMax ? v : v - Math.abs(xMinMax[i]);
180✔
481

482
                                return scale.zoom(value);
180✔
483
                        }).reduce((a, c) => Math.abs(a) + c) / maxDataCount;
90✔
484
                }
485

486
                const getWidth = (id?: string) => {
4,989✔
487
                        const width = id ? config[configName][id] : config[configName];
5,088✔
488
                        const ratio = id ? width.ratio : config[`${configName}_ratio`];
5,088✔
489
                        const max = id ? width.max : config[`${configName}_max`];
5,088✔
490
                        const w = isNumber(width) ? width : (
5,088✔
491
                                isFunction(width) ?
4,854✔
492
                                        width.call($$, state.width, targetsNum, maxDataCount) :
493
                                        (targetsNum ? (tickInterval * ratio) / targetsNum : 0)
4,827✔
494
                        );
495

496
                        return max && w > max ? max : w;
5,088✔
497
                };
498

499
                let result = getWidth();
4,989✔
500

501
                if (!isGrouped && isObjectType(config[configName])) {
4,989✔
502
                        result = {_$width: result, _$total: []};
270✔
503

504
                        $$.filterTargetsToShow($$.data.targets).forEach(v => {
270✔
505
                                if (config[configName][v.id]) {
468✔
506
                                        result[v.id] = getWidth(v.id);
99✔
507
                                        result._$total.push(result[v.id] || result._$width);
99!
508
                                }
509
                        });
510
                }
511

512
                return result;
4,989✔
513
        },
514

515
        /**
516
         * Get shape element
517
         * @param {string} shapeName Shape string
518
         * @param {number} i Index number
519
         * @param {string} id Data series id
520
         * @returns {d3Selection}
521
         * @private
522
         */
523
        getShapeByIndex(shapeName: string, i: number, id?: string): d3Selection {
524
                const $$ = this;
2,040✔
525
                const {$el} = $$;
2,040✔
526
                const suffix = isValue(i) ? `-${i}` : ``;
2,040✔
527
                let shape = $el[shapeName];
2,040✔
528

529
                // filter from shape reference if has
530
                if (shape && !shape.empty()) {
2,040✔
531
                        shape = shape
1,965✔
532
                                .filter(d => (id ? d.id === id : true))
18,972✔
533
                                .filter(d => (isValue(i) ? d.index === i : true));
17,865✔
534
                } else {
535
                        shape = (id ?
75!
536
                                $el.main
537
                                        .selectAll(
538
                                                `.${CLASS[`${shapeName}s`]}${$$.getTargetSelectorSuffix(id)}`
539
                                        ) :
540
                                $el.main)
541
                                .selectAll(`.${CLASS[shapeName]}${suffix}`);
542
                }
543

544
                return shape;
2,040✔
545
        },
546

547
        isWithinShape(that, d): boolean {
548
                const $$ = this;
663✔
549
                const shape = d3Select(that);
663✔
550
                let isWithin;
551

552
                if (!$$.isTargetToShow(d.id)) {
663!
553
                        isWithin = false;
×
554
                } else if ($$.hasValidPointType?.(that.nodeName)) {
663✔
555
                        isWithin = $$.isStepType(d) ?
441!
556
                                $$.isWithinStep(that, $$.getYScaleById(d.id)($$.getBaseValue(d))) :
557
                                $$.isWithinCircle(
558
                                        that,
559
                                        $$.isBubbleType(d) ? $$.pointSelectR(d) * 1.5 : 0
441✔
560
                                );
561
                } else if (that.nodeName === "path") {
222!
562
                        isWithin = shape.classed(CLASS.bar) ? $$.isWithinBar(that) : true;
222!
563
                }
564

565
                return isWithin;
663✔
566
        },
567

568
        getInterpolate(d) {
569
                const $$ = this;
9,789✔
570
                const interpolation = $$.getInterpolateType(d);
9,789✔
571

572
                return {
9,789✔
573
                        basis: d3CurveBasis,
574
                        "basis-closed": d3CurveBasisClosed,
575
                        "basis-open": d3CurveBasisOpen,
576
                        bundle: d3CurveBundle,
577
                        cardinal: d3CurveCardinal,
578
                        "cardinal-closed": d3CurveCardinalClosed,
579
                        "cardinal-open": d3CurveCardinalOpen,
580
                        "catmull-rom": d3CurveCatmullRom,
581
                        "catmull-rom-closed": d3CurveCatmullRomClosed,
582
                        "catmull-rom-open": d3CurveCatmullRomOpen,
583
                        "monotone-x": d3CurveMonotoneX,
584
                        "monotone-y": d3CurveMonotoneY,
585
                        natural: d3CurveNatural,
586
                        "linear-closed": d3CurveLinearClosed,
587
                        linear: d3CurveLinear,
588
                        step: d3CurveStep,
589
                        "step-after": d3CurveStepAfter,
590
                        "step-before": d3CurveStepBefore
591
                }[interpolation];
592
        },
593

594
        getInterpolateType(d) {
595
                const $$ = this;
9,795✔
596
                const {config} = $$;
9,795✔
597
                const type = config.spline_interpolation_type;
9,795✔
598
                const interpolation = $$.isInterpolationType(type) ? type : "cardinal";
9,795✔
599

600
                return $$.isSplineType(d) ? interpolation : (
9,795✔
601
                        $$.isStepType(d) ? config.line_step_type : "linear"
9,216✔
602
                );
603
        },
604

605
        isWithinBar(that): boolean {
606
                const mouse = getPointer(this.state.event, that);
273✔
607
                const list = getRectSegList(that);
273✔
608
                const [seg0, seg1] = list;
273✔
609
                const x = Math.min(seg0.x, seg1.x);
273✔
610
                const y = Math.min(seg0.y, seg1.y);
273✔
611
                const offset = this.config.bar_sensitivity;
273✔
612
                const {width, height} = getBBox(that, true);
273✔
613
                const sx = x - offset;
273✔
614
                const ex = x + width + offset;
273✔
615
                const sy = y + height + offset;
273✔
616
                const ey = y - offset;
273✔
617

618
                const isWithin = sx < mouse[0] &&
273✔
619
                        mouse[0] < ex &&
620
                        ey < mouse[1] &&
621
                        mouse[1] < sy;
622

623
                return isWithin;
273✔
624
        }
625
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc