@mantine/core
Version:
React components library focused on usability, accessibility and developer experience
1 lines • 9.93 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\nexport namespace AngleSlider {\n export type Props = AngleSliderProps;\n export type StylesNames = AngleSliderStylesNames;\n export type CssVariables = AngleSliderCssVariables;\n export type Factory = AngleSliderFactory;\n}\n"],"mappings":"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"}