Skip to content

Commit

Permalink
feat: update Multiselect padding, add xs sizing (#561)
Browse files Browse the repository at this point in the history
* fix(MultiSelect): use flex gap over margins

* feat(MultiSelect): update sm and md padding to design sysem

* fix: run npm i

* feat(MultiSelect): add xs sizing variant

* feat(MultiSelect): add `isStretchLayout` prop to follow Figma

* fix: set focus to false when blur

* feat: correct padding on multiselect input field

* feat: revert blur on input blur

* fix: remove input width when unfocused and in stretch mode
  • Loading branch information
karrui authored Nov 21, 2023
1 parent b7f4a8a commit 2b986cf
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 43 deletions.
1 change: 0 additions & 1 deletion react/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 22 additions & 7 deletions react/src/MultiSelect/MultiSelect.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,23 +122,38 @@ WithFixedItemHeight.args = {
],
}

export const WithIsStretchLayoutProp = Template.bind({})
WithIsStretchLayoutProp.args = {
isStretchLayout: true,
values: ['What happens when the label is fairly long', 'Bat'],
}
WithIsStretchLayoutProp.storyName = 'With isStretchLayout Prop'

export const Sizes = () => {
const items = ['sm', 'md']
const [first, setFirst] = useState(['sm'])
const [second, setSecond] = useState(['md'])
const items = ['xs', 'sm', 'md']
const [xs, setXs] = useState(['xs'])
const [sm, setSm] = useState(['sm'])
const [md, setMd] = useState(['md'])

return (
<Stack>
<MultiSelect
values={first}
onChange={setFirst}
values={xs}
onChange={setXs}
size="xs"
items={items}
name="xs"
/>
<MultiSelect
values={sm}
onChange={setSm}
size="sm"
items={items}
name="sm"
/>
<MultiSelect
values={second}
onChange={setSecond}
values={md}
onChange={setMd}
size="md"
items={items}
name="md"
Expand Down
4 changes: 4 additions & 0 deletions react/src/MultiSelect/MultiSelectContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ interface MultiSelectContextReturn<Item extends ComboboxItem = ComboboxItem>
> {
maxItems: number | null
colorScheme?: ThemingProps<'MultiSelect'>['colorScheme']
/**
* If `true`, the selected items will take up the full width of the input container. Defaults to `false`.
*/
isStretchLayout?: boolean
}

export const MultiSelectContext = createContext<
Expand Down
6 changes: 6 additions & 0 deletions react/src/MultiSelect/MultiSelectProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ export interface MultiSelectProviderProps<
downshiftMultiSelectProps?: Partial<UseMultipleSelectionProps<Item>>
colorScheme?: ThemingProps<'MultiSelect'>['colorScheme']
fixedItemHeight?: number
/**
* If `true`, the selected items will take up the full width of the input container. Defaults to `false`.
*/
isStretchLayout?: boolean
}
export const MultiSelectProvider = ({
items: rawItems,
Expand All @@ -90,6 +94,7 @@ export const MultiSelectProvider = ({
size: _size,
colorScheme,
fixedItemHeight,
isStretchLayout,
}: MultiSelectProviderProps): JSX.Element => {
const theme = useTheme()
// Required in case size is set in theme, we should respect the one set in theme.
Expand Down Expand Up @@ -351,6 +356,7 @@ export const MultiSelectProvider = ({
activeIndex,
setActiveIndex,
colorScheme,
isStretchLayout,
}}
>
{children}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { forwardRef, PropsWithChildren, useCallback } from 'react'
import { Box, chakra, Flex, Icon, useMergeRefs } from '@chakra-ui/react'
import { forwardRef, PropsWithChildren, useCallback, useMemo } from 'react'
import {
Box,
chakra,
Flex,
Icon,
SystemStyleObject,
useMergeRefs,
} from '@chakra-ui/react'

import { BxsChevronDown, BxsChevronUp } from '~/icons'
import { useSelectContext } from '~/SingleSelect'
Expand All @@ -10,7 +17,21 @@ import { SelectedItems } from './SelectedItems'

const MultiItemsContainer = ({ children }: PropsWithChildren) => {
const { styles } = useSelectContext()
return <Box sx={styles.itemContainer}>{children}</Box>
const { isStretchLayout } = useMultiSelectContext()

const containerStyles = useMemo(() => {
if (isStretchLayout)
return {
columnGap: '0',
}
return {}
}, [isStretchLayout])

return (
<Box __css={styles.itemContainer} {...containerStyles}>
{children}
</Box>
)
}

export const MultiSelectCombobox = forwardRef<HTMLInputElement>(
Expand All @@ -23,6 +44,7 @@ export const MultiSelectCombobox = forwardRef<HTMLInputElement>(
isRequired,
placeholder,
setIsFocused,
isFocused,
isOpen,
toggleMenu,
isInvalid,
Expand All @@ -31,7 +53,7 @@ export const MultiSelectCombobox = forwardRef<HTMLInputElement>(
getToggleButtonProps,
} = useSelectContext()

const { getDropdownProps } = useMultiSelectContext()
const { getDropdownProps, selectedItems } = useMultiSelectContext()

const mergedRefs = useMergeRefs(inputRef, ref)

Expand All @@ -56,6 +78,15 @@ export const MultiSelectCombobox = forwardRef<HTMLInputElement>(
[setIsFocused],
)

const focusInputStyles: SystemStyleObject = useMemo(() => {
if (isFocused || selectedItems.length === 0) return {}
return {
width: 0,
padding: 0,
flex: 0,
}
}, [isFocused, selectedItems.length])

return (
<Flex
aria-disabled={isDisabled}
Expand All @@ -69,9 +100,12 @@ export const MultiSelectCombobox = forwardRef<HTMLInputElement>(
<chakra.input
placeholder={placeholder}
__css={styles.field}
{...focusInputStyles}
{...getInputProps({
...getDropdownProps({
ref: mergedRefs,
// We do not remove focus on blur since the focus state
// refers to the entire field and not just this input.
onFocus: () => setIsFocused(true),
onKeyDown: handleInputTabKeydown,
readOnly: isReadOnly || !isSearchable,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,12 @@ export const MultiSelectItem = ({
styles,
size,
} = useSelectContext()
const { getSelectedItemProps, removeSelectedItem, colorScheme } =
useMultiSelectContext()
const {
getSelectedItemProps,
removeSelectedItem,
colorScheme,
isStretchLayout,
} = useMultiSelectContext()

const itemMeta = useMemo(() => {
return {
Expand Down Expand Up @@ -62,10 +66,18 @@ export const MultiSelectItem = ({
[closeMenu, isDisabled, isOpen, isReadOnly, setIsFocused],
)

const tagStyles = useMemo(() => {
if (!isStretchLayout) return styles.tag
return {
...styles.tag,
width: '100%',
}
}, [isStretchLayout, styles.tag])

return (
<Tag
title={itemMeta.label}
sx={styles.tag}
sx={tagStyles}
size={size}
colorScheme={colorScheme}
{...getSelectedItemProps({
Expand Down
67 changes: 39 additions & 28 deletions react/src/theme/components/MultiSelect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,18 @@ const baseStyle = definePartsStyle((props) => {

return {
...comboboxBaseStyle,
chevron: {
display: 'flex',
},
itemContainer: {
display: 'inline-flex',
flexWrap: 'wrap',
flexGrow: 1,
// Margin difference for selected items.
my: '-3px',
gap: '4px',
},
fieldwrapper: {
display: 'flex',
justifyContent: 'space-between',
flexWrap: 'wrap',
cursor: 'pointer',
_disabled: {
Expand Down Expand Up @@ -69,6 +72,8 @@ const baseStyle = definePartsStyle((props) => {
flexGrow: 1,
width: 0,
bg: 'transparent',
minW: '3.75rem',
px: '0.25rem',
_disabled: {
cursor: 'not-allowed',
},
Expand Down Expand Up @@ -117,15 +122,37 @@ const variants = {
}

const sizes = {
xs: definePartsStyle(
mergeThemeOverride(omit(SingleSelect.sizes?.xs, ['field', 'icon']), {
itemContainer: {
// Padding for dropdown toggle.
maxW: 'calc(100% - 2.5rem)',
},
tagIcon: {
fontSize: '0.875rem',
mr: '0.25rem',
},
icon: {
fontSize: '0.875rem',
},
chevron: {
py: '0.125rem',
fontSize: '1rem',
px: '0.5rem',
},
fieldwrapper: {
...SingleSelect.sizes?.xs.field,
p: '0.5rem',
minH: SingleSelect.sizes?.xs.field?.h,
h: 'auto',
},
}),
),
sm: definePartsStyle(
mergeThemeOverride(omit(SingleSelect.sizes?.sm, ['field', 'icon']), {
itemContainer: {
// Padding for dropdown toggle.
maxW: 'calc(100% - 2rem)',
},
tag: {
my: '6px',
mx: '2px',
maxW: 'calc(100% - 2.5rem)',
},
tagIcon: {
fontSize: '1rem',
Expand All @@ -135,58 +162,42 @@ const sizes = {
fontSize: '1rem',
},
chevron: {
pt: '0.5rem',
py: '0.25rem',
fontSize: '1rem',
px: '0.5rem',
},
fieldwrapper: {
...SingleSelect.sizes?.sm.field,
p: '0.25rem',
p: '0.5rem',
minH: SingleSelect.sizes?.sm.field?.h,
h: 'auto',
},
field: {
minW: '3.75rem',
px: '2px',
my: '2px',
pl: '0.5rem',
},
}),
),
md: definePartsStyle(
mergeThemeOverride(omit(SingleSelect.sizes?.md, ['field', 'icon']), {
itemContainer: {
// Padding for dropdown toggle.
maxW: 'calc(100% - 2.5rem)',
maxW: 'calc(100% - 2.75rem)',
},
icon: {
fontSize: '1.25rem',
},
tag: {
my: '4px',
mx: '2px',
},
tagIcon: {
fontSize: '1.25rem',
mr: '0.25rem',
},
chevron: {
pt: '0.25rem',
py: '0.25rem',
px: '0.5rem',
fontSize: '1.25rem',
},
fieldwrapper: {
...SingleSelect.sizes?.md.field,
p: '0.375rem',
p: '0.5rem',
minH: SingleSelect.sizes?.md.field?.h,
h: 'auto',
},
field: {
minW: '3.75rem',
px: '2px',
my: '2px',
pl: '0.5rem',
},
}),
),
}
Expand Down

0 comments on commit 2b986cf

Please sign in to comment.