import { React, PropTypes, classnames } from '@gravityforms/libraries';
import { spacerClasses, uniqueId } from '@gravityforms/utils';
import Label from '../Label';
import HelpText from '../HelpText';
const { useState, forwardRef } = React;
/**
* @module Range
* @description A multi type input component that supports standard text, email, tel.
*
* @since 1.1.15
*
* @param {object} props Component props.
* @param {object} props.customAttributes Custom attributes for the component.
* @param {string|Array|object} props.customClasses Custom classes for the component.
* @param {boolean} props.disabled If input is disabled.
* @param {object} props.helpTextAttributes Custom attribute for the help text.
* @param {string} props.helpTextPosition The position of the help text.
* @param {string} props.id Optional id. Auto generated if not passed.
* @param {object} props.labelAttributes Any custom attributes for the label.
* @param {number} props.max Maximum value for the range.
* @param {number} props.min Minimum value for the range.
* @param {string} props.name The name attribute for the input.
* @param {Function} props.onBlur On blur function handler.
* @param {Function} props.onChange On change function handler.
* @param {Function} props.onFocus On focus function handler.
* @param {boolean} props.showValueInput Whether to display the value input or not.
* @param {string|number|Array|object} props.spacing The spacing for the component, as a string, number, array, or object.
* @param {number} props.step The step size of the range.
* @param {string} props.theme The theme of the range.
* @param {number} props.value The input value.
* @param {string} props.valueInputPosition The position of the value input, one of `before` or `after`.
* @param {string} props.valueInputSuffix Suffix for the value input.
* @param {object} props.wrapperAttributes Custom attributes for the wrapper element.
* @param {string|Array|object} props.wrapperClasses Custom classes for the wrapper element.
* @param {object} props.wrapperTagName Tag to use for the input wrapper. Defaults to `div`.
* @param {object|null} ref Ref to the component.
*
* @return {JSX.Element} The range component.
*
* @example
* import Range from '@gravityforms/components/react/admin/elements/Range';
*
* return (
* <Range
* max={ 100 }
* min={ 0 }
* name="range-name"
* onChange={ () => {} }
* step={ 5 }
* value={ 50 }
* />
* );
*
*/
const Range = forwardRef( ( {
customAttributes = {},
customClasses = [],
disabled = false,
helpTextAttributes = {
size: 'text-xs',
weight: 'regular',
},
helpTextPosition = 'below',
id = '',
labelAttributes = {
size: 'text-sm',
weight: 'medium',
},
max = 0,
min = 0,
name = '',
onBlur = () => {},
onChange = () => {},
onFocus = () => {},
showValueInput = false,
spacing = '',
step = 1,
theme = 'cosmos',
value = 0,
valueInputPosition = 'before',
valueInputSuffix = '',
wrapperAttributes = {},
wrapperClasses = [],
wrapperTagName = 'div',
}, ref ) => {
const [ inputValue, setInputValue ] = useState( value );
const inputId = id || uniqueId( 'input' );
const helpTextId = `${ inputId }-help-text`;
const wrapperProps = {
...wrapperAttributes,
className: classnames( {
'gform-input-wrapper': true,
[ `gform-input-wrapper--theme-${ theme }` ]: true,
'gform-input-wrapper--input': true,
'gform-input-wrapper--disabled': disabled,
...spacerClasses( spacing ),
}, wrapperClasses ),
ref,
};
const inputProps = {
...customAttributes,
className: classnames( {
'gform-input': true,
'gform-input--range': true,
'gform-input--theme-cosmos': true,
}, customClasses ),
id: inputId,
max,
min,
step,
name,
onBlur,
onChange: ( e ) => {
const { value: newInputValue } = e.target;
setInputValue( newInputValue );
onChange( newInputValue, e );
},
onFocus,
type: 'range',
value: inputValue,
};
if ( disabled ) {
inputProps.disabled = true;
}
const labelProps = {
...labelAttributes,
htmlFor: inputId,
};
const helpTextProps = {
...helpTextAttributes,
id: helpTextId,
};
const inputWrapperProps = {
className: classnames( {
'gform-input-range-wrapper': true,
} ),
};
const Container = wrapperTagName;
/**
* @function getValueInput
* @description Get the value input for the range component.
*
* @since 1.1.15
*
* @return {JSX.Element} The value input react markup.
*/
const getValueInput = () => {
const valueInputProps = {
className: classnames( {
'gform-input--range-value-input': true,
} ),
disabled,
max,
min,
onChange: ( e ) => {
const { value: newInputValue } = e.target;
setInputValue( newInputValue );
},
type: 'number',
value: inputValue,
};
const containerProps = {
className: classnames( {
'gform-input-range-value-wrapper': true,
} ),
};
return (
<div { ...containerProps }>
<input { ...valueInputProps } />
{ valueInputSuffix && <div className="gform-input--range__suffix">{ valueInputSuffix }</div> }
</div>
);
};
return (
<Container { ...wrapperProps }>
<Label { ...labelProps } />
{ helpTextPosition === 'above' && <HelpText { ...helpTextProps } /> }
<div { ...inputWrapperProps } >
{ showValueInput && valueInputPosition === 'before' && getValueInput() }
<input { ...inputProps } />
{ showValueInput && valueInputPosition === 'after' && getValueInput() }
</div>
{ helpTextPosition === 'below' && <HelpText { ...helpTextProps } /> }
</Container>
);
} );
Range.propTypes = {
customAttributes: PropTypes.object,
customClasses: PropTypes.oneOfType( [
PropTypes.string,
PropTypes.array,
PropTypes.object,
] ),
disabled: PropTypes.bool,
helpTextAttributes: PropTypes.object,
helpTextPosition: PropTypes.string,
id: PropTypes.string,
labelAttributes: PropTypes.object,
max: PropTypes.number,
min: PropTypes.number,
name: PropTypes.string,
onBlur: PropTypes.func,
onChange: PropTypes.func,
onFocus: PropTypes.func,
showValueInput: PropTypes.bool,
spacing: PropTypes.oneOfType( [
PropTypes.string,
PropTypes.number,
PropTypes.array,
PropTypes.object,
] ),
step: PropTypes.number,
theme: PropTypes.string,
value: PropTypes.number,
valueInputPosition: PropTypes.oneOf( [ 'before', 'after' ] ),
valueInputSuffix: PropTypes.string,
wrapperAttributes: PropTypes.object,
wrapperClasses: PropTypes.oneOfType( [
PropTypes.string,
PropTypes.array,
PropTypes.object,
] ),
wrapperTagName: PropTypes.string,
};
Range.displayName = 'Range';
export default Range;