d3-array#bisector JavaScript Examples
The following examples show how to use
d3-array#bisector.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: ticks.js From cs-wiki with GNU General Public License v3.0 | 5 votes |
function ticker(year, month, week, day, hour, minute) {
const tickIntervals = [
[second, 1, durationSecond],
[second, 5, 5 * durationSecond],
[second, 15, 15 * durationSecond],
[second, 30, 30 * durationSecond],
[minute, 1, durationMinute],
[minute, 5, 5 * durationMinute],
[minute, 15, 15 * durationMinute],
[minute, 30, 30 * durationMinute],
[ hour, 1, durationHour ],
[ hour, 3, 3 * durationHour ],
[ hour, 6, 6 * durationHour ],
[ hour, 12, 12 * durationHour ],
[ day, 1, durationDay ],
[ day, 2, 2 * durationDay ],
[ week, 1, durationWeek ],
[ month, 1, durationMonth ],
[ month, 3, 3 * durationMonth ],
[ year, 1, durationYear ]
];
function ticks(start, stop, count) {
const reverse = stop < start;
if (reverse) [start, stop] = [stop, start];
const interval = count && typeof count.range === "function" ? count : tickInterval(start, stop, count);
const ticks = interval ? interval.range(start, +stop + 1) : []; // inclusive stop
return reverse ? ticks.reverse() : ticks;
}
function tickInterval(start, stop, count) {
const target = Math.abs(stop - start) / count;
const i = bisector(([,, step]) => step).right(tickIntervals, target);
if (i === tickIntervals.length) return year.every(tickStep(start / durationYear, stop / durationYear, count));
if (i === 0) return millisecond.every(Math.max(tickStep(start, stop, count), 1));
const [t, step] = tickIntervals[target / tickIntervals[i - 1][2] < tickIntervals[i][2] / target ? i - 1 : i];
return t.every(step);
}
return [ticks, tickInterval];
}
Example #2
Source File: time.js From cs-wiki with GNU General Public License v3.0 | 4 votes |
export function calendar(year, month, week, day, hour, minute, second, millisecond, format) {
var scale = continuous(identity, identity),
invert = scale.invert,
domain = scale.domain;
var formatMillisecond = format(".%L"),
formatSecond = format(":%S"),
formatMinute = format("%I:%M"),
formatHour = format("%I %p"),
formatDay = format("%a %d"),
formatWeek = format("%b %d"),
formatMonth = format("%B"),
formatYear = format("%Y");
var tickIntervals = [
[second, 1, durationSecond],
[second, 5, 5 * durationSecond],
[second, 15, 15 * durationSecond],
[second, 30, 30 * durationSecond],
[minute, 1, durationMinute],
[minute, 5, 5 * durationMinute],
[minute, 15, 15 * durationMinute],
[minute, 30, 30 * durationMinute],
[ hour, 1, durationHour ],
[ hour, 3, 3 * durationHour ],
[ hour, 6, 6 * durationHour ],
[ hour, 12, 12 * durationHour ],
[ day, 1, durationDay ],
[ day, 2, 2 * durationDay ],
[ week, 1, durationWeek ],
[ month, 1, durationMonth ],
[ month, 3, 3 * durationMonth ],
[ year, 1, durationYear ]
];
function tickFormat(date) {
return (second(date) < date ? formatMillisecond
: minute(date) < date ? formatSecond
: hour(date) < date ? formatMinute
: day(date) < date ? formatHour
: month(date) < date ? (week(date) < date ? formatDay : formatWeek)
: year(date) < date ? formatMonth
: formatYear)(date);
}
function tickInterval(interval, start, stop, step) {
if (interval == null) interval = 10;
// If a desired tick count is specified, pick a reasonable tick interval
// based on the extent of the domain and a rough estimate of tick size.
// Otherwise, assume interval is already a time interval and use it.
if (typeof interval === "number") {
var target = Math.abs(stop - start) / interval,
i = bisector(function(i) { return i[2]; }).right(tickIntervals, target);
if (i === tickIntervals.length) {
step = tickStep(start / durationYear, stop / durationYear, interval);
interval = year;
} else if (i) {
i = tickIntervals[target / tickIntervals[i - 1][2] < tickIntervals[i][2] / target ? i - 1 : i];
step = i[1];
interval = i[0];
} else {
step = Math.max(tickStep(start, stop, interval), 1);
interval = millisecond;
}
}
return step == null ? interval : interval.every(step);
}
scale.invert = function(y) {
return new Date(invert(y));
};
scale.domain = function(_) {
return arguments.length ? domain(map.call(_, number)) : domain().map(date);
};
scale.ticks = function(interval, step) {
var d = domain(),
t0 = d[0],
t1 = d[d.length - 1],
r = t1 < t0,
t;
if (r) t = t0, t0 = t1, t1 = t;
t = tickInterval(interval, t0, t1, step);
t = t ? t.range(t0, t1 + 1) : []; // inclusive stop
return r ? t.reverse() : t;
};
scale.tickFormat = function(count, specifier) {
return specifier == null ? tickFormat : format(specifier);
};
scale.nice = function(interval, step) {
var d = domain();
return (interval = tickInterval(interval, d[0], d[d.length - 1], step))
? domain(nice(d, interval))
: scale;
};
scale.copy = function() {
return copy(scale, calendar(year, month, week, day, hour, minute, second, millisecond, format));
};
return scale;
}
Example #3
Source File: Timeseries.js From covid19india-react with MIT License | 4 votes |
function Timeseries({
statistics,
timeseries,
dates,
endDate,
chartType,
isUniform,
isLog,
isMovingAverage,
noRegionHighlightedDistrictData,
}) {
const {t} = useTranslation();
const refs = useRef([]);
const [wrapperRef, {width, height}] = useMeasure();
const [highlightedDate, setHighlightedDate] = useState(
dates[dates.length - 1]
);
useEffect(() => {
setHighlightedDate(dates[dates.length - 1]);
}, [dates]);
const getTimeseriesStatistic = useCallback(
(date, statistic, movingAverage = isMovingAverage) => {
return getStatistic(timeseries?.[date], chartType, statistic, {
movingAverage,
});
},
[chartType, isMovingAverage, timeseries]
);
const condenseChart = useMemo(() => {
const T = dates.length;
const days = differenceInDays(
parseIndiaDate(dates[T - 1]),
parseIndiaDate(dates[0])
);
// Chart extremes
const chartRight = width - margin.right;
// Bar widths
const axisWidth = Math.max(0, chartRight - margin.left) / (1.25 * days);
return axisWidth < 4;
}, [width, dates]);
const xScale = useMemo(() => {
const T = dates.length;
const chartRight = width - margin.right;
return scaleTime()
.clamp(true)
.domain([
parseIndiaDate(dates[0] || endDate),
parseIndiaDate(dates[T - 1] || endDate),
])
.range([margin.left, chartRight]);
}, [width, endDate, dates]);
const yScales = useMemo(() => {
const chartBottom = height - margin.bottom;
const addScaleBuffer = (scale, log = false) => {
const domain = scale.domain();
if (log) {
scale.domain([
domain[0],
domain[0] * Math.pow(domain[1] / domain[0], 1 / yScaleShrinkFactor),
]);
} else {
scale.domain([
domain[0],
domain[0] + (domain[1] - domain[0]) / yScaleShrinkFactor,
]);
}
return scale;
};
const [uniformScaleMin, uniformScaleMax] = extent(
dates.reduce((res, date) => {
res.push(
...PRIMARY_STATISTICS.map((statistic) =>
getTimeseriesStatistic(date, statistic)
)
);
return res;
}, [])
);
const yScaleUniformLinear = addScaleBuffer(
scaleLinear()
.clamp(true)
.domain([Math.min(0, uniformScaleMin), Math.max(1, uniformScaleMax)])
.range([chartBottom, margin.top])
.nice(4)
);
const yScaleUniformLog = addScaleBuffer(
scaleLog()
.clamp(true)
.domain([1, Math.max(10, uniformScaleMax)])
.range([chartBottom, margin.top])
.nice(4),
true
);
return statistics.map((statistic) => {
if (isUniform) {
if (
chartType === 'total' &&
isLog &&
PRIMARY_STATISTICS.includes(statistic)
) {
return yScaleUniformLog;
} else if (PRIMARY_STATISTICS.includes(statistic)) {
return yScaleUniformLinear;
}
}
const [scaleMin, scaleMax] = extent(dates, (date) =>
getTimeseriesStatistic(date, statistic)
);
if (chartType === 'total' && isLog) {
return addScaleBuffer(
scaleLog()
.clamp(true)
.domain([1, Math.max(10, scaleMax)])
.range([chartBottom, margin.top])
.nice(4),
true
);
} else {
return addScaleBuffer(
scaleLinear()
.clamp(true)
.domain([
Math.min(0, scaleMin),
STATISTIC_CONFIGS[statistic].format === '%'
? Math.min(100, Math.max(1, scaleMax))
: Math.max(1, scaleMax),
])
.range([chartBottom, margin.top])
.nice(4)
);
}
});
}, [
height,
chartType,
isUniform,
isLog,
statistics,
dates,
getTimeseriesStatistic,
]);
useEffect(() => {
if (!width || !height) return;
const T = dates.length;
// Chart extremes
const chartRight = width - margin.right;
const chartBottom = height - margin.bottom;
const isDiscrete = chartType === 'delta' && !isMovingAverage;
const xAxis = (g) =>
g
.attr('class', 'x-axis')
.call(axisBottom(xScale).ticks(numTicksX(width)));
const xAxis2 = (g, yScale) => {
g.attr('class', 'x-axis2')
.call(axisBottom(xScale).tickValues([]).tickSize(0))
.select('.domain')
.style('transform', `translate3d(0, ${yScale(0)}px, 0)`);
if (yScale(0) !== chartBottom) g.select('.domain').attr('opacity', 0.4);
else g.select('.domain').attr('opacity', 0);
};
const yAxis = (g, yScale, format) =>
g.attr('class', 'y-axis').call(
axisRight(yScale)
.ticks(4)
.tickFormat((num) => formatNumber(num, format))
.tickPadding(4)
);
function mousemove(event) {
const xm = pointer(event)[0];
const date = xScale.invert(xm);
if (!isNaN(date)) {
const bisectDate = bisector((date) => parseIndiaDate(date)).left;
const index = bisectDate(dates, date, 1);
const dateLeft = dates[index - 1];
const dateRight = dates[index];
setHighlightedDate(
date - parseIndiaDate(dateLeft) < parseIndiaDate(dateRight) - date
? dateLeft
: dateRight
);
}
}
function mouseout(event) {
setHighlightedDate(dates[T - 1]);
}
/* Begin drawing charts */
statistics.forEach((statistic, i) => {
const ref = refs.current[i];
const svg = select(ref);
const t = svg.transition().duration(D3_TRANSITION_DURATION);
const yScale = yScales[i];
const statisticConfig = STATISTIC_CONFIGS[statistic];
const format =
STATISTIC_CONFIGS[statistic].format === '%' ? '%' : 'short';
const isNonLinear = !!STATISTIC_CONFIGS[statistic]?.nonLinear;
/* X axis */
svg
.select('.x-axis')
.style('transform', `translate3d(0, ${chartBottom}px, 0)`)
.transition(t)
.call(xAxis);
svg.select('.x-axis2').transition(t).call(xAxis2, yScale);
/* Y axis */
svg
.select('.y-axis')
.style('transform', `translate3d(${chartRight}px, 0, 0)`)
.transition(t)
.call(yAxis, yScale, format);
/* Path dots */
svg
.selectAll('circle.normal')
.data(condenseChart ? [] : dates, (date) => date)
.join((enter) =>
enter
.append('circle')
.attr('class', 'normal')
.attr('fill', statisticConfig?.color)
.attr('stroke', statisticConfig?.color)
.attr('cx', (date) => xScale(parseIndiaDate(date)))
.attr('cy', (date) =>
yScale(isDiscrete ? 0 : getTimeseriesStatistic(date, statistic))
)
.attr('r', 2)
)
.transition(t)
.attr('cx', (date) => xScale(parseIndiaDate(date)))
.attr('cy', (date) => yScale(getTimeseriesStatistic(date, statistic)));
const areaPath = (dates, allZero = false) =>
area()
.curve(curveStep)
.x((date) => xScale(parseIndiaDate(date)))
.y0(yScale(0))
.y1(
allZero
? yScale(0)
: (date) => yScale(getTimeseriesStatistic(date, statistic, false))
)(dates);
svg
.selectAll('.trend-area')
.data(
T && chartType === 'delta' && !isNonLinear && condenseChart
? [dates]
: []
)
.join(
(enter) =>
enter
.append('path')
.attr('class', 'trend-area')
.call((enter) =>
enter
.attr('d', (dates) => areaPath(dates, true))
.transition(t)
.attr('d', areaPath)
),
(update) =>
update.call((update) =>
update.transition(t).attrTween('d', function (dates) {
const previous = select(this).attr('d');
const current = areaPath(dates);
return interpolatePath(previous, current);
})
),
(exit) =>
exit.call((exit) =>
exit
.transition(t)
.attr('d', (dates) => areaPath(dates, true))
.remove()
)
)
.transition(t)
.attr('opacity', isMovingAverage ? 0.3 : 1);
svg
.selectAll('.stem')
.data(
T && chartType === 'delta' && !isNonLinear && !condenseChart
? dates
: [],
(date) => date
)
.join(
(enter) =>
enter
.append('line')
.attr('class', 'stem')
.attr('stroke-width', 4)
.attr('x1', (date) => xScale(parseIndiaDate(date)))
.attr('y1', yScale(0))
.attr('x2', (date) => xScale(parseIndiaDate(date)))
.attr('y2', yScale(0)),
(update) => update,
(exit) =>
exit.call((exit) =>
exit
.transition(t)
.attr('x1', (date) => xScale(parseIndiaDate(date)))
.attr('x2', (date) => xScale(parseIndiaDate(date)))
.attr('y1', yScale(0))
.attr('y2', yScale(0))
.remove()
)
)
.transition(t)
.attr('x1', (date) => xScale(parseIndiaDate(date)))
.attr('y1', yScale(0))
.attr('x2', (date) => xScale(parseIndiaDate(date)))
.attr('y2', (date) =>
yScale(getTimeseriesStatistic(date, statistic, false))
)
.attr('opacity', isMovingAverage ? 0.2 : 1);
const linePath = (
movingAverage = isMovingAverage,
curve = curveMonotoneX
) =>
line()
.curve(curve)
.x((date) => xScale(parseIndiaDate(date)))
.y((date) =>
yScale(getTimeseriesStatistic(date, statistic, movingAverage))
);
svg
.select('.trend')
.selectAll('path')
.data(
T && (chartType === 'total' || isNonLinear || isMovingAverage)
? [dates]
: []
)
.join(
(enter) =>
enter
.append('path')
.attr('class', 'trend')
.attr('fill', 'none')
.attr('stroke-width', 4)
.attr('d', linePath())
.call((enter) => enter.transition(t).attr('opacity', 1)),
(update) =>
update.call((update) =>
update
.transition(t)
.attrTween('d', function (date) {
const previous = select(this).attr('d');
const current = linePath()(date);
return interpolatePath(previous, current);
})
.attr('opacity', 1)
),
(exit) =>
exit.call((exit) => exit.transition(t).attr('opacity', 0).remove())
)
.attr('stroke', statisticConfig?.color + (condenseChart ? '99' : '50'));
svg
.select('.trend-background')
.selectAll('path')
.data(
T && chartType === 'delta' && isNonLinear && isMovingAverage
? [dates]
: []
)
.join(
(enter) =>
enter
.append('path')
.attr('class', 'trend-background')
.attr('fill', 'none')
.attr('stroke-width', 4)
.attr('d', linePath(false, curveLinear))
.call((enter) => enter.transition(t).attr('opacity', 0.2)),
(update) =>
update.call((update) =>
update
.transition(t)
.attrTween('d', function (date) {
const previous = select(this).attr('d');
const current = linePath(false, curveLinear)(date);
return interpolatePath(previous, current);
})
.attr('opacity', 0.2)
),
(exit) =>
exit.call((exit) => exit.transition(t).attr('opacity', 0).remove())
)
.attr('stroke', statisticConfig?.color + (condenseChart ? '99' : '50'));
svg.selectAll('*').attr('pointer-events', 'none');
svg
.on('mousemove', mousemove)
.on('touchmove', (event) => mousemove(event.touches[0]))
.on('mouseout touchend', mouseout);
});
}, [
width,
height,
chartType,
isMovingAverage,
condenseChart,
xScale,
yScales,
statistics,
getTimeseriesStatistic,
dates,
]);
useEffect(() => {
statistics.forEach((statistic, i) => {
const ref = refs.current[i];
const svg = select(ref);
const statisticConfig = STATISTIC_CONFIGS[statistic];
const yScale = yScales[i];
const t = svg.transition().duration(D3_TRANSITION_DURATION);
svg
.selectAll('circle.condensed')
.data(
condenseChart && highlightedDate ? [highlightedDate] : [],
(date) => date
)
.join((enter) =>
enter
.append('circle')
.attr('class', 'condensed')
.attr('fill', statisticConfig?.color)
.attr('stroke', statisticConfig?.color)
.attr('pointer-events', 'none')
.attr('cx', (date) => xScale(parseIndiaDate(date)))
.attr('cy', (date) =>
yScale(getTimeseriesStatistic(date, statistic))
)
.attr('r', 4)
)
.transition(t)
.attr('cx', (date) => xScale(parseIndiaDate(date)))
.attr('cy', (date) => yScale(getTimeseriesStatistic(date, statistic)));
if (!condenseChart) {
svg
.selectAll('circle')
.attr('r', (date) => (date === highlightedDate ? 4 : 2));
}
});
}, [
condenseChart,
highlightedDate,
xScale,
yScales,
statistics,
getTimeseriesStatistic,
]);
const getTimeseriesStatisticDelta = useCallback(
(statistic) => {
if (!highlightedDate) return;
const currCount = getTimeseriesStatistic(highlightedDate, statistic);
if (STATISTIC_CONFIGS[statistic]?.hideZero && currCount === 0) return;
const prevDate =
dates[dates.findIndex((date) => date === highlightedDate) - 1];
const prevCount = getTimeseriesStatistic(prevDate, statistic);
return currCount - prevCount;
},
[dates, highlightedDate, getTimeseriesStatistic]
);
const trail = useMemo(
() =>
statistics.map((statistic, index) => ({
animationDelay: `${index * 250}ms`,
})),
[statistics]
);
return (
<div className="Timeseries">
{statistics.map((statistic, index) => {
const total = getTimeseriesStatistic(highlightedDate, statistic);
const delta = getTimeseriesStatisticDelta(statistic, index);
const statisticConfig = STATISTIC_CONFIGS[statistic];
return (
<div
key={statistic}
className={classnames('svg-parent fadeInUp', `is-${statistic}`)}
style={trail[index]}
ref={index === 0 ? wrapperRef : null}
>
{highlightedDate && (
<div className={classnames('stats', `is-${statistic}`)}>
<h5 className="title">
{t(capitalize(statisticConfig.displayName))}
</h5>
<h5>{formatDate(highlightedDate, 'dd MMMM')}</h5>
<div className="stats-bottom">
<h2>
{!noRegionHighlightedDistrictData ||
!statisticConfig?.hasPrimary
? formatNumber(
total,
statisticConfig.format !== 'short'
? statisticConfig.format
: 'long',
statistic
)
: '-'}
</h2>
<h6>
{!noRegionHighlightedDistrictData ||
!statisticConfig?.hasPrimary
? `${delta > 0 ? '+' : ''}${formatNumber(
delta,
statisticConfig.format !== 'short'
? statisticConfig.format
: 'long',
statistic
)}`
: ''}
</h6>
</div>
</div>
)}
<svg
ref={(element) => {
refs.current[index] = element;
}}
preserveAspectRatio="xMidYMid meet"
>
<g className="x-axis" />
<g className="x-axis2" />
<g className="y-axis" />
<g className="trend-background" />
<g className="trend" />
</svg>
</div>
);
})}
</div>
);
}
Example #4
Source File: time.js From gamedesign with GNU General Public License v3.0 | 4 votes |
export function calendar(year, month, week, day, hour, minute, second, millisecond, format) {
var scale = continuous(deinterpolate, reinterpolate),
invert = scale.invert,
domain = scale.domain;
var formatMillisecond = format(".%L"),
formatSecond = format(":%S"),
formatMinute = format("%I:%M"),
formatHour = format("%I %p"),
formatDay = format("%a %d"),
formatWeek = format("%b %d"),
formatMonth = format("%B"),
formatYear = format("%Y");
var tickIntervals = [
[second, 1, durationSecond],
[second, 5, 5 * durationSecond],
[second, 15, 15 * durationSecond],
[second, 30, 30 * durationSecond],
[minute, 1, durationMinute],
[minute, 5, 5 * durationMinute],
[minute, 15, 15 * durationMinute],
[minute, 30, 30 * durationMinute],
[ hour, 1, durationHour ],
[ hour, 3, 3 * durationHour ],
[ hour, 6, 6 * durationHour ],
[ hour, 12, 12 * durationHour ],
[ day, 1, durationDay ],
[ day, 2, 2 * durationDay ],
[ week, 1, durationWeek ],
[ month, 1, durationMonth ],
[ month, 3, 3 * durationMonth ],
[ year, 1, durationYear ]
];
function tickFormat(date) {
return (second(date) < date ? formatMillisecond
: minute(date) < date ? formatSecond
: hour(date) < date ? formatMinute
: day(date) < date ? formatHour
: month(date) < date ? (week(date) < date ? formatDay : formatWeek)
: year(date) < date ? formatMonth
: formatYear)(date);
}
function tickInterval(interval, start, stop, step) {
if (interval == null) interval = 10;
// If a desired tick count is specified, pick a reasonable tick interval
// based on the extent of the domain and a rough estimate of tick size.
// Otherwise, assume interval is already a time interval and use it.
if (typeof interval === "number") {
var target = Math.abs(stop - start) / interval,
i = bisector(function(i) { return i[2]; }).right(tickIntervals, target);
if (i === tickIntervals.length) {
step = tickStep(start / durationYear, stop / durationYear, interval);
interval = year;
} else if (i) {
i = tickIntervals[target / tickIntervals[i - 1][2] < tickIntervals[i][2] / target ? i - 1 : i];
step = i[1];
interval = i[0];
} else {
step = Math.max(tickStep(start, stop, interval), 1);
interval = millisecond;
}
}
return step == null ? interval : interval.every(step);
}
scale.invert = function(y) {
return new Date(invert(y));
};
scale.domain = function(_) {
return arguments.length ? domain(map.call(_, number)) : domain().map(date);
};
scale.ticks = function(interval, step) {
var d = domain(),
t0 = d[0],
t1 = d[d.length - 1],
r = t1 < t0,
t;
if (r) t = t0, t0 = t1, t1 = t;
t = tickInterval(interval, t0, t1, step);
t = t ? t.range(t0, t1 + 1) : []; // inclusive stop
return r ? t.reverse() : t;
};
scale.tickFormat = function(count, specifier) {
return specifier == null ? tickFormat : format(specifier);
};
scale.nice = function(interval, step) {
var d = domain();
return (interval = tickInterval(interval, d[0], d[d.length - 1], step))
? domain(nice(d, interval))
: scale;
};
scale.copy = function() {
return copy(scale, calendar(year, month, week, day, hour, minute, second, millisecond, format));
};
return scale;
}
Example #5
Source File: Dots.jsx From intergalactic with MIT License | 4 votes |
function Dots(props) {
const {
Element: SDot,
styles,
data,
color,
d3,
x,
y,
eventEmitter,
display,
hide,
rootRef,
scale,
duration = 500,
} = props;
const SDots = 'g';
const bisect = bisector((d) => d[x]).center;
const [activeIndex, setActiveIndex] = useState(null);
const handlerMouseMoveRoot = useCallback(
trottle((e) => {
const [xScale] = scale;
const [pX] = eventToPoint(e, rootRef.current);
const vX = invert(xScale, pX);
setActiveIndex(bisect(data, vX));
}),
[scale, data],
);
const handlerMouseLeaveRoot = useCallback(
trottle(() => {
setActiveIndex(null);
}),
[],
);
useEffect(() => {
const unsubscribeMouseMoveRoot = eventEmitter.subscribe('onMouseMoveChart', (e) => {
e.persist();
handlerMouseMoveRoot(e);
});
const unsubscribeMouseLeaveRoot = eventEmitter.subscribe(
'onMouseLeaveChart',
handlerMouseLeaveRoot,
);
return () => {
unsubscribeMouseMoveRoot();
unsubscribeMouseLeaveRoot();
};
}, [eventEmitter, scale, data, x, y]);
const dots = data.reduce((acc, d, i) => {
const isPrev = d3.defined()(data[i - 1] || {});
const isNext = d3.defined()(data[i + 1] || {});
const active = i === activeIndex;
const visible = display || i === activeIndex || (!isPrev && !isNext);
if (!d3.defined()(d)) return acc;
if (!visible) return acc;
acc.push(
sstyled(styles)(
<SDot
key={i}
render="circle"
visible={visible}
__excludeProps={EXCLUDE_PROPS}
value={d}
index={i}
cx={d3.x()(d)}
cy={d3.y()(d)}
active={active}
hide={hide}
color={color}
/>,
),
);
return acc;
}, []);
return sstyled(styles)(<SDots use:duration={`${duration}ms`}>{dots}</SDots>);
}