index.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. import React, { useEffect, useState, useRef } from 'react';
  2. import { fromEvent, Subscription } from 'rxjs';
  3. import { throttleTime } from 'rxjs/operators';
  4. import { OuterWrapper, Wrapper, Rail, Track } from './styled';
  5. type Props = {
  6. color?: 'primary' | 'secondary';
  7. minimum?: number;
  8. maximum?: number;
  9. defaultValue?: number;
  10. value?: number;
  11. disabled?: boolean;
  12. onChange?: (value: number) => void;
  13. };
  14. const Sliders: React.FC<Props> = ({
  15. defaultValue = 0,
  16. value = 0,
  17. onChange,
  18. // minimum = 0,
  19. maximum = 100,
  20. }: Props) => {
  21. const sliderRef = useRef<HTMLDivElement>(null);
  22. const [valueState, setValueState] = useState(defaultValue);
  23. const [isActive, setActive] = useState(false);
  24. let mouseSubscription: Subscription | null = null;
  25. let touchSubscription: Subscription | null = null;
  26. const parseValueToPercent = (val: number): number => Math.floor(val * 100);
  27. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  28. const getFingerMoveValue = (event: any): void => {
  29. const { current: slider } = sliderRef;
  30. let clientX = 0;
  31. if (event.touches) {
  32. const touches = event.touches[0];
  33. ({ clientX } = touches);
  34. } else {
  35. ({ clientX } = event);
  36. }
  37. if (slider) {
  38. const { width, left } = slider.getBoundingClientRect();
  39. const moveDistance = clientX - left;
  40. const moveRate = moveDistance / width;
  41. let percent = parseValueToPercent(moveRate);
  42. if (percent <= 0) {
  43. percent = 0;
  44. } else if (percent >= 100) {
  45. percent = 100;
  46. }
  47. if (onChange) {
  48. const val = Math.floor(percent * (maximum * 0.01));
  49. onChange(val);
  50. }
  51. }
  52. };
  53. const handleTouchMove = (event: Event): void => {
  54. event.preventDefault();
  55. getFingerMoveValue(event);
  56. };
  57. const handleTouchEnd = (event: MouseEvent): void => {
  58. event.preventDefault();
  59. setActive(false);
  60. if (mouseSubscription) mouseSubscription.unsubscribe();
  61. if (touchSubscription) touchSubscription.unsubscribe();
  62. };
  63. const handleMouseDown = (
  64. event: React.MouseEvent<HTMLElement> | React.TouchEvent<HTMLElement>,
  65. ): void => {
  66. event.preventDefault();
  67. getFingerMoveValue(event);
  68. setActive(true);
  69. mouseSubscription = fromEvent(document.body, 'mousemove')
  70. .pipe(throttleTime(35))
  71. .subscribe(handleTouchMove);
  72. touchSubscription = fromEvent(document.body, 'touchmove')
  73. .pipe(throttleTime(35))
  74. .subscribe(handleTouchMove);
  75. document.body.addEventListener('mouseup', handleTouchEnd);
  76. };
  77. const setPercent = (): void => {
  78. const percent = parseValueToPercent((defaultValue || value) / maximum);
  79. setValueState(percent);
  80. };
  81. useEffect(() => {
  82. setPercent();
  83. }, []);
  84. useEffect(() => {
  85. setPercent();
  86. }, [value]);
  87. return (
  88. <OuterWrapper>
  89. <Wrapper
  90. ref={sliderRef}
  91. data-testid="sliders"
  92. onMouseDown={handleMouseDown}
  93. onTouchStart={handleMouseDown}
  94. >
  95. <Rail track={valueState} />
  96. <Track data-testid="track" track={valueState} isActive={isActive} />
  97. </Wrapper>
  98. </OuterWrapper>
  99. );
  100. };
  101. export default Sliders;