4778 multi select field front implement multi select type (#4887)
This commit is contained in:
@ -15,18 +15,20 @@ export const getRecordFromRecordNode = <T extends ObjectRecord>({
|
||||
return [fieldName, value];
|
||||
}
|
||||
|
||||
if (typeof value === 'object' && isDefined(value.edges)) {
|
||||
return [
|
||||
fieldName,
|
||||
getRecordsFromRecordConnection({ recordConnection: value }),
|
||||
];
|
||||
if (Array.isArray(value)) {
|
||||
return [fieldName, value];
|
||||
}
|
||||
|
||||
if (typeof value === 'object' && !isDefined(value.edges)) {
|
||||
return [fieldName, getRecordFromRecordNode<T>({ recordNode: value })];
|
||||
if (typeof value !== 'object') {
|
||||
return [fieldName, value];
|
||||
}
|
||||
|
||||
return [fieldName, value];
|
||||
return isDefined(value.edges)
|
||||
? [
|
||||
fieldName,
|
||||
getRecordsFromRecordConnection({ recordConnection: value }),
|
||||
]
|
||||
: [fieldName, getRecordFromRecordNode<T>({ recordNode: value })];
|
||||
}),
|
||||
),
|
||||
id: recordNode.id,
|
||||
|
||||
@ -6,7 +6,10 @@ import { getNodeTypename } from '@/object-record/cache/utils/getNodeTypename';
|
||||
import { getObjectTypename } from '@/object-record/cache/utils/getObjectTypename';
|
||||
import { getRecordConnectionFromRecords } from '@/object-record/cache/utils/getRecordConnectionFromRecords';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import {
|
||||
FieldMetadataType,
|
||||
RelationDefinitionType,
|
||||
} from '~/generated-metadata/graphql';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
import { lowerAndCapitalize } from '~/utils/string/lowerAndCapitalize';
|
||||
|
||||
@ -65,12 +68,16 @@ export const getRecordNodeFromRecord = <T extends ObjectRecord>({
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
const objectMetadataItem = objectMetadataItems.find(
|
||||
(objectMetadataItem) => objectMetadataItem.namePlural === fieldName,
|
||||
if (
|
||||
field.type === FieldMetadataType.Relation &&
|
||||
field.relationDefinition?.direction ===
|
||||
RelationDefinitionType.OneToMany
|
||||
) {
|
||||
const oneToManyObjectMetadataItem = objectMetadataItems.find(
|
||||
(item) => item.namePlural === fieldName,
|
||||
);
|
||||
|
||||
if (!objectMetadataItem) {
|
||||
if (!oneToManyObjectMetadataItem) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -78,7 +85,7 @@ export const getRecordNodeFromRecord = <T extends ObjectRecord>({
|
||||
fieldName,
|
||||
getRecordConnectionFromRecords({
|
||||
objectMetadataItems,
|
||||
objectMetadataItem: objectMetadataItem,
|
||||
objectMetadataItem: oneToManyObjectMetadataItem,
|
||||
records: value as ObjectRecord[],
|
||||
queryFields:
|
||||
queryFields?.[fieldName] === true ||
|
||||
|
||||
@ -9,4 +9,5 @@ export type FilterType =
|
||||
| 'LINK'
|
||||
| 'RELATION'
|
||||
| 'ADDRESS'
|
||||
| 'SELECT';
|
||||
| 'SELECT'
|
||||
| 'MULTI_SELECT';
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { JsonFieldDisplay } from '@/object-record/record-field/meta-types/display/components/JsonFieldDisplay';
|
||||
import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson';
|
||||
|
||||
import { FieldContext } from '../contexts/FieldContext';
|
||||
import { AddressFieldDisplay } from '../meta-types/display/components/AddressFieldDisplay';
|
||||
import { ChipFieldDisplay } from '../meta-types/display/components/ChipFieldDisplay';
|
||||
@ -10,7 +7,9 @@ import { CurrencyFieldDisplay } from '../meta-types/display/components/CurrencyF
|
||||
import { DateFieldDisplay } from '../meta-types/display/components/DateFieldDisplay';
|
||||
import { EmailFieldDisplay } from '../meta-types/display/components/EmailFieldDisplay';
|
||||
import { FullNameFieldDisplay } from '../meta-types/display/components/FullNameFieldDisplay';
|
||||
import { JsonFieldDisplay } from '../meta-types/display/components/JsonFieldDisplay';
|
||||
import { LinkFieldDisplay } from '../meta-types/display/components/LinkFieldDisplay';
|
||||
import { MultiSelectFieldDisplay } from '../meta-types/display/components/MultiSelectFieldDisplay.tsx';
|
||||
import { NumberFieldDisplay } from '../meta-types/display/components/NumberFieldDisplay';
|
||||
import { PhoneFieldDisplay } from '../meta-types/display/components/PhoneFieldDisplay';
|
||||
import { RelationFieldDisplay } from '../meta-types/display/components/RelationFieldDisplay';
|
||||
@ -23,8 +22,10 @@ import { isFieldDateTime } from '../types/guards/isFieldDateTime';
|
||||
import { isFieldEmail } from '../types/guards/isFieldEmail';
|
||||
import { isFieldFullName } from '../types/guards/isFieldFullName';
|
||||
import { isFieldLink } from '../types/guards/isFieldLink';
|
||||
import { isFieldMultiSelect } from '../types/guards/isFieldMultiSelect.ts';
|
||||
import { isFieldNumber } from '../types/guards/isFieldNumber';
|
||||
import { isFieldPhone } from '../types/guards/isFieldPhone';
|
||||
import { isFieldRawJson } from '../types/guards/isFieldRawJson';
|
||||
import { isFieldRelation } from '../types/guards/isFieldRelation';
|
||||
import { isFieldSelect } from '../types/guards/isFieldSelect';
|
||||
import { isFieldText } from '../types/guards/isFieldText';
|
||||
@ -60,6 +61,8 @@ export const FieldDisplay = () => {
|
||||
<PhoneFieldDisplay />
|
||||
) : isFieldSelect(fieldDefinition) ? (
|
||||
<SelectFieldDisplay />
|
||||
) : isFieldMultiSelect(fieldDefinition) ? (
|
||||
<MultiSelectFieldDisplay />
|
||||
) : isFieldAddress(fieldDefinition) ? (
|
||||
<AddressFieldDisplay />
|
||||
) : isFieldRawJson(fieldDefinition) ? (
|
||||
|
||||
@ -2,10 +2,12 @@ import { useContext } from 'react';
|
||||
|
||||
import { AddressFieldInput } from '@/object-record/record-field/meta-types/input/components/AddressFieldInput';
|
||||
import { FullNameFieldInput } from '@/object-record/record-field/meta-types/input/components/FullNameFieldInput';
|
||||
import { MultiSelectFieldInput } from '@/object-record/record-field/meta-types/input/components/MultiSelectFieldInput.tsx';
|
||||
import { RawJsonFieldInput } from '@/object-record/record-field/meta-types/input/components/RawJsonFieldInput';
|
||||
import { SelectFieldInput } from '@/object-record/record-field/meta-types/input/components/SelectFieldInput';
|
||||
import { RecordFieldInputScope } from '@/object-record/record-field/scopes/RecordFieldInputScope';
|
||||
import { isFieldFullName } from '@/object-record/record-field/types/guards/isFieldFullName';
|
||||
import { isFieldMultiSelect } from '@/object-record/record-field/types/guards/isFieldMultiSelect.ts';
|
||||
import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson';
|
||||
import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect';
|
||||
import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId';
|
||||
@ -131,6 +133,8 @@ export const FieldInput = ({
|
||||
<RatingFieldInput onSubmit={onSubmit} />
|
||||
) : isFieldSelect(fieldDefinition) ? (
|
||||
<SelectFieldInput onSubmit={onSubmit} onCancel={onCancel} />
|
||||
) : isFieldMultiSelect(fieldDefinition) ? (
|
||||
<MultiSelectFieldInput onCancel={onCancel} />
|
||||
) : isFieldAddress(fieldDefinition) ? (
|
||||
<AddressFieldInput
|
||||
onEnter={onEnter}
|
||||
|
||||
@ -5,6 +5,8 @@ import { isFieldAddress } from '@/object-record/record-field/types/guards/isFiel
|
||||
import { isFieldAddressValue } from '@/object-record/record-field/types/guards/isFieldAddressValue';
|
||||
import { isFieldFullName } from '@/object-record/record-field/types/guards/isFieldFullName';
|
||||
import { isFieldFullNameValue } from '@/object-record/record-field/types/guards/isFieldFullNameValue';
|
||||
import { isFieldMultiSelect } from '@/object-record/record-field/types/guards/isFieldMultiSelect.ts';
|
||||
import { isFieldMultiSelectValue } from '@/object-record/record-field/types/guards/isFieldMultiSelectValue.ts';
|
||||
import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson';
|
||||
import { isFieldRawJsonValue } from '@/object-record/record-field/types/guards/isFieldRawJsonValue';
|
||||
import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect';
|
||||
@ -86,6 +88,10 @@ export const usePersistField = () => {
|
||||
const fieldIsSelect =
|
||||
isFieldSelect(fieldDefinition) && isFieldSelectValue(valueToPersist);
|
||||
|
||||
const fieldIsMultiSelect =
|
||||
isFieldMultiSelect(fieldDefinition) &&
|
||||
isFieldMultiSelectValue(valueToPersist);
|
||||
|
||||
const fieldIsAddress =
|
||||
isFieldAddress(fieldDefinition) &&
|
||||
isFieldAddressValue(valueToPersist);
|
||||
@ -94,7 +100,7 @@ export const usePersistField = () => {
|
||||
isFieldRawJson(fieldDefinition) &&
|
||||
isFieldRawJsonValue(valueToPersist);
|
||||
|
||||
if (
|
||||
const isValuePersistable =
|
||||
fieldIsRelation ||
|
||||
fieldIsText ||
|
||||
fieldIsBoolean ||
|
||||
@ -107,9 +113,11 @@ export const usePersistField = () => {
|
||||
fieldIsCurrency ||
|
||||
fieldIsFullName ||
|
||||
fieldIsSelect ||
|
||||
fieldIsMultiSelect ||
|
||||
fieldIsAddress ||
|
||||
fieldIsRawJson
|
||||
) {
|
||||
fieldIsRawJson;
|
||||
|
||||
if (isValuePersistable === true) {
|
||||
const fieldName = fieldDefinition.metadata.fieldName;
|
||||
set(
|
||||
recordStoreFamilySelector({ recordId: entityId, fieldName }),
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { useMultiSelectField } from '@/object-record/record-field/meta-types/hooks/useMultiSelectField.ts';
|
||||
import { Tag } from '@/ui/display/tag/components/Tag';
|
||||
|
||||
const StyledTagContainer = styled.div`
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
export const MultiSelectFieldDisplay = () => {
|
||||
const { fieldValues, fieldDefinition } = useMultiSelectField();
|
||||
|
||||
const selectedOptions = fieldValues
|
||||
? fieldDefinition.metadata.options.filter((option) =>
|
||||
fieldValues.includes(option.value),
|
||||
)
|
||||
: [];
|
||||
|
||||
return selectedOptions ? (
|
||||
<StyledTagContainer>
|
||||
{selectedOptions.map((selectedOption, index) => (
|
||||
<Tag
|
||||
key={index}
|
||||
color={selectedOption.color}
|
||||
text={selectedOption.label}
|
||||
/>
|
||||
))}
|
||||
</StyledTagContainer>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,50 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext.ts';
|
||||
import { usePersistField } from '@/object-record/record-field/hooks/usePersistField.ts';
|
||||
import { useRecordFieldInput } from '@/object-record/record-field/hooks/useRecordFieldInput.ts';
|
||||
import { FieldMultiSelectValue } from '@/object-record/record-field/types/FieldMetadata.ts';
|
||||
import { assertFieldMetadata } from '@/object-record/record-field/types/guards/assertFieldMetadata.ts';
|
||||
import { isFieldMultiSelect } from '@/object-record/record-field/types/guards/isFieldMultiSelect.ts';
|
||||
import { isFieldMultiSelectValue } from '@/object-record/record-field/types/guards/isFieldMultiSelectValue.ts';
|
||||
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector.ts';
|
||||
import { FieldMetadataType } from '~/generated/graphql.tsx';
|
||||
|
||||
export const useMultiSelectField = () => {
|
||||
const { entityId, fieldDefinition, hotkeyScope } = useContext(FieldContext);
|
||||
|
||||
assertFieldMetadata(
|
||||
FieldMetadataType.MultiSelect,
|
||||
isFieldMultiSelect,
|
||||
fieldDefinition,
|
||||
);
|
||||
|
||||
const { fieldName } = fieldDefinition.metadata;
|
||||
|
||||
const [fieldValues, setFieldValue] = useRecoilState<FieldMultiSelectValue>(
|
||||
recordStoreFamilySelector({
|
||||
recordId: entityId,
|
||||
fieldName: fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const fieldMultiSelectValues = isFieldMultiSelectValue(fieldValues)
|
||||
? fieldValues
|
||||
: null;
|
||||
const persistField = usePersistField();
|
||||
|
||||
const { setDraftValue, getDraftValueSelector } =
|
||||
useRecordFieldInput<FieldMultiSelectValue>(`${entityId}-${fieldName}`);
|
||||
const draftValue = useRecoilValue(getDraftValueSelector());
|
||||
|
||||
return {
|
||||
fieldDefinition,
|
||||
persistField,
|
||||
fieldValues: fieldMultiSelectValues,
|
||||
draftValue,
|
||||
setDraftValue,
|
||||
setFieldValue,
|
||||
hotkeyScope,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,93 @@
|
||||
import { useRef, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { useMultiSelectField } from '@/object-record/record-field/meta-types/hooks/useMultiSelectField.ts';
|
||||
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
|
||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { MenuItemMultiSelectTag } from '@/ui/navigation/menu-item/components/MenuItemMultiSelectTag.tsx';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
const StyledRelationPickerContainer = styled.div`
|
||||
left: -1px;
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
`;
|
||||
|
||||
export type MultiSelectFieldInputProps = {
|
||||
onSubmit?: FieldInputEvent;
|
||||
onCancel?: () => void;
|
||||
};
|
||||
|
||||
export const MultiSelectFieldInput = ({
|
||||
onCancel,
|
||||
}: MultiSelectFieldInputProps) => {
|
||||
const { persistField, fieldDefinition, fieldValues } = useMultiSelectField();
|
||||
const [searchFilter, setSearchFilter] = useState('');
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const selectedOptions = fieldDefinition.metadata.options.filter(
|
||||
(option) => fieldValues?.includes(option.value),
|
||||
);
|
||||
|
||||
const optionsInDropDown = fieldDefinition.metadata.options;
|
||||
|
||||
const formatNewSelectedOptions = (value: string) => {
|
||||
const selectedOptionsValues = selectedOptions.map(
|
||||
(selectedOption) => selectedOption.value,
|
||||
);
|
||||
if (!selectedOptionsValues.includes(value)) {
|
||||
return [value, ...selectedOptionsValues];
|
||||
} else {
|
||||
return selectedOptionsValues.filter(
|
||||
(selectedOptionsValue) => selectedOptionsValue !== value,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
useListenClickOutside({
|
||||
refs: [containerRef],
|
||||
callback: (event) => {
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
const weAreNotInAnHTMLInput = !(
|
||||
event.target instanceof HTMLInputElement &&
|
||||
event.target.tagName === 'INPUT'
|
||||
);
|
||||
if (weAreNotInAnHTMLInput && isDefined(onCancel)) {
|
||||
onCancel();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<StyledRelationPickerContainer ref={containerRef}>
|
||||
<DropdownMenu data-select-disable>
|
||||
<DropdownMenuSearchInput
|
||||
value={searchFilter}
|
||||
onChange={(event) => setSearchFilter(event.currentTarget.value)}
|
||||
autoFocus
|
||||
/>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer hasMaxHeight>
|
||||
{optionsInDropDown.map((option) => {
|
||||
return (
|
||||
<MenuItemMultiSelectTag
|
||||
key={option.value}
|
||||
selected={fieldValues?.includes(option.value) || false}
|
||||
text={option.label}
|
||||
color={option.color}
|
||||
onClick={() =>
|
||||
persistField(formatNewSelectedOptions(option.value))
|
||||
}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownMenu>
|
||||
</StyledRelationPickerContainer>
|
||||
);
|
||||
};
|
||||
@ -7,6 +7,7 @@ import {
|
||||
FieldEmailValue,
|
||||
FieldFullNameValue,
|
||||
FieldLinkValue,
|
||||
FieldMultiSelectValue,
|
||||
FieldNumberValue,
|
||||
FieldPhoneValue,
|
||||
FieldRatingValue,
|
||||
@ -22,6 +23,7 @@ export type FieldDateTimeDraftValue = string;
|
||||
export type FieldPhoneDraftValue = string;
|
||||
export type FieldEmailDraftValue = string;
|
||||
export type FieldSelectDraftValue = string;
|
||||
export type FieldMultiSelectDraftValue = string[];
|
||||
export type FieldRelationDraftValue = string;
|
||||
export type FieldLinkDraftValue = { url: string; label: string };
|
||||
export type FieldCurrencyDraftValue = {
|
||||
@ -64,8 +66,10 @@ export type FieldInputDraftValue<FieldValue> = FieldValue extends FieldTextValue
|
||||
? FieldRatingValue
|
||||
: FieldValue extends FieldSelectValue
|
||||
? FieldSelectDraftValue
|
||||
: FieldValue extends FieldRelationValue
|
||||
? FieldRelationDraftValue
|
||||
: FieldValue extends FieldAddressValue
|
||||
? FieldAddressDraftValue
|
||||
: never;
|
||||
: FieldValue extends FieldMultiSelectValue
|
||||
? FieldMultiSelectDraftValue
|
||||
: FieldValue extends FieldRelationValue
|
||||
? FieldRelationDraftValue
|
||||
: FieldValue extends FieldAddressValue
|
||||
? FieldAddressDraftValue
|
||||
: never;
|
||||
|
||||
@ -103,6 +103,12 @@ export type FieldSelectMetadata = {
|
||||
options: { label: string; color: ThemeColor; value: string }[];
|
||||
};
|
||||
|
||||
export type FieldMultiSelectMetadata = {
|
||||
objectMetadataNameSingular?: string;
|
||||
fieldName: string;
|
||||
options: { label: string; color: ThemeColor; value: string }[];
|
||||
};
|
||||
|
||||
export type FieldMetadata =
|
||||
| FieldBooleanMetadata
|
||||
| FieldCurrencyMetadata
|
||||
@ -115,6 +121,7 @@ export type FieldMetadata =
|
||||
| FieldRatingMetadata
|
||||
| FieldRelationMetadata
|
||||
| FieldSelectMetadata
|
||||
| FieldMultiSelectMetadata
|
||||
| FieldTextMetadata
|
||||
| FieldUuidMetadata
|
||||
| FieldAddressMetadata;
|
||||
@ -145,6 +152,7 @@ export type FieldAddressValue = {
|
||||
};
|
||||
export type FieldRatingValue = (typeof RATING_VALUES)[number];
|
||||
export type FieldSelectValue = string | null;
|
||||
export type FieldMultiSelectValue = string[] | null;
|
||||
|
||||
export type FieldRelationValue = EntityForSelect | null;
|
||||
export type FieldJsonValue = string;
|
||||
|
||||
@ -10,6 +10,7 @@ import {
|
||||
FieldFullNameMetadata,
|
||||
FieldLinkMetadata,
|
||||
FieldMetadata,
|
||||
FieldMultiSelectMetadata,
|
||||
FieldNumberMetadata,
|
||||
FieldPhoneMetadata,
|
||||
FieldRatingMetadata,
|
||||
@ -34,27 +35,29 @@ type AssertFieldMetadataFunction = <
|
||||
? FieldEmailMetadata
|
||||
: E extends 'SELECT'
|
||||
? FieldSelectMetadata
|
||||
: E extends 'RATING'
|
||||
? FieldRatingMetadata
|
||||
: E extends 'LINK'
|
||||
? FieldLinkMetadata
|
||||
: E extends 'NUMBER'
|
||||
? FieldNumberMetadata
|
||||
: E extends 'PHONE'
|
||||
? FieldPhoneMetadata
|
||||
: E extends 'PROBABILITY'
|
||||
? FieldRatingMetadata
|
||||
: E extends 'RELATION'
|
||||
? FieldRelationMetadata
|
||||
: E extends 'TEXT'
|
||||
? FieldTextMetadata
|
||||
: E extends 'UUID'
|
||||
? FieldUuidMetadata
|
||||
: E extends 'ADDRESS'
|
||||
? FieldAddressMetadata
|
||||
: E extends 'RAW_JSON'
|
||||
? FieldRawJsonMetadata
|
||||
: never,
|
||||
: E extends 'MULTI_SELECT'
|
||||
? FieldMultiSelectMetadata
|
||||
: E extends 'RATING'
|
||||
? FieldRatingMetadata
|
||||
: E extends 'LINK'
|
||||
? FieldLinkMetadata
|
||||
: E extends 'NUMBER'
|
||||
? FieldNumberMetadata
|
||||
: E extends 'PHONE'
|
||||
? FieldPhoneMetadata
|
||||
: E extends 'PROBABILITY'
|
||||
? FieldRatingMetadata
|
||||
: E extends 'RELATION'
|
||||
? FieldRelationMetadata
|
||||
: E extends 'TEXT'
|
||||
? FieldTextMetadata
|
||||
: E extends 'UUID'
|
||||
? FieldUuidMetadata
|
||||
: E extends 'ADDRESS'
|
||||
? FieldAddressMetadata
|
||||
: E extends 'RAW_JSON'
|
||||
? FieldRawJsonMetadata
|
||||
: never,
|
||||
>(
|
||||
fieldType: E,
|
||||
fieldTypeGuard: (
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition.ts';
|
||||
import {
|
||||
FieldMetadata,
|
||||
FieldMultiSelectMetadata,
|
||||
} from '@/object-record/record-field/types/FieldMetadata.ts';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql.ts';
|
||||
|
||||
export const isFieldMultiSelect = (
|
||||
field: Pick<FieldDefinition<FieldMetadata>, 'type'>,
|
||||
): field is FieldDefinition<FieldMultiSelectMetadata> =>
|
||||
field.type === FieldMetadataType.MultiSelect;
|
||||
@ -0,0 +1,9 @@
|
||||
import { FieldMultiSelectValue } from '@/object-record/record-field/types/FieldMetadata.ts';
|
||||
import { multiSelectFieldValueSchema } from '@/object-record/record-field/validation-schemas/multiSelectFieldValueSchema.ts';
|
||||
|
||||
export const isFieldMultiSelectValue = (
|
||||
fieldValue: unknown,
|
||||
options?: string[],
|
||||
): fieldValue is FieldMultiSelectValue => {
|
||||
return multiSelectFieldValueSchema(options).safeParse(fieldValue).success;
|
||||
};
|
||||
@ -11,6 +11,8 @@ import { isFieldFullName } from '@/object-record/record-field/types/guards/isFie
|
||||
import { isFieldFullNameValue } from '@/object-record/record-field/types/guards/isFieldFullNameValue';
|
||||
import { isFieldLink } from '@/object-record/record-field/types/guards/isFieldLink';
|
||||
import { isFieldLinkValue } from '@/object-record/record-field/types/guards/isFieldLinkValue';
|
||||
import { isFieldMultiSelect } from '@/object-record/record-field/types/guards/isFieldMultiSelect.ts';
|
||||
import { isFieldMultiSelectValue } from '@/object-record/record-field/types/guards/isFieldMultiSelectValue.ts';
|
||||
import { isFieldNumber } from '@/object-record/record-field/types/guards/isFieldNumber';
|
||||
import { isFieldRating } from '@/object-record/record-field/types/guards/isFieldRating';
|
||||
import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson';
|
||||
@ -54,6 +56,13 @@ export const isFieldValueEmpty = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (isFieldMultiSelect(fieldDefinition)) {
|
||||
return (
|
||||
!isFieldMultiSelectValue(fieldValue, selectOptionValues) ||
|
||||
!isDefined(fieldValue)
|
||||
);
|
||||
}
|
||||
|
||||
if (isFieldCurrency(fieldDefinition)) {
|
||||
return (
|
||||
!isFieldCurrencyValue(fieldValue) ||
|
||||
|
||||
@ -0,0 +1,10 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { FieldMultiSelectValue } from '@/object-record/record-field/types/FieldMetadata';
|
||||
|
||||
export const multiSelectFieldValueSchema = (
|
||||
options?: string[],
|
||||
): z.ZodType<FieldMultiSelectValue> =>
|
||||
options?.length
|
||||
? z.array(z.enum(options as [string, ...string[]])).nullable()
|
||||
: z.array(z.string()).nullable();
|
||||
@ -143,6 +143,7 @@ export const isRecordMatchingFilter = ({
|
||||
case FieldMetadataType.Email:
|
||||
case FieldMetadataType.Phone:
|
||||
case FieldMetadataType.Select:
|
||||
case FieldMetadataType.MultiSelect:
|
||||
case FieldMetadataType.Text: {
|
||||
return isMatchingStringFilter({
|
||||
stringFilter: filterValue as StringFilter,
|
||||
|
||||
@ -73,7 +73,7 @@ export const generateEmptyFieldValue = (
|
||||
return null;
|
||||
}
|
||||
case FieldMetadataType.MultiSelect: {
|
||||
throw new Error('Not implemented yet');
|
||||
return null;
|
||||
}
|
||||
case FieldMetadataType.RawJson: {
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user