@fluentui/react
Version:
Reusable React components for building web experiences.
327 lines • 18.3 kB
JavaScript
define(["require", "exports", "tslib", "react", "@fluentui/react-hooks", "@fluentui/utilities", "../../utilities/dom"], function (require, exports, tslib_1, React, react_hooks_1, utilities_1, dom_1) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.useSlider = exports.ONKEYDOWN_TIMEOUT_DURATION = void 0;
exports.ONKEYDOWN_TIMEOUT_DURATION = 1000;
var getClassNames = (0, utilities_1.classNamesFunction)();
var getSlotStyleFn = function (sty) {
return function (value) {
var _a;
return _a = {},
_a[sty] = "".concat(value, "%"),
_a;
};
};
var getPercent = function (value, sliderMin, sliderMax) {
return sliderMax === sliderMin ? 0 : ((value - sliderMin) / (sliderMax - sliderMin)) * 100;
};
var useComponentRef = function (props, sliderBoxRef, value, range) {
React.useImperativeHandle(props.componentRef, function () { return ({
get value() {
return value;
},
get range() {
return range;
},
focus: function () {
var _a;
(_a = sliderBoxRef.current) === null || _a === void 0 ? void 0 : _a.focus();
},
}); }, [range, sliderBoxRef, value]);
};
var useSlider = function (props, ref) {
var _a = props.step, step = _a === void 0 ? 1 : _a, className = props.className, _b = props.disabled, disabled = _b === void 0 ? false : _b, label = props.label, _c = props.max, max = _c === void 0 ? 10 : _c, _d = props.min, min = _d === void 0 ? 0 : _d, _e = props.showValue, showValue = _e === void 0 ? true : _e, _f = props.buttonProps, buttonProps = _f === void 0 ? {} : _f, _g = props.vertical, vertical = _g === void 0 ? false : _g, snapToStep = props.snapToStep, valueFormat = props.valueFormat, styles = props.styles, theme = props.theme, originFromZero = props.originFromZero, ariaLabelledBy = props["aria-labelledby"], _h = props.ariaLabel, ariaLabel = _h === void 0 ? props['aria-label'] : _h, ranged = props.ranged, onChange = props.onChange, onChanged = props.onChanged;
var disposables = React.useRef([]);
var _j = (0, react_hooks_1.useSetTimeout)(), setTimeout = _j.setTimeout, clearTimeout = _j.clearTimeout;
var sliderLine = React.useRef(null);
var win = (0, dom_1.useWindowEx)();
// Casting here is necessary because useControllableValue expects the event for the change callback
// to extend React.SyntheticEvent, when in fact for Slider, the event could be either a React event
// or a native browser event depending on the context.
var _k = (0, react_hooks_1.useControllableValue)(props.value, props.defaultValue, function (ev, v) {
return onChange === null || onChange === void 0 ? void 0 : onChange(v, ranged ? [internalState.latestLowerValue, v] : undefined, ev);
}), unclampedValue = _k[0], setValue = _k[1];
var _l = (0, react_hooks_1.useControllableValue)(props.lowerValue, props.defaultLowerValue, function (ev, lv) { return onChange === null || onChange === void 0 ? void 0 : onChange(internalState.latestValue, [lv, internalState.latestValue], ev); }), unclampedLowerValue = _l[0], setLowerValue = _l[1];
// Ensure that value is always a number and is clamped by min/max.
var value = Math.max(min, Math.min(max, unclampedValue || 0));
var lowerValue = Math.max(min, Math.min(value, unclampedLowerValue || 0));
var internalState = (0, react_hooks_1.useConst)({
onKeyDownTimer: -1,
isAdjustingLowerValue: false,
latestValue: value,
latestLowerValue: lowerValue,
});
// On each render, update this saved value used by callbacks. (This should be safe even if render
// is called multiple times, because an event handler or timeout callback will only run once.)
internalState.latestValue = value;
internalState.latestLowerValue = lowerValue;
var id = (0, react_hooks_1.useId)('Slider', props.id || (buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.id));
var classNames = getClassNames(styles, {
className: className,
disabled: disabled,
vertical: vertical,
showTransitions: !snapToStep && !internalState.isBetweenSteps,
showValue: showValue,
ranged: ranged,
theme: theme,
});
var steps = (max - min) / step;
var clearOnKeyDownTimer = function () {
clearTimeout(internalState.onKeyDownTimer);
internalState.onKeyDownTimer = -1;
};
var setOnKeyDownTimer = function (event) {
clearOnKeyDownTimer();
if (onChanged) {
internalState.onKeyDownTimer = setTimeout(function () {
onChanged(event, internalState.latestValue, ranged ? [internalState.latestLowerValue, internalState.latestValue] : undefined);
}, exports.ONKEYDOWN_TIMEOUT_DURATION);
}
};
var getAriaValueText = function (valueProps) {
var ariaValueText = props.ariaValueText;
if (valueProps !== undefined) {
return ariaValueText ? ariaValueText(valueProps) : valueProps.toString();
}
return undefined;
};
/**
* Update `value` or `lowerValue`, including clamping between min/max and rounding to
* appropriate precision.
* @param newValue - New current value of the slider, possibly rounded to a whole step.
* @param newUnroundedValue - Like `newValue` but without the rounding to a step. If this is
* provided and not equal to `newValue`, `internalState.isBetweenSteps` will be set, which
* may cause thumb movement animations to be disabled.
*/
var updateValue = function (ev, newValue, newUnroundedValue) {
newValue = Math.min(max, Math.max(min, newValue));
newUnroundedValue = newUnroundedValue !== undefined ? Math.min(max, Math.max(min, newUnroundedValue)) : undefined;
var numDec = 0;
if (isFinite(step)) {
while (Math.round(step * Math.pow(10, numDec)) / Math.pow(10, numDec) !== step) {
numDec++;
}
}
// Make sure value has correct number of decimal places based on number of decimals in step
var roundedValue = parseFloat(newValue.toFixed(numDec));
internalState.isBetweenSteps = newUnroundedValue !== undefined && newUnroundedValue !== roundedValue;
if (ranged) {
// decided which thumb value to change
if (internalState.isAdjustingLowerValue &&
(originFromZero ? roundedValue <= 0 : roundedValue <= internalState.latestValue)) {
setLowerValue(roundedValue, ev);
}
else if (!internalState.isAdjustingLowerValue &&
(originFromZero ? roundedValue >= 0 : roundedValue >= internalState.latestLowerValue)) {
setValue(roundedValue, ev);
}
}
else {
setValue(roundedValue, ev);
}
};
var onKeyDown = function (event) {
var newCurrentValue = internalState.isAdjustingLowerValue
? internalState.latestLowerValue
: internalState.latestValue;
var diff = 0;
// eslint-disable-next-line @typescript-eslint/no-deprecated
switch (event.which) {
case (0, utilities_1.getRTLSafeKeyCode)(utilities_1.KeyCodes.left, props.theme):
case utilities_1.KeyCodes.down:
diff = -step;
clearOnKeyDownTimer();
setOnKeyDownTimer(event);
break;
case (0, utilities_1.getRTLSafeKeyCode)(utilities_1.KeyCodes.right, props.theme):
case utilities_1.KeyCodes.up:
diff = step;
clearOnKeyDownTimer();
setOnKeyDownTimer(event);
break;
case utilities_1.KeyCodes.home:
newCurrentValue = min;
clearOnKeyDownTimer();
setOnKeyDownTimer(event);
break;
case utilities_1.KeyCodes.end:
newCurrentValue = max;
clearOnKeyDownTimer();
setOnKeyDownTimer(event);
break;
default:
return;
}
updateValue(event, newCurrentValue + diff);
event.preventDefault();
event.stopPropagation();
};
var getPosition = function (event, verticalProp) {
var currentPosition = 0;
switch (event.type) {
case 'mousedown':
case 'mousemove':
currentPosition = !verticalProp ? event.clientX : event.clientY;
break;
case 'touchstart':
case 'touchmove':
currentPosition = !verticalProp
? event.touches[0].clientX
: event.touches[0].clientY;
break;
}
return currentPosition;
};
var calculateCurrentSteps = function (event) {
// eslint-disable-next-line @typescript-eslint/no-deprecated
var sliderPositionRect = sliderLine.current.getBoundingClientRect();
var sliderLength = !props.vertical ? sliderPositionRect.width : sliderPositionRect.height;
var stepLength = sliderLength / steps;
var currentSteps;
var distance;
if (!props.vertical) {
var left = getPosition(event, props.vertical);
distance = (0, utilities_1.getRTL)(props.theme) ? sliderPositionRect.right - left : left - sliderPositionRect.left;
currentSteps = distance / stepLength;
}
else {
var bottom = getPosition(event, props.vertical);
distance = sliderPositionRect.bottom - bottom;
currentSteps = distance / stepLength;
}
return currentSteps;
};
var onMouseMoveOrTouchMove = function (event, suppressEventCancelation) {
var currentSteps = calculateCurrentSteps(event);
var newUnroundedValue = min + step * currentSteps;
var newCurrentValue = min + step * Math.round(currentSteps);
updateValue(event, newCurrentValue, newUnroundedValue);
if (!suppressEventCancelation) {
event.preventDefault();
event.stopPropagation();
}
};
var onMouseDownOrTouchStart = function (event) {
if (ranged) {
var currentSteps = calculateCurrentSteps(event);
var newValue = min + step * currentSteps;
internalState.isAdjustingLowerValue =
newValue <= internalState.latestLowerValue ||
newValue - internalState.latestLowerValue <= internalState.latestValue - newValue;
}
// safe to use `win!` since it can only be called on the client
if (event.type === 'mousedown') {
disposables.current.push((0, utilities_1.on)(win, 'mousemove', onMouseMoveOrTouchMove, true), (0, utilities_1.on)(win, 'mouseup', onMouseUpOrTouchEnd, true));
}
else if (event.type === 'touchstart') {
disposables.current.push((0, utilities_1.on)(win, 'touchmove', onMouseMoveOrTouchMove, true), (0, utilities_1.on)(win, 'touchend', onMouseUpOrTouchEnd, true));
}
onMouseMoveOrTouchMove(event, true);
};
var onMouseUpOrTouchEnd = function (event) {
// Done adjusting, so clear this value
internalState.isBetweenSteps = undefined;
onChanged === null || onChanged === void 0 ? void 0 : onChanged(event, internalState.latestValue, ranged ? [internalState.latestLowerValue, internalState.latestValue] : undefined);
disposeListeners();
};
var onThumbFocus = function (event) {
internalState.isAdjustingLowerValue = event.target === lowerValueThumbRef.current;
};
var disposeListeners = React.useCallback(function () {
disposables.current.forEach(function (dispose) { return dispose(); });
disposables.current = [];
}, []);
React.useEffect(function () { return disposeListeners; }, [disposeListeners]);
var lowerValueThumbRef = React.useRef(null);
var thumbRef = React.useRef(null);
var sliderBoxRef = React.useRef(null);
useComponentRef(props, sliderBoxRef, value, ranged ? [lowerValue, value] : undefined);
var getPositionStyles = getSlotStyleFn(vertical ? 'bottom' : (0, utilities_1.getRTL)(props.theme) ? 'right' : 'left');
var getTrackStyles = getSlotStyleFn(vertical ? 'height' : 'width');
var originValue = originFromZero ? 0 : min;
var valuePercent = getPercent(value, min, max);
var lowerValuePercent = getPercent(lowerValue, min, max);
var originPercentOfLine = getPercent(originValue, min, max);
var activeSectionWidth = ranged ? valuePercent - lowerValuePercent : Math.abs(originPercentOfLine - valuePercent);
var topSectionWidth = Math.min(100 - valuePercent, 100 - originPercentOfLine);
var bottomSectionWidth = ranged ? lowerValuePercent : Math.min(valuePercent, originPercentOfLine);
var rootProps = {
className: classNames.root,
ref: ref,
};
var labelProps = {
className: classNames.titleLabel,
children: label,
disabled: disabled,
htmlFor: ariaLabel ? undefined : id,
};
var valueLabelProps = showValue
? {
className: classNames.valueLabel,
children: valueFormat ? valueFormat(value) : value,
disabled: disabled,
htmlFor: disabled ? id : undefined,
}
: undefined;
var lowerValueLabelProps = ranged && showValue
? {
className: classNames.valueLabel,
children: valueFormat ? valueFormat(lowerValue) : lowerValue,
disabled: disabled,
}
: undefined;
var zeroTickProps = originFromZero
? {
className: classNames.zeroTick,
style: getPositionStyles(originPercentOfLine),
}
: undefined;
var trackActiveProps = {
className: (0, utilities_1.css)(classNames.lineContainer, classNames.activeSection),
style: getTrackStyles(activeSectionWidth),
};
var trackTopInactiveProps = {
className: (0, utilities_1.css)(classNames.lineContainer, classNames.inactiveSection),
style: getTrackStyles(topSectionWidth),
};
var trackBottomInactiveProps = {
className: (0, utilities_1.css)(classNames.lineContainer, classNames.inactiveSection),
style: getTrackStyles(bottomSectionWidth),
};
var sliderProps = tslib_1.__assign({ 'aria-disabled': disabled, role: 'slider', tabIndex: disabled ? undefined : 0 }, { 'data-is-focusable': !disabled });
var sliderBoxProps = tslib_1.__assign(tslib_1.__assign(tslib_1.__assign({ id: id, className: (0, utilities_1.css)(classNames.slideBox, buttonProps.className), ref: sliderBoxRef }, (!disabled && {
onMouseDown: onMouseDownOrTouchStart,
onTouchStart: onMouseDownOrTouchStart,
onKeyDown: onKeyDown,
})), (buttonProps &&
(0, utilities_1.getNativeProps)(buttonProps, utilities_1.divProperties, ['id', 'className']))), (!ranged && tslib_1.__assign(tslib_1.__assign({}, sliderProps), { 'aria-valuemin': min, 'aria-valuemax': max, 'aria-valuenow': value, 'aria-valuetext': getAriaValueText(value), 'aria-label': ariaLabel || label, 'aria-labelledby': ariaLabelledBy })));
var onFocusProp = disabled ? {} : { onFocus: onThumbFocus };
var thumbProps = tslib_1.__assign({ ref: thumbRef, className: classNames.thumb, style: getPositionStyles(valuePercent) }, (ranged && tslib_1.__assign(tslib_1.__assign(tslib_1.__assign({}, sliderProps), onFocusProp), { id: "max-".concat(id), 'aria-valuemin': lowerValue, 'aria-valuemax': max, 'aria-valuenow': value, 'aria-valuetext': getAriaValueText(value), 'aria-label': "max ".concat(ariaLabel || label) })));
var lowerValueThumbProps = ranged
? tslib_1.__assign(tslib_1.__assign(tslib_1.__assign({ ref: lowerValueThumbRef, className: classNames.thumb, style: getPositionStyles(lowerValuePercent) }, sliderProps), onFocusProp), { id: "min-".concat(id), 'aria-valuemin': min, 'aria-valuemax': value, 'aria-valuenow': lowerValue, 'aria-valuetext': getAriaValueText(lowerValue), 'aria-label': "min ".concat(ariaLabel || label) }) : undefined;
var containerProps = {
className: classNames.container,
};
var sliderLineProps = {
ref: sliderLine,
className: classNames.line,
};
return {
root: rootProps,
label: labelProps,
sliderBox: sliderBoxProps,
container: containerProps,
valueLabel: valueLabelProps,
lowerValueLabel: lowerValueLabelProps,
thumb: thumbProps,
lowerValueThumb: lowerValueThumbProps,
zeroTick: zeroTickProps,
activeTrack: trackActiveProps,
topInactiveTrack: trackTopInactiveProps,
bottomInactiveTrack: trackBottomInactiveProps,
sliderLine: sliderLineProps,
};
};
exports.useSlider = useSlider;
});
//# sourceMappingURL=useSlider.js.map