feature to reset value in select field (#6067)
Fixes #6064 https://github.com/twentyhq/twenty/assets/55168611/8c553422-6ad2-4e6b-bd00-962dd81c0a93 --------- Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
@ -2,6 +2,7 @@ import { useContext } from 'react';
|
|||||||
import { useRecoilCallback } from 'recoil';
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||||
|
import { useSetRecordFieldValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
|
||||||
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
||||||
import { generateEmptyFieldValue } from '@/object-record/utils/generateEmptyFieldValue';
|
import { generateEmptyFieldValue } from '@/object-record/utils/generateEmptyFieldValue';
|
||||||
|
|
||||||
@ -16,6 +17,8 @@ export const useClearField = () => {
|
|||||||
|
|
||||||
const [updateRecord] = useUpdateRecord();
|
const [updateRecord] = useUpdateRecord();
|
||||||
|
|
||||||
|
const setRecordFieldValue = useSetRecordFieldValue();
|
||||||
|
|
||||||
const clearField = useRecoilCallback(
|
const clearField = useRecoilCallback(
|
||||||
({ snapshot, set }) =>
|
({ snapshot, set }) =>
|
||||||
() => {
|
() => {
|
||||||
@ -46,6 +49,8 @@ export const useClearField = () => {
|
|||||||
emptyFieldValue,
|
emptyFieldValue,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
setRecordFieldValue(entityId, fieldName, emptyFieldValue);
|
||||||
|
|
||||||
updateRecord?.({
|
updateRecord?.({
|
||||||
variables: {
|
variables: {
|
||||||
where: { id: entityId },
|
where: { id: entityId },
|
||||||
@ -55,7 +60,7 @@ export const useClearField = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[entityId, fieldDefinition, updateRecord],
|
[entityId, fieldDefinition, updateRecord, setRecordFieldValue],
|
||||||
);
|
);
|
||||||
|
|
||||||
return clearField;
|
return clearField;
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import React, { useRef, useState } from 'react';
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { Key } from 'ts-key-enum';
|
import { Key } from 'ts-key-enum';
|
||||||
|
|
||||||
|
import { useClearField } from '@/object-record/record-field/hooks/useClearField';
|
||||||
import { useSelectField } from '@/object-record/record-field/meta-types/hooks/useSelectField';
|
import { useSelectField } from '@/object-record/record-field/meta-types/hooks/useSelectField';
|
||||||
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
|
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
|
||||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||||
@ -30,12 +31,15 @@ export const SelectFieldInput = ({
|
|||||||
}: SelectFieldInputProps) => {
|
}: SelectFieldInputProps) => {
|
||||||
const { persistField, fieldDefinition, fieldValue, hotkeyScope } =
|
const { persistField, fieldDefinition, fieldValue, hotkeyScope } =
|
||||||
useSelectField();
|
useSelectField();
|
||||||
|
const clearField = useClearField();
|
||||||
|
|
||||||
const [searchFilter, setSearchFilter] = useState('');
|
const [searchFilter, setSearchFilter] = useState('');
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const selectedOption = fieldDefinition.metadata.options.find(
|
const selectedOption = fieldDefinition.metadata.options.find(
|
||||||
(option) => option.value === fieldValue,
|
(option) => option.value === fieldValue,
|
||||||
);
|
);
|
||||||
|
|
||||||
const optionsToSelect =
|
const optionsToSelect =
|
||||||
fieldDefinition.metadata.options.filter((option) => {
|
fieldDefinition.metadata.options.filter((option) => {
|
||||||
return (
|
return (
|
||||||
@ -43,10 +47,17 @@ export const SelectFieldInput = ({
|
|||||||
option.label.toLowerCase().includes(searchFilter.toLowerCase())
|
option.label.toLowerCase().includes(searchFilter.toLowerCase())
|
||||||
);
|
);
|
||||||
}) || [];
|
}) || [];
|
||||||
|
|
||||||
const optionsInDropDown = selectedOption
|
const optionsInDropDown = selectedOption
|
||||||
? [selectedOption, ...optionsToSelect]
|
? [selectedOption, ...optionsToSelect]
|
||||||
: optionsToSelect;
|
: optionsToSelect;
|
||||||
|
|
||||||
|
// handlers
|
||||||
|
const handleClearField = () => {
|
||||||
|
clearField();
|
||||||
|
onCancel?.();
|
||||||
|
};
|
||||||
|
|
||||||
useListenClickOutside({
|
useListenClickOutside({
|
||||||
refs: [containerRef],
|
refs: [containerRef],
|
||||||
callback: (event) => {
|
callback: (event) => {
|
||||||
@ -85,7 +96,17 @@ export const SelectFieldInput = ({
|
|||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
|
|
||||||
<DropdownMenuItemsContainer hasMaxHeight>
|
<DropdownMenuItemsContainer hasMaxHeight>
|
||||||
|
<MenuItemSelectTag
|
||||||
|
key={`No ${fieldDefinition.label}`}
|
||||||
|
selected={false}
|
||||||
|
text={`No ${fieldDefinition.label}`}
|
||||||
|
color="transparent"
|
||||||
|
variant="outline"
|
||||||
|
onClick={handleClearField}
|
||||||
|
/>
|
||||||
|
|
||||||
{optionsInDropDown.map((option) => {
|
{optionsInDropDown.map((option) => {
|
||||||
return (
|
return (
|
||||||
<MenuItemSelectTag
|
<MenuItemSelectTag
|
||||||
|
|||||||
@ -9,8 +9,9 @@ type MenuItemSelectTagProps = {
|
|||||||
selected: boolean;
|
selected: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
color: ThemeColor;
|
color: ThemeColor | 'transparent';
|
||||||
text: string;
|
text: string;
|
||||||
|
variant?: 'solid' | 'outline';
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MenuItemSelectTag = ({
|
export const MenuItemSelectTag = ({
|
||||||
@ -19,6 +20,7 @@ export const MenuItemSelectTag = ({
|
|||||||
className,
|
className,
|
||||||
onClick,
|
onClick,
|
||||||
text,
|
text,
|
||||||
|
variant = 'solid',
|
||||||
}: MenuItemSelectTagProps) => {
|
}: MenuItemSelectTagProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
@ -29,7 +31,7 @@ export const MenuItemSelectTag = ({
|
|||||||
selected={selected}
|
selected={selected}
|
||||||
>
|
>
|
||||||
<StyledMenuItemLeftContent>
|
<StyledMenuItemLeftContent>
|
||||||
<Tag color={color} text={text} />
|
<Tag variant={variant} color={color} text={text} />
|
||||||
</StyledMenuItemLeftContent>
|
</StyledMenuItemLeftContent>
|
||||||
{selected && <IconCheck size={theme.icon.size.sm} />}
|
{selected && <IconCheck size={theme.icon.size.sm} />}
|
||||||
</StyledMenuItemSelect>
|
</StyledMenuItemSelect>
|
||||||
|
|||||||
@ -16,14 +16,17 @@ const spacing1 = THEME_COMMON.spacing(1);
|
|||||||
|
|
||||||
const StyledTag = styled.h3<{
|
const StyledTag = styled.h3<{
|
||||||
theme: ThemeType;
|
theme: ThemeType;
|
||||||
color: ThemeColor;
|
color: TagColor;
|
||||||
weight: TagWeight;
|
weight: TagWeight;
|
||||||
|
variant: TagVariant;
|
||||||
preventShrink?: boolean;
|
preventShrink?: boolean;
|
||||||
}>`
|
}>`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: ${({ color, theme }) => theme.tag.background[color]};
|
background: ${({ color, theme }) =>
|
||||||
|
color === 'transparent' ? color : theme.tag.background[color]};
|
||||||
border-radius: ${BORDER_COMMON.radius.sm};
|
border-radius: ${BORDER_COMMON.radius.sm};
|
||||||
color: ${({ color, theme }) => theme.tag.text[color]};
|
color: ${({ color, theme }) =>
|
||||||
|
color === 'transparent' ? theme.tag.text['gray'] : theme.tag.text[color]};
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
font-size: ${({ theme }) => theme.font.size.md};
|
font-size: ${({ theme }) => theme.font.size.md};
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
@ -35,6 +38,8 @@ const StyledTag = styled.h3<{
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 0 ${spacing2};
|
padding: 0 ${spacing2};
|
||||||
|
border: ${({ variant, theme }) =>
|
||||||
|
variant === 'outline' ? `2px dashed ${theme.tag.background['gray']}` : ''};
|
||||||
|
|
||||||
gap: ${spacing1};
|
gap: ${spacing1};
|
||||||
|
|
||||||
@ -58,14 +63,17 @@ const StyledIconContainer = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
type TagWeight = 'regular' | 'medium';
|
type TagWeight = 'regular' | 'medium';
|
||||||
|
type TagVariant = 'solid' | 'outline';
|
||||||
|
type TagColor = ThemeColor | 'transparent';
|
||||||
|
|
||||||
type TagProps = {
|
type TagProps = {
|
||||||
className?: string;
|
className?: string;
|
||||||
color: ThemeColor;
|
color: TagColor;
|
||||||
text: string;
|
text: string;
|
||||||
Icon?: IconComponent;
|
Icon?: IconComponent;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
weight?: TagWeight;
|
weight?: TagWeight;
|
||||||
|
variant?: TagVariant;
|
||||||
preventShrink?: boolean;
|
preventShrink?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -77,6 +85,7 @@ export const Tag = ({
|
|||||||
Icon,
|
Icon,
|
||||||
onClick,
|
onClick,
|
||||||
weight = 'regular',
|
weight = 'regular',
|
||||||
|
variant = 'solid',
|
||||||
preventShrink,
|
preventShrink,
|
||||||
}: TagProps) => {
|
}: TagProps) => {
|
||||||
const { theme } = useContext(ThemeContext);
|
const { theme } = useContext(ThemeContext);
|
||||||
@ -88,6 +97,7 @@ export const Tag = ({
|
|||||||
color={color}
|
color={color}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
weight={weight}
|
weight={weight}
|
||||||
|
variant={variant}
|
||||||
preventShrink={preventShrink}
|
preventShrink={preventShrink}
|
||||||
>
|
>
|
||||||
{!!Icon && (
|
{!!Icon && (
|
||||||
|
|||||||
Reference in New Issue
Block a user