diff --git a/.storybook/main.ts b/.storybook/main.ts index e3d6d47a..a60e41f5 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -15,7 +15,7 @@ const storybookConfig: StorybookConfig = { } }, '@storybook/addon-storysource', - './theme-picker/register.ts' + './theme-settings/register.ts' ], core: { builder: 'webpack5' diff --git a/.storybook/preview.ts b/.storybook/preview.ts index 088dfca6..70151b31 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -1,8 +1,8 @@ import { DecoratorFn, Parameters } from '@storybook/react'; import { withGlobalStyle } from './decorators/withGlobalStyle'; -import { withThemesProvider } from './theme-picker/ThemeProvider'; +import { withThemeSettings } from './theme-settings/ThemeProvider'; -export const decorators: DecoratorFn[] = [withGlobalStyle, withThemesProvider]; +export const decorators: DecoratorFn[] = [withGlobalStyle, withThemeSettings]; export const parameters: Parameters = { layout: 'fullscreen', diff --git a/.storybook/theme-picker/ThemeList.tsx b/.storybook/theme-picker/ThemeList.tsx deleted file mode 100644 index 87e111b7..00000000 --- a/.storybook/theme-picker/ThemeList.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { useAddonState } from '@storybook/api'; -import React, { useCallback } from 'react'; -import styled from 'styled-components'; - -import themes from '../../src/common/themes'; -import { Theme } from '../../src/types'; -import { THEMES_ID } from './constants'; -import { ThemeButton } from './ThemeButton'; - -const { - original, - rainyDay, - vaporTeal, - theSixtiesUSA, - olive, - tokyoDark, - rose, - plum, - matrix, - travel, - ...otherThemes -} = themes; - -const themeList = [ - original, - rainyDay, - vaporTeal, - theSixtiesUSA, - olive, - tokyoDark, - rose, - plum, - matrix, - travel, - ...Object.values(otherThemes) -]; - -type ThemesProps = { - active?: boolean; -}; - -const Wrapper = styled.div<{ theme: Theme }>` - display: grid; - padding: 1em; - gap: 1em; - grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); - grid-template-rows: repeat(auto-fill, 40px); - background-color: ${({ theme }) => theme.material}; -`; - -export function ThemeList({ active }: ThemesProps) { - const [themeName, setThemeName] = useAddonState(THEMES_ID, 'original'); - - const handleChoose = useCallback( - (newThemeName: string) => { - setThemeName(newThemeName); - }, - [setThemeName] - ); - - if (!active) { - return <></>; - } - - return ( - <Wrapper key={THEMES_ID} theme={themes.original}> - {themeList.map(theme => ( - <ThemeButton - active={themeName === theme.name} - key={theme.name} - onChoose={handleChoose} - theme={theme} - /> - ))} - </Wrapper> - ); -} diff --git a/.storybook/theme-picker/ThemeProvider.tsx b/.storybook/theme-picker/ThemeProvider.tsx deleted file mode 100644 index e8dd4e2a..00000000 --- a/.storybook/theme-picker/ThemeProvider.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { useAddonState } from '@storybook/client-api'; -import { DecoratorFn } from '@storybook/react'; -import React from 'react'; -import { ThemeProvider } from 'styled-components'; - -import themes from '../../src/common/themes/index'; -import { THEMES_ID } from './constants'; - -export const withThemesProvider: DecoratorFn = story => { - const [themeName] = useAddonState(THEMES_ID, 'original'); - - return ( - <ThemeProvider theme={themes[themeName] ?? themes.original}> - {story()} - </ThemeProvider> - ); -}; diff --git a/.storybook/theme-picker/ThemeButton.tsx b/.storybook/theme-settings/ThemeButton.tsx similarity index 79% rename from .storybook/theme-picker/ThemeButton.tsx rename to .storybook/theme-settings/ThemeButton.tsx index 87c57329..53844113 100644 --- a/.storybook/theme-picker/ThemeButton.tsx +++ b/.storybook/theme-settings/ThemeButton.tsx @@ -1,6 +1,6 @@ import React, { useCallback } from 'react'; import { ThemeProvider } from 'styled-components'; -import { Button } from '../../src/Button/Button'; +import { Button } from '../../src/index'; import { Theme } from '../../src/types'; export function ThemeButton({ @@ -9,11 +9,11 @@ export function ThemeButton({ theme }: { active: boolean; - onChoose: (themeName: string) => void; + onChoose: (theme: Theme) => void; theme: Theme; }) { const handleClick = useCallback(() => { - onChoose(theme.name); + onChoose(theme); }, []); return ( diff --git a/.storybook/theme-settings/ThemeProvider.tsx b/.storybook/theme-settings/ThemeProvider.tsx new file mode 100644 index 00000000..a537d3c9 --- /dev/null +++ b/.storybook/theme-settings/ThemeProvider.tsx @@ -0,0 +1,41 @@ +import { useAddonState } from '@storybook/client-api'; +import { DecoratorFn } from '@storybook/react'; +import React from 'react'; +import { createGlobalStyle, ThemeProvider } from 'styled-components'; + +import themes from '../../src/common/themes/index'; +import { SCALE } from '../../src/common/constants'; +import { Theme } from '../../src/common/themes/types'; +import { THEMES_ID } from './constants'; + +const GlobalScaledFont = createGlobalStyle` + html { + font-size: ${({ theme }: { theme: Theme }) => + (theme?.scale ?? SCALE) * 2 + 12}px; + } +`; + +export const withThemeSettings: DecoratorFn = story => { + const [themeName] = useAddonState(`${THEMES_ID}/themeName`, 'original'); + const [themeScale] = useAddonState( + `${THEMES_ID}/themeScale`, + themes.original.scale + ); + const [themeShadow] = useAddonState( + `${THEMES_ID}/themeShadow`, + themes.original.shadow + ); + + const theme: Theme = { + ...themes[themeName], + scale: themeScale, + shadow: themeShadow + }; + + return ( + <ThemeProvider theme={theme}> + <GlobalScaledFont /> + {story()} + </ThemeProvider> + ); +}; diff --git a/.storybook/theme-settings/ThemeSettings.tsx b/.storybook/theme-settings/ThemeSettings.tsx new file mode 100644 index 00000000..4f1f6b18 --- /dev/null +++ b/.storybook/theme-settings/ThemeSettings.tsx @@ -0,0 +1,135 @@ +import { useAddonState } from '@storybook/api'; +import React, { useCallback } from 'react'; +import styled, { ThemeProvider } from 'styled-components'; +import { SCALE } from '../../src/common/constants'; + +import themes from '../../src/common/themes'; +import { Checkbox, GroupBox, Slider } from '../../src/index'; +import { Theme } from '../../src/types'; +import { THEMES_ID } from './constants'; +import { ThemeButton } from './ThemeButton'; + +const { + original, + rainyDay, + vaporTeal, + theSixtiesUSA, + olive, + tokyoDark, + rose, + plum, + matrix, + travel, + ...otherThemes +} = themes; + +const themeList = [ + original, + rainyDay, + vaporTeal, + theSixtiesUSA, + olive, + tokyoDark, + rose, + plum, + matrix, + travel, + ...Object.values(otherThemes) +]; + +type ThemesProps = { + active?: boolean; +}; + +const Wrapper = styled.div<{ theme: Theme }>` + padding: 1em; + background-color: ${({ theme }) => theme.material}; +`; + +const ThemesGrid = styled.div` + display: grid; + grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); + grid-template-rows: repeat(auto-fill, 40px); + gap: 1em; + margin-bottom: 1em; +`; + +const marks = [ + { label: '1', value: 1 }, + { label: '2', value: 2 }, + { label: '3', value: 3 }, + { label: '4', value: 4 }, + { label: '5', value: 5 } +]; + +export function ThemeSettings({ active }: ThemesProps) { + const [themeName, setThemeName] = useAddonState( + `${THEMES_ID}/themeName`, + 'original' + ); + const [themeScale, setThemeScale] = useAddonState( + `${THEMES_ID}/themeScale`, + themes.original.scale + ); + const [themeShadow, setThemeShadow] = useAddonState( + `${THEMES_ID}/themeShadow`, + themes.original.shadow + ); + + const handleChoose = useCallback( + (newTheme: Theme) => { + setThemeName(newTheme.name); + }, + [setThemeName] + ); + + const handleShadowChange = useCallback( + (event: React.ChangeEvent<HTMLInputElement>) => { + const checked = (event.target as HTMLInputElement).checked; + setThemeShadow(checked); + }, + [] + ); + + const handleScaleChange = useCallback( + (_: Event | React.SyntheticEvent, value: number) => { + setThemeScale(value); + }, + [] + ); + + if (!active) { + return <></>; + } + + return ( + <Wrapper key={THEMES_ID} theme={themes.original}> + <ThemesGrid> + {themeList.map(theme => ( + <ThemeButton + active={themeName === theme.name} + key={theme.name} + onChoose={handleChoose} + theme={theme} + /> + ))} + </ThemesGrid> + <ThemeProvider theme={themes.original}> + <GroupBox label='Options'> + <Checkbox + checked={themeShadow} + onChange={handleShadowChange} + label='Modern Shadows' + /> + <Slider + onChange={handleScaleChange} + min={1} + max={5} + marks={marks} + value={themeScale ?? SCALE} + /> + </GroupBox> + </ThemeProvider> + </Wrapper> + ); +} diff --git a/.storybook/theme-picker/constants.ts b/.storybook/theme-settings/constants.ts similarity index 100% rename from .storybook/theme-picker/constants.ts rename to .storybook/theme-settings/constants.ts diff --git a/.storybook/theme-picker/register.ts b/.storybook/theme-settings/register.ts similarity index 70% rename from .storybook/theme-picker/register.ts rename to .storybook/theme-settings/register.ts index 6c5d1834..60154c71 100644 --- a/.storybook/theme-picker/register.ts +++ b/.storybook/theme-settings/register.ts @@ -1,17 +1,17 @@ import addons, { makeDecorator, types } from '@storybook/addons'; import { THEMES_ID } from './constants'; -import { ThemeList } from './ThemeList'; +import { ThemeSettings } from './ThemeSettings'; addons.register(THEMES_ID, () => { addons.addPanel(`${THEMES_ID}/panel`, { - title: 'Themes', + title: 'Theme Settings', type: types.PANEL, - render: ThemeList + render: ThemeSettings }); }); export default makeDecorator({ - name: 'withThemesProvider', + name: 'withThemeSettingsProvider', parameterName: 'theme', wrapper: (getStory, context) => getStory(context) }); diff --git a/src/Avatar/Avatar.tsx b/src/Avatar/Avatar.tsx index 89990fd4..c88ab53f 100644 --- a/src/Avatar/Avatar.tsx +++ b/src/Avatar/Avatar.tsx @@ -1,5 +1,6 @@ import React, { forwardRef } from 'react'; -import styled from 'styled-components'; +import styled, { css } from 'styled-components'; +import { styledDimension } from '../common'; import { getSize } from '../common/utils'; import { CommonStyledProps } from '../types'; @@ -28,13 +29,13 @@ const StyledAvatar = styled.div< overflow: hidden; ${({ noBorder, theme }) => !noBorder && - ` - border-top: 2px solid ${theme.borderDark}; - border-left: 2px solid ${theme.borderDark}; - border-bottom: 2px solid ${theme.borderLightest}; - border-right: 2px solid ${theme.borderLightest}; - background: ${theme.material}; - `} + css` + border-top: ${styledDimension(1)} solid ${theme.borderDark}; + border-left: ${styledDimension(1)} solid ${theme.borderDark}; + border-bottom: ${styledDimension(1)} solid ${theme.borderLightest}; + border-right: ${styledDimension(1)} solid ${theme.borderLightest}; + background: ${theme.material}; + `} ${({ src }) => !src && ` diff --git a/src/Button/Button.spec.tsx b/src/Button/Button.spec.tsx index a980b3c1..963df13d 100644 --- a/src/Button/Button.spec.tsx +++ b/src/Button/Button.spec.tsx @@ -3,6 +3,7 @@ import React from 'react'; import { renderWithTheme, theme } from '../../test/utils'; import { blockSizes } from '../common/system'; +import { SCALE } from '../common/constants'; import { Button } from './Button'; @@ -10,6 +11,12 @@ const defaultProps = { children: 'click me' }; +const expectedBlockSizes = { + sm: `${blockSizes.sm * SCALE}px`, + md: `${blockSizes.md * SCALE}px`, + lg: `${blockSizes.lg * SCALE}px` +}; + describe('<Button />', () => { it('should render button', () => { const { getByRole } = render(<Button {...defaultProps} />); @@ -80,11 +87,11 @@ describe('<Button />', () => { ); const button = getByRole('button'); - expect(button).toHaveStyleRule('height', blockSizes.sm); + expect(button).toHaveStyleRule('height', expectedBlockSizes.sm); rerender(<Button {...defaultProps} size='lg' />); - expect(button).toHaveStyleRule('height', blockSizes.lg); + expect(button).toHaveStyleRule('height', expectedBlockSizes.lg); }); it('should handle square prop', () => { @@ -92,7 +99,7 @@ describe('<Button />', () => { const button = getByRole('button'); expect(button).toHaveStyleRule('padding', '0'); - expect(button).toHaveStyleRule('width', blockSizes.md); + expect(button).toHaveStyleRule('width', expectedBlockSizes.md); }); it('should render children', () => { diff --git a/src/Button/Button.tsx b/src/Button/Button.tsx index 6cbf6aa0..d84cd564 100644 --- a/src/Button/Button.tsx +++ b/src/Button/Button.tsx @@ -6,9 +6,10 @@ import { createDisabledTextStyles, createFlatBoxStyles, createHatchedBackground, - focusOutline + focusOutline, + styledDimension } from '../common'; -import { blockSizes } from '../common/system'; +import { styledBlockSize } from '../common/system'; import { noOp } from '../common/utils'; import { CommonStyledProps, Sizes } from '../types'; @@ -54,16 +55,17 @@ const commonButtonStyles = css<StyledButtonProps>` display: inline-flex; align-items: center; justify-content: center; - height: ${({ size = 'md' }) => blockSizes[size]}; + height: ${({ size = 'md' }) => styledBlockSize(size)}; width: ${({ fullWidth, size = 'md', square }) => - fullWidth ? '100%' : square ? blockSizes[size] : 'auto'}; - padding: ${({ square }) => (square ? 0 : `0 10px`)}; + fullWidth ? '100%' : square ? styledBlockSize(size) : 'auto'}; + padding: ${({ square }) => (square ? 0 : css`0 ${styledDimension(5)}`)}; font-size: 1rem; user-select: none; &:active { - padding-top: ${({ disabled }) => !disabled && '2px'}; + padding-top: ${({ disabled }) => !disabled && styledDimension(1)}; } - padding-top: ${({ active, disabled }) => active && !disabled && '2px'}; + padding-top: ${({ active, disabled }) => + active && !disabled && styledDimension(1)}; &:after { content: ''; position: absolute; @@ -85,25 +87,25 @@ export const StyledButton = styled.button<StyledButtonProps>` ? css` ${createFlatBoxStyles()} ${primary - ? ` - border: 2px solid ${theme.checkmark}; - outline: 2px solid ${theme.flatDark}; - outline-offset: -4px; - ` - : ` - border: 2px solid ${theme.flatDark}; - outline: 2px solid transparent; - outline-offset: -4px; - `} + ? css` + border: ${styledDimension(1)} solid ${theme.checkmark}; + outline: ${styledDimension(1)} solid ${theme.flatDark}; + outline-offset: -${styledDimension(2)}; + ` + : css` + border: ${styledDimension(1)} solid ${theme.flatDark}; + outline: ${styledDimension(1)} solid transparent; + outline-offset: -${styledDimension(2)}; + `} &:focus:after, &:active:after { ${!active && !disabled && focusOutline} - outline-offset: -4px; + outline-offset: -${styledDimension(2)}; } ` : variant === 'menu' || variant === 'thin' ? css` ${createBoxStyles()}; - border: 2px solid transparent; + border: ${styledDimension(1)} solid transparent; &:hover, &:focus { ${!disabled && @@ -132,13 +134,13 @@ export const StyledButton = styled.button<StyledButtonProps>` position: absolute; ${primary ? css` - left: 2px; - top: 2px; - width: calc(100% - 4px); - height: calc(100% - 4px); - outline: 2px solid ${theme.borderDarkest}; + left: ${styledDimension(1)}; + top: ${styledDimension(1)}; + width: calc(100% - ${styledDimension(2)}); + height: calc(100% - ${styledDimension(2)}); + outline: ${styledDimension(1)} solid ${theme.borderDarkest}; ` - : css` + : ` left: 0; top: 0; width: 100%; @@ -155,11 +157,11 @@ export const StyledButton = styled.button<StyledButtonProps>` &:focus:after, &:active:after { ${!active && !disabled && focusOutline} - outline-offset: -8px; + outline-offset: ${styledDimension(-4)}; } &:active:focus:after, &:active:after { - top: ${active ? '0' : '1px'}; + top: ${active ? '0' : styledDimension(0.5)}; } `} ${commonButtonStyles} diff --git a/src/Checkbox/Checkbox.tsx b/src/Checkbox/Checkbox.tsx index e1196985..943f40d6 100644 --- a/src/Checkbox/Checkbox.tsx +++ b/src/Checkbox/Checkbox.tsx @@ -1,7 +1,7 @@ import React, { forwardRef, useCallback } from 'react'; import styled, { css } from 'styled-components'; -import { createHatchedBackground } from '../common'; +import { createHatchedBackground, styledDimension } from '../common'; import useControlledOrUncontrolled from '../common/hooks/useControlledOrUncontrolled'; import { LabelText, @@ -45,8 +45,8 @@ type CheckmarkProps = { }; const sharedCheckboxStyles = css` - width: ${size}px; - height: ${size}px; + width: ${styledDimension(size)}; + height: ${styledDimension(size)}; display: flex; align-items: center; justify-content: space-around; @@ -54,8 +54,8 @@ const sharedCheckboxStyles = css` `; const StyledCheckbox = styled(StyledScrollView)<CommonThemeProps>` ${sharedCheckboxStyles} - width: ${size}px; - height: ${size}px; + width: ${styledDimension(size)}; + height: ${styledDimension(size)}; background: ${({ $disabled, theme }) => $disabled ? theme.material : theme.canvas}; &:before { @@ -69,10 +69,10 @@ const StyledFlatCheckbox = styled.div<CommonThemeProps>` background: ${({ $disabled, theme }) => $disabled ? theme.flatLight : theme.canvas}; ${sharedCheckboxStyles} - width: ${size - 4}px; - height: ${size - 4}px; + width: ${styledDimension(size, { delta: -4 })}; + height: ${styledDimension(size, { delta: -4 })}; outline: none; - border: 2px solid ${({ theme }) => theme.flatDark}; + border: ${styledDimension(1)} solid ${({ theme }) => theme.flatDark}; background: ${({ $disabled, theme }) => $disabled ? theme.flatLight : theme.canvas}; `; @@ -84,8 +84,8 @@ const StyledMenuCheckbox = styled.div<CommonThemeProps>` background: ${({ $disabled, theme }) => $disabled ? theme.flatLight : theme.canvas}; ${sharedCheckboxStyles} - width: ${size - 4}px; - height: ${size - 4}px; + width: ${styledDimension(size, { delta: -4 })}; + height: ${styledDimension(size, { delta: -4 })}; background: none; border: none; outline: none; @@ -103,14 +103,14 @@ const CheckmarkIcon = styled.span.attrs(() => ({ display: block; position: absolute; left: 50%; - top: calc(50% - 1px); - width: 3px; - height: 7px; + top: calc(50% - ${styledDimension(0.5)}); + width: ${styledDimension(1.5)}; + height: ${styledDimension(3.5)}; border: solid ${({ $disabled, theme }) => $disabled ? theme.checkmarkDisabled : theme.checkmark}; - border-width: 0 3px 3px 0; + border-width: 0 ${styledDimension(1.5)} ${styledDimension(1.5)} 0; transform: translate(-50%, -50%) rotate(45deg); ${({ $disabled, theme, variant }) => @@ -120,7 +120,7 @@ const CheckmarkIcon = styled.span.attrs(() => ({ ? theme.materialTextDisabled : theme.materialText}; filter: drop-shadow( - 1px 1px 0px + ${styledDimension(0.5)} ${styledDimension(0.5)} 0px ${$disabled ? theme.materialTextDisabledShadow : 'transparent'} ); ` @@ -147,8 +147,8 @@ const IndeterminateIcon = styled.span.attrs(() => ({ ${({ variant }) => variant === 'menu' ? css` - height: calc(100% - 4px); - width: calc(100% - 4px); + height: calc(100% - ${styledDimension(2)}); + width: calc(100% - ${styledDimension(2)}); ` : css` width: 100%; @@ -165,7 +165,7 @@ const IndeterminateIcon = styled.span.attrs(() => ({ createHatchedBackground({ mainColor: $disabled ? theme.checkmarkDisabled : theme.checkmark })} - background-position: 0px 0px, 2px 2px; + background-position: 0px 0px, ${styledDimension(1)} ${styledDimension(1)}; ${({ $disabled, theme, variant }) => variant === 'menu' && @@ -176,7 +176,7 @@ const IndeterminateIcon = styled.span.attrs(() => ({ })} } filter: drop-shadow( - 1px 1px 0px + ${styledDimension(0.5)} ${styledDimension(0.5)} 0px ${$disabled ? theme.materialTextDisabledShadow : 'transparent'} ); `}; diff --git a/src/ColorInput/ColorInput.tsx b/src/ColorInput/ColorInput.tsx index bf396d0c..5f0e82bc 100644 --- a/src/ColorInput/ColorInput.tsx +++ b/src/ColorInput/ColorInput.tsx @@ -1,7 +1,7 @@ import React, { forwardRef } from 'react'; import styled, { css } from 'styled-components'; import { StyledButton } from '../Button/Button'; -import { focusOutline } from '../common'; +import { focusOutline, styledDimension } from '../common'; import useControlledOrUncontrolled from '../common/hooks/useControlledOrUncontrolled'; import { noOp } from '../common/utils'; import { Separator } from '../Separator/Separator'; @@ -20,11 +20,11 @@ type ColorInputProps = { CommonStyledProps; const Trigger = styled(StyledButton)` - padding-left: 8px; + padding-left: ${styledDimension(4)}; `; const StyledSeparator = styled(Separator)` - height: 21px; + height: ${styledDimension(10.5)}; position: relative; top: 0; `; @@ -50,23 +50,26 @@ const ColorPreview = styled.div<{ $disabled: boolean; }>` box-sizing: border-box; - height: 19px; + height: ${styledDimension(9.5)}; display: inline-block; - width: 35px; - margin-right: 5px; + width: ${styledDimension(17.5)}; + margin-right: ${styledDimension(2.5)}; background: ${({ color }) => color}; ${({ $disabled }) => $disabled ? css` - border: 2px solid ${({ theme }) => theme.materialTextDisabled}; + border: ${styledDimension(1)} solid + ${({ theme }) => theme.materialTextDisabled}; filter: drop-shadow( - 1px 1px 0px ${({ theme }) => theme.materialTextDisabledShadow} + ${styledDimension(0.5)} ${styledDimension(0.5)} 0px + ${({ theme }) => theme.materialTextDisabledShadow} ); ` : css` - border: 2px solid ${({ theme }) => theme.materialText}; + border: ${styledDimension(1)} solid + ${({ theme }) => theme.materialText}; `} ${StyledColorInput}:focus:not(:active) + &:after { content: ''; @@ -76,7 +79,7 @@ const ColorPreview = styled.div<{ width: 100%; height: 100%; ${focusOutline} - outline-offset: -8px; + outline-offset: -${styledDimension(4)}; } `; @@ -87,30 +90,34 @@ const ChevronIcon = styled.span< >` width: 0px; height: 0px; - border-left: 6px solid transparent; - border-right: 6px solid transparent; + border-left: ${styledDimension(3)} solid transparent; + border-right: ${styledDimension(3)} solid transparent; display: inline-block; - margin-left: 6px; + margin-left: ${styledDimension(3)}; ${({ $disabled }) => $disabled ? css` - border-top: 6px solid ${({ theme }) => theme.materialTextDisabled}; + border-top: ${styledDimension(3)} solid + ${({ theme }) => theme.materialTextDisabled}; filter: drop-shadow( - 1px 1px 0px ${({ theme }) => theme.materialTextDisabledShadow} + ${styledDimension(0.5)} ${styledDimension(0.5)} 0px + ${({ theme }) => theme.materialTextDisabledShadow} ); ` : css` - border-top: 6px solid ${({ theme }) => theme.materialText}; + border-top: ${styledDimension(3)} solid + ${({ theme }) => theme.materialText}; `} &:after { content: ''; box-sizing: border-box; position: absolute; - top: ${({ variant }) => (variant === 'flat' ? '6px' : '8px')}; - right: 8px; - width: 16px; - height: 19px; + top: ${({ variant }) => + variant === 'flat' ? styledDimension(3) : styledDimension(4)}; + right: ${styledDimension(4)}; + width: ${styledDimension(8)}; + height: ${styledDimension(9.5)}; } `; diff --git a/src/DatePicker/DatePicker.tsx b/src/DatePicker/DatePicker.tsx index 11d1c53d..384e43af 100644 --- a/src/DatePicker/DatePicker.tsx +++ b/src/DatePicker/DatePicker.tsx @@ -1,7 +1,8 @@ import React, { forwardRef, useCallback, useMemo, useState } from 'react'; -import styled from 'styled-components'; +import styled, { useTheme } from 'styled-components'; import { Button } from '../Button/Button'; +import { styledDimension } from '../common'; import { NumberInput } from '../NumberInput/NumberInput'; import { ScrollView } from '../ScrollView/ScrollView'; import { Select } from '../Select/Select'; @@ -13,11 +14,12 @@ type DatePickerProps = { date?: string; onAccept?: (chosenDate: string) => void; onCancel?: React.MouseEventHandler<HTMLButtonElement>; + /** @deprecated Change `shadow` property on theme */ shadow?: boolean; }; const Calendar = styled(ScrollView)` - width: 234px; + width: ${styledDimension(117)}; margin: 1rem 0; background: ${({ theme }) => theme.canvas}; `; @@ -49,7 +51,7 @@ const DateItemContent = styled.span<{ active: boolean }>` active ? theme.canvasTextInvert : theme.canvasText}; &:hover { - border: 2px dashed + border: ${styledDimension(1)} dashed ${({ theme, active }) => (active ? 'none' : theme.materialDark)}; } `; @@ -93,10 +95,11 @@ const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>( date: initialDate = new Date().toISOString(), onAccept, onCancel, - shadow = true + shadow }, ref ) => { + const theme = useTheme(); const [date, setDate] = useState(() => convertDateToState(initialDate)); const { year, month, day } = date; @@ -172,10 +175,14 @@ const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>( options={months} value={month} onChange={handleMonthSelect} - width={128} - menuMaxHeight={200} + width={styledDimension(64)({ theme })} + menuMaxHeight={styledDimension(100)({ theme })} + /> + <NumberInput + value={year} + onChange={handleYearSelect} + width={styledDimension(50)({ theme })} /> - <NumberInput value={year} onChange={handleYearSelect} width={100} /> </Toolbar> <Calendar> <WeekDays> diff --git a/src/Frame/Frame.tsx b/src/Frame/Frame.tsx index 8094ee14..4f1f6438 100644 --- a/src/Frame/Frame.tsx +++ b/src/Frame/Frame.tsx @@ -5,6 +5,7 @@ import { CommonStyledProps } from '../types'; type FrameProps = { children?: React.ReactNode; + /** @deprecated Change `shadow` property on theme */ shadow?: boolean; } & ( | { diff --git a/src/GroupBox/GroupBox.tsx b/src/GroupBox/GroupBox.tsx index a2d381c5..3e41414e 100644 --- a/src/GroupBox/GroupBox.tsx +++ b/src/GroupBox/GroupBox.tsx @@ -1,6 +1,6 @@ import React, { forwardRef } from 'react'; import styled, { css } from 'styled-components'; -import { createDisabledTextStyles } from '../common'; +import { createDisabledTextStyles, styledDimension } from '../common'; import { CommonStyledProps } from '../types'; type GroupBoxProps = { @@ -15,18 +15,20 @@ const StyledFieldset = styled.fieldset< Pick<GroupBoxProps, 'variant'> & { $disabled: boolean } >` position: relative; - border: 2px solid + border: ${styledDimension(1)} solid ${({ theme, variant }) => variant === 'flat' ? theme.flatDark : theme.borderLightest}; - padding: 16px; - margin-top: 8px; + padding: ${styledDimension(8)}; + margin-top: ${styledDimension(4)}; font-size: 1rem; color: ${({ theme }) => theme.materialText}; ${({ variant }) => variant !== 'flat' && css` - box-shadow: -1px -1px 0 1px ${({ theme }) => theme.borderDark}, - inset -1px -1px 0 1px ${({ theme }) => theme.borderDark}; + box-shadow: -${styledDimension(0.5)} -${styledDimension(0.5)} 0 + ${styledDimension(0.5)} ${({ theme }) => theme.borderDark}, + inset -${styledDimension(0.5)} -${styledDimension(0.5)} 0 + ${styledDimension(0.5)} ${({ theme }) => theme.borderDark}; `} ${props => props.$disabled && createDisabledTextStyles()} `; @@ -35,9 +37,9 @@ const StyledLegend = styled.legend<Pick<GroupBoxProps, 'variant'>>` display: flex; position: absolute; top: 0; - left: 8px; - transform: translateY(calc(-50% - 2px)); - padding: 0 8px; + left: ${styledDimension(4)}; + transform: translateY(calc(-50% - ${styledDimension(1)})); + padding: 0 ${styledDimension(4)}; font-size: 1rem; background: ${({ theme, variant }) => diff --git a/src/Handle/Handle.tsx b/src/Handle/Handle.tsx index fc8fd851..313f2f84 100644 --- a/src/Handle/Handle.tsx +++ b/src/Handle/Handle.tsx @@ -1,7 +1,8 @@ import React from 'react'; -import styled from 'styled-components'; -import { CommonStyledProps } from '../types'; +import styled, { css } from 'styled-components'; +import { styledDimension } from '../common'; import { getSize } from '../common/utils'; +import { CommonStyledProps } from '../types'; type HandleProps = { size?: string | number; @@ -11,17 +12,17 @@ type HandleProps = { // TODO: add horizontal variant // TODO: allow user to specify number of bars (like 3 horizontal bars for drag handle) const Handle = styled.div<HandleProps>` - ${({ theme, size = '100%' }) => ` - display: inline-block; - box-sizing: border-box; - height: ${getSize(size)}; - width: 5px; - border-top: 2px solid ${theme.borderLightest}; - border-left: 2px solid ${theme.borderLightest}; - border-bottom: 2px solid ${theme.borderDark}; - border-right: 2px solid ${theme.borderDark}; - background: ${theme.material}; -`} + ${({ theme, size = '100%' }) => css` + display: inline-block; + box-sizing: border-box; + height: ${getSize(size)}; + width: ${styledDimension(2.5)}; + border-top: ${styledDimension(1)} solid ${theme.borderLightest}; + border-left: ${styledDimension(1)} solid ${theme.borderLightest}; + border-bottom: ${styledDimension(1)} solid ${theme.borderDark}; + border-right: ${styledDimension(1)} solid ${theme.borderDark}; + background: ${theme.material}; + `} `; Handle.displayName = 'Handle'; diff --git a/src/MenuList/MenuList.tsx b/src/MenuList/MenuList.tsx index 57b57848..bea42b16 100644 --- a/src/MenuList/MenuList.tsx +++ b/src/MenuList/MenuList.tsx @@ -1,22 +1,28 @@ import React from 'react'; import styled from 'styled-components'; -import { createBorderStyles, createBoxStyles } from '../common'; +import { + createBorderStyles, + createBoxStyles, + styledDimension +} from '../common'; import { CommonStyledProps } from '../types'; type MenuListProps = React.HTMLAttributes<HTMLUListElement> & { fullWidth?: boolean; + /** @deprecated Change `shadow` property on theme */ shadow?: boolean; inline?: boolean; } & CommonStyledProps; // TODO keyboard controls const MenuList = styled.ul.attrs(() => ({ - role: 'menu' + role: 'menu', + shadow: true }))<MenuListProps>` box-sizing: border-box; width: ${props => (props.fullWidth ? '100%' : 'auto')}; - padding: 4px; + padding: ${styledDimension(2)}; ${createBorderStyles({ style: 'window' })} ${createBoxStyles()} ${props => diff --git a/src/MenuList/MenuListItem.spec.tsx b/src/MenuList/MenuListItem.spec.tsx index 734d31d2..a6c2a263 100644 --- a/src/MenuList/MenuListItem.spec.tsx +++ b/src/MenuList/MenuListItem.spec.tsx @@ -1,9 +1,16 @@ import React from 'react'; import { renderWithTheme, theme } from '../../test/utils'; +import { SCALE } from '../common/constants'; import { blockSizes } from '../common/system'; import { MenuListItem } from './MenuListItem'; +const expectedBlockSizes = { + sm: `${blockSizes.sm * SCALE}px`, + md: `${blockSizes.md * SCALE}px`, + lg: `${blockSizes.lg * SCALE}px` +}; + const defaultSize = 'lg'; describe('<MenuListItem />', () => { it('renders MenuListItem', () => { @@ -76,8 +83,14 @@ describe('<MenuListItem />', () => { const { getByRole } = renderWithTheme(<MenuListItem square />); const menuListItem = getByRole('menuitem'); - expect(menuListItem).toHaveStyleRule('width', blockSizes[defaultSize]); - expect(menuListItem).toHaveStyleRule('height', blockSizes[defaultSize]); + expect(menuListItem).toHaveStyleRule( + 'width', + expectedBlockSizes[defaultSize] + ); + expect(menuListItem).toHaveStyleRule( + 'height', + expectedBlockSizes[defaultSize] + ); }); }); describe('prop: size', () => { @@ -86,7 +99,7 @@ describe('<MenuListItem />', () => { const { getByRole } = renderWithTheme(<MenuListItem size={size} />); const menuListItem = getByRole('menuitem'); - expect(menuListItem).toHaveStyleRule('height', blockSizes[size]); + expect(menuListItem).toHaveStyleRule('height', expectedBlockSizes[size]); }); }); }); diff --git a/src/MenuList/MenuListItem.tsx b/src/MenuList/MenuListItem.tsx index 45c7e1f2..4e94a420 100644 --- a/src/MenuList/MenuListItem.tsx +++ b/src/MenuList/MenuListItem.tsx @@ -1,8 +1,8 @@ import React, { forwardRef } from 'react'; import styled from 'styled-components'; -import { createDisabledTextStyles } from '../common'; -import { blockSizes } from '../common/system'; +import { createDisabledTextStyles, styledDimension } from '../common'; +import { styledBlockSize } from '../common/system'; import { CommonStyledProps, Sizes } from '../types'; type MenuListItemProps = { @@ -24,15 +24,15 @@ export const StyledMenuListItem = styled.li<{ display: flex; align-items: center; position: relative; - height: ${props => blockSizes[props.size]}; - width: ${props => (props.square ? blockSizes[props.size] : 'auto')}; - padding: 0 8px; + height: ${props => styledBlockSize(props.size)}; + width: ${props => (props.square ? styledBlockSize(props.size) : 'auto')}; + padding: 0 ${styledDimension(4)}; font-size: 1rem; white-space: nowrap; justify-content: ${props => props.square ? 'space-around' : 'space-between'}; text-align: center; - line-height: ${props => blockSizes[props.size]}; + line-height: ${props => styledBlockSize(props.size)}; color: ${({ theme }) => theme.materialText}; pointer-events: ${({ $disabled }) => ($disabled ? 'none' : 'auto')}; font-weight: ${({ primary }) => (primary ? 'bold' : 'normal')}; diff --git a/src/Monitor/Monitor.tsx b/src/Monitor/Monitor.tsx index 8abfa45e..817e89fa 100644 --- a/src/Monitor/Monitor.tsx +++ b/src/Monitor/Monitor.tsx @@ -1,5 +1,6 @@ import React, { forwardRef } from 'react'; import styled from 'styled-components'; +import { styledDimension } from '../common'; import { StyledScrollView } from '../ScrollView/ScrollView'; @@ -11,7 +12,7 @@ type MonitorProps = { const Wrapper = styled.div` position: relative; display: inline-block; - padding-bottom: 26px; + padding-bottom: ${styledDimension(13)}; `; const Inner = styled.div` @@ -22,17 +23,18 @@ const MonitorBody = styled.div` position: relative; z-index: 1; box-sizing: border-box; - width: 195px; - height: 155px; - padding: 12px; + width: ${styledDimension(97.5)}; + height: ${styledDimension(77.5)}; + padding: ${styledDimension(6)}; background: ${({ theme }) => theme.material}; - border-top: 4px solid ${({ theme }) => theme.borderLightest}; - border-left: 4px solid ${({ theme }) => theme.borderLightest}; - border-bottom: 4px solid ${({ theme }) => theme.borderDark}; - border-right: 4px solid ${({ theme }) => theme.borderDark}; + border-top: ${styledDimension(2)} solid ${({ theme }) => theme.borderLightest}; + border-left: ${styledDimension(2)} solid + ${({ theme }) => theme.borderLightest}; + border-bottom: ${styledDimension(2)} solid ${({ theme }) => theme.borderDark}; + border-right: ${styledDimension(2)} solid ${({ theme }) => theme.borderDark}; - outline: 1px dotted ${({ theme }) => theme.material}; - outline-offset: -3px; + outline: ${styledDimension(0.5)} dotted ${({ theme }) => theme.material}; + outline-offset: ${styledDimension(-1.5)}; &:before { content: ''; position: absolute; @@ -40,19 +42,20 @@ const MonitorBody = styled.div` top: 0; width: 100%; height: 100%; - outline: 1px dotted ${({ theme }) => theme.material}; + outline: ${styledDimension(0.5)} dotted ${({ theme }) => theme.material}; } - box-shadow: 1px 1px 0 1px ${({ theme }) => theme.borderDarkest}; + box-shadow: ${styledDimension(0.5)} ${styledDimension(0.5)} 0 + ${styledDimension(0.5)} ${({ theme }) => theme.borderDarkest}; &:after { content: ''; display: inline-block; position: absolute; - bottom: 4px; - right: 12px; - width: 10px; - border-top: 2px solid #4d9046; - border-bottom: 2px solid #07ff00; + bottom: ${styledDimension(2)}; + right: ${styledDimension(6)}; + width: ${styledDimension(5)}; + border-top: ${styledDimension(1)} solid #4d9046; + border-bottom: ${styledDimension(1)} solid #07ff00; } `; @@ -66,43 +69,52 @@ const Background = styled(StyledScrollView).attrs(() => ({ const Stand = styled.div` box-sizing: border-box; position: absolute; - top: calc(100% + 2px); + top: calc(100% + ${styledDimension(1)}); left: 50%; transform: translateX(-50%); - height: 10px; + height: ${styledDimension(5)}; width: 50%; background: ${({ theme }) => theme.material}; - border-left: 2px solid ${({ theme }) => theme.borderLightest}; - border-bottom: 2px solid ${({ theme }) => theme.borderDarkest}; - border-right: 2px solid ${({ theme }) => theme.borderDarkest}; - box-shadow: inset 0px 0px 0px 2px ${({ theme }) => theme.borderDark}; + border-left: ${styledDimension(1)} solid + ${({ theme }) => theme.borderLightest}; + border-bottom: ${styledDimension(1)} solid + ${({ theme }) => theme.borderDarkest}; + border-right: ${styledDimension(1)} solid + ${({ theme }) => theme.borderDarkest}; + box-shadow: inset 0px 0px 0px ${styledDimension(1)} + ${({ theme }) => theme.borderDark}; &:before { content: ''; position: absolute; - top: calc(100% + 2px); + top: calc(100% + ${styledDimension(1)}); left: 50%; transform: translateX(-50%); width: 80%; - height: 8px; + height: ${styledDimension(4)}; background: ${({ theme }) => theme.material}; - border-left: 2px solid ${({ theme }) => theme.borderLightest}; - border-right: 2px solid ${({ theme }) => theme.borderDarkest}; - box-shadow: inset 0px 0px 0px 2px ${({ theme }) => theme.borderDark}; + border-left: ${styledDimension(1)} solid + ${({ theme }) => theme.borderLightest}; + border-right: ${styledDimension(1)} solid + ${({ theme }) => theme.borderDarkest}; + box-shadow: inset 0px 0px 0px ${styledDimension(1)} + ${({ theme }) => theme.borderDark}; } &:after { content: ''; position: absolute; - top: calc(100% + 8px); + top: calc(100% + ${styledDimension(4)}); left: 50%; transform: translateX(-50%); width: 150%; - height: 4px; + height: ${styledDimension(2)}; background: ${({ theme }) => theme.material}; - border: 2px solid ${({ theme }) => theme.borderDark}; + border: ${styledDimension(1)} solid ${({ theme }) => theme.borderDark}; border-bottom: none; - box-shadow: inset 1px 1px 0px 1px ${({ theme }) => theme.borderLightest}, - 1px 1px 0 1px ${({ theme }) => theme.borderDarkest}; + box-shadow: inset ${styledDimension(0.5)} ${styledDimension(0.5)} 0px + ${styledDimension(0.5)} ${({ theme }) => theme.borderLightest}, + ${styledDimension(0.5)} ${styledDimension(0.5)} 0 ${styledDimension(0.5)} + ${({ theme }) => theme.borderDarkest}; } `; diff --git a/src/ProgressBar/ProgressBar.tsx b/src/ProgressBar/ProgressBar.tsx index 349f8079..ba9334f1 100644 --- a/src/ProgressBar/ProgressBar.tsx +++ b/src/ProgressBar/ProgressBar.tsx @@ -6,6 +6,7 @@ import React, { useState } from 'react'; import styled, { css } from 'styled-components'; +import { styledDimension } from '../common'; import { blockSizes } from '../common/system'; import { StyledScrollView } from '../ScrollView/ScrollView'; @@ -13,6 +14,7 @@ import { CommonStyledProps } from '../types'; type ProgressBarProps = { hideValue?: boolean; + /** @deprecated Change `shadow` property on theme */ shadow?: boolean; value?: number; variant?: 'default' | 'tile'; @@ -39,8 +41,8 @@ const ProgressCutout = styled(StyledScrollView)< } `; const commonBarStyles = css` - width: calc(100% - 4px); - height: calc(100% - 4px); + width: calc(100% - ${styledDimension(2)}); + height: calc(100% - ${styledDimension(2)}); display: flex; align-items: center; @@ -48,19 +50,19 @@ const commonBarStyles = css` `; const WhiteBar = styled.div` position: relative; - top: 4px; + top: ${styledDimension(2)}; ${commonBarStyles} background: ${({ theme }) => theme.canvas}; color: #000; - margin-left: 2px; - margin-top: -2px; + margin-left: ${styledDimension(1)}; + margin-top: ${styledDimension(-1)}; color: ${({ theme }) => theme.materialText}; `; const BlueBar = styled.div<Pick<ProgressBarProps, 'value'>>` position: absolute; - top: 2px; - left: 2px; + top: ${styledDimension(1)}; + left: ${styledDimension(1)}; ${commonBarStyles} color: ${({ theme }) => theme.materialTextInvert}; background: ${({ theme }) => theme.progress}; @@ -74,35 +76,29 @@ const BlueBar = styled.div<Pick<ProgressBarProps, 'value'>>` `; const TilesWrapper = styled.div` - width: calc(100% - 6px); - height: calc(100% - 8px); + width: calc(100% - ${styledDimension(3)}); + height: calc(100% - ${styledDimension(4)}); position: absolute; - left: 3px; - top: 4px; + left: ${styledDimension(1.5)}; + top: ${styledDimension(2)}; box-sizing: border-box; display: inline-flex; `; -const tileWidth = 17; +const tileWidth = 8.5; const Tile = styled.span` display: inline-block; - width: ${tileWidth}px; + width: ${styledDimension(tileWidth)}; box-sizing: border-box; height: 100%; background: ${({ theme }) => theme.progress}; border-color: ${({ theme }) => theme.material}; - border-width: 0px 1px; + border-width: 0px ${styledDimension(0.5)}; border-style: solid; `; const ProgressBar = forwardRef<HTMLDivElement, ProgressBarProps>( ( - { - hideValue = false, - shadow = true, - value, - variant = 'default', - ...otherProps - }, + { hideValue = false, shadow, value, variant = 'default', ...otherProps }, ref ) => { const displayValue = hideValue ? null : `${value}%`; @@ -138,7 +134,7 @@ const ProgressBar = forwardRef<HTMLDivElement, ProgressBarProps>( variant={variant} {...otherProps} > - <ProgressCutout variant={variant} shadow={shadow}> + <ProgressCutout shadow={shadow} variant={variant}> {variant === 'default' ? ( <> <WhiteBar data-testid='defaultProgress1'>{displayValue}</WhiteBar> diff --git a/src/Radio/Radio.tsx b/src/Radio/Radio.tsx index f49b51ca..a146f87e 100644 --- a/src/Radio/Radio.tsx +++ b/src/Radio/Radio.tsx @@ -1,7 +1,7 @@ import React, { forwardRef } from 'react'; import styled, { css, CSSProperties } from 'styled-components'; -import { createFlatBoxStyles } from '../common'; +import { createFlatBoxStyles, styledDimension } from '../common'; import { LabelText, size, @@ -31,8 +31,8 @@ type RadioProps = { CommonStyledProps; const sharedCheckboxStyles = css` - width: ${size}px; - height: ${size}px; + width: ${styledDimension(size)}; + height: ${styledDimension(size)}; border-radius: 50%; display: flex; align-items: center; @@ -54,8 +54,8 @@ const StyledCheckbox = styled(StyledScrollView)<StyledCheckboxProps>` position: absolute; left: 0px; top: 0px; - width: calc(100% - 4px); - height: calc(100% - 4px); + width: calc(100% - ${styledDimension(2)}); + height: calc(100% - ${styledDimension(2)}); border-radius: 50%; box-shadow: none; } @@ -72,9 +72,9 @@ const StyledFlatCheckbox = styled.div<StyledCheckboxProps>` position: absolute; top: 0; left: 0; - width: calc(100% - 4px); - height: calc(100% - 4px); - border: 2px solid ${({ theme }) => theme.flatDark}; + width: calc(100% - ${styledDimension(2)}); + height: calc(100% - ${styledDimension(2)}); + border: ${styledDimension(1)} solid ${({ theme }) => theme.flatDark}; border-radius: 50%; } `; @@ -102,8 +102,8 @@ const Icon = styled.span.attrs(() => ({ display: inline-block; top: 50%; left: 50%; - width: 6px; - height: 6px; + width: ${styledDimension(3)}; + height: ${styledDimension(3)}; transform: translate(-50%, -50%); border-radius: 50%; ${({ $disabled, theme, variant }) => @@ -113,7 +113,7 @@ const Icon = styled.span.attrs(() => ({ ? theme.materialTextDisabled : theme.materialText}; filter: drop-shadow( - 1px 1px 0px + ${styledDimension(0.5)} ${styledDimension(0.5)} 0px ${$disabled ? theme.materialTextDisabledShadow : 'transparent'} ); ` diff --git a/src/ScrollView/ScrollView.tsx b/src/ScrollView/ScrollView.tsx index 632ae1e3..e348a94d 100644 --- a/src/ScrollView/ScrollView.tsx +++ b/src/ScrollView/ScrollView.tsx @@ -1,10 +1,16 @@ import React, { forwardRef } from 'react'; import styled from 'styled-components'; -import { insetShadow, createScrollbars } from '../common'; +import { + createScrollbars, + createInnerBorderWithShadow, + styledDimension +} from '../common'; +import { defaultTrue } from '../common/utils'; import { CommonStyledProps } from '../types'; type ScrollViewProps = { children?: React.ReactNode; + /** @deprecated Change `shadow` property on theme */ shadow?: boolean; } & React.HTMLAttributes<HTMLDivElement> & CommonStyledProps; @@ -12,10 +18,10 @@ type ScrollViewProps = { export const StyledScrollView = styled.div<Pick<ScrollViewProps, 'shadow'>>` position: relative; box-sizing: border-box; - padding: 2px; + padding: ${styledDimension(1)}; font-size: 1rem; border-style: solid; - border-width: 2px; + border-width: ${styledDimension(1)}; border-left-color: ${({ theme }) => theme.borderDark}; border-top-color: ${({ theme }) => theme.borderDark}; border-right-color: ${({ theme }) => theme.borderLightest}; @@ -26,18 +32,23 @@ export const StyledScrollView = styled.div<Pick<ScrollViewProps, 'shadow'>>` left: 0; top: 0; content: ''; - width: calc(100% - 4px); - height: calc(100% - 4px); + width: calc(100% - ${styledDimension(2)}); + height: calc(100% - ${styledDimension(2)}); border-style: solid; - border-width: 2px; + border-width: ${styledDimension(1)}; border-left-color: ${({ theme }) => theme.borderDarkest}; border-top-color: ${({ theme }) => theme.borderDarkest}; border-right-color: ${({ theme }) => theme.borderLight}; border-bottom-color: ${({ theme }) => theme.borderLight}; pointer-events: none; - ${props => props.shadow && `box-shadow:${insetShadow};`} + ${props => + defaultTrue(props.shadow ?? props.theme.shadow) && + `box-shadow: ${createInnerBorderWithShadow({ + theme: props.theme, + hasInsetShadow: true + })};`} } `; @@ -45,13 +56,13 @@ const Content = styled.div` box-sizing: border-box; width: 100%; height: 100%; - padding: 4px; + padding: ${styledDimension(2)}; overflow: auto; ${createScrollbars()} `; const ScrollView = forwardRef<HTMLDivElement, ScrollViewProps>( - ({ children, shadow = true, ...otherProps }, ref) => { + ({ children, shadow, ...otherProps }, ref) => { return ( <StyledScrollView ref={ref} shadow={shadow} {...otherProps}> <Content>{children}</Content> diff --git a/src/Select/Select.styles.tsx b/src/Select/Select.styles.tsx index 5ff251d1..cdc4b8dc 100644 --- a/src/Select/Select.styles.tsx +++ b/src/Select/Select.styles.tsx @@ -5,9 +5,10 @@ import { createDisabledTextStyles, createFlatBoxStyles, createScrollbars, - shadow as commonShadow + shadow as commonShadow, + styledDimension } from '../common'; -import { blockSizes } from '../common/system'; +import { blockSizes, styledBlockSize } from '../common/system'; import { StyledScrollView } from '../ScrollView/ScrollView'; import { CommonThemeProps } from '../types'; @@ -21,7 +22,7 @@ type CommonSelectStyleProps = { const sharedInputContentStyles = css` box-sizing: border-box; - padding-left: 4px; + padding-left: ${styledDimension(2)}; overflow: hidden; white-space: nowrap; user-select: none; @@ -45,20 +46,20 @@ export const StyledInner = styled.div` export const StyledSelectContent = styled.div` ${sharedInputContentStyles} - padding-right: 8px; + padding-right: ${styledDimension(4)}; align-items: center; display: flex; - height: calc(100% - 4px); - width: calc(100% - 4px); - margin: 0 2px; - border: 2px solid transparent; + height: calc(100% - ${styledDimension(2)}); + width: calc(100% - ${styledDimension(2)}); + margin: 0 ${styledDimension(1)}; + border: ${styledDimension(1)} solid transparent; ${StyledInner}:focus & { ${sharedHoverStyles} - border: 2px dotted ${({ theme }) => theme.focusSecondary}; + border: ${styledDimension(1)} dotted ${({ theme }) => theme.focusSecondary}; } `; const sharedWrapperStyles = css<CommonSelectStyleProps>` - height: ${blockSizes.md}; + height: ${styledBlockSize('md')}; display: inline-block; color: ${({ $disabled = false, theme }) => $disabled ? createDisabledTextStyles() : theme.canvasText}; @@ -97,7 +98,7 @@ export const StyledNativeSelect = styled.select` background: none; -webkit-tap-highlight-color: transparent; border-radius: 0; - padding-right: 30px; + padding-right: ${styledDimension(15)}; ${sharedInputContentStyles} cursor: pointer; &:disabled { @@ -110,7 +111,7 @@ export const StyledNativeSelect = styled.select` export const StyledDropdownButton = styled(Button).attrs(() => ({ 'aria-hidden': 'true' }))<CommonSelectStyleProps>` - width: 30px; + width: ${styledDimension(15)}; padding: 0; flex-shrink: 0; ${({ variant = 'default' }) => @@ -124,9 +125,11 @@ export const StyledDropdownButton = styled(Button).attrs(() => ({ &:before { border-left-color: ${({ theme }) => theme.borderLight}; border-top-color: ${({ theme }) => theme.borderLight}; - box-shadow: inset 1px 1px 0px 1px + box-shadow: inset ${styledDimension(0.5)} ${styledDimension(0.5)} + 0px ${styledDimension(0.5)} ${({ theme }) => theme.borderLightest}, - inset -1px -1px 0 1px ${({ theme }) => theme.borderDark}; + inset -${styledDimension(0.5)} -${styledDimension(0.5)} 0 + ${styledDimension(0.5)} ${({ theme }) => theme.borderDark}; } `} ${({ native = false, variant = 'default' }) => @@ -137,12 +140,12 @@ export const StyledDropdownButton = styled(Button).attrs(() => ({ right: 0; height: 100%; ` - : ` - position: absolute; - top: 2px; - right: 2px; - height: calc(100% - 4px); - `)} + : css` + position: absolute; + top: ${styledDimension(1)}; + right: ${styledDimension(1)}; + height: calc(100% - ${styledDimension(2)}); + `)} pointer-events: ${({ $disabled = false, native = false }) => $disabled || native ? 'none' : 'auto'} `; @@ -154,20 +157,23 @@ export const StyledDropdownIcon = styled.span<CommonSelectStyleProps>` transform: translate(-50%, -50%); width: 0; height: 0; - border-left: 6px solid transparent; - border-right: 6px solid transparent; + border-left: ${styledDimension(3)} solid transparent; + border-right: ${styledDimension(3)} solid transparent; display: inline-block; - border-top: 6px solid + border-top: ${styledDimension(3)} solid ${({ $disabled = false, theme }) => $disabled ? theme.materialTextDisabled : theme.materialText}; ${({ $disabled = false, theme }) => $disabled && - ` - filter: drop-shadow(1px 1px 0px ${theme.materialTextDisabledShadow}); - border-top-color: ${theme.materialTextDisabled}; + css` + filter: drop-shadow( + ${styledDimension(0.5)} ${styledDimension(0.5)} 0px + ${theme.materialTextDisabledShadow} + ); + border-top-color: ${theme.materialTextDisabled}; `} ${StyledDropdownButton}:active & { - margin-top: 2px; + margin-top: ${styledDimension(1)}; } `; @@ -179,7 +185,7 @@ export const StyledDropdownMenu = styled.ul<CommonSelectStyleProps>` transform: translateY(100%); left: 0; background: ${({ theme }) => theme.canvas}; - padding: 2px; + padding: ${styledDimension(1)}; border-top: none; cursor: default; z-index: 1; @@ -188,14 +194,15 @@ export const StyledDropdownMenu = styled.ul<CommonSelectStyleProps>` ${({ variant = 'default' }) => variant === 'flat' ? css` - bottom: 2px; + bottom: ${styledDimension(1)}; width: 100%; - border: 2px solid ${({ theme }) => theme.flatDark}; + border: ${styledDimension(1)} solid ${({ theme }) => theme.flatDark}; ` : css` - bottom: -2px; - width: calc(100% - 2px); - border: 2px solid ${({ theme }) => theme.borderDarkest}; + bottom: -${styledDimension(1)}; + width: calc(100% - ${styledDimension(1)}); + border: ${styledDimension(1)} solid + ${({ theme }) => theme.borderDarkest}; `} ${({ variant = 'default' }) => createScrollbars(variant)} `; @@ -204,10 +211,10 @@ export const StyledDropdownMenuItem = styled.li<{ active: boolean }>` box-sizing: border-box; width: 100%; - padding-left: 8px; + padding-left: ${styledDimension(4)}; - height: calc(${blockSizes.md} - 4px); - line-height: calc(${blockSizes.md} - 4px); + height: calc(${blockSizes.md} - ${styledDimension(2)}); + line-height: calc(${blockSizes.md} - ${styledDimension(2)}); font-size: 1rem; white-space: nowrap; overflow: hidden; diff --git a/src/Select/Select.tsx b/src/Select/Select.tsx index 1f195a7a..938a5203 100644 --- a/src/Select/Select.tsx +++ b/src/Select/Select.tsx @@ -15,7 +15,7 @@ import { StyledInner, StyledSelectContent } from './Select.styles'; -import { SelectOption, SelectInnerProps, SelectRef } from './Select.types'; +import { SelectInnerProps, SelectOption, SelectRef } from './Select.types'; import { useSelectCommon } from './useSelectCommon'; import { useSelectState } from './useSelectState'; @@ -95,7 +95,7 @@ function SelectInner<T>( open: openProp, options: optionsProp, readOnly, - shadow = true, + shadow, style, variant = 'default', value: valueProp, @@ -228,6 +228,7 @@ function SelectInner<T>( <Wrapper {...wrapperProps} $disabled={disabled} + defaultShadow ref={wrapperRef} shadow={shadow} style={{ ...style, width }} diff --git a/src/Select/Select.types.ts b/src/Select/Select.types.ts index 7d2577b8..1d068d19 100644 --- a/src/Select/Select.types.ts +++ b/src/Select/Select.types.ts @@ -46,6 +46,7 @@ export type SelectCommonProps<T> = { ) => void; options?: (SelectOption<T> | null | undefined)[]; readOnly?: boolean; + /** @deprecated Change `shadow` property on theme */ shadow?: boolean; style?: React.CSSProperties; value?: T; diff --git a/src/Separator/Separator.tsx b/src/Separator/Separator.tsx index 1956a098..4b68a7c4 100644 --- a/src/Separator/Separator.tsx +++ b/src/Separator/Separator.tsx @@ -1,4 +1,5 @@ -import styled from 'styled-components'; +import styled, { css } from 'styled-components'; +import { styledDimension } from '../common'; import { getSize } from '../common/utils'; import { Orientation } from '../types'; @@ -10,18 +11,18 @@ type SeparatorProps = { const Separator = styled.div<SeparatorProps>` ${({ orientation, theme, size = '100%' }) => orientation === 'vertical' - ? ` - height: ${getSize(size)}; - border-left: 2px solid ${theme.borderDark}; - border-right: 2px solid ${theme.borderLightest}; - margin: 0; - ` - : ` - width: ${getSize(size)}; - border-bottom: 2px solid ${theme.borderLightest}; - border-top: 2px solid ${theme.borderDark}; - margin: 0; - `} + ? css` + height: ${getSize(size)}; + border-left: ${styledDimension(1)} solid ${theme.borderDark}; + border-right: ${styledDimension(1)} solid ${theme.borderLightest}; + margin: 0; + ` + : css` + width: ${getSize(size)}; + border-bottom: ${styledDimension(1)} solid ${theme.borderLightest}; + border-top: ${styledDimension(1)} solid ${theme.borderDark}; + margin: 0; + `} `; Separator.displayName = 'Separator'; diff --git a/src/Slider/Slider.tsx b/src/Slider/Slider.tsx index 3be02071..0cef6fa4 100644 --- a/src/Slider/Slider.tsx +++ b/src/Slider/Slider.tsx @@ -14,7 +14,8 @@ import { createBoxStyles, createDisabledTextStyles, createFlatBoxStyles, - createHatchedBackground + createHatchedBackground, + styledDimension } from '../common'; import useControlledOrUncontrolled from '../common/hooks/useControlledOrUncontrolled'; import useForkRef from '../common/hooks/useForkRef'; @@ -132,15 +133,16 @@ const Wrapper = styled.div<StyledSliderProps>` content: ''; display: inline-block; position: absolute; - top: -2px; - left: -15px; - width: calc(100% + 30px); - height: ${({ hasMarks }) => (hasMarks ? '41px' : '39px')}; + top: ${styledDimension(-1)}; + left: ${styledDimension(-7.5)}; + width: calc(100% + ${styledDimension(15)}); + height: ${({ hasMarks }) => + hasMarks ? styledDimension(20.5) : styledDimension(19.5)}; ${({ isFocused, theme }) => isFocused && - ` - outline: 2px dotted ${theme.materialText}; - `} + css` + outline: ${styledDimension(1)} dotted ${theme.materialText}; + `} } ${({ orientation, size }) => @@ -149,20 +151,22 @@ const Wrapper = styled.div<StyledSliderProps>` height: ${size}; margin-right: 1.5rem; &:before { - left: -6px; - top: -15px; - height: calc(100% + 30px); - width: ${({ hasMarks }) => (hasMarks ? '41px' : '39px')}; + left: ${styledDimension(-1)}; + top: ${styledDimension(-7.5)}; + height: calc(100% + ${styledDimension(15)}); + width: ${({ hasMarks }) => + hasMarks ? styledDimension(20.5) : styledDimension(19.5)}; } ` : css<StyledSliderProps>` width: ${size}; margin-bottom: 1.5rem; &:before { - top: -2px; - left: -15px; - width: calc(100% + 30px); - height: ${({ hasMarks }) => (hasMarks ? '41px' : '39px')}; + top: ${styledDimension(-1)}; + left: ${styledDimension(-7.5)}; + width: calc(100% + ${styledDimension(15)}); + height: ${({ hasMarks }) => + hasMarks ? styledDimension(20.5) : styledDimension(19.5)}; } `} @@ -177,13 +181,13 @@ const sharedGrooveStyles = () => css<StyledSliderProps>` left: 50%; transform: translateX(-50%); height: 100%; - width: 8px; + width: ${styledDimension(4)}; ` : css` left: 0; top: 50%; transform: translateY(-50%); - height: 8px; + height: ${styledDimension(4)}; width: 100%; `} `; @@ -209,22 +213,22 @@ const Thumb = styled.span<StyledSliderProps>` ${({ orientation }) => orientation === 'vertical' ? css` - width: 32px; - height: 18px; - right: 2px; + width: ${styledDimension(16)}; + height: ${styledDimension(9)}; + right: ${styledDimension(1)}; transform: translateY(-50%); ` : css` - height: 32px; - width: 18px; - top: 2px; + height: ${styledDimension(16)}; + width: ${styledDimension(9)}; + top: ${styledDimension(1)}; transform: translateX(-50%); `} ${({ variant }) => variant === 'flat' ? css` ${createFlatBoxStyles()} - outline: 2px solid ${({ theme }) => theme.flatDark}; + outline: ${styledDimension(1)} solid ${({ theme }) => theme.flatDark}; background: ${({ theme }) => theme.flatLight}; ` : css` @@ -242,7 +246,7 @@ const Thumb = styled.span<StyledSliderProps>` })} `; -const tickHeight = 6; +const tickHeight = 3; const Tick = styled.span<StyledSliderProps>` display: inline-block; position: absolute; @@ -250,18 +254,21 @@ const Tick = styled.span<StyledSliderProps>` ${({ orientation }) => orientation === 'vertical' ? css` - right: ${-tickHeight - 2}px; + right: ${styledDimension(-tickHeight - 1)}; bottom: 0px; - transform: translateY(1px); - width: ${tickHeight}px; - border-bottom: 2px solid ${({ theme }) => theme.materialText}; + transform: translateY(${styledDimension(0.5)}); + width: ${styledDimension(tickHeight)}; + border-bottom: ${styledDimension(1)} solid + ${({ theme }) => theme.materialText}; ` : css` - bottom: ${-tickHeight}px; - height: ${tickHeight}px; - transform: translateX(-1px); - border-left: 1px solid ${({ theme }) => theme.materialText}; - border-right: 1px solid ${({ theme }) => theme.materialText}; + bottom: ${styledDimension(-tickHeight)}; + height: ${styledDimension(tickHeight)}; + transform: translateX(${styledDimension(-0.5)}); + border-left: ${styledDimension(0.5)} solid + ${({ theme }) => theme.materialText}; + border-right: ${styledDimension(0.5)} solid + ${({ theme }) => theme.materialText}; `} color: ${({ theme }) => theme.materialText}; @@ -269,7 +276,9 @@ const Tick = styled.span<StyledSliderProps>` $disabled && css` ${createDisabledTextStyles()} - box-shadow: 1px 1px 0px ${theme.materialTextDisabledShadow}; + box-shadow: ${styledDimension(0.5)} ${styledDimension( + 0.5 + )} 0px ${theme.materialTextDisabledShadow}; border-color: ${theme.materialTextDisabled}; `} `; @@ -283,10 +292,13 @@ const Mark = styled.div<StyledSliderProps>` ${({ orientation }) => orientation === 'vertical' ? css` - transform: translate(${tickHeight + 2}px, ${tickHeight + 1}px); + transform: translate( + ${styledDimension(tickHeight + 1)}, + ${styledDimension(tickHeight + 0.5)} + ); ` : css` - transform: translate(-0.5ch, calc(100% + 2px)); + transform: translate(-0.5ch, calc(100% + ${styledDimension(1)})); `} `; diff --git a/src/Table/TableBody.tsx b/src/Table/TableBody.tsx index 542de3a0..8213e999 100644 --- a/src/Table/TableBody.tsx +++ b/src/Table/TableBody.tsx @@ -1,24 +1,30 @@ import React, { forwardRef } from 'react'; import styled from 'styled-components'; -import { insetShadow } from '../common'; +import { createInnerBorderWithShadow } from '../common'; +import { defaultTrue } from '../common/utils'; import { CommonStyledProps } from '../types'; type TableBodyProps = { children?: React.ReactNode; + shadow?: boolean; } & React.HTMLAttributes<HTMLTableSectionElement> & CommonStyledProps; -const StyledTableBody = styled.tbody` +const StyledTableBody = styled.tbody<Pick<TableBodyProps, 'shadow'>>` background: ${({ theme }) => theme.canvas}; display: table-row-group; - box-shadow: ${insetShadow}; + box-shadow: ${({ theme, shadow }) => + createInnerBorderWithShadow({ + theme, + hasInsetShadow: defaultTrue(shadow ?? theme.shadow) + })}; overflow-y: auto; `; const TableBody = forwardRef<HTMLTableSectionElement, TableBodyProps>( - function TableBody({ children, ...otherProps }, ref) { + function TableBody({ children, shadow = true, ...otherProps }, ref) { return ( - <StyledTableBody ref={ref} {...otherProps}> + <StyledTableBody ref={ref} shadow={shadow} {...otherProps}> {children} </StyledTableBody> ); diff --git a/src/Table/TableDataCell.tsx b/src/Table/TableDataCell.tsx index f97f8b76..c1d3e473 100644 --- a/src/Table/TableDataCell.tsx +++ b/src/Table/TableDataCell.tsx @@ -1,5 +1,6 @@ import React, { forwardRef } from 'react'; import styled from 'styled-components'; +import { styledDimension } from '../common'; import { CommonStyledProps } from '../types'; type TableDataCellProps = { @@ -8,7 +9,7 @@ type TableDataCellProps = { CommonStyledProps; const StyledTd = styled.td` - padding: 0 8px; + padding: 0 ${styledDimension(4)}; `; const TableDataCell = forwardRef<HTMLTableCellElement, TableDataCellProps>( diff --git a/src/Table/TableHeadCell.tsx b/src/Table/TableHeadCell.tsx index e653d6b6..89a70f7b 100644 --- a/src/Table/TableHeadCell.tsx +++ b/src/Table/TableHeadCell.tsx @@ -1,6 +1,10 @@ import React, { forwardRef } from 'react'; import styled, { css } from 'styled-components'; -import { createBorderStyles, createDisabledTextStyles } from '../common'; +import { + createBorderStyles, + createDisabledTextStyles, + styledDimension +} from '../common'; import { noOp } from '../common/utils'; import { CommonStyledProps } from '../types'; @@ -13,7 +17,7 @@ type TableHeadCellProps = { const StyledHeadCell = styled.th<{ $disabled: boolean }>` position: relative; - padding: 0 8px; + padding: 0 ${styledDimension(4)}; display: table-cell; vertical-align: inherit; background: ${({ theme }) => theme.material}; @@ -40,12 +44,12 @@ const StyledHeadCell = styled.th<{ $disabled: boolean }>` ${createBorderStyles({ invert: true, style: 'window' })} border-left: none; border-top: none; - padding-top: 2px; + padding-top: ${styledDimension(1)}; } & > div { position: relative; - top: 2px; + top: ${styledDimension(1)}; } } `} diff --git a/src/Table/TableRow.tsx b/src/Table/TableRow.tsx index b601c093..9f5ac94b 100644 --- a/src/Table/TableRow.tsx +++ b/src/Table/TableRow.tsx @@ -1,5 +1,6 @@ import React, { forwardRef } from 'react'; import styled from 'styled-components'; +import { styledDimension } from '../common'; import { blockSizes } from '../common/system'; type TableRowProps = { @@ -9,8 +10,8 @@ type TableRowProps = { const StyledTr = styled.tr` color: inherit; display: table-row; - height: calc(${blockSizes.md} - 2px); - line-height: calc(${blockSizes.md} - 2px); + height: calc(${blockSizes.md} - ${styledDimension(1)}); + line-height: calc(${blockSizes.md} - ${styledDimension(1)}); vertical-align: middle; outline: none; diff --git a/src/Tabs/Tab.tsx b/src/Tabs/Tab.tsx index 17f0666f..4f7c2e8b 100644 --- a/src/Tabs/Tab.tsx +++ b/src/Tabs/Tab.tsx @@ -1,8 +1,13 @@ import React, { forwardRef } from 'react'; -import styled from 'styled-components'; +import styled, { css } from 'styled-components'; -import { createBorderStyles, createBoxStyles, focusOutline } from '../common'; -import { blockSizes } from '../common/system'; +import { + createBorderStyles, + createBoxStyles, + focusOutline, + styledDimension +} from '../common'; +import { styledBlockSize } from '../common/system'; import { CommonStyledProps } from '../types'; type TabProps = { @@ -23,13 +28,13 @@ const StyledTab = styled.button<TabProps>` align-items: center; justify-content: center; font-size: 1rem; - height: ${blockSizes.md}; - line-height: ${blockSizes.md}; - padding: 0 8px; + height: ${styledBlockSize('md')}; + line-height: ${styledBlockSize('md')}; + padding: 0 ${styledDimension(4)}; border-bottom: none; - border-top-left-radius: 5px; - border-top-right-radius: 5px; - margin: 0 0 -2px 0; + border-top-left-radius: ${styledDimension(2.5)}; + border-top-right-radius: ${styledDimension(2.5)}; + margin: 0 0 ${styledDimension(-1)} 0; cursor: default; color: ${({ theme }) => theme.materialText}; user-select: none; @@ -43,29 +48,29 @@ const StyledTab = styled.button<TabProps>` width: 100%; height: 100%; ${focusOutline} - outline-offset: -6px; + outline-offset: ${styledDimension(-3)}; } ${props => props.selected && - ` - z-index: 1; - height: calc(${blockSizes.md} + 4px); - top: -4px; - margin-bottom: -6px; - padding: 0 16px; - margin-left: -8px; - &:not(:last-child) { - margin-right: -8px; - } - `} + css` + z-index: 1; + height: calc(${styledBlockSize('md')} + ${styledDimension(2)}); + top: ${styledDimension(-2)}; + margin-bottom: ${styledDimension(-3)}; + padding: 0 ${styledDimension(8)}; + margin-left: ${styledDimension(-4)}; + &:not(:last-child) { + margin-right: ${styledDimension(-4)}; + } + `} &:before { content: ''; position: absolute; - width: calc(100% - 4px); - height: 6px; + width: calc(100% - ${styledDimension(2)}); + height: ${styledDimension(3)}; background: ${({ theme }) => theme.material}; - bottom: -4px; - left: 2px; + bottom: ${styledDimension(-2)}; + left: ${styledDimension(1)}; } `; diff --git a/src/Tabs/TabBody.tsx b/src/Tabs/TabBody.tsx index 1afb4f54..83f781c7 100644 --- a/src/Tabs/TabBody.tsx +++ b/src/Tabs/TabBody.tsx @@ -1,7 +1,11 @@ import React, { forwardRef } from 'react'; import styled from 'styled-components'; -import { createBorderStyles, createBoxStyles } from '../common'; +import { + createBorderStyles, + createBoxStyles, + styledDimension +} from '../common'; import { CommonStyledProps } from '../types'; type TabBodyProps = { @@ -15,7 +19,7 @@ const StyledTabBody = styled.div` position: relative; display: block; height: 100%; - padding: 16px; + padding: ${styledDimension(8)}; font-size: 1rem; `; const TabBody = forwardRef<HTMLDivElement, TabBodyProps>( diff --git a/src/Tabs/Tabs.tsx b/src/Tabs/Tabs.tsx index b0c95582..67665cb1 100644 --- a/src/Tabs/Tabs.tsx +++ b/src/Tabs/Tabs.tsx @@ -1,6 +1,7 @@ import React, { forwardRef, useMemo } from 'react'; import styled from 'styled-components'; +import { styledDimension } from '../common'; import { noOp } from '../common/utils'; import { CommonStyledProps } from '../types'; import { TabProps } from './Tab'; @@ -22,7 +23,7 @@ const StyledTabs = styled.div<{ isMultiRow: boolean }>` flex-grow: 1; } button:last-child:before { - border-right: 2px solid ${theme.borderDark}; + border-right: ${styledDimension(1)} solid ${theme.borderDark}; } `} `; @@ -34,8 +35,8 @@ const Row = styled.div.attrs(() => ({ display: flex; flex-wrap: no-wrap; text-align: left; - left: 8px; - width: calc(100% - 8px); + left: ${styledDimension(4)}; + width: calc(100% - ${styledDimension(4)}); &:not(:first-child):before { content: ''; @@ -43,8 +44,10 @@ const Row = styled.div.attrs(() => ({ right: 0; left: 0; height: 100%; - border-right: 2px solid ${({ theme }) => theme.borderDarkest}; - border-left: 2px solid ${({ theme }) => theme.borderLightest}; + border-right: ${styledDimension(1)} solid + ${({ theme }) => theme.borderDarkest}; + border-left: ${styledDimension(1)} solid + ${({ theme }) => theme.borderLightest}; } `; diff --git a/src/TextInput/TextInput.tsx b/src/TextInput/TextInput.tsx index 11f92999..49f3f6b0 100644 --- a/src/TextInput/TextInput.tsx +++ b/src/TextInput/TextInput.tsx @@ -33,6 +33,7 @@ type TextInputProps = { disabled?: boolean; fullWidth?: boolean; multiline?: boolean; + /** @deprecated Change `shadow` property on theme */ shadow?: boolean; style?: React.CSSProperties; variant?: 'default' | 'flat'; @@ -105,7 +106,7 @@ const TextInput = forwardRef< disabled = false, fullWidth, onChange = noOp, - shadow = true, + shadow, style, variant = 'default', ...otherProps @@ -141,9 +142,10 @@ const TextInput = forwardRef< return ( <WrapperComponent + $disabled={disabled} className={className} + defaultShadow fullWidth={fullWidth} - $disabled={disabled} shadow={shadow} style={style} > diff --git a/src/Tooltip/Tooltip.tsx b/src/Tooltip/Tooltip.tsx index 60cf5558..0e44b803 100644 --- a/src/Tooltip/Tooltip.tsx +++ b/src/Tooltip/Tooltip.tsx @@ -1,7 +1,12 @@ import React, { forwardRef, useState } from 'react'; -import styled from 'styled-components'; - -import { shadow } from '../common'; +import styled, { + css, + DefaultTheme, + FlattenInterpolation, + ThemeProps +} from 'styled-components'; + +import { shadow, styledDimension } from '../common'; import { isReactFocusEvent, isReactMouseEvent } from '../common/utils/events'; import { CommonStyledProps } from '../types'; @@ -33,19 +38,30 @@ type TooltipProps = { > & CommonStyledProps; -const positioningStyles: Record<TooltipPosition, string> = { - top: `top: -4px; - left: 50%; - transform: translate(-50%, -100%);`, - bottom: `bottom: -4px; - left: 50%; - transform: translate(-50%, 100%);`, - left: `left: -4px; - top: 50%; - transform: translate(-100%, -50%);`, - right: `right: -4px; - top: 50%; - transform: translate(100%, -50%);` +const positioningStyles: Record< + TooltipPosition, + FlattenInterpolation<ThemeProps<DefaultTheme>> +> = { + top: css` + top: -${styledDimension(2)}; + left: 50%; + transform: translate(-50%, -100%); + `, + bottom: css` + bottom: -${styledDimension(2)}; + left: 50%; + transform: translate(-50%, 100%); + `, + left: css` + left: -${styledDimension(2)}; + top: 50%; + transform: translate(-100%, -50%); + `, + right: css` + right: -${styledDimension(2)}; + top: 50%; + transform: translate(100%, -50%); + ` }; const Tip = styled.span<{ position: TooltipPosition; show: boolean }>` @@ -53,8 +69,8 @@ const Tip = styled.span<{ position: TooltipPosition; show: boolean }>` z-index: 1; display: ${props => (props.show ? 'block' : 'none')}; - padding: 4px; - border: 2px solid ${({ theme }) => theme.borderDarkest}; + padding: ${styledDimension(2)}; + border: ${styledDimension(1)} solid ${({ theme }) => theme.borderDarkest}; background: ${({ theme }) => theme.tooltip}; box-shadow: ${shadow}; text-align: center; diff --git a/src/TreeView/TreeView.tsx b/src/TreeView/TreeView.tsx index d896957f..a2c55280 100644 --- a/src/TreeView/TreeView.tsx +++ b/src/TreeView/TreeView.tsx @@ -1,5 +1,6 @@ import React, { forwardRef, useCallback } from 'react'; import styled, { css } from 'styled-components'; +import { styledDimension } from '../common'; import useControlledOrUncontrolled from '../common/hooks/useControlledOrUncontrolled'; import { LabelText, StyledLabel } from '../common/SwitchBase'; @@ -59,7 +60,8 @@ const focusedElementStyles = css<{ $disabled: boolean }>` ${Text} { background: ${({ theme }) => theme.hoverBackground}; color: ${({ theme }) => theme.materialTextInvert}; - outline: 2px dotted ${({ theme }) => theme.focusSecondary}; + outline: ${styledDimension(1)} dotted + ${({ theme }) => theme.focusSecondary}; } } ` @@ -76,16 +78,17 @@ const TreeWrapper = styled.ul<{ isRootLevel: boolean }>` &:before { content: ''; position: absolute; - top: 20px; + top: ${styledDimension(10)}; bottom: 0; - left: 5.5px; - width: 1px; - border-left: 2px dashed ${({ theme }) => theme.borderDark}; + left: ${styledDimension(2.75)}; + width: ${styledDimension(0.5)}; + border-left: ${styledDimension(1)} dashed + ${({ theme }) => theme.borderDark}; } `} ul { - padding-left: 19.5px; + padding-left: ${styledDimension(9.75)}; } li { @@ -94,18 +97,19 @@ const TreeWrapper = styled.ul<{ isRootLevel: boolean }>` &:before { content: ''; position: absolute; - top: 17.5px; - left: 5.5px; - width: 22px; - border-top: 2px dashed ${({ theme }) => theme.borderDark}; - font-size: 12px; + top: ${styledDimension(8.75)}; + left: ${styledDimension(2.75)}; + width: ${styledDimension(11)}; + border-top: ${styledDimension(1)} dashed + ${({ theme }) => theme.borderDark}; + font-size: ${styledDimension(6)}; } } `; const TreeItem = styled.li<{ hasItems: boolean; isRootLevel: boolean }>` position: relative; - padding-left: ${({ hasItems }) => (!hasItems ? '13px' : '0')}; + padding-left: ${({ hasItems }) => (!hasItems ? styledDimension(6.5) : '0')}; ${({ isRootLevel }) => !isRootLevel @@ -115,10 +119,10 @@ const TreeItem = styled.li<{ hasItems: boolean; isRootLevel: boolean }>` content: ''; position: absolute; z-index: 1; - top: 19.5px; + top: ${styledDimension(9.75)}; bottom: 0; - left: 1.5px; - width: 10px; + left: ${styledDimension(0.75)}; + width: ${styledDimension(5)}; background: ${({ theme }) => theme.material}; } } @@ -128,10 +132,10 @@ const TreeItem = styled.li<{ hasItems: boolean; isRootLevel: boolean }>` &:after { content: ''; position: absolute; - top: 19.5px; - left: 1px; + top: ${styledDimension(9.75)}; + left: ${styledDimension(0.5)}; bottom: 0; - width: 10px; + width: ${styledDimension(5)}; background: ${({ theme }) => theme.material}; } } @@ -141,10 +145,11 @@ const TreeItem = styled.li<{ hasItems: boolean; isRootLevel: boolean }>` &:after { content: ''; position: absolute; - top: -18px; + top: ${styledDimension(-9)}; bottom: 0; - left: 25px; - border-left: 2px dashed ${({ theme }) => theme.borderDark}; + left: ${styledDimension(12.5)}; + border-left: ${styledDimension(1)} dashed + ${({ theme }) => theme.borderDark}; } } `; @@ -165,7 +170,7 @@ const Summary = styled.summary` align-items: center; color: ${({ theme }) => theme.materialText}; user-select: none; - padding-left: 18px; + padding-left: ${styledDimension(9)}; ${focusedElementStyles}; &::-webkit-details-marker { @@ -177,12 +182,12 @@ const Summary = styled.summary` position: absolute; left: 0; display: block; - width: 8px; - height: 9px; - border: 2px solid #808080; - padding-left: 1px; + width: ${styledDimension(4)}; + height: ${styledDimension(4.5)}; + border: ${styledDimension(1)} solid #808080; + padding-left: ${styledDimension(0.5)}; background-color: #fff; - line-height: 8px; + line-height: ${styledDimension(4)}; text-align: center; } `; @@ -193,8 +198,8 @@ const TitleWithIcon = styled(StyledLabel)` background: none; border: 0; font-family: inherit; - padding-top: 8px; - padding-bottom: 8px; + padding-top: ${styledDimension(4)}; + padding-bottom: ${styledDimension(4)}; margin: 0; ${focusedElementStyles}; `; @@ -203,9 +208,9 @@ const Icon = styled.span` display: flex; align-items: center; justify-content: center; - width: 16px; - height: 16px; - margin-right: 6px; + width: ${styledDimension(8)}; + height: ${styledDimension(8)}; + margin-right: ${styledDimension(3)}; `; function toggleItem<T>(state: T[], id: T) { diff --git a/src/Window/Window.tsx b/src/Window/Window.tsx index 79c003dd..9cd03304 100644 --- a/src/Window/Window.tsx +++ b/src/Window/Window.tsx @@ -1,19 +1,24 @@ import React, { forwardRef } from 'react'; import styled, { css } from 'styled-components'; -import { createBorderStyles, createBoxStyles } from '../common'; +import { + createBorderStyles, + createBoxStyles, + styledDimension +} from '../common'; import { CommonStyledProps } from '../types'; type WindowProps = { children?: React.ReactNode; resizable?: boolean; resizeRef?: React.Ref<HTMLSpanElement>; + /** @deprecated Change `shadow` property on theme */ shadow?: boolean; } & React.HTMLAttributes<HTMLDivElement> & CommonStyledProps; const StyledWindow = styled.div` position: relative; - padding: 4px; + padding: ${styledDimension(2)}; font-size: 1rem; ${createBorderStyles({ style: 'window' })} ${createBoxStyles()} @@ -23,10 +28,10 @@ const ResizeHandle = styled.span` ${({ theme }) => css` display: inline-block; position: absolute; - bottom: 10px; - right: 10px; - width: 25px; - height: 25px; + bottom: ${styledDimension(5)}; + right: ${styledDimension(5)}; + width: ${styledDimension(12.5)}; + height: ${styledDimension(12.5)}; background-image: linear-gradient( 135deg, ${theme.borderLightest} 16.67%, @@ -41,19 +46,16 @@ const ResizeHandle = styled.span` ${theme.borderDark} 83.33%, ${theme.borderDark} 100% ); - background-size: 8.49px 8.49px; + background-size: ${styledDimension(4.245)} ${styledDimension(4.245)}; clip-path: polygon(100% 0px, 0px 100%, 100% 100%); cursor: nwse-resize; `} `; const Window = forwardRef<HTMLDivElement, WindowProps>( - ( - { children, resizable = false, resizeRef, shadow = true, ...otherProps }, - ref - ) => { + ({ children, resizable = false, resizeRef, shadow, ...otherProps }, ref) => { return ( - <StyledWindow ref={ref} shadow={shadow} {...otherProps}> + <StyledWindow defaultShadow ref={ref} shadow={shadow} {...otherProps}> {children} {resizable && ( <ResizeHandle data-testid='resizeHandle' ref={resizeRef} /> diff --git a/src/Window/WindowContent.tsx b/src/Window/WindowContent.tsx index 7206ef7a..8ee39bcd 100644 --- a/src/Window/WindowContent.tsx +++ b/src/Window/WindowContent.tsx @@ -1,5 +1,6 @@ import React, { forwardRef } from 'react'; import styled from 'styled-components'; +import { styledDimension } from '../common'; import { CommonStyledProps } from '../types'; type WindowContentProps = { @@ -8,7 +9,7 @@ type WindowContentProps = { CommonStyledProps; const StyledWindowContent = styled.div` - padding: 16px; + padding: ${styledDimension(8)}; `; const WindowContent = forwardRef<HTMLDivElement, WindowContentProps>( diff --git a/src/Window/WindowHeader.tsx b/src/Window/WindowHeader.tsx index 8c1bdd71..6c93b3ca 100644 --- a/src/Window/WindowHeader.tsx +++ b/src/Window/WindowHeader.tsx @@ -1,6 +1,7 @@ import React, { forwardRef } from 'react'; import styled, { css } from 'styled-components'; import { StyledButton } from '../Button/Button'; +import { styledDimension } from '../common'; import { CommonStyledProps } from '../types'; type WindowHeaderProps = { @@ -10,12 +11,12 @@ type WindowHeaderProps = { CommonStyledProps; const StyledWindowHeader = styled.div<Pick<WindowHeaderProps, 'active'>>` - height: 33px; - line-height: 33px; + height: ${styledDimension(16.5)}; + line-height: ${styledDimension(16.5)}; padding-left: 0.25rem; - padding-right: 3px; + padding-right: ${styledDimension(1.5)}; font-weight: bold; - border: 2px solid ${({ theme }) => theme.material}; + border: ${styledDimension(1)} solid ${({ theme }) => theme.material}; ${({ active }) => active === false ? css` @@ -30,8 +31,8 @@ const StyledWindowHeader = styled.div<Pick<WindowHeaderProps, 'active'>>` ${StyledButton} { padding-left: 0; padding-right: 0; - height: 27px; - width: 31px; + height: ${styledDimension(13.5)}; + width: ${styledDimension(15.5)}; } `; diff --git a/src/common/SwitchBase.ts b/src/common/SwitchBase.ts index 717e268f..f807996a 100644 --- a/src/common/SwitchBase.ts +++ b/src/common/SwitchBase.ts @@ -1,16 +1,16 @@ import styled, { css } from 'styled-components'; -import { createDisabledTextStyles, focusOutline } from '.'; +import { createDisabledTextStyles, focusOutline, styledDimension } from '.'; import { StyledMenuListItem } from '../MenuList/MenuList'; -export const size = 20; +export const size = 10; export const StyledInput = styled.input` position: absolute; left: 0; margin: 0; - width: ${size}px; - height: ${size}px; + width: ${styledDimension(size)}; + height: ${styledDimension(size)}; opacity: 0; z-index: -1; `; @@ -19,7 +19,7 @@ export const StyledLabel = styled.label<{ $disabled: boolean }>` display: inline-flex; align-items: center; position: relative; - margin: 8px 0; + margin: ${styledDimension(4)} 0; cursor: ${({ $disabled }) => (!$disabled ? 'pointer' : 'auto')}; user-select: none; font-size: 1rem; @@ -43,8 +43,8 @@ export const StyledLabel = styled.label<{ $disabled: boolean }>` export const LabelText = styled.span` display: inline-block; line-height: 1; - padding: 2px; - ${StyledInput}:focus ~ & { + padding: ${styledDimension(1)}; + ${StyledInput}:focus ~& { ${focusOutline} } ${StyledInput}:not(:disabled) ~ &:active { diff --git a/src/common/constants.ts b/src/common/constants.ts index aaee2778..9f811c90 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -10,3 +10,5 @@ export const KEYBOARD_KEY_CODES = { SPACE: 'Space', TAB: 'Tab' }; + +export const SCALE = 2; diff --git a/src/common/index.spec.ts b/src/common/index.spec.ts new file mode 100644 index 00000000..f60e20a6 --- /dev/null +++ b/src/common/index.spec.ts @@ -0,0 +1,34 @@ +import { SCALE } from './constants'; +import { styledDimension } from './index'; +import original from './themes/original'; + +describe(styledDimension, () => { + it('returns the default scale with no theme', () => { + expect(styledDimension()({ theme: undefined })).toEqual(`${SCALE}px`); + }); + + it('returns the default scale with a theme that has no scale', () => { + const themeWithoutScale = { + ...original, + scale: undefined + }; + expect(styledDimension()({ theme: themeWithoutScale })).toEqual( + `${SCALE}px` + ); + }); + + it('returns a proportional scale', () => { + const themeScale1 = { + ...original, + scale: 1 + }; + const themeScale3 = { + ...original, + scale: 3 + }; + expect(styledDimension()({ theme: themeScale1 })).toEqual('1px'); + expect(styledDimension()({ theme: themeScale3 })).toEqual('3px'); + expect(styledDimension(5)({ theme: themeScale1 })).toEqual('5px'); + expect(styledDimension(5)({ theme: themeScale3 })).toEqual('15px'); + }); +}); diff --git a/src/common/index.ts b/src/common/index.ts index 6789ed15..dee7b432 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -1,13 +1,18 @@ import { css } from 'styled-components'; -import { Color, CommonThemeProps, Theme } from '../types'; +import { CommonThemeProps, Theme } from '../types'; +import { SCALE } from './constants'; +import { Color, ThemeColors } from './themes/types'; -export const shadow = '4px 4px 10px 0 rgba(0, 0, 0, 0.35)'; -export const insetShadow = 'inset 2px 2px 3px rgba(0,0,0,0.2)'; +export const styledDimension = + (ratio = 1, { delta = 0, unit = 'px' } = {}) => + ({ theme }: { theme: Theme | undefined }) => + `${(theme?.scale ?? SCALE) * ratio + delta}${unit}`; export const createDisabledTextStyles = () => css` -webkit-text-fill-color: ${({ theme }) => theme.materialTextDisabled}; color: ${({ theme }) => theme.materialTextDisabled}; - text-shadow: 1px 1px ${({ theme }) => theme.materialTextDisabledShadow}; + text-shadow: ${styledDimension(0.5)} ${styledDimension(0.5)} + ${({ theme }) => theme.materialTextDisabledShadow}; /* filter: grayscale(100%); */ `; @@ -15,8 +20,8 @@ export const createBoxStyles = ({ background = 'material', color = 'materialText' }: { - background?: keyof Theme; - color?: keyof Theme; + background?: keyof ThemeColors; + color?: keyof ThemeColors; } = {}) => css` box-sizing: border-box; display: inline-block; @@ -28,28 +33,34 @@ export const createBoxStyles = ({ export const createHatchedBackground = ({ mainColor = 'black', secondaryColor = 'transparent', - pixelSize = 2 -}) => css` - background-image: ${[ - `linear-gradient( - 45deg, - ${mainColor} 25%, - transparent 25%, - transparent 75%, - ${mainColor} 75% - )`, - `linear-gradient( - 45deg, - ${mainColor} 25%, - transparent 25%, - transparent 75%, - ${mainColor} 75% - )` - ].join(',')}; - background-color: ${secondaryColor}; - background-size: ${`${pixelSize * 2}px ${pixelSize * 2}px`}; - background-position: 0 0, ${`${pixelSize}px ${pixelSize}px`}; -`; + pixelSize +}: { + mainColor?: Color; + secondaryColor?: Color; + pixelSize?: number; +}) => { + const size = styledDimension(pixelSize !== undefined ? pixelSize / 2 : 1); + const doubleSize = styledDimension(pixelSize !== undefined ? pixelSize : 2); + return css` + background-image: linear-gradient( + 45deg, + ${mainColor} 25%, + transparent 25%, + transparent 75%, + ${mainColor} 75% + ), + linear-gradient( + 45deg, + ${mainColor} 25%, + transparent 25%, + transparent 75%, + ${mainColor} 75% + ); + background-color: ${secondaryColor}; + background-size: ${doubleSize} ${doubleSize}; + background-position: 0 0, ${size} ${size}; + `; +}; export const createFlatBoxStyles = () => css<CommonThemeProps>` position: relative; @@ -58,9 +69,9 @@ export const createFlatBoxStyles = () => css<CommonThemeProps>` color: ${({ theme }) => theme.materialText}; background: ${({ $disabled, theme }) => $disabled ? theme.flatLight : theme.canvas}; - border: 2px solid ${({ theme }) => theme.canvas}; - outline: 2px solid ${({ theme }) => theme.flatDark}; - outline-offset: -4px; + border: ${styledDimension(1)} solid ${({ theme }) => theme.canvas}; + outline: ${styledDimension(1)} solid ${({ theme }) => theme.flatDark}; + outline-offset: -${styledDimension(2)}; `; export type BorderStyles = @@ -74,10 +85,10 @@ export type BorderStyles = | 'window'; type BorderStyle = { - topLeftOuter: keyof Theme; - topLeftInner: keyof Theme | null; - bottomRightInner: keyof Theme | null; - bottomRightOuter: keyof Theme; + topLeftOuter: keyof ThemeColors; + topLeftInner: keyof ThemeColors | undefined; + bottomRightInner: keyof ThemeColors | undefined; + bottomRightOuter: keyof ThemeColors; }; const borderStyles: Record<BorderStyles, BorderStyle> = { @@ -95,14 +106,14 @@ const borderStyles: Record<BorderStyles, BorderStyle> = { }, buttonThin: { topLeftOuter: 'borderLightest', - topLeftInner: null, - bottomRightInner: null, + topLeftInner: undefined, + bottomRightInner: undefined, bottomRightOuter: 'borderDark' }, buttonThinPressed: { topLeftOuter: 'borderDark', - topLeftInner: null, - bottomRightInner: null, + topLeftInner: undefined, + bottomRightInner: undefined, bottomRightOuter: 'borderLightest' }, field: { @@ -119,8 +130,8 @@ const borderStyles: Record<BorderStyles, BorderStyle> = { }, status: { topLeftOuter: 'borderDark', - topLeftInner: null, - bottomRightInner: null, + topLeftInner: undefined, + bottomRightInner: undefined, bottomRightOuter: 'borderLightest' }, window: { @@ -139,23 +150,35 @@ export const createInnerBorderWithShadow = ({ hasInsetShadow = false }: { theme: Theme; - topLeftInner: keyof Theme | null; - bottomRightInner: keyof Theme | null; + topLeftInner?: keyof ThemeColors; + bottomRightInner?: keyof ThemeColors; hasShadow?: boolean; hasInsetShadow?: boolean; -}) => - [ - hasShadow ? shadow : false, - hasInsetShadow ? insetShadow : false, - topLeftInner !== null - ? `inset 1px 1px 0px 1px ${theme[topLeftInner]}` +}) => { + const scale = Number(styledDimension(1, { unit: '' })({ theme })); + return [ + hasShadow + ? `${scale * 2}px ${scale * 2}px ${scale * 5}px 0 rgba(0, 0, 0, 0.35)` : false, - bottomRightInner !== null - ? `inset -1px -1px 0 1px ${theme[bottomRightInner]}` + hasInsetShadow + ? `inset ${scale * 1}px ${scale * 1}px ${ + scale * 1.5 + }px rgba(0, 0, 0, 0.2)` + : false, + topLeftInner !== undefined + ? `inset ${scale / 2}px ${scale / 2}px 0px ${scale / 2}px ${ + theme[topLeftInner] + }` + : false, + bottomRightInner !== undefined + ? `inset -${scale / 2}px -${scale / 2}px 0 ${scale / 2}px ${ + theme[bottomRightInner] + }` : false ] .filter(Boolean) .join(', '); +}; export const createBorderStyles = ({ invert = false, @@ -169,7 +192,7 @@ export const createBorderStyles = ({ } as const; return css<CommonThemeProps>` border-style: solid; - border-width: 2px; + border-width: ${styledDimension(1)}; border-left-color: ${({ theme }) => theme[borderStyles[style][borders.topLeftOuter]]}; border-top-color: ${({ theme }) => @@ -178,12 +201,12 @@ export const createBorderStyles = ({ theme[borderStyles[style][borders.bottomRightOuter]]}; border-bottom-color: ${({ theme }) => theme[borderStyles[style][borders.bottomRightOuter]]}; - box-shadow: ${({ theme, shadow: hasShadow }) => + box-shadow: ${({ defaultShadow, theme, shadow: hasShadow }) => createInnerBorderWithShadow({ theme, topLeftInner: borderStyles[style][borders.topLeftInner], bottomRightInner: borderStyles[style][borders.bottomRightInner], - hasShadow + hasShadow: (defaultShadow && theme.shadow) || hasShadow })}; `; }; @@ -196,16 +219,23 @@ export const createWellBorderStyles = (invert = false) => }); export const focusOutline = () => css` - outline: 2px dotted ${({ theme }) => theme.materialText}; + outline: ${styledDimension(1)} dotted ${({ theme }) => theme.materialText}; `; const nodeBtoa = (string: string) => Buffer.from(string).toString('base64'); const base64encode = typeof btoa !== 'undefined' ? btoa : nodeBtoa; -const createTriangleSVG = (color: Color, angle = 0) => { - const svg = `<svg height="26" width="26" viewBox="0 0 26 26" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> - <g transform="rotate(${angle} 13 13)"> - <polygon fill="${color}" points="6,10 20,10 13,17"/> +const createTriangleSVG = (theme: Theme, angle = 0) => { + const scale = Number(styledDimension(1, { unit: '' })({ theme })); + const svg = `<svg height="${scale * 13}" width="${scale * 13}" viewBox="0 0 ${ + scale * 13 + } ${ + scale * 13 + }" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <g transform="rotate(${angle} ${scale * 6.5} ${scale * 6.5})"> + <polygon fill="${theme.materialText}" points="${scale * 3},${scale * 5} ${ + scale * 10 + },${scale * 5} ${scale * 6.5},${scale * 8.5}"/> </g> </svg>`; const encoded = base64encode(svg); @@ -214,8 +244,8 @@ const createTriangleSVG = (color: Color, angle = 0) => { export const createScrollbars = (variant = 'default') => css` ::-webkit-scrollbar { - width: 26px; - height: 26px; + width: ${styledDimension(13)}; + height: ${styledDimension(13)}; } ::-webkit-scrollbar-track { ${({ theme }) => @@ -229,7 +259,7 @@ export const createScrollbars = (variant = 'default') => css` ${variant === 'flat' ? createFlatBoxStyles() : createBorderStyles({ style: 'window' })} - outline-offset: -2px; + outline-offset: -${styledDimension(1)}; } ::-webkit-scrollbar-corner { @@ -241,9 +271,9 @@ export const createScrollbars = (variant = 'default') => css` ? createFlatBoxStyles() : createBorderStyles({ style: 'window' })} display: block; - outline-offset: -2px; - height: 26px; - width: 26px; + outline-offset: -${styledDimension(1)}; + height: ${styledDimension(13)}; + width: ${styledDimension(13)}; background-repeat: no-repeat; background-size: 100%; background-position: 0 0; @@ -264,22 +294,18 @@ export const createScrollbars = (variant = 'default') => css` } ::-webkit-scrollbar-button:horizontal:decrement { - background-image: ${({ theme }) => - createTriangleSVG(theme.materialText, 90)}; + background-image: ${({ theme }) => createTriangleSVG(theme, 90)}; } ::-webkit-scrollbar-button:horizontal:increment { - background-image: ${({ theme }) => - createTriangleSVG(theme.materialText, 270)}; + background-image: ${({ theme }) => createTriangleSVG(theme, 270)}; } ::-webkit-scrollbar-button:vertical:decrement { - background-image: ${({ theme }) => - createTriangleSVG(theme.materialText, 180)}; + background-image: ${({ theme }) => createTriangleSVG(theme, 180)}; } ::-webkit-scrollbar-button:vertical:increment { - background-image: ${({ theme }) => - createTriangleSVG(theme.materialText, 0)}; + background-image: ${({ theme }) => createTriangleSVG(theme, 0)}; } `; diff --git a/src/common/styleReset.ts b/src/common/styleReset.ts index 6a6339db..41443a54 100644 --- a/src/common/styleReset.ts +++ b/src/common/styleReset.ts @@ -83,8 +83,7 @@ video { margin: 0; padding: 0; border: 0; - font-size: 100%; - font: inherit; + font-weight: inherit; vertical-align: baseline; } /* HTML5 display-role reset for older browsers */ diff --git a/src/common/system.ts b/src/common/system.ts index deba781b..04e2221a 100644 --- a/src/common/system.ts +++ b/src/common/system.ts @@ -1,9 +1,13 @@ // TODO - implement styled-system +import { styledDimension } from '.'; import { Sizes } from '../types'; -export const blockSizes: Record<Sizes, string> = { - sm: '28px', - md: '36px', - lg: '44px' +export const blockSizes: Record<Sizes, number> = { + sm: 14, + md: 18, + lg: 22 }; + +export const styledBlockSize = (size: Sizes) => + styledDimension(blockSizes[size]); diff --git a/src/common/themes/aiee.ts b/src/common/themes/aiee.ts index 2bf3c2f0..b9a780ba 100644 --- a/src/common/themes/aiee.ts +++ b/src/common/themes/aiee.ts @@ -37,5 +37,7 @@ export default { materialTextDisabledShadow: 'rgb(250,254,255)', materialTextInvert: 'rgb(0,62,109)', progress: 'rgb(251,211,61)', + scale: 2, + shadow: true, tooltip: 'rgb(255,243,185)' } as Theme; diff --git a/src/common/themes/ash.ts b/src/common/themes/ash.ts index 5158a37b..c0acff0b 100644 --- a/src/common/themes/ash.ts +++ b/src/common/themes/ash.ts @@ -35,5 +35,7 @@ export default { materialTextDisabledShadow: 'rgb(175, 175, 175)', materialTextInvert: 'rgb(255, 255, 255)', progress: 'rgb(0, 0, 0)', + scale: 2, + shadow: true, tooltip: 'rgb(0, 0, 0)' } as Theme; diff --git a/src/common/themes/azureOrange.ts b/src/common/themes/azureOrange.ts index 2c650ea5..340f62ca 100644 --- a/src/common/themes/azureOrange.ts +++ b/src/common/themes/azureOrange.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#7ebfff', materialTextInvert: '#000000', progress: '#F46036', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/bee.ts b/src/common/themes/bee.ts index c8d51775..47d9fbd4 100644 --- a/src/common/themes/bee.ts +++ b/src/common/themes/bee.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#f8df6e', materialTextInvert: '#ffffff', progress: '#0C1618', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/blackAndWhite.ts b/src/common/themes/blackAndWhite.ts index 0af5dbd8..d9421cb3 100644 --- a/src/common/themes/blackAndWhite.ts +++ b/src/common/themes/blackAndWhite.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#ffffff', materialTextInvert: '#ffffff', progress: '#000000', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/blue.ts b/src/common/themes/blue.ts index 6cd4ef4a..ca3782fa 100644 --- a/src/common/themes/blue.ts +++ b/src/common/themes/blue.ts @@ -37,5 +37,7 @@ export default { materialTextDisabledShadow: 'rgb(211, 228, 248)', materialTextInvert: 'rgb(255, 255, 255)', progress: 'rgb(51, 153, 255)', + scale: 2, + shadow: true, tooltip: 'rgb(225, 225, 255)' } as Theme; diff --git a/src/common/themes/brick.ts b/src/common/themes/brick.ts index 8a629c26..ed3024a8 100644 --- a/src/common/themes/brick.ts +++ b/src/common/themes/brick.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#ffffff', materialTextInvert: '#ffffff', progress: '#8e0101', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/candy.ts b/src/common/themes/candy.ts index 8202d44d..290ca375 100644 --- a/src/common/themes/candy.ts +++ b/src/common/themes/candy.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#EFF1F3', materialTextInvert: '#EFF1F3', progress: '#256EFF', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/cherry.ts b/src/common/themes/cherry.ts index 5557c70e..ddf5e7c6 100644 --- a/src/common/themes/cherry.ts +++ b/src/common/themes/cherry.ts @@ -37,5 +37,7 @@ export default { materialTextDisabledShadow: 'rgb(255, 255, 255)', materialTextInvert: 'rgb(255, 255, 255)', progress: 'rgb(106, 10, 36)', + scale: 2, + shadow: true, tooltip: 'rgb(225, 254, 255)' } as Theme; diff --git a/src/common/themes/coldGray.ts b/src/common/themes/coldGray.ts index dad0b4d7..199fa2e0 100644 --- a/src/common/themes/coldGray.ts +++ b/src/common/themes/coldGray.ts @@ -33,5 +33,7 @@ export default { materialTextDisabledShadow: '#c7c7df', materialTextInvert: '#c7c7df', progress: '#8d88c2', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/counterStrike.ts b/src/common/themes/counterStrike.ts index b3586626..31f3bcaa 100644 --- a/src/common/themes/counterStrike.ts +++ b/src/common/themes/counterStrike.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#788475', materialTextInvert: '#fefefe', progress: '#978830', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/darkTeal.ts b/src/common/themes/darkTeal.ts index f71ffbbd..b8987c19 100644 --- a/src/common/themes/darkTeal.ts +++ b/src/common/themes/darkTeal.ts @@ -37,5 +37,7 @@ export default { materialTextDisabledShadow: 'rgb(88, 139, 139)', materialTextInvert: 'rgb(255, 255, 255)', progress: 'rgb(0, 128, 128)', + scale: 2, + shadow: true, tooltip: 'rgb(0, 32, 32)' } as Theme; diff --git a/src/common/themes/denim.ts b/src/common/themes/denim.ts index 2275debc..ec705b1f 100644 --- a/src/common/themes/denim.ts +++ b/src/common/themes/denim.ts @@ -37,5 +37,7 @@ export default { materialTextDisabledShadow: 'rgb(191, 191, 255)', materialTextInvert: 'rgb(255, 255, 255)', progress: 'rgb(10, 36, 106)', + scale: 2, + shadow: true, tooltip: 'rgb(255, 255, 225)' } as Theme; diff --git a/src/common/themes/eggplant.ts b/src/common/themes/eggplant.ts index 62cf04f9..591b9603 100644 --- a/src/common/themes/eggplant.ts +++ b/src/common/themes/eggplant.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#cee8e3', materialTextInvert: '#ffffff', progress: '#4b8178', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/fxDev.ts b/src/common/themes/fxDev.ts index 80e970ba..1786f051 100644 --- a/src/common/themes/fxDev.ts +++ b/src/common/themes/fxDev.ts @@ -37,5 +37,7 @@ export default { materialTextDisabledShadow: 'rgb(107, 113, 122)', materialTextInvert: 'rgb(255, 255, 255)', progress: 'rgb(7, 77, 117)', + scale: 2, + shadow: true, tooltip: 'rgb(243, 242, 219)' } as Theme; diff --git a/src/common/themes/highContrast.ts b/src/common/themes/highContrast.ts index dd6f401d..5b8466a2 100644 --- a/src/common/themes/highContrast.ts +++ b/src/common/themes/highContrast.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#ffffff', materialTextInvert: '#ffffff', progress: '#8e0284', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/honey.ts b/src/common/themes/honey.ts index 7384de3f..bb7d1716 100644 --- a/src/common/themes/honey.ts +++ b/src/common/themes/honey.ts @@ -37,5 +37,7 @@ export default { materialTextDisabledShadow: 'rgb(255, 220, 128)', materialTextInvert: 'rgb(255, 255, 0)', progress: 'rgb(170, 123, 0)', + scale: 2, + shadow: true, tooltip: 'rgb(255, 220, 128)' } as Theme; diff --git a/src/common/themes/hotChocolate.ts b/src/common/themes/hotChocolate.ts index 1c70379f..2f394c62 100644 --- a/src/common/themes/hotChocolate.ts +++ b/src/common/themes/hotChocolate.ts @@ -37,5 +37,7 @@ export default { materialTextDisabledShadow: 'rgb(219, 200, 181)', materialTextInvert: 'rgb(255, 255, 255)', progress: 'rgb(128, 96, 64)', + scale: 2, + shadow: true, tooltip: 'rgb(219, 200, 181)' } as Theme; diff --git a/src/common/themes/hotdogStand.ts b/src/common/themes/hotdogStand.ts index 95da5767..3caab616 100644 --- a/src/common/themes/hotdogStand.ts +++ b/src/common/themes/hotdogStand.ts @@ -36,5 +36,7 @@ export default { materialTextDisabledShadow: 'rgb(0, 0, 0)', materialTextInvert: 'rgb(255, 255, 255)', progress: 'rgb(0, 0, 0)', + scale: 2, + shadow: true, tooltip: 'rgb(255, 255, 225)' } as Theme; diff --git a/src/common/themes/lilac.ts b/src/common/themes/lilac.ts index 2839d60c..3acf7c85 100644 --- a/src/common/themes/lilac.ts +++ b/src/common/themes/lilac.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#ffffff', materialTextInvert: '#ffffff', progress: '#5e4dba', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/lilacRoseDark.ts b/src/common/themes/lilacRoseDark.ts index 25c848b1..8ed22f25 100644 --- a/src/common/themes/lilacRoseDark.ts +++ b/src/common/themes/lilacRoseDark.ts @@ -33,5 +33,7 @@ export default { materialTextDisabledShadow: '#ecbfe3', materialTextInvert: '#ecbfe3', progress: '#713259', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/maple.ts b/src/common/themes/maple.ts index 3ce68c59..625733b9 100644 --- a/src/common/themes/maple.ts +++ b/src/common/themes/maple.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#ffffff', materialTextInvert: '#ffffff', progress: '#8e0101', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/marine.ts b/src/common/themes/marine.ts index a30b4ec5..899bd416 100644 --- a/src/common/themes/marine.ts +++ b/src/common/themes/marine.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#ffffff', materialTextInvert: '#ffffff', progress: '#000080', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/matrix.ts b/src/common/themes/matrix.ts index 929cb830..462253a6 100644 --- a/src/common/themes/matrix.ts +++ b/src/common/themes/matrix.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#a7a7a7', materialTextInvert: '#ffffff', progress: '#000000', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/millenium.ts b/src/common/themes/millenium.ts index 499f979d..2d07f661 100644 --- a/src/common/themes/millenium.ts +++ b/src/common/themes/millenium.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#ffffff', materialTextInvert: '#ffffff', progress: '#00256e', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/modernDark.ts b/src/common/themes/modernDark.ts index 88949925..771acc18 100644 --- a/src/common/themes/modernDark.ts +++ b/src/common/themes/modernDark.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#121317', materialTextInvert: '#202127', progress: '#f88702', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/molecule.ts b/src/common/themes/molecule.ts index 7752c297..9ab0fd6e 100644 --- a/src/common/themes/molecule.ts +++ b/src/common/themes/molecule.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#d79099', materialTextInvert: '#f1f5f6', progress: '#a03d49', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/ninjaTurtles.ts b/src/common/themes/ninjaTurtles.ts index 8b1fdbed..25479eba 100644 --- a/src/common/themes/ninjaTurtles.ts +++ b/src/common/themes/ninjaTurtles.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#55fd55', materialTextInvert: '#000000', progress: '#FF1D15', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/olive.ts b/src/common/themes/olive.ts index 4b33f700..576c61cf 100644 --- a/src/common/themes/olive.ts +++ b/src/common/themes/olive.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#fcfd3e', materialTextInvert: '#000000', progress: '#F3DE2C', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/original.ts b/src/common/themes/original.ts index 86d63974..a0aa3455 100644 --- a/src/common/themes/original.ts +++ b/src/common/themes/original.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#fefefe', materialTextInvert: '#fefefe', progress: '#060084', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/pamelaAnderson.ts b/src/common/themes/pamelaAnderson.ts index 3b8c8049..843495ca 100644 --- a/src/common/themes/pamelaAnderson.ts +++ b/src/common/themes/pamelaAnderson.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#ff7ebf', materialTextInvert: '#F1E4E8', progress: '#004FFF', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/peggysPastels.ts b/src/common/themes/peggysPastels.ts index 3f2bdf98..a79e17ec 100644 --- a/src/common/themes/peggysPastels.ts +++ b/src/common/themes/peggysPastels.ts @@ -37,5 +37,7 @@ export default { materialTextDisabledShadow: 'rgb(250, 224, 228)', materialTextInvert: 'rgb(0, 0, 0)', progress: 'rgb(162, 219, 210)', + scale: 2, + shadow: true, tooltip: 'rgb(204, 255, 255)' } as Theme; diff --git a/src/common/themes/plum.ts b/src/common/themes/plum.ts index 4bd1937e..d967edc2 100644 --- a/src/common/themes/plum.ts +++ b/src/common/themes/plum.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#e8dad6', materialTextInvert: '#ffffff', progress: '#483f63', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/polarized.ts b/src/common/themes/polarized.ts index dc439dff..200cfb28 100644 --- a/src/common/themes/polarized.ts +++ b/src/common/themes/polarized.ts @@ -37,5 +37,7 @@ export default { materialTextDisabledShadow: 'rgb(60, 60, 194)', materialTextInvert: 'rgb(0, 0, 255)', progress: 'rgb(192, 192, 64)', + scale: 2, + shadow: true, tooltip: 'rgb(16, 16, 240)' } as Theme; diff --git a/src/common/themes/powerShell.ts b/src/common/themes/powerShell.ts index 3515ec80..51f4e0a6 100644 --- a/src/common/themes/powerShell.ts +++ b/src/common/themes/powerShell.ts @@ -36,5 +36,7 @@ export default { materialTextDisabledShadow: 'rgb(128, 128, 128)', materialTextInvert: 'rgb(255, 255, 255)', progress: 'rgb(0, 128, 128)', + scale: 2, + shadow: true, tooltip: 'rgb(255, 255, 225)' } as Theme; diff --git a/src/common/themes/rainyDay.ts b/src/common/themes/rainyDay.ts index cc89213c..8543761d 100644 --- a/src/common/themes/rainyDay.ts +++ b/src/common/themes/rainyDay.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#b7cee5', materialTextInvert: '#ffffff', progress: '#4b6480', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/raspberry.ts b/src/common/themes/raspberry.ts index 6898381f..2c96f1a9 100644 --- a/src/common/themes/raspberry.ts +++ b/src/common/themes/raspberry.ts @@ -37,5 +37,7 @@ export default { materialTextDisabledShadow: 'rgb(200, 204, 206)', materialTextInvert: 'rgb(255, 255, 255)', progress: 'rgb(10, 36, 106)', + scale: 2, + shadow: true, tooltip: 'rgb(255, 255, 225)' } as Theme; diff --git a/src/common/themes/redWine.ts b/src/common/themes/redWine.ts index a2bf371c..eb94113e 100644 --- a/src/common/themes/redWine.ts +++ b/src/common/themes/redWine.ts @@ -37,5 +37,7 @@ export default { materialTextDisabledShadow: 'rgb(192, 64, 56)', materialTextInvert: 'rgb(255, 255, 255)', progress: 'rgb(192, 0, 0)', + scale: 2, + shadow: true, tooltip: 'rgb(64, 0, 0)' } as Theme; diff --git a/src/common/themes/rose.ts b/src/common/themes/rose.ts index d6bb92c6..1d721990 100644 --- a/src/common/themes/rose.ts +++ b/src/common/themes/rose.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#f1d4dc', materialTextInvert: '#ffffff', progress: '#ab5a71', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/seawater.ts b/src/common/themes/seawater.ts index 1e6bf847..94b3d4ee 100644 --- a/src/common/themes/seawater.ts +++ b/src/common/themes/seawater.ts @@ -36,5 +36,7 @@ export default { materialTextDisabledShadow: 'rgb(167, 194, 224)', materialTextInvert: 'rgb(255, 255, 255)', progress: 'rgb(0, 128, 128)', + scale: 2, + shadow: true, tooltip: 'rgb(255, 255, 225)' } as Theme; diff --git a/src/common/themes/shelbiTeal.ts b/src/common/themes/shelbiTeal.ts index ca6a473d..5a447258 100644 --- a/src/common/themes/shelbiTeal.ts +++ b/src/common/themes/shelbiTeal.ts @@ -37,5 +37,7 @@ export default { materialTextDisabledShadow: 'rgb(204, 224, 224)', materialTextInvert: 'rgb(255, 255, 255)', progress: 'rgb(0, 128, 128)', + scale: 2, + shadow: true, tooltip: 'rgb(224, 255, 255)' } as Theme; diff --git a/src/common/themes/slate.ts b/src/common/themes/slate.ts index 0b1246ec..1ec5d30d 100644 --- a/src/common/themes/slate.ts +++ b/src/common/themes/slate.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#c3d9e9', materialTextInvert: '#f2ffff', progress: '#448199', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/solarizedDark.ts b/src/common/themes/solarizedDark.ts index aa3adf4f..76b4baa2 100644 --- a/src/common/themes/solarizedDark.ts +++ b/src/common/themes/solarizedDark.ts @@ -36,5 +36,7 @@ export default { materialTextDisabledShadow: 'rgb(0, 43, 54)', materialTextInvert: 'rgb(238, 232, 213)', progress: 'rgb(211, 54, 130)', + scale: 2, + shadow: true, tooltip: 'rgb(253, 246, 227)' } as Theme; diff --git a/src/common/themes/solarizedLight.ts b/src/common/themes/solarizedLight.ts index 368d877e..4db6fdc1 100644 --- a/src/common/themes/solarizedLight.ts +++ b/src/common/themes/solarizedLight.ts @@ -37,5 +37,7 @@ export default { materialTextDisabledShadow: 'rgb(253, 246, 227)', materialTextInvert: 'rgb(238, 232, 213)', progress: 'rgb(211, 54, 130)', + scale: 2, + shadow: true, tooltip: 'rgb(253, 246, 227)' } as Theme; diff --git a/src/common/themes/spruce.ts b/src/common/themes/spruce.ts index 4cb63366..e1494f6c 100644 --- a/src/common/themes/spruce.ts +++ b/src/common/themes/spruce.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#cdead2', materialTextInvert: '#fcfff6', progress: '#3d9961', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/stormClouds.ts b/src/common/themes/stormClouds.ts index f9fa0929..23f4b002 100644 --- a/src/common/themes/stormClouds.ts +++ b/src/common/themes/stormClouds.ts @@ -37,5 +37,7 @@ export default { materialTextDisabledShadow: 'rgb(159, 171, 172)', materialTextInvert: 'rgb(255, 255, 255)', progress: 'rgb(128, 128, 0)', + scale: 2, + shadow: true, tooltip: 'rgb(48, 64, 80)' } as Theme; diff --git a/src/common/themes/theSixtiesUSA.ts b/src/common/themes/theSixtiesUSA.ts index 079b7e68..a9f4ced7 100644 --- a/src/common/themes/theSixtiesUSA.ts +++ b/src/common/themes/theSixtiesUSA.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#df9be7', materialTextInvert: '#010001', progress: '#0f0', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/tokyoDark.ts b/src/common/themes/tokyoDark.ts index 71444bd4..067b14fc 100644 --- a/src/common/themes/tokyoDark.ts +++ b/src/common/themes/tokyoDark.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#93a0a1', materialTextInvert: '#ffffff', progress: '#F61067', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/toner.ts b/src/common/themes/toner.ts index d39096f5..8a4cf986 100644 --- a/src/common/themes/toner.ts +++ b/src/common/themes/toner.ts @@ -37,5 +37,7 @@ export default { materialTextDisabledShadow: 'rgb(127, 127, 127)', materialTextInvert: 'rgb(0, 0, 0)', progress: 'rgb(255, 255, 255)', + scale: 2, + shadow: true, tooltip: 'rgb(128, 128, 128)' } as Theme; diff --git a/src/common/themes/tooSexy.ts b/src/common/themes/tooSexy.ts index d30bde63..be110eb6 100644 --- a/src/common/themes/tooSexy.ts +++ b/src/common/themes/tooSexy.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#fe5757', materialTextInvert: '#ffffff', progress: '#474973', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/travel.ts b/src/common/themes/travel.ts index 83fe5a43..8c0c6e8a 100644 --- a/src/common/themes/travel.ts +++ b/src/common/themes/travel.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#baae9f', materialTextInvert: '#ffffff', progress: '#48604f', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/types.ts b/src/common/themes/types.ts index 90260edc..013660a9 100644 --- a/src/common/themes/types.ts +++ b/src/common/themes/types.ts @@ -1,7 +1,6 @@ export type Color = string; -export type Theme = { - name: string; +export type ThemeColors = { anchor: Color; anchorVisited: Color; borderDark: Color; @@ -34,6 +33,12 @@ export type Theme = { tooltip: Color; }; +export type Theme = { + name: string; + scale?: number; + shadow?: boolean; +} & ThemeColors; + export type WindowsTheme = { ActiveBorder: Color; ActiveTitle: Color; diff --git a/src/common/themes/vaporTeal.ts b/src/common/themes/vaporTeal.ts index 297cde97..e494e435 100644 --- a/src/common/themes/vaporTeal.ts +++ b/src/common/themes/vaporTeal.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#58ffff', materialTextInvert: '#000000', progress: '#FF99C8', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/vermillion.ts b/src/common/themes/vermillion.ts index acf486c3..c8795d38 100644 --- a/src/common/themes/vermillion.ts +++ b/src/common/themes/vermillion.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#e59697', materialTextInvert: '#EFE9F4', progress: '#000103', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/violetDark.ts b/src/common/themes/violetDark.ts index 42bb148f..c203a05a 100644 --- a/src/common/themes/violetDark.ts +++ b/src/common/themes/violetDark.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#c47bcc', materialTextInvert: '#c47bcc', progress: '#000080', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/vistaesqueMidnight.ts b/src/common/themes/vistaesqueMidnight.ts index 252af0a1..d9db9ada 100644 --- a/src/common/themes/vistaesqueMidnight.ts +++ b/src/common/themes/vistaesqueMidnight.ts @@ -37,5 +37,7 @@ export default { materialTextDisabledShadow: 'rgb(128, 128, 128)', materialTextInvert: 'rgb(255, 255, 255)', progress: 'rgb(49, 106, 197)', + scale: 2, + shadow: true, tooltip: 'rgb(0, 0, 30)' } as Theme; diff --git a/src/common/themes/water.ts b/src/common/themes/water.ts index 3eb1fcfb..e6fcbc06 100644 --- a/src/common/themes/water.ts +++ b/src/common/themes/water.ts @@ -32,5 +32,7 @@ export default { materialTextDisabledShadow: '#ffffff', materialTextInvert: '#ffffff', progress: '#72b3b4', + scale: 2, + shadow: true, tooltip: '#fefbcc' } as Theme; diff --git a/src/common/themes/white.ts b/src/common/themes/white.ts index 5da6405e..cdb43609 100644 --- a/src/common/themes/white.ts +++ b/src/common/themes/white.ts @@ -36,5 +36,7 @@ export default { materialTextDisabledShadow: 'rgb(255, 255, 255)', materialTextInvert: 'rgb(255, 255, 255)', progress: 'rgb(0, 0, 128)', + scale: 2, + shadow: true, tooltip: 'rgb(255, 255, 128)' } as Theme; diff --git a/src/common/themes/windows1.ts b/src/common/themes/windows1.ts index d9a82b74..b34972d5 100644 --- a/src/common/themes/windows1.ts +++ b/src/common/themes/windows1.ts @@ -36,5 +36,7 @@ export default { materialTextDisabledShadow: 'rgb(255, 255, 255)', materialTextInvert: 'rgb(255, 255, 255)', progress: 'rgb(0, 0, 0)', + scale: 2, + shadow: true, tooltip: 'rgb(255, 255, 85)' } as Theme; diff --git a/src/common/themes/wmii.ts b/src/common/themes/wmii.ts index bca9ceb6..238c5876 100644 --- a/src/common/themes/wmii.ts +++ b/src/common/themes/wmii.ts @@ -37,5 +37,7 @@ export default { materialTextDisabledShadow: 'rgb(224, 225, 198)', materialTextInvert: 'rgb(255, 255, 255)', progress: 'rgb(0, 0, 0)', + scale: 2, + shadow: true, tooltip: 'rgb(255, 255, 225)' } as Theme; diff --git a/src/common/utils/__snapshots__/index.spec.ts.snap b/src/common/utils/__snapshots__/index.spec.ts.snap new file mode 100644 index 00000000..5837c6fb --- /dev/null +++ b/src/common/utils/__snapshots__/index.spec.ts.snap @@ -0,0 +1,77 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`mapFromWindowsTheme should map corresponding properties directly 1`] = ` +Object { + "anchor": "#00001c", + "anchorVisited": "#00001c", + "borderDark": "#000005", + "borderDarkest": "#000001", + "borderLight": "#000004", + "borderLightest": "#000003", + "canvas": "#00000c", + "canvasText": "#00000e", + "canvasTextDisabled": "#000005", + "canvasTextDisabledShadow": "#000003", + "canvasTextInvert": "#00001b", + "checkmark": "#00000e", + "checkmarkDisabled": "#000019", + "desktopBackground": "#000009", + "flatDark": "#000005", + "flatLight": "#000004", + "focusSecondary": "#000003", + "headerBackground": "#00000f", + "headerNotActiveBackground": "#000012", + "headerNotActiveText": "#000013", + "headerText": "#000014", + "hoverBackground": "#00001a", + "material": "#000002", + "materialDark": "#000012", + "materialText": "#000006", + "materialTextDisabled": "#000005", + "materialTextDisabledShadow": "#000003", + "materialTextInvert": "#00001b", + "name": "theme", + "progress": "#00001a", + "scale": 2, + "shadow": true, + "tooltip": "#00001e", +} +`; + +exports[`mapFromWindowsTheme should support gradients and shadows and scale 1`] = ` +Object { + "anchor": "#00001c", + "anchorVisited": "#00001c", + "borderDark": "#000005", + "borderDarkest": "#000001", + "borderLight": "#000004", + "borderLightest": "#000003", + "canvas": "#00000c", + "canvasText": "#00000e", + "canvasTextDisabled": "#000005", + "canvasTextDisabledShadow": "#000003", + "canvasTextInvert": "#00001b", + "checkmark": "#00000e", + "checkmarkDisabled": "#000019", + "desktopBackground": "#000009", + "flatDark": "#000005", + "flatLight": "#000004", + "focusSecondary": "#000003", + "headerBackground": "linear-gradient(to right, #00000f, #000010)", + "headerNotActiveBackground": "linear-gradient(to right, #000012, #000011)", + "headerNotActiveText": "#000013", + "headerText": "#000014", + "hoverBackground": "#00001a", + "material": "#000002", + "materialDark": "#000012", + "materialText": "#000006", + "materialTextDisabled": "#000005", + "materialTextDisabledShadow": "#000003", + "materialTextInvert": "#00001b", + "name": "theme", + "progress": "#00001a", + "scale": 1.5, + "shadow": false, + "tooltip": "#00001e", +} +`; diff --git a/src/common/utils/index.spec.ts b/src/common/utils/index.spec.ts index 9aed50ec..c32cc2b9 100644 --- a/src/common/utils/index.spec.ts +++ b/src/common/utils/index.spec.ts @@ -2,7 +2,8 @@ import { clamp, mapFromWindowsTheme, getDecimalPrecision, - roundValueToStep + roundValueToStep, + defaultTrue } from './index'; describe('clamp', () => { @@ -27,147 +28,64 @@ describe('clamp', () => { }); }); +describe(defaultTrue, () => { + it.each([ + [null, true], + [undefined, true], + [true, true], + [false, false] + ])('when %s, returns %s', (value, expected) => { + expect(defaultTrue(value)).toEqual(expected); + }); +}); + describe('mapFromWindowsTheme', () => { - it('should map corresponding properties directly if gradients are disabled', () => { - const theme = { - ButtonAlternateFace: '#000000', - ButtonDkShadow: '#000001', - ButtonFace: '#000002', - ButtonHilight: '#000003', - ButtonLight: '#000004', - ButtonShadow: '#000005', - ButtonText: '#000006', - ActiveBorder: '#000007', - AppWorkspace: '#000008', - Background: '#000009', - InactiveBorder: '#00000a', - Scrollbar: '#00000b', - Window: '#00000c', - WindowFrame: '#00000d', - WindowText: '#00000e', - ActiveTitle: '#00000f', - GradientActiveTitle: '#000010', - GradientInactiveTitle: '#000011', - InactiveTitle: '#000012', - InactiveTitleText: '#000013', - TitleText: '#000014', - Menu: '#000015', - MenuBar: '#000016', - MenuHilight: '#000017', - MenuText: '#000018', - GrayText: '#000019', - Hilight: '#00001a', - HilightText: '#00001b', - HotTrackingColor: '#00001c', - InfoText: '#00001d', - InfoWindow: '#00001e' - }; - const expectedTheme = { - name: 'theme', - anchor: '#00001c', - anchorVisited: '#00001c', - borderDark: '#000005', - borderDarkest: '#000001', - borderLight: '#000004', - borderLightest: '#000003', - canvas: '#00000c', - canvasText: '#00000e', - canvasTextDisabled: '#000005', - canvasTextDisabledShadow: '#000003', - canvasTextInvert: '#00001b', - checkmark: '#00000e', - checkmarkDisabled: '#000019', - desktopBackground: '#000009', - flatDark: '#000005', - flatLight: '#000004', - focusSecondary: '#000003', - headerBackground: '#00000f', - headerNotActiveBackground: '#000012', - headerNotActiveText: '#000013', - headerText: '#000014', - hoverBackground: '#00001a', - material: '#000002', - materialDark: '#000012', - materialText: '#000006', - materialTextDisabled: '#000005', - materialTextDisabledShadow: '#000003', - materialTextInvert: '#00001b', - progress: '#00001a', - tooltip: '#00001e' - }; + const theme = { + ButtonAlternateFace: '#000000', + ButtonDkShadow: '#000001', + ButtonFace: '#000002', + ButtonHilight: '#000003', + ButtonLight: '#000004', + ButtonShadow: '#000005', + ButtonText: '#000006', + ActiveBorder: '#000007', + AppWorkspace: '#000008', + Background: '#000009', + InactiveBorder: '#00000a', + Scrollbar: '#00000b', + Window: '#00000c', + WindowFrame: '#00000d', + WindowText: '#00000e', + ActiveTitle: '#00000f', + GradientActiveTitle: '#000010', + GradientInactiveTitle: '#000011', + InactiveTitle: '#000012', + InactiveTitleText: '#000013', + TitleText: '#000014', + Menu: '#000015', + MenuBar: '#000016', + MenuHilight: '#000017', + MenuText: '#000018', + GrayText: '#000019', + Hilight: '#00001a', + HilightText: '#00001b', + HotTrackingColor: '#00001c', + InfoText: '#00001d', + InfoWindow: '#00001e' + }; - expect(mapFromWindowsTheme('theme', theme, false)).toEqual(expectedTheme); + it('should map corresponding properties directly', () => { + expect(mapFromWindowsTheme('theme', theme)).toMatchSnapshot(); }); - it('should map corresponding properties with gradients if gradients are enabled', () => { - const theme = { - ButtonAlternateFace: '#000000', - ButtonDkShadow: '#000001', - ButtonFace: '#000002', - ButtonHilight: '#000003', - ButtonLight: '#000004', - ButtonShadow: '#000005', - ButtonText: '#000006', - ActiveBorder: '#000007', - AppWorkspace: '#000008', - Background: '#000009', - InactiveBorder: '#00000a', - Scrollbar: '#00000b', - Window: '#00000c', - WindowFrame: '#00000d', - WindowText: '#00000e', - ActiveTitle: '#00000f', - GradientActiveTitle: '#000010', - GradientInactiveTitle: '#000011', - InactiveTitle: '#000012', - InactiveTitleText: '#000013', - TitleText: '#000014', - Menu: '#000015', - MenuBar: '#000016', - MenuHilight: '#000017', - MenuText: '#000018', - GrayText: '#000019', - Hilight: '#00001a', - HilightText: '#00001b', - HotTrackingColor: '#00001c', - InfoText: '#00001d', - InfoWindow: '#00001e' - }; - const expectedTheme = { - name: 'theme', - anchor: '#00001c', - anchorVisited: '#00001c', - borderDark: '#000005', - borderDarkest: '#000001', - borderLight: '#000004', - borderLightest: '#000003', - canvas: '#00000c', - canvasText: '#00000e', - canvasTextDisabled: '#000005', - canvasTextDisabledShadow: '#000003', - canvasTextInvert: '#00001b', - checkmark: '#00000e', - checkmarkDisabled: '#000019', - desktopBackground: '#000009', - flatDark: '#000005', - flatLight: '#000004', - focusSecondary: '#000003', - headerBackground: 'linear-gradient(to right, #00000f, #000010)', - headerNotActiveBackground: 'linear-gradient(to right, #000012, #000011)', - headerNotActiveText: '#000013', - headerText: '#000014', - hoverBackground: '#00001a', - material: '#000002', - materialDark: '#000012', - materialText: '#000006', - materialTextDisabled: '#000005', - materialTextDisabledShadow: '#000003', - materialTextInvert: '#00001b', - progress: '#00001a', - tooltip: '#00001e' - }; - - expect(mapFromWindowsTheme('theme', theme, true)).toEqual(expectedTheme); + it('should support gradients and shadows and scale', () => { + expect( + mapFromWindowsTheme('theme', theme, { + useGradients: true, + useShadow: false, + scale: 1.5 + }) + ).toMatchSnapshot(); }); }); diff --git a/src/common/utils/index.ts b/src/common/utils/index.ts index 2a3dd702..001470b0 100644 --- a/src/common/utils/index.ts +++ b/src/common/utils/index.ts @@ -1,4 +1,5 @@ import { WindowsTheme } from '../../types'; +import { SCALE } from '../constants'; export const noOp = () => {}; @@ -12,6 +13,10 @@ export function clamp(value: number, min: number | null, max: number | null) { return value; } +export function defaultTrue(value: boolean | null | undefined) { + return value === undefined || value === null ? true : value; +} + function linearGradient(left: string, right: string) { return `linear-gradient(to right, ${left}, ${right})`; } @@ -19,7 +24,11 @@ function linearGradient(left: string, right: string) { export function mapFromWindowsTheme( name: string, windowsTheme: WindowsTheme, - useGradients: boolean + { + useGradients = false, + useShadow = true, + scale = SCALE + }: { useGradients?: boolean; useShadow?: boolean; scale?: number } = {} ) { const { ButtonDkShadow, @@ -80,6 +89,8 @@ export function mapFromWindowsTheme( materialTextDisabledShadow: ButtonHilight, materialTextInvert: HilightText, progress: Hilight, + scale, + shadow: useShadow, tooltip: InfoWindow }; } diff --git a/src/types.ts b/src/types.ts index 9e75b220..80e00a17 100644 --- a/src/types.ts +++ b/src/types.ts @@ -24,6 +24,7 @@ export type HTMLDataAttributes = Record<`data-${string}`, any>; export type CommonThemeProps = { 'data-testid'?: string; $disabled?: boolean; + defaultShadow?: boolean; shadow?: boolean; };