From 3be2ff0c7fe90d933a416b2888c5d727dae89a5f Mon Sep 17 00:00:00 2001 From: Resi Respati Date: Tue, 12 Oct 2021 19:30:18 +0700 Subject: [PATCH] feat(react): slider (#507) * feat(react): slider Closes #439. * fix(react): fix incorrect styling --- packages/react/package.json | 2 + packages/react/src/components/index.ts | 1 + .../react/src/components/slider/README.md | 17 +++++++ .../src/components/slider/Slider.stories.tsx | 31 ++++++++++++ .../react/src/components/slider/Slider.tsx | 48 +++++++++++++++++++ packages/react/src/components/slider/index.ts | 2 + .../react/src/theme/componentStyles/index.ts | 2 + .../react/src/theme/componentStyles/slider.ts | 40 ++++++++++++++++ yarn.lock | 12 +++++ 9 files changed, 155 insertions(+) create mode 100644 packages/react/src/components/slider/README.md create mode 100644 packages/react/src/components/slider/Slider.stories.tsx create mode 100644 packages/react/src/components/slider/Slider.tsx create mode 100644 packages/react/src/components/slider/index.ts create mode 100644 packages/react/src/theme/componentStyles/slider.ts diff --git a/packages/react/package.json b/packages/react/package.json index 0659f24ee..b9165dba4 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -50,6 +50,7 @@ "react-fast-compare": "^3.2.0", "react-popper": "^2.2.5", "react-popper-tooltip": "^4.3.0", + "react-ranger": "^2.1.0", "react-transition-group": "^4.4.2", "styled-system": "^5.1.4", "tslib": "^2.3.1" @@ -72,6 +73,7 @@ "@testing-library/jest-dom": "^5.14.1", "@testing-library/react": "^12.0.0", "@types/jest": "^26.0.24", + "@types/react-ranger": "^2.0.1", "@types/react-transition-group": "^4.4.2", "@types/styled-components": "^5.1.14", "babel-loader": "^8.2.2", diff --git a/packages/react/src/components/index.ts b/packages/react/src/components/index.ts index 472ce8c7e..a5c53a425 100644 --- a/packages/react/src/components/index.ts +++ b/packages/react/src/components/index.ts @@ -19,6 +19,7 @@ export * from './panel'; export * from './pill'; export * from './popover'; export * from './side-sheet'; +export * from './slider'; export * from './skeleton'; export * from './table'; export * from './tabs'; diff --git a/packages/react/src/components/slider/README.md b/packages/react/src/components/slider/README.md new file mode 100644 index 000000000..d0ac0601d --- /dev/null +++ b/packages/react/src/components/slider/README.md @@ -0,0 +1,17 @@ +# Slider + +> Slider element. + +## Usage + +To use this component in your app, import as follows: + +```jsx +import { Slider } from '@aksara-ui/react'; + +export function Example({ min, max, stepSize }) { + const [values, setValues] = React.useState([10]); + + return ; +}; +``` diff --git a/packages/react/src/components/slider/Slider.stories.tsx b/packages/react/src/components/slider/Slider.stories.tsx new file mode 100644 index 000000000..f60868582 --- /dev/null +++ b/packages/react/src/components/slider/Slider.stories.tsx @@ -0,0 +1,31 @@ +import { Story } from '@storybook/react'; +import * as React from 'react'; +import { Slider, SliderProps } from '.'; + +export default { + title: 'Core/Components/Slider', + component: Slider, + argTypes: { + min: { + control: 'number', + }, + max: { + control: 'number', + }, + stepSize: { + control: 'number', + }, + }, +}; + +export const Example: Story = ({ values, min, max, stepSize }) => { + const [sliderValues, setSliderValues] = React.useState(values); + + return ; +}; +Example.args = { + values: [10], + min: 0, + max: 100, + stepSize: 5, +}; diff --git a/packages/react/src/components/slider/Slider.tsx b/packages/react/src/components/slider/Slider.tsx new file mode 100644 index 000000000..b3da8381b --- /dev/null +++ b/packages/react/src/components/slider/Slider.tsx @@ -0,0 +1,48 @@ +import VisuallyHidden from '@reach/visually-hidden'; +import * as React from 'react'; +import { RangerOptions, useRanger } from 'react-ranger'; +import { Box, BoxProps } from '../../layout'; +import { useComponentStyles } from '../../system'; +import { UnstyledButton } from '../button'; + +export interface SliderProps extends RangerOptions, BoxProps {} + +const Slider = React.forwardRef( + ({ values, onChange, min = 0, max = 100, stepSize = 5, sx, ...rest }, ref) => { + const sliderTrackStyles = useComponentStyles('sliderTrack'); + const sliderHandleStyles = useComponentStyles('sliderHandle'); + const { getTrackProps, segments, handles } = useRanger({ + values, + onChange, + min, + max, + stepSize, + ...rest, + }); + + return ( + + {segments.map(({ getSegmentProps }, i) => { + return ( + + ); + })} + {handles.map(({ value, getHandleProps }) => ( + + {value} + + ))} + + ); + } +); + +Slider.displayName = 'InputSlider'; + +export default Slider; diff --git a/packages/react/src/components/slider/index.ts b/packages/react/src/components/slider/index.ts new file mode 100644 index 000000000..bd5997ca0 --- /dev/null +++ b/packages/react/src/components/slider/index.ts @@ -0,0 +1,2 @@ +export { default as Slider } from './Slider'; +export * from './Slider'; diff --git a/packages/react/src/theme/componentStyles/index.ts b/packages/react/src/theme/componentStyles/index.ts index 620896082..ba1e2264f 100644 --- a/packages/react/src/theme/componentStyles/index.ts +++ b/packages/react/src/theme/componentStyles/index.ts @@ -7,6 +7,7 @@ import form from './form'; import message from './message'; import navigation from './navigation'; import pill from './pill'; +import slider from './slider'; import typography from './typography'; import tabs from './tabs'; import toast from './toast'; @@ -25,6 +26,7 @@ const componentStyles = { ...message, ...navigation, ...pill, + ...slider, ...typography, ...tabs, ...toast, diff --git a/packages/react/src/theme/componentStyles/slider.ts b/packages/react/src/theme/componentStyles/slider.ts new file mode 100644 index 000000000..9831637fe --- /dev/null +++ b/packages/react/src/theme/componentStyles/slider.ts @@ -0,0 +1,40 @@ +import { transparentize } from 'polished'; +import { DefaultTheme } from 'styled-components'; +import { ComponentThemeConfig } from '../types'; + +const sliderTrack: ComponentThemeConfig = { + baseStyle: ({ theme }: { theme: DefaultTheme }) => ({ + height: 8, + background: theme.colors.blue01, + borderRadius: 8, + }), +}; + +const sliderHandle: ComponentThemeConfig = { + baseStyle: ({ theme }: { theme: DefaultTheme }) => ({ + width: 16, + height: 16, + borderRadius: 9999, + backgroundColor: theme.colors.blue07, + outline: '2px solid', + outlineColor: theme.colors.greylight01, + cursor: 'unset', + '&:hover, &:focus, &:active': { + outline: '4px solid', + outlineColor: transparentize(0.3, theme.colors.blue03), + }, + '&:hover, &:focus': { + backgroundColor: theme.colors.blue08, + }, + '&:active': { + backgroundColor: theme.colors.blue09, + }, + }), +}; + +const slider = { + sliderTrack, + sliderHandle, +}; + +export default slider; diff --git a/yarn.lock b/yarn.lock index cd046644a..730aa34d4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4395,6 +4395,13 @@ dependencies: "@types/react" "*" +"@types/react-ranger@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/react-ranger/-/react-ranger-2.0.1.tgz#63f1b7d71b7b3e666a1a62a0482470d965b66aa6" + integrity sha512-q+vEB4N38b/x3jpqUns05/7QoggT/A3WKVD7X153InQdlhuwYEZ1KxrlNoJLrMpQvB6sDru8+Gq9bGcndU6yMA== + dependencies: + "@types/react" "*" + "@types/react-syntax-highlighter@11.0.5": version "11.0.5" resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.5.tgz#0d546261b4021e1f9d85b50401c0a42acb106087" @@ -14017,6 +14024,11 @@ react-popper@^2.2.4, react-popper@^2.2.5: react-fast-compare "^3.0.1" warning "^4.0.2" +react-ranger@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/react-ranger/-/react-ranger-2.1.0.tgz#a9007d67a7871736fffc5fe0d7c54a736771e9a1" + integrity sha512-d8ezhyX3v/KlN8SkyoE5e8Dybsdmn94eUAqBDsAPrVhZde8sVt6pLJw4fC784KiMmS4LyAjvtjGxhAEqjjGYgw== + react-refresh@^0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f"