From 7aa75f1d9e39b4e823100b3352e1ee24e0655b74 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 14 Nov 2023 11:21:46 +0200 Subject: [PATCH] refact --- .prettierignore | 2 + src/components/AppBtn/index.tsx | 88 ------------------- src/components/AppRadioBtn/index.stories.tsx | 37 -------- src/components/Breadcrumbs/index.stories.tsx | 1 + src/components/Breadcrumbs/index.tsx | 29 ++---- src/components/Breadcrumbs/styles.tsx | 15 ++++ src/components/Breadcrumbs/types.ts | 4 + .../{AppBtn => Button}/index.stories.tsx | 72 +++++++-------- src/components/Button/index.tsx | 38 ++++++++ src/components/Button/styles.tsx | 52 +++++++++++ src/components/{AppBtn => Button}/theme.ts | 4 +- src/components/{AppBtn => Button}/types.ts | 6 +- .../index.stories.tsx | 16 ++-- src/components/Checkbox/index.tsx | 15 ++++ .../index.tsx => Checkbox/styles.tsx} | 28 ++---- src/components/Checkbox/types.ts | 7 ++ src/components/Dropdown/index.tsx | 85 +++--------------- src/components/Dropdown/styles.tsx | 62 +++++++++++++ src/components/Dropdown/types.ts | 6 ++ src/components/RadioButton/index.stories.tsx | 37 ++++++++ src/components/RadioButton/index.tsx | 24 +++++ .../index.tsx => RadioButton/styles.tsx} | 42 ++------- src/components/RadioButton/types.ts | 11 +++ src/components/Tabs/index.stories.tsx | 7 +- src/components/Tabs/index.tsx | 50 +---------- src/components/Tabs/styles.ts | 39 ++++++++ src/components/Tabs/types.ts | 6 ++ src/components/Toggle/index.stories.tsx | 2 +- src/components/Toggle/index.tsx | 58 ++---------- src/components/Toggle/styles.tsx | 43 +++++++++ src/components/Toggle/types.ts | 7 ++ src/components/Tooltip/index.tsx | 58 +----------- src/components/Tooltip/styles.tsx | 52 +++++++++++ src/components/Tooltip/types.ts | 6 ++ src/components/index.ts | 6 +- src/hooks/index.ts | 1 + .../Dropdown => hooks}/useOnClickOutside.ts | 0 src/index.ts | 5 +- src/theme/colors.ts | 4 +- 39 files changed, 529 insertions(+), 496 deletions(-) create mode 100644 .prettierignore delete mode 100644 src/components/AppBtn/index.tsx delete mode 100644 src/components/AppRadioBtn/index.stories.tsx create mode 100644 src/components/Breadcrumbs/styles.tsx create mode 100644 src/components/Breadcrumbs/types.ts rename src/components/{AppBtn => Button}/index.stories.tsx (62%) create mode 100644 src/components/Button/index.tsx create mode 100644 src/components/Button/styles.tsx rename src/components/{AppBtn => Button}/theme.ts (93%) rename src/components/{AppBtn => Button}/types.ts (81%) rename src/components/{AppCheckbox => Checkbox}/index.stories.tsx (60%) create mode 100644 src/components/Checkbox/index.tsx rename src/components/{AppCheckbox/index.tsx => Checkbox/styles.tsx} (67%) create mode 100644 src/components/Checkbox/types.ts create mode 100644 src/components/Dropdown/styles.tsx create mode 100644 src/components/Dropdown/types.ts create mode 100644 src/components/RadioButton/index.stories.tsx create mode 100644 src/components/RadioButton/index.tsx rename src/components/{AppRadioBtn/index.tsx => RadioButton/styles.tsx} (58%) create mode 100644 src/components/RadioButton/types.ts create mode 100644 src/components/Tabs/styles.ts create mode 100644 src/components/Tabs/types.ts create mode 100644 src/components/Toggle/styles.tsx create mode 100644 src/components/Toggle/types.ts create mode 100644 src/components/Tooltip/styles.tsx create mode 100644 src/components/Tooltip/types.ts create mode 100644 src/hooks/index.ts rename src/{components/Dropdown => hooks}/useOnClickOutside.ts (100%) diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..76add87 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +node_modules +dist \ No newline at end of file diff --git a/src/components/AppBtn/index.tsx b/src/components/AppBtn/index.tsx deleted file mode 100644 index ba17679..0000000 --- a/src/components/AppBtn/index.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import styled from "styled-components"; -import { layout, space, variant } from "styled-system"; -import { variantVariants, scaleVariants } from "./theme"; -import { AppBtnProps } from "./types"; -import LoadingSpinner from "../Loaders/LoadingSpinner"; - -const AppBtnWrap = styled.button` - ${layout} - ${space} - - ${({ theme, color }) => - variant({ - prop: "variant", - variants: variantVariants(theme, color), - })} - ${variant({ - prop: "scale", - variants: scaleVariants, - })} - - position: relative; - display: flex; - justify-content: center; - align-items: center; - box-sizing: border-box; - border: none; - cursor: pointer; - text-decoration: none; - - &:disabled { - cursor: default; - opacity: 0.5; - } -`; - -const AppBtnContainer = styled.span<{ isLoading?: boolean }>` - display: flex; - align-items: center; - justify-content: center; - opacity: ${({ isLoading }) => (isLoading ? 0.3 : 1)}; - transition: opacity 0.15s; -`; -const AppBtnLoader = styled.span<{ isLoading?: boolean }>` - position: absolute; - display: flex; - opacity: ${({ isLoading }) => (isLoading ? 1 : 0)}; - pointer-events: none; - transition: opacity 0.15s; -`; - -const AppBtnContent = styled.span` - margin: 0 8px; -`; - -AppBtnWrap.defaultProps = { - variant: "default", - scale: "default", - color: "primary", - as: "button", - width: "100%", -}; - -export default function AppBtn({ - loading, - onClick, - ...props -}: AppBtnProps & { loading?: boolean; onClick?: () => void }) { - return ( - { - if (loading || props.disabled) e.preventDefault(); - else if (onClick) onClick(); - }} - > - - {props.startIcon} - {props.children} - {props.endIcon} - - - - - - ); -} diff --git a/src/components/AppRadioBtn/index.stories.tsx b/src/components/AppRadioBtn/index.stories.tsx deleted file mode 100644 index f70cc08..0000000 --- a/src/components/AppRadioBtn/index.stories.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { useState } from "react"; -import AppRadioBtnComponent from "./index"; -import { Box, Grid } from "../Box"; -import { QuestionIcon } from "../Svg"; -import Tooltip from "../Tooltip"; - -export default { - title: "Components/AppRadioBtn", - component: AppRadioBtnComponent, - argTypes: {}, -}; - -export const AppRadioBtn = () => { - const [gender, setGender] = useState("male"); - - return ( - - - - Female - - - Male - - - Non Binary{" "} - - - - - - Other (disabled) - - - - ); -}; diff --git a/src/components/Breadcrumbs/index.stories.tsx b/src/components/Breadcrumbs/index.stories.tsx index 1e87532..ff418f1 100644 --- a/src/components/Breadcrumbs/index.stories.tsx +++ b/src/components/Breadcrumbs/index.stories.tsx @@ -1,5 +1,6 @@ import BreadcrumbsComponent from "./index"; import { BrowserRouter } from "react-router-dom"; +import React from "react"; export default { title: "Components/Breadcrumbs", diff --git a/src/components/Breadcrumbs/index.tsx b/src/components/Breadcrumbs/index.tsx index afe6baf..d0bdeaa 100644 --- a/src/components/Breadcrumbs/index.tsx +++ b/src/components/Breadcrumbs/index.tsx @@ -1,36 +1,19 @@ import { createContext, ElementType, useContext } from "react"; -import { Flex } from "../Box"; -import styled from "styled-components"; -import { rgba } from "polished"; import { ArrowLeftIcon } from "../Svg"; import Text from "../Text"; -export const MenuContext = createContext<{ linkComponent: ElementType }>({ linkComponent: "a" }); - -interface IProps { - label: string; - to: string; -} +import { BreadcrumbsWrapper } from "./styles"; +import { IBreadcrumbsProps } from './types'; -const BreadcrumbsWrap = styled(Flex)` - display: inline-flex; - justify-content: center; - align-items: center; - padding: 2px 16px; - border-radius: 12px; - min-width: 89px; - text-decoration: none; - border: 0.5px solid ${({ theme }) => rgba(theme.colors.strokeGray, 0.2)}; - background: ${({ theme }) => rgba(theme.colors.strokeGray, 0.08)}; -`; +export const MenuContext = createContext<{ linkComponent: ElementType }>({ linkComponent: "a" }); -export default function Breadcrumbs({ label, to }: IProps) { +export default function ({ label, to }: IBreadcrumbsProps) { const { linkComponent } = useContext(MenuContext); return ( - + {label} - + ); } diff --git a/src/components/Breadcrumbs/styles.tsx b/src/components/Breadcrumbs/styles.tsx new file mode 100644 index 0000000..cbf384d --- /dev/null +++ b/src/components/Breadcrumbs/styles.tsx @@ -0,0 +1,15 @@ +import styled from "styled-components"; +import { Flex } from "../Box"; +import { rgba } from "polished"; + +export const BreadcrumbsWrapper = styled(Flex)` + display: inline-flex; + justify-content: center; + align-items: center; + padding: 2px 16px; + border-radius: 12px; + min-width: 89px; + text-decoration: none; + border: 0.5px solid ${({ theme }) => rgba(theme.colors.strokeGray, 0.2)}; + background: ${({ theme }) => rgba(theme.colors.strokeGray, 0.08)}; +`; \ No newline at end of file diff --git a/src/components/Breadcrumbs/types.ts b/src/components/Breadcrumbs/types.ts new file mode 100644 index 0000000..d62ea2c --- /dev/null +++ b/src/components/Breadcrumbs/types.ts @@ -0,0 +1,4 @@ +export interface IBreadcrumbsProps { + label: string; + to: string; +} \ No newline at end of file diff --git a/src/components/AppBtn/index.stories.tsx b/src/components/Button/index.stories.tsx similarity index 62% rename from src/components/AppBtn/index.stories.tsx rename to src/components/Button/index.stories.tsx index 2ac50e7..a3a1da4 100644 --- a/src/components/AppBtn/index.stories.tsx +++ b/src/components/Button/index.stories.tsx @@ -1,5 +1,5 @@ -import { useEffect, useState } from "react"; -import AppBtnComponent from "./index"; +import React, { useEffect, useState } from "react"; +import Button from "./index"; import { BrowserRouter } from "react-router-dom"; import { Box, Grid } from "../Box"; import Text from "../Text"; @@ -8,7 +8,7 @@ import Tooltip from "../Tooltip"; export default { title: "Components/Buttons", - component: AppBtnComponent, + component: Button, argTypes: {}, }; @@ -33,26 +33,26 @@ export const Buttons = () => { Outlined Text - Button + - Button + - Button + - Button + - + - + @@ -63,32 +63,32 @@ export const Buttons = () => { Outlined Text - Button + - + - + - + - + - + @@ -96,36 +96,36 @@ export const Buttons = () => { - Button + - + - + Link Button - + With Start/End components - }>Button - } variant={"outlined"}> + + With Loader - { setLoading(true); @@ -170,7 +170,7 @@ export const Buttons = () => { variant={"outlined"} > Click here - + ); }; diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx new file mode 100644 index 0000000..e5a0d72 --- /dev/null +++ b/src/components/Button/index.tsx @@ -0,0 +1,38 @@ +import { ButtonProps } from "./types"; +import LoadingSpinner from "../Loaders/LoadingSpinner"; +import { ButtonContainer, ButtonContent, ButtonLoader, ButtonWrapper } from './styles'; + +ButtonWrapper.defaultProps = { + variant: "default", + scale: "default", + color: "primary", + as: "button", + width: "100%", +}; + +export default function ({ + loading, + onClick, + ...props +}: ButtonProps & { loading?: boolean; onClick?: () => void }) { + return ( + { + if (loading || props.disabled) e.preventDefault(); + else if (onClick) onClick(); + }} + > + + {props.startIcon} + {props.children} + {props.endIcon} + + + + + + ); +} diff --git a/src/components/Button/styles.tsx b/src/components/Button/styles.tsx new file mode 100644 index 0000000..8d5cfb0 --- /dev/null +++ b/src/components/Button/styles.tsx @@ -0,0 +1,52 @@ +import styled from "styled-components"; +import { ButtonProps } from './types'; +import { layout, space, variant } from "styled-system"; +import { variantVariants, scaleVariants } from "./theme"; + +export const ButtonWrapper = styled.button` + ${layout} + ${space} + + ${({ theme, color }) => + variant({ + prop: "variant", + variants: variantVariants(theme, color), + })} + ${variant({ + prop: "scale", + variants: scaleVariants, + })} + + position: relative; + display: flex; + justify-content: center; + align-items: center; + box-sizing: border-box; + border: none; + cursor: pointer; + text-decoration: none; + + &:disabled { + cursor: default; + opacity: 0.5; + } +`; + +export const ButtonContainer = styled.span<{ isLoading?: boolean }>` + display: flex; + align-items: center; + justify-content: center; + opacity: ${({ isLoading }) => (isLoading ? 0.3 : 1)}; + transition: opacity 0.15s; +`; +export const ButtonLoader = styled.span<{ isLoading?: boolean }>` + position: absolute; + display: flex; + opacity: ${({ isLoading }) => (isLoading ? 1 : 0)}; + pointer-events: none; + transition: opacity 0.15s; +`; + +export const ButtonContent = styled.span` + margin: 0 8px; +`; \ No newline at end of file diff --git a/src/components/AppBtn/theme.ts b/src/components/Button/theme.ts similarity index 93% rename from src/components/AppBtn/theme.ts rename to src/components/Button/theme.ts index 467b4ba..2203a74 100644 --- a/src/components/AppBtn/theme.ts +++ b/src/components/Button/theme.ts @@ -1,7 +1,7 @@ -import { AppBtnColor, scales, variants } from "./types"; +import { ButtonColor, scales, variants } from "./types"; import { DefaultTheme } from "styled-components"; import { rgba } from "polished"; -export const variantVariants = (theme: DefaultTheme, color: AppBtnColor = "primary") => { +export const variantVariants = (theme: DefaultTheme, color: ButtonColor = "primary") => { const colors = theme.buttons[color]; const hoverGrad0 = colors.hoverGrad0 || colors.grad0; const hoverGrad1 = colors.hoverGrad1 || colors.grad1; diff --git a/src/components/AppBtn/types.ts b/src/components/Button/types.ts similarity index 81% rename from src/components/AppBtn/types.ts rename to src/components/Button/types.ts index 6f434a5..1875444 100644 --- a/src/components/AppBtn/types.ts +++ b/src/components/Button/types.ts @@ -17,12 +17,12 @@ export const scales = { SMALL: "small", } as const; -export type AppBtnColor = (typeof colors)[keyof typeof colors]; -export interface AppBtnProps extends LayoutProps, SpaceProps { +export type ButtonColor = (typeof colors)[keyof typeof colors]; +export interface ButtonProps extends LayoutProps, SpaceProps { as?: "a" | "button" | ElementType; variant?: (typeof variants)[keyof typeof variants]; scale?: (typeof scales)[keyof typeof scales]; - color?: AppBtnColor; + color?: ButtonColor; disabled?: boolean; to?: string; target?: HTMLAttributeAnchorTarget; diff --git a/src/components/AppCheckbox/index.stories.tsx b/src/components/Checkbox/index.stories.tsx similarity index 60% rename from src/components/AppCheckbox/index.stories.tsx rename to src/components/Checkbox/index.stories.tsx index e01840f..452bc6f 100644 --- a/src/components/AppCheckbox/index.stories.tsx +++ b/src/components/Checkbox/index.stories.tsx @@ -1,11 +1,11 @@ -import { useState } from "react"; -import AppCheckboxComponent from "./index"; +import React, { useState } from "react"; +import Checkbox from "./index"; import { Box } from "../Box"; import Text from "../Text"; export default { - title: "Components/AppCheckbox", - component: AppCheckboxComponent, + title: "Components/Checkbox", + component: Checkbox, argTypes: {}, }; @@ -18,18 +18,18 @@ export const AppCheckbox = () => { Default - + Disabled - - + + With Label ); diff --git a/src/components/Checkbox/index.tsx b/src/components/Checkbox/index.tsx new file mode 100644 index 0000000..9faa063 --- /dev/null +++ b/src/components/Checkbox/index.tsx @@ -0,0 +1,15 @@ +import { ICheckboxProps } from './types'; +import { CheckboxWrapper } from './styles'; + +export default function ({ value, onChange, ...props }: ICheckboxProps) { + return ( + { + if (onChange) onChange(!value); + }} + type="button" + active={!!value} + > + ); +} diff --git a/src/components/AppCheckbox/index.tsx b/src/components/Checkbox/styles.tsx similarity index 67% rename from src/components/AppCheckbox/index.tsx rename to src/components/Checkbox/styles.tsx index 354e939..b0a7fd9 100644 --- a/src/components/AppCheckbox/index.tsx +++ b/src/components/Checkbox/styles.tsx @@ -1,14 +1,9 @@ import styled from "styled-components"; -import { layout, LayoutProps, space, SpaceProps } from "styled-system"; +import { ICheckboxProps } from './types'; +import { layout, space } from "styled-system"; -interface IProps extends LayoutProps, SpaceProps { - value?: boolean; - onChange?: (value: boolean) => void; - disabled?: boolean; -} - -const AppCheckboxWrap = styled.button< - IProps & { +export const CheckboxWrapper = styled.button< + ICheckboxProps & { active?: boolean; } >` @@ -57,17 +52,4 @@ const AppCheckboxWrap = styled.button< } transition: outline-color 0.2s; -`; - -export default function AppCheckbox({ value, onChange, ...props }: IProps) { - return ( - { - if (onChange) onChange(!value); - }} - type="button" - active={!!value} - > - ); -} +`; \ No newline at end of file diff --git a/src/components/Checkbox/types.ts b/src/components/Checkbox/types.ts new file mode 100644 index 0000000..5e014fb --- /dev/null +++ b/src/components/Checkbox/types.ts @@ -0,0 +1,7 @@ +import { LayoutProps, SpaceProps } from "styled-system"; + +export interface ICheckboxProps extends LayoutProps, SpaceProps { + value?: boolean; + onChange?: (value: boolean) => void; + disabled?: boolean; +} \ No newline at end of file diff --git a/src/components/Dropdown/index.tsx b/src/components/Dropdown/index.tsx index e55471e..4556b44 100644 --- a/src/components/Dropdown/index.tsx +++ b/src/components/Dropdown/index.tsx @@ -1,76 +1,11 @@ import { Box, Grid } from "../Box"; -import styled from "styled-components"; import { useEffect, useMemo, useRef, useState } from "react"; -import { rgba } from "polished"; -import { useOnClickOutside } from "./useOnClickOutside"; +import { useOnClickOutside } from "../../hooks"; import { ArrowDownIcon } from "../Svg"; +import { DropdownWrapper, DropdownActivatorWrapper, DropdownItemWrapper } from './styles'; +import { IDropdownProps } from './types'; -type DropdownItemValue = string | number; -interface IProps { - items: { title?: string; value?: DropdownItemValue }[]; - value?: DropdownItemValue; - onChange?: (value: any) => void; -} - -const DropdownWrap = styled(Grid)<{ maxHeight: number; opened?: boolean }>` - width: max-content; - max-height: ${({ maxHeight }) => maxHeight}px; - padding: 0 0 4px; - overflow: hidden; - box-sizing: border-box; - background: ${({ theme, opened }) => - !opened ? rgba(theme.colors.primaryDefault, 0.08) : rgba(theme.colors.strokeGray, 0.16)}; - outline: 0.5px solid ${({ theme }) => rgba(theme.colors.strokeGray, 0.2)}; - outline-offset: -0.5px; - border-radius: 12px; - backdrop-filter: blur(10px); - - & svg { - transform: rotate(${({ opened }) => (opened ? "180deg" : "0")}); - stroke: ${({ theme, opened }) => (!opened ? theme.colors.textGray : theme.colors.white)}; - - transition: all 0.15s; - } - - &:hover { - background: ${({ theme }) => rgba(theme.colors.strokeGray, 0.16)}; - - & svg { - stroke: ${({ theme }) => theme.colors.white}; - } - } - - transition: max-height 0.15s, background 0.2s; -`; - -const DropdownItemWrap = styled.button<{ active?: boolean }>` - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 40px 0 20px; - height: 26px; - border: none; - outline: none; - font-size: 14px; - font-weight: 400; - line-height: 18px; - cursor: pointer; - color: ${({ theme, active }) => (active ? theme.colors.primaryDefault : theme.colors.textGray)}; - background: transparent; - - &:hover, - &:focus { - color: ${({ theme }) => theme.colors.primaryDefault}; - } - - transition: color 0.15s; -`; - -const DropdownActivatorWrap = styled(DropdownItemWrap)` - height: 34px; - padding: 0 20px; -`; -export default function Dropdown({ items, value, onChange }: IProps) { +export default function Dropdown({ items, value, onChange }: IDropdownProps) { const activeItem = useMemo(() => items.find((v) => v.value === value), [items, value]); const minHeight = 34; const [maxHeight, setMaxHeight] = useState(minHeight); @@ -93,7 +28,7 @@ export default function Dropdown({ items, value, onChange }: IProps) { return ( - { @@ -113,7 +48,7 @@ export default function Dropdown({ items, value, onChange }: IProps) { }} opened={opened} > - { setTimeout(() => { setOpened(!opened); @@ -124,11 +59,11 @@ export default function Dropdown({ items, value, onChange }: IProps) { > {activeItem?.title || activeItem?.value || "-"} - + {items.map((item) => ( - { if (onChange) onChange(item.value); @@ -137,10 +72,10 @@ export default function Dropdown({ items, value, onChange }: IProps) { active={item.value === value} > {item.title || item.value} - + ))} - + ); } diff --git a/src/components/Dropdown/styles.tsx b/src/components/Dropdown/styles.tsx new file mode 100644 index 0000000..07972d3 --- /dev/null +++ b/src/components/Dropdown/styles.tsx @@ -0,0 +1,62 @@ +import styled from "styled-components"; +import { Grid } from "../Box"; +import { rgba } from "polished"; + +export const DropdownWrapper = styled(Grid)<{ maxHeight: number; opened?: boolean }>` + width: max-content; + max-height: ${({ maxHeight }) => maxHeight}px; + padding: 0 0 4px; + overflow: hidden; + box-sizing: border-box; + background: ${({ theme, opened }) => + !opened ? rgba(theme.colors.primaryDefault, 0.08) : rgba(theme.colors.strokeGray, 0.16)}; + outline: 0.5px solid ${({ theme }) => rgba(theme.colors.strokeGray, 0.2)}; + outline-offset: -0.5px; + border-radius: 12px; + backdrop-filter: blur(10px); + + & svg { + transform: rotate(${({ opened }) => (opened ? "180deg" : "0")}); + stroke: ${({ theme, opened }) => (!opened ? theme.colors.textGray : theme.colors.white)}; + + transition: all 0.15s; + } + + &:hover { + background: ${({ theme }) => rgba(theme.colors.strokeGray, 0.16)}; + + & svg { + stroke: ${({ theme }) => theme.colors.white}; + } + } + + transition: max-height 0.15s, background 0.2s; +`; + +export const DropdownItemWrapper = styled.button<{ active?: boolean }>` + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 40px 0 20px; + height: 26px; + border: none; + outline: none; + font-size: 14px; + font-weight: 400; + line-height: 18px; + cursor: pointer; + color: ${({ theme, active }) => (active ? theme.colors.primaryDefault : theme.colors.textGray)}; + background: transparent; + + &:hover, + &:focus { + color: ${({ theme }) => theme.colors.primaryDefault}; + } + + transition: color 0.15s; +`; + +export const DropdownActivatorWrapper = styled(DropdownItemWrapper)` + height: 34px; + padding: 0 20px; +`; \ No newline at end of file diff --git a/src/components/Dropdown/types.ts b/src/components/Dropdown/types.ts new file mode 100644 index 0000000..8b3047c --- /dev/null +++ b/src/components/Dropdown/types.ts @@ -0,0 +1,6 @@ +export type DropdownItemValue = string | number; +export interface IDropdownProps { + items: { title?: string; value?: DropdownItemValue }[]; + value?: DropdownItemValue; + onChange?: (value?: DropdownItemValue) => void; +} \ No newline at end of file diff --git a/src/components/RadioButton/index.stories.tsx b/src/components/RadioButton/index.stories.tsx new file mode 100644 index 0000000..e4db8fa --- /dev/null +++ b/src/components/RadioButton/index.stories.tsx @@ -0,0 +1,37 @@ +import React, { useState } from "react"; +import RadioButton from "./index"; +import { Box, Grid } from "../Box"; +import { QuestionIcon } from "../Svg"; +import Tooltip from "../Tooltip"; + +export default { + title: "Components/RadioButton", + component: RadioButton, + argTypes: {}, +}; + +export const RadioButtons = () => { + const [gender, setGender] = useState("male"); + + return ( + + + + Female + + + Male + + + Non Binary{" "} + + + + + + Other (disabled) + + + + ); +}; diff --git a/src/components/RadioButton/index.tsx b/src/components/RadioButton/index.tsx new file mode 100644 index 0000000..0e9ab69 --- /dev/null +++ b/src/components/RadioButton/index.tsx @@ -0,0 +1,24 @@ +import { useMemo } from "react"; +import { RadioButtonLabel, RadioButtonWrapper } from './styles'; +import { IRadioButtonProps } from './types' + +export default function ({ value, name, onChange, children, ...props }: IRadioButtonProps) { + const isActive = useMemo(() => value === name, [value, name]); + + return ( + + { + if (onChange && name) { + onChange(name); + } + }} + type="button" + active={isActive} + mr={"8px"} + > + {children || name} + + ); +} diff --git a/src/components/AppRadioBtn/index.tsx b/src/components/RadioButton/styles.tsx similarity index 58% rename from src/components/AppRadioBtn/index.tsx rename to src/components/RadioButton/styles.tsx index 357af24..b6fe939 100644 --- a/src/components/AppRadioBtn/index.tsx +++ b/src/components/RadioButton/styles.tsx @@ -1,17 +1,8 @@ import styled from "styled-components"; -import { layout, LayoutProps, space, SpaceProps } from "styled-system"; -import { ReactNode, useMemo } from "react"; +import { IRadioButtonProps } from './types'; +import { layout, space } from "styled-system"; -type AppRadioBtnName = string | number; -interface IProps extends LayoutProps, SpaceProps { - value?: AppRadioBtnName; - name?: AppRadioBtnName; - onChange?: (value: any) => void; - disabled?: boolean; - children?: ReactNode; -} - -const AppRadioBtnLabel = styled.label<{ disabled?: boolean }>` +export const RadioButtonLabel = styled.label<{ disabled?: boolean }>` display: flex; align-items: center; cursor: ${({ disabled }) => (disabled ? "default" : "pointer")}; @@ -22,8 +13,8 @@ const AppRadioBtnLabel = styled.label<{ disabled?: boolean }>` line-height: 18px; `; -const AppRadioBtnWrap = styled.button< - IProps & { +export const RadioButtonWrapper = styled.button< +IRadioButtonProps & { active?: boolean; } >` @@ -72,25 +63,4 @@ const AppRadioBtnWrap = styled.button< } transition: outline-color 0.2s; -`; - -export default function AppRadioBtn({ value, name, onChange, children, ...props }: IProps) { - const isActive = useMemo(() => value === name, [value, name]); - - return ( - - { - if (onChange && name) { - onChange(name); - } - }} - type="button" - active={isActive} - mr={"8px"} - > - {children || name} - - ); -} +`; \ No newline at end of file diff --git a/src/components/RadioButton/types.ts b/src/components/RadioButton/types.ts new file mode 100644 index 0000000..717e34b --- /dev/null +++ b/src/components/RadioButton/types.ts @@ -0,0 +1,11 @@ +import { ReactNode } from "react"; +import { LayoutProps, SpaceProps } from "styled-system"; + +export type RadioButtonValue = string | number; +export interface IRadioButtonProps extends LayoutProps, SpaceProps { + value?: RadioButtonValue; + name?: RadioButtonValue; + onChange?: (value: any) => void; + disabled?: boolean; + children?: ReactNode; +} \ No newline at end of file diff --git a/src/components/Tabs/index.stories.tsx b/src/components/Tabs/index.stories.tsx index 13236dc..39ae276 100644 --- a/src/components/Tabs/index.stories.tsx +++ b/src/components/Tabs/index.stories.tsx @@ -1,6 +1,7 @@ import TabsComponent from "./index"; -import { useState } from "react"; +import React, { useState } from "react"; import { Box } from "../Box"; +import { TabValue } from "./types"; export default { title: "Components/Tabs", @@ -9,7 +10,7 @@ export default { }; export const Tabs = () => { - const [tab, setTab] = useState(1); + const [tab, setTab] = useState(1); return ( { { value: 3, title: "Last" }, ]} value={tab} - onChange={setTab} + onChange={(value) => setTab(() => value!)} /> ); diff --git a/src/components/Tabs/index.tsx b/src/components/Tabs/index.tsx index c37c746..d4184ac 100644 --- a/src/components/Tabs/index.tsx +++ b/src/components/Tabs/index.tsx @@ -1,51 +1,7 @@ -import { Flex } from "../Box"; -import styled from "styled-components"; -import { rgba } from "polished"; +import { TabsWrap, TabWrap } from './styles'; +import { ITabsProps } from './types'; -type TabValue = string | number; -interface IProps { - tabs: { title?: string; value?: TabValue }[]; - value?: TabValue; - onChange?: (value: any) => void; -} - -const TabsWrap = styled(Flex)` - display: inline-flex; - box-shadow: 0 2px 16px -4px ${({ theme }) => rgba(theme.colors.shadowDark, 0.04)}; - outline: 1px solid ${({ theme }) => rgba(theme.colors.strokeGray, 0.2)}; - outline-offset: -1px; - background: ${({ theme }) => rgba(theme.colors.strokeGray, 0.08)}; - border-radius: 32px; - padding: 5px 10px; -`; - -const TabWrap = styled.button<{ active?: boolean }>` - padding: 10px; - border: none; - border-radius: 32px; - font-size: 16px; - font-weight: 400; - line-height: 11px; - min-width: 62px; - cursor: pointer; - outline: none; - - background: ${({ theme, active }) => (active ? rgba(theme.colors.textGray, 0.08) : "transparent")}; - color: ${({ theme, active }) => (active ? theme.colors.primaryDefault : theme.colors.darkGray)}; - - &:not(:last-child) { - margin-right: 16px; - } - - &:hover, - &:focus { - background: ${({ theme }) => rgba(theme.colors.textGray, 0.08)}; - } - - transition: background 0.2s; -`; - -export default function Tabs({ tabs, value, onChange }: IProps) { +export default function Tabs({ tabs, value, onChange }: ITabsProps) { return ( {tabs.map((tab, i) => ( diff --git a/src/components/Tabs/styles.ts b/src/components/Tabs/styles.ts new file mode 100644 index 0000000..54c148f --- /dev/null +++ b/src/components/Tabs/styles.ts @@ -0,0 +1,39 @@ +import styled from "styled-components"; +import { Flex } from "../Box"; +import { rgba } from "polished"; + +export const TabsWrap = styled(Flex)` + display: inline-flex; + box-shadow: 0 2px 16px -4px ${({ theme }) => rgba(theme.colors.shadowDark, 0.04)}; + outline: 1px solid ${({ theme }) => rgba(theme.colors.strokeGray, 0.2)}; + outline-offset: -1px; + background: ${({ theme }) => rgba(theme.colors.strokeGray, 0.08)}; + border-radius: 32px; + padding: 5px 10px; +`; + +export const TabWrap = styled.button<{ active?: boolean }>` + padding: 10px; + border: none; + border-radius: 32px; + font-size: 16px; + font-weight: 400; + line-height: 11px; + min-width: 62px; + cursor: pointer; + outline: none; + + background: ${({ theme, active }) => (active ? rgba(theme.colors.textGray, 0.08) : "transparent")}; + color: ${({ theme, active }) => (active ? theme.colors.primaryDefault : theme.colors.darkGray)}; + + &:not(:last-child) { + margin-right: 16px; + } + + &:hover, + &:focus { + background: ${({ theme }) => rgba(theme.colors.textGray, 0.08)}; + } + + transition: background 0.2s; +`; \ No newline at end of file diff --git a/src/components/Tabs/types.ts b/src/components/Tabs/types.ts new file mode 100644 index 0000000..67b5a5b --- /dev/null +++ b/src/components/Tabs/types.ts @@ -0,0 +1,6 @@ +export type TabValue = string | number; +export interface ITabsProps { + tabs: { title?: string; value?: TabValue }[]; + value?: TabValue; + onChange?: (value?: TabValue) => void; +} \ No newline at end of file diff --git a/src/components/Toggle/index.stories.tsx b/src/components/Toggle/index.stories.tsx index fe8900d..675ef01 100644 --- a/src/components/Toggle/index.stories.tsx +++ b/src/components/Toggle/index.stories.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import React, { useState } from "react"; import ToggleComponent from "./index"; import { Box } from "../Box"; import Text from "../Text"; diff --git a/src/components/Toggle/index.tsx b/src/components/Toggle/index.tsx index 9bc5f25..afcf0ff 100644 --- a/src/components/Toggle/index.tsx +++ b/src/components/Toggle/index.tsx @@ -1,55 +1,9 @@ -import styled from "styled-components"; -import { layout, LayoutProps, space, SpaceProps } from "styled-system"; +import { IToggleProps } from './types'; +import { ToggleWrapper, ToggleCircle } from './styles'; -interface IProps extends LayoutProps, SpaceProps { - value?: boolean; - onChange?: (value: boolean) => void; - disabled?: boolean; -} - -const AppToggleWrap = styled.button< - IProps & { - active?: boolean; - } ->` - ${layout} - ${space} - - position: relative; - width: 36px; - height: 20px; - background: ${({ theme, active }) => (active ? theme.colors.primaryDefault : theme.colors.blueGray)}; - cursor: pointer; - border-radius: 20px; - padding: 2px; - border: none; - outline: none; - transition: background 0.2s; - - &:hover:not(:disabled) { - background: ${({ theme, active }) => (active ? theme.colors.primaryHovered : theme.colors.blueGrayLight)}; - } - - &:disabled { - background: ${({ theme }) => theme.colors.gray}; - cursor: default; - } -`; - -const AppToggleCircle = styled.span<{ active: boolean }>` - position: absolute; - top: 2px; - left: ${({ active }) => (active ? "calc(100% - 18px)" : "2px")}; - width: 16px; - height: 16px; - background: ${({ theme }) => theme.colors.white}; - border-radius: inherit; - transition: left 0.2s, background 0.2s; -`; - -export default function Toggle({ value, onChange, ...props }: IProps) { +export default function ({ value, onChange, ...props }: IToggleProps) { return ( - { if (onChange) onChange(!value); @@ -57,7 +11,7 @@ export default function Toggle({ value, onChange, ...props }: IProps) { type="button" active={!!value} > - - + + ); } diff --git a/src/components/Toggle/styles.tsx b/src/components/Toggle/styles.tsx new file mode 100644 index 0000000..90476ab --- /dev/null +++ b/src/components/Toggle/styles.tsx @@ -0,0 +1,43 @@ +import styled from "styled-components"; +import { layout, space } from "styled-system"; +import { IToggleProps } from './types'; + +export const ToggleWrapper = styled.button< + IToggleProps & { + active?: boolean; + } +>` + ${layout} + ${space} + + position: relative; + width: 36px; + height: 20px; + background: ${({ theme, active }) => (active ? theme.colors.primaryDefault : theme.colors.blueGray)}; + cursor: pointer; + border-radius: 20px; + padding: 2px; + border: none; + outline: none; + transition: background 0.2s; + + &:hover:not(:disabled) { + background: ${({ theme, active }) => (active ? theme.colors.primaryHovered : theme.colors.blueGrayLight)}; + } + + &:disabled { + background: ${({ theme }) => theme.colors.gray}; + cursor: default; + } +`; + +export const ToggleCircle = styled.span<{ active: boolean }>` + position: absolute; + top: 2px; + left: ${({ active }) => (active ? "calc(100% - 18px)" : "2px")}; + width: 16px; + height: 16px; + background: ${({ theme }) => theme.colors.white}; + border-radius: inherit; + transition: left 0.2s, background 0.2s; +`; \ No newline at end of file diff --git a/src/components/Toggle/types.ts b/src/components/Toggle/types.ts new file mode 100644 index 0000000..1c8ddb1 --- /dev/null +++ b/src/components/Toggle/types.ts @@ -0,0 +1,7 @@ +import { LayoutProps, SpaceProps } from "styled-system"; + +export interface IToggleProps extends LayoutProps, SpaceProps { + value?: boolean; + onChange?: (value: boolean) => void; + disabled?: boolean; +} \ No newline at end of file diff --git a/src/components/Tooltip/index.tsx b/src/components/Tooltip/index.tsx index 2b05382..eb9c0cc 100644 --- a/src/components/Tooltip/index.tsx +++ b/src/components/Tooltip/index.tsx @@ -1,62 +1,12 @@ -import { transparentize } from "polished"; -import React, { ReactNode, useEffect, useRef, useState } from "react"; -import styled from "styled-components"; - +import { ReactNode, useEffect, useRef, useState } from "react"; import { usePopper } from "react-popper"; import * as PopperJS from "@popperjs/core"; import { Box, Flex } from "../Box"; import { Portal } from "@reach/portal"; +import { ITooltipProps } from './types'; +import { Arrow, TooltipContainer } from './styles' -const Arrow = styled.div` - width: 8px; - height: 8px; - - ::before { - position: absolute; - width: 100%; - height: 100%; - box-sizing: border-box; - content: ""; - transform: rotate(45deg); - background: ${({ theme }) => theme.colors.strokeGray}; - } - - &.arrow-top { - bottom: -4px; - } - - &.arrow-bottom { - top: -4px; - } - - &.arrow-left { - right: -4px; - } - - &.arrow-right { - left: -4px; - } -`; - -const TooltipContainer = styled.div<{ show: boolean }>` - pointer-events: none; - visibility: ${(props) => (props.show ? "visible" : "hidden")}; - opacity: ${(props) => (props.show ? 1 : 0)}; - transition: visibility 150ms linear, opacity 150ms linear; - max-width: 256px; - cursor: default; - padding: 0.6rem 1rem; - color: ${({ theme }) => theme.colors.white}; - font-weight: 400; - font-size: 12px; - line-height: 16px; - word-break: break-word; - background: ${({ theme }) => theme.colors.strokeGray}; - border: 1px solid ${({ theme }) => theme.colors.darkGray}; - border-radius: 12px; - box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.9, theme.colors.white)}; -`; -export default function Tooltip({ content, children }: { content: ReactNode; children: ReactNode }) { +export default function ({ content, children }: ITooltipProps) { const activatorElement = useRef(null); const popperElement = useRef(null); const arrowElement = useRef(null); diff --git a/src/components/Tooltip/styles.tsx b/src/components/Tooltip/styles.tsx new file mode 100644 index 0000000..00f443b --- /dev/null +++ b/src/components/Tooltip/styles.tsx @@ -0,0 +1,52 @@ +import styled from "styled-components"; +import { transparentize } from "polished"; + +export const Arrow = styled.div` + width: 8px; + height: 8px; + + ::before { + position: absolute; + width: 100%; + height: 100%; + box-sizing: border-box; + content: ""; + transform: rotate(45deg); + background: ${({ theme }) => theme.colors.strokeGray}; + } + + &.arrow-top { + bottom: -4px; + } + + &.arrow-bottom { + top: -4px; + } + + &.arrow-left { + right: -4px; + } + + &.arrow-right { + left: -4px; + } +`; + +export const TooltipContainer = styled.div<{ show: boolean }>` + pointer-events: none; + visibility: ${(props) => (props.show ? "visible" : "hidden")}; + opacity: ${(props) => (props.show ? 1 : 0)}; + transition: visibility 150ms linear, opacity 150ms linear; + max-width: 256px; + cursor: default; + padding: 0.6rem 1rem; + color: ${({ theme }) => theme.colors.white}; + font-weight: 400; + font-size: 12px; + line-height: 16px; + word-break: break-word; + background: ${({ theme }) => theme.colors.strokeGray}; + border: 1px solid ${({ theme }) => theme.colors.darkGray}; + border-radius: 12px; + box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.9, theme.colors.white)}; +`; \ No newline at end of file diff --git a/src/components/Tooltip/types.ts b/src/components/Tooltip/types.ts new file mode 100644 index 0000000..37bcb6e --- /dev/null +++ b/src/components/Tooltip/types.ts @@ -0,0 +1,6 @@ +import { ReactNode } from "react"; + +export interface ITooltipProps { + content: ReactNode; + children: ReactNode; +} \ No newline at end of file diff --git a/src/components/index.ts b/src/components/index.ts index 42b73b4..74e9388 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -4,9 +4,9 @@ export * from "./Breadcrumbs"; export * from "./Loaders"; export * from "./Charts"; export * from "./Text"; -export * from "./AppBtn"; +export * from "./Button"; export * from "./Toggle"; -export * from "./AppCheckbox"; -export * from "./AppRadioBtn"; +export * from "./Checkbox"; +export * from "./RadioButton"; export * from "./Tabs"; export * from "./Dropdown"; diff --git a/src/hooks/index.ts b/src/hooks/index.ts new file mode 100644 index 0000000..46323eb --- /dev/null +++ b/src/hooks/index.ts @@ -0,0 +1 @@ +export * from './useOnClickOutside'; \ No newline at end of file diff --git a/src/components/Dropdown/useOnClickOutside.ts b/src/hooks/useOnClickOutside.ts similarity index 100% rename from src/components/Dropdown/useOnClickOutside.ts rename to src/hooks/useOnClickOutside.ts diff --git a/src/index.ts b/src/index.ts index c082585..0cc3263 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,4 @@ export * from "./components"; - export * from "./theme"; - -export * from "./Providers"; \ No newline at end of file +export * from "./Providers"; +export * from './hooks'; \ No newline at end of file diff --git a/src/theme/colors.ts b/src/theme/colors.ts index 634e084..609adb1 100644 --- a/src/theme/colors.ts +++ b/src/theme/colors.ts @@ -1,5 +1,5 @@ import { ThemeColors } from "./types"; -import { AppBtnColor } from "../components/AppBtn/types"; +import { ButtonColor } from "../components/Button/types"; export const colors: ThemeColors = { black: "#000000", @@ -26,7 +26,7 @@ type ButtonColorsConfig = { text: string; }; -export type ButtonsColors = { [key in AppBtnColor]: ButtonColorsConfig }; +export type ButtonsColors = { [key in ButtonColor]: ButtonColorsConfig }; export const buttons: ButtonsColors = { primary: { grad0: "#7da0c1",