11// @flow
22
3+ import { useTimer } from '@performant-software/shared-components' ;
34import Slider from 'rc-slider' ;
45import React , {
56 forwardRef ,
7+ useCallback ,
68 useEffect ,
79 useState
810} from 'react' ;
9- import { Grid } from 'semantic-ui-react' ;
11+ import { Grid , Input } from 'semantic-ui-react' ;
12+ import _ from 'underscore' ;
1013import Facet , { type Props as FacetProps } from './Facet' ;
1114import { type RangeSliderProps } from '../types/InstantSearch' ;
12-
1315import './FacetSlider.css' ;
1416
15- type Props = FacetProps & RangeSliderProps ;
17+ type Props = FacetProps & RangeSliderProps & {
18+ editable ?: boolean
19+ } ;
20+
21+ const RADIX = 10 ;
1622
1723/**
1824 * This component can be used with the `useRange` hook from Instant Search Hooks.
@@ -29,9 +35,51 @@ const FacetSlider = forwardRef(({ useRangeSlider, ...props }: Props, ref: HTMLEl
2935
3036 const [ value , setValue ] = useState ( [ min , max ] ) ;
3137
38+ const { clearTimer, setTimer } = useTimer ( ) ;
39+
3240 const from = Math . max ( min , Number . isFinite ( start [ 0 ] ) ? start [ 0 ] : min ) ;
3341 const to = Math . min ( max , Number . isFinite ( start [ 1 ] ) ? start [ 1 ] : max ) ;
3442
43+ /**
44+ * Parses the input string to an integer.
45+ *
46+ * @type {function(*): number }
47+ */
48+ const getInputValue = useCallback ( ( str ) => {
49+ let inputValue = parseInt ( str , RADIX ) ;
50+
51+ if ( _ . isNaN ( inputValue ) ) {
52+ inputValue = str ;
53+ }
54+
55+ return inputValue ;
56+ } , [ ] ) ;
57+
58+ /**
59+ * Parses the input strings, sets the value on the state, and sets a timer to call "refine".
60+ *
61+ * @type {(function(*, *): void)|* }
62+ */
63+ const onChange = useCallback ( ( newStart , newEnd ) => {
64+ // Set the new value on the state
65+ const newValue = [
66+ getInputValue ( newStart ) ,
67+ getInputValue ( newEnd )
68+ ] ;
69+
70+ setValue ( newValue ) ;
71+
72+ // Use a timer to only refine the value when the user stops typing
73+ clearTimer ( ) ;
74+
75+ if ( _ . isNumber ( newValue [ 0 ] ) && _ . isNumber ( newValue [ 1 ] ) ) {
76+ setTimer ( ( ) => refine ( newValue ) ) ;
77+ }
78+ } , [ getInputValue , refine ] ) ;
79+
80+ /**
81+ * Sets the view value when to/from change.
82+ */
3583 useEffect ( ( ) => {
3684 setValue ( [ from , to ] ) ;
3785 } , [ from , to ] ) ;
@@ -66,19 +114,34 @@ const FacetSlider = forwardRef(({ useRangeSlider, ...props }: Props, ref: HTMLEl
66114 columns = { 2 }
67115 >
68116 < Grid . Column >
69- { value [ 0 ] }
117+ { ! props . editable && value [ 0 ] }
118+ { props . editable && (
119+ < Input
120+ onChange = { ( e , data ) => onChange ( data . value , value [ 1 ] ) }
121+ value = { value [ 0 ] }
122+ />
123+ ) }
70124 </ Grid . Column >
71125 < Grid . Column
72126 textAlign = 'right'
73127 >
74- { value [ 1 ] }
128+ { ! props . editable && value [ 1 ] }
129+ { props . editable && (
130+ < Input
131+ onChange = { ( e , data ) => onChange ( value [ 0 ] , data . value ) }
132+ value = { value [ 1 ] }
133+ />
134+ ) }
75135 </ Grid . Column >
76136 </ Grid >
77137 </ div >
78138 </ Facet >
79139 ) ;
80140} ) ;
81141
82- FacetSlider . defaultProps = Facet . defaultProps ;
142+ FacetSlider . defaultProps = {
143+ ...Facet . defaultProps ,
144+ editable : false
145+ } ;
83146
84147export default FacetSlider ;
0 commit comments