Skip to content

Commit

Permalink
Add sliderBase (#96)
Browse files Browse the repository at this point in the history
* Add sliderBase

* work in progress for range filter

* update yarn lock

* styling fixes

* slider

* slider

* fix types

* v0.13.1-1

* Fix more types

* options icon

* v0.13.1-2

* Fix styles

* v0.13.1-3

* WIP

* v0.13.1-4

* v0.13.1
  • Loading branch information
mikeldking committed Aug 2, 2023
1 parent 189859c commit 5a830d9
Show file tree
Hide file tree
Showing 16 changed files with 1,145 additions and 199 deletions.
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "0.13.1-0",
"version": "0.13.1",
"license": "MIT",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
Expand Down Expand Up @@ -112,14 +112,15 @@
"@react-aria/button": "^3.6.2",
"@react-aria/dialog": "^3.4.0",
"@react-aria/focus": "^3.9.0",
"@react-aria/i18n": "^3.6.1",
"@react-aria/i18n": "^3.8.0",
"@react-aria/interactions": "^3.12.0",
"@react-aria/listbox": "^3.7.0",
"@react-aria/overlays": "^3.11.0",
"@react-aria/progress": "^3.3.4",
"@react-aria/radio": "^3.4.0",
"@react-aria/select": "^3.8.2",
"@react-aria/separator": "^3.2.4",
"@react-aria/slider": "^3.2.4",
"@react-aria/switch": "^3.3.1",
"@react-aria/textfield": "^3.7.2",
"@react-aria/tooltip": "^3.3.2",
Expand All @@ -132,6 +133,7 @@
"@react-stately/overlays": "^3.4.2",
"@react-stately/radio": "^3.6.0",
"@react-stately/select": "^3.3.2",
"@react-stately/slider": "^3.2.4",
"@react-stately/tooltip": "^3.2.2",
"@react-stately/utils": "^3.5.1",
"@react-stately/virtualizer": "^3.3.1",
Expand Down
13 changes: 13 additions & 0 deletions src/icon/Icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -740,3 +740,16 @@ export const FileOutline = () => (
</g>
</svg>
);

export const OptionsOutline = () => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g data-name="Layer 2">
<g data-name="options">
<rect width="24" height="24" opacity="0" />
<path d="M7 14.18V3a1 1 0 0 0-2 0v11.18a3 3 0 0 0 0 5.64V21a1 1 0 0 0 2 0v-1.18a3 3 0 0 0 0-5.64zM6 18a1 1 0 1 1 1-1 1 1 0 0 1-1 1z" />
<path d="M21 13a3 3 0 0 0-2-2.82V3a1 1 0 0 0-2 0v7.18a3 3 0 0 0 0 5.64V21a1 1 0 0 0 2 0v-5.18A3 3 0 0 0 21 13zm-3 1a1 1 0 1 1 1-1 1 1 0 0 1-1 1z" />
<path d="M15 5a3 3 0 1 0-4 2.82V21a1 1 0 0 0 2 0V7.82A3 3 0 0 0 15 5zm-3 1a1 1 0 1 1 1-1 1 1 0 0 1-1 1z" />
</g>
</g>
</svg>
);
1 change: 1 addition & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export * from './view';
export * from './menu';
export * from './icon';
export * from './breadcrumbs';
export * from './slider';
export * from './navlist';
export * from './progress';
export * from './switch';
Expand Down
4 changes: 4 additions & 0 deletions src/provider/GlobalStyles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,10 @@ export const globalCSS = css`
--ac-global-grid-margin-xlarge: var(
--ac-global-dimension-static-size-600
);

/* Aliases */
--ac-alias-single-line-height: var(--ac-global-dimension-size-400);
--ac-alias-single-line-width: var(--ac-global-dimension-size-2400);
}
}
`;
Expand Down
98 changes: 98 additions & 0 deletions src/slider/RangeSlider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { FocusableRef } from '@react-types/shared';
import React from 'react';
import {
SliderBase,
SliderBaseChildArguments,
SliderBaseProps,
} from './SliderBase';
import { SliderThumb } from './SliderThumb';
import { RangeSliderProps } from '../types/slider';
import { useLocale } from '@react-aria/i18n';

