Behaviour Fix on new record addition (#3113)
* Delete record if no company added * EditMode on First column of new row added * Fix * Minor fixes * Passed scopeId * Changed FieldInputs to accept onChange handler * Removed getFieldType --------- Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
@ -10,12 +10,15 @@ import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObje
|
|||||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||||
import { RecordTableActionBar } from '@/object-record/record-table/action-bar/components/RecordTableActionBar';
|
import { RecordTableActionBar } from '@/object-record/record-table/action-bar/components/RecordTableActionBar';
|
||||||
import { RecordTableContextMenu } from '@/object-record/record-table/context-menu/components/RecordTableContextMenu';
|
import { RecordTableContextMenu } from '@/object-record/record-table/context-menu/components/RecordTableContextMenu';
|
||||||
|
import { useSelectedTableCellEditMode } from '@/object-record/record-table/record-table-cell/hooks/useSelectedTableCellEditMode';
|
||||||
|
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useTableCell';
|
||||||
import { useIcons } from '@/ui/display/icon/hooks/useIcons';
|
import { useIcons } from '@/ui/display/icon/hooks/useIcons';
|
||||||
import { PageAddButton } from '@/ui/layout/page/PageAddButton';
|
import { PageAddButton } from '@/ui/layout/page/PageAddButton';
|
||||||
import { PageBody } from '@/ui/layout/page/PageBody';
|
import { PageBody } from '@/ui/layout/page/PageBody';
|
||||||
import { PageContainer } from '@/ui/layout/page/PageContainer';
|
import { PageContainer } from '@/ui/layout/page/PageContainer';
|
||||||
import { PageHeader } from '@/ui/layout/page/PageHeader';
|
import { PageHeader } from '@/ui/layout/page/PageHeader';
|
||||||
import { PageHotkeysEffect } from '@/ui/layout/page/PageHotkeysEffect';
|
import { PageHotkeysEffect } from '@/ui/layout/page/PageHotkeysEffect';
|
||||||
|
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||||
|
|
||||||
import { RecordTableContainer } from './RecordTableContainer';
|
import { RecordTableContainer } from './RecordTableContainer';
|
||||||
|
|
||||||
@ -57,11 +60,19 @@ export const RecordTablePage = () => {
|
|||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const recordTableId = objectNamePlural ?? '';
|
||||||
|
|
||||||
|
const { setSelectedTableCellEditMode } = useSelectedTableCellEditMode({
|
||||||
|
scopeId: recordTableId,
|
||||||
|
});
|
||||||
|
const setHotkeyScope = useSetHotkeyScope();
|
||||||
|
|
||||||
const handleAddButtonClick = async () => {
|
const handleAddButtonClick = async () => {
|
||||||
await createOneObject?.({});
|
await createOneObject?.({});
|
||||||
};
|
|
||||||
|
|
||||||
const recordTableId = objectNamePlural ?? '';
|
setSelectedTableCellEditMode(0, 0);
|
||||||
|
setHotkeyScope(DEFAULT_CELL_SCOPE.scope, DEFAULT_CELL_SCOPE.customScopes);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
|
|||||||
@ -0,0 +1,22 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { isEntityFieldEditModeEmptyFamilySelector } from '@/object-record/field/states/selectors/isEntityFieldEditModeEmptyFamilySelector';
|
||||||
|
|
||||||
|
import { FieldContext } from '../contexts/FieldContext';
|
||||||
|
|
||||||
|
export const useIsFieldEditModeValueEmpty = () => {
|
||||||
|
const { entityId, fieldDefinition } = useContext(FieldContext);
|
||||||
|
|
||||||
|
const isFieldEditModeValueEmpty = useRecoilValue(
|
||||||
|
isEntityFieldEditModeEmptyFamilySelector({
|
||||||
|
fieldDefinition: {
|
||||||
|
type: fieldDefinition.type,
|
||||||
|
},
|
||||||
|
fieldName: fieldDefinition.metadata.fieldName,
|
||||||
|
entityId,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return isFieldEditModeValueEmpty;
|
||||||
|
};
|
||||||
@ -0,0 +1,112 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
|
import { entityFieldsEditModeValueFamilySelector } from '@/object-record/field/states/selectors/entityFieldsEditModeValueFamilySelector';
|
||||||
|
import { isFieldFullName } from '@/object-record/field/types/guards/isFieldFullName';
|
||||||
|
import { isFieldFullNameValue } from '@/object-record/field/types/guards/isFieldFullNameValue';
|
||||||
|
|
||||||
|
import { FieldContext } from '../contexts/FieldContext';
|
||||||
|
import { isFieldBoolean } from '../types/guards/isFieldBoolean';
|
||||||
|
import { isFieldBooleanValue } from '../types/guards/isFieldBooleanValue';
|
||||||
|
import { isFieldCurrency } from '../types/guards/isFieldCurrency';
|
||||||
|
import { isFieldCurrencyValue } from '../types/guards/isFieldCurrencyValue';
|
||||||
|
import { isFieldDateTime } from '../types/guards/isFieldDateTime';
|
||||||
|
import { isFieldDateTimeValue } from '../types/guards/isFieldDateTimeValue';
|
||||||
|
import { isFieldEmail } from '../types/guards/isFieldEmail';
|
||||||
|
import { isFieldEmailValue } from '../types/guards/isFieldEmailValue';
|
||||||
|
import { isFieldLink } from '../types/guards/isFieldLink';
|
||||||
|
import { isFieldLinkValue } from '../types/guards/isFieldLinkValue';
|
||||||
|
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 { isFieldRating } from '../types/guards/isFieldRating';
|
||||||
|
import { isFieldRatingValue } from '../types/guards/isFieldRatingValue';
|
||||||
|
import { isFieldRelation } from '../types/guards/isFieldRelation';
|
||||||
|
import { isFieldRelationValue } from '../types/guards/isFieldRelationValue';
|
||||||
|
import { isFieldText } from '../types/guards/isFieldText';
|
||||||
|
import { isFieldTextValue } from '../types/guards/isFieldTextValue';
|
||||||
|
|
||||||
|
export const useSaveFieldEditModeValue = () => {
|
||||||
|
const { entityId, fieldDefinition } = useContext(FieldContext);
|
||||||
|
|
||||||
|
const saveFieldEditModeValue = useRecoilCallback(
|
||||||
|
({ set }) =>
|
||||||
|
(currentValue: unknown) => {
|
||||||
|
const fieldIsRelation =
|
||||||
|
isFieldRelation(fieldDefinition) &&
|
||||||
|
isFieldRelationValue(currentValue);
|
||||||
|
|
||||||
|
const fieldIsText =
|
||||||
|
isFieldText(fieldDefinition) && isFieldTextValue(currentValue);
|
||||||
|
|
||||||
|
const fieldIsEmail =
|
||||||
|
isFieldEmail(fieldDefinition) && isFieldEmailValue(currentValue);
|
||||||
|
|
||||||
|
const fieldIsDateTime =
|
||||||
|
isFieldDateTime(fieldDefinition) &&
|
||||||
|
isFieldDateTimeValue(currentValue);
|
||||||
|
|
||||||
|
const fieldIsLink =
|
||||||
|
isFieldLink(fieldDefinition) && isFieldLinkValue(currentValue);
|
||||||
|
|
||||||
|
const fieldIsBoolean =
|
||||||
|
isFieldBoolean(fieldDefinition) && isFieldBooleanValue(currentValue);
|
||||||
|
|
||||||
|
const fieldIsProbability =
|
||||||
|
isFieldRating(fieldDefinition) && isFieldRatingValue(currentValue);
|
||||||
|
|
||||||
|
const fieldIsNumber =
|
||||||
|
isFieldNumber(fieldDefinition) && isFieldNumberValue(currentValue);
|
||||||
|
|
||||||
|
const fieldIsCurrency =
|
||||||
|
isFieldCurrency(fieldDefinition) &&
|
||||||
|
isFieldCurrencyValue(currentValue);
|
||||||
|
|
||||||
|
const fieldIsFullName =
|
||||||
|
isFieldFullName(fieldDefinition) &&
|
||||||
|
isFieldFullNameValue(currentValue);
|
||||||
|
|
||||||
|
const fieldIsPhone =
|
||||||
|
isFieldPhone(fieldDefinition) && isFieldPhoneValue(currentValue);
|
||||||
|
|
||||||
|
if (fieldIsRelation) {
|
||||||
|
const fieldName = fieldDefinition.metadata.fieldName;
|
||||||
|
|
||||||
|
set(
|
||||||
|
entityFieldsEditModeValueFamilySelector({ entityId, fieldName }),
|
||||||
|
currentValue,
|
||||||
|
);
|
||||||
|
} else if (
|
||||||
|
fieldIsText ||
|
||||||
|
fieldIsBoolean ||
|
||||||
|
fieldIsEmail ||
|
||||||
|
fieldIsProbability ||
|
||||||
|
fieldIsNumber ||
|
||||||
|
fieldIsDateTime ||
|
||||||
|
fieldIsPhone ||
|
||||||
|
fieldIsLink ||
|
||||||
|
fieldIsCurrency ||
|
||||||
|
fieldIsFullName
|
||||||
|
) {
|
||||||
|
const fieldName = fieldDefinition.metadata.fieldName;
|
||||||
|
|
||||||
|
set(
|
||||||
|
entityFieldsEditModeValueFamilySelector({ entityId, fieldName }),
|
||||||
|
currentValue,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
`Invalid value to save: ${JSON.stringify(
|
||||||
|
currentValue,
|
||||||
|
)} for type : ${
|
||||||
|
fieldDefinition.type
|
||||||
|
}, type may not be implemented in useSaveFieldEditModeValue.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[entityId, fieldDefinition],
|
||||||
|
);
|
||||||
|
|
||||||
|
return saveFieldEditModeValue;
|
||||||
|
};
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { useSaveFieldEditModeValue } from '@/object-record/field/hooks/useSaveFieldEditModeValue';
|
||||||
import { TextInput } from '@/ui/field/input/components/TextInput';
|
import { TextInput } from '@/ui/field/input/components/TextInput';
|
||||||
|
|
||||||
import { FieldInputOverlay } from '../../../../../ui/field/input/components/FieldInputOverlay';
|
import { FieldInputOverlay } from '../../../../../ui/field/input/components/FieldInputOverlay';
|
||||||
@ -27,6 +28,8 @@ export const CurrencyFieldInput = ({
|
|||||||
persistCurrencyField,
|
persistCurrencyField,
|
||||||
} = useCurrencyField();
|
} = useCurrencyField();
|
||||||
|
|
||||||
|
const saveEditModeValue = useSaveFieldEditModeValue();
|
||||||
|
|
||||||
const handleEnter = (newValue: string) => {
|
const handleEnter = (newValue: string) => {
|
||||||
onEnter?.(() => {
|
onEnter?.(() => {
|
||||||
persistCurrencyField({
|
persistCurrencyField({
|
||||||
@ -75,6 +78,13 @@ export const CurrencyFieldInput = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleChange = (newValue: string) => {
|
||||||
|
saveEditModeValue({
|
||||||
|
amountText: newValue,
|
||||||
|
currencyCode: initialCurrencyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FieldInputOverlay>
|
<FieldInputOverlay>
|
||||||
<TextInput
|
<TextInput
|
||||||
@ -87,6 +97,7 @@ export const CurrencyFieldInput = ({
|
|||||||
onShiftTab={handleShiftTab}
|
onShiftTab={handleShiftTab}
|
||||||
onTab={handleTab}
|
onTab={handleTab}
|
||||||
hotkeyScope={hotkeyScope}
|
hotkeyScope={hotkeyScope}
|
||||||
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
</FieldInputOverlay>
|
</FieldInputOverlay>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { useSaveFieldEditModeValue } from '@/object-record/field/hooks/useSaveFieldEditModeValue';
|
||||||
import { DateInput } from '@/ui/field/input/components/DateInput';
|
import { DateInput } from '@/ui/field/input/components/DateInput';
|
||||||
import { Nullable } from '~/types/Nullable';
|
import { Nullable } from '~/types/Nullable';
|
||||||
|
|
||||||
@ -20,6 +21,7 @@ export const DateFieldInput = ({
|
|||||||
const { fieldValue, hotkeyScope, clearable } = useDateTimeField();
|
const { fieldValue, hotkeyScope, clearable } = useDateTimeField();
|
||||||
|
|
||||||
const persistField = usePersistField();
|
const persistField = usePersistField();
|
||||||
|
const saveEditModeValue = useSaveFieldEditModeValue();
|
||||||
|
|
||||||
const persistDate = (newDate: Nullable<Date>) => {
|
const persistDate = (newDate: Nullable<Date>) => {
|
||||||
if (!newDate) {
|
if (!newDate) {
|
||||||
@ -46,6 +48,10 @@ export const DateFieldInput = ({
|
|||||||
onClickOutside?.(() => persistDate(newDate));
|
onClickOutside?.(() => persistDate(newDate));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleChange = (newDate: Nullable<Date>) => {
|
||||||
|
saveEditModeValue(newDate);
|
||||||
|
};
|
||||||
|
|
||||||
const dateValue = fieldValue ? new Date(fieldValue) : null;
|
const dateValue = fieldValue ? new Date(fieldValue) : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -56,6 +62,7 @@ export const DateFieldInput = ({
|
|||||||
onEscape={handleEscape}
|
onEscape={handleEscape}
|
||||||
value={dateValue}
|
value={dateValue}
|
||||||
clearable={clearable}
|
clearable={clearable}
|
||||||
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { useSaveFieldEditModeValue } from '@/object-record/field/hooks/useSaveFieldEditModeValue';
|
||||||
import { TextInput } from '@/ui/field/input/components/TextInput';
|
import { TextInput } from '@/ui/field/input/components/TextInput';
|
||||||
|
|
||||||
import { FieldInputOverlay } from '../../../../../ui/field/input/components/FieldInputOverlay';
|
import { FieldInputOverlay } from '../../../../../ui/field/input/components/FieldInputOverlay';
|
||||||
@ -24,6 +25,7 @@ export const EmailFieldInput = ({
|
|||||||
const { fieldDefinition, initialValue, hotkeyScope } = useEmailField();
|
const { fieldDefinition, initialValue, hotkeyScope } = useEmailField();
|
||||||
|
|
||||||
const persistField = usePersistField();
|
const persistField = usePersistField();
|
||||||
|
const saveEditModeValue = useSaveFieldEditModeValue();
|
||||||
|
|
||||||
const handleEnter = (newText: string) => {
|
const handleEnter = (newText: string) => {
|
||||||
onEnter?.(() => persistField(newText));
|
onEnter?.(() => persistField(newText));
|
||||||
@ -48,6 +50,10 @@ export const EmailFieldInput = ({
|
|||||||
onShiftTab?.(() => persistField(newText));
|
onShiftTab?.(() => persistField(newText));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleChange = (newText: string) => {
|
||||||
|
saveEditModeValue(newText);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FieldInputOverlay>
|
<FieldInputOverlay>
|
||||||
<TextInput
|
<TextInput
|
||||||
@ -60,6 +66,7 @@ export const EmailFieldInput = ({
|
|||||||
onShiftTab={handleShiftTab}
|
onShiftTab={handleShiftTab}
|
||||||
onTab={handleTab}
|
onTab={handleTab}
|
||||||
hotkeyScope={hotkeyScope}
|
hotkeyScope={hotkeyScope}
|
||||||
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
</FieldInputOverlay>
|
</FieldInputOverlay>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { useSaveFieldEditModeValue } from '@/object-record/field/hooks/useSaveFieldEditModeValue';
|
||||||
import { useFullNameField } from '@/object-record/field/meta-types/hooks/useFullNameField';
|
import { useFullNameField } from '@/object-record/field/meta-types/hooks/useFullNameField';
|
||||||
import { FieldDoubleText } from '@/object-record/field/types/FieldDoubleText';
|
import { FieldDoubleText } from '@/object-record/field/types/FieldDoubleText';
|
||||||
import { DoubleTextInput } from '@/ui/field/input/components/DoubleTextInput';
|
import { DoubleTextInput } from '@/ui/field/input/components/DoubleTextInput';
|
||||||
@ -7,6 +8,12 @@ import { usePersistField } from '../../../hooks/usePersistField';
|
|||||||
|
|
||||||
import { FieldInputEvent } from './DateFieldInput';
|
import { FieldInputEvent } from './DateFieldInput';
|
||||||
|
|
||||||
|
const FIRST_NAME_PLACEHOLDER_WITH_SPECIAL_CHARACTER_TO_AVOID_PASSWORD_MANAGERS =
|
||||||
|
'First name';
|
||||||
|
|
||||||
|
const LAST_NAME_PLACEHOLDER_WITH_SPECIAL_CHARACTER_TO_AVOID_PASSWORD_MANAGERS =
|
||||||
|
'Last name';
|
||||||
|
|
||||||
export type FullNameFieldInputProps = {
|
export type FullNameFieldInputProps = {
|
||||||
onClickOutside?: FieldInputEvent;
|
onClickOutside?: FieldInputEvent;
|
||||||
onEnter?: FieldInputEvent;
|
onEnter?: FieldInputEvent;
|
||||||
@ -25,6 +32,8 @@ export const FullNameFieldInput = ({
|
|||||||
const { hotkeyScope, initialValue } = useFullNameField();
|
const { hotkeyScope, initialValue } = useFullNameField();
|
||||||
|
|
||||||
const persistField = usePersistField();
|
const persistField = usePersistField();
|
||||||
|
const saveEditModeValue = useSaveFieldEditModeValue();
|
||||||
|
|
||||||
const convertToFullName = (newDoubleText: FieldDoubleText) => {
|
const convertToFullName = (newDoubleText: FieldDoubleText) => {
|
||||||
return {
|
return {
|
||||||
firstName: newDoubleText.firstValue,
|
firstName: newDoubleText.firstValue,
|
||||||
@ -55,19 +64,28 @@ export const FullNameFieldInput = ({
|
|||||||
onShiftTab?.(() => persistField(convertToFullName(newDoubleText)));
|
onShiftTab?.(() => persistField(convertToFullName(newDoubleText)));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleChange = (newDoubleText: FieldDoubleText) => {
|
||||||
|
saveEditModeValue(convertToFullName(newDoubleText));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FieldInputOverlay>
|
<FieldInputOverlay>
|
||||||
<DoubleTextInput
|
<DoubleTextInput
|
||||||
firstValue={initialValue.firstName}
|
firstValue={initialValue.firstName}
|
||||||
secondValue={initialValue.lastName}
|
secondValue={initialValue.lastName}
|
||||||
firstValuePlaceholder={'First name'}
|
firstValuePlaceholder={
|
||||||
secondValuePlaceholder={'Last name'}
|
FIRST_NAME_PLACEHOLDER_WITH_SPECIAL_CHARACTER_TO_AVOID_PASSWORD_MANAGERS
|
||||||
|
}
|
||||||
|
secondValuePlaceholder={
|
||||||
|
LAST_NAME_PLACEHOLDER_WITH_SPECIAL_CHARACTER_TO_AVOID_PASSWORD_MANAGERS
|
||||||
|
}
|
||||||
onClickOutside={handleClickOutside}
|
onClickOutside={handleClickOutside}
|
||||||
onEnter={handleEnter}
|
onEnter={handleEnter}
|
||||||
onEscape={handleEscape}
|
onEscape={handleEscape}
|
||||||
onShiftTab={handleShiftTab}
|
onShiftTab={handleShiftTab}
|
||||||
onTab={handleTab}
|
onTab={handleTab}
|
||||||
hotkeyScope={hotkeyScope}
|
hotkeyScope={hotkeyScope}
|
||||||
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
</FieldInputOverlay>
|
</FieldInputOverlay>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { useSaveFieldEditModeValue } from '@/object-record/field/hooks/useSaveFieldEditModeValue';
|
||||||
import { TextInput } from '@/ui/field/input/components/TextInput';
|
import { TextInput } from '@/ui/field/input/components/TextInput';
|
||||||
|
|
||||||
import { FieldInputOverlay } from '../../../../../ui/field/input/components/FieldInputOverlay';
|
import { FieldInputOverlay } from '../../../../../ui/field/input/components/FieldInputOverlay';
|
||||||
@ -22,6 +23,8 @@ export const LinkFieldInput = ({
|
|||||||
}: LinkFieldInputProps) => {
|
}: LinkFieldInputProps) => {
|
||||||
const { initialValue, hotkeyScope, persistLinkField } = useLinkField();
|
const { initialValue, hotkeyScope, persistLinkField } = useLinkField();
|
||||||
|
|
||||||
|
const saveEditModeValue = useSaveFieldEditModeValue();
|
||||||
|
|
||||||
const handleEnter = (newURL: string) => {
|
const handleEnter = (newURL: string) => {
|
||||||
onEnter?.(() =>
|
onEnter?.(() =>
|
||||||
persistLinkField({
|
persistLinkField({
|
||||||
@ -70,6 +73,13 @@ export const LinkFieldInput = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleChange = (newURL: string) => {
|
||||||
|
saveEditModeValue({
|
||||||
|
url: newURL,
|
||||||
|
label: newURL,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FieldInputOverlay>
|
<FieldInputOverlay>
|
||||||
<TextInput
|
<TextInput
|
||||||
@ -82,6 +92,7 @@ export const LinkFieldInput = ({
|
|||||||
onEscape={handleEscape}
|
onEscape={handleEscape}
|
||||||
onTab={handleTab}
|
onTab={handleTab}
|
||||||
onShiftTab={handleShiftTab}
|
onShiftTab={handleShiftTab}
|
||||||
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
</FieldInputOverlay>
|
</FieldInputOverlay>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { useSaveFieldEditModeValue } from '@/object-record/field/hooks/useSaveFieldEditModeValue';
|
||||||
import { TextInput } from '@/ui/field/input/components/TextInput';
|
import { TextInput } from '@/ui/field/input/components/TextInput';
|
||||||
|
|
||||||
import { FieldInputOverlay } from '../../../../../ui/field/input/components/FieldInputOverlay';
|
import { FieldInputOverlay } from '../../../../../ui/field/input/components/FieldInputOverlay';
|
||||||
@ -23,6 +24,8 @@ export const NumberFieldInput = ({
|
|||||||
const { fieldDefinition, initialValue, hotkeyScope, persistNumberField } =
|
const { fieldDefinition, initialValue, hotkeyScope, persistNumberField } =
|
||||||
useNumberField();
|
useNumberField();
|
||||||
|
|
||||||
|
const saveEditModeValue = useSaveFieldEditModeValue();
|
||||||
|
|
||||||
const handleEnter = (newText: string) => {
|
const handleEnter = (newText: string) => {
|
||||||
onEnter?.(() => persistNumberField(newText));
|
onEnter?.(() => persistNumberField(newText));
|
||||||
};
|
};
|
||||||
@ -46,6 +49,10 @@ export const NumberFieldInput = ({
|
|||||||
onShiftTab?.(() => persistNumberField(newText));
|
onShiftTab?.(() => persistNumberField(newText));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleChange = (newText: string) => {
|
||||||
|
saveEditModeValue(newText);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FieldInputOverlay>
|
<FieldInputOverlay>
|
||||||
<TextInput
|
<TextInput
|
||||||
@ -58,6 +65,7 @@ export const NumberFieldInput = ({
|
|||||||
onShiftTab={handleShiftTab}
|
onShiftTab={handleShiftTab}
|
||||||
onTab={handleTab}
|
onTab={handleTab}
|
||||||
hotkeyScope={hotkeyScope}
|
hotkeyScope={hotkeyScope}
|
||||||
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
</FieldInputOverlay>
|
</FieldInputOverlay>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { useSaveFieldEditModeValue } from '@/object-record/field/hooks/useSaveFieldEditModeValue';
|
||||||
import { PhoneInput } from '@/ui/field/input/components/PhoneInput';
|
import { PhoneInput } from '@/ui/field/input/components/PhoneInput';
|
||||||
|
|
||||||
import { FieldInputOverlay } from '../../../../../ui/field/input/components/FieldInputOverlay';
|
import { FieldInputOverlay } from '../../../../../ui/field/input/components/FieldInputOverlay';
|
||||||
@ -23,6 +24,8 @@ export const PhoneFieldInput = ({
|
|||||||
const { fieldDefinition, initialValue, hotkeyScope, persistPhoneField } =
|
const { fieldDefinition, initialValue, hotkeyScope, persistPhoneField } =
|
||||||
usePhoneField();
|
usePhoneField();
|
||||||
|
|
||||||
|
const saveEditModeValue = useSaveFieldEditModeValue();
|
||||||
|
|
||||||
const handleEnter = (newText: string) => {
|
const handleEnter = (newText: string) => {
|
||||||
onEnter?.(() => persistPhoneField(newText));
|
onEnter?.(() => persistPhoneField(newText));
|
||||||
};
|
};
|
||||||
@ -46,6 +49,10 @@ export const PhoneFieldInput = ({
|
|||||||
onShiftTab?.(() => persistPhoneField(newText));
|
onShiftTab?.(() => persistPhoneField(newText));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleChange = (newText: string) => {
|
||||||
|
saveEditModeValue(newText);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FieldInputOverlay>
|
<FieldInputOverlay>
|
||||||
<PhoneInput
|
<PhoneInput
|
||||||
@ -58,6 +65,7 @@ export const PhoneFieldInput = ({
|
|||||||
onShiftTab={handleShiftTab}
|
onShiftTab={handleShiftTab}
|
||||||
onTab={handleTab}
|
onTab={handleTab}
|
||||||
hotkeyScope={hotkeyScope}
|
hotkeyScope={hotkeyScope}
|
||||||
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
</FieldInputOverlay>
|
</FieldInputOverlay>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { useSaveFieldEditModeValue } from '@/object-record/field/hooks/useSaveFieldEditModeValue';
|
||||||
import { TextInput } from '@/ui/field/input/components/TextInput';
|
import { TextInput } from '@/ui/field/input/components/TextInput';
|
||||||
|
|
||||||
import { FieldInputOverlay } from '../../../../../ui/field/input/components/FieldInputOverlay';
|
import { FieldInputOverlay } from '../../../../../ui/field/input/components/FieldInputOverlay';
|
||||||
@ -24,6 +25,7 @@ export const TextFieldInput = ({
|
|||||||
const { fieldDefinition, initialValue, hotkeyScope } = useTextField();
|
const { fieldDefinition, initialValue, hotkeyScope } = useTextField();
|
||||||
|
|
||||||
const persistField = usePersistField();
|
const persistField = usePersistField();
|
||||||
|
const saveEditModeValue = useSaveFieldEditModeValue();
|
||||||
|
|
||||||
const handleEnter = (newText: string) => {
|
const handleEnter = (newText: string) => {
|
||||||
onEnter?.(() => persistField(newText));
|
onEnter?.(() => persistField(newText));
|
||||||
@ -48,6 +50,10 @@ export const TextFieldInput = ({
|
|||||||
onShiftTab?.(() => persistField(newText));
|
onShiftTab?.(() => persistField(newText));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleChange = (newText: string) => {
|
||||||
|
saveEditModeValue(newText);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FieldInputOverlay>
|
<FieldInputOverlay>
|
||||||
<TextInput
|
<TextInput
|
||||||
@ -60,6 +66,7 @@ export const TextFieldInput = ({
|
|||||||
onShiftTab={handleShiftTab}
|
onShiftTab={handleShiftTab}
|
||||||
onTab={handleTab}
|
onTab={handleTab}
|
||||||
hotkeyScope={hotkeyScope}
|
hotkeyScope={hotkeyScope}
|
||||||
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
</FieldInputOverlay>
|
</FieldInputOverlay>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -0,0 +1,9 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
export const entityFieldsEditModeValueFamilyState = atomFamily<
|
||||||
|
Record<string, unknown> | null,
|
||||||
|
string
|
||||||
|
>({
|
||||||
|
key: 'entityFieldsEditModeValueFamilyState',
|
||||||
|
default: null,
|
||||||
|
});
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import { selectorFamily } from 'recoil';
|
||||||
|
|
||||||
|
import { entityFieldsEditModeValueFamilyState } from '@/object-record/field/states/entityFieldsEditModeValueFamilyState';
|
||||||
|
|
||||||
|
export const entityFieldsEditModeValueFamilySelector = selectorFamily({
|
||||||
|
key: 'entityFieldsEditModeValueFamilySelector',
|
||||||
|
get:
|
||||||
|
<T>({ fieldName, entityId }: { fieldName: string; entityId: string }) =>
|
||||||
|
({ get }) =>
|
||||||
|
get(entityFieldsEditModeValueFamilyState(entityId))?.[fieldName] as T,
|
||||||
|
set:
|
||||||
|
<T>({ fieldName, entityId }: { fieldName: string; entityId: string }) =>
|
||||||
|
({ set }, newValue: T) =>
|
||||||
|
set(entityFieldsEditModeValueFamilyState(entityId), (prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
[fieldName]: newValue,
|
||||||
|
})),
|
||||||
|
});
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
import { selectorFamily } from 'recoil';
|
||||||
|
|
||||||
|
import { entityFieldsEditModeValueFamilyState } from '@/object-record/field/states/entityFieldsEditModeValueFamilyState';
|
||||||
|
import { isFieldValueEmpty } from '@/object-record/field/utils/isFieldValueEmpty';
|
||||||
|
|
||||||
|
import { FieldDefinition } from '../../types/FieldDefinition';
|
||||||
|
import { FieldMetadata } from '../../types/FieldMetadata';
|
||||||
|
|
||||||
|
export const isEntityFieldEditModeEmptyFamilySelector = selectorFamily({
|
||||||
|
key: 'isEntityFieldEditModeEmptyFamilySelector',
|
||||||
|
get: ({
|
||||||
|
fieldDefinition,
|
||||||
|
fieldName,
|
||||||
|
entityId,
|
||||||
|
}: {
|
||||||
|
fieldDefinition: Pick<FieldDefinition<FieldMetadata>, 'type'>;
|
||||||
|
fieldName: string;
|
||||||
|
entityId: string;
|
||||||
|
}) => {
|
||||||
|
return ({ get }) => {
|
||||||
|
const fieldValue = get(entityFieldsEditModeValueFamilyState(entityId))?.[
|
||||||
|
fieldName
|
||||||
|
];
|
||||||
|
|
||||||
|
return isFieldValueEmpty({
|
||||||
|
fieldDefinition,
|
||||||
|
fieldValue,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -1,28 +1,10 @@
|
|||||||
import { selectorFamily } from 'recoil';
|
import { selectorFamily } from 'recoil';
|
||||||
|
|
||||||
import { isFieldFullName } from '@/object-record/field/types/guards/isFieldFullName';
|
import { entityFieldsFamilyState } from '@/object-record/field/states/entityFieldsFamilyState';
|
||||||
import { isFieldFullNameValue } from '@/object-record/field/types/guards/isFieldFullNameValue';
|
import { isFieldValueEmpty } from '@/object-record/field/utils/isFieldValueEmpty';
|
||||||
import { isFieldSelect } from '@/object-record/field/types/guards/isFieldSelect';
|
|
||||||
import { isFieldUuid } from '@/object-record/field/types/guards/isFieldUuid';
|
|
||||||
import { assertNotNull } from '~/utils/assert';
|
|
||||||
|
|
||||||
import { FieldDefinition } from '../../types/FieldDefinition';
|
import { FieldDefinition } from '../../types/FieldDefinition';
|
||||||
import { FieldMetadata } from '../../types/FieldMetadata';
|
import { FieldMetadata } from '../../types/FieldMetadata';
|
||||||
import { isFieldBoolean } from '../../types/guards/isFieldBoolean';
|
|
||||||
import { isFieldCurrency } from '../../types/guards/isFieldCurrency';
|
|
||||||
import { isFieldCurrencyValue } from '../../types/guards/isFieldCurrencyValue';
|
|
||||||
import { isFieldDateTime } from '../../types/guards/isFieldDateTime';
|
|
||||||
import { isFieldEmail } from '../../types/guards/isFieldEmail';
|
|
||||||
import { isFieldLink } from '../../types/guards/isFieldLink';
|
|
||||||
import { isFieldLinkValue } from '../../types/guards/isFieldLinkValue';
|
|
||||||
import { isFieldNumber } from '../../types/guards/isFieldNumber';
|
|
||||||
import { isFieldRating } from '../../types/guards/isFieldRating';
|
|
||||||
import { isFieldRelation } from '../../types/guards/isFieldRelation';
|
|
||||||
import { isFieldRelationValue } from '../../types/guards/isFieldRelationValue';
|
|
||||||
import { isFieldText } from '../../types/guards/isFieldText';
|
|
||||||
import { entityFieldsFamilyState } from '../entityFieldsFamilyState';
|
|
||||||
|
|
||||||
const isValueEmpty = (value: unknown) => !assertNotNull(value) || value === '';
|
|
||||||
|
|
||||||
export const isEntityFieldEmptyFamilySelector = selectorFamily({
|
export const isEntityFieldEmptyFamilySelector = selectorFamily({
|
||||||
key: 'isEntityFieldEmptyFamilySelector',
|
key: 'isEntityFieldEmptyFamilySelector',
|
||||||
@ -36,57 +18,12 @@ export const isEntityFieldEmptyFamilySelector = selectorFamily({
|
|||||||
entityId: string;
|
entityId: string;
|
||||||
}) => {
|
}) => {
|
||||||
return ({ get }) => {
|
return ({ get }) => {
|
||||||
if (
|
const fieldValue = get(entityFieldsFamilyState(entityId))?.[fieldName];
|
||||||
isFieldUuid(fieldDefinition) ||
|
|
||||||
isFieldText(fieldDefinition) ||
|
|
||||||
isFieldDateTime(fieldDefinition) ||
|
|
||||||
isFieldNumber(fieldDefinition) ||
|
|
||||||
isFieldRating(fieldDefinition) ||
|
|
||||||
isFieldEmail(fieldDefinition) ||
|
|
||||||
isFieldBoolean(fieldDefinition) ||
|
|
||||||
isFieldSelect(fieldDefinition)
|
|
||||||
//|| isFieldPhone(fieldDefinition)
|
|
||||||
) {
|
|
||||||
const fieldValue = get(entityFieldsFamilyState(entityId))?.[
|
|
||||||
fieldName
|
|
||||||
] as string | number | boolean | null;
|
|
||||||
|
|
||||||
return isValueEmpty(fieldValue);
|
return isFieldValueEmpty({
|
||||||
}
|
fieldDefinition,
|
||||||
|
fieldValue,
|
||||||
if (isFieldRelation(fieldDefinition)) {
|
});
|
||||||
const fieldValue = get(entityFieldsFamilyState(entityId))?.[fieldName];
|
|
||||||
|
|
||||||
return isFieldRelationValue(fieldValue) && isValueEmpty(fieldValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isFieldCurrency(fieldDefinition)) {
|
|
||||||
const fieldValue = get(entityFieldsFamilyState(entityId))?.[fieldName];
|
|
||||||
|
|
||||||
return (
|
|
||||||
!isFieldCurrencyValue(fieldValue) ||
|
|
||||||
isValueEmpty(fieldValue?.amountMicros)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isFieldFullName(fieldDefinition)) {
|
|
||||||
const fieldValue = get(entityFieldsFamilyState(entityId))?.[fieldName];
|
|
||||||
|
|
||||||
return (
|
|
||||||
!isFieldFullNameValue(fieldValue) ||
|
|
||||||
isValueEmpty(fieldValue?.firstName + fieldValue?.lastName)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isFieldLink(fieldDefinition)) {
|
|
||||||
const fieldValue = get(entityFieldsFamilyState(entityId))?.[fieldName];
|
|
||||||
|
|
||||||
return !isFieldLinkValue(fieldValue) || isValueEmpty(fieldValue?.url);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(
|
|
||||||
`Entity field type not supported in isEntityFieldEmptyFamilySelector : ${fieldDefinition.type}}`,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -0,0 +1,69 @@
|
|||||||
|
import { FieldDefinition } from '@/object-record/field/types/FieldDefinition';
|
||||||
|
import { FieldMetadata } from '@/object-record/field/types/FieldMetadata';
|
||||||
|
import { isFieldBoolean } from '@/object-record/field/types/guards/isFieldBoolean';
|
||||||
|
import { isFieldCurrency } from '@/object-record/field/types/guards/isFieldCurrency';
|
||||||
|
import { isFieldCurrencyValue } from '@/object-record/field/types/guards/isFieldCurrencyValue';
|
||||||
|
import { isFieldDateTime } from '@/object-record/field/types/guards/isFieldDateTime';
|
||||||
|
import { isFieldEmail } from '@/object-record/field/types/guards/isFieldEmail';
|
||||||
|
import { isFieldFullName } from '@/object-record/field/types/guards/isFieldFullName';
|
||||||
|
import { isFieldFullNameValue } from '@/object-record/field/types/guards/isFieldFullNameValue';
|
||||||
|
import { isFieldLink } from '@/object-record/field/types/guards/isFieldLink';
|
||||||
|
import { isFieldLinkValue } from '@/object-record/field/types/guards/isFieldLinkValue';
|
||||||
|
import { isFieldNumber } from '@/object-record/field/types/guards/isFieldNumber';
|
||||||
|
import { isFieldRating } from '@/object-record/field/types/guards/isFieldRating';
|
||||||
|
import { isFieldRelation } from '@/object-record/field/types/guards/isFieldRelation';
|
||||||
|
import { isFieldRelationValue } from '@/object-record/field/types/guards/isFieldRelationValue';
|
||||||
|
import { isFieldSelect } from '@/object-record/field/types/guards/isFieldSelect';
|
||||||
|
import { isFieldText } from '@/object-record/field/types/guards/isFieldText';
|
||||||
|
import { isFieldUuid } from '@/object-record/field/types/guards/isFieldUuid';
|
||||||
|
import { assertNotNull } from '~/utils/assert';
|
||||||
|
|
||||||
|
const isValueEmpty = (value: unknown) => !assertNotNull(value) || value === '';
|
||||||
|
|
||||||
|
export const isFieldValueEmpty = ({
|
||||||
|
fieldDefinition,
|
||||||
|
fieldValue,
|
||||||
|
}: {
|
||||||
|
fieldDefinition: Pick<FieldDefinition<FieldMetadata>, 'type'>;
|
||||||
|
fieldValue: unknown;
|
||||||
|
}) => {
|
||||||
|
if (
|
||||||
|
isFieldUuid(fieldDefinition) ||
|
||||||
|
isFieldText(fieldDefinition) ||
|
||||||
|
isFieldDateTime(fieldDefinition) ||
|
||||||
|
isFieldNumber(fieldDefinition) ||
|
||||||
|
isFieldRating(fieldDefinition) ||
|
||||||
|
isFieldEmail(fieldDefinition) ||
|
||||||
|
isFieldBoolean(fieldDefinition) ||
|
||||||
|
isFieldSelect(fieldDefinition)
|
||||||
|
//|| isFieldPhone(fieldDefinition)
|
||||||
|
) {
|
||||||
|
return isValueEmpty(fieldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFieldRelation(fieldDefinition)) {
|
||||||
|
return isFieldRelationValue(fieldValue) && isValueEmpty(fieldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFieldCurrency(fieldDefinition)) {
|
||||||
|
return (
|
||||||
|
!isFieldCurrencyValue(fieldValue) ||
|
||||||
|
isValueEmpty(fieldValue?.amountMicros)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFieldFullName(fieldDefinition)) {
|
||||||
|
return (
|
||||||
|
!isFieldFullNameValue(fieldValue) ||
|
||||||
|
isValueEmpty(fieldValue?.firstName + fieldValue?.lastName)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFieldLink(fieldDefinition)) {
|
||||||
|
return !isFieldLinkValue(fieldValue) || isValueEmpty(fieldValue?.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
`Entity field type not supported in isEntityFieldEditModeEmptyFamilySelector : ${fieldDefinition.type}}`,
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -4,9 +4,11 @@ import { useRecoilCallback, useRecoilValue } from 'recoil';
|
|||||||
|
|
||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
||||||
|
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
||||||
import { RecordTable } from '@/object-record/record-table/components/RecordTable';
|
import { RecordTable } from '@/object-record/record-table/components/RecordTable';
|
||||||
import { RecordTableFirstColumnScrollObserver } from '@/object-record/record-table/components/RecordTableFirstColumnScrollObserver';
|
import { RecordTableFirstColumnScrollObserver } from '@/object-record/record-table/components/RecordTableFirstColumnScrollObserver';
|
||||||
import { RecordTableRefContextWrapper } from '@/object-record/record-table/components/RecordTableRefContext';
|
import { RecordTableRefContextWrapper } from '@/object-record/record-table/components/RecordTableRefContext';
|
||||||
|
import { EntityDeleteContext } from '@/object-record/record-table/contexts/EntityDeleteHookContext';
|
||||||
import { useRecordTableScopedStates } from '@/object-record/record-table/hooks/internal/useRecordTableScopedStates';
|
import { useRecordTableScopedStates } from '@/object-record/record-table/hooks/internal/useRecordTableScopedStates';
|
||||||
import { getRecordTableScopeInjector } from '@/object-record/record-table/utils/getRecordTableScopeInjector';
|
import { getRecordTableScopeInjector } from '@/object-record/record-table/utils/getRecordTableScopeInjector';
|
||||||
import { IconPlus } from '@/ui/display/icon';
|
import { IconPlus } from '@/ui/display/icon';
|
||||||
@ -122,6 +124,8 @@ export const RecordTableWithWrappers = ({
|
|||||||
|
|
||||||
const { persistViewFields } = useViewFields(viewBarId);
|
const { persistViewFields } = useViewFields(viewBarId);
|
||||||
|
|
||||||
|
const { deleteOneRecord } = useDeleteOneRecord({ objectNameSingular });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RecordTableScope
|
<RecordTableScope
|
||||||
recordTableScopeId={recordTableId}
|
recordTableScopeId={recordTableId}
|
||||||
@ -129,42 +133,44 @@ export const RecordTableWithWrappers = ({
|
|||||||
persistViewFields(mapColumnDefinitionsToViewFields(columns));
|
persistViewFields(mapColumnDefinitionsToViewFields(columns));
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<ScrollWrapper>
|
<EntityDeleteContext.Provider value={deleteOneRecord}>
|
||||||
<RecordTableRefContextWrapper>
|
<ScrollWrapper>
|
||||||
<RecordTableFirstColumnScrollObserver />
|
<RecordTableRefContextWrapper>
|
||||||
<RecordUpdateContext.Provider value={updateRecordMutation}>
|
<RecordTableFirstColumnScrollObserver />
|
||||||
<StyledTableWithHeader>
|
<RecordUpdateContext.Provider value={updateRecordMutation}>
|
||||||
<StyledTableContainer>
|
<StyledTableWithHeader>
|
||||||
<div ref={tableBodyRef}>
|
<StyledTableContainer>
|
||||||
<RecordTable createRecord={createRecord} />
|
<div ref={tableBodyRef}>
|
||||||
<DragSelect
|
<RecordTable createRecord={createRecord} />
|
||||||
dragSelectable={tableBodyRef}
|
<DragSelect
|
||||||
onDragSelectionStart={resetTableRowSelection}
|
dragSelectable={tableBodyRef}
|
||||||
onDragSelectionChange={setRowSelectedState}
|
onDragSelectionStart={resetTableRowSelection}
|
||||||
/>
|
onDragSelectionChange={setRowSelectedState}
|
||||||
</div>
|
|
||||||
<RecordTableInternalEffect tableBodyRef={tableBodyRef} />
|
|
||||||
{!isRecordTableInitialLoading && numberOfTableRows === 0 && (
|
|
||||||
<StyledObjectEmptyContainer>
|
|
||||||
<StyledEmptyObjectTitle>
|
|
||||||
No {foundObjectMetadataItem?.namePlural}
|
|
||||||
</StyledEmptyObjectTitle>
|
|
||||||
<StyledEmptyObjectSubTitle>
|
|
||||||
Create one:
|
|
||||||
</StyledEmptyObjectSubTitle>
|
|
||||||
<Button
|
|
||||||
Icon={IconPlus}
|
|
||||||
title={`Add a ${foundObjectMetadataItem?.nameSingular}`}
|
|
||||||
variant={'secondary'}
|
|
||||||
onClick={createRecord}
|
|
||||||
/>
|
/>
|
||||||
</StyledObjectEmptyContainer>
|
</div>
|
||||||
)}
|
<RecordTableInternalEffect tableBodyRef={tableBodyRef} />
|
||||||
</StyledTableContainer>
|
{!isRecordTableInitialLoading && numberOfTableRows === 0 && (
|
||||||
</StyledTableWithHeader>
|
<StyledObjectEmptyContainer>
|
||||||
</RecordUpdateContext.Provider>
|
<StyledEmptyObjectTitle>
|
||||||
</RecordTableRefContextWrapper>
|
No {foundObjectMetadataItem?.namePlural}
|
||||||
</ScrollWrapper>
|
</StyledEmptyObjectTitle>
|
||||||
|
<StyledEmptyObjectSubTitle>
|
||||||
|
Create one:
|
||||||
|
</StyledEmptyObjectSubTitle>
|
||||||
|
<Button
|
||||||
|
Icon={IconPlus}
|
||||||
|
title={`Add a ${foundObjectMetadataItem?.nameSingular}`}
|
||||||
|
variant={'secondary'}
|
||||||
|
onClick={createRecord}
|
||||||
|
/>
|
||||||
|
</StyledObjectEmptyContainer>
|
||||||
|
)}
|
||||||
|
</StyledTableContainer>
|
||||||
|
</StyledTableWithHeader>
|
||||||
|
</RecordUpdateContext.Provider>
|
||||||
|
</RecordTableRefContextWrapper>
|
||||||
|
</ScrollWrapper>
|
||||||
|
</EntityDeleteContext.Provider>
|
||||||
</RecordTableScope>
|
</RecordTableScope>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,5 @@
|
|||||||
|
import { createContext } from 'react';
|
||||||
|
|
||||||
|
export const EntityDeleteContext = createContext<
|
||||||
|
(idToDelete: string) => Promise<unknown>
|
||||||
|
>(async () => {});
|
||||||
@ -1,6 +1,10 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
|
||||||
import { FieldDisplay } from '@/object-record/field/components/FieldDisplay';
|
import { FieldDisplay } from '@/object-record/field/components/FieldDisplay';
|
||||||
import { FieldInput } from '@/object-record/field/components/FieldInput';
|
import { FieldInput } from '@/object-record/field/components/FieldInput';
|
||||||
|
import { useIsFieldEditModeValueEmpty } from '@/object-record/field/hooks/useIsFieldEditModeValueEmpty';
|
||||||
import { FieldInputEvent } from '@/object-record/field/types/FieldInputEvent';
|
import { FieldInputEvent } from '@/object-record/field/types/FieldInputEvent';
|
||||||
|
import { ColumnIndexContext } from '@/object-record/record-table/contexts/ColumnIndexContext';
|
||||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||||
|
|
||||||
import { useRecordTable } from '../../hooks/useRecordTable';
|
import { useRecordTable } from '../../hooks/useRecordTable';
|
||||||
@ -17,14 +21,25 @@ export const RecordTableCell = ({
|
|||||||
|
|
||||||
const { moveLeft, moveRight, moveDown } = useRecordTable();
|
const { moveLeft, moveRight, moveDown } = useRecordTable();
|
||||||
|
|
||||||
|
const isFirstColumnCell = useContext(ColumnIndexContext) === 0;
|
||||||
|
const isEditModeValueEmpty = useIsFieldEditModeValueEmpty();
|
||||||
|
|
||||||
|
const skipFieldPersist = isFirstColumnCell && isEditModeValueEmpty;
|
||||||
|
|
||||||
const handleEnter: FieldInputEvent = (persistField) => {
|
const handleEnter: FieldInputEvent = (persistField) => {
|
||||||
persistField();
|
if (!skipFieldPersist) {
|
||||||
|
persistField();
|
||||||
|
}
|
||||||
|
|
||||||
closeTableCell();
|
closeTableCell();
|
||||||
moveDown();
|
moveDown();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit: FieldInputEvent = (persistField) => {
|
const handleSubmit: FieldInputEvent = (persistField) => {
|
||||||
persistField();
|
if (!skipFieldPersist) {
|
||||||
|
persistField();
|
||||||
|
}
|
||||||
|
|
||||||
closeTableCell();
|
closeTableCell();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -33,24 +48,35 @@ export const RecordTableCell = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleClickOutside: FieldInputEvent = (persistField) => {
|
const handleClickOutside: FieldInputEvent = (persistField) => {
|
||||||
persistField();
|
if (!skipFieldPersist) {
|
||||||
|
persistField();
|
||||||
|
}
|
||||||
|
|
||||||
closeTableCell();
|
closeTableCell();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEscape: FieldInputEvent = (persistField) => {
|
const handleEscape: FieldInputEvent = (persistField) => {
|
||||||
persistField();
|
if (!skipFieldPersist) {
|
||||||
|
persistField();
|
||||||
|
}
|
||||||
|
|
||||||
closeTableCell();
|
closeTableCell();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTab: FieldInputEvent = (persistField) => {
|
const handleTab: FieldInputEvent = (persistField) => {
|
||||||
persistField();
|
if (!skipFieldPersist) {
|
||||||
|
persistField();
|
||||||
|
}
|
||||||
|
|
||||||
closeTableCell();
|
closeTableCell();
|
||||||
moveRight();
|
moveRight();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleShiftTab: FieldInputEvent = (persistField) => {
|
const handleShiftTab: FieldInputEvent = (persistField) => {
|
||||||
persistField();
|
if (!skipFieldPersist) {
|
||||||
|
persistField();
|
||||||
|
}
|
||||||
|
|
||||||
closeTableCell();
|
closeTableCell();
|
||||||
moveLeft();
|
moveLeft();
|
||||||
};
|
};
|
||||||
|
|||||||
@ -33,13 +33,17 @@ export const useMoveSoftFocusToCurrentCellOnHover = () => {
|
|||||||
currentTableCellInEditModePositionScopeInjector,
|
currentTableCellInEditModePositionScopeInjector,
|
||||||
);
|
);
|
||||||
|
|
||||||
const isSomeCellInEditMode = snapshot.getLoadable(
|
const isSomeCellInEditMode = snapshot
|
||||||
isTableCellInEditModeFamilyState(currentTableCellInEditModePosition),
|
.getLoadable(
|
||||||
);
|
isTableCellInEditModeFamilyState(
|
||||||
|
currentTableCellInEditModePosition,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.getValue();
|
||||||
|
|
||||||
const currentHotkeyScope = snapshot
|
const currentHotkeyScope = snapshot
|
||||||
.getLoadable(currentHotkeyScopeState)
|
.getLoadable(currentHotkeyScopeState)
|
||||||
.valueOrThrow();
|
.getValue();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
currentHotkeyScope.scope !== TableHotkeyScope.TableSoftFocus &&
|
currentHotkeyScope.scope !== TableHotkeyScope.TableSoftFocus &&
|
||||||
@ -49,7 +53,7 @@ export const useMoveSoftFocusToCurrentCellOnHover = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isSomeCellInEditMode.contents) {
|
if (!isSomeCellInEditMode) {
|
||||||
setSoftFocusOnCurrentTableCell();
|
setSoftFocusOnCurrentTableCell();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -0,0 +1,21 @@
|
|||||||
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
|
import { useMoveEditModeToTableCellPosition } from '../../hooks/internal/useMoveEditModeToCellPosition';
|
||||||
|
|
||||||
|
export const useSelectedTableCellEditMode = ({
|
||||||
|
scopeId,
|
||||||
|
}: {
|
||||||
|
scopeId: string;
|
||||||
|
}) => {
|
||||||
|
const moveEditModeToTableCellPosition =
|
||||||
|
useMoveEditModeToTableCellPosition(scopeId);
|
||||||
|
|
||||||
|
const setSelectedTableCellEditMode = useCallback(
|
||||||
|
(row: number, column: number) => {
|
||||||
|
moveEditModeToTableCellPosition({ column, row });
|
||||||
|
},
|
||||||
|
[moveEditModeToTableCellPosition],
|
||||||
|
);
|
||||||
|
|
||||||
|
return { setSelectedTableCellEditMode };
|
||||||
|
};
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
|
import { useRecordTableScopedStates } from '@/object-record/record-table/hooks/internal/useRecordTableScopedStates';
|
||||||
|
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||||
|
import { TableCellPosition } from '@/object-record/record-table/types/TableCellPosition';
|
||||||
|
import { getRecordTableScopeInjector } from '@/object-record/record-table/utils/getRecordTableScopeInjector';
|
||||||
|
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||||
|
|
||||||
|
import { TableHotkeyScope } from '../../types/TableHotkeyScope';
|
||||||
|
|
||||||
|
export const useSetSoftFocus = () => {
|
||||||
|
const { setSoftFocusPosition } = useRecordTable();
|
||||||
|
|
||||||
|
const { isSoftFocusActiveScopeInjector } = getRecordTableScopeInjector();
|
||||||
|
|
||||||
|
const { injectStateWithRecordTableScopeId } = useRecordTableScopedStates();
|
||||||
|
|
||||||
|
const isSoftFocusActiveState = injectStateWithRecordTableScopeId(
|
||||||
|
isSoftFocusActiveScopeInjector,
|
||||||
|
);
|
||||||
|
|
||||||
|
const setHotkeyScope = useSetHotkeyScope();
|
||||||
|
|
||||||
|
return useRecoilCallback(
|
||||||
|
({ set }) =>
|
||||||
|
(newPosition: TableCellPosition) => {
|
||||||
|
setSoftFocusPosition(newPosition);
|
||||||
|
|
||||||
|
set(isSoftFocusActiveState, true);
|
||||||
|
|
||||||
|
setHotkeyScope(TableHotkeyScope.TableSoftFocus);
|
||||||
|
},
|
||||||
|
[setSoftFocusPosition, isSoftFocusActiveState, setHotkeyScope],
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,43 +1,13 @@
|
|||||||
import { useRecoilCallback } from 'recoil';
|
import { useSetSoftFocus } from '@/object-record/record-table/record-table-cell/hooks/useSetSoftFocus';
|
||||||
|
|
||||||
import { useRecordTableScopedStates } from '@/object-record/record-table/hooks/internal/useRecordTableScopedStates';
|
|
||||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
|
||||||
import { getRecordTableScopeInjector } from '@/object-record/record-table/utils/getRecordTableScopeInjector';
|
|
||||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
|
||||||
|
|
||||||
import { TableHotkeyScope } from '../../types/TableHotkeyScope';
|
|
||||||
|
|
||||||
import { useCurrentTableCellPosition } from './useCurrentCellPosition';
|
import { useCurrentTableCellPosition } from './useCurrentCellPosition';
|
||||||
|
|
||||||
export const useSetSoftFocusOnCurrentTableCell = () => {
|
export const useSetSoftFocusOnCurrentTableCell = () => {
|
||||||
const { setSoftFocusPosition } = useRecordTable();
|
const setSoftFocus = useSetSoftFocus();
|
||||||
|
|
||||||
const { isSoftFocusActiveScopeInjector } = getRecordTableScopeInjector();
|
|
||||||
|
|
||||||
const { injectStateWithRecordTableScopeId } = useRecordTableScopedStates();
|
|
||||||
|
|
||||||
const isSoftFocusActiveState = injectStateWithRecordTableScopeId(
|
|
||||||
isSoftFocusActiveScopeInjector,
|
|
||||||
);
|
|
||||||
|
|
||||||
const currentTableCellPosition = useCurrentTableCellPosition();
|
const currentTableCellPosition = useCurrentTableCellPosition();
|
||||||
|
|
||||||
const setHotkeyScope = useSetHotkeyScope();
|
return () => {
|
||||||
|
setSoftFocus(currentTableCellPosition);
|
||||||
return useRecoilCallback(
|
};
|
||||||
({ set }) =>
|
|
||||||
() => {
|
|
||||||
setSoftFocusPosition(currentTableCellPosition);
|
|
||||||
|
|
||||||
set(isSoftFocusActiveState, true);
|
|
||||||
|
|
||||||
setHotkeyScope(TableHotkeyScope.TableSoftFocus);
|
|
||||||
},
|
|
||||||
[
|
|
||||||
setSoftFocusPosition,
|
|
||||||
currentTableCellPosition,
|
|
||||||
isSoftFocusActiveState,
|
|
||||||
setHotkeyScope,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { FieldContext } from '@/object-record/field/contexts/FieldContext';
|
import { FieldContext } from '@/object-record/field/contexts/FieldContext';
|
||||||
|
import { useIsFieldEditModeValueEmpty } from '@/object-record/field/hooks/useIsFieldEditModeValueEmpty';
|
||||||
import { useIsFieldEmpty } from '@/object-record/field/hooks/useIsFieldEmpty';
|
import { useIsFieldEmpty } from '@/object-record/field/hooks/useIsFieldEmpty';
|
||||||
import { entityFieldInitialValueFamilyState } from '@/object-record/field/states/entityFieldInitialValueFamilyState';
|
import { entityFieldInitialValueFamilyState } from '@/object-record/field/states/entityFieldInitialValueFamilyState';
|
||||||
import { FieldInitialValue } from '@/object-record/field/types/FieldInitialValue';
|
import { FieldInitialValue } from '@/object-record/field/types/FieldInitialValue';
|
||||||
|
import { EntityDeleteContext } from '@/object-record/record-table/contexts/EntityDeleteHookContext';
|
||||||
import { useRecordTableScopedStates } from '@/object-record/record-table/hooks/internal/useRecordTableScopedStates';
|
import { useRecordTableScopedStates } from '@/object-record/record-table/hooks/internal/useRecordTableScopedStates';
|
||||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||||
import { getRecordTableScopeInjector } from '@/object-record/record-table/utils/getRecordTableScopeInjector';
|
import { getRecordTableScopeInjector } from '@/object-record/record-table/utils/getRecordTableScopeInjector';
|
||||||
@ -20,7 +22,7 @@ import { TableHotkeyScope } from '../../types/TableHotkeyScope';
|
|||||||
|
|
||||||
import { useCurrentTableCellEditMode } from './useCurrentTableCellEditMode';
|
import { useCurrentTableCellEditMode } from './useCurrentTableCellEditMode';
|
||||||
|
|
||||||
const DEFAULT_CELL_SCOPE: HotkeyScope = {
|
export const DEFAULT_CELL_SCOPE: HotkeyScope = {
|
||||||
scope: TableHotkeyScope.CellEditMode,
|
scope: TableHotkeyScope.CellEditMode,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -51,9 +53,12 @@ export const useTableCell = () => {
|
|||||||
const isFirstColumnCell = useContext(ColumnIndexContext) === 0;
|
const isFirstColumnCell = useContext(ColumnIndexContext) === 0;
|
||||||
|
|
||||||
const isEmpty = useIsFieldEmpty();
|
const isEmpty = useIsFieldEmpty();
|
||||||
|
const isEditModeValueEmpty = useIsFieldEditModeValueEmpty();
|
||||||
|
|
||||||
const { entityId, fieldDefinition } = useContext(FieldContext);
|
const { entityId, fieldDefinition } = useContext(FieldContext);
|
||||||
|
|
||||||
|
const deleteOneRecord = useContext(EntityDeleteContext);
|
||||||
|
|
||||||
const [, setFieldInitialValue] = useRecoilState(
|
const [, setFieldInitialValue] = useRecoilState(
|
||||||
entityFieldInitialValueFamilyState({
|
entityFieldInitialValueFamilyState({
|
||||||
entityId,
|
entityId,
|
||||||
@ -61,6 +66,16 @@ export const useTableCell = () => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { tableRowIdsScopeInjector } = getRecordTableScopeInjector();
|
||||||
|
|
||||||
|
const deleteRow = useRecoilCallback(({ snapshot }) => async () => {
|
||||||
|
const tableRowIds = snapshot
|
||||||
|
.getLoadable(tableRowIdsScopeInjector(recordTableScopeId))
|
||||||
|
.getValue();
|
||||||
|
|
||||||
|
await deleteOneRecord(tableRowIds[0]);
|
||||||
|
});
|
||||||
|
|
||||||
const openTableCell = (options?: { initialValue?: FieldInitialValue }) => {
|
const openTableCell = (options?: { initialValue?: FieldInitialValue }) => {
|
||||||
if (isFirstColumnCell && !isEmpty && basePathToShowPage) {
|
if (isFirstColumnCell && !isEmpty && basePathToShowPage) {
|
||||||
navigate(`${basePathToShowPage}${entityId}`);
|
navigate(`${basePathToShowPage}${entityId}`);
|
||||||
@ -84,11 +99,15 @@ export const useTableCell = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeTableCell = () => {
|
const closeTableCell = async () => {
|
||||||
setDragSelectionStartEnabled(true);
|
setDragSelectionStartEnabled(true);
|
||||||
closeCurrentTableCellInEditMode();
|
closeCurrentTableCellInEditMode();
|
||||||
setFieldInitialValue(undefined);
|
setFieldInitialValue(undefined);
|
||||||
setHotkeyScope(TableHotkeyScope.TableSoftFocus);
|
setHotkeyScope(TableHotkeyScope.TableSoftFocus);
|
||||||
|
|
||||||
|
if (isFirstColumnCell && isEditModeValueEmpty) {
|
||||||
|
await deleteRow();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -38,6 +38,7 @@ export type DateInputProps = {
|
|||||||
) => void;
|
) => void;
|
||||||
hotkeyScope: string;
|
hotkeyScope: string;
|
||||||
clearable?: boolean;
|
clearable?: boolean;
|
||||||
|
onChange?: (newDate: Nullable<Date>) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DateInput = ({
|
export const DateInput = ({
|
||||||
@ -47,6 +48,7 @@ export const DateInput = ({
|
|||||||
onEscape,
|
onEscape,
|
||||||
onClickOutside,
|
onClickOutside,
|
||||||
clearable,
|
clearable,
|
||||||
|
onChange,
|
||||||
}: DateInputProps) => {
|
}: DateInputProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
@ -66,6 +68,7 @@ export const DateInput = ({
|
|||||||
|
|
||||||
const handleChange = (newDate: Date) => {
|
const handleChange = (newDate: Date) => {
|
||||||
setInternalValue(newDate);
|
setInternalValue(newDate);
|
||||||
|
onChange?.(newDate);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@ -38,6 +38,7 @@ type DoubleTextInputProps = {
|
|||||||
event: MouseEvent | TouchEvent,
|
event: MouseEvent | TouchEvent,
|
||||||
newDoubleTextValue: FieldDoubleText,
|
newDoubleTextValue: FieldDoubleText,
|
||||||
) => void;
|
) => void;
|
||||||
|
onChange?: (newDoubleTextValue: FieldDoubleText) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DoubleTextInput = ({
|
export const DoubleTextInput = ({
|
||||||
@ -51,6 +52,7 @@ export const DoubleTextInput = ({
|
|||||||
onEscape,
|
onEscape,
|
||||||
onShiftTab,
|
onShiftTab,
|
||||||
onTab,
|
onTab,
|
||||||
|
onChange,
|
||||||
}: DoubleTextInputProps) => {
|
}: DoubleTextInputProps) => {
|
||||||
const [firstInternalValue, setFirstInternalValue] = useState(firstValue);
|
const [firstInternalValue, setFirstInternalValue] = useState(firstValue);
|
||||||
const [secondInternalValue, setSecondInternalValue] = useState(secondValue);
|
const [secondInternalValue, setSecondInternalValue] = useState(secondValue);
|
||||||
@ -70,6 +72,11 @@ export const DoubleTextInput = ({
|
|||||||
): void => {
|
): void => {
|
||||||
setFirstInternalValue(newFirstValue);
|
setFirstInternalValue(newFirstValue);
|
||||||
setSecondInternalValue(newSecondValue);
|
setSecondInternalValue(newSecondValue);
|
||||||
|
|
||||||
|
onChange?.({
|
||||||
|
firstValue: newFirstValue,
|
||||||
|
secondValue: newSecondValue,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const [focusPosition, setFocusPosition] = useState<'left' | 'right'>('left');
|
const [focusPosition, setFocusPosition] = useState<'left' | 'right'>('left');
|
||||||
|
|||||||
@ -54,6 +54,7 @@ export type PhoneInputProps = {
|
|||||||
onTab?: (newText: string) => void;
|
onTab?: (newText: string) => void;
|
||||||
onShiftTab?: (newText: string) => void;
|
onShiftTab?: (newText: string) => void;
|
||||||
onClickOutside: (event: MouseEvent | TouchEvent, inputValue: string) => void;
|
onClickOutside: (event: MouseEvent | TouchEvent, inputValue: string) => void;
|
||||||
|
onChange?: (newText: string) => void;
|
||||||
hotkeyScope: string;
|
hotkeyScope: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -66,11 +67,17 @@ export const PhoneInput = ({
|
|||||||
onShiftTab,
|
onShiftTab,
|
||||||
onClickOutside,
|
onClickOutside,
|
||||||
hotkeyScope,
|
hotkeyScope,
|
||||||
|
onChange,
|
||||||
}: PhoneInputProps) => {
|
}: PhoneInputProps) => {
|
||||||
const [internalValue, setInternalValue] = useState<string | undefined>(value);
|
const [internalValue, setInternalValue] = useState<string | undefined>(value);
|
||||||
|
|
||||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const handleChange = (newValue: string) => {
|
||||||
|
setInternalValue(newValue);
|
||||||
|
onChange?.(newValue);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setInternalValue(value);
|
setInternalValue(value);
|
||||||
}, [value]);
|
}, [value]);
|
||||||
@ -92,7 +99,7 @@ export const PhoneInput = ({
|
|||||||
autoFocus={autoFocus}
|
autoFocus={autoFocus}
|
||||||
placeholder="Phone number"
|
placeholder="Phone number"
|
||||||
value={value}
|
value={value}
|
||||||
onChange={setInternalValue}
|
onChange={handleChange}
|
||||||
international={true}
|
international={true}
|
||||||
withCountryCallingCode={true}
|
withCountryCallingCode={true}
|
||||||
countrySelectComponent={CountryPickerDropdownButton}
|
countrySelectComponent={CountryPickerDropdownButton}
|
||||||
|
|||||||
@ -21,6 +21,7 @@ type TextInputProps = {
|
|||||||
onShiftTab?: (newText: string) => void;
|
onShiftTab?: (newText: string) => void;
|
||||||
onClickOutside: (event: MouseEvent | TouchEvent, inputValue: string) => void;
|
onClickOutside: (event: MouseEvent | TouchEvent, inputValue: string) => void;
|
||||||
hotkeyScope: string;
|
hotkeyScope: string;
|
||||||
|
onChange?: (newText: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TextInput = ({
|
export const TextInput = ({
|
||||||
@ -33,6 +34,7 @@ export const TextInput = ({
|
|||||||
onTab,
|
onTab,
|
||||||
onShiftTab,
|
onShiftTab,
|
||||||
onClickOutside,
|
onClickOutside,
|
||||||
|
onChange,
|
||||||
}: TextInputProps) => {
|
}: TextInputProps) => {
|
||||||
const [internalText, setInternalText] = useState(value);
|
const [internalText, setInternalText] = useState(value);
|
||||||
|
|
||||||
@ -40,6 +42,7 @@ export const TextInput = ({
|
|||||||
|
|
||||||
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
|
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
setInternalText(event.target.value);
|
setInternalText(event.target.value);
|
||||||
|
onChange?.(event.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user