From d3cf1c92121c1dc6702c3c30667b64f886608e71 Mon Sep 17 00:00:00 2001 From: Kamino0 <40913082+Kamino0@users.noreply.github.com> Date: Wed, 17 Jan 2024 09:20:26 +0300 Subject: [PATCH] [utils] Centralize clamp implementation in utils (#40267) Co-authored-by: Ragim Musakaev Co-authored-by: ZeeshanTamboli --- .../numberInputReducer.ts | 4 +-- .../unstable_useNumberInput/useNumberInput.ts | 4 +-- .../src/unstable_useNumberInput/utils.test.ts | 32 +++++++++---------- .../src/unstable_useNumberInput/utils.ts | 16 +++------- packages/mui-base/src/useSlider/useSlider.ts | 11 ++----- packages/mui-material/src/Rating/Rating.js | 12 +------ .../mui-material/src/SpeedDial/SpeedDial.js | 11 +------ packages/mui-system/src/colorManipulator.js | 11 ++++--- packages/mui-utils/src/clamp/clamp.test.ts | 10 ++++++ packages/mui-utils/src/clamp/clamp.ts | 9 ++++++ packages/mui-utils/src/clamp/index.ts | 1 + packages/mui-utils/src/index.ts | 1 + 12 files changed, 57 insertions(+), 65 deletions(-) create mode 100644 packages/mui-utils/src/clamp/clamp.test.ts create mode 100644 packages/mui-utils/src/clamp/clamp.ts create mode 100644 packages/mui-utils/src/clamp/index.ts diff --git a/packages/mui-base/src/unstable_useNumberInput/numberInputReducer.ts b/packages/mui-base/src/unstable_useNumberInput/numberInputReducer.ts index f3fbc18ba9fbfd..0ccb4c8748c73a 100644 --- a/packages/mui-base/src/unstable_useNumberInput/numberInputReducer.ts +++ b/packages/mui-base/src/unstable_useNumberInput/numberInputReducer.ts @@ -5,13 +5,13 @@ import { StepDirection, } from './useNumberInput.types'; import { NumberInputActionTypes } from './numberInputAction.types'; -import { clamp, isNumber } from './utils'; +import { clampStepwise, isNumber } from './utils'; // extracted from handleValueChange function getClampedValues(rawValue: number | undefined, context: NumberInputActionContext) { const { min, max, step } = context; - const clampedValue = rawValue === undefined ? '' : clamp(rawValue, min, max, step); + const clampedValue = rawValue === undefined ? '' : clampStepwise(rawValue, min, max, step); const newInputValue = clampedValue === undefined ? '' : String(clampedValue); diff --git a/packages/mui-base/src/unstable_useNumberInput/useNumberInput.ts b/packages/mui-base/src/unstable_useNumberInput/useNumberInput.ts index 7d01f9b15f2f96..e5ee54ca792c53 100644 --- a/packages/mui-base/src/unstable_useNumberInput/useNumberInput.ts +++ b/packages/mui-base/src/unstable_useNumberInput/useNumberInput.ts @@ -19,7 +19,7 @@ import { UseNumberInputReturnValue, StepDirection, } from './useNumberInput.types'; -import { clamp, isNumber } from './utils'; +import { clampStepwise, isNumber } from './utils'; const STEP_KEYS = ['ArrowUp', 'ArrowDown', 'PageUp', 'PageDown']; @@ -132,7 +132,7 @@ export function useNumberInput(parameters: UseNumberInputParameters): UseNumberI newValue = val; setDirtyValue(''); } else { - newValue = clamp(val, min, max, step); + newValue = clampStepwise(val, min, max, step); setDirtyValue(String(newValue)); } diff --git a/packages/mui-base/src/unstable_useNumberInput/utils.test.ts b/packages/mui-base/src/unstable_useNumberInput/utils.test.ts index 213294e133bf95..c01da437fbd511 100644 --- a/packages/mui-base/src/unstable_useNumberInput/utils.test.ts +++ b/packages/mui-base/src/unstable_useNumberInput/utils.test.ts @@ -1,24 +1,24 @@ import { expect } from 'chai'; -import { clamp, isNumber } from './utils'; +import { clampStepwise, isNumber } from './utils'; describe('utils', () => { - it('clamp: clamps a value based on min and max', () => { - expect(clamp(1, 2, 4)).to.equal(2); - expect(clamp(5, 2, 4)).to.equal(4); - expect(clamp(-5, -1, 5)).to.equal(-1); + it('clampStepwise: clamps a value based on min and max', () => { + expect(clampStepwise(1, 2, 4)).to.equal(2); + expect(clampStepwise(5, 2, 4)).to.equal(4); + expect(clampStepwise(-5, -1, 5)).to.equal(-1); }); - it('clamp: clamps a value between min and max and on a valid step', () => { - expect(clamp(2, -15, 15, 3)).to.equal(3); - expect(clamp(-1, -15, 15, 3)).to.equal(0); - expect(clamp(5, -15, 15, 3)).to.equal(6); - expect(clamp(-5, -15, 15, 3)).to.equal(-6); - expect(clamp(-55, -15, 15, 3)).to.equal(-15); - expect(clamp(57, -15, 15, 3)).to.equal(15); - expect(clamp(3, -20, 20, 5)).to.equal(5); - expect(clamp(2, -20, 20, 5)).to.equal(0); - expect(clamp(8, -20, 20, 5)).to.equal(10); - expect(clamp(-7, -20, 20, 5)).to.equal(-5); + it('clampStepwise: clamps a value between min and max and on a valid step', () => { + expect(clampStepwise(2, -15, 15, 3)).to.equal(3); + expect(clampStepwise(-1, -15, 15, 3)).to.equal(0); + expect(clampStepwise(5, -15, 15, 3)).to.equal(6); + expect(clampStepwise(-5, -15, 15, 3)).to.equal(-6); + expect(clampStepwise(-55, -15, 15, 3)).to.equal(-15); + expect(clampStepwise(57, -15, 15, 3)).to.equal(15); + expect(clampStepwise(3, -20, 20, 5)).to.equal(5); + expect(clampStepwise(2, -20, 20, 5)).to.equal(0); + expect(clampStepwise(8, -20, 20, 5)).to.equal(10); + expect(clampStepwise(-7, -20, 20, 5)).to.equal(-5); }); it('isNumber: rejects NaN', () => { diff --git a/packages/mui-base/src/unstable_useNumberInput/utils.ts b/packages/mui-base/src/unstable_useNumberInput/utils.ts index c20907be5b683a..b9d44c06bd2fed 100644 --- a/packages/mui-base/src/unstable_useNumberInput/utils.ts +++ b/packages/mui-base/src/unstable_useNumberInput/utils.ts @@ -1,19 +1,13 @@ -function simpleClamp( - val: number, - min: number = Number.MIN_SAFE_INTEGER, - max: number = Number.MAX_SAFE_INTEGER, -): number { - return Math.max(min, Math.min(val, max)); -} +import { clamp } from '@mui/utils'; -export function clamp( +export function clampStepwise( val: number, min: number = Number.MIN_SAFE_INTEGER, max: number = Number.MAX_SAFE_INTEGER, stepProp: number = NaN, ): number { if (Number.isNaN(stepProp)) { - return simpleClamp(val, min, max); + return clamp(val, min, max); } const step = stepProp || 1; @@ -23,10 +17,10 @@ export function clamp( const positivity = Math.sign(remainder); if (Math.abs(remainder) > step / 2) { - return simpleClamp(val + positivity * (step - Math.abs(remainder)), min, max); + return clamp(val + positivity * (step - Math.abs(remainder)), min, max); } - return simpleClamp(val - positivity * Math.abs(remainder), min, max); + return clamp(val - positivity * Math.abs(remainder), min, max); } export function isNumber(val: unknown): val is number { diff --git a/packages/mui-base/src/useSlider/useSlider.ts b/packages/mui-base/src/useSlider/useSlider.ts index 805872a998b717..0cabfaf5039f93 100644 --- a/packages/mui-base/src/useSlider/useSlider.ts +++ b/packages/mui-base/src/useSlider/useSlider.ts @@ -8,6 +8,7 @@ import { unstable_useForkRef as useForkRef, unstable_useIsFocusVisible as useIsFocusVisible, visuallyHidden, + clamp, } from '@mui/utils'; import { Mark, @@ -25,13 +26,6 @@ function asc(a: number, b: number) { return a - b; } -function clamp(value: number, min: number, max: number) { - if (value == null) { - return min; - } - return Math.min(Math.max(min, value), max); -} - function findClosest(values: number[], currentValue: number) { const { index: closestIndex } = values.reduce<{ distance: number; index: number } | null>( @@ -259,7 +253,8 @@ export function useSlider(parameters: UseSliderParameters): UseSliderReturnValue const range = Array.isArray(valueDerived); let values = range ? valueDerived.slice().sort(asc) : [valueDerived]; - values = values.map((value) => clamp(value, min, max)); + values = values.map((value) => (value == null ? min : clamp(value, min, max))); + const marks = marksProp === true && step !== null ? [...Array(Math.floor((max - min) / step) + 1)].map((_, index) => ({ diff --git a/packages/mui-material/src/Rating/Rating.js b/packages/mui-material/src/Rating/Rating.js index 22b8f7c06e53a0..f409ed3485fd78 100644 --- a/packages/mui-material/src/Rating/Rating.js +++ b/packages/mui-material/src/Rating/Rating.js @@ -2,7 +2,7 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import clsx from 'clsx'; -import { chainPropTypes, visuallyHidden } from '@mui/utils'; +import { chainPropTypes, visuallyHidden, clamp } from '@mui/utils'; import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; import useTheme from '../styles/useTheme'; import { @@ -18,16 +18,6 @@ import useThemeProps from '../styles/useThemeProps'; import styled, { slotShouldForwardProp } from '../styles/styled'; import ratingClasses, { getRatingUtilityClass } from './ratingClasses'; -function clamp(value, min, max) { - if (value < min) { - return min; - } - if (value > max) { - return max; - } - return value; -} - function getDecimalPrecision(num) { const decimalPart = num.toString().split('.')[1]; return decimalPart ? decimalPart.length : 0; diff --git a/packages/mui-material/src/SpeedDial/SpeedDial.js b/packages/mui-material/src/SpeedDial/SpeedDial.js index 828243c4437aa5..f1e081fbe21d01 100644 --- a/packages/mui-material/src/SpeedDial/SpeedDial.js +++ b/packages/mui-material/src/SpeedDial/SpeedDial.js @@ -4,6 +4,7 @@ import { isFragment } from 'react-is'; import PropTypes from 'prop-types'; import clsx from 'clsx'; import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; +import { clamp } from '@mui/utils'; import styled from '../styles/styled'; import useThemeProps from '../styles/useThemeProps'; import useTheme from '../styles/useTheme'; @@ -37,16 +38,6 @@ function getOrientation(direction) { return undefined; } -function clamp(value, min, max) { - if (value < min) { - return min; - } - if (value > max) { - return max; - } - return value; -} - const dialRadius = 32; const spacingActions = 16; diff --git a/packages/mui-system/src/colorManipulator.js b/packages/mui-system/src/colorManipulator.js index 323951464bff25..1bbea01568563c 100644 --- a/packages/mui-system/src/colorManipulator.js +++ b/packages/mui-system/src/colorManipulator.js @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/naming-convention */ +import { clamp } from '@mui/utils'; import MuiError from '@mui-internal/babel-macros/MuiError.macro'; /** @@ -8,14 +9,14 @@ import MuiError from '@mui-internal/babel-macros/MuiError.macro'; * @param {number} max The upper boundary of the output range * @returns {number} A number in the range [min, max] */ -function clamp(value, min = 0, max = 1) { +function clampWrapper(value, min = 0, max = 1) { if (process.env.NODE_ENV !== 'production') { if (value < min || value > max) { console.error(`MUI: The value provided ${value} is out of range [${min}, ${max}].`); } } - return Math.min(Math.max(min, value), max); + return clamp(value, min, max); } /** @@ -238,7 +239,7 @@ export function getContrastRatio(foreground, background) { */ export function alpha(color, value) { color = decomposeColor(color); - value = clamp(value); + value = clampWrapper(value); if (color.type === 'rgb' || color.type === 'hsl') { color.type += 'a'; @@ -270,7 +271,7 @@ export function private_safeAlpha(color, value, warning) { */ export function darken(color, coefficient) { color = decomposeColor(color); - coefficient = clamp(coefficient); + coefficient = clampWrapper(coefficient); if (color.type.indexOf('hsl') !== -1) { color.values[2] *= 1 - coefficient; @@ -300,7 +301,7 @@ export function private_safeDarken(color, coefficient, warning) { */ export function lighten(color, coefficient) { color = decomposeColor(color); - coefficient = clamp(coefficient); + coefficient = clampWrapper(coefficient); if (color.type.indexOf('hsl') !== -1) { color.values[2] += (100 - color.values[2]) * coefficient; diff --git a/packages/mui-utils/src/clamp/clamp.test.ts b/packages/mui-utils/src/clamp/clamp.test.ts new file mode 100644 index 00000000000000..922c4665304c42 --- /dev/null +++ b/packages/mui-utils/src/clamp/clamp.test.ts @@ -0,0 +1,10 @@ +import { expect } from 'chai'; +import clamp from './clamp'; + +describe('clamp', () => { + it('clamps a value based on min and max', () => { + expect(clamp(1, 2, 4)).to.equal(2); + expect(clamp(5, 2, 4)).to.equal(4); + expect(clamp(-5, -1, 5)).to.equal(-1); + }); +}); diff --git a/packages/mui-utils/src/clamp/clamp.ts b/packages/mui-utils/src/clamp/clamp.ts new file mode 100644 index 00000000000000..4decd36f8adb13 --- /dev/null +++ b/packages/mui-utils/src/clamp/clamp.ts @@ -0,0 +1,9 @@ +function clamp( + val: number, + min: number = Number.MIN_SAFE_INTEGER, + max: number = Number.MAX_SAFE_INTEGER, +): number { + return Math.max(min, Math.min(val, max)); +} + +export default clamp; diff --git a/packages/mui-utils/src/clamp/index.ts b/packages/mui-utils/src/clamp/index.ts new file mode 100644 index 00000000000000..5937934e65e316 --- /dev/null +++ b/packages/mui-utils/src/clamp/index.ts @@ -0,0 +1 @@ +export { default } from './clamp'; diff --git a/packages/mui-utils/src/index.ts b/packages/mui-utils/src/index.ts index 91bd208d3a7288..5be17f53c79a33 100644 --- a/packages/mui-utils/src/index.ts +++ b/packages/mui-utils/src/index.ts @@ -42,3 +42,4 @@ export { isGlobalState as unstable_isGlobalState } from './generateUtilityClass' export * from './generateUtilityClass'; export { default as unstable_generateUtilityClasses } from './generateUtilityClasses'; export { default as unstable_ClassNameGenerator } from './ClassNameGenerator'; +export { default as clamp } from './clamp';