From 1d410d919c359d94e7e0559754f9d4115ebfc790 Mon Sep 17 00:00:00 2001 From: Stuart Hendren Date: Mon, 24 May 2021 15:45:40 +0000 Subject: [PATCH] Improvements to AppBar, Link and Slider Changes the grey stack so affects other components. See #81 --- .github/workflows/build.yml | 5 +- package-lock.json | 42 +++ package.json | 3 + src/components/Accordion/Accordion.tsx | 7 +- src/components/Alert/Alert.tsx | 6 +- src/components/AppBar/AppBar.stories.tsx | 3 + src/components/AppBar/AppBar.tsx | 10 +- src/components/Avatar/Avatar.tsx | 2 +- src/components/Button/Button.stories.tsx | 5 + src/components/Button/Button.tsx | 7 + src/components/Card/Card.stories.tsx | 14 +- src/components/Checkbox/Checkbox.tsx | 15 +- .../Container/Container.stories.tsx | 2 +- src/components/Grid/Grid.stories.tsx | 61 ++-- src/components/Input/Input.tsx | 17 +- src/components/Link/Link.stories.tsx | 2 + src/components/Link/Link.tsx | 14 +- src/components/Slider/Slider.stories.tsx | 144 +++++++++- src/components/Slider/Slider.tsx | 261 ++++++++++++++++-- src/components/Tooltip/Tooltip.tsx | 22 +- src/docs/util/GridBox.tsx | 14 - src/utils/stitches.config.ts | 61 +++- src/utils/test-utils.tsx | 5 + 23 files changed, 586 insertions(+), 136 deletions(-) delete mode 100644 src/docs/util/GridBox.tsx diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a19f1bab..131146a3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,10 @@ jobs: - name: Set up Node uses: actions/setup-node@v1 with: - node-version: '16' + node-version: '14' + + - name: npm 7 + run: npm i -g npm@7 --registry=https://registry.npmjs.org - name: Install deps and build (with cache) if: ${{ !env.ACT }} diff --git a/package-lock.json b/package-lock.json index 63e8e7ab..e0877c4a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,8 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { + "@committed/hooks": "^0.4.0", + "@radix-ui/colors": "^0.1.4", "@radix-ui/react-accordion": "^0.0.14", "@radix-ui/react-avatar": "^0.0.13", "@radix-ui/react-checkbox": "^0.0.15", @@ -49,6 +51,7 @@ "react-dom": "^17.0.2", "react-is": "^17.0.2", "react-router-dom": "^5.2.0", + "resize-observer-polyfill": "^1.5.1", "rollpkg": "^0.5.6", "size-limit": "^4.10.2", "story-description-loader": "^1.0.0", @@ -1590,6 +1593,17 @@ "node": ">=0.1.95" } }, + "node_modules/@committed/hooks": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@committed/hooks/-/hooks-0.4.0.tgz", + "integrity": "sha512-l+v9cS/3DuzkyZDLwgnzj4kFph/vp8bEB6z7HcFeuPcKW87cs4K08IcMqQMDS0TH/n+2X0Qw+/U+aOvrQh2VUQ==", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16" + } + }, "node_modules/@cspell/dict-aws": { "version": "1.0.14", "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-1.0.14.tgz", @@ -3104,6 +3118,11 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@radix-ui/colors": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/colors/-/colors-0.1.4.tgz", + "integrity": "sha512-w5BxyjellZVWFCoeE6VH6SlW62PwkdTuo/ZG2CQ6Y7l9bZSBO+AFy68/T9zSiJeEclG+qIsKjQx0CtybEA4oxg==" + }, "node_modules/@radix-ui/number": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-0.0.6.tgz", @@ -31059,6 +31078,12 @@ "node": ">=0.10.5" } }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "dev": true + }, "node_modules/resolve": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", @@ -40497,6 +40522,12 @@ "minimist": "^1.2.0" } }, + "@committed/hooks": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@committed/hooks/-/hooks-0.4.0.tgz", + "integrity": "sha512-l+v9cS/3DuzkyZDLwgnzj4kFph/vp8bEB6z7HcFeuPcKW87cs4K08IcMqQMDS0TH/n+2X0Qw+/U+aOvrQh2VUQ==", + "requires": {} + }, "@cspell/dict-aws": { "version": "1.0.14", "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-1.0.14.tgz", @@ -41760,6 +41791,11 @@ "integrity": "sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q==", "dev": true }, + "@radix-ui/colors": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/colors/-/colors-0.1.4.tgz", + "integrity": "sha512-w5BxyjellZVWFCoeE6VH6SlW62PwkdTuo/ZG2CQ6Y7l9bZSBO+AFy68/T9zSiJeEclG+qIsKjQx0CtybEA4oxg==" + }, "@radix-ui/number": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-0.0.6.tgz", @@ -63947,6 +63983,12 @@ "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", "dev": true }, + "resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "dev": true + }, "resolve": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", diff --git a/package.json b/package.json index eb9a67b8..264c8301 100644 --- a/package.json +++ b/package.json @@ -139,6 +139,7 @@ "react-dom": "^17.0.2", "react-is": "^17.0.2", "react-router-dom": "^5.2.0", + "resize-observer-polyfill": "^1.5.1", "rollpkg": "^0.5.6", "size-limit": "^4.10.2", "story-description-loader": "^1.0.0", @@ -147,6 +148,8 @@ "typescript": "^4.2.4" }, "dependencies": { + "@committed/hooks": "^0.4.0", + "@radix-ui/colors": "^0.1.4", "@radix-ui/react-accordion": "^0.0.14", "@radix-ui/react-avatar": "^0.0.13", "@radix-ui/react-checkbox": "^0.0.15", diff --git a/src/components/Accordion/Accordion.tsx b/src/components/Accordion/Accordion.tsx index a717a721..034d141c 100644 --- a/src/components/Accordion/Accordion.tsx +++ b/src/components/Accordion/Accordion.tsx @@ -1,12 +1,11 @@ +import { Button, Header, Item, Panel, Root } from '@radix-ui/react-accordion' import React, { - forwardRef, - PropsWithChildren, ComponentProps, + forwardRef, ForwardRefExoticComponent, } from 'react' -import { styled, keyframes } from 'stitches.config' +import { keyframes, styled } from 'stitches.config' import { ChevronDown } from '../Icons' -import { Root, Item, Header, Button, Panel } from '@radix-ui/react-accordion' const slideDown = keyframes({ from: { height: 0 }, diff --git a/src/components/Alert/Alert.tsx b/src/components/Alert/Alert.tsx index c2a6ae88..dcd07344 100644 --- a/src/components/Alert/Alert.tsx +++ b/src/components/Alert/Alert.tsx @@ -27,9 +27,9 @@ const StyledAlert = styled(DEFAULT_TAG, { variants: { severity: { ghost: { - backgroundColor: '$paper', - borderColor: '$grey500', - color: '$text', + backgroundColor: '$greyBgLight', + borderColor: '$greyBorder', + color: '$greyText', }, warning: { backgroundColor: '$warningBackground', diff --git a/src/components/AppBar/AppBar.stories.tsx b/src/components/AppBar/AppBar.stories.tsx index d2a247e3..e59ed6f6 100644 --- a/src/components/AppBar/AppBar.stories.tsx +++ b/src/components/AppBar/AppBar.stories.tsx @@ -33,7 +33,10 @@ export const WithReactRouter: React.FC = () => { *': { - color: '$primaryContrast', + color: '$brandContrast', }, }) @@ -67,7 +67,7 @@ export const AppBarButton = forwardRef< HTMLButtonElement, PropsWithChildren >(({ children, ...props }, forwardedRef) => ( - )) as AppBarButtonComponent diff --git a/src/components/Avatar/Avatar.tsx b/src/components/Avatar/Avatar.tsx index 7b616423..564c749e 100644 --- a/src/components/Avatar/Avatar.tsx +++ b/src/components/Avatar/Avatar.tsx @@ -42,7 +42,7 @@ const StyledFallback = styled(Fallback, { */ export const Avatar: React.FC = ({ src, - backgroundColor = '$grey500', + backgroundColor = '$greyLine', color = '$text', alt = 'Avatar', children, diff --git a/src/components/Button/Button.stories.tsx b/src/components/Button/Button.stories.tsx index 168ea7aa..c80db85c 100644 --- a/src/components/Button/Button.stories.tsx +++ b/src/components/Button/Button.stories.tsx @@ -78,6 +78,11 @@ export const Disabled = () => ( ) +/** + * A `brand` variant is also supplied for particular uses cases, like in the AppBar, where changing to the dark theme does not affect the coloring. + */ +export const Brand = () => + /** * This uses the force prop to simulate hover, focus and active states so they can be compared at the same time. * This prop is not intended for normal use and the buttons here will not interact normally. diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index f7d6071d..ee3dedfd 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -69,6 +69,13 @@ const StyledButton = styled(DEFAULT_TAG, { variants: { variant: { + brand: { + $$active: '$colors$brandActive', + $$lowlight: '$colors$brandLowlight', + $$hover: '$colors$brandHighlight', + backgroundColor: '$colors$brand', + color: '$colors$brandContrast', + }, primary: { $$active: '$colors$primaryActive', $$lowlight: '$colors$primaryLowlight', diff --git a/src/components/Card/Card.stories.tsx b/src/components/Card/Card.stories.tsx index 5a164160..12f1787a 100644 --- a/src/components/Card/Card.stories.tsx +++ b/src/components/Card/Card.stories.tsx @@ -18,7 +18,7 @@ export const Default: React.FC = () => { return ( Default Card - + Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, @@ -35,7 +35,7 @@ export const Classic = () => { Default Card - + Lorem Ipsum is simply dummy text... @@ -43,7 +43,7 @@ export const Classic = () => { Default Interactive - + Lorem Ipsum is simply dummy text... @@ -61,7 +61,7 @@ export const Outline = () => { Outline - + Lorem Ipsum is simply dummy text... @@ -69,7 +69,7 @@ export const Outline = () => { Outline Interactive - + Lorem Ipsum is simply dummy text... @@ -87,13 +87,13 @@ export const Ghost = () => { Ghost - + Lorem Ipsum is simply dummy text... Ghost - + Lorem Ipsum is simply dummy text... diff --git a/src/components/Checkbox/Checkbox.tsx b/src/components/Checkbox/Checkbox.tsx index c7e11f61..d0e90a5f 100644 --- a/src/components/Checkbox/Checkbox.tsx +++ b/src/components/Checkbox/Checkbox.tsx @@ -1,10 +1,7 @@ import { Indicator, Root } from '@radix-ui/react-checkbox' -import React, { - ComponentProps, - forwardRef, - ForwardRefExoticComponent, -} from 'react' -import { styled } from 'stitches.config' +import type * as Polymorphic from '@radix-ui/react-polymorphic' +import React, { forwardRef, ForwardRefExoticComponent } from 'react' +import { CSS, StitchesVariants, styled } from 'stitches.config' import { Check, CheckIndeterminate } from '../Icons' const StyledRoot = styled(Root, { @@ -129,7 +126,11 @@ const StyledIndicator = styled(Indicator, { width: '100%', }) -type CheckboxProps = ComponentProps +type CheckboxCSSProp = { css?: CSS } +type CheckboxVariants = StitchesVariants +type CheckboxProps = Polymorphic.OwnProps & + CheckboxVariants & + CheckboxCSSProp /** * Checkboxes can be used as toggle actions or as part of input forms. diff --git a/src/components/Container/Container.stories.tsx b/src/components/Container/Container.stories.tsx index 1f39f5e8..5864f4cc 100644 --- a/src/components/Container/Container.stories.tsx +++ b/src/components/Container/Container.stories.tsx @@ -55,7 +55,7 @@ export const Responsive: React.FC = () => ( { +const keys = ['yellow', 'sand', 'blue', 'green', 'red', 'orange'] +const randomColor = (): CSS['backgroundColor'] => { const color = keys[Math.floor(Math.random() * keys.length)] return `$${color}300` } +const border = '1px solid $greyLine' + type BoxProps = React.ComponentProps type GridBoxProps = Omit & { - css?: any + css?: CSS } const GridBox: React.FC = ({ css, ...props }) => ( @@ -157,7 +158,7 @@ const Template: Story<{ css={{ justifyItems, alignItems, - border: '1px solid grey', + border, gridTemplateColumns: '$7 $7', gridTemplateRows: '$7 $7', width: '$10', @@ -188,7 +189,7 @@ export const JustifyItems = () => ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( css={{ height: '$10', alignContent: 'start', - border: '1px solid grey', + border, gridTemplateColumns: '$7 $7', gridTemplateRows: '$7 $7', }} @@ -475,7 +476,7 @@ export const AlignContent = () => ( css={{ minHeight: '$10', alignContent: 'end', - border: '1px solid grey', + border, gridTemplateColumns: '$7 $7', gridTemplateRows: '$7 $7', }} @@ -489,7 +490,7 @@ export const AlignContent = () => ( css={{ minHeight: '$10', alignContent: 'center', - border: '1px solid grey', + border, gridTemplateColumns: '$7 $7', gridTemplateRows: '$7 $7', }} @@ -503,7 +504,7 @@ export const AlignContent = () => ( css={{ minHeight: '$10', alignContent: 'stretch', - border: '1px solid grey', + border, gridTemplateColumns: '$7 $7', gridTemplateRows: '$7 $7', }} @@ -517,7 +518,7 @@ export const AlignContent = () => ( css={{ minHeight: '$10', alignContent: 'space-around', - border: '1px solid grey', + border, gridTemplateColumns: '$7 $7', gridTemplateRows: '$7 $7', }} @@ -531,7 +532,7 @@ export const AlignContent = () => ( css={{ minHeight: '$10', alignContent: 'space-between', - border: '1px solid grey', + border, gridTemplateColumns: '$7 $7', gridTemplateRows: '$7 $7', }} @@ -545,7 +546,7 @@ export const AlignContent = () => ( css={{ minHeight: '$10', alignContent: 'space-evenly', - border: '1px solid grey', + border, gridTemplateColumns: '$7 $7', gridTemplateRows: '$7 $7', }} @@ -738,7 +739,7 @@ export const ItemArea = () => ( export const Self = () => ( { Click to change Router path diff --git a/src/components/Link/Link.tsx b/src/components/Link/Link.tsx index 67019faa..a98250fa 100644 --- a/src/components/Link/Link.tsx +++ b/src/components/Link/Link.tsx @@ -1,6 +1,6 @@ -import React, { forwardRef, ComponentProps } from 'react' -import { CSS, StitchesVariants, styled } from 'stitches.config' import type * as Polymorphic from '@radix-ui/react-polymorphic' +import React, { forwardRef } from 'react' +import { CSS, styled } from 'stitches.config' const DEFAULT_TAG = 'a' @@ -58,11 +58,13 @@ export const StyledLink = styled(DEFAULT_TAG, { }) type LinkCSSProp = { css?: CSS } -type LinkVariants = ComponentProps -type LinkVariants = StitchesVariants -type LinkOwnProps = LinkCSSProp & Omit +type LinkVariants = Polymorphic.OwnProps +type LinkOwnProps = LinkCSSProp & Omit -type LinkComponent = Polymorphic.ForwardRefComponent +type LinkComponent = Polymorphic.ForwardRefComponent< + typeof DEFAULT_TAG, + LinkOwnProps +> export const Link = forwardRef(({ href, ...props }, forwardedRef) => { return ( diff --git a/src/components/Slider/Slider.stories.tsx b/src/components/Slider/Slider.stories.tsx index d2bac8a7..5967cfe4 100644 --- a/src/components/Slider/Slider.stories.tsx +++ b/src/components/Slider/Slider.stories.tsx @@ -1,17 +1,145 @@ -import React from 'react' -import { Story, Meta } from '@storybook/react' -import { Slider, SliderProps } from '.' +import { Meta, Story } from '@storybook/react' +import React, { useState } from 'react' +import { Slider } from '.' +import { Column, Row } from '../' +import { usePortalled } from '../../docs/util' export default { title: 'Components/Slider', component: Slider, } as Meta -export const Default: React.FC = () => { - return +export const Default: React.FC = () => + +/** A primary and secondary variant can be used. Secondary, is the default. */ +export const Variants: Story = (_args, context) => { + const portalled = usePortalled(context) + return ( + + + + + ) +} + +/** + * By default the `Slider` labels are rendered using a react portal. However, this can cause issues. For example, in storybook rendering with a portal puts the label outside of the theme decorator + * so portalled label remains in the light theme. + * If we switch to un-portalled, then they are rendered inside the theme decorator but due to storybook again they may not appear in the right place in the docs. + * + * You can toggle the portalled state in the toolbar to see how they function in the docs and canvas, but in most situations they should work correctly with the default. + * + * The use of `usePortalled` in these stories facilitates this switch, you can ignore it in normal use. + */ +export const Portalled: Story = (_args, context) => { + const portalled = usePortalled(context) + return ( + + `Currently${portalled ? ' ' : ' not '}portalled ` + } + portalled={portalled} + /> + ) +} + +export const Disabled: Story = (_args, context) => { + const portalled = usePortalled(context) + return ( + + + + + ) +} + +export const Vertical: Story = (_args, context) => { + const portalled = usePortalled(context) + return ( + + + + + + + + + ) } -const Template: Story = (args) => +/** + * Passing an array as the `defaultValue` or `value` will add multiple markers on the track. + */ +export const Contained: Story = (_args, context) => { + const portalled = usePortalled(context) + return ( + + + + + ) +} -export const Primary = Template.bind({}) -Primary.args = {} +/** + * Use `onValueChange` to update the controlled value array. + */ +export const Controlled: Story = (_args, context) => { + const portalled = usePortalled(context) + const [value, setValue] = useState([50]) + return +} + +/** + * The Slider labels cna be shown, `always`, on `hover`, or `none`. Using the `labelStyle` prop. + */ +export const LabelMarkers: Story = (_args, context) => { + const portalled = usePortalled(context) + return ( + + + + + + ) +} + +/** + * A custom label function can be provided to format the label + */ +export const LabelContent: Story = (_args, context) => { + const portalled = usePortalled(context) + return ( + `Current value is ${value}`} + portalled={portalled} + /> + ) +} + +/** + * The props `min`, `max` and `step` can be used to control the range. + */ +export const RangeValues: Story = (_args, context) => { + const portalled = usePortalled(context) + return +} diff --git a/src/components/Slider/Slider.tsx b/src/components/Slider/Slider.tsx index 7c415902..38426aad 100644 --- a/src/components/Slider/Slider.tsx +++ b/src/components/Slider/Slider.tsx @@ -1,23 +1,246 @@ -import React from 'react' -import { styled } from 'stitches.config' -import * as RadixSlider from '@radix-ui/react-slider' +import { useHover } from '@committed/hooks' +import type * as Polymorphic from '@radix-ui/react-polymorphic' +import { Range, Root, Thumb, Track } from '@radix-ui/react-slider' +import { useCallbackRef } from '@radix-ui/react-use-callback-ref' +import { useControllableState } from '@radix-ui/react-use-controllable-state' +import React, { + FC, + forwardRef, + ForwardRefExoticComponent, + useMemo, + useRef, +} from 'react' +import { CSS, StitchesVariants, styled } from 'stitches.config' +import { Tooltip } from '../' + +type LabelStyle = 'always' | 'hover' | 'none' +type LabelSide = React.ComponentProps['side'] + +const SliderTrack = styled(Track, { + position: 'relative', + flexGrow: 1, + borderRadius: '$default', + '&[data-orientation="horizontal"]': { + height: 2, + }, + '&[data-orientation="vertical"]': { + width: 2, + height: 100, + }, +}) + +const SliderRange = styled(Range, { + position: 'absolute', + background: '$primary', + borderRadius: 'inherit', + '&[data-orientation="horizontal"]': { + height: '100%', + }, + '&[data-orientation="vertical"]': { + width: '100%', + }, +}) + +const StyledThumb = styled(Thumb, { + position: 'relative', + display: 'block', + width: 15, + height: 15, + outline: 'none', + boxShadow: '$1', + borderRadius: '$round', + + '&::after': { + content: '""', + position: 'absolute', + top: 0, + right: 0, + bottom: 0, + left: 0, + zIndex: -2, + backgroundColor: '$transparency000', + transform: 'scale(1)', + borderRadius: '$round', + transition: 'transform 200ms cubic-bezier(0.22, 1, 0.36, 1)', + }, + + '&:focus': { + '&::after': { + transform: 'scale(2)', + }, + }, +}) + +export const StyledSlider = styled(Root, { + $$track: '$colors$greyBg', + $$trackHover: '$colors$greyBgHover', + $$range: '$colors$default', + + position: 'relative', + display: 'flex', + alignItems: 'center', + flexShrink: 0, + userSelect: 'none', + touchAction: 'none', + height: 15, + flexGrow: 1, + + '&[data-orientation="vertical"]': { + flexDirection: 'column', + width: 15, + }, + + [`& ${SliderTrack}`]: { + backgroundColor: '$$track', + }, + + [`& ${StyledThumb}`]: { + backgroundColor: '$$range', + }, + + [`& ${SliderRange}`]: { + backgroundColor: '$$range', + }, + + '@hover': { + '&:hover': { + [`& ${SliderTrack}`]: { + backgroundColor: '$$trackHover', + }, + }, + }, + + variants: { + variant: { + primary: { + $$range: '$colors$brandYellow', + }, + secondary: { + $$range: '$colors$default', + }, + }, + }, + + defaultVariants: { + variant: 'secondary', + }, +}) + +type SliderThumbProps = Polymorphic.OwnProps & { + labelStyle: LabelStyle + value: number | string + labelSide: LabelSide + portalled: boolean +} + +export const SliderThumb: FC = ({ + value, + labelStyle, + labelSide, + portalled, + ...props +}) => { + const trackRef = useRef(null) + const [isHovered] = useHover(trackRef) + + const isOpen = useMemo(() => { + if (labelStyle == 'always') { + return true + } + if (labelStyle == 'none') { + return false + } + return isHovered + }, [isHovered, labelStyle]) + + return ( + + + + ) +} + +type SliderCSSProp = { css?: CSS } +type SliderVariants = StitchesVariants +type SliderOwnProps = Polymorphic.OwnProps & + SliderVariants & + SliderCSSProp & { + /** Add labels to the markers, permanently, on hover or none */ + labelStyle?: LabelStyle + /** Move the label location */ + labelSide?: LabelSide + /** Label function, to configure the displayed value */ + labelFunction?: (value: number) => string | number + /** Set `false` to turn off the use of portals for labels */ + portalled?: boolean + } /** - * Slider component + * Sliders can be used for selection from a (numeric) range of values. + * + * Based on [Radix Slider](https://radix-ui.com/primitives/docs/components/slider). */ -export const StyledSlider = styled('div', {}) - -type StyledSliderProps = React.ComponentProps -export type SliderProps = StyledSliderProps - -export const Slider: React.FC = () => ( - - - - - - - - +export const Slider: ForwardRefExoticComponent = forwardRef< + HTMLSpanElement, + SliderOwnProps +>( + ( + { + min = 0, + value, + defaultValue = [min], + onValueChange, + labelStyle = 'hover', + labelSide = 'top', + labelFunction = (value) => value, + portalled = true, + ...props + }, + forwardedRef + ) => { + if (process.env.NODE_ENV !== 'production') { + const hasRange = Array.isArray(defaultValue || value) + if (!hasRange) { + console.warn( + '[Slider] The provided value, or defaultValue must be an array of numbers.' + ) + } + } + const [values = [], setValues] = useControllableState({ + prop: value, + defaultProp: defaultValue, + onChange: onValueChange, + }) + const handleLabelFunction = useCallbackRef((value: number) => + labelFunction(value) + ) + + return ( + + + + + {values.map((value: number, i: number) => ( + + ))} + + ) + } ) -Slider.toString = () => `.${StyledSlider.className}` diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx index f01526b8..f09f457e 100644 --- a/src/components/Tooltip/Tooltip.tsx +++ b/src/components/Tooltip/Tooltip.tsx @@ -2,8 +2,7 @@ import { Slot } from '@radix-ui/react-slot' import { Arrow, Content, Root, Trigger } from '@radix-ui/react-tooltip' import React, { FC } from 'react' import { styled } from 'stitches.config' -// TODO import { Text } from '../Text' -// TODO Fix dark mode - colours don't change +import { Text } from '../Text' type TooltipProps = React.ComponentProps & React.ComponentProps & { @@ -61,18 +60,15 @@ export const Tooltip: FC = ({ {...props} multiline={multiline} > - - {/* */} + {content} - - {/* */} + diff --git a/src/docs/util/GridBox.tsx b/src/docs/util/GridBox.tsx deleted file mode 100644 index 25fda2fc..00000000 --- a/src/docs/util/GridBox.tsx +++ /dev/null @@ -1,14 +0,0 @@ -// import React from 'react' -// import { Box, BoxProps } from '../../src' -// import { randomColor } from './colors' - -// export const GridBox = (props: BoxProps) => ( -// -// ) diff --git a/src/utils/stitches.config.ts b/src/utils/stitches.config.ts index 021f2741..4b1dae8b 100644 --- a/src/utils/stitches.config.ts +++ b/src/utils/stitches.config.ts @@ -1,5 +1,6 @@ import { createCss, StitchesCss } from '@stitches/react' export type { StitchesVariants } from '@stitches/react' +import { sand, sandDark } from '@radix-ui/colors' const stitches = createCss({ theme: { @@ -14,6 +15,8 @@ const stitches = createCss({ yellow800: '#664500', yellow900: '#332200', + ...sand, + grey100: '#d8d8d8', grey200: '#b2b2b2', grey300: '#8b8b8b', @@ -64,23 +67,40 @@ const stitches = createCss({ orange800: '#5c2f15', orange900: '#2e180b', - transparency100: 'hsla(0,0%,0%,0.1)', - transparency200: 'hsla(0,0%,0%,0.2)', - transparency300: 'hsla(0,0%,0%,0.3)', - transparency400: 'hsla(0,0%,0%,0.4)', - transparency500: 'hsla(0,0%,0%,0.5)', - transparency600: 'hsla(0,0%,0%,0.6)', - transparency700: 'hsla(0,0%,0%,0.7)', - transparency800: 'hsla(0,0%,0%,0.8)', - transparency900: 'hsla(0,0%,0%,0.9)', + transparency000: 'hsla(0,0%,0%,0.05)', + transparency100: 'hsla(0,0%,0%,0.08)', + transparency200: 'hsla(0,0%,0%,0.1)', + transparency300: 'hsla(0,0%,0%,0.15)', + transparency400: 'hsla(0,0%,0%,0.2)', + transparency500: 'hsla(0,0%,0%,0.25)', + transparency600: 'hsla(0,0%,0%,0.3)', + transparency700: 'hsla(0,0%,0%,0.35)', + transparency800: 'hsla(0,0%,0%,0.4)', + transparency900: 'hsla(0,0%,0%,0.5)', brandYellow: '#ffbb00', brandGrey: '#565555', + brand: '$grey500', + brandHighlight: '$grey700', + brandLowlight: '$grey400', + brandBackground: '$grey100', + brandContrast: '$brandYellow', + brandActive: '#ffbb00aa', white: '#FFFFFF', black: '#000000', // Semantic colors + greyBgApp: '$sand000', + greyBgLight: '$sand100', + greyBg: '$sand200', + greyBgHover: '$sand300', + greyBgActive: '$sand400', + greyLine: '$sand500', + greyBorder: '$sand600', + greyBorderHover: '$sand700', + greyPure: '$sand800', + greyText: '$sand900', background: '#f7f7f7', paper: '$white', @@ -372,7 +392,30 @@ export const lightTheme = theme('light-theme', {}) export const darkTheme = theme('dark-theme', { colors: { + ...sandDark, + + transparency000: 'hsla(0,0%,0%,0.55)', + transparency100: 'hsla(0,0%,0%,0.58)', + transparency200: 'hsla(0,0%,0%,0.6)', + transparency300: 'hsla(0,0%,0%,0.65)', + transparency400: 'hsla(0,0%,0%,0.7)', + transparency500: 'hsla(0,0%,0%,0.75)', + transparency600: 'hsla(0,0%,0%,0.8)', + transparency700: 'hsla(0,0%,0%,0.85)', + transparency800: 'hsla(0,0%,0%,0.9)', + transparency900: 'hsla(0,0%,0%,0.95)', + // Semantic colors + greyBgApp: '$sand000', + greyBgLight: '$sand100', + greyBg: '$sand200', + greyBgHover: '$sand300', + greyBgActive: '$sand400', + greyLine: '$sand500', + greyBorder: '$sand600', + greyBorderHover: '$sand700', + greyPure: '$sand800', + greyText: '$sand900', background: '#000000', text: '#f7f7f7', diff --git a/src/utils/test-utils.tsx b/src/utils/test-utils.tsx index da8c1498..3803ee18 100644 --- a/src/utils/test-utils.tsx +++ b/src/utils/test-utils.tsx @@ -11,6 +11,11 @@ import { render, RenderOptions, RenderResult } from '@testing-library/react' import userEvent from '@testing-library/user-event' import React from 'react' import { ThemeProvider } from '..' +import ResizeObserver from 'resize-observer-polyfill' + +// Use the polyfill for the ResizeObserver. +// This is used in some components. +global.ResizeObserver = ResizeObserver const LightTheme: React.FC = ({ children }) => ( {children}