@mantine/core
Version:
React components library focused on usability, accessibility and developer experience
1 lines • 9.67 kB
Source Map (JSON)
{"version":3,"file":"AngleSlider.cjs","names":["createVarsResolver","rem","factory","useProps","findClosestNumber","useStyles","Box","classes"],"sources":["../../../src/components/AngleSlider/AngleSlider.tsx"],"sourcesContent":["import { useRef } from 'react';\nimport { normalizeRadialValue, useMergedRef, useRadialMove, useUncontrolled } from '@mantine/hooks';\nimport {\n Box,\n BoxProps,\n createVarsResolver,\n ElementProps,\n factory,\n Factory,\n findClosestNumber,\n rem,\n StylesApiProps,\n useProps,\n useStyles,\n} from '../../core';\nimport classes from './AngleSlider.module.css';\n\nexport type AngleSliderStylesNames = 'root' | 'thumb' | 'label' | 'marks' | 'mark';\nexport type AngleSliderCssVariables = {\n root: '--slider-size' | '--thumb-size';\n};\n\nexport interface AngleSliderProps\n extends BoxProps, StylesApiProps<AngleSliderFactory>, ElementProps<'div', 'onChange'> {\n /** Step between values @default 1 */\n step?: number;\n\n /** Controlled component value */\n value?: number;\n\n /** Uncontrolled component default value */\n defaultValue?: number;\n\n /** Called on value change */\n onChange?: (value: number) => void;\n\n /** Called after the selection is finished */\n onChangeEnd?: (value: number) => void;\n\n /** Called in `onMouseDown` and `onTouchStart` */\n onScrubStart?: () => void;\n\n /** Called in `onMouseUp` and `onTouchEnd` */\n onScrubEnd?: () => void;\n\n /** If set, the label is displayed inside the slider @default true */\n withLabel?: boolean;\n\n /** Array of marks displayed on the slider */\n marks?: { value: number; label?: string }[];\n\n /** Slider size in px @default 60 */\n size?: number;\n\n /** Size of the thumb in px. Calculated based on the `size` value by default. */\n thumbSize?: number;\n\n /** A function to format label based on the current value */\n formatLabel?: (value: number) => React.ReactNode;\n\n /** Sets `data-disabled` attribute, disables interactions */\n disabled?: boolean;\n\n /** If set, the selection is allowed only from the given marks array @default false */\n restrictToMarks?: boolean;\n\n /** Props passed down to the hidden input */\n hiddenInputProps?: React.ComponentProps<'input'>;\n\n /** Hidden input name, use with uncontrolled component */\n name?: string;\n}\n\nexport type AngleSliderFactory = Factory<{\n props: AngleSliderProps;\n ref: HTMLDivElement;\n stylesNames: AngleSliderStylesNames;\n vars: AngleSliderCssVariables;\n}>;\n\nconst defaultProps = {\n step: 1,\n withLabel: true,\n} satisfies Partial<AngleSliderProps>;\n\nconst varsResolver = createVarsResolver<AngleSliderFactory>((_, { size, thumbSize }) => ({\n root: {\n '--slider-size': rem(size),\n '--thumb-size': rem(thumbSize),\n },\n}));\n\nexport const AngleSlider = factory<AngleSliderFactory>((_props) => {\n const props = useProps('AngleSlider', defaultProps, _props);\n const {\n classNames,\n className,\n style,\n styles,\n unstyled,\n vars,\n step,\n value,\n defaultValue,\n onChange,\n onMouseDown,\n withLabel,\n marks,\n thumbSize,\n restrictToMarks,\n formatLabel,\n onChangeEnd,\n disabled,\n onTouchStart,\n name,\n hiddenInputProps,\n 'aria-label': ariaLabel,\n tabIndex,\n onScrubStart,\n onScrubEnd,\n mod,\n attributes,\n ref,\n ...others\n } = props;\n\n const rootRef = useRef<HTMLDivElement | null>(null);\n\n const [_value, setValue] = useUncontrolled({\n value,\n defaultValue,\n finalValue: 0,\n onChange,\n });\n\n const update = (val: number) => {\n if (rootRef.current && !disabled) {\n const newValue =\n restrictToMarks && Array.isArray(marks)\n ? findClosestNumber(\n val,\n marks.map((mark) => mark.value)\n )\n : val;\n\n setValue(newValue);\n }\n };\n\n const { ref: radialMoveRef } = useRadialMove(update, {\n step,\n onChangeEnd,\n onScrubStart,\n onScrubEnd,\n });\n\n const getStyles = useStyles<AngleSliderFactory>({\n name: 'AngleSlider',\n classes,\n props,\n className,\n style,\n classNames,\n styles,\n unstyled,\n attributes,\n vars,\n varsResolver,\n });\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (disabled) {\n return;\n }\n\n let newValue = _value;\n\n if (event.key === 'ArrowLeft' || event.key === 'ArrowDown') {\n event.preventDefault();\n if (_value === 0) {\n newValue = 359;\n } else {\n newValue = normalizeRadialValue(_value - step, step);\n }\n }\n\n if (event.key === 'ArrowRight' || event.key === 'ArrowUp') {\n event.preventDefault();\n if (_value === 359) {\n newValue = 0;\n } else {\n newValue = normalizeRadialValue(_value + step, step);\n }\n }\n\n if (event.key === 'Home') {\n newValue = 0;\n }\n\n if (event.key === 'End') {\n newValue = 359;\n }\n\n if (restrictToMarks && Array.isArray(marks)) {\n const markValues = marks.map((mark) => mark.value);\n const currentIndex = markValues.indexOf(_value);\n\n if (currentIndex !== -1) {\n if (event.key === 'ArrowLeft' || event.key === 'ArrowDown') {\n newValue = markValues[currentIndex === 0 ? markValues.length - 1 : currentIndex - 1];\n } else if (event.key === 'ArrowRight' || event.key === 'ArrowUp') {\n newValue = markValues[currentIndex === markValues.length - 1 ? 0 : currentIndex + 1];\n } else {\n newValue = findClosestNumber(newValue, markValues);\n }\n } else {\n newValue = findClosestNumber(newValue, markValues);\n }\n }\n\n setValue(newValue);\n onChangeEnd?.(newValue);\n };\n\n const marksItems = marks?.map((mark, index) => (\n <div\n {...getStyles('mark', { style: { '--angle': `${mark.value}deg` } })}\n data-label={mark.label || undefined}\n key={index}\n />\n ));\n\n return (\n <Box\n ref={useMergedRef(ref, rootRef, radialMoveRef)}\n {...getStyles('root', { focusable: true })}\n mod={[{ disabled }, mod]}\n {...others}\n >\n {marksItems && marksItems.length > 0 && <div {...getStyles('marks')}>{marksItems}</div>}\n\n {withLabel && (\n <div {...getStyles('label')}>\n {typeof formatLabel === 'function' ? formatLabel(_value) : _value}\n </div>\n )}\n <div\n tabIndex={tabIndex ?? (disabled ? -1 : 0)}\n role=\"slider\"\n aria-valuemax={360}\n aria-valuemin={0}\n aria-valuenow={_value}\n onKeyDown={handleKeyDown}\n aria-label={ariaLabel}\n {...getStyles('thumb', { style: { transform: `rotate(${_value}deg)` } })}\n />\n <input type=\"hidden\" name={name} value={_value} {...hiddenInputProps} />\n </Box>\n );\n});\n\nAngleSlider.displayName = '@mantine/core/AngleSlider';\nAngleSlider.classes = classes;\nAngleSlider.varsResolver = varsResolver;\n"],"mappings":"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"}