UNPKG

@awsui/components-react

Version:

On July 19th, 2022, we launched [Cloudscape Design System](https://cloudscape.design). Cloudscape is an evolution of AWS-UI. It consists of user interface guidelines, front-end components, design resources, and development tools for building intuitive, en

130 lines • 8.14 kB
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import React, { useRef, useState } from 'react'; import clsx from 'clsx'; import { useUniqueId, warnOnce } from '@awsui/component-toolkit/internal'; import { useInternalI18n } from '../i18n/context'; import { getBaseProps } from '../internal/base-component/index.js'; import Tooltip from '../internal/components/tooltip/index.js'; import { useFormFieldContext } from '../internal/context/form-field-context.js'; import { fireNonCancelableEvent } from '../internal/events/index.js'; import customCssProps from '../internal/generated/custom-css-properties/index.js'; import SliderLabels from './slider-labels.js'; import SliderTickMarks from './tick-marks.js'; import { findLowerAndHigherValues, getPercent, getStepArray, THUMB_READONLY_SIZE, THUMB_SIZE, valuesAreValid, } from './utils.js'; import styles from './styles.css.js'; export default function InternalSlider({ value, min, max, onChange, step, disabled, readOnly, ariaLabel, ariaDescription, referenceValues, tickMarks, hideFillLine, valueFormatter, i18nStrings, __internalRootRef, ...rest }) { const baseProps = getBaseProps(rest); const i18n = useInternalI18n('slider'); const handleRef = useRef(null); const [showTooltip, setShowTooltip] = useState(false); const [isActive, setIsActive] = useState(false); const labelsId = useUniqueId('labels'); const { ariaLabelledby, ariaDescribedby, controlId, invalid, warning } = useFormFieldContext(rest); const showWarning = warning && !invalid; if (referenceValues && valuesAreValid(referenceValues) === false) { warnOnce('Slider', 'All reference values must be integers. Non-integer values will not be displayed.'); } if (min >= max) { warnOnce('Slider', 'The min value cannot be greater than the max value.'); } if (step && step > max - min) { warnOnce('Slider', 'The step value cannot be greater than the difference between the min and max.'); } if (step && value !== undefined && (value - min) % step !== 0) { warnOnce('Slider', 'Slider value must be a multiple of the step. The value will round to the nearest step value.'); } const getValue = () => { const stepIsValid = step && step < max - min && step > min; if (value === undefined) { // this is the default html input's fallback value return max < min ? min : min + (max - min) / 2; } if (!step) { return value; } // if the value is not a multiple of the step, then find the closest step // and make that the value (this is also the native input behavior) if (step && stepIsValid && (value - min) % step !== 0) { const closest = getStepArray(step, [min, max]).reduce(function (prev, curr) { return Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev; }); return closest; } return value; }; const sliderValue = getValue(); const percent = getPercent(Math.max(Math.min(sliderValue, max), min), [min, max]); const getAriaValueText = () => { if (valueFormatter && valueFormatter(sliderValue)) { return valueFormatter(sliderValue); } if (valueFormatter && !valueFormatter(sliderValue)) { const middleValues = referenceValues ? referenceValues : []; const valueArray = [min, ...middleValues, sliderValue, max]; const prevAndNext = findLowerAndHigherValues(valueArray, sliderValue); const previousValue = prevAndNext.lower ? valueFormatter(prevAndNext.lower) : valueFormatter(min); const nextValue = prevAndNext.higher ? valueFormatter(prevAndNext.higher) : valueFormatter(max); const value = sliderValue; return i18n('i18nStrings.valueTextRange', i18nStrings === null || i18nStrings === void 0 ? void 0 : i18nStrings.valueTextRange(previousValue, value, nextValue), format => format({ value, previousValue, nextValue })); } return undefined; }; const thumbSize = readOnly ? THUMB_READONLY_SIZE : THUMB_SIZE; return (React.createElement("div", { ...baseProps, ref: __internalRootRef, className: clsx(baseProps.className, styles.root) }, React.createElement("div", { onMouseEnter: () => { setShowTooltip(true); }, onMouseLeave: () => { setShowTooltip(false); } }, showTooltip && (React.createElement(Tooltip, { value: valueFormatter ? valueFormatter(sliderValue) : sliderValue, trackRef: handleRef, onDismiss: () => setShowTooltip(false) })), React.createElement("div", { ref: handleRef, className: clsx(styles['tooltip-thumb'], { [styles.readonly]: readOnly, }), style: { [customCssProps.sliderTooltipPosition]: `calc(${percent}% - ${thumbSize}px)`, } }), React.createElement("div", { className: styles.slider }, React.createElement("div", { className: clsx(styles['slider-track'], { [styles.disabled]: disabled, [styles.readonly]: readOnly, }) }), !hideFillLine && (React.createElement("div", { className: clsx(styles['slider-range'], { [styles.error]: invalid, [styles.warning]: showWarning, [styles.active]: isActive, [styles['error-active']]: invalid && isActive, [styles['warning-active']]: showWarning && isActive, [styles.disabled]: disabled, [styles.readonly]: readOnly, }), style: { [customCssProps.sliderRangeInlineSize]: `${percent}%` } }))), !!step && tickMarks && (React.createElement(SliderTickMarks, { hideFillLine: hideFillLine, disabled: disabled, readOnly: readOnly, invalid: invalid, warning: warning, isActive: isActive, step: step, min: min, max: max, value: sliderValue })), React.createElement("input", { // we need to add this because input[type=range] isn't natively focusable in Safari. tabIndex: 0, "aria-label": ariaLabel, "aria-labelledby": ariaLabel && !rest.ariaLabelledby ? undefined : ariaLabelledby, "aria-describedby": valueFormatter ? (ariaDescribedby ? `${labelsId} ${ariaDescribedby}` : labelsId) : ariaDescribedby, "aria-valuetext": getAriaValueText(), "aria-invalid": invalid ? 'true' : undefined, "aria-disabled": readOnly && !disabled ? 'true' : undefined, id: controlId, type: "range", min: min, max: max, disabled: disabled, onFocus: () => { setShowTooltip(true); setIsActive(true); }, onBlur: () => { setShowTooltip(false); setIsActive(false); }, onTouchStart: () => { setShowTooltip(true); setIsActive(true); }, onTouchEnd: () => { setShowTooltip(false); setIsActive(false); }, step: step, value: sliderValue, onChange: event => { if (readOnly) { return; } fireNonCancelableEvent(onChange, { value: Number(event.target.value) }); }, className: clsx(styles.thumb, { [styles.error]: invalid, [styles.warning]: showWarning, [styles.disabled]: disabled, [styles.readonly]: readOnly, [styles.min]: sliderValue <= min || max < min, [styles.max]: sliderValue >= max && min < max, }) })), React.createElement(SliderLabels, { min: min, max: max, referenceValues: referenceValues, valueFormatter: valueFormatter, labelsId: labelsId, ariaDescription: ariaDescription }))); } //# sourceMappingURL=internal.js.map