From b6c4c710bfd4faff1dc0fdbd3c82900070cee609 Mon Sep 17 00:00:00 2001 From: Tony Vi Date: Fri, 11 Aug 2023 21:00:17 +0300 Subject: [PATCH 1/2] feat!: support spread attributes --- src/components/Badge.tsx | 11 ++- src/components/Button.tsx | 2 - src/components/Card.tsx | 2 - src/components/CardActions.tsx | 2 - src/components/CardComment.tsx | 2 - src/components/CardContent.tsx | 2 - src/components/CardInfo.tsx | 8 +- src/components/CircleProgressBar.tsx | 10 +-- src/components/CleanButton.tsx | 10 +-- src/components/ComboBox.tsx | 6 +- src/components/Dot.tsx | 4 +- src/components/Dropdown.tsx | 8 +- src/components/Fieldset.tsx | 8 +- src/components/FiltersContainers.tsx | 3 +- src/components/FiltersCounter.tsx | 13 ++- src/components/FiltersDropdown.tsx | 2 +- src/components/Footer.tsx | 12 ++- src/components/Form.tsx | 8 +- src/components/FormAction.tsx | 2 - src/components/FormActions.tsx | 2 - src/components/FormCard.tsx | 2 - src/components/FormEditor.tsx | 8 +- src/components/FormInput.tsx | 12 +-- src/components/FormMultiInput.tsx | 10 +-- src/components/FormRadio.tsx | 28 +++---- src/components/FormRadioInput.tsx | 23 +++--- src/components/FormTextarea.tsx | 9 +-- src/components/FormTitle.tsx | 6 +- src/components/Gravatar.tsx | 14 ++-- src/components/Header.tsx | 18 ++--- src/components/Icon/BaseIcon.tsx | 16 ++-- src/components/Input.tsx | 4 +- src/components/InputContainer.tsx | 4 +- src/components/Link.tsx | 4 +- src/components/MarkedListItem.tsx | 23 +++--- src/components/Md.tsx | 2 - src/components/MenuItem.tsx | 26 +----- src/components/Modal.tsx | 35 ++++---- src/components/ModalPreview.tsx | 3 - src/components/PageLoadProgress.tsx | 115 ++++++++++++++------------- src/components/Popup.tsx | 58 +++++++------- src/components/Portal.tsx | 2 - src/components/SheepLogo.tsx | 2 - src/components/StateDot.tsx | 28 +++---- src/components/Tag.tsx | 16 +++- src/components/TaskanyLogo.tsx | 2 - src/components/Text.tsx | 4 +- src/components/UserGroup.tsx | 16 ++-- src/components/UserMenu.tsx | 8 +- src/components/UserMenuItem.tsx | 20 ++--- src/components/UserPic.tsx | 21 ++--- 51 files changed, 269 insertions(+), 387 deletions(-) diff --git a/src/components/Badge.tsx b/src/components/Badge.tsx index 2cef88db..e4810294 100644 --- a/src/components/Badge.tsx +++ b/src/components/Badge.tsx @@ -2,11 +2,12 @@ import React from 'react'; import styled from 'styled-components'; import { gray7, gray9, radiusL } from '@taskany/colors'; -interface BadgeProps { +interface BadgeProps extends React.HTMLAttributes { + children: React.ReactNode; size?: 's' | 'm'; color?: string; - children: React.ReactNode; className?: string; + onClick?: React.MouseEventHandler; } @@ -38,10 +39,8 @@ const StyledBadge = styled.div<{ size: BadgeProps['size']; color?: BadgeProps['c } `; -export const Badge: React.FC = ({ size = 's', children, className, onClick }) => ( - +export const Badge: React.FC = ({ size = 's', children, ...props }) => ( + {children} ); - -export default Badge; diff --git a/src/components/Button.tsx b/src/components/Button.tsx index 7f8a387e..9736c5f5 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -331,5 +331,3 @@ export const Button = React.forwardRef( ); }, ); - -export default Button; diff --git a/src/components/Card.tsx b/src/components/Card.tsx index 244ef41c..2eda3642 100644 --- a/src/components/Card.tsx +++ b/src/components/Card.tsx @@ -9,5 +9,3 @@ export const Card = styled.div` border: 1px solid ${gray4}; border-radius: ${radiusM}; `; - -export default Card; diff --git a/src/components/CardActions.tsx b/src/components/CardActions.tsx index f3c9092b..d23b31f5 100644 --- a/src/components/CardActions.tsx +++ b/src/components/CardActions.tsx @@ -11,5 +11,3 @@ export const CardActions = styled.div` left: 0; padding: 20px 12px 10px; `; - -export default CardActions; diff --git a/src/components/CardComment.tsx b/src/components/CardComment.tsx index 1ea3a802..a9348a78 100644 --- a/src/components/CardComment.tsx +++ b/src/components/CardComment.tsx @@ -6,5 +6,3 @@ export const CardComment = styled.div` padding: 12px 14px 12px; user-select: auto; `; - -export default CardComment; diff --git a/src/components/CardContent.tsx b/src/components/CardContent.tsx index 53d096df..63137554 100644 --- a/src/components/CardContent.tsx +++ b/src/components/CardContent.tsx @@ -5,5 +5,3 @@ export const CardContent = styled.div` box-sizing: border-box; padding: 12px 14px; `; - -export default CardContent; diff --git a/src/components/CardInfo.tsx b/src/components/CardInfo.tsx index 74c58fc7..bc0c11b1 100644 --- a/src/components/CardInfo.tsx +++ b/src/components/CardInfo.tsx @@ -15,16 +15,14 @@ const StyledCardInfo = styled(Text)` border-top-right-radius: ${radiusM}; `; -interface CardInfoProps { +interface CardInfoProps extends React.HTMLAttributes { className?: string; children?: React.ReactNode; onClick?: (e?: React.MouseEvent) => void; } -export const CardInfo: React.FC = ({ className, children, onClick }) => ( - +export const CardInfo: React.FC = ({ children, ...props }) => ( + {children} ); - -export default CardInfo; diff --git a/src/components/CircleProgressBar.tsx b/src/components/CircleProgressBar.tsx index 147b4f25..676623b1 100644 --- a/src/components/CircleProgressBar.tsx +++ b/src/components/CircleProgressBar.tsx @@ -2,9 +2,9 @@ import React from 'react'; import { brandColor, gray5 } from '@taskany/colors'; import styled from 'styled-components'; -import Text from './Text'; +import { Text } from './Text'; -interface CircleProgressBarProps { +interface CircleProgressBarProps extends React.HTMLAttributes { value: number; size?: 's' | 'm' | 'l'; className?: string; @@ -41,7 +41,7 @@ const StyledCircle = styled.circle` transition: stroke-dasharray, stroke-dashoffset 100ms ease-in-out; `; -export const CircleProgressBar: React.FC = ({ value, size = 'm', className }) => { +export const CircleProgressBar: React.FC = ({ value, size = 'm', ...props }) => { const diameter = sizeMap[size]; const strokeWidth = size === 's' ? 2 : 3; const radius = diameter / 2; @@ -51,7 +51,7 @@ export const CircleProgressBar: React.FC = ({ value, siz const offset = circumReference - (value / 100) * circumReference; return ( - + @@ -82,5 +82,3 @@ export const CircleProgressBar: React.FC = ({ value, siz ); }; - -export default CircleProgressBar; diff --git a/src/components/CleanButton.tsx b/src/components/CleanButton.tsx index b0084c43..099fdf9a 100644 --- a/src/components/CleanButton.tsx +++ b/src/components/CleanButton.tsx @@ -2,7 +2,7 @@ import React from 'react'; import styled from 'styled-components'; import { gray10, gray7, gray8 } from '@taskany/colors'; -interface CleanButtonProps { +interface CleanButtonProps extends React.HTMLAttributes { className?: string; onClick?: (e: React.MouseEvent) => void; @@ -35,10 +35,4 @@ const StyledCleanButton = styled.div` } `; -export const CleanButton: React.FC = ({ className, onClick }) => ( - - + - -); - -export default CleanButton; +export const CleanButton: React.FC = (props) => +; diff --git a/src/components/ComboBox.tsx b/src/components/ComboBox.tsx index 6f65d4df..e92398cd 100644 --- a/src/components/ComboBox.tsx +++ b/src/components/ComboBox.tsx @@ -33,7 +33,7 @@ interface ComboBoxItemProps { onClick: (value?: any) => void; } -interface ComboBoxProps { +interface ComboBoxProps extends React.HTMLAttributes { renderInput: (props: ComboBoxInputProps) => React.ReactNode; renderItem: (props: ComboBoxItemProps) => React.ReactNode | Record; renderTrigger?: (props: ComboBoxTriggerProps) => React.ReactNode; @@ -98,6 +98,7 @@ export const ComboBox = forwardRef( onChange, onClose, onClickOutside, + ...attrs }, ref, ) => { @@ -207,6 +208,7 @@ export const ComboBox = forwardRef( minWidth={minWidth} maxWidth={maxWidth} offset={offset} + {...attrs} >
{renderItems ? renderItems(children as React.ReactNode) : (children as React.ReactNode)} @@ -216,5 +218,3 @@ export const ComboBox = forwardRef( ); }, ); - -export default ComboBox; diff --git a/src/components/Dot.tsx b/src/components/Dot.tsx index 9d1d8814..ce21cfcd 100644 --- a/src/components/Dot.tsx +++ b/src/components/Dot.tsx @@ -3,7 +3,7 @@ import { colorPrimary, danger9, gray5, warn0 } from '@taskany/colors'; type ViewType = 'default' | 'primary' | 'warning' | 'danger'; -interface DotProps { +interface DotProps extends React.HTMLAttributes { size?: 's' | 'm'; view?: ViewType; } @@ -37,5 +37,3 @@ export const Dot = styled.span` `, }[size])} `; - -export default Dot; diff --git a/src/components/Dropdown.tsx b/src/components/Dropdown.tsx index 439fbdd6..a2054400 100644 --- a/src/components/Dropdown.tsx +++ b/src/components/Dropdown.tsx @@ -8,9 +8,9 @@ import { useKeyPress } from '../hooks/useKeyPress'; import { useKeyboard, KeyCode } from '../hooks/useKeyboard'; import { Popup } from './Popup'; -import Input from './Input'; +import { Input } from './Input'; import { CrossIcon } from './Icon/CrossIcon'; -import MenuItem from './MenuItem'; +import { MenuItem } from './MenuItem'; interface DropdownTriggerProps { ref: React.RefObject; @@ -30,7 +30,7 @@ export interface DropdownItemProps { onClick: (value?: any) => void; } -export interface DropdownProps { +export interface DropdownProps extends React.HTMLAttributes { renderItem: (props: DropdownItemProps) => React.ReactNode; renderTrigger: (props: DropdownTriggerProps) => React.ReactNode; text?: string; @@ -88,6 +88,7 @@ export const Dropdown = React.forwardRef( placement = 'bottom-start', arrow = false, offset = [-4, 8], + ...attrs }, ref, ) => { @@ -195,6 +196,7 @@ export const Dropdown = React.forwardRef( minWidth={100} maxWidth={250} offset={offset} + {...attrs} >
{nullable(onSearchChange, () => ( diff --git a/src/components/Fieldset.tsx b/src/components/Fieldset.tsx index d7b1019b..697a4861 100644 --- a/src/components/Fieldset.tsx +++ b/src/components/Fieldset.tsx @@ -6,7 +6,7 @@ import { Text } from './Text'; type FieldsetViewType = 'default' | 'warning' | 'danger'; -interface FieldsetProps { +interface FieldsetProps extends React.HTMLAttributes { title?: string; view?: FieldsetViewType; children: React.ReactNode; @@ -30,9 +30,9 @@ const colorsMap: Record = { danger: danger0, }; -export const Fieldset: React.FC = ({ view = 'default', title, children, className }) => { +export const Fieldset: React.FC = ({ view = 'default', title, children, ...props }) => { return ( - + {title} @@ -41,5 +41,3 @@ export const Fieldset: React.FC = ({ view = 'default', title, chi ); }; - -export default Fieldset; diff --git a/src/components/FiltersContainers.tsx b/src/components/FiltersContainers.tsx index e3bcbbf6..67180521 100644 --- a/src/components/FiltersContainers.tsx +++ b/src/components/FiltersContainers.tsx @@ -2,8 +2,9 @@ import React from 'react'; import styled from 'styled-components'; import { gapM, gapS, gray5, gray6, gray9, radiusXl, textColor } from '@taskany/colors'; -import Text from './Text'; +import { Text } from './Text'; +// eslint-disable-next-line @typescript-eslint/no-unused-vars export const FiltersPanelContainer = styled(({ loading, ...props }) =>
)<{ loading?: boolean }>` margin: ${gapM} 0; padding: ${gapS} 0; diff --git a/src/components/FiltersCounter.tsx b/src/components/FiltersCounter.tsx index 7833386b..e46ea4f9 100644 --- a/src/components/FiltersCounter.tsx +++ b/src/components/FiltersCounter.tsx @@ -1,11 +1,16 @@ import React, { FC } from 'react'; import { textColor } from '@taskany/colors'; -import Badge from './Badge'; -import Text from './Text'; +import { Badge } from './Badge'; +import { Text } from './Text'; -export const FiltersCounter: FC<{ counter?: number; total: number }> = ({ counter, total }) => ( - +interface FiltersCounterProps extends React.HTMLAttributes { + counter?: number; + total: number; +} + +export const FiltersCounter: FC = ({ counter, total, ...attrs }) => ( + {counter === undefined || total === counter ? ( total ) : ( diff --git a/src/components/FiltersDropdown.tsx b/src/components/FiltersDropdown.tsx index b0dde201..00683132 100644 --- a/src/components/FiltersDropdown.tsx +++ b/src/components/FiltersDropdown.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useMemo, useState, ReactNode } from 'react'; import { Dropdown, DropdownItemProps, DropdownProps } from './Dropdown'; -import MenuItem from './MenuItem'; +import { MenuItem } from './MenuItem'; import { FiltersMenuItem } from './FiltersContainers'; export type FilterDropdownItem = { diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx index 0a85f8ee..5324495b 100644 --- a/src/components/Footer.tsx +++ b/src/components/Footer.tsx @@ -3,7 +3,7 @@ import styled from 'styled-components'; import { gray0 } from '@taskany/colors'; import { SheepLogo } from './SheepLogo'; -import Text from './Text'; +import { Text } from './Text'; const StyledFooter = styled.footer` display: grid; @@ -19,19 +19,17 @@ const StyledFooterMenu = styled.div` export const FooterItem = styled(Text)` padding: 0px 10px; `; -interface FooterProps { - className?: string; +interface FooterProps extends React.HTMLAttributes { children: React.ReactNode; + className?: string; } -export const Footer: React.FC = ({ className, children }) => { +export const Footer: React.FC = ({ className, children, ...attrs }) => { return ( - + {`© ${new Date().getFullYear()} Taskany, Inc.`} {children} ); }; - -export default Footer; diff --git a/src/components/Form.tsx b/src/components/Form.tsx index f6010500..5e687c76 100644 --- a/src/components/Form.tsx +++ b/src/components/Form.tsx @@ -5,7 +5,7 @@ import { gray3 } from '@taskany/colors'; import { KeyCode, KeyMod, useKeyboard } from '../hooks/useKeyboard'; import { formContext } from '../context/form'; -interface FormProps { +interface FormProps extends React.HTMLAttributes { children: React.ReactNode; disabled?: boolean; submitHotkey?: Array; @@ -19,7 +19,7 @@ const StyledForm = styled.form` background-color: ${gray3}; `; -export const Form: React.FC = ({ children, disabled, submitHotkey = submitKeys, onSubmit }) => { +export const Form: React.FC = ({ children, disabled, submitHotkey = submitKeys, onSubmit, ...attrs }) => { const handleSubmit = (e?: React.SyntheticEvent) => { e?.preventDefault(); @@ -33,11 +33,9 @@ export const Form: React.FC = ({ children, disabled, submitHotkey = s return ( - + {children} ); }; - -export default Form; diff --git a/src/components/FormAction.tsx b/src/components/FormAction.tsx index 72de9dd1..c0c5d226 100644 --- a/src/components/FormAction.tsx +++ b/src/components/FormAction.tsx @@ -39,5 +39,3 @@ export const FormAction = styled.div<{ left?: boolean; right?: boolean; columns? } `} `; - -export default FormAction; diff --git a/src/components/FormActions.tsx b/src/components/FormActions.tsx index 83e18994..c948be5b 100644 --- a/src/components/FormActions.tsx +++ b/src/components/FormActions.tsx @@ -42,5 +42,3 @@ export const FormActions = styled(({ flat, focused, ...props }: FormActionsProps border-bottom-right-radius: 0; `} `; - -export default FormActions; diff --git a/src/components/FormCard.tsx b/src/components/FormCard.tsx index 4352194b..bce7ca77 100644 --- a/src/components/FormCard.tsx +++ b/src/components/FormCard.tsx @@ -9,5 +9,3 @@ export const FormCard = styled.div` border: 1px solid ${gray4}; border-radius: ${radiusM}; `; - -export default FormCard; diff --git a/src/components/FormEditor.tsx b/src/components/FormEditor.tsx index c1fc256d..fd810a6a 100644 --- a/src/components/FormEditor.tsx +++ b/src/components/FormEditor.tsx @@ -15,7 +15,7 @@ import { Popup } from './Popup'; import { Link } from './Link'; import { AttachIcon } from './Icon'; -interface FormEditorProps { +interface FormEditorBaseProps { id?: string; name?: string; value?: string; @@ -44,6 +44,8 @@ interface FormEditorProps { onUploadFail?: (message?: string) => void; } +type FormEditorProps = FormEditorBaseProps & React.HTMLAttributes; + const defaultAttachmentsButtonMessage = 'Attach files'; const defaultAttachmentsDescriptionMesssage = "drag'n'drop or pasting also supported"; const defaultAttachmentsUploadingMessage = 'Uploading...'; @@ -274,6 +276,7 @@ export const FormEditor = React.forwardRef( uploadLink, onUploadSuccess, onUploadFail, + ...attrs }, ref, ) => { @@ -429,6 +432,7 @@ export const FormEditor = React.forwardRef( ref={ref} {...onESC} onClick={disabled ? undefined : () => {}} + {...attrs} > {nullable(isDragActive && !disableAttaches, () => ( @@ -502,5 +506,3 @@ export const FormEditor = React.forwardRef( ); }, ); - -export default FormEditor; diff --git a/src/components/FormInput.tsx b/src/components/FormInput.tsx index 86bdc987..7a300ca8 100644 --- a/src/components/FormInput.tsx +++ b/src/components/FormInput.tsx @@ -9,7 +9,7 @@ import { formContext } from '../context/form'; import { Text } from './Text'; import { Popup } from './Popup'; -interface FormInputProps { +interface FormInputProps extends React.HTMLAttributes { id?: string; name?: string; label?: string; @@ -25,14 +25,6 @@ interface FormInputProps { message?: string; }; brick?: 'left' | 'right' | 'center'; - - onMouseLeave?: React.MouseEventHandler; - onMouseEnter?: React.MouseEventHandler; - onChange?: React.ChangeEventHandler; - onInput?: React.ChangeEventHandler; - onKeyDown?: React.KeyboardEventHandler; - onBlur?: React.FocusEventHandler; - onFocus?: React.FocusEventHandler; } const StyledFormInputContainer = styled.div<{ flat: FormInputProps['flat'] }>` @@ -255,5 +247,3 @@ export const FormInput = React.forwardRef((pro ); }); - -export default FormInput; diff --git a/src/components/FormMultiInput.tsx b/src/components/FormMultiInput.tsx index 044d829f..82b65c52 100644 --- a/src/components/FormMultiInput.tsx +++ b/src/components/FormMultiInput.tsx @@ -13,7 +13,7 @@ import { Input } from './Input'; import { MenuItem } from './MenuItem'; import { ComboBox } from './ComboBox'; -interface FormMultiInputProps { +interface FormMultiInputBaseProps { items?: Array<{ title: string; id: any }>; id?: string; name?: string; @@ -30,6 +30,8 @@ interface FormMultiInputProps { onClick?: (item: { title: string; id: any }) => void; } +type FormMultiInputProps = FormMultiInputBaseProps & React.HTMLAttributes; + const StyledFormInputContainer = styled.div` box-sizing: border-box; display: flex; @@ -73,7 +75,7 @@ export const FormMultiInput = React.forwardRef { @@ -101,7 +103,7 @@ export const FormMultiInput = React.forwardRef + {nullable(label, (l) => ( {l}: @@ -148,5 +150,3 @@ export const FormMultiInput = React.forwardRef void; +} + +type FormRadioProps = FormRadioBaseProps & React.HTMLAttributes; + export const StyledFormRadioInputContainer = styled.div` padding: 8px 16px; @@ -19,16 +31,6 @@ export const StyledFormRadioInputLabel = styled(Text)` padding-left: ${gapS}; `; -interface FormRadioProps { - name?: string; - label?: string; - value?: string; - flat?: 'top' | 'bottom' | 'both'; - children?: React.ReactNode; - - onChange?: (v: FormRadioProps['value']) => void; -} - const StyledFormRadio = styled.div<{ flat: FormRadioProps['flat'] }>` box-sizing: border-box; display: flex; @@ -66,10 +68,10 @@ const StyledFormRadioLabel = styled(Text)` background-color: transparent; `; -export const FormRadio: React.FC = ({ name, label, value, flat, children, onChange }) => { +export const FormRadio: React.FC = ({ name, label, value, flat, children, onChange, ...attrs }) => { return ( - + {nullable(label, (l) => ( {l}: @@ -81,5 +83,3 @@ export const FormRadio: React.FC = ({ name, label, value, flat, ); }; - -export default FormRadio; diff --git a/src/components/FormRadioInput.tsx b/src/components/FormRadioInput.tsx index 0e7d29ef..6c2ffda6 100644 --- a/src/components/FormRadioInput.tsx +++ b/src/components/FormRadioInput.tsx @@ -6,7 +6,7 @@ import { formContext } from '../context/form'; import { StyledFormRadioInput, StyledFormRadioInputContainer, StyledFormRadioInputLabel } from './FormRadio'; -interface FormRadioInputProps { +interface FormRadioInputProps extends React.HTMLAttributes { name?: string; label?: string; value: string; @@ -15,10 +15,15 @@ interface FormRadioInputProps { disabled?: boolean; } -export const FormRadioInput: React.FC = (props) => { +export const FormRadioInput: React.FC = ({ + disabled: innerDisabled, + value: innerValue, + label, + ...attrs +}) => { const { name, value, onChange } = useContext(radioContext); const formCtx = useContext(formContext); - const disabled = formCtx.disabled || props.disabled; + const disabled = formCtx.disabled || innerDisabled; const onRadioInputChange = useCallback( (e: React.ChangeEvent) => { @@ -31,20 +36,18 @@ export const FormRadioInput: React.FC = (props) => { - {nullable(props.label, (l) => ( - + {nullable(label, (l) => ( + {l} ))} ); }; - -export default FormRadioInput; diff --git a/src/components/FormTextarea.tsx b/src/components/FormTextarea.tsx index b95977a4..7c541d36 100644 --- a/src/components/FormTextarea.tsx +++ b/src/components/FormTextarea.tsx @@ -4,7 +4,7 @@ import { fontDisplay, gray2, gray3, gray7, radiusS, textColor } from '@taskany/c import { formContext } from '../context/form'; -interface FormTextareaProps { +interface FormTextareaProps extends React.HTMLAttributes { id?: string; name?: string; value?: string | number; @@ -19,11 +19,6 @@ interface FormTextareaProps { error?: { message?: string; }; - - onChange?: React.ChangeEventHandler; - onInput?: React.ChangeEventHandler; - onBlur?: React.FocusEventHandler; - onFocus?: React.FocusEventHandler; } const StyledFormTextarea = styled( @@ -93,5 +88,3 @@ export const FormTextarea = React.forwardRef; }); - -export default FormTextarea; diff --git a/src/components/FormTitle.tsx b/src/components/FormTitle.tsx index 439fd766..918d6f56 100644 --- a/src/components/FormTitle.tsx +++ b/src/components/FormTitle.tsx @@ -1,9 +1,9 @@ import styled from 'styled-components'; import { gapM, gapS } from '@taskany/colors'; -import { Text, TextProps } from './Text'; +import { Text } from './Text'; -export const FormTitle = styled(Text)` +export const FormTitle = styled(Text)` padding: ${gapS} 0 ${gapM}; `; @@ -11,5 +11,3 @@ FormTitle.defaultProps = { size: 'xl', weight: 'bolder', }; - -export default FormTitle; diff --git a/src/components/Gravatar.tsx b/src/components/Gravatar.tsx index 7e123de4..1affaeea 100644 --- a/src/components/Gravatar.tsx +++ b/src/components/Gravatar.tsx @@ -4,7 +4,7 @@ import styled from 'styled-components'; import { isRetina } from '../utils/isRetina'; -interface GravatarProps { +interface GravatarProps extends React.HTMLAttributes { email: string; md5?: string; size: number; @@ -30,16 +30,15 @@ const StyledImage = styled.img<{ visible: boolean }>` `} `; -export const Gravatar = ({ +export const Gravatar: React.FC = ({ size = 50, rating = 'g', def = 'retro', domain = process.env.NEXT_PUBLIC_GRAVATAR_HOST || 'www.gravatar.com', email, md5, - className, - onClick, -}: GravatarProps) => { + ...props +}) => { const [modernBrowser, setModernBrowser] = useState(true); const [mounted, setMounted] = useState(false); const [visible, setVisible] = useState(false); @@ -100,11 +99,8 @@ export const Gravatar = ({ src="/anonymous.png" height={size} width={size} - className={className} - onClick={onClick} onError={onLoadError} + {...props} /> ); }; - -export default Gravatar; diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 0923ff0f..b8b31ab0 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -5,6 +5,13 @@ import { textColor, gray7, colorPrimary, gray3 } from '@taskany/colors'; import { Link } from './Link'; import { TaskanyLogo } from './TaskanyLogo'; +interface HeaderProps extends React.HTMLAttributes { + logo?: React.ReactNode; + nav?: React.ReactNode; + children?: React.ReactNode; + menu?: React.ReactNode; +} + const HeaderContainer = styled.header` display: grid; grid-template-columns: 20px 1fr minmax(150px, auto) 55px; @@ -75,18 +82,11 @@ const DefaultLogo: React.FC = () => ( ); -export const Header: React.FC<{ - logo?: React.ReactNode; - nav?: React.ReactNode; - children?: React.ReactNode; - menu?: React.ReactNode; -}> = ({ menu, logo = , nav, children }) => ( - +export const Header: React.FC = ({ menu, logo = , nav, children, ...attrs }) => ( + {logo} {nav} {children} {menu} ); - -export default Header; diff --git a/src/components/Icon/BaseIcon.tsx b/src/components/Icon/BaseIcon.tsx index 0bb6561b..a7c5d39b 100644 --- a/src/components/Icon/BaseIcon.tsx +++ b/src/components/Icon/BaseIcon.tsx @@ -9,7 +9,7 @@ export const iconSizesMap = { l: 48, }; -export interface BaseIconProps { +export interface BaseIconProps extends React.HTMLAttributes { size: keyof typeof iconSizesMap | number; value: React.FunctionComponent>; color?: string; @@ -35,20 +35,16 @@ const StyledIcon = styled.span<{ onClick?: BaseIconProps['onClick']; color?: Bas `; export const BaseIcon = React.forwardRef( - ({ size, value: Component, color = 'inherit', stroke = 1, className, onClick, noWrap }, ref) => { + ({ size, value: Component, color = 'inherit', stroke = 1, onClick, noWrap, ...props }, ref) => { const sizePx = `${typeof size === 'string' ? iconSizesMap[size] : size}px`; - const content = ; + const content = ( + + ); return noWrap ? ( content ) : ( - + {content} ); diff --git a/src/components/Input.tsx b/src/components/Input.tsx index b718da57..e8e1fd5e 100644 --- a/src/components/Input.tsx +++ b/src/components/Input.tsx @@ -88,7 +88,7 @@ const StyledInput = styled(({ forwardRef, size, view, brick, iconLeft, iconRight ` border-radius: 0; `} - + ${({ size }) => size === 'm' && ` @@ -134,5 +134,3 @@ export const Input = React.forwardRef( ), ); - -export default Input; diff --git a/src/components/InputContainer.tsx b/src/components/InputContainer.tsx index 60be0004..5f7a6fbe 100644 --- a/src/components/InputContainer.tsx +++ b/src/components/InputContainer.tsx @@ -2,7 +2,7 @@ import React from 'react'; import styled from 'styled-components'; import { gapS, gray2 } from '@taskany/colors'; -interface InputContainerProps { +interface InputContainerProps extends React.HTMLAttributes { brick?: 'left' | 'right' | 'center'; focused?: boolean; hovered?: boolean; @@ -45,5 +45,3 @@ export const InputContainer = styled.div` background-color: ${gray2}; `} `; - -export default InputContainer; diff --git a/src/components/Link.tsx b/src/components/Link.tsx index 0a967faa..16238f71 100644 --- a/src/components/Link.tsx +++ b/src/components/Link.tsx @@ -2,7 +2,7 @@ import React from 'react'; import styled from 'styled-components'; import { link10 } from '@taskany/colors'; -interface LinkProps { +interface LinkProps extends React.HTMLAttributes { inline?: boolean; className?: string; children?: React.ReactNode; @@ -46,5 +46,3 @@ const StyledLink = styled( export const Link = React.forwardRef(({ ...props }, ref) => { return ; }); - -export default Link; diff --git a/src/components/MarkedListItem.tsx b/src/components/MarkedListItem.tsx index 1af13831..251a4120 100644 --- a/src/components/MarkedListItem.tsx +++ b/src/components/MarkedListItem.tsx @@ -4,7 +4,17 @@ import { gapS, gapXs, gray3, radiusM } from '@taskany/colors'; import { Text } from './Text'; -const StyledItemCard = styled.div<{ focused?: boolean; checked?: boolean; hoverColor?: string }>` +interface MarkedListItemProps extends React.HTMLAttributes { + children?: React.ReactNode; + mark?: React.ReactNode; + hoverColor?: string; + focused?: boolean; + checked?: boolean; + + onClick?: () => void; +} + +const StyledItemCard = styled.div>` display: flex; align-items: center; @@ -58,15 +68,8 @@ const StyledItemInfo = styled(Text)` padding-left: ${gapS}; `; -export const MarkedListItem: React.FC<{ - children?: React.ReactNode; - mark?: React.ReactNode; - hoverColor?: string; - focused?: boolean; - checked?: boolean; - onClick?: () => void; -}> = ({ mark, hoverColor, children, focused, checked, onClick }) => ( - +export const MarkedListItem: React.FC = ({ mark, children, ...props }) => ( + {mark} {children} diff --git a/src/components/Md.tsx b/src/components/Md.tsx index ca2a4d8e..47066ac8 100644 --- a/src/components/Md.tsx +++ b/src/components/Md.tsx @@ -22,5 +22,3 @@ export const Md = styled.div` border-radius: ${radiusS}; } `; - -export default Md; diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index b34f86c3..38c30230 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -6,7 +6,7 @@ import { nullable } from '../utils/nullable'; import { Dot } from './Dot'; -interface MenuItemProps { +interface MenuItemProps extends React.HTMLAttributes { selected?: boolean; focused?: boolean; disabled?: boolean; @@ -67,26 +67,8 @@ const StyledIcon = styled.span` padding-right: ${gapS}; `; -export const MenuItem: React.FC = ({ - icon, - color, - ghost, - children, - selected, - focused, - disabled, - view, - onClick, - className, -}) => ( - +export const MenuItem: React.FC = ({ icon, children, selected, view, ...props }) => ( + {nullable(icon, () => ( {icon} ))} @@ -96,5 +78,3 @@ export const MenuItem: React.FC = ({ ))} ); - -export default MenuItem; diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index f652b07d..f9930bbb 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -3,7 +3,6 @@ import styled from 'styled-components'; import { backgroundColor, danger0, gapM, gapS, gray4, radiusM, warn0 } from '@taskany/colors'; import { useKeyboard, KeyCode } from '../hooks/useKeyboard'; -import { nullable } from '../utils/nullable'; import { Portal } from './Portal'; import { CrossIcon } from './Icon'; @@ -16,7 +15,7 @@ const colorsMap: Record = { danger: danger0, }; -interface ModalProps { +interface ModalProps extends React.HTMLAttributes { children: React.ReactNode; className?: string; visible?: boolean; @@ -46,7 +45,8 @@ const StyledModalSurface = styled.div` background-color: rgba(0, 0, 0, 0.9); `; -const StyledModal = styled.div<{ view?: ModalViewType }>` +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const StyledModal = styled(({ width, view, ...props }: ModalProps) =>
)` box-sizing: border-box; position: absolute; z-index: 101; @@ -60,6 +60,7 @@ const StyledModal = styled.div<{ view?: ModalViewType }>` background-color: ${backgroundColor}; border: 1px solid ${({ view = 'default' }) => colorsMap[view]}; + width: ${({ width }) => width}px; `; const StyledCross = styled.div` @@ -83,8 +84,14 @@ const StyledCross = styled.div` } `; -export const ModalCross: React.FC<{ onClick?: () => void; className?: string }> = ({ onClick, className }) => ( - +interface ModalCrossProps extends React.HTMLAttributes { + className?: string; + + onClick?: () => void; +} + +export const ModalCross: React.FC = ({ onClick, className, ...attrs }) => ( + ); @@ -106,16 +113,7 @@ export const ModalContent = styled.div` padding: ${gapM}; `; -export const Modal: React.FC = ({ - visible, - view, - children, - width = 800, - className, - cross = true, - onClose, - onShow, -}) => { +export const Modal: React.FC = ({ visible, children, width = 800, onClose, onShow, ...props }) => { const [onESC] = useKeyboard([KeyCode.Escape], () => onClose?.(), { disableGlobalEvent: false, }); @@ -137,15 +135,10 @@ export const Modal: React.FC = ({ return visible ? ( - - {nullable(cross && onClose, () => ( - - ))} + {children} ) : null; }; - -export default Modal; diff --git a/src/components/ModalPreview.tsx b/src/components/ModalPreview.tsx index 8b164c9b..6d3b09d4 100644 --- a/src/components/ModalPreview.tsx +++ b/src/components/ModalPreview.tsx @@ -11,7 +11,6 @@ import { Portal } from './Portal'; interface ModalPreviewProps { children: React.ReactNode; className?: string; - visible?: boolean; width?: number; @@ -89,5 +88,3 @@ export const ModalPreview: React.FC = ({ visible, children, o ); }; - -export default ModalPreview; diff --git a/src/components/PageLoadProgress.tsx b/src/components/PageLoadProgress.tsx index 9a463216..29f4ca50 100644 --- a/src/components/PageLoadProgress.tsx +++ b/src/components/PageLoadProgress.tsx @@ -2,12 +2,12 @@ import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useRedu import styled, { css, keyframes } from 'styled-components'; import { brandColor, textColor } from '@taskany/colors'; -type PageLoadProgressCommonProps = { +interface PageLoadProgressCommonProps extends React.HTMLAttributes { color?: string; start?: number; max?: number; height?: number; -}; +} type StrippedProps = | { @@ -29,7 +29,6 @@ export interface PageLoadProgressRef { } type StyledProgressBarInnerProps = Pick; - type Action = { type: 'start' } | { type: 'tick'; payload?: number } | { type: 'stop' } | { type: 'reset' }; type ProgressState = 'idle' | 'load' | 'finish'; @@ -47,7 +46,8 @@ const animatedLoadedBackground = (shift: number) => keyframes` background-position: ${shift}px 0; }`; -const StyledProgressWrapper = styled.div>` +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const StyledProgressWrapper = styled(({ height, ...props }: PageLoadProgressProps) =>
)` position: absolute; top: 0; left: 0; @@ -132,59 +132,60 @@ const reducer: React.Reducer = (state, action) => { } }; -export const PageLoadProgress = forwardRef((props, ref) => { - const { color, start = 0.05, max = 0.995, height = 4, stripped = false, stopColor, size } = props; - const barRef = useRef(null); - const [{ state, progress }, dispatch] = useReducer(reducer, { - progress: start, - max, - state: 'idle', - }); - - useImperativeHandle( - ref, - () => ({ - start: () => { - dispatch({ type: 'start' }); - }, - done: () => { - dispatch({ type: 'stop' }); - }, - }), - [], - ); - - useEffect(() => { - if (barRef.current) { - const node = barRef.current; - if (state !== 'idle') { - node.style.width = `${progress * 100}%`; - } else { - node.style.transition = 'none'; - node.style.removeProperty('width'); - - node.offsetHeight; // trigger reflow - - node.style.removeProperty('transition'); +export const PageLoadProgress = forwardRef( + ({ color, start = 0.05, max = 0.995, height = 4, stripped = false, stopColor, size, ...attrs }, ref) => { + const barRef = useRef(null); + const [{ state, progress }, dispatch] = useReducer(reducer, { + progress: start, + max, + state: 'idle', + }); + + useImperativeHandle( + ref, + () => ({ + start: () => { + dispatch({ type: 'start' }); + }, + done: () => { + dispatch({ type: 'stop' }); + }, + }), + [], + ); + + useEffect(() => { + if (barRef.current) { + const node = barRef.current; + if (state !== 'idle') { + node.style.width = `${progress * 100}%`; + } else { + node.style.transition = 'none'; + node.style.removeProperty('width'); + + node.offsetHeight; // trigger reflow + + node.style.removeProperty('transition'); + } } - } - }, [progress, state]); + }, [progress, state]); - const transitionEndHandle = useCallback(() => { - if (state === 'load') { - dispatch({ type: 'tick' }); - } + const transitionEndHandle = useCallback(() => { + if (state === 'load') { + dispatch({ type: 'tick' }); + } - if (state === 'finish') { - dispatch({ type: 'reset' }); - } - }, [state]); - - return ( - - - - - - ); -}); + if (state === 'finish') { + dispatch({ type: 'reset' }); + } + }, [state]); + + return ( + + + + + + ); + }, +); diff --git a/src/components/Popup.tsx b/src/components/Popup.tsx index 0d4ff3d8..1bed5ecd 100644 --- a/src/components/Popup.tsx +++ b/src/components/Popup.tsx @@ -6,11 +6,9 @@ import Tippy from '@tippyjs/react/headless'; /** * @see all props https://atomiks.github.io/tippyjs/v6/all-props/ */ -interface PopupProps { - placement?: ComponentProps['placement']; - interactive?: ComponentProps['interactive']; - hideOnClick?: ComponentProps['hideOnClick']; - reference?: ComponentProps['reference']; +interface PopupProps + extends Omit, 'role' | 'content' | 'children' | 'offset' | 'ref'>, + React.HTMLAttributes { visible?: boolean; target?: React.ReactElement; arrow?: boolean; @@ -21,14 +19,6 @@ interface PopupProps { view?: 'warning' | 'danger' | 'primary'; offset?: number[]; children: React.ReactNode; - - onTrigger?: ComponentProps['onTrigger']; - onShow?: ComponentProps['onShow']; - onShown?: ComponentProps['onShown']; - onMount?: ComponentProps['onMount']; - onHide?: ComponentProps['onHide']; - onHidden?: ComponentProps['onHidden']; - onClickOutside?: ComponentProps['onClickOutside']; } const colorsMap = { @@ -170,30 +160,38 @@ const StyledPopupContainer = styled.div<{ */ export const Popup: React.FC = ({ placement = 'auto', + interactive, + reference, children, target, - overflow, - minWidth, - maxWidth, - tooltip, - view, arrow = true, offset, + visible, + onTrigger, + onShow, + onShown, + onMount, + onHide, + onHidden, + onClickOutside, + hideOnClick, ...props }) => ( ( - + visible={visible} + render={(tippyProps) => ( + {children} {arrow && } @@ -214,5 +212,3 @@ export const Popup: React.FC = ({ {target} ); - -export default Popup; diff --git a/src/components/Portal.tsx b/src/components/Portal.tsx index f6fc3fd2..fce1fa08 100644 --- a/src/components/Portal.tsx +++ b/src/components/Portal.tsx @@ -8,5 +8,3 @@ interface PortalProps { } export const Portal: React.FC = ({ id, children }) => createPortal(children, usePortal(id)); - -export default Portal; diff --git a/src/components/SheepLogo.tsx b/src/components/SheepLogo.tsx index 39e6ee71..121c9e5a 100644 --- a/src/components/SheepLogo.tsx +++ b/src/components/SheepLogo.tsx @@ -69,5 +69,3 @@ export const SheepLogo: React.FC = () => { ); }; - -export default SheepLogo; diff --git a/src/components/StateDot.tsx b/src/components/StateDot.tsx index 306ae1bf..b8a969a7 100644 --- a/src/components/StateDot.tsx +++ b/src/components/StateDot.tsx @@ -2,7 +2,7 @@ import React from 'react'; import styled from 'styled-components'; import { gray6 } from '@taskany/colors'; -export interface StateDotProps { +export interface StateDotProps extends React.HTMLAttributes { title?: string; color?: string; hoverColor?: string; @@ -11,10 +11,8 @@ export interface StateDotProps { onClick?: () => void; } -const StyledStateDot = styled.div<{ - size: StateDotProps['size']; - onClick: StateDotProps['onClick']; -}>` +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const StyledStateDot = styled(({ size, ...props }: Pick) =>
)` width: 14px; height: 14px; border-radius: 100%; @@ -27,10 +25,6 @@ const StyledStateDot = styled.div<{ background-color: var(--bkg); - /* &:hover { - background-color: var(--bkg-hover); - } */ - ${({ onClick }) => onClick && ` @@ -45,13 +39,11 @@ const StyledStateDot = styled.div<{ `} `; -export const StateDot: React.FC = React.memo( - ({ title, size = 'm', color, hoverColor, onClick, className }) => { - const style = { - '--bkg': color ?? gray6, - '--bkgHover': hoverColor ?? color ?? gray6, - } as React.CSSProperties; +export const StateDot: React.FC = React.memo(({ size = 'm', color, hoverColor, ...props }) => { + const style = { + '--bkg': color ?? gray6, + '--bkgHover': hoverColor ?? color ?? gray6, + } as React.CSSProperties; - return ; - }, -); + return ; +}); diff --git a/src/components/Tag.tsx b/src/components/Tag.tsx index 7b71d249..32d79275 100644 --- a/src/components/Tag.tsx +++ b/src/components/Tag.tsx @@ -6,7 +6,7 @@ import { nullable } from '../utils/nullable'; import { CleanButton } from './CleanButton'; -interface TagProps { +interface TagProps extends React.HTMLAttributes { title: string; description?: string; size?: 's' | 'm'; @@ -86,7 +86,16 @@ const StyledTag = styled(({ onHide, ...props }: Partial & { children?: `} `; -export const Tag: React.FC = ({ title, description, size = 'm', onClick, onHide, className, checked }) => { +export const Tag: React.FC = ({ + title, + description, + size = 'm', + onClick, + onHide, + className, + checked, + ...attrs +}) => { const onHideClick = useCallback( (e: React.MouseEvent) => { e.preventDefault(); @@ -105,6 +114,7 @@ export const Tag: React.FC = ({ title, description, size = 'm', onClic title={description} className={className} checked={checked} + {...attrs} > {nullable(onHide, () => ( @@ -113,5 +123,3 @@ export const Tag: React.FC = ({ title, description, size = 'm', onClic ); }; - -export default Tag; diff --git a/src/components/TaskanyLogo.tsx b/src/components/TaskanyLogo.tsx index 7897090e..0c70ca89 100644 --- a/src/components/TaskanyLogo.tsx +++ b/src/components/TaskanyLogo.tsx @@ -38,5 +38,3 @@ export const TaskanyLogo: React.FC = ({ size = 's' }) => { ); }; - -export default TaskanyLogo; diff --git a/src/components/Text.tsx b/src/components/Text.tsx index 92919196..4450b2f6 100644 --- a/src/components/Text.tsx +++ b/src/components/Text.tsx @@ -36,7 +36,7 @@ const calcTextSize = (size: keyof typeof textSizes, weight: keyof typeof textWei font-weight: ${textWeight[weight]}; `; -export interface TextProps { +export interface TextProps extends React.HTMLAttributes { size?: keyof typeof textSizes; weight?: keyof typeof textWeight; color?: string; @@ -154,5 +154,3 @@ export const TextStyle = createGlobalStyle` font-weight: ${textWeight.bolder}; } `; - -export default Text; diff --git a/src/components/UserGroup.tsx b/src/components/UserGroup.tsx index efc0d86c..a396160f 100644 --- a/src/components/UserGroup.tsx +++ b/src/components/UserGroup.tsx @@ -4,11 +4,11 @@ import { gapSm, gray4, gray9, gray6, radiusL, radiusXl } from '@taskany/colors'; import { Nullish } from '../types/void'; -import UserPic from './UserPic'; -import Text from './Text'; -import Popup from './Popup'; +import { UserPic } from './UserPic'; +import { Text } from './Text'; +import { Popup } from './Popup'; -interface UserGroupProps { +interface UserGroupProps extends React.HTMLAttributes { users: Nullish<{ name: string; email: string; image: string }>[]; className?: string; size?: number; @@ -37,7 +37,7 @@ const UserContainer = styled.div` } `; -const UserImage = styled(UserPic)` +const StyledUserPic = styled(UserPic)` display: block; `; @@ -71,7 +71,7 @@ const StyledSmallCircle = styled.div` transform: translateY(8px); `; -export const UserGroup: FC = ({ users, className, size = 24, limit = 3 }) => { +export const UserGroup: FC = ({ users, size = 24, limit = 3, ...props }) => { const showCounter = users.length > limit; const items = users.slice(0, showCounter ? limit : users.length); @@ -83,13 +83,13 @@ export const UserGroup: FC = ({ users, className, size = 24, lim }, [limit, users]); return ( - + {items.map((user, i) => ( - + } tooltip diff --git a/src/components/UserMenu.tsx b/src/components/UserMenu.tsx index 8890af3b..90cf1794 100644 --- a/src/components/UserMenu.tsx +++ b/src/components/UserMenu.tsx @@ -6,7 +6,7 @@ import { nullable } from '../utils/nullable'; import { UserPic } from './UserPic'; -interface UserMenuProps { +interface UserMenuProps extends React.HTMLAttributes { notifications?: boolean; avatar?: string | null; email?: string | null; @@ -38,9 +38,9 @@ const StyledNotifier = styled.div` } `; -export const UserMenu = ({ notifications, avatar, email, onClick }: UserMenuProps) => { +export const UserMenu: React.FC = ({ notifications, avatar, email, onClick, ...attrs }) => { return ( - + {nullable(notifications, () => ( ))} @@ -49,5 +49,3 @@ export const UserMenu = ({ notifications, avatar, email, onClick }: UserMenuProp ); }; - -export default UserMenu; diff --git a/src/components/UserMenuItem.tsx b/src/components/UserMenuItem.tsx index b7fab54b..09872771 100644 --- a/src/components/UserMenuItem.tsx +++ b/src/components/UserMenuItem.tsx @@ -4,7 +4,7 @@ import { gray4, gray6, radiusM, textColor } from '@taskany/colors'; import { UserPic } from './UserPic'; -interface UserMenuItemProps { +interface UserMenuItemProps extends React.HTMLAttributes { name?: string; email?: string; image?: string; @@ -68,21 +68,13 @@ const StyledUserEmail = styled.div` color: ${textColor}; `; -const StyledUserPick = styled(UserPic)` +const StyledUserPic = styled(UserPic)` justify-self: center; `; -export const UserMenuItem: React.FC = ({ - className, - name, - email, - image, - focused, - checked, - onClick, -}) => ( - - +export const UserMenuItem: React.FC = ({ name, email, image, ...props }) => ( + + {name} @@ -90,5 +82,3 @@ export const UserMenuItem: React.FC = ({ ); - -export default UserMenuItem; diff --git a/src/components/UserPic.tsx b/src/components/UserPic.tsx index 44678d27..c287a6f4 100644 --- a/src/components/UserPic.tsx +++ b/src/components/UserPic.tsx @@ -3,7 +3,7 @@ import styled from 'styled-components'; import { Gravatar } from './Gravatar'; -interface UserPicProps { +interface UserPicProps extends React.HTMLAttributes { src?: string | null; size?: number; email?: string | null; @@ -17,7 +17,7 @@ const StyledImage = styled.img` border-radius: 100%; `; -export const UserPic: React.FC = ({ src, email, size = 32, className, onClick }) => { +export const UserPic: React.FC = ({ src, email, size = 32, ...props }) => { const sizePx = `${size}px`; const onLoadError: React.ReactEventHandler = useCallback(({ currentTarget }) => { @@ -26,24 +26,13 @@ export const UserPic: React.FC = ({ src, email, size = 32, classNa }, []); if (src) { - return ( - - ); + return ; } if (email) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return ; + return ; } - return ; + return ; }; - -export default UserPic; From 6ffea0cc0fb0b86c00b2b9dee598c9e6f3027fe1 Mon Sep 17 00:00:00 2001 From: Tony Vi Date: Fri, 11 Aug 2023 21:42:18 +0300 Subject: [PATCH 2/2] refactor(Tag)!: move out clean button --- src/components/CleanButton.tsx | 2 +- src/components/FormMultiInput.tsx | 7 ++-- src/components/Tag.tsx | 54 ++++++------------------------- 3 files changed, 15 insertions(+), 48 deletions(-) diff --git a/src/components/CleanButton.tsx b/src/components/CleanButton.tsx index 099fdf9a..c0f8842a 100644 --- a/src/components/CleanButton.tsx +++ b/src/components/CleanButton.tsx @@ -2,7 +2,7 @@ import React from 'react'; import styled from 'styled-components'; import { gray10, gray7, gray8 } from '@taskany/colors'; -interface CleanButtonProps extends React.HTMLAttributes { +export interface CleanButtonProps extends React.HTMLAttributes { className?: string; onClick?: (e: React.MouseEvent) => void; diff --git a/src/components/FormMultiInput.tsx b/src/components/FormMultiInput.tsx index 82b65c52..6b4c008c 100644 --- a/src/components/FormMultiInput.tsx +++ b/src/components/FormMultiInput.tsx @@ -8,7 +8,7 @@ import { formContext } from '../context/form'; import { PlusIcon } from './Icon/PlusIcon'; import { Text } from './Text'; -import { Tag } from './Tag'; +import { Tag, TagCleanButton } from './Tag'; import { Input } from './Input'; import { MenuItem } from './MenuItem'; import { ComboBox } from './ComboBox'; @@ -111,7 +111,10 @@ export const FormMultiInput = React.forwardRef ( - + + + {item.title} + ))} { - title: string; + children: React.ReactNode; description?: string; size?: 's' | 'm'; className?: string; checked?: boolean; onClick?: MouseEventHandler; - onHide?: () => void; } -const StyledCleanButton = styled(CleanButton)``; +export const TagCleanButton = styled(CleanButton)``; // eslint-disable-next-line @typescript-eslint/no-unused-vars -const StyledTag = styled(({ onHide, ...props }: Partial & { children?: React.ReactNode }) => ( -
-))` +const StyledTag = styled(({ size, ...props }: TagProps) =>
)` display: inline-block; position: relative; padding: 4px 12px 5px; @@ -45,8 +40,7 @@ const StyledTag = styled(({ onHide, ...props }: Partial & { children?: margin-left: ${gapXs}; } - ${({ onHide, checked }) => - !onHide && + ${({ checked }) => !checked && ` &:hover { @@ -57,7 +51,7 @@ const StyledTag = styled(({ onHide, ...props }: Partial & { children?: `} &:hover { - ${StyledCleanButton} { + ${TagCleanButton} { visibility: visible; cursor: pointer; @@ -86,40 +80,10 @@ const StyledTag = styled(({ onHide, ...props }: Partial & { children?: `} `; -export const Tag: React.FC = ({ - title, - description, - size = 'm', - onClick, - onHide, - className, - checked, - ...attrs -}) => { - const onHideClick = useCallback( - (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - - onHide?.(); - }, - [onHide], - ); - +export const Tag: React.FC = ({ children, description, size = 'm', ...props }) => { return ( - - {nullable(onHide, () => ( - - ))} - {title} + + {children} ); };