import React, { useState, ChangeEvent } from 'react'; import styled from 'styled-components'; // --- Типы --- interface SliderProps { min?: number; max?: number; step?: number; defaultValue?: number; onChange?: (value: number) => void; } // --- Стили (Styled-Components) --- const Container = styled.div` width: 100%; max-width: 800px; margin: 0 auto; padding: 20px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; `; const SliderWrapper = styled.div` position: relative; width: 100%; height: 24px; // Высота для кликабельной области бегунка display: flex; align-items: center; `; const Track = styled.div` position: relative; width: 100%; height: 12px; background-color: #f0f7ec; // Светло-зеленый фон border-radius: 6px; cursor: pointer; `; const Fill = styled.div<{ $percent: number }>` position: absolute; top: 0; left: 0; height: 100%; background-color: #5b9e30; // Зеленый цвет заполнения width: ${({ $percent }) => $percent}%; // Скругление только слева, если не достигнут максимум border-radius: ${({ $percent }) => $percent >= 100 ? '6px' : $percent <= 0 ? '6px' : '6px 0 0 6px' }; pointer-events: none; `; const Thumb = styled.div<{ $percent: number }>` position: absolute; top: 50%; left: ${({ $percent }) => $percent}%; transform: translate(-50%, -50%); width: 24px; height: 24px; background-color: #5b9e30; border-radius: 50%; pointer-events: none; box-shadow: 0 1px 3px rgba(0,0,0,0.2); // Мобильная версия: немного увеличиваем для удобного нажатия пальцем @media (max-width: 768px) { width: 28px; height: 28px; } `; // Прозрачный нативный инпут для обработки перетаскивания и кликов const RangeInput = styled.input` position: absolute; top: 0; left: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; margin: 0; `; const LabelsContainer = styled.div` position: relative; width: 100%; height: 30px; margin-top: 8px; `; const Label = styled.span` position: absolute; font-size: 16px; color: #333333; @media (max-width: 768px) { font-size: 14px; } `; const LeftLabel = styled(Label)` left: 0; transform: translateX(0); `; const RightLabel = styled(Label)` right: 0; transform: translateX(0); `; const CenterLabel = styled(Label)<{ $percent: number }>` left: ${({ $percent }) => $percent}%; transform: translateX(-50%); `; // --- Основной Компонент --- const CustomSlider: React.FC = ({ min = 30, max = 170, step = 10, defaultValue = 90, onChange, }) => { const [value, setValue] = useState(defaultValue); const handleChange = (e: ChangeEvent) => { const newValue = Number(e.target.value); setValue(newValue); if (onChange) onChange(newValue); }; // Процент заполнения трека (от 0% до 100%) const percent = ((value - min) / (max - min)) * 100; return ( {min}% {(percent > 10) && (percent < 90) ? ({value}%) : null} {max}% ); }; export default CustomSlider;