diff --git a/src/components/Chip.tsx b/src/components/Chip.tsx index 2ed688a9..b4a9f803 100644 --- a/src/components/Chip.tsx +++ b/src/components/Chip.tsx @@ -1,4 +1,4 @@ -import { Flex, type FlexProps } from 'honorable' +import { type FlexProps } from 'honorable' import PropTypes from 'prop-types' import { type ComponentProps, @@ -84,16 +84,43 @@ const sizeToCloseHeight = { large: 12, } as const satisfies Record -const ChipCardSC = styled(Card)(({ theme }) => ({ - '.closeIcon': { - color: theme.colors['text-light'], - }, - '&:hover': { +const ChipCardSC = styled(Card)<{ $size: ChipSize; $severity: ChipSeverity }>(({ + $size, + $severity, + theme, +}) => { + const textColor = + theme.mode === 'light' + ? theme.colors['text-light'] + : theme.colors[severityToColor[$severity]] || theme.colors.text + + return { '.closeIcon': { - color: theme.colors.text, + color: theme.colors['text-light'], + paddingLeft: theme.spacing.xsmall, + }, + '&:hover': { + '.closeIcon': { + color: theme.colors.text, + }, + }, + '.spinner': { + marginRight: theme.spacing.xsmall, + }, + '.icon': { + marginRight: theme.spacing.xsmall, }, - }, -})) + '.children': { + display: 'flex', + ...theme.partials.text.body2, + color: textColor, + fontSize: $size === 'small' ? 12 : 14, + fontWeight: $size === 'small' ? 400 : 600, + lineHeight: $size === 'small' ? '16px' : '20px', + gap: 4, + }, + } +}) function ChipRef( { @@ -114,8 +141,7 @@ function ChipRef( const theme = useTheme() hue = hue || parentFillLevelToHue[parentFillLevel] - const textColor = - theme.mode === 'light' ? 'text-light' : severityToColor[severity] || 'text' + const iconCol = severityToIconColor[severity] || 'icon-default' return ( @@ -130,39 +156,30 @@ function ChipRef( alignItems="center" display="inline-flex" textDecoration="none" + $size={size} + $severity={severity} {...(as ? { forwardedAs: as } : {})} {...props} > {loading && ( )} {icon && ( )} - - {children} - +
{children}
{closeButton && ( )} diff --git a/src/components/ComboBox.tsx b/src/components/ComboBox.tsx index 5d5da950..592fee86 100644 --- a/src/components/ComboBox.tsx +++ b/src/components/ComboBox.tsx @@ -1,4 +1,4 @@ -import { ExtendTheme, Spinner, mergeTheme } from 'honorable' +import { mergeTheme } from 'honorable' import { omitBy } from 'lodash' import { isUndefined, omit, pick } from 'lodash-es' import { @@ -28,6 +28,7 @@ import { useFloatingDropdown } from '../hooks/useFloatingDropdown' import DropdownArrowIcon from './icons/DropdownArrowIcon' import SearchIcon from './icons/SearchIcon' import Input, { type InputProps } from './Input' +import { Spinner } from './Spinner' import { type ListBoxItemBaseProps } from './ListBoxItem' import { PopoverListBox } from './PopoverListBox' @@ -131,21 +132,6 @@ const OpenButton = styled( }, })) -const StartIconButton = styled.div(({ theme }) => ({ - cursor: 'pointer', - display: 'flex', - alignItems: 'center', - paddingLeft: theme.spacing.medium, - paddingRight: theme.spacing.medium, -})) - -const comboBoxLeftRightStyles = { - alignSelf: 'stretch', - paddingHorizontal: 0, - marginLeft: 0, - marginRight: 0, -} - const honorableInputPropNames = [ 'onChange', 'onFocus', @@ -213,7 +199,10 @@ function ComboBoxInput({ Root: [{ paddingRight: 0 }], EndIcon: [ { - ...comboBoxLeftRightStyles, + alignSelf: 'stretch', + paddingHorizontal: 0, + marginLeft: 0, + marginRight: 0, }, ], }, @@ -221,34 +210,27 @@ function ComboBoxInput({ } return ( - - - - - ) : ( - startIcon && {startIcon} - ) - } - endIcon={ - showArrow ? ( - - ) : undefined - } - inputProps={{ - ref: inputRef, - onClick: onInputClick, - ...innerInputProps, - }} - {...outerInputProps} - /> - + : startIcon + } + endIcon={ + showArrow ? ( + + ) : undefined + } + inputProps={{ + ref: inputRef, + onClick: onInputClick, + ...innerInputProps, + }} + {...outerInputProps} + /> ) } diff --git a/src/components/Input.tsx b/src/components/Input.tsx index 6e83175b..abdeee6b 100644 --- a/src/components/Input.tsx +++ b/src/components/Input.tsx @@ -1,9 +1,8 @@ -import { ExtendTheme, Input as HonorableInput } from 'honorable' +import { ExtendTheme, Input as HonorableInput, mergeTheme } from 'honorable' import type { InputProps as HonorableInputProps } from 'honorable' import { type ComponentProps, type ReactNode, forwardRef, useRef } from 'react' import styled from 'styled-components' import { mergeRefs } from 'react-merge-refs' - import { mergeProps } from 'react-aria' import { simulateInputChange } from '../utils/simulateInputChange' @@ -21,6 +20,7 @@ export type InputProps = HonorableInputProps & { prefix?: ReactNode titleContent?: ReactNode showClearButton?: boolean + themeExtension?: Record } const PrefixSuffix = styled.div(({ theme }) => ({ @@ -85,6 +85,7 @@ const Input = forwardRef( showClearButton, titleContent, inputProps, + themeExtension: themeExtensionProp, ...props }: InputProps, ref @@ -95,7 +96,7 @@ const Input = forwardRef( ...(inputProps ?? {}), ref: mergeRefs([inputRef, ...(inputProps?.ref ? [inputProps.ref] : [])]), } - const themeExtension: any = { + let themeExtension: any = { Input: { Root: [ { @@ -135,6 +136,9 @@ const Input = forwardRef( ], }, } + + themeExtension = mergeTheme(themeExtension, themeExtensionProp) + const parentFillLevel = useFillLevel() const size = (props as any).large ? 'large' diff --git a/src/components/Spinner.tsx b/src/components/Spinner.tsx index f28f7cf2..e3aeaa47 100644 --- a/src/components/Spinner.tsx +++ b/src/components/Spinner.tsx @@ -1,23 +1,53 @@ -import { Spinner as HonorableSpinner } from 'honorable' import { type ComponentProps } from 'react' -import styled from 'styled-components' +import styled, { keyframes } from 'styled-components' -const SpinnerSC = styled(HonorableSpinner)<{ $color: string }>( - ({ $color }) => ({ - '&&::before': { - borderTopColor: $color || 'red', - }, - }) -) +const rotateAnim = keyframes` + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +` + +const SpinnerSC = styled( + styled('div')<{ $color: string; $size: number }>( + ({ theme, $color, $size }) => ({ + display: 'block', + width: $size, + height: $size, + position: 'relative', + '&::before': { + content: '""', + position: 'absolute', + top: '50%', + left: '50%', + width: $size, + height: $size, + marginTop: $size * -0.5, + marginLeft: $size * -0.5, + borderRadius: '50%', + borderTop: `2px solid ${$color || theme.colors['icon-default']}`, + borderRight: `2px solid transparent`, + }, + }) + ) +)` + &::before { + animation: ${rotateAnim} 0.8s linear infinite; + } +` export function Spinner({ color, + size = 16, as, ...props -}: { color: string } & ComponentProps) { +}: { color?: string; size?: number } & ComponentProps) { return ( diff --git a/src/stories/ComboBox.stories.tsx b/src/stories/ComboBox.stories.tsx index 6ef459a4..cdbde35d 100644 --- a/src/stories/ComboBox.stories.tsx +++ b/src/stories/ComboBox.stories.tsx @@ -178,7 +178,7 @@ const ChipList = styled(ListBoxItemChipList)(({ theme }) => ({ justifyContent: 'start', })) -function Template({ onFillLevel }: { onFillLevel: any }) { +function Template({ onFillLevel, ...args }: { onFillLevel: any }) { const [selectedKeys, setSelectedKeys] = useState(new Set()) const [inputValue, setInputValue] = useState('') @@ -242,6 +242,7 @@ function Template({ onFillLevel }: { onFillLevel: any }) { onSelectionChange={onSelectionChange} onInputChange={onInputChange} inputProps={{ placeholder: 'Pick something' }} + {...args} > {searchResults.map( ({ item, score: _score, refIndex: _refIndex }) => ( @@ -280,7 +281,7 @@ function Template({ onFillLevel }: { onFillLevel: any }) { ) } -function TagsTemplate() { +function TagsTemplate({ ...args }: any) { const [selectedKeys, setSelectedKeys] = useState(new Set()) const [inputValue, setInputValue] = useState('') const [isOpen, setIsOpen] = useState(false) @@ -370,6 +371,7 @@ function TagsTemplate() { } maxHeight={232} allowsEmptyCollection={!!newKey} + {...args} > {searchResults.map(({ item, score: _score, refIndex: _refIndex }) => (