Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import classNames from 'classnames';
import { type FC, type JSX } from 'react';
import { useRange } from '../../contexts/useRange';
import style from './rangeBounds.module.scss';

interface RangeBoundsProp {
disabled?: boolean,
max: number,
min: number
}

const RangeBounds: FC<RangeBoundsProp> = ({
disabled,
max,
min,
}): JSX.Element => {
const { disabled } = useRange();

return (
<div className={ classNames(
style['range-bounds'],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
@use '../../../../../style/range';

@layer ods-atoms {
.range-bounds {
display: flex;
justify-content: space-between;
padding-top: 8px;
padding-top: range.$ods-range-label-padding-top;
color: var(--ods-color-text);
font-weight: 600;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,24 @@ import classNames from 'classnames';
import { type FC, type JSX, type KeyboardEvent, useRef, useState } from 'react';
import { useFormField } from '../../../../form-field/src';
import { Tooltip, TooltipContent, TooltipTrigger } from '../../../../tooltip/src';
import { useRange } from '../../contexts/useRange';
import style from './rangeThumb.module.scss';

interface RangeThumbProp {
disabled?: boolean,
displayTooltip?: boolean,
index: number,
invalid?: boolean,
}

const RangeThumb: FC<RangeThumbProp> = ({
disabled,
displayTooltip,
index,
invalid,
}): JSX.Element => {
const thumbRef = useRef<HTMLDivElement>(null);
const fieldContext = useFormField();
const { value } = useSliderContext();
const { disabled } = useRange();
const [isFocused, setIsFocused] = useState(false);
const [isTooltipOpen, setIsTooltipOpen] = useState(false);

Expand All @@ -39,11 +41,11 @@ const RangeThumb: FC<RangeThumbProp> = ({
}

return (
<Tooltip open={ !disabled && (isFocused || isTooltipOpen) }>
<Tooltip open={ displayTooltip && !disabled && (isFocused || isTooltipOpen) }>
<TooltipTrigger asChild>
<Slider.Thumb
aria-invalid={ invalid }
aria-describedby={ fieldContext?.ariaDescribedBy }
aria-invalid={ invalid }
aria-labelledby={ index === 0 ? fieldContext?.labelId : undefined }
className={ classNames(
style['range-thumb'],
Expand All @@ -57,8 +59,7 @@ const RangeThumb: FC<RangeThumbProp> = ({
onMouseLeave={ () => setIsTooltipOpen(false) }
onMouseOver={ () => setIsTooltipOpen(true) }
ref={ thumbRef }
role="slider"
>
role="slider">
<Slider.HiddenInput />
</Slider.Thumb>
</TooltipTrigger>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { Slider } from '@ark-ui/react/slider';
import classNames from 'classnames';
import { type FC, type JSX, useEffect, useRef } from 'react';
import { THUMB_SIZE } from '../../constants/thumb';
import { type RangeTickItem, useRange } from '../../contexts/useRange';
import style from './rangeTick.module.scss';

interface RangeTickProp {
index: number,
isLast: boolean,
singleMode: boolean,
tick: RangeTickItem,
}

const RangeTick: FC<RangeTickProp> = ({
index,
isLast,
singleMode,
tick,
}): JSX.Element => {
const { disabled, setRootPadding } = useRange();
const tickRef = useRef<HTMLSpanElement>(null);
const isNumber = typeof tick === 'number';

useEffect(() => {
if (!tickRef.current || typeof tick === 'number') {
return;
}

const resizeObserver = new ResizeObserver((entries) => {
if (entries && entries.length) {
const { height, top, width } = entries[0].contentRect;

if (index === 0) {
setRootPadding((padding) => ({
...padding,
left: Math.max(0, (width / 2) - (THUMB_SIZE / 2)),
}));
} else if (isLast) {
setRootPadding((padding) => ({
...padding,
right: Math.max(0, (width / 2) - (THUMB_SIZE / 2)),
}));
}

setRootPadding((padding) => ({
...padding,
bottom: height + top,
}));
}
});

resizeObserver.observe(tickRef.current);

return () => {
resizeObserver.disconnect();
};
}, [index, isLast, setRootPadding, tick, tickRef]);

return (
<Slider.Marker
className={ classNames(
style['range-tick'],
{ [style['range-tick--custom-marker']]: !isNumber },
{ [style['range-tick--single-mode']]: singleMode },
)}
ref={ tickRef }
value={ isNumber ? tick : tick.value }>
{
!isNumber &&
<span className={ classNames(
style['range-tick__label'],
{ [style['range-tick__label--disabled']]: disabled },
)}>
{ tick.label }
</span>
}
</Slider.Marker>
);
};

RangeTick.displayName = 'RangeTick';

export {
RangeTick,
type RangeTickProp,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
@use 'sass:math';
@use '../../../../../style/range';

@layer ods-atoms {
.range-tick {
$tick-width: 2px;

position: absolute;

&::before {
display: block;
position: absolute;
bottom: -(math.div(range.$ods-range-thumb-size, 2) - math.div(range.$ods-range-track-height, 2));
border-radius: 6px;
background-color: range.$ods-range-background-color;
width: $tick-width;
height: range.$ods-range-thumb-size;
content: '';
}

&[data-state="at-value"],
&--single-mode[data-state="under-value"] {
&::before {
background-color: range.$ods-range-background-color-active;
}
}

&--custom-marker {
padding-top: range.$ods-range-label-padding-top;

&::before {
top: -(range.$ods-range-track-height + math.div(range.$ods-range-thumb-size - range.$ods-range-track-height, 2));
bottom: auto;
left: calc(50% - ($tick-width / 2));
}
}

&__label {
color: var(--ods-color-text);
font-weight: 600;

&--disabled {
color: var(--ods-color-text-disabled-default);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Slider } from '@ark-ui/react/slider';
import { type FC, type JSX } from 'react';
import { type RangeTickItem } from '../../contexts/useRange';
import { RangeTick } from '../range-tick/RangeTick';
import style from './rangeTicks.module.scss';

interface RangeTicksProp {
singleMode: boolean,
ticks: RangeTickItem[],
}

const RangeTicks: FC<RangeTicksProp> = ({
singleMode,
ticks,
}): JSX.Element => {
return (
<Slider.MarkerGroup className={ style['range-ticks'] }>
{
ticks.map((tick, i) => (
<RangeTick
index={ i }
isLast={ i === ticks.length - 1 }
key={ typeof tick === 'number' ? tick : tick.value }
singleMode={ singleMode }
tick={ tick } />
))
}
</Slider.MarkerGroup>
);
};

RangeTicks.displayName = 'RangeTicks';

export {
RangeTicks,
type RangeTicksProp,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@layer ods-atoms {
.range-ticks {
position: relative;
}
}
Loading