diff --git a/package.json b/package.json index 59ef717b2..8e835c24f 100644 --- a/package.json +++ b/package.json @@ -129,7 +129,7 @@ "react-ace": "^11.0.1", "react-dom": "18.2.0", "react-router-dom": "6.15.0", - "react-window": "1.8.9", + "react-window": "1.8.11", "recharts": "^2.9.0", "sanitize-html": "2.12.1", "styled-components": "^6.1.13", diff --git a/src/components/form/ComboBox/ComboBox.tsx b/src/components/form/ComboBox/ComboBox.tsx index 98b22e702..083fd8682 100644 --- a/src/components/form/ComboBox/ComboBox.tsx +++ b/src/components/form/ComboBox/ComboBox.tsx @@ -1,12 +1,10 @@ import { Autocomplete, createFilterOptions } from '@mui/material' import _sortBy from 'lodash/sortBy' import { useEffect, useMemo, useRef } from 'react' -import styled from 'styled-components' import { Skeleton } from '~/components/designSystem' import { useInternationalization } from '~/hooks/core/useInternationalization' import { useDebouncedSearch } from '~/hooks/useDebouncedSearch' -import { theme } from '~/styles' import { ComboBoxInput } from './ComboBoxInput' import { ComboBoxItem } from './ComboBoxItem' @@ -132,14 +130,17 @@ export const ComboBox = ({ value={value || null} loading={isLoading} loadingText={ - +
{[1, 2, 3].map((i) => ( - +
- +
))} - +
} noOptionsText={emptyText ?? translate('text_623b3acb8ee4e000ba87d082')} selectOnFocus={allowAddValue} @@ -213,19 +214,3 @@ export const ComboBox = ({ /> ) } - -const LoadingIemsWrapper = styled.div` - margin: ${theme.spacing(4)} 0; - display: flex; - flex-direction: column; - gap: ${theme.spacing(8)}; -` - -const LoadingItem = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - box-sizing: border-box; - margin: 0 ${theme.spacing(2)}; - padding: 0 ${theme.spacing(4)}; -` diff --git a/src/components/form/ComboBox/ComboBoxInput.tsx b/src/components/form/ComboBox/ComboBoxInput.tsx index 3a216889c..e270dce62 100644 --- a/src/components/form/ComboBox/ComboBoxInput.tsx +++ b/src/components/form/ComboBox/ComboBoxInput.tsx @@ -1,9 +1,7 @@ import { InputAdornment } from '@mui/material' import _omit from 'lodash/omit' -import styled from 'styled-components' import { Button, Typography } from '~/components/designSystem' -import { theme } from '~/styles' import { tw } from '~/styles/utils' import { ComboBoxInputProps } from './types' @@ -84,10 +82,10 @@ export const ComboBoxInput = ({ ), startAdornment: startAdornmentValue && ( - - {startAdornmentValue} + + {startAdornmentValue} - + ), }} @@ -96,9 +94,3 @@ export const ComboBoxInput = ({ /> ) } - -const StartAdornmentTypography = styled(Typography)` - > span:first-child { - margin-right: ${theme.spacing(2)}; - } -` diff --git a/src/components/form/ComboBox/ComboBoxItem.tsx b/src/components/form/ComboBox/ComboBoxItem.tsx index 4f01d9ac7..2d3daeb4d 100644 --- a/src/components/form/ComboBox/ComboBoxItem.tsx +++ b/src/components/form/ComboBox/ComboBoxItem.tsx @@ -54,7 +54,7 @@ export const ComboBoxItem = ({ > {customValue ? ( <> - + {labelNode ?? label} @@ -99,10 +99,6 @@ const ItemWrapper = styled.div` } ` -const AddCustomValueIcon = styled(Icon)` - margin-right: ${theme.spacing(4)}; -` - export const Item = styled.div<{ $virtualized?: boolean }>` display: flex; align-items: center; diff --git a/src/components/form/ComboBox/ComboBoxPopperFactory.tsx b/src/components/form/ComboBox/ComboBoxPopperFactory.tsx index 269e924d5..697f263aa 100644 --- a/src/components/form/ComboBox/ComboBoxPopperFactory.tsx +++ b/src/components/form/ComboBox/ComboBoxPopperFactory.tsx @@ -67,11 +67,6 @@ const StyledPopper = styled(Popper)<{ .MuiAutocomplete-paper { border: 1px solid ${theme.palette.grey[200]}; - padding: ${theme.spacing(2)} 0; box-sizing: content-box; } - - > *.combobox-popper--grouped .MuiAutocomplete-paper { - padding: 0 0 ${theme.spacing(2)} 0; - } ` diff --git a/src/components/form/ComboBox/ComboBoxVirtualizedList.tsx b/src/components/form/ComboBox/ComboBoxVirtualizedList.tsx index 47f6c1952..565f57c3c 100644 --- a/src/components/form/ComboBox/ComboBoxVirtualizedList.tsx +++ b/src/components/form/ComboBox/ComboBoxVirtualizedList.tsx @@ -1,6 +1,8 @@ import { ReactElement, useEffect, useRef } from 'react' import { VariableSizeList } from 'react-window' +import { tw } from '~/styles/utils' + import { ITEM_HEIGHT } from './ComboBoxItem' import { ComboBoxProps } from './types' @@ -25,19 +27,18 @@ type ComboBoxVirtualizedListProps = { export const ComboBoxVirtualizedList = (props: ComboBoxVirtualizedListProps) => { const { elements, value } = props - const hasDescription = elements.some( - (el) => (el.props?.children?.props?.option?.description as string)?.length > 0, - ) - const itemCount = elements?.length - const elementHeight = hasDescription ? ITEM_HEIGHT + 4 : ITEM_HEIGHT const getHeight = () => { + const hasAnyGroupHeader = elements.some((el) => (el.key as string).includes(GROUP_ITEM_KEY)) + // recommended perf best practice if (itemCount > 5) { - return 5 * (elementHeight + 4) - 4 // Last item does not have 4px margin-bottom + return 5 * (ITEM_HEIGHT + 4) + 4 // Add 4px for margins + } else if (itemCount <= 2 && hasAnyGroupHeader) { + return itemCount * (ITEM_HEIGHT + 2) // Add 2px for margins } - return itemCount * (elementHeight + 4) - 4 // Last item does not have 4px margin-bottom + return itemCount * (ITEM_HEIGHT + 4) + 4 // Add 4px for margins } // reset the `VariableSizeList` cache if data gets updated @@ -63,6 +64,9 @@ export const ComboBoxVirtualizedList = (props: ComboBoxVirtualizedListProps) => return ( 1, + })} itemData={elements} height={getHeight()} width="100%" @@ -70,10 +74,10 @@ export const ComboBoxVirtualizedList = (props: ComboBoxVirtualizedListProps) => innerElementType="div" itemSize={(index) => { return index === itemCount - 1 - ? elementHeight + ? ITEM_HEIGHT : ((elements[index].key as string) || '').includes(GROUP_ITEM_KEY) - ? GROUP_HEADER_HEIGHT + (index === 0 ? 8 : 12) - : elementHeight + 4 + ? GROUP_HEADER_HEIGHT + (index === 0 ? 2 : 6) + : ITEM_HEIGHT + 8 }} overscanCount={5} itemCount={itemCount} diff --git a/src/components/form/ComboBox/ComboboxList.tsx b/src/components/form/ComboBox/ComboboxList.tsx index 619882a26..92b0a57c6 100644 --- a/src/components/form/ComboBox/ComboboxList.tsx +++ b/src/components/form/ComboBox/ComboboxList.tsx @@ -1,15 +1,18 @@ import _groupBy from 'lodash/groupBy' -import { Children, ForwardedRef, forwardRef, ReactElement, ReactNode, useMemo } from 'react' -import styled, { css } from 'styled-components' +import { + Children, + ForwardedRef, + forwardRef, + PropsWithChildren, + ReactElement, + ReactNode, + useMemo, +} from 'react' import { Typography } from '~/components/designSystem' -import { theme } from '~/styles' +import { tw } from '~/styles/utils' -import { - ComboBoxVirtualizedList, - GROUP_HEADER_HEIGHT, - GROUP_ITEM_KEY, -} from './ComboBoxVirtualizedList' +import { ComboBoxVirtualizedList, GROUP_ITEM_KEY } from './ComboBoxVirtualizedList' import { ComboBoxData, ComboBoxProps } from './types' const randomKey = Math.round(Math.random() * 100000) @@ -19,6 +22,27 @@ interface ComboBoxVirtualizedListProps children: ReactNode } +const ComboboxListItem = ({ + children, + virtualized, + className, + ...propsToForward +}: { className?: string } & PropsWithChildren & ComboBoxVirtualizedListProps) => ( +
+ {children} +
+) + export const ComboboxList = forwardRef( ( { @@ -36,9 +60,9 @@ export const ComboboxList = forwardRef( if (!isGrouped) { return Children.toArray( (children as ReactElement[]).map((item, i) => ( - + {item} - + )), ) } @@ -60,22 +84,35 @@ export const ComboboxList = forwardRef( ...acc, isGrouped ? [ - // If renderGroupHeader is provided, render the html, otherewise simply render the key - {(!!renderGroupHeader && (renderGroupHeader[key] as ReactNode)) || ( {key} )} - , + , ] : [], ...(groupedBy[key] as ReactElement[]).map((item, j) => ( - + {item} - + )), ] }, []), @@ -83,59 +120,21 @@ export const ComboboxList = forwardRef( }, [isGrouped, renderGroupHeader, children, propsToForward, virtualized]) return ( - +
{virtualized ? ( ) : ( htmlItems )} - +
) }, ) ComboboxList.displayName = 'ComboboxList' - -const Item = styled.div`` - -const Container = styled.div<{ $virtualized?: boolean }>` - max-height: inherit; - position: relative; - padding-bottom: 0; - box-sizing: border-box; - overflow: ${({ $virtualized }) => ($virtualized ? 'hidden' : 'auto')}; - - .MuiAutocomplete-listbox { - max-height: inherit; - } - - ${Item}:not(:last-child) { - margin: ${({ $virtualized }) => - $virtualized ? `0 ${theme.spacing(2)}` : `0 0 ${theme.spacing(1)}`}; - } -` - -const GroupHeader = styled.div<{ $isFirst?: boolean; $virtualized?: boolean }>` - height: ${GROUP_HEADER_HEIGHT}px; - display: flex; - width: inherit; - align-items: center; - padding: 0 ${theme.spacing(6)}; - background-color: ${theme.palette.grey[100]}; - box-sizing: border-box; - box-shadow: - ${theme.shadows[7]}, - 0px -1px 0px 0px ${theme.palette.divider}; - - ${({ $virtualized, $isFirst }) => - !$virtualized - ? css` - z-index: ${theme.zIndex.dialog + 2}; - position: sticky; - top: 0; - margin: ${$isFirst ? 0 : theme.spacing(2)} 0 ${theme.spacing(2)}; - ` - : css` - margin: ${$isFirst ? 0 : theme.spacing(1)} 0 ${theme.spacing(2)}; - `}; -` diff --git a/src/components/form/MultipleComboBox/MultipleComboBoxList.tsx b/src/components/form/MultipleComboBox/MultipleComboBoxList.tsx index b5bcd6c8c..47589e20b 100644 --- a/src/components/form/MultipleComboBox/MultipleComboBoxList.tsx +++ b/src/components/form/MultipleComboBox/MultipleComboBoxList.tsx @@ -106,10 +106,6 @@ const Container = styled.div<{ $virtualized?: boolean }>` box-sizing: border-box; overflow: ${({ $virtualized }) => ($virtualized ? 'hidden' : 'auto')}; - .MuiAutocomplete-listbox { - max-height: inherit; - } - ${Item}:not(:last-child) { margin: ${({ $virtualized }) => $virtualized ? `0 ${theme.spacing(2)}` : `0 0 ${theme.spacing(1)}`}; diff --git a/src/styles/muiTheme.ts b/src/styles/muiTheme.ts index 5f295828b..d5a5917b0 100644 --- a/src/styles/muiTheme.ts +++ b/src/styles/muiTheme.ts @@ -463,6 +463,7 @@ export const theme = createTheme({ display: 'flex', flexDirection: 'column', gap: '4px', + maxHeight: 'inherit', padding: 0, }, root: { diff --git a/yarn.lock b/yarn.lock index c70ec213c..143394c49 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10669,10 +10669,10 @@ react-transition-group@^4.4.5: loose-envify "^1.4.0" prop-types "^15.6.2" -react-window@1.8.9: - version "1.8.9" - resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.9.tgz#24bc346be73d0468cdf91998aac94e32bc7fa6a8" - integrity sha512-+Eqx/fj1Aa5WnhRfj9dJg4VYATGwIUP2ItwItiJ6zboKWA6EX3lYDAXfGF2hyNqplEprhbtjbipiADEcwQ823Q== +react-window@1.8.11: + version "1.8.11" + resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.11.tgz#a857b48fa85bd77042d59cc460964ff2e0648525" + integrity sha512-+SRbUVT2scadgFSWx+R1P754xHPEqvcfSfVX10QYg6POOz+WNgkN48pS+BtZNIMGiL1HYrSEiCkwsMS15QogEQ== dependencies: "@babel/runtime" "^7.0.0" memoize-one ">=3.1.1 <6"