eletrotupi / tcc/ frontend/components/ui/ScaleSlider.tsx3dd5c9c336e53556c29b79567dbbc8913667907e
3.9 KB | 173 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
// ============================================================================
116
// STYLES
117
// ============================================================================
118

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

139
  // Rich variant
140
  labelRich: {
141
    fontSize: 14,
142
    fontWeight: 500
143
  },
144
  valueRich: {
145
    fontSize: 18,
146
    lineHeight: 28,
147
    fontWeight: 600
148
  },
149

150
  // Compact variant
151
  labelCompact: {
152
    fontSize: Theme.typography.bodyLg.fontSize,
153
  },
154
  valueCompact: {
155
    fontSize: Theme.typography.bodyMd.fontSize,
156
    fontWeight: Theme.typography.bodyMd.fontWeight,
157
    color: secondaryColor,
158
  },
159

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