1import { useCallback, useMemo } from 'react';
2import { View, StyleSheet, TouchableOpacity, Text } from 'react-native';
3
4import { PrimitiveArgument } from './index.types';
5import Colors from '../../constants/Colors';
6
7// Exclude boolean as this type should not be handled by this component.
8type Value = Exclude<PrimitiveArgument, boolean>;
9type EnumValue = { name: string; value: Value };
10
11type Props = {
12  value: Value;
13  onChange: (value: Value) => void;
14  values: Value[] | EnumValue[];
15  disabled?: boolean;
16};
17
18function valuesAreEnumValues(values: (Value | EnumValue)[]): values is EnumValue[] {
19  return values.every((value) => typeof value === 'object' && 'name' in value && 'value' in value);
20}
21
22function useEnumValues(values: Value[] | EnumValue[]): values is EnumValue[] {
23  return useMemo(() => valuesAreEnumValues(values), [values]);
24}
25
26function getSuccessorCyclically(values: Value[], value: Value) {
27  const valueIdx = values.findIndex((v) => v === value);
28  const successorIdx = (valueIdx + 1) % values.length;
29  return values[successorIdx];
30}
31
32/**
33 * Button component that upon every press switches to the next value from the array.
34 */
35export default function EnumButton({ value, onChange, values, disabled }: Props) {
36  const valuesAreEnums = useEnumValues(values);
37
38  const handleOnPress = useCallback(() => {
39    const plainValues = valuesAreEnums ? values.map((v) => v.value) : values;
40    const newValue = getSuccessorCyclically(plainValues, value);
41    return onChange(newValue);
42  }, [valuesAreEnums, onChange, value, values]);
43
44  return (
45    <TouchableOpacity disabled={disabled} onPress={handleOnPress}>
46      <View style={[styles.button, disabled && styles.buttonDisabled]}>
47        <Text style={styles.text}>
48          {valuesAreEnums ? values.find((element) => element.value === value)?.name : value}
49        </Text>
50      </View>
51    </TouchableOpacity>
52  );
53}
54
55const styles = StyleSheet.create({
56  button: {
57    marginLeft: 5,
58    paddingVertical: 3,
59    paddingHorizontal: 6,
60    backgroundColor: Colors.tintColor,
61    borderRadius: 5,
62    minWidth: 30,
63    alignItems: 'center',
64  },
65  text: {
66    fontSize: 10,
67    padding: 2,
68    fontWeight: '500',
69    color: 'white',
70  },
71  buttonDisabled: {
72    backgroundColor: '#CCD6DD',
73  },
74});
75