276 lines
10 KiB
JavaScript
276 lines
10 KiB
JavaScript
'use client';
|
|
import { makeStyles, mergeClasses } from '@griffel/react';
|
|
import { createFocusOutlineStyle } from '@fluentui/react-tabster';
|
|
import { tokens } from '@fluentui/react-theme';
|
|
export const sliderClassNames = {
|
|
root: 'fui-Slider',
|
|
rail: 'fui-Slider__rail',
|
|
thumb: 'fui-Slider__thumb',
|
|
input: 'fui-Slider__input'
|
|
};
|
|
// Internal CSS variables
|
|
const thumbPositionVar = `--fui-Slider__thumb--position`;
|
|
export const sliderCSSVars = {
|
|
sliderDirectionVar: `--fui-Slider--direction`,
|
|
sliderInnerThumbRadiusVar: `--fui-Slider__inner-thumb--radius`,
|
|
sliderProgressVar: `--fui-Slider--progress`,
|
|
sliderProgressColorVar: `--fui-Slider__progress--color`,
|
|
sliderRailSizeVar: `--fui-Slider__rail--size`,
|
|
sliderRailColorVar: `--fui-Slider__rail--color`,
|
|
sliderStepsPercentVar: `--fui-Slider--steps-percent`,
|
|
sliderThumbColorVar: `--fui-Slider__thumb--color`,
|
|
sliderThumbSizeVar: `--fui-Slider__thumb--size`
|
|
};
|
|
const { sliderDirectionVar, sliderInnerThumbRadiusVar, sliderProgressVar, sliderProgressColorVar, sliderRailSizeVar, sliderRailColorVar, sliderStepsPercentVar, sliderThumbColorVar, sliderThumbSizeVar } = sliderCSSVars;
|
|
/**
|
|
* Styles for the root slot
|
|
*/ const useRootStyles = makeStyles({
|
|
root: {
|
|
position: 'relative',
|
|
display: 'inline-grid',
|
|
touchAction: 'none',
|
|
alignItems: 'center',
|
|
justifyItems: 'center'
|
|
},
|
|
small: {
|
|
[sliderThumbSizeVar]: '16px',
|
|
[sliderInnerThumbRadiusVar]: '5px',
|
|
[sliderRailSizeVar]: '2px',
|
|
minHeight: '24px'
|
|
},
|
|
medium: {
|
|
[sliderThumbSizeVar]: '20px',
|
|
[sliderInnerThumbRadiusVar]: '6px',
|
|
[sliderRailSizeVar]: '4px',
|
|
minHeight: '32px'
|
|
},
|
|
horizontal: {
|
|
minWidth: '120px',
|
|
// 3x3 grid with the rail and thumb in the center cell [2,2] and the hidden input stretching across all cells
|
|
gridTemplateRows: `1fr var(${sliderThumbSizeVar}) 1fr`,
|
|
gridTemplateColumns: `1fr calc(100% - var(${sliderThumbSizeVar})) 1fr`
|
|
},
|
|
vertical: {
|
|
minHeight: '120px',
|
|
// 3x3 grid with the rail and thumb in the center cell [2,2] and the hidden input stretching across all cells
|
|
gridTemplateRows: `1fr calc(100% - var(${sliderThumbSizeVar})) 1fr`,
|
|
gridTemplateColumns: `1fr var(${sliderThumbSizeVar}) 1fr`
|
|
},
|
|
enabled: {
|
|
[sliderRailColorVar]: tokens.colorNeutralStrokeAccessible,
|
|
[sliderProgressColorVar]: tokens.colorCompoundBrandBackground,
|
|
[sliderThumbColorVar]: tokens.colorCompoundBrandBackground,
|
|
':hover': {
|
|
[sliderThumbColorVar]: tokens.colorCompoundBrandBackgroundHover,
|
|
[sliderProgressColorVar]: tokens.colorCompoundBrandBackgroundHover
|
|
},
|
|
':active': {
|
|
[sliderThumbColorVar]: tokens.colorCompoundBrandBackgroundPressed,
|
|
[sliderProgressColorVar]: tokens.colorCompoundBrandBackgroundPressed
|
|
},
|
|
'@media (forced-colors: active)': {
|
|
[sliderRailColorVar]: 'CanvasText',
|
|
[sliderThumbColorVar]: 'Highlight',
|
|
[sliderProgressColorVar]: 'Highlight',
|
|
':hover': {
|
|
[sliderThumbColorVar]: 'Highlight',
|
|
[sliderProgressColorVar]: 'Highlight'
|
|
}
|
|
}
|
|
},
|
|
disabled: {
|
|
[sliderThumbColorVar]: tokens.colorNeutralForegroundDisabled,
|
|
[sliderRailColorVar]: tokens.colorNeutralBackgroundDisabled,
|
|
[sliderProgressColorVar]: tokens.colorNeutralForegroundDisabled,
|
|
'@media (forced-colors: active)': {
|
|
[sliderRailColorVar]: 'GrayText',
|
|
[sliderCSSVars.sliderThumbColorVar]: 'GrayText',
|
|
[sliderCSSVars.sliderProgressColorVar]: 'GrayText'
|
|
}
|
|
},
|
|
focusIndicatorHorizontal: createFocusOutlineStyle({
|
|
selector: 'focus-within',
|
|
style: {
|
|
outlineOffset: {
|
|
top: '-2px',
|
|
bottom: '-2px',
|
|
left: '-4px',
|
|
right: '-4px'
|
|
}
|
|
}
|
|
}),
|
|
focusIndicatorVertical: createFocusOutlineStyle({
|
|
selector: 'focus-within',
|
|
style: {
|
|
outlineOffset: {
|
|
top: '-2px',
|
|
bottom: '-2px',
|
|
left: '4px',
|
|
right: '4px'
|
|
}
|
|
}
|
|
})
|
|
});
|
|
/**
|
|
* Styles for the rail slot
|
|
*/ const useRailStyles = makeStyles({
|
|
rail: {
|
|
borderRadius: tokens.borderRadiusXLarge,
|
|
pointerEvents: 'none',
|
|
gridRowStart: '2',
|
|
gridRowEnd: '2',
|
|
gridColumnStart: '2',
|
|
gridColumnEnd: '2',
|
|
position: 'relative',
|
|
forcedColorAdjust: 'none',
|
|
// Background gradient represents the progress of the slider
|
|
backgroundImage: `linear-gradient(
|
|
var(${sliderDirectionVar}),
|
|
var(${sliderProgressColorVar}) 0%,
|
|
var(${sliderProgressColorVar}) var(${sliderProgressVar}),
|
|
var(${sliderRailColorVar}) var(${sliderProgressVar})
|
|
)`,
|
|
outlineWidth: '1px',
|
|
outlineStyle: 'solid',
|
|
outlineColor: tokens.colorTransparentStroke,
|
|
'::before': {
|
|
content: "''",
|
|
position: 'absolute',
|
|
// Repeating gradient represents the steps if provided
|
|
backgroundImage: `repeating-linear-gradient(
|
|
var(${sliderDirectionVar}),
|
|
#0000 0%,
|
|
#0000 calc(var(${sliderStepsPercentVar}) - 1px),
|
|
${tokens.colorNeutralBackground1} calc(var(${sliderStepsPercentVar}) - 1px),
|
|
${tokens.colorNeutralBackground1} var(${sliderStepsPercentVar})
|
|
)`,
|
|
// force steps to use HighlightText for high contrast mode
|
|
'@media (forced-colors: active)': {
|
|
backgroundImage: `repeating-linear-gradient(
|
|
var(${sliderDirectionVar}),
|
|
#0000 0%,
|
|
#0000 calc(var(${sliderStepsPercentVar}) - 1px),
|
|
HighlightText calc(var(${sliderStepsPercentVar}) - 1px),
|
|
HighlightText var(${sliderStepsPercentVar})
|
|
)`
|
|
}
|
|
}
|
|
},
|
|
horizontal: {
|
|
width: '100%',
|
|
height: `var(${sliderRailSizeVar})`,
|
|
'::before': {
|
|
left: '-1px',
|
|
right: '-1px',
|
|
height: `var(${sliderRailSizeVar})`
|
|
}
|
|
},
|
|
vertical: {
|
|
width: `var(${sliderRailSizeVar})`,
|
|
height: '100%',
|
|
'::before': {
|
|
width: `var(${sliderRailSizeVar})`,
|
|
top: '-1px',
|
|
bottom: '-1px'
|
|
}
|
|
}
|
|
});
|
|
/**
|
|
* Styles for the thumb slot
|
|
*/ const useThumbStyles = makeStyles({
|
|
thumb: {
|
|
// Ensure the thumb stays within the track boundaries.
|
|
// When the value is at 0% or 100%, the distance from the track edge
|
|
// to the thumb center equals the inner thumb radius.
|
|
[`${thumbPositionVar}`]: `clamp(var(${sliderInnerThumbRadiusVar}), var(${sliderProgressVar}), calc(100% - var(${sliderInnerThumbRadiusVar})))`,
|
|
gridRowStart: '2',
|
|
gridRowEnd: '2',
|
|
gridColumnStart: '2',
|
|
gridColumnEnd: '2',
|
|
position: 'absolute',
|
|
width: `var(${sliderThumbSizeVar})`,
|
|
height: `var(${sliderThumbSizeVar})`,
|
|
pointerEvents: 'none',
|
|
outlineStyle: 'none',
|
|
forcedColorAdjust: 'none',
|
|
borderRadius: tokens.borderRadiusCircular,
|
|
boxShadow: `0 0 0 calc(var(${sliderThumbSizeVar}) * .2) ${tokens.colorNeutralBackground1} inset`,
|
|
backgroundColor: `var(${sliderThumbColorVar})`,
|
|
'::before': {
|
|
position: 'absolute',
|
|
top: '0px',
|
|
left: '0px',
|
|
bottom: '0px',
|
|
right: '0px',
|
|
borderRadius: tokens.borderRadiusCircular,
|
|
boxSizing: 'border-box',
|
|
content: "''",
|
|
border: `calc(var(${sliderThumbSizeVar}) * .05) solid ${tokens.colorNeutralStroke1}`
|
|
}
|
|
},
|
|
disabled: {
|
|
'::before': {
|
|
border: `calc(var(${sliderThumbSizeVar}) * .05) solid ${tokens.colorNeutralForegroundDisabled}`
|
|
}
|
|
},
|
|
horizontal: {
|
|
transform: 'translateX(-50%)',
|
|
left: `var(${thumbPositionVar})`
|
|
},
|
|
vertical: {
|
|
transform: 'translateY(50%)',
|
|
bottom: `var(${thumbPositionVar})`
|
|
}
|
|
});
|
|
/**
|
|
* Styles for the Input slot
|
|
*/ const useInputStyles = makeStyles({
|
|
input: {
|
|
cursor: 'pointer',
|
|
opacity: 0,
|
|
gridRowStart: '1',
|
|
gridRowEnd: '-1',
|
|
gridColumnStart: '1',
|
|
gridColumnEnd: '-1',
|
|
padding: '0',
|
|
margin: '0'
|
|
},
|
|
disabled: {
|
|
cursor: 'default'
|
|
},
|
|
horizontal: {
|
|
height: `var(${sliderThumbSizeVar})`,
|
|
width: '100%'
|
|
},
|
|
vertical: {
|
|
height: '100%',
|
|
width: `var(${sliderThumbSizeVar})`,
|
|
// Workaround to check if the browser supports `writing-mode: vertical-lr` for inputs and input[type=range] specifically.
|
|
// We check if the `writing-mode: sideways-lr` is supported as it's newer feature and it means
|
|
// that vertical controls should also support `writing-mode: vertical-lr`.
|
|
'@supports (writing-mode: sideways-lr)': {
|
|
writingMode: 'vertical-lr',
|
|
direction: 'rtl'
|
|
},
|
|
// Fallback for browsers that don't support `writing-mode: vertical-lr` for inputs
|
|
'@supports not (writing-mode: sideways-lr)': {
|
|
WebkitAppearance: 'slider-vertical'
|
|
}
|
|
}
|
|
});
|
|
/**
|
|
* Apply styling to the Slider slots based on the state
|
|
*/ export const useSliderStyles_unstable = (state)=>{
|
|
'use no memo';
|
|
const rootStyles = useRootStyles();
|
|
const railStyles = useRailStyles();
|
|
const thumbStyles = useThumbStyles();
|
|
const inputStyles = useInputStyles();
|
|
const isVertical = state.vertical;
|
|
state.root.className = mergeClasses(sliderClassNames.root, rootStyles.root, isVertical ? rootStyles.focusIndicatorVertical : rootStyles.focusIndicatorHorizontal, rootStyles[state.size], isVertical ? rootStyles.vertical : rootStyles.horizontal, state.disabled ? rootStyles.disabled : rootStyles.enabled, state.root.className);
|
|
state.rail.className = mergeClasses(sliderClassNames.rail, railStyles.rail, isVertical ? railStyles.vertical : railStyles.horizontal, state.rail.className);
|
|
state.thumb.className = mergeClasses(sliderClassNames.thumb, thumbStyles.thumb, isVertical ? thumbStyles.vertical : thumbStyles.horizontal, state.disabled && thumbStyles.disabled, state.thumb.className);
|
|
state.input.className = mergeClasses(sliderClassNames.input, inputStyles.input, isVertical ? inputStyles.vertical : inputStyles.horizontal, state.disabled && inputStyles.disabled, state.input.className);
|
|
return state;
|
|
};
|