import {
createElement,
Component,
createRef,
Fragment
} from '@wordpress/element'
import classnames from 'classnames'
import linearScale from 'simple-linear-scale'
import OutsideClickHandler from './react-outside-click-handler'
const clamp = (min, max, value) => Math.max(min, Math.min(max, value))
const round = (value, decimalPlaces = 1) => {
// return Math.round(value)
const multiplier = Math.pow(10, decimalPlaces)
const rounded = Math.round(value * multiplier + Number.EPSILON) / multiplier
return rounded
}
const UnitsList = ({
option,
onChange,
is_open,
toggleOpen,
currentUnit,
getNumericValue,
getAllowedDecimalPlaces
}) => {
const pickUnit = unit => {
const numericValue = getNumericValue()
onChange(
`${round(
clamp(
option.units.find(({ unit: u }) => u === unit).min,
option.units.find(({ unit: u }) => u === unit).max,
numericValue === '' ? -Infinity : numericValue
),
getAllowedDecimalPlaces(unit)
)}${unit}`
)
}
return (
toggleOpen()} className="ct-current-value">
{currentUnit || '―'}
{
if (!is_open) {
return
}
toggleOpen()
}}>
{option.units
.filter(({ unit }) => unit !== currentUnit)
.reduce(
(current, el, index) => [
...current.slice(
0,
index % 2 === 0 ? undefined : -1
),
...(index % 2 === 0
? [[el]]
: [[current[current.length - 1][0], el]])
],
[]
)
.map(group => (
-
{group.map(({ unit }) => (
{
pickUnit(unit)
toggleOpen()
}}>
{unit || '―'}
))}
))}
)
}
export default class Slider extends Component {
state = {
is_dragging: false,
is_open: false
}
el = createRef()
hasUnitsList = () =>
this.props.option.units && this.props.option.units.length > 1
getAllowedDecimalPlaces = (properUnit = null) => {
const decimals = this.props.option.units
? this.props.option.units.find(
({ unit }) => unit === (properUnit || this.getCurrentUnit())
).decimals
: this.props.option.decimals
return decimals !== 0 && !decimals ? 0 : decimals
}
withDefault = (currentUnit, defaultUnit) =>
this.props.option.units
? this.props.option.units.find(({ unit }) => unit === currentUnit)
? currentUnit
: currentUnit || defaultUnit
: currentUnit || defaultUnit
getCurrentUnit = () =>
this.props.option.units
? this.withDefault(
this.props.value
.toString()
.replace(/[0-9]/g, '')
.replace(/\-/g, '')
.replace(/\./g, '')
.replace('CT_CSS_SKIP_RULE', ''),
this.props.option.units[0].unit
)
: ''
getMax = () =>
this.props.option.units
? this.props.option.units.find(
({ unit }) => unit === this.getCurrentUnit()
).max
: this.props.option.max
getMin = () =>
this.props.option.units
? this.props.option.units.find(
({ unit }) => unit === this.getCurrentUnit()
).min
: this.props.option.min
getNumericValue = () => {
const maybeValue = parseFloat(this.props.value, 10)
if (maybeValue === 0) {
return maybeValue
}
if (!maybeValue) {
return ''
}
return maybeValue
}
computeAndSendNewValue({ pageX }) {
let { top, left, width } = this.el.current.getBoundingClientRect()
this.props.onChange(
`${round(
linearScale(
[0, width],
[
parseFloat(this.getMin(), 10),
parseFloat(this.getMax(), 10)
],
true
)(pageX - left - pageXOffset),
this.getAllowedDecimalPlaces()
)}${this.getCurrentUnit()}`
)
}
handleMove = event => {
if (!this.state.is_dragging) return
this.computeAndSendNewValue(event)
}
handleUp = () => {
this.setState({
is_dragging: false
})
this.detachEvents()
}
handleBlur = () => {
if (this.props.option.value === 'CT_CSS_SKIP_RULE') {
if (this.props.value === 'CT_CSS_SKIP_RULE') {
return
}
if (this.getNumericValue() === '') {
this.props.onChange('CT_CSS_SKIP_RULE')
return
}
}
this.props.onChange(
`${clamp(
parseFloat(this.getMin(), 10),
parseFloat(this.getMax(), 10),
parseFloat(this.getNumericValue(), 10)
)}${this.getCurrentUnit()}`
)
}
handleChange = value => {
if (this.props.option.value === 'CT_CSS_SKIP_RULE') {
if (value.toString().trim() === '') {
this.props.onChange('CT_CSS_SKIP_RULE')
return
}
}
this.props.onChange(`${value || this.getMin()}${this.getCurrentUnit()}`)
}
attachEvents() {
document.documentElement.addEventListener(
'mousemove',
this.handleMove,
true
)
document.documentElement.addEventListener(
'mouseup',
this.handleUp,
true
)
}
detachEvents() {
document.documentElement.removeEventListener(
'mousemove',
this.handleMove,
true
)
document.documentElement.removeEventListener(
'mouseup',
this.handleUp,
true
)
}
render() {
const leftValue = `${linearScale(
[parseFloat(this.getMin(), 10), parseFloat(this.getMax(), 10)],
[0, 100]
)(
clamp(
parseFloat(this.getMin(), 10),
parseFloat(this.getMax(), 10),
parseFloat(this.getNumericValue(), 10) === 0
? 0
: parseFloat(this.getNumericValue(), 10)
? parseFloat(this.getNumericValue(), 10)
: parseFloat(this.getMin(), 10)
)
)}`
return (
{this.props.beforeOption && this.props.beforeOption()}
{
this.attachEvents()
this.setState({ is_dragging: true })
}}
onClick={e => this.computeAndSendNewValue(e)}
ref={this.el}
className="ct-slider"
{...(this.props.option.steps
? { ['data-steps']: '' }
: {})}>
{this.props.option.steps && (
)}
{!this.props.option.skipInput && (
this.handleBlur()}
onChange={({ target: { value } }) =>
this.handleChange(value)
}
/>
{!this.hasUnitsList() && (
{this.withDefault(
this.getCurrentUnit(),
this.props.option.defaultUnit || 'px'
)}
)}
{this.hasUnitsList() && (
this.setState({
is_open: !this.state.is_open
})
}
currentUnit={this.getCurrentUnit()}
getNumericValue={this.getNumericValue}
getAllowedDecimalPlaces={
this.getAllowedDecimalPlaces
}
/>
)}
)}
)
}
}