eletrotupi / tcc/ frontend/components/ui/ScaleSlider.tsx67829b445ee2039ee20f14c92a27b1e707feec83
3.8 KB | 169 lines Raw
1
import React, { useState } from 'react';
2
import {
3
  View,
4
  Text,
5
  StyleSheet,
6
  ViewStyle,
7
} from 'react-native';
8
import Slider from '@react-native-community/slider';
9
import { useThemeColor } from '@/hooks/use-theme-color';
10
import { Theme } from '@/constants/theme';
11

12
type SliderVariant = 'rich' | 'compact';
13

14
interface ScaleSliderProps {
15
  label: string;
16
  value: number;
17
  onValueChange: (value: number) => void;
18
  min?: number;
19
  max?: number;
20
  step?: number;
21
  minLabel?: string;
22
  maxLabel?: string;
23
  icon?: React.ReactNode;      // rich variant only
24
  showMax?: boolean;           // shows "x/10" format instead of just "x"
25
  variant?: SliderVariant;
26
  style?: ViewStyle;
27
}
28

29
export const ScaleSlider: React.FC<ScaleSliderProps> = ({
30
  label,
31
  value,
32
  onValueChange,
33
  min = 0,
34
  max = 10,
35
  step = 1,
36
  minLabel,
37
  maxLabel,
38
  icon,
39
  showMax,
40
  variant = 'compact',
41
  style,
42
}) => {
43
  const textColor = useThemeColor({}, 'text');
44
  const secondaryColor = useThemeColor({}, 'textSecondary');
45
  const tintColor = useThemeColor({}, 'tint');
46
  const trackingColor = useThemeColor({}, 'sliderTracking');
47
  const sliderLabelsColor = useThemeColor({}, 'sliderLabels');
48

49
  const isRich = variant === 'rich';
50

51
  const valueDisplay = showMax || !isRich
52
    ? `${value}/${max}`
53
    : `${value}`;
54

55
  return (
56
    <View style={[styles.container, style]}>
57

58
      {/* Header row */}
59
      <View style={styles.header}>
60
        <View style={styles.labelRow}>
61
          {isRich && icon && (
62
            <View style={styles.iconWrapper}>{icon}</View>
63
          )}
64
          <Text style={[
65
            isRich ? styles.labelRich : styles.labelCompact,
66
            { color: textColor },
67
          ]}>
68
            {label}
69
          </Text>
70
        </View>
71

72
        <Text style={[
73
          isRich ? styles.valueRich : styles.valueCompact,
74
          { color: textColor },
75
        ]}>
76
          {valueDisplay}
77
        </Text>
78
      </View>
79

80
      <View style={{ marginRight: -10, marginLeft: -10 }}>
81
        {/* Slider */}
82
        <Slider
83
          style={styles.slider}
84
          minimumValue={min}
85
          maximumValue={max}
86
          step={step}
87
          value={value}
88
          onValueChange={onValueChange}
89
          minimumTrackTintColor={trackingColor}
90
          maximumTrackTintColor={trackingColor}
91
          thumbTintColor={tintColor}
92
        />
93
      </View>
94

95
      {/* Min / Max labels */}
96
      {(minLabel || maxLabel) && (
97
        <View style={styles.rangeLabels}>
98
          {minLabel && (
99
            <Text style={[styles.rangeLabel, { color: sliderLabelsColor }]}>
100
              {minLabel.toUpperCase()}
101
            </Text>
102
          )}
103
          {maxLabel && (
104
            <Text style={[styles.rangeLabel, { color: sliderLabelsColor }]}>
105
              {maxLabel.toUpperCase()}
106
            </Text>
107
          )}
108
        </View>
109
      )}
110

111
    </View>
112
  );
113
};
114

115
const styles = StyleSheet.create({
116
  container: {
117
    width: '100%',
118
  },
119
  header: {
120
    flexDirection: 'row',
121
    justifyContent: 'space-between',
122
    alignItems: 'center',
123
    marginBottom: 4,
124
  },
125
  labelRow: {
126
    flexDirection: 'row',
127
    alignItems: 'center',
128
    gap: 8,
129
  },
130
  iconWrapper: {
131
    justifyContent: 'center',
132
    alignItems: 'center',
133
  },
134

135
  // Rich variant
136
  labelRich: {
137
    fontSize: 14,
138
    fontWeight: 500
139
  },
140
  valueRich: {
141
    fontSize: 18,
142
    lineHeight: 28,
143
    fontWeight: 600
144
  },
145

146
  // Compact variant
147
  labelCompact: {
148
    fontSize: Theme.typography.bodyLg.fontSize,
149
  },
150
  valueCompact: {
151
    fontSize: Theme.typography.bodyMd.fontSize,
152
    fontWeight: Theme.typography.bodyMd.fontWeight,
153
    color: Theme.colors.light.textSecondary,
154
  },
155

156
  slider: {
157
    width: '100%',
158
    height: 40,
159
  },
160
  rangeLabels: {
161
    flexDirection: 'row',
162
    justifyContent: 'space-between',
163
    marginTop: -4,
164
  },
165
  rangeLabel: {
166
    ...Theme.typography.labelXs,
167
    fontWeight: 500,
168
  },
169
});