Reafactor/UI input and displays (#1544)
* WIP * Text field * URL * Finished PhoneInput * Refactored input sub-folders * Boolean * Fix lint * Fix lint * Fix useOutsideClick --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -83,8 +83,6 @@ type OwnProps = {
|
||||
isDisplayModeContentEmpty?: boolean;
|
||||
isDisplayModeFixHeight?: boolean;
|
||||
disableHoverEffect?: boolean;
|
||||
onSubmit?: () => void;
|
||||
onCancel?: () => void;
|
||||
};
|
||||
|
||||
export function EditableField({
|
||||
@ -99,8 +97,6 @@ export function EditableField({
|
||||
displayModeContentOnly,
|
||||
isDisplayModeFixHeight,
|
||||
disableHoverEffect,
|
||||
onSubmit,
|
||||
onCancel,
|
||||
}: OwnProps) {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
@ -115,10 +111,13 @@ export function EditableField({
|
||||
const { isFieldInEditMode, openEditableField } = useEditableField();
|
||||
|
||||
function handleDisplayModeClick() {
|
||||
openEditableField(customEditHotkeyScope);
|
||||
if (!displayModeContentOnly) {
|
||||
openEditableField(customEditHotkeyScope);
|
||||
}
|
||||
}
|
||||
|
||||
const showEditButton = !isFieldInEditMode && isHovered && useEditButton;
|
||||
const showEditButton =
|
||||
!isFieldInEditMode && isHovered && useEditButton && !displayModeContentOnly;
|
||||
|
||||
return (
|
||||
<StyledEditableFieldBaseContainer
|
||||
@ -137,10 +136,8 @@ export function EditableField({
|
||||
</StyledLabelAndIconContainer>
|
||||
|
||||
<StyledValueContainer>
|
||||
{isFieldInEditMode && !displayModeContentOnly ? (
|
||||
<EditableFieldEditMode onSubmit={onSubmit} onCancel={onCancel}>
|
||||
{editModeContent}
|
||||
</EditableFieldEditMode>
|
||||
{isFieldInEditMode ? (
|
||||
<EditableFieldEditMode>{editModeContent}</EditableFieldEditMode>
|
||||
) : (
|
||||
<StyledClickableContainer onClick={handleDisplayModeClick}>
|
||||
<EditableFieldDisplayMode
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { useRef } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { useRegisterCloseFieldHandlers } from '../hooks/useRegisterCloseFieldHandlers';
|
||||
import { overlayBackground } from '@/ui/theme/constants/effects';
|
||||
|
||||
const StyledEditableFieldEditModeContainer = styled.div<OwnProps>`
|
||||
align-items: center;
|
||||
@ -14,28 +13,28 @@ const StyledEditableFieldEditModeContainer = styled.div<OwnProps>`
|
||||
z-index: 10;
|
||||
`;
|
||||
|
||||
const StyledEditableFieldInput = styled.div`
|
||||
align-items: center;
|
||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
display: flex;
|
||||
margin-left: -1px;
|
||||
min-height: 32px;
|
||||
width: inherit;
|
||||
|
||||
${overlayBackground}
|
||||
|
||||
z-index: 10;
|
||||
`;
|
||||
|
||||
type OwnProps = {
|
||||
children: React.ReactNode;
|
||||
onOutsideClick?: () => void;
|
||||
onCancel?: () => void;
|
||||
onSubmit?: () => void;
|
||||
};
|
||||
|
||||
export function EditableFieldEditMode({
|
||||
children,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
}: OwnProps) {
|
||||
const wrapperRef = useRef(null);
|
||||
|
||||
useRegisterCloseFieldHandlers(wrapperRef, onSubmit, onCancel);
|
||||
|
||||
export function EditableFieldEditMode({ children }: OwnProps) {
|
||||
return (
|
||||
<StyledEditableFieldEditModeContainer
|
||||
data-testid="editable-field-edit-mode-container"
|
||||
ref={wrapperRef}
|
||||
>
|
||||
{children}
|
||||
<StyledEditableFieldEditModeContainer data-testid="editable-field-edit-mode-container">
|
||||
<StyledEditableFieldInput>{children}</StyledEditableFieldInput>
|
||||
</StyledEditableFieldEditModeContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
import { RoundedLink } from '@/ui/link/components/RoundedLink';
|
||||
|
||||
export function FieldDisplayURL({ URL }: { URL: string | undefined }) {
|
||||
return <RoundedLink href={URL ? 'https://' + URL : ''}>{URL}</RoundedLink>;
|
||||
}
|
||||
@ -1,9 +1,7 @@
|
||||
import { useContext } from 'react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { IconCheck, IconX } from '@/ui/icon';
|
||||
import { BooleanInput } from '@/ui/input/components/BooleanInput';
|
||||
|
||||
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
|
||||
import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext';
|
||||
@ -12,16 +10,6 @@ import { genericEntityFieldFamilySelector } from '../states/selectors/genericEnt
|
||||
import { FieldDefinition } from '../types/FieldDefinition';
|
||||
import { FieldBooleanMetadata } from '../types/FieldMetadata';
|
||||
|
||||
const StyledEditableBooleanFieldContainer = styled.div`
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
const StyledEditableBooleanFieldValue = styled.div`
|
||||
margin-left: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
export function GenericEditableBooleanFieldDisplayMode() {
|
||||
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
|
||||
const currentEditableFieldDefinition = useContext(
|
||||
@ -37,32 +25,20 @@ export function GenericEditableBooleanFieldDisplayMode() {
|
||||
}),
|
||||
);
|
||||
|
||||
const theme = useTheme();
|
||||
const updateField = useUpdateGenericEntityField();
|
||||
|
||||
function toggleValue() {
|
||||
const newToggledValue = !fieldValue;
|
||||
setFieldValue(newToggledValue);
|
||||
|
||||
function handleSubmit(newValue: boolean) {
|
||||
if (currentEditableFieldEntityId && updateField) {
|
||||
updateField(
|
||||
currentEditableFieldEntityId,
|
||||
currentEditableFieldDefinition,
|
||||
newToggledValue,
|
||||
newValue,
|
||||
);
|
||||
|
||||
// TODO: use optimistic effect instead, but needs generic refactor
|
||||
setFieldValue(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledEditableBooleanFieldContainer onClick={toggleValue}>
|
||||
{fieldValue ? (
|
||||
<IconCheck size={theme.icon.size.sm} />
|
||||
) : (
|
||||
<IconX size={theme.icon.size.sm} />
|
||||
)}
|
||||
<StyledEditableBooleanFieldValue>
|
||||
{fieldValue ? 'True' : 'False'}
|
||||
</StyledEditableBooleanFieldValue>
|
||||
</StyledEditableBooleanFieldContainer>
|
||||
);
|
||||
return <BooleanInput value={fieldValue} onToggle={handleSubmit} />;
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { DateInputDisplay } from '@/ui/input/date/components/DateInputDisplay';
|
||||
import { DateInputDisplay } from '@/ui/input/components/DateInputDisplay';
|
||||
import { parseDate } from '~/utils/date-utils';
|
||||
|
||||
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { PhoneInputDisplay } from '@/ui/input/phone/components/PhoneInputDisplay';
|
||||
import { PhoneDisplay } from '@/ui/content-display/components/PhoneDisplay';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
|
||||
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
|
||||
@ -35,7 +35,7 @@ export function GenericEditablePhoneField() {
|
||||
useEditButton
|
||||
IconLabel={currentEditableFieldDefinition.Icon}
|
||||
editModeContent={<GenericEditablePhoneFieldEditMode />}
|
||||
displayModeContent={<PhoneInputDisplay value={fieldValue} />}
|
||||
displayModeContent={<PhoneDisplay value={fieldValue} />}
|
||||
isDisplayModeContentEmpty={!fieldValue}
|
||||
/>
|
||||
</RecoilScope>
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
import { useContext, useRef, useState } from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { isPossiblePhoneNumber } from 'react-phone-number-input';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { TextInputEdit } from '@/ui/input/text/components/TextInputEdit';
|
||||
import { PhoneInput } from '@/ui/input/components/PhoneInput';
|
||||
|
||||
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
|
||||
import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext';
|
||||
import { useRegisterCloseFieldHandlers } from '../hooks/useRegisterCloseFieldHandlers';
|
||||
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';
|
||||
|
||||
@ -27,46 +29,34 @@ export function GenericEditablePhoneFieldEditMode() {
|
||||
}),
|
||||
);
|
||||
|
||||
const [internalValue, setInternalValue] = useState(fieldValue);
|
||||
|
||||
const updateField = useUpdateGenericEntityField();
|
||||
|
||||
const wrapperRef = useRef(null);
|
||||
function handleSubmit(newValue: string) {
|
||||
if (!isPossiblePhoneNumber(newValue)) return;
|
||||
|
||||
useRegisterCloseFieldHandlers(wrapperRef, handleSubmit, onCancel);
|
||||
|
||||
function handleSubmit() {
|
||||
if (internalValue === fieldValue) return;
|
||||
|
||||
setFieldValue(internalValue);
|
||||
setFieldValue(newValue);
|
||||
|
||||
if (currentEditableFieldEntityId && updateField) {
|
||||
updateField(
|
||||
currentEditableFieldEntityId,
|
||||
currentEditableFieldDefinition,
|
||||
internalValue,
|
||||
newValue,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function onCancel() {
|
||||
setFieldValue(fieldValue);
|
||||
}
|
||||
|
||||
function handleChange(newValue: string) {
|
||||
setInternalValue(newValue);
|
||||
}
|
||||
const { handleEnter, handleEscape, handleClickOutside } =
|
||||
useFieldInputEventHandlers({
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
return (
|
||||
<div ref={wrapperRef}>
|
||||
<TextInputEdit
|
||||
autoFocus
|
||||
placeholder={currentEditableFieldDefinition.metadata.placeHolder}
|
||||
value={internalValue}
|
||||
onChange={(newValue: string) => {
|
||||
handleChange(newValue);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<PhoneInput
|
||||
value={fieldValue ?? ''}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
hotkeyScope={EditableFieldHotkeyScope.EditableField}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
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';
|
||||
@ -33,7 +34,7 @@ export function GenericEditableTextField() {
|
||||
<EditableField
|
||||
IconLabel={currentEditableFieldDefinition.Icon}
|
||||
editModeContent={<GenericEditableTextFieldEditMode />}
|
||||
displayModeContent={fieldValue}
|
||||
displayModeContent={<TextDisplay text={fieldValue} />}
|
||||
isDisplayModeContentEmpty={!fieldValue}
|
||||
/>
|
||||
</RecoilScope>
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import { useContext, useRef, useState } from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { TextInputEdit } from '@/ui/input/text/components/TextInputEdit';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
|
||||
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
|
||||
import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext';
|
||||
import { useRegisterCloseFieldHandlers } from '../hooks/useRegisterCloseFieldHandlers';
|
||||
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';
|
||||
|
||||
@ -27,46 +28,35 @@ export function GenericEditableTextFieldEditMode() {
|
||||
}),
|
||||
);
|
||||
|
||||
const [internalValue, setInternalValue] = useState(fieldValue);
|
||||
|
||||
const updateField = useUpdateGenericEntityField();
|
||||
|
||||
const wrapperRef = useRef(null);
|
||||
|
||||
useRegisterCloseFieldHandlers(wrapperRef, handleSubmit, onCancel);
|
||||
|
||||
function handleSubmit() {
|
||||
if (internalValue === fieldValue) return;
|
||||
|
||||
setFieldValue(internalValue);
|
||||
|
||||
function handleSubmit(newValue: string) {
|
||||
if (currentEditableFieldEntityId && updateField) {
|
||||
updateField(
|
||||
currentEditableFieldEntityId,
|
||||
currentEditableFieldDefinition,
|
||||
internalValue,
|
||||
newValue,
|
||||
);
|
||||
|
||||
// TODO: use optimistic effect instead, but needs generic refactor
|
||||
setFieldValue(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
function onCancel() {
|
||||
setFieldValue(fieldValue);
|
||||
}
|
||||
|
||||
function handleChange(newValue: string) {
|
||||
setInternalValue(newValue);
|
||||
}
|
||||
const { handleEnter, handleEscape, handleClickOutside } =
|
||||
useFieldInputEventHandlers({
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
return (
|
||||
<div ref={wrapperRef}>
|
||||
<TextInputEdit
|
||||
autoFocus
|
||||
placeholder={currentEditableFieldDefinition.metadata.placeHolder}
|
||||
value={internalValue}
|
||||
onChange={(newValue: string) => {
|
||||
handleChange(newValue);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<TextInput
|
||||
placeholder={currentEditableFieldDefinition.metadata.placeHolder}
|
||||
autoFocus
|
||||
value={fieldValue ?? ''}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
hotkeyScope={EditableFieldHotkeyScope.EditableField}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
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';
|
||||
@ -11,7 +12,6 @@ import { FieldDefinition } from '../types/FieldDefinition';
|
||||
import { FieldNumberMetadata } from '../types/FieldMetadata';
|
||||
|
||||
import { EditableField } from './EditableField';
|
||||
import { FieldDisplayURL } from './FieldDisplayURL';
|
||||
import { GenericEditableURLFieldEditMode } from './GenericEditableURLFieldEditMode';
|
||||
|
||||
export function GenericEditableURLField() {
|
||||
@ -35,7 +35,7 @@ export function GenericEditableURLField() {
|
||||
useEditButton
|
||||
IconLabel={currentEditableFieldDefinition.Icon}
|
||||
editModeContent={<GenericEditableURLFieldEditMode />}
|
||||
displayModeContent={<FieldDisplayURL URL={fieldValue} />}
|
||||
displayModeContent={<URLDisplay value={fieldValue} />}
|
||||
isDisplayModeContentEmpty={!fieldValue}
|
||||
isDisplayModeFixHeight
|
||||
/>
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import { useContext, useRef, useState } from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { TextInputEdit } from '@/ui/input/text/components/TextInputEdit';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
|
||||
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
|
||||
import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext';
|
||||
import { useRegisterCloseFieldHandlers } from '../hooks/useRegisterCloseFieldHandlers';
|
||||
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';
|
||||
|
||||
@ -29,46 +30,34 @@ export function GenericEditableURLFieldEditMode() {
|
||||
}),
|
||||
);
|
||||
|
||||
const [internalValue, setInternalValue] = useState(fieldValue);
|
||||
|
||||
const updateField = useUpdateGenericEntityField();
|
||||
|
||||
const wrapperRef = useRef(null);
|
||||
|
||||
useRegisterCloseFieldHandlers(wrapperRef, handleSubmit, onCancel);
|
||||
|
||||
function handleSubmit() {
|
||||
if (internalValue === fieldValue) return;
|
||||
|
||||
setFieldValue(internalValue);
|
||||
function handleSubmit(newValue: string) {
|
||||
setFieldValue(newValue);
|
||||
|
||||
if (currentEditableFieldEntityId && updateField) {
|
||||
updateField(
|
||||
currentEditableFieldEntityId,
|
||||
currentEditableFieldDefinition,
|
||||
internalValue,
|
||||
newValue,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function onCancel() {
|
||||
setFieldValue(fieldValue);
|
||||
}
|
||||
|
||||
function handleChange(newValue: string) {
|
||||
setInternalValue(newValue);
|
||||
}
|
||||
const { handleEnter, handleEscape, handleClickOutside } =
|
||||
useFieldInputEventHandlers({
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
return (
|
||||
<div ref={wrapperRef}>
|
||||
<TextInputEdit
|
||||
autoFocus
|
||||
placeholder={currentEditableFieldDefinition.metadata.placeHolder}
|
||||
value={internalValue}
|
||||
onChange={(newValue: string) => {
|
||||
handleChange(newValue);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<TextInput
|
||||
placeholder={currentEditableFieldDefinition.metadata.placeHolder}
|
||||
autoFocus
|
||||
value={fieldValue ?? ''}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
hotkeyScope={EditableFieldHotkeyScope.EditableField}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
import { useEditableField } from './useEditableField';
|
||||
|
||||
export function 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,7 +1,7 @@
|
||||
import { EditableField } from '@/ui/editable-field/components/EditableField';
|
||||
import { FieldRecoilScopeContext } from '@/ui/editable-field/states/recoil-scope-contexts/FieldRecoilScopeContext';
|
||||
import { IconComponent } from '@/ui/icon/types/IconComponent';
|
||||
import { DateInputDisplay } from '@/ui/input/date/components/DateInputDisplay';
|
||||
import { DateInputDisplay } from '@/ui/input/components/DateInputDisplay';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
import { parseDate } from '~/utils/date-utils';
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { DateInputEdit } from '@/ui/input/date/components/DateInputEdit';
|
||||
import { DateInputEdit } from '@/ui/input/components/DateInputEdit';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import { parseDate } from '~/utils/date-utils';
|
||||
|
||||
|
||||
@ -1,65 +0,0 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { EditableField } from '@/ui/editable-field/components/EditableField';
|
||||
import { FieldRecoilScopeContext } from '@/ui/editable-field/states/recoil-scope-contexts/FieldRecoilScopeContext';
|
||||
import { IconComponent } from '@/ui/icon/types/IconComponent';
|
||||
import { PhoneInputDisplay } from '@/ui/input/phone/components/PhoneInputDisplay';
|
||||
import { TextInputEdit } from '@/ui/input/text/components/TextInputEdit';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
|
||||
type OwnProps = {
|
||||
Icon?: IconComponent;
|
||||
placeholder?: string;
|
||||
value: string | null | undefined;
|
||||
onSubmit?: (newValue: string) => void;
|
||||
};
|
||||
|
||||
export function PhoneEditableField({
|
||||
Icon,
|
||||
placeholder,
|
||||
value,
|
||||
onSubmit,
|
||||
}: OwnProps) {
|
||||
const [internalValue, setInternalValue] = useState(value);
|
||||
|
||||
useEffect(() => {
|
||||
setInternalValue(value);
|
||||
}, [value]);
|
||||
|
||||
async function handleChange(newValue: string) {
|
||||
setInternalValue(newValue);
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!internalValue) return;
|
||||
|
||||
onSubmit?.(internalValue);
|
||||
}
|
||||
|
||||
async function handleCancel() {
|
||||
setInternalValue(value);
|
||||
}
|
||||
|
||||
return (
|
||||
<RecoilScope SpecificContext={FieldRecoilScopeContext}>
|
||||
<EditableField
|
||||
onSubmit={handleSubmit}
|
||||
onCancel={handleCancel}
|
||||
IconLabel={Icon}
|
||||
editModeContent={
|
||||
<TextInputEdit
|
||||
placeholder={placeholder ?? ''}
|
||||
autoFocus
|
||||
value={internalValue ?? ''}
|
||||
onChange={(newValue: string) => {
|
||||
handleChange(newValue);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
displayModeContent={<PhoneInputDisplay value={internalValue ?? ''} />}
|
||||
isDisplayModeContentEmpty={!(internalValue !== '')}
|
||||
useEditButton
|
||||
/>
|
||||
</RecoilScope>
|
||||
);
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { IconPhone } from '@/ui/icon';
|
||||
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
|
||||
|
||||
import { PhoneEditableField } from '../PhoneEditableField';
|
||||
|
||||
const meta: Meta<typeof PhoneEditableField> = {
|
||||
title: 'UI/EditableField/PhoneEditableField',
|
||||
component: PhoneEditableField,
|
||||
decorators: [ComponentWithRouterDecorator],
|
||||
argTypes: {
|
||||
Icon: {
|
||||
type: 'boolean',
|
||||
mapping: {
|
||||
true: IconPhone,
|
||||
false: undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
args: {
|
||||
value: '+33714446494',
|
||||
Icon: IconPhone,
|
||||
placeholder: 'Phone',
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof PhoneEditableField>;
|
||||
|
||||
export const Default: Story = {};
|
||||
Reference in New Issue
Block a user