FieldDisplay & FieldInput (#1708)
* Removed view field duplicate types * wip * wip 2 * wip 3 * Unified state for fields * Renaming * Wip * Post merge * Post post merge * wip * Delete unused file * Boolean and Probability * Finished InlineCell * Renamed EditableCell to TableCell * Finished double texts * Finished MoneyField * Fixed bug inline cell click outside * Fixed hotkey scope * Final fixes * Phone * Fix url and number input validation * Fix * Fix position * wip refactor activity editor * Fixed activity editor --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -1,27 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
|
||||
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
|
||||
import { FieldRecoilScopeContext } from '../states/recoil-scope-contexts/FieldRecoilScopeContext';
|
||||
import { FieldDefinition } from '../types/FieldDefinition';
|
||||
import { FieldBooleanMetadata } from '../types/FieldMetadata';
|
||||
|
||||
import { EditableField } from './EditableField';
|
||||
import { GenericEditableBooleanFieldDisplayMode } from './GenericEditableBooleanFieldDisplayMode';
|
||||
|
||||
export const GenericEditableBooleanField = () => {
|
||||
const currentEditableFieldDefinition = useContext(
|
||||
EditableFieldDefinitionContext,
|
||||
) as FieldDefinition<FieldBooleanMetadata>;
|
||||
|
||||
return (
|
||||
<RecoilScope CustomRecoilScopeContext={FieldRecoilScopeContext}>
|
||||
<EditableField
|
||||
IconLabel={currentEditableFieldDefinition.Icon}
|
||||
displayModeContent={<GenericEditableBooleanFieldDisplayMode />}
|
||||
displayModeContentOnly
|
||||
/>
|
||||
</RecoilScope>
|
||||
);
|
||||
};
|
||||
@ -1,44 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { BooleanInput } from '@/ui/input/components/BooleanInput';
|
||||
|
||||
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
|
||||
import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext';
|
||||
import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField';
|
||||
import { genericEntityFieldFamilySelector } from '../states/selectors/genericEntityFieldFamilySelector';
|
||||
import { FieldDefinition } from '../types/FieldDefinition';
|
||||
import { FieldBooleanMetadata } from '../types/FieldMetadata';
|
||||
|
||||
export const GenericEditableBooleanFieldDisplayMode = () => {
|
||||
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
|
||||
const currentEditableFieldDefinition = useContext(
|
||||
EditableFieldDefinitionContext,
|
||||
) as FieldDefinition<FieldBooleanMetadata>;
|
||||
|
||||
const [fieldValue, setFieldValue] = useRecoilState<boolean>(
|
||||
genericEntityFieldFamilySelector({
|
||||
entityId: currentEditableFieldEntityId ?? '',
|
||||
fieldName: currentEditableFieldDefinition
|
||||
? currentEditableFieldDefinition.metadata.fieldName
|
||||
: '',
|
||||
}),
|
||||
);
|
||||
|
||||
const updateField = useUpdateGenericEntityField();
|
||||
|
||||
const handleSubmit = (newValue: boolean) => {
|
||||
if (currentEditableFieldEntityId && updateField) {
|
||||
updateField(
|
||||
currentEditableFieldEntityId,
|
||||
currentEditableFieldDefinition,
|
||||
newValue,
|
||||
);
|
||||
|
||||
// TODO: use optimistic effect instead, but needs generic refactor
|
||||
setFieldValue(newValue);
|
||||
}
|
||||
};
|
||||
|
||||
return <BooleanInput value={fieldValue} onToggle={handleSubmit} />;
|
||||
};
|
||||
@ -1,42 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { DateDisplay } from '@/ui/content-display/components/DateDisplay';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
|
||||
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
|
||||
import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext';
|
||||
import { FieldRecoilScopeContext } from '../states/recoil-scope-contexts/FieldRecoilScopeContext';
|
||||
import { genericEntityFieldFamilySelector } from '../states/selectors/genericEntityFieldFamilySelector';
|
||||
import { FieldDefinition } from '../types/FieldDefinition';
|
||||
import { FieldDateMetadata } from '../types/FieldMetadata';
|
||||
|
||||
import { EditableField } from './EditableField';
|
||||
import { GenericEditableDateFieldEditMode } from './GenericEditableDateFieldEditMode';
|
||||
|
||||
export const GenericEditableDateField = () => {
|
||||
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
|
||||
const currentEditableFieldDefinition = useContext(
|
||||
EditableFieldDefinitionContext,
|
||||
) as FieldDefinition<FieldDateMetadata>;
|
||||
|
||||
const fieldValue = useRecoilValue<string>(
|
||||
genericEntityFieldFamilySelector({
|
||||
entityId: currentEditableFieldEntityId ?? '',
|
||||
fieldName: currentEditableFieldDefinition
|
||||
? currentEditableFieldDefinition.metadata.fieldName
|
||||
: '',
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<RecoilScope CustomRecoilScopeContext={FieldRecoilScopeContext}>
|
||||
<EditableField
|
||||
IconLabel={currentEditableFieldDefinition.Icon}
|
||||
editModeContent={<GenericEditableDateFieldEditMode />}
|
||||
displayModeContent={<DateDisplay value={fieldValue} />}
|
||||
isDisplayModeContentEmpty={!fieldValue}
|
||||
/>
|
||||
</RecoilScope>
|
||||
);
|
||||
};
|
||||
@ -1,76 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { DateInput } from '@/ui/input/components/DateInput';
|
||||
import { Nullable } from '~/types/Nullable';
|
||||
|
||||
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
|
||||
import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext';
|
||||
import { useFieldInputEventHandlers } from '../hooks/useFieldInputEventHandlers';
|
||||
import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField';
|
||||
import { genericEntityFieldFamilySelector } from '../states/selectors/genericEntityFieldFamilySelector';
|
||||
import { EditableFieldHotkeyScope } from '../types/EditableFieldHotkeyScope';
|
||||
import { FieldDefinition } from '../types/FieldDefinition';
|
||||
import { FieldDateMetadata } from '../types/FieldMetadata';
|
||||
|
||||
export const GenericEditableDateFieldEditMode = () => {
|
||||
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
|
||||
const currentEditableFieldDefinition = useContext(
|
||||
EditableFieldDefinitionContext,
|
||||
) as FieldDefinition<FieldDateMetadata>;
|
||||
|
||||
// TODO: we could use a hook that would return the field value with the right type
|
||||
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||
genericEntityFieldFamilySelector({
|
||||
entityId: currentEditableFieldEntityId ?? '',
|
||||
fieldName: currentEditableFieldDefinition
|
||||
? currentEditableFieldDefinition.metadata.fieldName
|
||||
: '',
|
||||
}),
|
||||
);
|
||||
|
||||
const updateField = useUpdateGenericEntityField();
|
||||
|
||||
const handleSubmit = (newDate: Nullable<Date>) => {
|
||||
if (!newDate) {
|
||||
setFieldValue('');
|
||||
|
||||
if (currentEditableFieldEntityId && updateField) {
|
||||
updateField(
|
||||
currentEditableFieldEntityId,
|
||||
currentEditableFieldDefinition,
|
||||
'',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const newDateISO = newDate?.toISOString();
|
||||
|
||||
if (newDateISO === fieldValue || !newDateISO) return;
|
||||
|
||||
setFieldValue(newDateISO);
|
||||
|
||||
if (currentEditableFieldEntityId && updateField) {
|
||||
updateField(
|
||||
currentEditableFieldEntityId,
|
||||
currentEditableFieldDefinition,
|
||||
newDateISO,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const { handleEnter, handleEscape, handleClickOutside } =
|
||||
useFieldInputEventHandlers({
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
return (
|
||||
<DateInput
|
||||
hotkeyScope={EditableFieldHotkeyScope.EditableField}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
value={fieldValue ? new Date(fieldValue) : null}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,47 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
|
||||
import { isFieldBoolean } from '../types/guards/isFieldBoolean';
|
||||
import { isFieldDate } from '../types/guards/isFieldDate';
|
||||
import { isFieldNumber } from '../types/guards/isFieldNumber';
|
||||
import { isFieldPhone } from '../types/guards/isFieldPhone';
|
||||
import { isFieldProbability } from '../types/guards/isFieldProbability';
|
||||
import { isFieldRelation } from '../types/guards/isFieldRelation';
|
||||
import { isFieldText } from '../types/guards/isFieldText';
|
||||
import { isFieldURL } from '../types/guards/isFieldURL';
|
||||
|
||||
import { GenericEditableBooleanField } from './GenericEditableBooleanField';
|
||||
import { GenericEditableDateField } from './GenericEditableDateField';
|
||||
import { GenericEditableNumberField } from './GenericEditableNumberField';
|
||||
import { GenericEditablePhoneField } from './GenericEditablePhoneField';
|
||||
import { GenericEditableRelationField } from './GenericEditableRelationField';
|
||||
import { GenericEditableTextField } from './GenericEditableTextField';
|
||||
import { GenericEditableURLField } from './GenericEditableURLField';
|
||||
import { ProbabilityEditableField } from './ProbabilityEditableField';
|
||||
|
||||
export const GenericEditableField = () => {
|
||||
const fieldDefinition = useContext(EditableFieldDefinitionContext);
|
||||
|
||||
if (isFieldRelation(fieldDefinition)) {
|
||||
return <GenericEditableRelationField />;
|
||||
} else if (isFieldDate(fieldDefinition)) {
|
||||
return <GenericEditableDateField />;
|
||||
} else if (isFieldNumber(fieldDefinition)) {
|
||||
return <GenericEditableNumberField />;
|
||||
} else if (isFieldProbability(fieldDefinition)) {
|
||||
return <ProbabilityEditableField />;
|
||||
} else if (isFieldURL(fieldDefinition)) {
|
||||
return <GenericEditableURLField />;
|
||||
} else if (isFieldText(fieldDefinition)) {
|
||||
return <GenericEditableTextField />;
|
||||
} else if (isFieldPhone(fieldDefinition)) {
|
||||
return <GenericEditablePhoneField />;
|
||||
} else if (isFieldBoolean(fieldDefinition)) {
|
||||
return <GenericEditableBooleanField />;
|
||||
} else {
|
||||
console.warn(
|
||||
`Unknown field metadata type: ${fieldDefinition.type} in GenericEditableField`,
|
||||
);
|
||||
return <></>;
|
||||
}
|
||||
};
|
||||
@ -1,42 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { NumberDisplay } from '@/ui/content-display/components/NumberDisplay';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
|
||||
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
|
||||
import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext';
|
||||
import { FieldRecoilScopeContext } from '../states/recoil-scope-contexts/FieldRecoilScopeContext';
|
||||
import { genericEntityFieldFamilySelector } from '../states/selectors/genericEntityFieldFamilySelector';
|
||||
import { FieldDefinition } from '../types/FieldDefinition';
|
||||
import { FieldNumberMetadata } from '../types/FieldMetadata';
|
||||
|
||||
import { EditableField } from './EditableField';
|
||||
import { GenericEditableNumberFieldEditMode } from './GenericEditableNumberFieldEditMode';
|
||||
|
||||
export const GenericEditableNumberField = () => {
|
||||
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
|
||||
const currentEditableFieldDefinition = useContext(
|
||||
EditableFieldDefinitionContext,
|
||||
) as FieldDefinition<FieldNumberMetadata>;
|
||||
|
||||
const fieldValue = useRecoilValue<string>(
|
||||
genericEntityFieldFamilySelector({
|
||||
entityId: currentEditableFieldEntityId ?? '',
|
||||
fieldName: currentEditableFieldDefinition
|
||||
? currentEditableFieldDefinition.metadata.fieldName
|
||||
: '',
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<RecoilScope CustomRecoilScopeContext={FieldRecoilScopeContext}>
|
||||
<EditableField
|
||||
IconLabel={currentEditableFieldDefinition.Icon}
|
||||
editModeContent={<GenericEditableNumberFieldEditMode />}
|
||||
displayModeContent={<NumberDisplay value={fieldValue} />}
|
||||
isDisplayModeContentEmpty={!fieldValue}
|
||||
/>
|
||||
</RecoilScope>
|
||||
);
|
||||
};
|
||||
@ -1,73 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import {
|
||||
canBeCastAsIntegerOrNull,
|
||||
castAsIntegerOrNull,
|
||||
} from '~/utils/cast-as-integer-or-null';
|
||||
|
||||
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
|
||||
import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext';
|
||||
import { useFieldInputEventHandlers } from '../hooks/useFieldInputEventHandlers';
|
||||
import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField';
|
||||
import { genericEntityFieldFamilySelector } from '../states/selectors/genericEntityFieldFamilySelector';
|
||||
import { EditableFieldHotkeyScope } from '../types/EditableFieldHotkeyScope';
|
||||
import { FieldDefinition } from '../types/FieldDefinition';
|
||||
import { FieldNumberMetadata } from '../types/FieldMetadata';
|
||||
|
||||
export const GenericEditableNumberFieldEditMode = () => {
|
||||
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
|
||||
const currentEditableFieldDefinition = useContext(
|
||||
EditableFieldDefinitionContext,
|
||||
) as FieldDefinition<FieldNumberMetadata>;
|
||||
|
||||
// TODO: we could use a hook that would return the field value with the right type
|
||||
const [fieldValue, setFieldValue] = useRecoilState<number | null>(
|
||||
genericEntityFieldFamilySelector({
|
||||
entityId: currentEditableFieldEntityId ?? '',
|
||||
fieldName: currentEditableFieldDefinition
|
||||
? currentEditableFieldDefinition.metadata.fieldName
|
||||
: '',
|
||||
}),
|
||||
);
|
||||
|
||||
const updateField = useUpdateGenericEntityField();
|
||||
|
||||
const handleSubmit = (newValue: string) => {
|
||||
if (!canBeCastAsIntegerOrNull(newValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newValue === fieldValue) return;
|
||||
|
||||
const castedValue = castAsIntegerOrNull(newValue);
|
||||
|
||||
setFieldValue(castedValue);
|
||||
|
||||
if (currentEditableFieldEntityId && updateField) {
|
||||
updateField(
|
||||
currentEditableFieldEntityId,
|
||||
currentEditableFieldDefinition,
|
||||
castedValue,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const { handleEnter, handleEscape, handleClickOutside } =
|
||||
useFieldInputEventHandlers({
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
return (
|
||||
<TextInput
|
||||
autoFocus
|
||||
placeholder={currentEditableFieldDefinition.metadata.placeHolder}
|
||||
hotkeyScope={EditableFieldHotkeyScope.EditableField}
|
||||
value={fieldValue ? fieldValue.toString() : ''}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,43 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { PhoneDisplay } from '@/ui/content-display/components/PhoneDisplay';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
|
||||
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
|
||||
import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext';
|
||||
import { FieldRecoilScopeContext } from '../states/recoil-scope-contexts/FieldRecoilScopeContext';
|
||||
import { genericEntityFieldFamilySelector } from '../states/selectors/genericEntityFieldFamilySelector';
|
||||
import { FieldDefinition } from '../types/FieldDefinition';
|
||||
import { FieldPhoneMetadata } from '../types/FieldMetadata';
|
||||
|
||||
import { EditableField } from './EditableField';
|
||||
import { GenericEditablePhoneFieldEditMode } from './GenericEditablePhoneFieldEditMode';
|
||||
|
||||
export const GenericEditablePhoneField = () => {
|
||||
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
|
||||
const currentEditableFieldDefinition = useContext(
|
||||
EditableFieldDefinitionContext,
|
||||
) as FieldDefinition<FieldPhoneMetadata>;
|
||||
|
||||
const fieldValue = useRecoilValue<string>(
|
||||
genericEntityFieldFamilySelector({
|
||||
entityId: currentEditableFieldEntityId ?? '',
|
||||
fieldName: currentEditableFieldDefinition
|
||||
? currentEditableFieldDefinition.metadata.fieldName
|
||||
: '',
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<RecoilScope CustomRecoilScopeContext={FieldRecoilScopeContext}>
|
||||
<EditableField
|
||||
useEditButton
|
||||
IconLabel={currentEditableFieldDefinition.Icon}
|
||||
editModeContent={<GenericEditablePhoneFieldEditMode />}
|
||||
displayModeContent={<PhoneDisplay value={fieldValue} />}
|
||||
isDisplayModeContentEmpty={!fieldValue}
|
||||
/>
|
||||
</RecoilScope>
|
||||
);
|
||||
};
|
||||
@ -1,62 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { isPossiblePhoneNumber } from 'react-phone-number-input';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { PhoneInput } from '@/ui/input/components/PhoneInput';
|
||||
|
||||
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
|
||||
import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext';
|
||||
import { useFieldInputEventHandlers } from '../hooks/useFieldInputEventHandlers';
|
||||
import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField';
|
||||
import { genericEntityFieldFamilySelector } from '../states/selectors/genericEntityFieldFamilySelector';
|
||||
import { EditableFieldHotkeyScope } from '../types/EditableFieldHotkeyScope';
|
||||
import { FieldDefinition } from '../types/FieldDefinition';
|
||||
import { FieldPhoneMetadata } from '../types/FieldMetadata';
|
||||
|
||||
export const GenericEditablePhoneFieldEditMode = () => {
|
||||
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
|
||||
const currentEditableFieldDefinition = useContext(
|
||||
EditableFieldDefinitionContext,
|
||||
) as FieldDefinition<FieldPhoneMetadata>;
|
||||
|
||||
// TODO: we could use a hook that would return the field value with the right type
|
||||
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||
genericEntityFieldFamilySelector({
|
||||
entityId: currentEditableFieldEntityId ?? '',
|
||||
fieldName: currentEditableFieldDefinition
|
||||
? currentEditableFieldDefinition.metadata.fieldName
|
||||
: '',
|
||||
}),
|
||||
);
|
||||
|
||||
const updateField = useUpdateGenericEntityField();
|
||||
|
||||
const handleSubmit = (newValue: string) => {
|
||||
if (!isPossiblePhoneNumber(newValue)) return;
|
||||
|
||||
setFieldValue(newValue);
|
||||
|
||||
if (currentEditableFieldEntityId && updateField) {
|
||||
updateField(
|
||||
currentEditableFieldEntityId,
|
||||
currentEditableFieldDefinition,
|
||||
newValue,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const { handleEnter, handleEscape, handleClickOutside } =
|
||||
useFieldInputEventHandlers({
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
return (
|
||||
<PhoneInput
|
||||
value={fieldValue ?? ''}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
hotkeyScope={EditableFieldHotkeyScope.EditableField}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,50 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
|
||||
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
|
||||
import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext';
|
||||
import { FieldRecoilScopeContext } from '../states/recoil-scope-contexts/FieldRecoilScopeContext';
|
||||
import { genericEntityFieldFamilySelector } from '../states/selectors/genericEntityFieldFamilySelector';
|
||||
import { FieldDefinition } from '../types/FieldDefinition';
|
||||
import { FieldRelationMetadata } from '../types/FieldMetadata';
|
||||
|
||||
import { EditableField } from './EditableField';
|
||||
import { GenericEditableRelationFieldDisplayMode } from './GenericEditableRelationFieldDisplayMode';
|
||||
import { GenericEditableRelationFieldEditMode } from './GenericEditableRelationFieldEditMode';
|
||||
|
||||
export const GenericEditableRelationField = () => {
|
||||
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
|
||||
const currentEditableFieldDefinition = useContext(
|
||||
EditableFieldDefinitionContext,
|
||||
) as FieldDefinition<FieldRelationMetadata>;
|
||||
|
||||
const fieldValue = useRecoilValue<any | null>(
|
||||
genericEntityFieldFamilySelector({
|
||||
entityId: currentEditableFieldEntityId ?? '',
|
||||
fieldName: currentEditableFieldDefinition
|
||||
? currentEditableFieldDefinition.metadata.fieldName
|
||||
: '',
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<RecoilScope CustomRecoilScopeContext={FieldRecoilScopeContext}>
|
||||
<RecoilScope>
|
||||
<EditableField
|
||||
useEditButton={currentEditableFieldDefinition.metadata.useEditButton}
|
||||
customEditHotkeyScope={{
|
||||
scope: RelationPickerHotkeyScope.RelationPicker,
|
||||
}}
|
||||
IconLabel={currentEditableFieldDefinition.Icon}
|
||||
editModeContent={<GenericEditableRelationFieldEditMode />}
|
||||
displayModeContent={<GenericEditableRelationFieldDisplayMode />}
|
||||
isDisplayModeContentEmpty={!fieldValue}
|
||||
isDisplayModeFixHeight
|
||||
/>
|
||||
</RecoilScope>
|
||||
</RecoilScope>
|
||||
);
|
||||
};
|
||||
@ -1,70 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { CompanyChip } from '@/companies/components/CompanyChip';
|
||||
import { PersonChip } from '@/people/components/PersonChip';
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
import { UserChip } from '@/users/components/UserChip';
|
||||
import { getLogoUrlFromDomainName } from '~/utils';
|
||||
|
||||
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
|
||||
import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext';
|
||||
import { genericEntityFieldFamilySelector } from '../states/selectors/genericEntityFieldFamilySelector';
|
||||
import { FieldDefinition } from '../types/FieldDefinition';
|
||||
import { FieldRelationMetadata } from '../types/FieldMetadata';
|
||||
|
||||
export const GenericEditableRelationFieldDisplayMode = () => {
|
||||
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
|
||||
const currentEditableFieldDefinition = useContext(
|
||||
EditableFieldDefinitionContext,
|
||||
) as FieldDefinition<FieldRelationMetadata>;
|
||||
|
||||
const fieldValue = useRecoilValue<any | null>(
|
||||
genericEntityFieldFamilySelector({
|
||||
entityId: currentEditableFieldEntityId ?? '',
|
||||
fieldName: currentEditableFieldDefinition
|
||||
? currentEditableFieldDefinition.metadata.fieldName
|
||||
: '',
|
||||
}),
|
||||
);
|
||||
|
||||
switch (currentEditableFieldDefinition.metadata.relationType) {
|
||||
case Entity.Person: {
|
||||
return (
|
||||
<PersonChip
|
||||
id={fieldValue?.id ?? ''}
|
||||
name={fieldValue?.displayName ?? ''}
|
||||
pictureUrl={fieldValue?.avatarUrl ?? ''}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case Entity.User: {
|
||||
return (
|
||||
<UserChip
|
||||
id={fieldValue?.id ?? ''}
|
||||
name={fieldValue?.displayName ?? ''}
|
||||
pictureUrl={fieldValue?.avatarUrl ?? ''}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case Entity.Company: {
|
||||
return (
|
||||
<CompanyChip
|
||||
id={fieldValue?.id ?? ''}
|
||||
name={fieldValue?.name ?? ''}
|
||||
pictureUrl={
|
||||
fieldValue?.domainName
|
||||
? getLogoUrlFromDomainName(fieldValue.domainName)
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
default:
|
||||
console.warn(
|
||||
`Unknown relation type: "${currentEditableFieldDefinition.metadata.relationType}"
|
||||
in GenericEditableRelationField`,
|
||||
);
|
||||
return <> </>;
|
||||
}
|
||||
};
|
||||
@ -1,135 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { CompanyPicker } from '@/companies/components/CompanyPicker';
|
||||
import { companyProgressesFamilyState } from '@/companies/states/companyProgressesFamilyState';
|
||||
import { PeoplePicker } from '@/people/components/PeoplePicker';
|
||||
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
import { UserPicker } from '@/users/components/UserPicker';
|
||||
|
||||
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
|
||||
import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext';
|
||||
import { useEditableField } from '../hooks/useEditableField';
|
||||
import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField';
|
||||
import { genericEntityFieldFamilySelector } from '../states/selectors/genericEntityFieldFamilySelector';
|
||||
import { FieldDefinition } from '../types/FieldDefinition';
|
||||
import {
|
||||
FieldRelationMetadata,
|
||||
FieldRelationValue,
|
||||
} from '../types/FieldMetadata';
|
||||
|
||||
const StyledRelationPickerContainer = styled.div`
|
||||
left: 0px;
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
`;
|
||||
|
||||
const RelationPicker = ({
|
||||
fieldDefinition,
|
||||
fieldValue,
|
||||
handleEntitySubmit,
|
||||
handleCancel,
|
||||
}: {
|
||||
fieldDefinition: FieldDefinition<FieldRelationMetadata>;
|
||||
fieldValue: FieldRelationValue & { companyId?: string };
|
||||
handleEntitySubmit: (newRelationId: EntityForSelect | null) => void;
|
||||
handleCancel: () => void;
|
||||
}) => {
|
||||
switch (fieldDefinition.metadata.relationType) {
|
||||
case Entity.Person: {
|
||||
return (
|
||||
<PeoplePicker
|
||||
personId={fieldValue ? fieldValue.id : ''}
|
||||
companyId={fieldValue?.companyId ?? ''}
|
||||
onSubmit={handleEntitySubmit}
|
||||
onCancel={handleCancel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case Entity.User: {
|
||||
return (
|
||||
<UserPicker
|
||||
userId={fieldValue ? fieldValue.id : ''}
|
||||
onSubmit={handleEntitySubmit}
|
||||
onCancel={handleCancel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case Entity.Company: {
|
||||
return (
|
||||
<CompanyPicker
|
||||
companyId={fieldValue ? fieldValue.id : ''}
|
||||
onSubmit={handleEntitySubmit}
|
||||
onCancel={handleCancel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
default:
|
||||
console.warn(
|
||||
`Unknown relation type: "${fieldDefinition.metadata.relationType}" in GenericEditableRelationField`,
|
||||
);
|
||||
return <></>;
|
||||
}
|
||||
};
|
||||
|
||||
export const GenericEditableRelationFieldEditMode = () => {
|
||||
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
|
||||
const currentEditableFieldDefinition = useContext(
|
||||
EditableFieldDefinitionContext,
|
||||
) as FieldDefinition<FieldRelationMetadata>;
|
||||
|
||||
const [companyProgress] = useRecoilState(
|
||||
companyProgressesFamilyState(currentEditableFieldEntityId ?? ''),
|
||||
);
|
||||
const { company } = companyProgress ?? {};
|
||||
|
||||
// TODO: we could use a hook that would return the field value with the right type
|
||||
const [fieldValue, setFieldValue] = useRecoilState<any | null>(
|
||||
genericEntityFieldFamilySelector({
|
||||
entityId: currentEditableFieldEntityId ?? '',
|
||||
fieldName: currentEditableFieldDefinition
|
||||
? currentEditableFieldDefinition.metadata.fieldName
|
||||
: '',
|
||||
}),
|
||||
);
|
||||
|
||||
const updateField = useUpdateGenericEntityField();
|
||||
const { closeEditableField } = useEditableField();
|
||||
|
||||
const handleSubmit = (newRelation: EntityForSelect | null) => {
|
||||
if (newRelation?.id === fieldValue?.id) return;
|
||||
|
||||
setFieldValue({
|
||||
id: newRelation?.id ?? null,
|
||||
displayName: newRelation?.name ?? null,
|
||||
avatarUrl: newRelation?.avatarUrl ?? null,
|
||||
});
|
||||
|
||||
if (currentEditableFieldEntityId && updateField) {
|
||||
updateField(
|
||||
currentEditableFieldEntityId,
|
||||
currentEditableFieldDefinition,
|
||||
newRelation,
|
||||
);
|
||||
}
|
||||
|
||||
closeEditableField();
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
closeEditableField();
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledRelationPickerContainer>
|
||||
<RelationPicker
|
||||
fieldDefinition={currentEditableFieldDefinition}
|
||||
fieldValue={{ ...fieldValue, companyId: company?.id }}
|
||||
handleEntitySubmit={handleSubmit}
|
||||
handleCancel={handleCancel}
|
||||
/>
|
||||
</StyledRelationPickerContainer>
|
||||
);
|
||||
};
|
||||
@ -1,42 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { TextDisplay } from '@/ui/content-display/components/TextDisplay';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
|
||||
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
|
||||
import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext';
|
||||
import { FieldRecoilScopeContext } from '../states/recoil-scope-contexts/FieldRecoilScopeContext';
|
||||
import { genericEntityFieldFamilySelector } from '../states/selectors/genericEntityFieldFamilySelector';
|
||||
import { FieldDefinition } from '../types/FieldDefinition';
|
||||
import { FieldNumberMetadata } from '../types/FieldMetadata';
|
||||
|
||||
import { EditableField } from './EditableField';
|
||||
import { GenericEditableTextFieldEditMode } from './GenericEditableTextFieldEditMode';
|
||||
|
||||
export const GenericEditableTextField = () => {
|
||||
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
|
||||
const currentEditableFieldDefinition = useContext(
|
||||
EditableFieldDefinitionContext,
|
||||
) as FieldDefinition<FieldNumberMetadata>;
|
||||
|
||||
const fieldValue = useRecoilValue<string>(
|
||||
genericEntityFieldFamilySelector({
|
||||
entityId: currentEditableFieldEntityId ?? '',
|
||||
fieldName: currentEditableFieldDefinition
|
||||
? currentEditableFieldDefinition.metadata.fieldName
|
||||
: '',
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<RecoilScope CustomRecoilScopeContext={FieldRecoilScopeContext}>
|
||||
<EditableField
|
||||
IconLabel={currentEditableFieldDefinition.Icon}
|
||||
editModeContent={<GenericEditableTextFieldEditMode />}
|
||||
displayModeContent={<TextDisplay text={fieldValue} />}
|
||||
isDisplayModeContentEmpty={!fieldValue}
|
||||
/>
|
||||
</RecoilScope>
|
||||
);
|
||||
};
|
||||
@ -1,62 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
|
||||
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
|
||||
import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext';
|
||||
import { useFieldInputEventHandlers } from '../hooks/useFieldInputEventHandlers';
|
||||
import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField';
|
||||
import { genericEntityFieldFamilySelector } from '../states/selectors/genericEntityFieldFamilySelector';
|
||||
import { EditableFieldHotkeyScope } from '../types/EditableFieldHotkeyScope';
|
||||
import { FieldDefinition } from '../types/FieldDefinition';
|
||||
import { FieldTextMetadata } from '../types/FieldMetadata';
|
||||
|
||||
export const GenericEditableTextFieldEditMode = () => {
|
||||
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
|
||||
const currentEditableFieldDefinition = useContext(
|
||||
EditableFieldDefinitionContext,
|
||||
) as FieldDefinition<FieldTextMetadata>;
|
||||
|
||||
// TODO: we could use a hook that would return the field value with the right type
|
||||
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||
genericEntityFieldFamilySelector({
|
||||
entityId: currentEditableFieldEntityId ?? '',
|
||||
fieldName: currentEditableFieldDefinition
|
||||
? currentEditableFieldDefinition.metadata.fieldName
|
||||
: '',
|
||||
}),
|
||||
);
|
||||
|
||||
const updateField = useUpdateGenericEntityField();
|
||||
|
||||
const handleSubmit = (newValue: string) => {
|
||||
if (currentEditableFieldEntityId && updateField) {
|
||||
updateField(
|
||||
currentEditableFieldEntityId,
|
||||
currentEditableFieldDefinition,
|
||||
newValue,
|
||||
);
|
||||
|
||||
// TODO: use optimistic effect instead, but needs generic refactor
|
||||
setFieldValue(newValue);
|
||||
}
|
||||
};
|
||||
|
||||
const { handleEnter, handleEscape, handleClickOutside } =
|
||||
useFieldInputEventHandlers({
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
return (
|
||||
<TextInput
|
||||
placeholder={currentEditableFieldDefinition.metadata.placeHolder}
|
||||
autoFocus
|
||||
value={fieldValue ?? ''}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
hotkeyScope={EditableFieldHotkeyScope.EditableField}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,44 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { URLDisplay } from '@/ui/content-display/components/URLDisplay';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
|
||||
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
|
||||
import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext';
|
||||
import { FieldRecoilScopeContext } from '../states/recoil-scope-contexts/FieldRecoilScopeContext';
|
||||
import { genericEntityFieldFamilySelector } from '../states/selectors/genericEntityFieldFamilySelector';
|
||||
import { FieldDefinition } from '../types/FieldDefinition';
|
||||
import { FieldNumberMetadata } from '../types/FieldMetadata';
|
||||
|
||||
import { EditableField } from './EditableField';
|
||||
import { GenericEditableURLFieldEditMode } from './GenericEditableURLFieldEditMode';
|
||||
|
||||
export const GenericEditableURLField = () => {
|
||||
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
|
||||
const currentEditableFieldDefinition = useContext(
|
||||
EditableFieldDefinitionContext,
|
||||
) as FieldDefinition<FieldNumberMetadata>;
|
||||
|
||||
const fieldValue = useRecoilValue<string>(
|
||||
genericEntityFieldFamilySelector({
|
||||
entityId: currentEditableFieldEntityId ?? '',
|
||||
fieldName: currentEditableFieldDefinition
|
||||
? currentEditableFieldDefinition.metadata.fieldName
|
||||
: '',
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<RecoilScope CustomRecoilScopeContext={FieldRecoilScopeContext}>
|
||||
<EditableField
|
||||
useEditButton
|
||||
IconLabel={currentEditableFieldDefinition.Icon}
|
||||
editModeContent={<GenericEditableURLFieldEditMode />}
|
||||
displayModeContent={<URLDisplay value={fieldValue} />}
|
||||
isDisplayModeContentEmpty={!fieldValue}
|
||||
isDisplayModeFixHeight
|
||||
/>
|
||||
</RecoilScope>
|
||||
);
|
||||
};
|
||||
@ -1,63 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
|
||||
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
|
||||
import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext';
|
||||
import { useFieldInputEventHandlers } from '../hooks/useFieldInputEventHandlers';
|
||||
import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField';
|
||||
import { genericEntityFieldFamilySelector } from '../states/selectors/genericEntityFieldFamilySelector';
|
||||
import { EditableFieldHotkeyScope } from '../types/EditableFieldHotkeyScope';
|
||||
import { FieldDefinition } from '../types/FieldDefinition';
|
||||
import { FieldURLMetadata } from '../types/FieldMetadata';
|
||||
|
||||
// This one is very similar to GenericEditableTextFieldEditMode
|
||||
// We could probably merge them since FieldURLMetadata is basically a FieldTextMetadata
|
||||
export const GenericEditableURLFieldEditMode = () => {
|
||||
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
|
||||
const currentEditableFieldDefinition = useContext(
|
||||
EditableFieldDefinitionContext,
|
||||
) as FieldDefinition<FieldURLMetadata>;
|
||||
|
||||
// TODO: we could use a hook that would return the field value with the right type
|
||||
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||
genericEntityFieldFamilySelector({
|
||||
entityId: currentEditableFieldEntityId ?? '',
|
||||
fieldName: currentEditableFieldDefinition
|
||||
? currentEditableFieldDefinition.metadata.fieldName
|
||||
: '',
|
||||
}),
|
||||
);
|
||||
|
||||
const updateField = useUpdateGenericEntityField();
|
||||
|
||||
const handleSubmit = (newValue: string) => {
|
||||
setFieldValue(newValue);
|
||||
|
||||
if (currentEditableFieldEntityId && updateField) {
|
||||
updateField(
|
||||
currentEditableFieldEntityId,
|
||||
currentEditableFieldDefinition,
|
||||
newValue,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const { handleEnter, handleEscape, handleClickOutside } =
|
||||
useFieldInputEventHandlers({
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
return (
|
||||
<TextInput
|
||||
placeholder={currentEditableFieldDefinition.metadata.placeHolder}
|
||||
autoFocus
|
||||
value={fieldValue ?? ''}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
hotkeyScope={EditableFieldHotkeyScope.EditableField}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,87 @@
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { FieldDisplay } from '@/ui/field/components/FieldDisplay';
|
||||
import { FieldInput } from '@/ui/field/components/FieldInput';
|
||||
import { FieldContext } from '@/ui/field/contexts/FieldContext';
|
||||
import { useIsFieldEmpty } from '@/ui/field/hooks/useIsFieldEmpty';
|
||||
import { useIsFieldInputOnly } from '@/ui/field/hooks/useIsFieldInputOnly';
|
||||
import { FieldInputEvent } from '@/ui/field/types/FieldInputEvent';
|
||||
import { isFieldRelation } from '@/ui/field/types/guards/isFieldRelation';
|
||||
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
|
||||
|
||||
import { useInlineCell } from '../hooks/useInlineCell';
|
||||
|
||||
import { InlineCellContainer } from './InlineCellContainer';
|
||||
|
||||
export const InlineCell = () => {
|
||||
const { fieldDefinition } = useContext(FieldContext);
|
||||
|
||||
const isFieldEmpty = useIsFieldEmpty();
|
||||
|
||||
const isFieldInputOnly = useIsFieldInputOnly();
|
||||
|
||||
const { closeInlineCell } = useInlineCell();
|
||||
|
||||
const handleEnter: FieldInputEvent = (persistField) => {
|
||||
persistField();
|
||||
closeInlineCell();
|
||||
};
|
||||
|
||||
const handleSubmit: FieldInputEvent = (persistField) => {
|
||||
persistField();
|
||||
closeInlineCell();
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
closeInlineCell();
|
||||
};
|
||||
|
||||
const handleEscape = () => {
|
||||
closeInlineCell();
|
||||
};
|
||||
|
||||
const handleTab: FieldInputEvent = (persistField) => {
|
||||
persistField();
|
||||
closeInlineCell();
|
||||
};
|
||||
|
||||
const handleShiftTab: FieldInputEvent = (persistField) => {
|
||||
persistField();
|
||||
closeInlineCell();
|
||||
};
|
||||
|
||||
const handleClickOutside: FieldInputEvent = (persistField) => {
|
||||
persistField();
|
||||
closeInlineCell();
|
||||
};
|
||||
console.log(JSON.stringify({ fieldDefinition }));
|
||||
|
||||
return (
|
||||
<InlineCellContainer
|
||||
useEditButton={fieldDefinition.useEditButton}
|
||||
customEditHotkeyScope={
|
||||
isFieldRelation(fieldDefinition)
|
||||
? {
|
||||
scope: RelationPickerHotkeyScope.RelationPicker,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
IconLabel={fieldDefinition.Icon}
|
||||
editModeContent={
|
||||
<FieldInput
|
||||
onEnter={handleEnter}
|
||||
onCancel={handleCancel}
|
||||
onEscape={handleEscape}
|
||||
onSubmit={handleSubmit}
|
||||
onTab={handleTab}
|
||||
onShiftTab={handleShiftTab}
|
||||
onClickOutside={handleClickOutside}
|
||||
/>
|
||||
}
|
||||
displayModeContent={<FieldDisplay />}
|
||||
isDisplayModeContentEmpty={isFieldEmpty}
|
||||
isDisplayModeFixHeight
|
||||
editModeContentOnly={isFieldInputOnly}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,15 +1,16 @@
|
||||
import { useState } from 'react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
import { IconComponent } from '@/ui/icon/types/IconComponent';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
|
||||
import { useEditableField } from '../hooks/useEditableField';
|
||||
import { useInlineCell } from '../hooks/useInlineCell';
|
||||
|
||||
import { EditableFieldDisplayMode } from './EditableFieldDisplayMode';
|
||||
import { EditableFieldEditButton } from './EditableFieldEditButton';
|
||||
import { EditableFieldEditMode } from './EditableFieldEditMode';
|
||||
import { InlineCellDisplayMode } from './InlineCellDisplayMode';
|
||||
import { InlineCellEditButton } from './InlineCellEditButton';
|
||||
import { InlineCellEditMode } from './InlineCellEditMode';
|
||||
|
||||
const StyledIconContainer = styled.div`
|
||||
align-items: center;
|
||||
@ -57,7 +58,7 @@ const StyledClickableContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledEditableFieldBaseContainer = styled.div`
|
||||
const StyledInlineCellBaseContainer = styled.div`
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
|
||||
@ -77,7 +78,7 @@ type OwnProps = {
|
||||
labelFixedWidth?: number;
|
||||
useEditButton?: boolean;
|
||||
editModeContent?: React.ReactNode;
|
||||
displayModeContentOnly?: boolean;
|
||||
editModeContentOnly?: boolean;
|
||||
displayModeContent: React.ReactNode;
|
||||
customEditHotkeyScope?: HotkeyScope;
|
||||
isDisplayModeContentEmpty?: boolean;
|
||||
@ -85,7 +86,7 @@ type OwnProps = {
|
||||
disableHoverEffect?: boolean;
|
||||
};
|
||||
|
||||
export const EditableField = ({
|
||||
export const InlineCellContainer = ({
|
||||
IconLabel,
|
||||
label,
|
||||
labelFixedWidth,
|
||||
@ -94,7 +95,7 @@ export const EditableField = ({
|
||||
displayModeContent,
|
||||
customEditHotkeyScope,
|
||||
isDisplayModeContentEmpty,
|
||||
displayModeContentOnly,
|
||||
editModeContentOnly,
|
||||
isDisplayModeFixHeight,
|
||||
disableHoverEffect,
|
||||
}: OwnProps) => {
|
||||
@ -108,46 +109,61 @@ export const EditableField = ({
|
||||
setIsHovered(false);
|
||||
};
|
||||
|
||||
const { isFieldInEditMode, openEditableField } = useEditableField();
|
||||
const { isInlineCellInEditMode, openInlineCell } = useInlineCell();
|
||||
|
||||
const handleDisplayModeClick = () => {
|
||||
if (!displayModeContentOnly) {
|
||||
openEditableField(customEditHotkeyScope);
|
||||
if (!editModeContentOnly) {
|
||||
openInlineCell(customEditHotkeyScope);
|
||||
}
|
||||
};
|
||||
|
||||
const showEditButton =
|
||||
!isFieldInEditMode && isHovered && useEditButton && !displayModeContentOnly;
|
||||
!isInlineCellInEditMode &&
|
||||
isHovered &&
|
||||
useEditButton &&
|
||||
!editModeContentOnly;
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<StyledEditableFieldBaseContainer
|
||||
<StyledInlineCellBaseContainer
|
||||
onMouseEnter={handleContainerMouseEnter}
|
||||
onMouseLeave={handleContainerMouseLeave}
|
||||
>
|
||||
<StyledLabelAndIconContainer>
|
||||
{IconLabel && (
|
||||
<StyledIconContainer>
|
||||
<IconLabel />
|
||||
<IconLabel stroke={theme.icon.stroke.sm} />
|
||||
</StyledIconContainer>
|
||||
)}
|
||||
{label && (
|
||||
<StyledLabel labelFixedWidth={labelFixedWidth}>{label}</StyledLabel>
|
||||
)}
|
||||
</StyledLabelAndIconContainer>
|
||||
|
||||
<StyledValueContainer>
|
||||
{isFieldInEditMode ? (
|
||||
<EditableFieldEditMode>{editModeContent}</EditableFieldEditMode>
|
||||
{isInlineCellInEditMode ? (
|
||||
<InlineCellEditMode>{editModeContent}</InlineCellEditMode>
|
||||
) : editModeContentOnly ? (
|
||||
<StyledClickableContainer>
|
||||
<InlineCellDisplayMode
|
||||
disableHoverEffect={disableHoverEffect}
|
||||
isDisplayModeContentEmpty={isDisplayModeContentEmpty}
|
||||
isDisplayModeFixHeight={isDisplayModeFixHeight}
|
||||
isHovered={isHovered}
|
||||
>
|
||||
{editModeContent}
|
||||
</InlineCellDisplayMode>
|
||||
</StyledClickableContainer>
|
||||
) : (
|
||||
<StyledClickableContainer onClick={handleDisplayModeClick}>
|
||||
<EditableFieldDisplayMode
|
||||
<InlineCellDisplayMode
|
||||
disableHoverEffect={disableHoverEffect}
|
||||
isDisplayModeContentEmpty={isDisplayModeContentEmpty}
|
||||
isDisplayModeFixHeight={isDisplayModeFixHeight}
|
||||
isHovered={isHovered}
|
||||
>
|
||||
{displayModeContent}
|
||||
</EditableFieldDisplayMode>
|
||||
</InlineCellDisplayMode>
|
||||
{showEditButton && (
|
||||
<StyledEditButtonContainer
|
||||
initial={{ opacity: 0 }}
|
||||
@ -155,12 +171,12 @@ export const EditableField = ({
|
||||
transition={{ duration: 0.1 }}
|
||||
whileHover={{ scale: 1.04 }}
|
||||
>
|
||||
<EditableFieldEditButton />
|
||||
<InlineCellEditButton />
|
||||
</StyledEditButtonContainer>
|
||||
)}
|
||||
</StyledClickableContainer>
|
||||
)}
|
||||
</StyledValueContainer>
|
||||
</StyledEditableFieldBaseContainer>
|
||||
</StyledInlineCellBaseContainer>
|
||||
);
|
||||
};
|
||||
@ -57,7 +57,7 @@ type OwnProps = {
|
||||
isHovered?: boolean;
|
||||
};
|
||||
|
||||
export const EditableFieldDisplayMode = ({
|
||||
export const InlineCellDisplayMode = ({
|
||||
children,
|
||||
isDisplayModeContentEmpty,
|
||||
disableHoverEffect,
|
||||
@ -1,13 +1,13 @@
|
||||
import { FloatingIconButton } from '@/ui/button/components/FloatingIconButton';
|
||||
import { IconPencil } from '@/ui/icon';
|
||||
|
||||
import { useEditableField } from '../hooks/useEditableField';
|
||||
import { useInlineCell } from '../hooks/useInlineCell';
|
||||
|
||||
export const EditableFieldEditButton = () => {
|
||||
const { openEditableField } = useEditableField();
|
||||
export const InlineCellEditButton = () => {
|
||||
const { openInlineCell } = useInlineCell();
|
||||
|
||||
const handleClick = () => {
|
||||
openEditableField();
|
||||
openInlineCell();
|
||||
};
|
||||
|
||||
return (
|
||||
@ -15,7 +15,7 @@ export const EditableFieldEditButton = () => {
|
||||
size="small"
|
||||
onClick={handleClick}
|
||||
Icon={IconPencil}
|
||||
data-testid="editable-field-edit-mode-container"
|
||||
data-testid="inline-cell-edit-mode-container"
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,6 +1,6 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
const StyledEditableFieldEditModeContainer = styled.div<OwnProps>`
|
||||
const StyledInlineCellEditModeContainer = styled.div<OwnProps>`
|
||||
align-items: center;
|
||||
|
||||
display: flex;
|
||||
@ -11,7 +11,7 @@ const StyledEditableFieldEditModeContainer = styled.div<OwnProps>`
|
||||
z-index: 10;
|
||||
`;
|
||||
|
||||
const StyledEditableFieldInput = styled.div`
|
||||
const StyledInlineCellInput = styled.div`
|
||||
align-items: center;
|
||||
background: ${({ theme }) => theme.background.transparent.secondary};
|
||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
@ -30,8 +30,8 @@ type OwnProps = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export const EditableFieldEditMode = ({ children }: OwnProps) => (
|
||||
<StyledEditableFieldEditModeContainer data-testid="editable-field-edit-mode-container">
|
||||
<StyledEditableFieldInput>{children}</StyledEditableFieldInput>
|
||||
</StyledEditableFieldEditModeContainer>
|
||||
export const InlineCellEditMode = ({ children }: OwnProps) => (
|
||||
<StyledInlineCellEditModeContainer data-testid="inline-cell-edit-mode-container">
|
||||
<StyledInlineCellInput>{children}</StyledInlineCellInput>
|
||||
</StyledInlineCellEditModeContainer>
|
||||
);
|
||||
@ -1,28 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { EditableField } from '@/ui/editable-field/components/EditableField';
|
||||
import { FieldRecoilScopeContext } from '@/ui/editable-field/states/recoil-scope-contexts/FieldRecoilScopeContext';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
|
||||
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
|
||||
import { FieldDefinition } from '../types/FieldDefinition';
|
||||
import { FieldProbabilityMetadata } from '../types/FieldMetadata';
|
||||
|
||||
import { ProbabilityEditableFieldEditMode } from './ProbabilityEditableFieldEditMode';
|
||||
|
||||
export const ProbabilityEditableField = () => {
|
||||
const currentEditableFieldDefinition = useContext(
|
||||
EditableFieldDefinitionContext,
|
||||
) as FieldDefinition<FieldProbabilityMetadata>;
|
||||
|
||||
return (
|
||||
<RecoilScope CustomRecoilScopeContext={FieldRecoilScopeContext}>
|
||||
<EditableField
|
||||
IconLabel={currentEditableFieldDefinition.Icon}
|
||||
displayModeContent={<ProbabilityEditableFieldEditMode />}
|
||||
displayModeContentOnly
|
||||
disableHoverEffect
|
||||
/>
|
||||
</RecoilScope>
|
||||
);
|
||||
};
|
||||
@ -1,53 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { useEditableField } from '@/ui/editable-field/hooks/useEditableField';
|
||||
import { ProbabilityInput } from '@/ui/input/components/ProbabilityInput';
|
||||
|
||||
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
|
||||
import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext';
|
||||
import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField';
|
||||
import { genericEntityFieldFamilySelector } from '../states/selectors/genericEntityFieldFamilySelector';
|
||||
import { FieldDefinition } from '../types/FieldDefinition';
|
||||
import { FieldProbabilityMetadata } from '../types/FieldMetadata';
|
||||
|
||||
export const ProbabilityEditableFieldEditMode = () => {
|
||||
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
|
||||
const currentEditableFieldDefinition = useContext(
|
||||
EditableFieldDefinitionContext,
|
||||
) as FieldDefinition<FieldProbabilityMetadata>;
|
||||
|
||||
const [fieldValue, setFieldValue] = useRecoilState<number>(
|
||||
genericEntityFieldFamilySelector({
|
||||
entityId: currentEditableFieldEntityId ?? '',
|
||||
fieldName: currentEditableFieldDefinition
|
||||
? currentEditableFieldDefinition.metadata.fieldName
|
||||
: '',
|
||||
}),
|
||||
);
|
||||
|
||||
const { closeEditableField } = useEditableField();
|
||||
|
||||
const updateField = useUpdateGenericEntityField();
|
||||
|
||||
const probabilityIndex = Math.ceil(fieldValue / 25);
|
||||
|
||||
const handleChange = (newValue: number) => {
|
||||
setFieldValue(newValue);
|
||||
if (currentEditableFieldEntityId && updateField) {
|
||||
updateField(
|
||||
currentEditableFieldEntityId,
|
||||
currentEditableFieldDefinition,
|
||||
newValue,
|
||||
);
|
||||
}
|
||||
closeEditableField();
|
||||
};
|
||||
|
||||
return (
|
||||
<ProbabilityInput
|
||||
probabilityIndex={probabilityIndex}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,8 +0,0 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
import { FieldDefinition } from '../types/FieldDefinition';
|
||||
import { FieldMetadata } from '../types/FieldMetadata';
|
||||
|
||||
export const EditableFieldDefinitionContext = createContext<
|
||||
FieldDefinition<FieldMetadata>
|
||||
>({} as FieldDefinition<FieldMetadata>);
|
||||
@ -1,3 +0,0 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
export const EditableFieldEntityIdContext = createContext<string>('');
|
||||
@ -1,28 +0,0 @@
|
||||
import { useEditableField } from './useEditableField';
|
||||
|
||||
export const useFieldInputEventHandlers = <T>({
|
||||
onSubmit,
|
||||
onCancel,
|
||||
}: {
|
||||
onSubmit?: (newValue: T) => void;
|
||||
onCancel?: () => void;
|
||||
}) => {
|
||||
const { closeEditableField, isFieldInEditMode } = useEditableField();
|
||||
|
||||
return {
|
||||
handleClickOutside: (_event: MouseEvent | TouchEvent, newValue: T) => {
|
||||
if (isFieldInEditMode) {
|
||||
onSubmit?.(newValue);
|
||||
closeEditableField();
|
||||
}
|
||||
},
|
||||
handleEscape: () => {
|
||||
closeEditableField();
|
||||
onCancel?.();
|
||||
},
|
||||
handleEnter: (newValue: T) => {
|
||||
onSubmit?.(newValue);
|
||||
closeEditableField();
|
||||
},
|
||||
};
|
||||
};
|
||||
@ -1,15 +1,18 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { FieldContext } from '@/ui/field/contexts/FieldContext';
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
|
||||
import { isFieldInEditModeScopedState } from '../states/isFieldInEditModeScopedState';
|
||||
import { FieldRecoilScopeContext } from '../states/recoil-scope-contexts/FieldRecoilScopeContext';
|
||||
import { isInlineCellInEditModeScopedState } from '../states/isInlineCellInEditModeScopedState';
|
||||
import { EditableFieldHotkeyScope } from '../types/EditableFieldHotkeyScope';
|
||||
|
||||
export const useEditableField = () => {
|
||||
const [isFieldInEditMode, setIsFieldInEditMode] = useRecoilScopedState(
|
||||
isFieldInEditModeScopedState,
|
||||
FieldRecoilScopeContext,
|
||||
export const useInlineCell = () => {
|
||||
const { recoilScopeId } = useContext(FieldContext);
|
||||
|
||||
const [isInlineCellInEditMode, setIsInlineCellInEditMode] = useRecoilState(
|
||||
isInlineCellInEditModeScopedState(recoilScopeId),
|
||||
);
|
||||
|
||||
const {
|
||||
@ -17,14 +20,14 @@ export const useEditableField = () => {
|
||||
goBackToPreviousHotkeyScope,
|
||||
} = usePreviousHotkeyScope();
|
||||
|
||||
const closeEditableField = () => {
|
||||
setIsFieldInEditMode(false);
|
||||
const closeInlineCell = () => {
|
||||
setIsInlineCellInEditMode(false);
|
||||
|
||||
goBackToPreviousHotkeyScope();
|
||||
};
|
||||
|
||||
const openEditableField = (customEditHotkeyScopeForField?: HotkeyScope) => {
|
||||
setIsFieldInEditMode(true);
|
||||
const openInlineCell = (customEditHotkeyScopeForField?: HotkeyScope) => {
|
||||
setIsInlineCellInEditMode(true);
|
||||
|
||||
if (customEditHotkeyScopeForField) {
|
||||
setHotkeyScopeAndMemorizePreviousScope(
|
||||
@ -39,8 +42,8 @@ export const useEditableField = () => {
|
||||
};
|
||||
|
||||
return {
|
||||
isFieldInEditMode,
|
||||
closeEditableField,
|
||||
openEditableField,
|
||||
isInlineCellInEditMode,
|
||||
closeInlineCell,
|
||||
openInlineCell,
|
||||
};
|
||||
};
|
||||
@ -1,44 +0,0 @@
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
|
||||
import { EditableFieldHotkeyScope } from '../types/EditableFieldHotkeyScope';
|
||||
|
||||
import { useEditableField } from './useEditableField';
|
||||
|
||||
export const useRegisterCloseFieldHandlers = (
|
||||
wrapperRef: React.RefObject<HTMLDivElement>,
|
||||
onSubmit?: () => void,
|
||||
onCancel?: () => void,
|
||||
) => {
|
||||
const { closeEditableField, isFieldInEditMode } = useEditableField();
|
||||
|
||||
useListenClickOutside({
|
||||
refs: [wrapperRef],
|
||||
callback: () => {
|
||||
if (isFieldInEditMode) {
|
||||
onSubmit?.();
|
||||
closeEditableField();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
useScopedHotkeys(
|
||||
'enter',
|
||||
() => {
|
||||
onSubmit?.();
|
||||
closeEditableField();
|
||||
},
|
||||
EditableFieldHotkeyScope.EditableField,
|
||||
[closeEditableField, onSubmit],
|
||||
);
|
||||
|
||||
useScopedHotkeys(
|
||||
'esc',
|
||||
() => {
|
||||
closeEditableField();
|
||||
onCancel?.();
|
||||
},
|
||||
EditableFieldHotkeyScope.EditableField,
|
||||
[closeEditableField, onCancel],
|
||||
);
|
||||
};
|
||||
@ -1,170 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { EditableFieldMutationContext } from '../contexts/EditableFieldMutationContext';
|
||||
import { FieldDefinition } from '../types/FieldDefinition';
|
||||
import {
|
||||
FieldBooleanMetadata,
|
||||
FieldBooleanValue,
|
||||
FieldChipMetadata,
|
||||
FieldChipValue,
|
||||
FieldDateMetadata,
|
||||
FieldDateValue,
|
||||
FieldDoubleTextChipMetadata,
|
||||
FieldDoubleTextChipValue,
|
||||
FieldDoubleTextMetadata,
|
||||
FieldDoubleTextValue,
|
||||
FieldMetadata,
|
||||
FieldNumberMetadata,
|
||||
FieldNumberValue,
|
||||
FieldPhoneMetadata,
|
||||
FieldPhoneValue,
|
||||
FieldProbabilityMetadata,
|
||||
FieldProbabilityValue,
|
||||
FieldRelationMetadata,
|
||||
FieldRelationValue,
|
||||
FieldTextMetadata,
|
||||
FieldTextValue,
|
||||
FieldURLMetadata,
|
||||
FieldURLValue,
|
||||
} from '../types/FieldMetadata';
|
||||
import { isFieldBoolean } from '../types/guards/isFieldBoolean';
|
||||
import { isFieldBooleanValue } from '../types/guards/isFieldBooleanValue';
|
||||
import { isFieldChip } from '../types/guards/isFieldChip';
|
||||
import { isFieldChipValue } from '../types/guards/isFieldChipValue';
|
||||
import { isFieldDate } from '../types/guards/isFieldDate';
|
||||
import { isFieldDateValue } from '../types/guards/isFieldDateValue';
|
||||
import { isFieldDoubleText } from '../types/guards/isFieldDoubleText';
|
||||
import { isFieldDoubleTextChip } from '../types/guards/isFieldDoubleTextChip';
|
||||
import { isFieldDoubleTextChipValue } from '../types/guards/isFieldDoubleTextChipValue';
|
||||
import { isFieldDoubleTextValue } from '../types/guards/isFieldDoubleTextValue';
|
||||
import { isFieldNumber } from '../types/guards/isFieldNumber';
|
||||
import { isFieldNumberValue } from '../types/guards/isFieldNumberValue';
|
||||
import { isFieldPhone } from '../types/guards/isFieldPhone';
|
||||
import { isFieldPhoneValue } from '../types/guards/isFieldPhoneValue';
|
||||
import { isFieldProbability } from '../types/guards/isFieldProbability';
|
||||
import { isFieldProbabilityValue } from '../types/guards/isFieldProbabilityValue';
|
||||
import { isFieldRelation } from '../types/guards/isFieldRelation';
|
||||
import { isFieldRelationValue } from '../types/guards/isFieldRelationValue';
|
||||
import { isFieldText } from '../types/guards/isFieldText';
|
||||
import { isFieldTextValue } from '../types/guards/isFieldTextValue';
|
||||
import { isFieldURL } from '../types/guards/isFieldURL';
|
||||
import { isFieldURLValue } from '../types/guards/isFieldURLValue';
|
||||
|
||||
export const useUpdateGenericEntityField = () => {
|
||||
const useUpdateEntityMutation = useContext(EditableFieldMutationContext);
|
||||
|
||||
const [updateEntity] = useUpdateEntityMutation();
|
||||
|
||||
const updateEntityField = <
|
||||
ValueType extends FieldMetadata extends FieldDoubleTextMetadata
|
||||
? FieldDoubleTextValue
|
||||
: FieldMetadata extends FieldTextMetadata
|
||||
? FieldTextValue
|
||||
: FieldMetadata extends FieldPhoneMetadata
|
||||
? FieldPhoneValue
|
||||
: FieldMetadata extends FieldURLMetadata
|
||||
? FieldURLValue
|
||||
: FieldMetadata extends FieldNumberMetadata
|
||||
? FieldNumberValue
|
||||
: FieldMetadata extends FieldDateMetadata
|
||||
? FieldDateValue
|
||||
: FieldMetadata extends FieldChipMetadata
|
||||
? FieldChipValue
|
||||
: FieldMetadata extends FieldDoubleTextChipMetadata
|
||||
? FieldDoubleTextChipValue
|
||||
: FieldMetadata extends FieldRelationMetadata
|
||||
? FieldRelationValue
|
||||
: FieldMetadata extends FieldProbabilityMetadata
|
||||
? FieldProbabilityValue
|
||||
: FieldMetadata extends FieldBooleanMetadata
|
||||
? FieldBooleanValue
|
||||
: unknown,
|
||||
>(
|
||||
currentEntityId: string,
|
||||
field: FieldDefinition<FieldMetadata>,
|
||||
newFieldValue: ValueType | null,
|
||||
) => {
|
||||
// TODO: improve type guards organization, maybe with a common typeguard for all fields
|
||||
// taking an object of options as parameter ?
|
||||
//
|
||||
// The goal would be to check that the field value not only is valid,
|
||||
// but also that it is validated against the corresponding field type
|
||||
|
||||
if (
|
||||
// Relation
|
||||
isFieldRelation(field) &&
|
||||
isFieldRelationValue(newFieldValue)
|
||||
) {
|
||||
updateEntity({
|
||||
variables: {
|
||||
where: { id: currentEntityId },
|
||||
data: {
|
||||
[field.metadata.fieldName]: newFieldValue
|
||||
? { connect: { id: newFieldValue.id } }
|
||||
: { disconnect: true },
|
||||
},
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
// Chip
|
||||
isFieldChip(field) &&
|
||||
isFieldChipValue(newFieldValue)
|
||||
) {
|
||||
updateEntity({
|
||||
variables: {
|
||||
where: { id: currentEntityId },
|
||||
data: { [field.metadata.contentFieldName]: newFieldValue },
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
// Text
|
||||
(isFieldText(field) && isFieldTextValue(newFieldValue)) ||
|
||||
// Phone
|
||||
(isFieldPhone(field) && isFieldPhoneValue(newFieldValue)) ||
|
||||
// URL
|
||||
(isFieldURL(field) && isFieldURLValue(newFieldValue)) ||
|
||||
// Number
|
||||
(isFieldNumber(field) && isFieldNumberValue(newFieldValue)) ||
|
||||
// Date
|
||||
(isFieldDate(field) && isFieldDateValue(newFieldValue)) ||
|
||||
// Probability
|
||||
(isFieldProbability(field) && isFieldProbabilityValue(newFieldValue)) ||
|
||||
// Boolean
|
||||
(isFieldBoolean(field) && isFieldBooleanValue(newFieldValue))
|
||||
) {
|
||||
updateEntity({
|
||||
variables: {
|
||||
where: { id: currentEntityId },
|
||||
data: { [field.metadata.fieldName]: newFieldValue },
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
// Double text
|
||||
(isFieldDoubleText(field) && isFieldDoubleTextValue(newFieldValue)) ||
|
||||
// Double Text Chip
|
||||
(isFieldDoubleTextChip(field) &&
|
||||
isFieldDoubleTextChipValue(newFieldValue))
|
||||
) {
|
||||
updateEntity({
|
||||
variables: {
|
||||
where: { id: currentEntityId },
|
||||
data: {
|
||||
[field.metadata.firstValueFieldName]: newFieldValue.firstValue,
|
||||
[field.metadata.secondValueFieldName]: newFieldValue.secondValue,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return updateEntityField;
|
||||
};
|
||||
@ -1,9 +0,0 @@
|
||||
import { atomFamily } from 'recoil';
|
||||
|
||||
export const genericEntitiesFamilyState = atomFamily<
|
||||
Record<string, unknown> | null,
|
||||
string
|
||||
>({
|
||||
key: 'genericEntitiesFamilyState',
|
||||
default: null,
|
||||
});
|
||||
@ -1,6 +0,0 @@
|
||||
import { atomFamily } from 'recoil';
|
||||
|
||||
export const isFieldInEditModeScopedState = atomFamily<boolean, string>({
|
||||
key: 'isFieldInEditModeScopedState',
|
||||
default: false,
|
||||
});
|
||||
@ -0,0 +1,6 @@
|
||||
import { atomFamily } from 'recoil';
|
||||
|
||||
export const isInlineCellInEditModeScopedState = atomFamily<boolean, string>({
|
||||
key: 'isInlineCellInEditModeScopedState',
|
||||
default: false,
|
||||
});
|
||||
@ -1,18 +0,0 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import { genericEntitiesFamilyState } from '../genericEntitiesFamilyState';
|
||||
|
||||
export const genericEntityFieldFamilySelector = selectorFamily({
|
||||
key: 'genericEntityFieldFamilySelector',
|
||||
get:
|
||||
<T>({ fieldName, entityId }: { fieldName: string; entityId: string }) =>
|
||||
({ get }) =>
|
||||
get(genericEntitiesFamilyState(entityId))?.[fieldName] as T,
|
||||
set:
|
||||
<T>({ fieldName, entityId }: { fieldName: string; entityId: string }) =>
|
||||
({ set }, newValue: T) =>
|
||||
set(genericEntitiesFamilyState(entityId), (prevState) => ({
|
||||
...prevState,
|
||||
[fieldName]: newValue,
|
||||
})),
|
||||
});
|
||||
@ -1,11 +0,0 @@
|
||||
import { IconComponent } from '@/ui/icon/types/IconComponent';
|
||||
|
||||
import { FieldMetadata, FieldType } from './FieldMetadata';
|
||||
|
||||
export type FieldDefinition<T extends FieldMetadata | unknown> = {
|
||||
key: string;
|
||||
name: string;
|
||||
Icon?: IconComponent;
|
||||
type: FieldType;
|
||||
metadata: T;
|
||||
};
|
||||
@ -1,121 +0,0 @@
|
||||
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
|
||||
export type FieldType =
|
||||
| 'unknown'
|
||||
| 'text'
|
||||
| 'relation'
|
||||
| 'chip'
|
||||
| 'double-text-chip'
|
||||
| 'double-text'
|
||||
| 'number'
|
||||
| 'email'
|
||||
| 'boolean'
|
||||
| 'date'
|
||||
| 'phone'
|
||||
| 'url'
|
||||
| 'probability'
|
||||
| 'moneyAmount';
|
||||
|
||||
export type FieldTextMetadata = {
|
||||
placeHolder: string;
|
||||
fieldName: string;
|
||||
};
|
||||
|
||||
export type FieldPhoneMetadata = {
|
||||
placeHolder: string;
|
||||
fieldName: string;
|
||||
};
|
||||
|
||||
export type FieldURLMetadata = {
|
||||
placeHolder: string;
|
||||
fieldName: string;
|
||||
};
|
||||
|
||||
export type FieldDateMetadata = {
|
||||
fieldName: string;
|
||||
};
|
||||
|
||||
export type FieldNumberMetadata = {
|
||||
fieldName: string;
|
||||
placeHolder: string;
|
||||
};
|
||||
|
||||
export type FieldEmailMetadata = {
|
||||
fieldName: string;
|
||||
placeHolder: string;
|
||||
};
|
||||
|
||||
export type FieldRelationMetadata = {
|
||||
relationType: Entity;
|
||||
fieldName: string;
|
||||
useEditButton?: boolean;
|
||||
};
|
||||
|
||||
export type FieldChipMetadata = {
|
||||
relationType: Entity;
|
||||
contentFieldName: string;
|
||||
urlFieldName: string;
|
||||
placeHolder: string;
|
||||
};
|
||||
|
||||
export type FieldDoubleTextMetadata = {
|
||||
firstValueFieldName: string;
|
||||
firstValuePlaceholder: string;
|
||||
secondValueFieldName: string;
|
||||
secondValuePlaceholder: string;
|
||||
};
|
||||
|
||||
export type FieldDoubleTextChipMetadata = {
|
||||
firstValueFieldName: string;
|
||||
firstValuePlaceholder: string;
|
||||
secondValueFieldName: string;
|
||||
secondValuePlaceholder: string;
|
||||
avatarUrlFieldName: string;
|
||||
entityType: Entity;
|
||||
};
|
||||
|
||||
export type FieldProbabilityMetadata = {
|
||||
fieldName: string;
|
||||
};
|
||||
|
||||
export type FieldBooleanMetadata = {
|
||||
fieldName: string;
|
||||
};
|
||||
|
||||
export type FieldMetadata =
|
||||
| FieldTextMetadata
|
||||
| FieldRelationMetadata
|
||||
| FieldChipMetadata
|
||||
| FieldDoubleTextChipMetadata
|
||||
| FieldDoubleTextMetadata
|
||||
| FieldPhoneMetadata
|
||||
| FieldURLMetadata
|
||||
| FieldNumberMetadata
|
||||
| FieldEmailMetadata
|
||||
| FieldDateMetadata
|
||||
| FieldProbabilityMetadata
|
||||
| FieldBooleanMetadata;
|
||||
|
||||
export type FieldTextValue = string;
|
||||
|
||||
export type FieldChipValue = string;
|
||||
export type FieldDateValue = string | null;
|
||||
export type FieldPhoneValue = string;
|
||||
export type FieldURLValue = string;
|
||||
export type FieldNumberValue = number | null;
|
||||
export type FieldEmailValue = string;
|
||||
export type FieldProbabilityValue = number;
|
||||
export type FieldBooleanValue = boolean;
|
||||
|
||||
export type FieldDoubleTextValue = {
|
||||
firstValue: string;
|
||||
secondValue: string;
|
||||
};
|
||||
|
||||
export type FieldDoubleTextChipValue = {
|
||||
firstValue: string;
|
||||
secondValue: string;
|
||||
};
|
||||
|
||||
export type FieldRelationValue = EntityForSelect | null;
|
||||
@ -1,150 +0,0 @@
|
||||
import { IconComponent } from '@/ui/icon/types/IconComponent';
|
||||
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
|
||||
export type ViewFieldType =
|
||||
| 'text'
|
||||
| 'relation'
|
||||
| 'chip'
|
||||
| 'double-text-chip'
|
||||
| 'double-text'
|
||||
| 'number'
|
||||
| 'date'
|
||||
| 'phone'
|
||||
| 'email'
|
||||
| 'url'
|
||||
| 'probability'
|
||||
| 'boolean'
|
||||
| 'moneyAmount';
|
||||
|
||||
export type ViewFieldTextMetadata = {
|
||||
type: 'text';
|
||||
placeHolder: string;
|
||||
fieldName: string;
|
||||
};
|
||||
|
||||
export type ViewFieldPhoneMetadata = {
|
||||
type: 'phone';
|
||||
placeHolder: string;
|
||||
fieldName: string;
|
||||
};
|
||||
|
||||
export type ViewFieldEmailMetadata = {
|
||||
type: 'email';
|
||||
placeHolder: string;
|
||||
fieldName: string;
|
||||
};
|
||||
|
||||
export type ViewFieldURLMetadata = {
|
||||
type: 'url';
|
||||
placeHolder: string;
|
||||
fieldName: string;
|
||||
};
|
||||
|
||||
export type ViewFieldDateMetadata = {
|
||||
type: 'date';
|
||||
fieldName: string;
|
||||
};
|
||||
|
||||
export type ViewFieldNumberMetadata = {
|
||||
type: 'number';
|
||||
fieldName: string;
|
||||
isPositive?: boolean;
|
||||
};
|
||||
|
||||
export type ViewFieldMoneyMetadata = {
|
||||
type: 'moneyAmount';
|
||||
fieldName: string;
|
||||
};
|
||||
|
||||
export type ViewFieldBooleanMetadata = {
|
||||
type: 'boolean';
|
||||
fieldName: string;
|
||||
};
|
||||
|
||||
export type ViewFieldRelationMetadata = {
|
||||
type: 'relation';
|
||||
relationType: Entity;
|
||||
fieldName: string;
|
||||
useEditButton?: boolean;
|
||||
};
|
||||
|
||||
export type ViewFieldChipMetadata = {
|
||||
type: 'chip';
|
||||
relationType: Entity;
|
||||
contentFieldName: string;
|
||||
urlFieldName: string;
|
||||
placeHolder: string;
|
||||
};
|
||||
|
||||
export type ViewFieldDoubleTextMetadata = {
|
||||
type: 'double-text';
|
||||
firstValueFieldName: string;
|
||||
firstValuePlaceholder: string;
|
||||
secondValueFieldName: string;
|
||||
secondValuePlaceholder: string;
|
||||
};
|
||||
|
||||
export type ViewFieldDoubleTextChipMetadata = {
|
||||
type: 'double-text-chip';
|
||||
firstValueFieldName: string;
|
||||
firstValuePlaceholder: string;
|
||||
secondValueFieldName: string;
|
||||
secondValuePlaceholder: string;
|
||||
avatarUrlFieldName: string;
|
||||
entityType: Entity;
|
||||
};
|
||||
|
||||
export type ViewFieldProbabilityMetadata = {
|
||||
type: 'probability';
|
||||
fieldName: string;
|
||||
};
|
||||
|
||||
export type ViewFieldMetadata = { type: ViewFieldType } & (
|
||||
| ViewFieldTextMetadata
|
||||
| ViewFieldRelationMetadata
|
||||
| ViewFieldChipMetadata
|
||||
| ViewFieldDoubleTextChipMetadata
|
||||
| ViewFieldDoubleTextMetadata
|
||||
| ViewFieldPhoneMetadata
|
||||
| ViewFieldEmailMetadata
|
||||
| ViewFieldURLMetadata
|
||||
| ViewFieldNumberMetadata
|
||||
| ViewFieldBooleanMetadata
|
||||
| ViewFieldDateMetadata
|
||||
| ViewFieldProbabilityMetadata
|
||||
| ViewFieldMoneyMetadata
|
||||
);
|
||||
|
||||
export type ViewFieldDefinition<T extends ViewFieldMetadata | unknown> = {
|
||||
Icon?: IconComponent;
|
||||
index: number;
|
||||
isVisible?: boolean;
|
||||
key: string;
|
||||
metadata: T;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type ViewFieldTextValue = string;
|
||||
|
||||
export type ViewFieldChipValue = string;
|
||||
export type ViewFieldDateValue = string;
|
||||
export type ViewFieldPhoneValue = string;
|
||||
export type ViewFieldEmailValue = string;
|
||||
export type ViewFieldBooleanValue = boolean;
|
||||
export type ViewFieldMoneyValue = number | null;
|
||||
export type ViewFieldURLValue = string;
|
||||
export type ViewFieldNumberValue = number | null;
|
||||
export type ViewFieldProbabilityValue = number;
|
||||
|
||||
export type ViewFieldDoubleTextValue = {
|
||||
firstValue: string;
|
||||
secondValue: string;
|
||||
};
|
||||
|
||||
export type ViewFieldDoubleTextChipValue = {
|
||||
firstValue: string;
|
||||
secondValue: string;
|
||||
};
|
||||
|
||||
export type ViewFieldRelationValue = EntityForSelect | null;
|
||||
@ -1,6 +0,0 @@
|
||||
import { FieldDefinition } from '../FieldDefinition';
|
||||
import { FieldBooleanMetadata, FieldMetadata } from '../FieldMetadata';
|
||||
|
||||
export const isFieldBoolean = (
|
||||
field: FieldDefinition<FieldMetadata>,
|
||||
): field is FieldDefinition<FieldBooleanMetadata> => field.type === 'boolean';
|
||||
@ -1,9 +0,0 @@
|
||||
import { FieldBooleanValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add yup
|
||||
export const isFieldBooleanValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldBooleanValue =>
|
||||
fieldValue !== null &&
|
||||
fieldValue !== undefined &&
|
||||
typeof fieldValue === 'boolean';
|
||||
@ -1,6 +0,0 @@
|
||||
import { FieldDefinition } from '../FieldDefinition';
|
||||
import { FieldChipMetadata, FieldMetadata } from '../FieldMetadata';
|
||||
|
||||
export const isFieldChip = (
|
||||
field: FieldDefinition<FieldMetadata>,
|
||||
): field is FieldDefinition<FieldChipMetadata> => field.type === 'chip';
|
||||
@ -1,9 +0,0 @@
|
||||
import { FieldChipValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add yup
|
||||
export const isFieldChipValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldChipValue =>
|
||||
fieldValue !== null &&
|
||||
fieldValue !== undefined &&
|
||||
typeof fieldValue === 'string';
|
||||
@ -1,6 +0,0 @@
|
||||
import { FieldDefinition } from '../FieldDefinition';
|
||||
import { FieldDateMetadata, FieldMetadata } from '../FieldMetadata';
|
||||
|
||||
export const isFieldDate = (
|
||||
field: FieldDefinition<FieldMetadata>,
|
||||
): field is FieldDefinition<FieldDateMetadata> => field.type === 'date';
|
||||
@ -1,8 +0,0 @@
|
||||
import { FieldDateValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add yup
|
||||
export const isFieldDateValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldDateValue =>
|
||||
fieldValue === null ||
|
||||
(fieldValue !== undefined && typeof fieldValue === 'string');
|
||||
@ -1,7 +0,0 @@
|
||||
import { FieldDefinition } from '../FieldDefinition';
|
||||
import { FieldDoubleTextMetadata, FieldMetadata } from '../FieldMetadata';
|
||||
|
||||
export const isFieldDoubleText = (
|
||||
field: FieldDefinition<FieldMetadata>,
|
||||
): field is FieldDefinition<FieldDoubleTextMetadata> =>
|
||||
field.type === 'double-text';
|
||||
@ -1,7 +0,0 @@
|
||||
import { FieldDefinition } from '../FieldDefinition';
|
||||
import { FieldDoubleTextChipMetadata, FieldMetadata } from '../FieldMetadata';
|
||||
|
||||
export const isFieldDoubleTextChip = (
|
||||
field: FieldDefinition<FieldMetadata>,
|
||||
): field is FieldDefinition<FieldDoubleTextChipMetadata> =>
|
||||
field.type === 'double-text-chip';
|
||||
@ -1,9 +0,0 @@
|
||||
import { FieldDoubleTextChipValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add yup
|
||||
export const isFieldDoubleTextChipValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldDoubleTextChipValue =>
|
||||
fieldValue !== null &&
|
||||
fieldValue !== undefined &&
|
||||
typeof fieldValue === 'object';
|
||||
@ -1,9 +0,0 @@
|
||||
import { FieldDoubleTextValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add yup
|
||||
export const isFieldDoubleTextValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldDoubleTextValue =>
|
||||
fieldValue !== null &&
|
||||
fieldValue !== undefined &&
|
||||
typeof fieldValue === 'object';
|
||||
@ -1,6 +0,0 @@
|
||||
import { FieldDefinition } from '../FieldDefinition';
|
||||
import { FieldMetadata, FieldNumberMetadata } from '../FieldMetadata';
|
||||
|
||||
export const isFieldNumber = (
|
||||
field: FieldDefinition<FieldMetadata>,
|
||||
): field is FieldDefinition<FieldNumberMetadata> => field.type === 'number';
|
||||
@ -1,8 +0,0 @@
|
||||
import { FieldNumberValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add yup
|
||||
export const isFieldNumberValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldNumberValue =>
|
||||
fieldValue === null ||
|
||||
(fieldValue !== undefined && typeof fieldValue === 'number');
|
||||
@ -1,6 +0,0 @@
|
||||
import { FieldDefinition } from '../FieldDefinition';
|
||||
import { FieldMetadata, FieldPhoneMetadata } from '../FieldMetadata';
|
||||
|
||||
export const isFieldPhone = (
|
||||
field: FieldDefinition<FieldMetadata>,
|
||||
): field is FieldDefinition<FieldPhoneMetadata> => field.type === 'phone';
|
||||
@ -1,9 +0,0 @@
|
||||
import { FieldPhoneValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add yup
|
||||
export const isFieldPhoneValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldPhoneValue =>
|
||||
fieldValue !== null &&
|
||||
fieldValue !== undefined &&
|
||||
typeof fieldValue === 'string';
|
||||
@ -1,7 +0,0 @@
|
||||
import { FieldDefinition } from '../FieldDefinition';
|
||||
import { FieldMetadata, FieldProbabilityMetadata } from '../FieldMetadata';
|
||||
|
||||
export const isFieldProbability = (
|
||||
field: FieldDefinition<FieldMetadata>,
|
||||
): field is FieldDefinition<FieldProbabilityMetadata> =>
|
||||
field.type === 'probability';
|
||||
@ -1,9 +0,0 @@
|
||||
import { FieldProbabilityValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add yup
|
||||
export const isFieldProbabilityValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldProbabilityValue =>
|
||||
fieldValue !== null &&
|
||||
fieldValue !== undefined &&
|
||||
typeof fieldValue === 'number';
|
||||
@ -1,6 +0,0 @@
|
||||
import { FieldDefinition } from '../FieldDefinition';
|
||||
import { FieldMetadata, FieldRelationMetadata } from '../FieldMetadata';
|
||||
|
||||
export const isFieldRelation = (
|
||||
field: FieldDefinition<FieldMetadata>,
|
||||
): field is FieldDefinition<FieldRelationMetadata> => field.type === 'relation';
|
||||
@ -1,7 +0,0 @@
|
||||
import { FieldRelationValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add yup
|
||||
export const isFieldRelationValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldRelationValue =>
|
||||
fieldValue !== undefined && typeof fieldValue === 'object';
|
||||
@ -1,6 +0,0 @@
|
||||
import { FieldDefinition } from '../FieldDefinition';
|
||||
import { FieldMetadata, FieldTextMetadata } from '../FieldMetadata';
|
||||
|
||||
export const isFieldText = (
|
||||
field: FieldDefinition<FieldMetadata>,
|
||||
): field is FieldDefinition<FieldTextMetadata> => field.type === 'text';
|
||||
@ -1,9 +0,0 @@
|
||||
import { FieldTextValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add yup
|
||||
export const isFieldTextValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldTextValue =>
|
||||
fieldValue !== null &&
|
||||
fieldValue !== undefined &&
|
||||
typeof fieldValue === 'string';
|
||||
@ -1,6 +0,0 @@
|
||||
import { FieldDefinition } from '../FieldDefinition';
|
||||
import { FieldMetadata, FieldURLMetadata } from '../FieldMetadata';
|
||||
|
||||
export const isFieldURL = (
|
||||
field: FieldDefinition<FieldMetadata>,
|
||||
): field is FieldDefinition<FieldURLMetadata> => field.type === 'url';
|
||||
@ -1,9 +0,0 @@
|
||||
import { FieldURLValue } from '../FieldMetadata';
|
||||
|
||||
// TODO: add yup
|
||||
export const isFieldURLValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldURLValue =>
|
||||
fieldValue !== null &&
|
||||
fieldValue !== undefined &&
|
||||
typeof fieldValue === 'string';
|
||||
@ -1,10 +0,0 @@
|
||||
import {
|
||||
ViewFieldBooleanMetadata,
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
} from '../ViewField';
|
||||
|
||||
export const isViewFieldBoolean = (
|
||||
field: ViewFieldDefinition<ViewFieldMetadata>,
|
||||
): field is ViewFieldDefinition<ViewFieldBooleanMetadata> =>
|
||||
field.metadata.type === 'boolean';
|
||||
@ -1,5 +0,0 @@
|
||||
import { ViewFieldBooleanValue } from '../ViewField';
|
||||
|
||||
export const isViewFieldBooleanValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is ViewFieldBooleanValue => typeof fieldValue === 'boolean';
|
||||
@ -1,10 +0,0 @@
|
||||
import {
|
||||
ViewFieldChipMetadata,
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
} from '../ViewField';
|
||||
|
||||
export const isViewFieldChip = (
|
||||
field: ViewFieldDefinition<ViewFieldMetadata>,
|
||||
): field is ViewFieldDefinition<ViewFieldChipMetadata> =>
|
||||
field.metadata.type === 'chip';
|
||||
@ -1,9 +0,0 @@
|
||||
import { ViewFieldChipValue } from '../ViewField';
|
||||
|
||||
// TODO: add yup
|
||||
export const isViewFieldChipValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is ViewFieldChipValue =>
|
||||
fieldValue !== null &&
|
||||
fieldValue !== undefined &&
|
||||
typeof fieldValue === 'string';
|
||||
@ -1,10 +0,0 @@
|
||||
import {
|
||||
ViewFieldDateMetadata,
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
} from '../ViewField';
|
||||
|
||||
export const isViewFieldDate = (
|
||||
field: ViewFieldDefinition<ViewFieldMetadata>,
|
||||
): field is ViewFieldDefinition<ViewFieldDateMetadata> =>
|
||||
field.metadata.type === 'date';
|
||||
@ -1,9 +0,0 @@
|
||||
import { ViewFieldDateValue } from '../ViewField';
|
||||
|
||||
// TODO: add yup
|
||||
export const isViewFieldDateValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is ViewFieldDateValue =>
|
||||
fieldValue !== null &&
|
||||
fieldValue !== undefined &&
|
||||
typeof fieldValue === 'string';
|
||||
@ -1,10 +0,0 @@
|
||||
import {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldDoubleTextMetadata,
|
||||
ViewFieldMetadata,
|
||||
} from '../ViewField';
|
||||
|
||||
export const isViewFieldDoubleText = (
|
||||
field: ViewFieldDefinition<ViewFieldMetadata>,
|
||||
): field is ViewFieldDefinition<ViewFieldDoubleTextMetadata> =>
|
||||
field.metadata.type === 'double-text';
|
||||
@ -1,10 +0,0 @@
|
||||
import {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldDoubleTextChipMetadata,
|
||||
ViewFieldMetadata,
|
||||
} from '../ViewField';
|
||||
|
||||
export const isViewFieldDoubleTextChip = (
|
||||
field: ViewFieldDefinition<ViewFieldMetadata>,
|
||||
): field is ViewFieldDefinition<ViewFieldDoubleTextChipMetadata> =>
|
||||
field.metadata.type === 'double-text-chip';
|
||||
@ -1,9 +0,0 @@
|
||||
import { ViewFieldDoubleTextChipValue } from '../ViewField';
|
||||
|
||||
// TODO: add yup
|
||||
export const isViewFieldDoubleTextChipValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is ViewFieldDoubleTextChipValue =>
|
||||
fieldValue !== null &&
|
||||
fieldValue !== undefined &&
|
||||
typeof fieldValue === 'object';
|
||||
@ -1,9 +0,0 @@
|
||||
import { ViewFieldDoubleTextValue } from '../ViewField';
|
||||
|
||||
// TODO: add yup
|
||||
export const isViewFieldDoubleTextValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is ViewFieldDoubleTextValue =>
|
||||
fieldValue !== null &&
|
||||
fieldValue !== undefined &&
|
||||
typeof fieldValue === 'object';
|
||||
@ -1,10 +0,0 @@
|
||||
import {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldEmailMetadata,
|
||||
ViewFieldMetadata,
|
||||
} from '../ViewField';
|
||||
|
||||
export const isViewFieldEmail = (
|
||||
field: ViewFieldDefinition<ViewFieldMetadata>,
|
||||
): field is ViewFieldDefinition<ViewFieldEmailMetadata> =>
|
||||
field.metadata.type === 'email';
|
||||
@ -1,8 +0,0 @@
|
||||
import { ViewFieldEmailValue } from '../ViewField';
|
||||
|
||||
export const isViewFieldEmailValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is ViewFieldEmailValue =>
|
||||
fieldValue !== null &&
|
||||
fieldValue !== undefined &&
|
||||
typeof fieldValue === 'string';
|
||||
@ -1,10 +0,0 @@
|
||||
import {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
ViewFieldMoneyMetadata,
|
||||
} from '../ViewField';
|
||||
|
||||
export const isViewFieldMoney = (
|
||||
field: ViewFieldDefinition<ViewFieldMetadata>,
|
||||
): field is ViewFieldDefinition<ViewFieldMoneyMetadata> =>
|
||||
field.metadata.type === 'moneyAmount';
|
||||
@ -1,7 +0,0 @@
|
||||
import { ViewFieldMoneyValue } from '../ViewField';
|
||||
|
||||
export const isViewFieldMoneyValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is ViewFieldMoneyValue =>
|
||||
fieldValue === null ||
|
||||
(fieldValue !== undefined && typeof fieldValue === 'number');
|
||||
@ -1,10 +0,0 @@
|
||||
import {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
ViewFieldNumberMetadata,
|
||||
} from '../ViewField';
|
||||
|
||||
export const isViewFieldNumber = (
|
||||
field: ViewFieldDefinition<ViewFieldMetadata>,
|
||||
): field is ViewFieldDefinition<ViewFieldNumberMetadata> =>
|
||||
field.metadata.type === 'number';
|
||||
@ -1,9 +0,0 @@
|
||||
import { ViewFieldNumberValue } from '../ViewField';
|
||||
|
||||
// TODO: add yup
|
||||
export const isViewFieldNumberValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is ViewFieldNumberValue =>
|
||||
fieldValue !== null &&
|
||||
fieldValue !== undefined &&
|
||||
typeof fieldValue === 'number';
|
||||
@ -1,10 +0,0 @@
|
||||
import {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
ViewFieldPhoneMetadata,
|
||||
} from '../ViewField';
|
||||
|
||||
export const isViewFieldPhone = (
|
||||
field: ViewFieldDefinition<ViewFieldMetadata>,
|
||||
): field is ViewFieldDefinition<ViewFieldPhoneMetadata> =>
|
||||
field.metadata.type === 'phone';
|
||||
@ -1,9 +0,0 @@
|
||||
import { ViewFieldPhoneValue } from '../ViewField';
|
||||
|
||||
// TODO: add yup
|
||||
export const isViewFieldPhoneValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is ViewFieldPhoneValue =>
|
||||
fieldValue !== null &&
|
||||
fieldValue !== undefined &&
|
||||
typeof fieldValue === 'string';
|
||||
@ -1,10 +0,0 @@
|
||||
import {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
ViewFieldProbabilityMetadata,
|
||||
} from '../ViewField';
|
||||
|
||||
export const isViewFieldProbability = (
|
||||
field: ViewFieldDefinition<ViewFieldMetadata>,
|
||||
): field is ViewFieldDefinition<ViewFieldProbabilityMetadata> =>
|
||||
field.metadata.type === 'probability';
|
||||
@ -1,9 +0,0 @@
|
||||
import { ViewFieldProbabilityValue } from '../ViewField';
|
||||
|
||||
// TODO: add yup
|
||||
export const isViewFieldProbabilityValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is ViewFieldProbabilityValue =>
|
||||
fieldValue !== null &&
|
||||
fieldValue !== undefined &&
|
||||
typeof fieldValue === 'number';
|
||||
@ -1,10 +0,0 @@
|
||||
import {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
ViewFieldRelationMetadata,
|
||||
} from '../ViewField';
|
||||
|
||||
export const isViewFieldRelation = (
|
||||
field: ViewFieldDefinition<ViewFieldMetadata>,
|
||||
): field is ViewFieldDefinition<ViewFieldRelationMetadata> =>
|
||||
field.metadata.type === 'relation';
|
||||
@ -1,7 +0,0 @@
|
||||
import { ViewFieldRelationValue } from '../ViewField';
|
||||
|
||||
// TODO: add yup
|
||||
export const isViewFieldRelationValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is ViewFieldRelationValue =>
|
||||
fieldValue !== undefined && typeof fieldValue === 'object';
|
||||
@ -1,10 +0,0 @@
|
||||
import {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
ViewFieldTextMetadata,
|
||||
} from '../ViewField';
|
||||
|
||||
export const isViewFieldText = (
|
||||
field: ViewFieldDefinition<ViewFieldMetadata>,
|
||||
): field is ViewFieldDefinition<ViewFieldTextMetadata> =>
|
||||
field.metadata.type === 'text';
|
||||
@ -1,9 +0,0 @@
|
||||
import { ViewFieldTextValue } from '../ViewField';
|
||||
|
||||
// TODO: add yup
|
||||
export const isViewFieldTextValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is ViewFieldTextValue =>
|
||||
fieldValue !== null &&
|
||||
fieldValue !== undefined &&
|
||||
typeof fieldValue === 'string';
|
||||
@ -1,10 +0,0 @@
|
||||
import {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
ViewFieldURLMetadata,
|
||||
} from '../ViewField';
|
||||
|
||||
export const isViewFieldURL = (
|
||||
field: ViewFieldDefinition<ViewFieldMetadata>,
|
||||
): field is ViewFieldDefinition<ViewFieldURLMetadata> =>
|
||||
field.metadata.type === 'url';
|
||||
@ -1,9 +0,0 @@
|
||||
import { ViewFieldURLValue } from '../ViewField';
|
||||
|
||||
// TODO: add yup
|
||||
export const isViewFieldURLValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is ViewFieldURLValue =>
|
||||
fieldValue !== null &&
|
||||
fieldValue !== undefined &&
|
||||
typeof fieldValue === 'string';
|
||||
@ -1,6 +1,6 @@
|
||||
import { DateDisplay } from '@/ui/content-display/components/DateDisplay';
|
||||
import { EditableField } from '@/ui/editable-field/components/EditableField';
|
||||
import { InlineCellContainer } from '@/ui/editable-field/components/InlineCellContainer';
|
||||
import { FieldRecoilScopeContext } from '@/ui/editable-field/states/recoil-scope-contexts/FieldRecoilScopeContext';
|
||||
import { DateDisplay } from '@/ui/field/meta-types/display/content-display/components/DateDisplay';
|
||||
import { IconComponent } from '@/ui/icon/types/IconComponent';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
import { parseDate } from '~/utils/date-utils';
|
||||
@ -30,7 +30,7 @@ export const DateEditableField = ({
|
||||
|
||||
return (
|
||||
<RecoilScope CustomRecoilScopeContext={FieldRecoilScopeContext}>
|
||||
<EditableField
|
||||
<InlineCellContainer
|
||||
IconLabel={Icon}
|
||||
label={label}
|
||||
editModeContent={
|
||||
|
||||
@ -4,7 +4,7 @@ import { DateInput } from '@/ui/input/components/DateInput';
|
||||
import { Nullable } from '~/types/Nullable';
|
||||
import { parseDate } from '~/utils/date-utils';
|
||||
|
||||
import { useEditableField } from '../../hooks/useEditableField';
|
||||
import { useInlineCell } from '../../hooks/useInlineCell';
|
||||
|
||||
type OwnProps = {
|
||||
value: string;
|
||||
@ -24,7 +24,7 @@ export const EditableFieldEditModeDate = ({
|
||||
setInternalValue(value);
|
||||
}, [value]);
|
||||
|
||||
const { closeEditableField } = useEditableField();
|
||||
const { closeInlineCell: closeEditableField } = useInlineCell();
|
||||
|
||||
const handleClickOutside = () => {
|
||||
closeEditableField();
|
||||
|
||||
Reference in New Issue
Block a user