Files
mtdb_movie/common/resources/client/ui/forms/slider/base-slider.tsx
maher 703f50a09d
Some checks failed
Build / run (push) Has been cancelled
first commit
2025-10-29 11:42:25 +01:00

192 lines
4.7 KiB
TypeScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, {ReactNode} from 'react';
import clsx from 'clsx';
import {getInputFieldClassNames} from '../input-field/get-input-field-class-names';
import {UseSliderProps, UseSliderReturn} from './use-slider';
export interface BaseSliderProps extends UseSliderProps {
slider: UseSliderReturn;
children: ReactNode;
}
export function BaseSlider(props: BaseSliderProps) {
const {
size = 'md',
inline,
label,
showValueLabel = !!label,
className,
width = 'w-full',
slider,
children,
trackColor = 'primary',
fillColor = 'primary',
} = props;
const {
domProps,
trackRef,
getThumbPercent,
getThumbValueLabel,
labelId,
groupId,
thumbIds,
isDisabled,
numberFormatter,
minValue,
maxValue,
step,
values,
getValueLabel,
} = slider;
let outputValue = '';
let maxLabelLength = Math.max(
[...numberFormatter.format(minValue)].length,
[...numberFormatter.format(maxValue)].length,
[...numberFormatter.format(step)].length,
);
if (getValueLabel) {
outputValue = getValueLabel(values[0]);
} else if (values.length === 1) {
outputValue = getThumbValueLabel(0);
} else if (values.length === 2) {
// This should really use the NumberFormat#formatRange proposal...
// https://github.com/tc39/ecma402/issues/393
// https://github.com/tc39/proposal-intl-numberformat-v3#formatrange-ecma-402-393
outputValue = `${getThumbValueLabel(0)} ${getThumbValueLabel(1)}`;
maxLabelLength =
3 +
2 *
Math.max(
maxLabelLength,
[...numberFormatter.format(minValue)].length,
[...numberFormatter.format(maxValue)].length,
);
}
const style = getInputFieldClassNames({
size,
disabled: isDisabled,
labelDisplay: 'flex',
});
const wrapperClassname = clsx('touch-none', className, width, {
'flex items-center': inline,
});
return (
<div className={wrapperClassname} role="group" id={groupId}>
{(label || showValueLabel) && (
<div className={clsx(style.label, 'select-none')}>
{label && (
<label
onClick={() => {
// Safari does not focus <input type="range"> elements when clicking on an associated <label>,
// so do it manually. In addition, make sure we show the focus ring.
document.getElementById(thumbIds[0])?.focus();
}}
id={labelId}
htmlFor={groupId}
>
{label}
</label>
)}
{showValueLabel && (
<output
htmlFor={thumbIds[0]}
className="ml-auto text-right"
aria-live="off"
style={
!maxLabelLength
? undefined
: {
width: `${maxLabelLength}ch`,
minWidth: `${maxLabelLength}ch`,
}
}
>
{outputValue}
</output>
)}
</div>
)}
<div
ref={trackRef}
className={clsx('relative', getWrapperHeight(props))}
{...domProps}
role="presentation"
>
<div
className={clsx(
'absolute inset-0 m-auto rounded',
getTrackColor(trackColor, isDisabled),
getTrackHeight(size),
)}
/>
<div
className={clsx(
'absolute inset-0 my-auto rounded',
getFillColor(fillColor, isDisabled),
getTrackHeight(size),
)}
style={{width: `${Math.max(getThumbPercent(0) * 100, 0)}%`}}
/>
{children}
</div>
</div>
);
}
function getWrapperHeight({size, wrapperHeight}: UseSliderProps): string {
if (wrapperHeight) return wrapperHeight;
switch (size) {
case 'xs':
return 'h-14';
case 'sm':
return 'h-20';
default:
return 'h-30';
}
}
function getTrackHeight(size: UseSliderProps['size']): string {
switch (size) {
case 'xs':
return 'h-2';
case 'sm':
return 'h-3';
default:
return 'h-4';
}
}
function getTrackColor(color: string, isDisabled: boolean): string {
if (isDisabled) {
color = 'disabled';
}
switch (color) {
case 'disabled':
return 'bg-slider-disabled/60';
case 'primary':
return 'bg-primary-light';
case 'neutral':
return 'bg-divider';
default:
return color;
}
}
function getFillColor(color: string, isDisabled: boolean): string {
if (isDisabled) {
color = 'disabled';
}
switch (color) {
case 'disabled':
return 'bg-slider-disabled';
case 'primary':
return 'bg-primary';
default:
return color;
}
}