function RangeSlider(
props: RangeSliderProps,
ref: FocusableRef<HTMLDivElement>
) {
let {
onChange,
onChangeEnd,
value,
defaultValue,
getValueLabel,
...otherProps
} = props;

let baseProps: Omit<SliderBaseProps<number[]>, 'children'> = {
...otherProps,
value: value != null ? [value.start, value.end] : undefined,
defaultValue:
defaultValue != null
? [defaultValue.start, defaultValue.end]
: // make sure that useSliderState knows we have two handles
[props.minValue ?? 0, props.maxValue ?? 100],
onChange(v) {
onChange?.({ start: v[0], end: v[1] });
},
onChangeEnd(v) {
onChangeEnd?.({ start: v[0], end: v[1] });
},
getValueLabel: getValueLabel
? // @ts-expect-error update typescript version
([start, end]) => getValueLabel({ start, end })
: undefined,
};

let { direction } = useLocale();

return (
<SliderBase {...baseProps} classes={'ac-slider--range'} ref={ref}>
{({ trackRef, inputRef, state }: SliderBaseChildArguments) => {
let cssDirection = direction === 'rtl' ? 'right' : 'left';
return (
<>
<div
className={'ac-slider-track'}
style={{ width: `${state.getThumbPercent(0) * 100}%` }}
/>
<SliderThumb
index={0}
aria-label={'minimum'}
isDisabled={props.isDisabled}
trackRef={trackRef}
inputRef={inputRef}
state={state}
/>
<div
className={'ac-slider-track'}
style={{
[cssDirection]: `${state.getThumbPercent(0) * 100}%`,
width: `${Math.abs(
state.getThumbPercent(0) - state.getThumbPercent(1)
) * 100}%`,
}}
/>
<SliderThumb
index={1}
aria-label={'maximum'}
isDisabled={props.isDisabled}
trackRef={trackRef}
state={state}
/>
<div
className={'ac-slider-track'}
style={{
width: `${(1 - state.getThumbPercent(1)) * 100}%`,
}}
/>
</>
);
}}
</SliderBase>
);
}

/**
* RangeSliders allow users to quickly select a subset range. They should be used when the upper and lower bounds to the range are invariable.
*/
const _RangeSlider = React.forwardRef(RangeSlider);
export { _RangeSlider as RangeSlider };
130 changes: 130 additions & 0 deletions src/slider/Slider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { clamp } from '@react-aria/utils';
import { classNames } from '../utils';
import { FocusableRef } from '../types';
import React, { ReactNode } from 'react';
import {
SliderBase,
SliderBaseChildArguments,
SliderBaseProps,
} from './SliderBase';
import { SliderThumb } from './SliderThumb';
import { ACSliderProps } from '../types/slider';

function Slider(props: ACSliderProps, ref: FocusableRef<HTMLDivElement>) {
let {
onChange,
onChangeEnd,
value,
defaultValue,
isFilled,
fillOffset,
trackGradient,
getValueLabel,
...otherProps
} = props;

let baseProps: Omit<SliderBaseProps, 'children'> = {
...otherProps,
// Normalize `value: number[]` to `value: number`
value: value != null ? [value] : undefined,
defaultValue: defaultValue != null ? [defaultValue] : undefined,
onChange: (v: number[]): void => {
onChange?.(v[0]);
},
onChangeEnd: (v: number[]): void => {
onChangeEnd?.(v[0]);
},

getValueLabel:
typeof getValueLabel === 'function'
? // @ts-ignore
([v]) => getValueLabel(v)
: undefined,
};

return (
<SliderBase {...baseProps} ref={ref}>
{({ trackRef, inputRef, state }: SliderBaseChildArguments) => {
fillOffset =
fillOffset != null
? clamp(
fillOffset,
state.getThumbMinValue(0),
state.getThumbMaxValue(0)
)
: fillOffset;

let lowerTrack = (
<div
className={classNames('ac-slider-track')}
style={{
width: `${state.getThumbPercent(0) * 100}%`,
// TODO not sure if it has advantages, but this could also be implemented as CSS calc():
// .track::before {
// background-size: calc((1/ (var(--width)/100)) * 100%);
// width: calc(var(--width) * 1%)M
// }
// @ts-ignore
'--ac-track-background-size': `${(1 / state.getThumbPercent(0)) *
100}%`,
}}
/>
);
let upperTrack = (
<div
className="ac-slider-track"
style={{
width: `${(1 - state.getThumbPercent(0)) * 100}%`,
// @ts-ignore
'--ac-track-background-size': `${(1 /
(1 - state.getThumbPercent(0))) *
100}%`,
'--ac-track-background-position': '100%',
}}
/>
);

let filledTrack: ReactNode = null;
if (isFilled && fillOffset != null) {
let width =
state.getThumbPercent(0) - state.getValuePercent(fillOffset);
let isRightOfOffset = width > 0;
let offset = isRightOfOffset
? state.getValuePercent(fillOffset)
: state.getThumbPercent(0);
filledTrack = (
<div
className="ac-slider-fill"
style={{
left: `${offset * 100}%`,
width: `${Math.abs(width) * 100}%`,
}}
/>
);
}

return (
<>
{lowerTrack}
<SliderThumb
index={0}
isDisabled={props.isDisabled}
trackRef={trackRef}
inputRef={inputRef}
state={state}
name={props.name}
/>
{filledTrack}
{upperTrack}
</>
);
}}
</SliderBase>
);
}

/**
* Sliders allow users to quickly select a value within a range. They should be used when the upper and lower bounds to the range are invariable.
*/
const _Slider = React.forwardRef(Slider);
export { _Slider as Slider };
Loading

0 comments on commit 5a830d9

Please sign in to comment.