barchart

mail@pastecode.io avatar
unknown
javascript
2 years ago
4.1 kB
1
Indexable
Never
import { StyleSheet, Text, View } from 'react-native';
import React from 'react';
import GridDivider from './components/grid_divider';
import ColumnItem from './components/column_item';
const _data = [
  { x: '12/2021', y: 2 },
  { x: '1/2022', y: 11 },
  { x: '2/2022', y: 4 },
  { x: '3/2022', y: 0 },
  { x: '4/2022', y: 0 },
];
const getYMin = YAxis => {
  let min = YAxis[0];
  for (let i = 1; i < YAxis.length; i++) {
    if ((YAxis[i] < min && YAxis[i] !== 0) || min === 0) {
      min = YAxis[i];
    }
  }
  return min;
};
const BarChart = React.forwardRef(
  (
    {
      onPressColumn,
      data = _data,
      renderYAxis = e => (
        <Text key={`${e}`} style={styles.txtYAxis}>
          {e}M
        </Text>
      ),
      renderXAxis = e => <Text style={styles.txtXAxis}>{e.x}</Text>,
    },
    ref,
  ) => {
    const [currentIndex, setCurrentIndex] = React.useState(-1);

    const _yAxis = React.useMemo(() => data.map(e => e.y), [data]);
    const yMin = React.useMemo(() => Math.round(getYMin(_yAxis)), [_yAxis]);
    const yMax = React.useMemo(() => {
      let max = Math.ceil(Math.max(..._yAxis));
      if (max % 2 === 1) {
        return max + 1;
      }
      return max;
    }, [_yAxis]);

    const yAxisDisplay = React.useMemo(() => {
      if (yMin === yMax) {
        return [yMax, (yMax + yMax / 2) / 2, yMax / 2, 0];
      }
      return [yMax, (yMax + yMin) / 2, yMin, 0];
    });
    const percents = React.useMemo(() => {
      if (yMin === 0) return [0, 0, 0, 0, 0];
      if (yMin !== yMax) {
        return data.map(e => {
          if (e.y === 0) return 0;
          return heightCell + ((e.y - yMin) / (yMax - yMin)) * heightCell * 2;
        });
      } else {
        return data.map(e => {
          if (e.y === yMin) return heightCell * 3;
          return 0;
        });
      }
    }, [data]); // list height item)

    React.useImperativeHandle(ref, () => ({
      selectAll: () => {
        setCurrentIndex(-1);
      },
    }));

    return (
      <View style={styles.container}>
        <View style={styles.containerTop}>
          <View style={styles.yAxis}>
            {yAxisDisplay.map(e => renderYAxis(e))}
          </View>
          <View style={styles.containerChart}>
            <GridDivider length={data.length} />
            {percents.map((e, i) => (
              <ColumnItem
                height={e}
                key={`${i}`}
                index={i}
                onPress={index => {
                  onPressColumn && onPressColumn(data[index]);
                  setCurrentIndex(index);
                }}
                selected={currentIndex === i || currentIndex === -1}
              />
            ))}
          </View>
        </View>
        <View style={styles.bottom}>
          {data.map((e, i) => {
            return (
              <View style={styles.itemXAxis} key={`${e.x}`}>
                {i === 0 || i === data.length - 1 ? renderXAxis(e) : null}
              </View>
            );
          })}
        </View>
      </View>
    );
  },
);

export default React.memo(BarChart);

const widthYAxis = 40;
const heightCell = 50;
const widthCell = 60;
const heightContainerChart = heightCell * 3;
const widthContainerChart = widthCell * 5;
const heightYAxis = heightContainerChart + 17;
const widthChart = widthContainerChart + widthYAxis;
const styles = StyleSheet.create({
  container: {
    width: widthChart,
    backgroundColor: '#1B222D',
  },
  containerTop: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  yAxis: {
    width: 40,
    justifyContent: 'space-between',
    height: heightYAxis,
    backgroundColor: '#1B222D',
  },
  txtYAxis: {
    fontSize: 12,
    fontWeight: '500',
    color: '#8492A7',
  },
  containerChart: {
    flexDirection: 'row',
    height: heightContainerChart,
    width: widthContainerChart,
  },
  bottom: {
    flexDirection: 'row',
    paddingLeft: widthYAxis,
    paddingBottom: 8,
  },
  txtXAxis: {
    fontSize: 12,
    fontWeight: '500',
    color: '#8492A7',
  },
  itemXAxis: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
});