Feat: Advanced filter (#7700)

Design:


![twenty-advanced-filters-design](https://github.com/user-attachments/assets/7d99971c-9ee1-4a78-a2fb-7ae5a9b3a836)

Not ready to be merged yet!

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
ad-elias
2024-10-24 16:59:59 +02:00
committed by GitHub
parent 1dfeba39eb
commit 315820ec86
99 changed files with 3349 additions and 1079 deletions

View File

@ -1,7 +1,6 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { MouseEvent, useMemo, useRef, useState } from 'react';
import { IconChevronDown, IconComponent } from 'twenty-ui';
import { IconComponent } from 'twenty-ui';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
@ -10,8 +9,8 @@ import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownM
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { SelectControl } from '@/ui/input/components/SelectControl';
import { isDefined } from '~/utils/isDefined';
import { EllipsisDisplay } from '@/ui/field/display/components/EllipsisDisplay';
import { SelectHotkeyScope } from '../types/SelectHotkeyScope';
export type SelectOption<Value extends string | number | null> = {
@ -47,22 +46,6 @@ const StyledContainer = styled.div<{ fullWidth?: boolean }>`
width: ${({ fullWidth }) => (fullWidth ? '100%' : 'auto')};
`;
const StyledControlContainer = styled.div<{ disabled?: boolean }>`
align-items: center;
background-color: ${({ theme }) => theme.background.transparent.lighter};
border: 1px solid ${({ theme }) => theme.border.color.medium};
box-sizing: border-box;
border-radius: ${({ theme }) => theme.border.radius.sm};
color: ${({ disabled, theme }) =>
disabled ? theme.font.color.tertiary : theme.font.color.primary};
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
height: ${({ theme }) => theme.spacing(8)};
justify-content: space-between;
padding: 0 ${({ theme }) => theme.spacing(2)};
`;
const StyledLabel = styled.span`
color: ${({ theme }) => theme.font.color.light};
display: block;
@ -71,20 +54,6 @@ const StyledLabel = styled.span`
margin-bottom: ${({ theme }) => theme.spacing(1)};
`;
const StyledControlLabel = styled.div`
align-items: center;
display: flex;
overflow: hidden;
gap: ${({ theme }) => theme.spacing(1)};
`;
const StyledIconChevronDown = styled(IconChevronDown)<{
disabled?: boolean;
}>`
color: ${({ disabled, theme }) =>
disabled ? theme.font.color.extraLight : theme.font.color.tertiary};
`;
export const Select = <Value extends string | number | null>({
className,
disabled: disabledFromProps,
@ -103,7 +72,6 @@ export const Select = <Value extends string | number | null>({
}: SelectProps<Value>) => {
const selectContainerRef = useRef<HTMLDivElement>(null);
const theme = useTheme();
const [searchInputValue, setSearchInputValue] = useState('');
const selectedOption =
@ -126,24 +94,6 @@ export const Select = <Value extends string | number | null>({
const { closeDropdown } = useDropdown(dropdownId);
const selectControl = (
<StyledControlContainer disabled={isDisabled}>
<StyledControlLabel>
{!!selectedOption?.Icon && (
<selectedOption.Icon
color={
isDisabled ? theme.font.color.light : theme.font.color.primary
}
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
)}
<EllipsisDisplay> {selectedOption?.label} </EllipsisDisplay>
</StyledControlLabel>
<StyledIconChevronDown disabled={isDisabled} size={theme.icon.size.md} />
</StyledControlContainer>
);
return (
<StyledContainer
className={className}
@ -154,13 +104,21 @@ export const Select = <Value extends string | number | null>({
>
{!!label && <StyledLabel>{label}</StyledLabel>}
{isDisabled ? (
selectControl
<SelectControl
selectedOption={selectedOption}
isDisabled={isDisabled}
/>
) : (
<Dropdown
dropdownId={dropdownId}
dropdownMenuWidth={dropdownWidth}
dropdownPlacement="bottom-start"
clickableComponent={selectControl}
clickableComponent={
<SelectControl
selectedOption={selectedOption}
isDisabled={isDisabled}
/>
}
disableBlur={disableBlur}
dropdownComponents={
<>

View File

@ -0,0 +1,63 @@
import { SelectOption } from '@/ui/input/components/Select';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { IconChevronDown } from 'twenty-ui';
const StyledControlContainer = styled.div<{ disabled?: boolean }>`
align-items: center;
background-color: ${({ theme }) => theme.background.transparent.lighter};
border: 1px solid ${({ theme }) => theme.border.color.medium};
box-sizing: border-box;
border-radius: ${({ theme }) => theme.border.radius.sm};
color: ${({ disabled, theme }) =>
disabled ? theme.font.color.tertiary : theme.font.color.primary};
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
height: ${({ theme }) => theme.spacing(8)};
justify-content: space-between;
padding: 0 ${({ theme }) => theme.spacing(2)};
`;
const StyledControlLabel = styled.div`
align-items: center;
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
`;
const StyledIconChevronDown = styled(IconChevronDown)<{
disabled?: boolean;
}>`
color: ${({ disabled, theme }) =>
disabled ? theme.font.color.extraLight : theme.font.color.tertiary};
`;
type SelectControlProps = {
selectedOption: SelectOption<string | number | null>;
isDisabled?: boolean;
};
export const SelectControl = ({
selectedOption,
isDisabled,
}: SelectControlProps) => {
const theme = useTheme();
return (
<StyledControlContainer disabled={isDisabled}>
<StyledControlLabel>
{!!selectedOption?.Icon && (
<selectedOption.Icon
color={
isDisabled ? theme.font.color.light : theme.font.color.primary
}
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
)}
{selectedOption?.label}
</StyledControlLabel>
<StyledIconChevronDown disabled={isDisabled} size={theme.icon.size.md} />
</StyledControlContainer>
);
};