@massds/mayflower-react
Version:
React versions of Mayflower design system UI components
343 lines • 13.2 kB
JavaScript
function _inheritsLoose(t, o) { t.prototype = Object.create(o.prototype), t.prototype.constructor = t, _setPrototypeOf(t, o); }
function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); }
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
/**
* CompoundSlider module.
* @module @massds/mayflower-react/CompoundSlider
*/
import React from "react";
import PropTypes from "prop-types";
import classNames from "classnames";
import numbro from "numbro";
import { Slider, Rail, Handles, Tracks, Ticks } from "react-compound-slider/es";
import { InputContext } from "../Input/context.mjs";
import { countDecimals } from "../Input/utility.mjs";
const Handle = props => {
const _props$handle = props.handle,
id = _props$handle.id,
value = _props$handle.value,
percent = _props$handle.percent,
getHandleProps = props.getHandleProps,
axis = props.axis,
min = props.min,
max = props.max,
step = props.step,
displayValueFormat = props.displayValueFormat,
disabled = props.disabled;
const decimalPlaces = countDecimals(step);
const roundedValue = Number.isInteger(step) ? value : Number(Number.parseFloat(value).toFixed(decimalPlaces));
const divProps = _extends({
'aria-valuemin': min,
'aria-valuemax': max,
'aria-valuenow': roundedValue,
disabled: disabled,
role: 'slider',
onClick: e => {
e.preventDefault();
}
}, getHandleProps(id));
if (axis === 'x') {
divProps.style = {
left: percent + "%"
};
} else if (axis === 'y') {
divProps.style = {
top: percent + "%"
};
}
return /*#__PURE__*/React.createElement("button", _extends({
type: "button",
className: "ma__slider-handle"
}, divProps), displayValueFormat && /*#__PURE__*/React.createElement("div", {
className: "ma__slider-handle-value"
}, displayValueFormat === 'percentage' ? numbro(value).format({
output: 'percent',
mantissa: 0
}) : roundedValue));
};
Handle.propTypes = process.env.NODE_ENV !== "production" ? {
handle: {
id: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
percent: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
},
getHandleProps: PropTypes.func,
axis: PropTypes.string,
min: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
max: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
step: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
displayValueFormat: PropTypes.string,
disabled: PropTypes.bool
} : {};
const Track = props => {
const source = props.source,
target = props.target,
getTrackProps = props.getTrackProps,
axis = props.axis;
const divProps = _extends({}, getTrackProps());
if (axis === 'x') {
divProps.style = {
left: source.percent + "%",
width: target.percent - source.percent + "%"
};
} else if (axis === 'y') {
divProps.style = {
top: source.percent + "%",
height: target.percent - source.percent + "%"
};
}
return /*#__PURE__*/React.createElement("div", _extends({
className: "ma__slider-track"
}, divProps));
};
Track.propTypes = process.env.NODE_ENV !== "production" ? {
source: {
percent: PropTypes.string
},
target: {
percent: PropTypes.string
},
getTrackProps: PropTypes.func,
axis: PropTypes.string
} : {};
const Tick = props => {
const tick = props.tick,
count = props.count,
axis = props.axis,
id = props.id;
const top = {};
const bottom = {};
if (axis === 'x') {
top.style = {
left: tick.percent + "%"
};
bottom.style = {
marginLeft: -(100 / count) / 2 + "%",
width: 100 / count + "%",
left: tick.percent + "%"
};
} else if (axis === 'y') {
top.style = {
top: tick.percent + "%"
};
bottom.style = {
top: tick.percent + "%"
};
}
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", _extends({
className: "ma__slider-tick ma__slider-tick--top"
}, top)), /*#__PURE__*/React.createElement("div", _extends({
className: "ma__slider-tick ma__slider-tick--bottom"
}, bottom), /*#__PURE__*/React.createElement("label", {
htmlFor: id
}, tick.value)));
};
Tick.propTypes = process.env.NODE_ENV !== "production" ? {
tick: {
percent: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
},
count: PropTypes.number,
getTrackProps: PropTypes.func,
id: PropTypes.string,
axis: PropTypes.string
} : {};
let CompoundSlider = /*#__PURE__*/function (_React$Component) {
function CompoundSlider() {
return _React$Component.apply(this, arguments) || this;
}
_inheritsLoose(CompoundSlider, _React$Component);
var _proto = CompoundSlider.prototype;
_proto.render = function render() {
return /*#__PURE__*/React.createElement(InputContext.Consumer, null, context => {
const _this$props = this.props,
min = _this$props.min,
max = _this$props.max,
step = _this$props.step,
disabled = _this$props.disabled,
domain = _this$props.domain,
onChange = _this$props.onChange,
onUpdate = _this$props.onUpdate;
const decimalPlaces = countDecimals(step);
const handleChange = values => {
const value = Number.isInteger(step) ? values[0] : Number(Number.parseFloat(values[0]).toFixed(decimalPlaces));
context.updateState({
value: value
}, () => {
if (typeof onChange === 'function') {
onChange(value, this.props.id);
}
});
};
const handleUpdate = values => {
const value = Number.isInteger(step) ? values[0] : Number(Number.parseFloat(values[0]).toFixed(decimalPlaces));
context.updateState({
value: value
}, () => {
if (typeof onUpdate === 'function') {
onUpdate(value, this.props.id);
}
});
};
const domainCheck = valToCheck => {
let minCheck = Number(min);
let maxCheck = Number(max);
if (Number.isNaN(valToCheck)) {
return Number.isInteger(step) ? minCheck : Number(Number.parseFloat(minCheck).toFixed(decimalPlaces));
}
let returnValue = valToCheck;
const domainMin = domain[0],
domainMax = domain[1];
// If the min/max passed falls outside of the domain, set it to the respective domain min/max.
if (Number.isNaN(minCheck) || Math.abs(minCheck - domainMax) > Math.abs(domainMin - domainMax)) {
minCheck = domainMin;
}
if (Number.isNaN(maxCheck) || Math.abs(maxCheck - domainMin) > Math.abs(domainMin - domainMax)) {
maxCheck = domainMax;
}
// Ensure the value is always between the min or max values, if any.
if (valToCheck < minCheck) {
returnValue = minCheck;
}
if (valToCheck > maxCheck) {
returnValue = maxCheck;
}
return Number.isInteger(step) ? returnValue : Number(Number.parseFloat(returnValue).toFixed(decimalPlaces));
};
// Anything returned by mode when set to a function will become the value.
// This can be used for min/max validation.
// Next and current values are not numbers, but arrays of objects.
const handleMode = (current, next) => {
const nextValue = next[0].val;
const checkValue = Number.isInteger(step) ? nextValue : Number(Number.parseFloat(nextValue).toFixed(decimalPlaces));
if (checkValue === domainCheck(nextValue)) {
return next;
}
return current;
};
const defaultValue = domainCheck(Number(context.getValue()));
const sliderProps = {
domain: domain,
step: step,
vertical: !(this.props.axis === 'x'),
onChange: handleChange,
values: [defaultValue],
mode: handleMode,
disabled: disabled
};
if (onUpdate) {
sliderProps.onUpdate = handleUpdate;
}
const wrapperClasses = classNames({
'ma__input-slider': true,
'ma__input-slider--disabled': disabled,
'ma__input-slider-x': this.props.axis === 'x',
'ma__input-slider-y': this.props.axis === 'y'
});
return /*#__PURE__*/React.createElement("div", {
id: this.props.id,
className: wrapperClasses
}, /*#__PURE__*/React.createElement(Slider, _extends({
className: "ma__slider"
}, sliderProps), /*#__PURE__*/React.createElement(Rail, null, _ref => {
let getRailProps = _ref.getRailProps;
return /*#__PURE__*/React.createElement("div", _extends({
className: "ma__slider-rail"
}, getRailProps()));
}), /*#__PURE__*/React.createElement(Handles, null, _ref2 => {
let handles = _ref2.handles,
activeHandleID = _ref2.activeHandleID,
getHandleProps = _ref2.getHandleProps;
return /*#__PURE__*/React.createElement("div", {
className: "slider-handles"
}, handles.map(handle => /*#__PURE__*/React.createElement(Handle, {
key: handle.id,
handle: handle,
getHandleProps: getHandleProps,
isActive: handle.id === activeHandleID,
axis: this.props.axis,
min: min,
max: max,
step: step,
displayValueFormat: this.props.displayValueFormat,
disabled: disabled
})));
}), /*#__PURE__*/React.createElement(Tracks, {
right: false
}, _ref3 => {
let tracks = _ref3.tracks,
getTrackProps = _ref3.getTrackProps;
return /*#__PURE__*/React.createElement("div", {
className: "slider-tracks"
}, tracks.map(_ref4 => {
let id = _ref4.id,
source = _ref4.source,
target = _ref4.target;
return /*#__PURE__*/React.createElement(Track, {
key: id,
source: source,
target: target,
getTrackProps: getTrackProps,
axis: this.props.axis
});
}));
}), /*#__PURE__*/React.createElement(Ticks, {
values: Array.from(this.props.ticks.keys())
}, _ref5 => {
let ticks = _ref5.ticks;
const ticksLength = ticks.length;
// Placing this check here because Slider can't handle null children but Ticks can.
if (ticksLength > 0) {
return /*#__PURE__*/React.createElement("div", {
className: "slider-ticks"
}, ticks.map(oldTick => {
const tick = _extends({}, oldTick, {
value: this.props.ticks.get(oldTick.value)
});
const tickProps = {
key: "CompoundSlider.tick." + tick.value,
count: ticksLength,
tick: tick,
axis: this.props.axis,
id: this.props.id
};
return /*#__PURE__*/React.createElement(Tick, tickProps);
}));
}
return null;
})));
});
};
return CompoundSlider;
}(React.Component);
CompoundSlider.propTypes = process.env.NODE_ENV !== "production" ? {
/** The unique ID for the input field */
id: PropTypes.string.isRequired,
/** Custom update function, triggered with the values on drag (caution: high-volume updates when dragging). Only if a function is passed to onUpdate will form context get updated on drag. */
onUpdate: PropTypes.func,
/** Custom on change function, triggered when the value of the slider has changed. This will recieve changes at the end of a slide as well as changes from clicks on rails and tracks. */
onChange: PropTypes.func,
/** Default input text value */
defaultValue: PropTypes.string,
/** Max value for the field. */
max: PropTypes.number.isRequired,
/** Min value for the field. */
min: PropTypes.number.isRequired,
/** This controls how much sliding the handle increments/decrements the value of the slider. */
step: PropTypes.number,
/** A Map object where each entry is a key (number inclusively between min and max) and value (label to display at the key) pair for displaying tick marks. */
ticks: PropTypes.instanceOf(Map),
/** The direction for the slider, where x is horizontal and y is vertical. */
axis: PropTypes.oneOf(['x', 'y']),
/** Disables the slider if true. */
disabled: PropTypes.bool,
/** The range of numbers, inclusively, for the slider to fall between. First number is the min and second number is the max. */
domain: PropTypes.arrayOf(PropTypes.number),
/** Display the value of the slider based. If null, don't display. If equals percentage, format the value in percentage. */
displayValueFormat: PropTypes.oneOf(['percentage', 'value', null])
} : {};
CompoundSlider.defaultProps = {
ticks: new Map(),
domain: [0, 1]
};
export default CompoundSlider;