UNPKG

@naarni/design-system

Version:

Naarni React Native Design System for EV Fleet Apps

118 lines (110 loc) 5.55 kB
import React, { useMemo, useState } from 'react'; import { View, TouchableWithoutFeedback, Text } from 'react-native'; import { Canvas, Rect, Line, Group } from '@shopify/react-native-skia'; import { useDeviceTheme } from '../../theme/deviceTheme'; import { styles } from './styles'; import { Tooltip } from './Tooltip'; import { getChartDimensions, generateGridLines, generateYAxisLabels, generateXAxisLabels, formatAxisLabel } from './utils'; export const BarChart = ({ data, width, height, showXAxis = true, showYAxis = true, isLabelOnXaxis = true, isLabelOnYaxis = true, color, showGrid = true, showTooltip = true, tooltipFormatter, }) => { const { colors } = useDeviceTheme(); const [tooltipData, setTooltipData] = useState(null); const [tooltipVisible, setTooltipVisible] = useState(false); const [selectedBar, setSelectedBar] = useState(null); const dimensions = getChartDimensions(width, height); const barColor = color || colors.primary.main; const barWidth = dimensions.chartWidth / data.length - 8; const maxValue = Math.max(...data, 1); const gridLines = useMemo(() => showGrid ? generateGridLines(data, dimensions) : [], [data, dimensions, showGrid]); const yAxisLabels = useMemo(() => isLabelOnYaxis ? generateYAxisLabels(data, dimensions) : [], [data, dimensions, isLabelOnYaxis]); const xAxisLabels = useMemo(() => isLabelOnXaxis ? generateXAxisLabels(data, dimensions) : [], [data, dimensions, isLabelOnXaxis]); const handleBarPress = (event, index) => { if (!showTooltip) return; const touchX = event.nativeEvent.locationX; const x = dimensions.padding + index * (barWidth + 8); const barHeight = (data[index] / maxValue) * dimensions.chartHeight; const y = height - dimensions.padding - barHeight; if (selectedBar === index) { // Toggle tooltip off if same bar is tapped setTooltipVisible(false); setSelectedBar(null); setTooltipData(null); } else { // Show tooltip for new bar setSelectedBar(index); setTooltipData({ x: x + barWidth / 2, y: y - 10, value: data[index], index, }); setTooltipVisible(true); } }; return (<View style={[styles.container, { width, height }]}> <View style={{ flex: 1 }}> {/* Y-Axis Labels */} {isLabelOnYaxis && yAxisLabels.map((label, index) => (<Text key={`y-label-${index}`} style={[ styles.yAxisLabel, { position: 'absolute', left: 5, top: label.y - 8, color: colors.text.secondary, fontSize: 10, }, ]}> {formatAxisLabel(label.value)} </Text>))} {/* X-Axis Labels */} {isLabelOnXaxis && xAxisLabels.map((label, index) => (<Text key={`x-label-${index}`} style={[ styles.xAxisLabel, { position: 'absolute', left: label.x - 20, top: height - dimensions.padding + 5, color: colors.text.secondary, fontSize: 10, }, ]}> {label.label} </Text>))} <Canvas style={styles.canvas}> {/* Grid Lines */} {showGrid && gridLines.map((line, index) => (<Line key={`grid-${index}`} p1={{ x: dimensions.padding, y: line.y }} p2={{ x: width - dimensions.padding, y: line.y }} color={colors.surface.border} strokeWidth={0.5} opacity={0.3}/>))} {/* X-Axis */} {showXAxis && (<Line p1={{ x: dimensions.padding, y: height - dimensions.padding }} p2={{ x: width - dimensions.padding, y: height - dimensions.padding }} color={colors.surface.border} strokeWidth={1}/>)} {/* Y-Axis */} {showYAxis && (<Line p1={{ x: dimensions.padding, y: dimensions.padding }} p2={{ x: dimensions.padding, y: height - dimensions.padding }} color={colors.surface.border} strokeWidth={1}/>)} {/* Bars */} <Group> {data.map((value, i) => { const x = dimensions.padding + i * (barWidth + 8); const barHeight = (value / maxValue) * dimensions.chartHeight; const y = height - dimensions.padding - barHeight; return (<Rect key={i} x={x} y={y} width={barWidth} height={barHeight} color={selectedBar === i ? colors.primary.light : barColor}/>); })} </Group> </Canvas> {/* Touchable overlay for bar interaction */} {data.map((value, i) => { const x = dimensions.padding + i * (barWidth + 8); const barHeight = (value / maxValue) * dimensions.chartHeight; const y = height - dimensions.padding - barHeight; return (<TouchableWithoutFeedback key={`touch-${i}`} onPress={(event) => handleBarPress(event, i)}> <View style={{ position: 'absolute', left: x, top: y, width: barWidth, height: barHeight, backgroundColor: 'transparent', }}/> </TouchableWithoutFeedback>); })} </View> {/* Tooltip */} {tooltipData && (<Tooltip data={tooltipData} visible={tooltipVisible} width={width} height={height}/>)} </View>); };