eletrotupi / tcc / frontend/components/ui/ScaleSlider.tsx master
4.2 KB Raw
import React, { useState } from 'react';
import {
  View,
  Text,
  StyleSheet,
  ViewStyle,
} from 'react-native';
import Slider from '@react-native-community/slider';
import { useThemeColor } from '@/hooks/use-theme-color';
import { Theme } from '@/constants/theme';

type SliderVariant = 'rich' | 'compact';

interface ScaleSliderProps {
  label: string;
  value: number;
  onValueChange: (value: number) => void;
  onValueFormat?: (value: number) => string;
  min?: number;
  max?: number;
  step?: number;
  minLabel?: string;
  maxLabel?: string;
  icon?: React.ReactNode;      // rich variant only
  showMax?: boolean;           // shows "x/10" format instead of just "x"
  variant?: SliderVariant;
  style?: ViewStyle;
}

export const ScaleSlider: React.FC<ScaleSliderProps> = ({
  label,
  value,
  onValueChange,
  onValueFormat,
  min = 0,
  max = 10,
  step = 1,
  minLabel,
  maxLabel,
  icon,
  showMax,
  variant = 'compact',
  style,
}) => {
  const textColor = useThemeColor({}, 'text');
  const secondaryColor = useThemeColor({}, 'textSecondary');
  const tintColor = useThemeColor({}, 'tint');
  const trackingColor = useThemeColor({}, 'sliderTracking');
  const sliderLabelsColor = useThemeColor({}, 'sliderLabels');

  const isRich = variant === 'rich';

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

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

      {/* Header row */}
      <View style={[styles.header, isRich ? styles.headerRich : styles.headerCompact ]}>
        <View style={styles.labelRow}>
          {isRich && icon && (
            <View style={styles.iconWrapper}>{icon}</View>
          )}
          <Text style={[
            isRich ? styles.labelRich : styles.labelCompact,
            { color: textColor },
          ]}>
            {label}
          </Text>
        </View>

        <Text style={[
          isRich ? styles.valueRich : styles.valueCompact,
          { color: textColor },
        ]}>
          {(onValueFormat ? onValueFormat(valueDisplay) : valueDisplay)}
        </Text>
      </View>

      {/* XXX: Rich variant needs some extra margin to account for icons */}
      <View style={{ marginRight: isRich ? -10 : -15, marginLeft: isRich ? -10 : -15 }}>
        {/* Slider has a glitch with the full padding of it */}
        <Slider
          style={styles.slider}
          minimumValue={min}
          maximumValue={max}
          step={step}
          value={value}
          onValueChange={onValueChange}
          minimumTrackTintColor={trackingColor}
          maximumTrackTintColor={trackingColor}
          thumbTintColor={tintColor}
        />
      </View>

      {/* Min / Max labels */}
      {(minLabel || maxLabel) && (
        <View style={styles.rangeLabels}>
          {minLabel && (
            <Text style={[styles.rangeLabel, { color: sliderLabelsColor }]}>
              {minLabel.toUpperCase()}
            </Text>
          )}
          {maxLabel && (
            <Text style={[styles.rangeLabel, { color: sliderLabelsColor }]}>
              {maxLabel.toUpperCase()}
            </Text>
          )}
        </View>
      )}

    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    width: '100%',
  },
  header: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
  },

  // Rich has bigger margin than compact
  headerRich: {
    marginBottom: 4,
  },

  headerCompact: {
    marginBottom: 1
  },

  labelRow: {
    flexDirection: 'row',
    alignItems: 'center',
    gap: 8,
  },
  iconWrapper: {
    justifyContent: 'center',
    alignItems: 'center',
  },

  // Rich variant
  labelRich: {
    fontSize: 14,
    fontWeight: 500
  },
  valueRich: {
    fontSize: 18,
    lineHeight: 28,
    fontWeight: 600
  },

  // Compact variant
  labelCompact: {
    fontSize: 12,
    fontWeight: 600,
    lineHeight: 16
  },
  valueCompact: {
    fontSize: 12,
    lineHeight: 16,
    fontWeight: 600,
    color: Theme.colors.light.textSecondary,
  },

  slider: {
    width: '100%',
    height: 40,
  },
  rangeLabels: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    marginTop: -4,
  },
  rangeLabel: {
    ...Theme.typography.labelXs,
    fontWeight: 500,
  },
});