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:
@ -3,7 +3,7 @@ import styled from '@emotion/styled';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { actionBarOpenState } from '@/ui/action-bar/states/actionBarIsOpenState';
|
||||
import { Checkbox } from '@/ui/input/checkbox/components/Checkbox';
|
||||
import { Checkbox } from '@/ui/input/components/Checkbox';
|
||||
|
||||
import { useCurrentRowSelected } from '../hooks/useCurrentRowSelected';
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { Checkbox } from '@/ui/input/checkbox/components/Checkbox';
|
||||
import { Checkbox } from '@/ui/input/components/Checkbox';
|
||||
|
||||
import { useSelectAllRows } from '../hooks/useSelectAllRows';
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useEditableCell } from '../hooks/useEditableCell';
|
||||
import { useSetSoftFocusOnCurrentCell } from '../hooks/useSetSoftFocusOnCurrentCell';
|
||||
|
||||
import { EditableCellDisplayContainer } from './EditableCellContainer';
|
||||
import { EditableCellDisplayContainer } from './EditableCellDisplayContainer';
|
||||
|
||||
export function EditableCellDisplayMode({
|
||||
children,
|
||||
|
||||
@ -6,7 +6,7 @@ import { isNonTextWritingKey } from '@/ui/utilities/hotkey/utils/isNonTextWritin
|
||||
import { TableHotkeyScope } from '../../types/TableHotkeyScope';
|
||||
import { useEditableCell } from '../hooks/useEditableCell';
|
||||
|
||||
import { EditableCellDisplayContainer } from './EditableCellContainer';
|
||||
import { EditableCellDisplayContainer } from './EditableCellDisplayContainer';
|
||||
|
||||
type OwnProps = PropsWithChildren<unknown>;
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import { useRef } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
import { DateInputEdit } from '@/ui/input/date/components/DateInputEdit';
|
||||
import { DateInputEdit } from '@/ui/input/components/DateInputEdit';
|
||||
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
|
||||
@ -6,11 +6,10 @@ import { useMoveSoftFocus } from '@/ui/table/hooks/useMoveSoftFocus';
|
||||
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
|
||||
import { StyledInput } from '../../../../input/components/TextInput';
|
||||
import { useEditableCell } from '../../hooks/useEditableCell';
|
||||
import { useRegisterCloseCellHandlers } from '../../hooks/useRegisterCloseCellHandlers';
|
||||
|
||||
import { StyledInput } from './TextCellEdit';
|
||||
|
||||
type OwnProps = {
|
||||
firstValue: string;
|
||||
secondValue: string;
|
||||
|
||||
@ -2,13 +2,13 @@ import styled from '@emotion/styled';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import type { ViewFieldBooleanMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { IconCheck, IconX } from '@/ui/icon';
|
||||
import { BooleanInput } from '@/ui/input/components/BooleanInput';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
|
||||
import type { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
import { EditableCellDisplayContainer } from '../../components/EditableCellContainer';
|
||||
import { EditableCellDisplayContainer } from '../../components/EditableCellDisplayContainer';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldBooleanMetadata>;
|
||||
@ -26,14 +26,6 @@ const StyledCellBaseContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledCellBooleancontainer = styled.div`
|
||||
margin-left: 5px;
|
||||
`;
|
||||
|
||||
function capitalizeFirstLetter(value: string) {
|
||||
return value.charAt(0).toUpperCase() + value.slice(1);
|
||||
}
|
||||
|
||||
export function GenericEditableBooleanCell({ columnDefinition }: OwnProps) {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
@ -48,6 +40,7 @@ export function GenericEditableBooleanCell({ columnDefinition }: OwnProps) {
|
||||
|
||||
function handleClick() {
|
||||
const newValue = !fieldValue;
|
||||
|
||||
try {
|
||||
setFieldValue(newValue);
|
||||
|
||||
@ -64,11 +57,7 @@ export function GenericEditableBooleanCell({ columnDefinition }: OwnProps) {
|
||||
return (
|
||||
<StyledCellBaseContainer>
|
||||
<EditableCellDisplayContainer onClick={handleClick}>
|
||||
{fieldValue ? <IconCheck /> : <IconX />}
|
||||
<StyledCellBooleancontainer>
|
||||
{fieldValue !== undefined &&
|
||||
capitalizeFirstLetter(fieldValue.toString())}
|
||||
</StyledCellBooleancontainer>
|
||||
<BooleanInput value={fieldValue} />
|
||||
</EditableCellDisplayContainer>
|
||||
</StyledCellBaseContainer>
|
||||
);
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import type { ViewFieldChipMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { useCellInputEventHandlers } from '@/ui/table/hooks/useCellInputEventHandlers';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
|
||||
|
||||
import { TextInput } from '../../../../input/components/TextInput';
|
||||
import type { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
import { TextCellEdit } from './TextCellEdit';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldChipMetadata>;
|
||||
};
|
||||
@ -38,12 +39,27 @@ export function GenericEditableChipCellEditMode({
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
handleEnter,
|
||||
handleEscape,
|
||||
handleTab,
|
||||
handleShiftTab,
|
||||
handleClickOutside,
|
||||
} = useCellInputEventHandlers({
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
return (
|
||||
<TextCellEdit
|
||||
<TextInput
|
||||
placeholder={columnDefinition.metadata.placeHolder ?? ''}
|
||||
autoFocus
|
||||
value={fieldValue ?? ''}
|
||||
onSubmit={handleSubmit}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
onTab={handleTab}
|
||||
onShiftTab={handleShiftTab}
|
||||
hotkeyScope={TableHotkeyScope.CellEditMode}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import type { ViewFieldDateMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { DateInputDisplay } from '@/ui/input/date/components/DateInputDisplay';
|
||||
import { DateInputDisplay } from '@/ui/input/components/DateInputDisplay';
|
||||
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { TextDisplay } from '@/ui/content-display/components/TextDisplay';
|
||||
import type { ViewFieldDoubleTextMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { TextInputDisplay } from '@/ui/input/text/components/TextInputDisplay';
|
||||
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
@ -40,7 +40,7 @@ export function GenericEditableDoubleTextCell({ columnDefinition }: OwnProps) {
|
||||
columnDefinition={columnDefinition}
|
||||
/>
|
||||
}
|
||||
nonEditModeContent={<TextInputDisplay>{displayName}</TextInputDisplay>}
|
||||
nonEditModeContent={<TextDisplay text={displayName} />}
|
||||
></EditableCell>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import type { ViewFieldEmailMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { EmailInputDisplay } from '@/ui/input/email/components/EmailInputDisplay';
|
||||
import { EmailInputDisplay } from '@/ui/input/components/EmailInputDisplay';
|
||||
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import type { ViewFieldEmailMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { useCellInputEventHandlers } from '@/ui/table/hooks/useCellInputEventHandlers';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
|
||||
|
||||
import { TextInput } from '../../../../input/components/TextInput';
|
||||
import type { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
import { TextCellEdit } from './TextCellEdit';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldEmailMetadata>;
|
||||
};
|
||||
@ -38,12 +39,27 @@ export function GenericEditableEmailCellEditMode({
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
handleEnter,
|
||||
handleEscape,
|
||||
handleTab,
|
||||
handleShiftTab,
|
||||
handleClickOutside,
|
||||
} = useCellInputEventHandlers({
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
return (
|
||||
<TextCellEdit
|
||||
<TextInput
|
||||
placeholder={columnDefinition.metadata.placeHolder ?? ''}
|
||||
autoFocus
|
||||
value={fieldValue ?? ''}
|
||||
onSubmit={handleSubmit}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
onTab={handleTab}
|
||||
onShiftTab={handleShiftTab}
|
||||
hotkeyScope={TableHotkeyScope.CellEditMode}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import type { ViewFieldMoneyMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { useCellInputEventHandlers } from '@/ui/table/hooks/useCellInputEventHandlers';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
|
||||
|
||||
import { TextInput } from '../../../../input/components/TextInput';
|
||||
import type { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
import { TextCellEdit } from './TextCellEdit';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldMoneyMetadata>;
|
||||
};
|
||||
@ -53,7 +54,26 @@ export function GenericEditableMoneyCellEditMode({
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
handleEnter,
|
||||
handleEscape,
|
||||
handleTab,
|
||||
handleShiftTab,
|
||||
handleClickOutside,
|
||||
} = useCellInputEventHandlers({
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
return (
|
||||
<TextCellEdit autoFocus value={fieldValue ?? ''} onSubmit={handleSubmit} />
|
||||
<TextInput
|
||||
autoFocus
|
||||
value={fieldValue ?? ''}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
onTab={handleTab}
|
||||
onShiftTab={handleShiftTab}
|
||||
hotkeyScope={TableHotkeyScope.CellEditMode}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,18 +1,19 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import type { ViewFieldNumberMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { useCellInputEventHandlers } from '@/ui/table/hooks/useCellInputEventHandlers';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
|
||||
import {
|
||||
canBeCastAsPositiveIntegerOrNull,
|
||||
castAsPositiveIntegerOrNull,
|
||||
} from '~/utils/cast-as-positive-integer-or-null';
|
||||
|
||||
import { TextInput } from '../../../../input/components/TextInput';
|
||||
import type { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
import { TextCellEdit } from './TextCellEdit';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldNumberMetadata>;
|
||||
};
|
||||
@ -74,7 +75,26 @@ export function GenericEditableNumberCellEditMode({
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
handleEnter,
|
||||
handleEscape,
|
||||
handleTab,
|
||||
handleShiftTab,
|
||||
handleClickOutside,
|
||||
} = useCellInputEventHandlers({
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
return (
|
||||
<TextCellEdit autoFocus value={fieldValue ?? ''} onSubmit={handleSubmit} />
|
||||
<TextInput
|
||||
autoFocus
|
||||
value={fieldValue ?? ''}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
onTab={handleTab}
|
||||
onShiftTab={handleShiftTab}
|
||||
hotkeyScope={TableHotkeyScope.CellEditMode}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import type { ViewFieldPhoneMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { PhoneInputDisplay } from '@/ui/input/phone/components/PhoneInputDisplay';
|
||||
import { PhoneDisplay } from '@/ui/content-display/components/PhoneDisplay';
|
||||
import { ViewFieldPhoneMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
@ -35,7 +35,7 @@ export function GenericEditablePhoneCell({
|
||||
editModeContent={
|
||||
<GenericEditablePhoneCellEditMode columnDefinition={columnDefinition} />
|
||||
}
|
||||
nonEditModeContent={<PhoneInputDisplay value={fieldValue} />}
|
||||
nonEditModeContent={<PhoneDisplay value={fieldValue} />}
|
||||
></EditableCell>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,14 +1,16 @@
|
||||
import { isPossiblePhoneNumber } from 'libphonenumber-js';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import type { ViewFieldPhoneMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { useCellInputEventHandlers } from '@/ui/table/hooks/useCellInputEventHandlers';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
|
||||
|
||||
import { PhoneInput } from '../../../../input/components/PhoneInput';
|
||||
import type { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
import { PhoneCellEdit } from './PhoneCellEdit';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldPhoneMetadata>;
|
||||
};
|
||||
@ -28,22 +30,39 @@ export function GenericEditablePhoneCellEditMode({
|
||||
|
||||
const updateField = useUpdateEntityField();
|
||||
|
||||
function handleSubmit(newText: string) {
|
||||
if (newText === fieldValue) return;
|
||||
function handleSubmit(newValue: string) {
|
||||
if (!isPossiblePhoneNumber(newValue)) return;
|
||||
|
||||
setFieldValue(newText);
|
||||
if (newValue === fieldValue) return;
|
||||
|
||||
setFieldValue(newValue);
|
||||
|
||||
if (currentRowEntityId && updateField) {
|
||||
updateField(currentRowEntityId, columnDefinition, newText);
|
||||
updateField(currentRowEntityId, columnDefinition, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
handleEnter,
|
||||
handleEscape,
|
||||
handleTab,
|
||||
handleShiftTab,
|
||||
handleClickOutside,
|
||||
} = useCellInputEventHandlers({
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
return (
|
||||
<PhoneCellEdit
|
||||
<PhoneInput
|
||||
placeholder={columnDefinition.metadata.placeHolder ?? ''}
|
||||
autoFocus
|
||||
value={fieldValue ?? ''}
|
||||
onSubmit={handleSubmit}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
onShiftTab={handleShiftTab}
|
||||
onTab={handleTab}
|
||||
hotkeyScope={TableHotkeyScope.CellEditMode}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { TextDisplay } from '@/ui/content-display/components/TextDisplay';
|
||||
import type { ViewFieldTextMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { TextInputDisplay } from '@/ui/input/text/components/TextInputDisplay';
|
||||
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
@ -34,7 +34,7 @@ export function GenericEditableTextCell({
|
||||
editModeContent={
|
||||
<GenericEditableTextCellEditMode columnDefinition={columnDefinition} />
|
||||
}
|
||||
nonEditModeContent={<TextInputDisplay>{fieldValue}</TextInputDisplay>}
|
||||
nonEditModeContent={<TextDisplay text={fieldValue} />}
|
||||
></EditableCell>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import type { ViewFieldTextMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { useCellInputEventHandlers } from '@/ui/table/hooks/useCellInputEventHandlers';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
|
||||
|
||||
import { TextInput } from '../../../../input/components/TextInput';
|
||||
import type { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
import { TextCellEdit } from './TextCellEdit';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldTextMetadata>;
|
||||
};
|
||||
@ -38,12 +39,27 @@ export function GenericEditableTextCellEditMode({
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
handleEnter,
|
||||
handleEscape,
|
||||
handleTab,
|
||||
handleShiftTab,
|
||||
handleClickOutside,
|
||||
} = useCellInputEventHandlers({
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
return (
|
||||
<TextCellEdit
|
||||
<TextInput
|
||||
placeholder={columnDefinition.metadata.placeHolder ?? ''}
|
||||
autoFocus
|
||||
value={fieldValue ?? ''}
|
||||
onSubmit={handleSubmit}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
onTab={handleTab}
|
||||
onShiftTab={handleShiftTab}
|
||||
hotkeyScope={TableHotkeyScope.CellEditMode}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { URLDisplay } from '@/ui/content-display/components/URLDisplay';
|
||||
import type { ViewFieldURLMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { InplaceInputURLDisplayMode } from '@/ui/input/url/components/URLTextInputDisplay';
|
||||
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
@ -36,9 +36,7 @@ export function GenericEditableURLCell({
|
||||
editModeContent={
|
||||
<GenericEditableURLCellEditMode columnDefinition={columnDefinition} />
|
||||
}
|
||||
nonEditModeContent={
|
||||
<InplaceInputURLDisplayMode value={sanitizeURL(fieldValue)} />
|
||||
}
|
||||
nonEditModeContent={<URLDisplay value={sanitizeURL(fieldValue)} />}
|
||||
></EditableCell>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,15 +1,16 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import type { ViewFieldURLMetadata } from '@/ui/editable-field/types/ViewField';
|
||||
import { useCellInputEventHandlers } from '@/ui/table/hooks/useCellInputEventHandlers';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
|
||||
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
|
||||
import { isURL } from '~/utils/is-url';
|
||||
|
||||
import { TextInput } from '../../../../input/components/TextInput';
|
||||
import type { ColumnDefinition } from '../../../types/ColumnDefinition';
|
||||
|
||||
import { TextCellEdit } from './TextCellEdit';
|
||||
|
||||
type OwnProps = {
|
||||
columnDefinition: ColumnDefinition<ViewFieldURLMetadata>;
|
||||
};
|
||||
@ -39,12 +40,27 @@ export function GenericEditableURLCellEditMode({ columnDefinition }: OwnProps) {
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
handleEnter,
|
||||
handleEscape,
|
||||
handleTab,
|
||||
handleShiftTab,
|
||||
handleClickOutside,
|
||||
} = useCellInputEventHandlers({
|
||||
onSubmit: handleSubmit,
|
||||
});
|
||||
|
||||
return (
|
||||
<TextCellEdit
|
||||
<TextInput
|
||||
placeholder={columnDefinition.metadata.placeHolder ?? ''}
|
||||
autoFocus
|
||||
value={fieldValue ?? ''}
|
||||
onSubmit={handleSubmit}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
onTab={handleTab}
|
||||
onShiftTab={handleShiftTab}
|
||||
hotkeyScope={TableHotkeyScope.CellEditMode}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,108 +0,0 @@
|
||||
import { useRef, useState } from 'react';
|
||||
import PhoneInput, { isPossiblePhoneNumber } from 'react-phone-number-input';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { useRegisterCloseCellHandlers } from '../../hooks/useRegisterCloseCellHandlers';
|
||||
|
||||
import 'react-phone-number-input/style.css';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
|
||||
border: none;
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
export type PhoneCellEditProps = {
|
||||
placeholder?: string;
|
||||
autoFocus?: boolean;
|
||||
value: string;
|
||||
onSubmit: (newText: string) => void;
|
||||
};
|
||||
|
||||
const StyledCustomPhoneInput = styled(PhoneInput)`
|
||||
--PhoneInput-color--focus: transparent;
|
||||
--PhoneInputCountryFlag-borderColor--focus: transparent;
|
||||
--PhoneInputCountrySelect-marginRight: ${({ theme }) => theme.spacing(2)};
|
||||
--PhoneInputCountrySelectArrow-color: ${({ theme }) =>
|
||||
theme.font.color.tertiary};
|
||||
--PhoneInputCountrySelectArrow-opacity: 1;
|
||||
font-family: ${({ theme }) => theme.font.family};
|
||||
height: 32px;
|
||||
|
||||
.PhoneInputCountry {
|
||||
--PhoneInputCountryFlag-height: 12px;
|
||||
--PhoneInputCountryFlag-width: 16px;
|
||||
border-right: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-left: ${({ theme }) => theme.spacing(2)};
|
||||
}
|
||||
|
||||
.PhoneInputCountryIcon {
|
||||
background: none;
|
||||
border-radius: ${({ theme }) => theme.border.radius.xs};
|
||||
box-shadow: none;
|
||||
margin-right: 1px;
|
||||
overflow: hidden;
|
||||
&:focus {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.PhoneInputCountrySelectArrow {
|
||||
margin-right: ${({ theme }) => theme.spacing(2)};
|
||||
}
|
||||
|
||||
.PhoneInputInput {
|
||||
background: ${({ theme }) => theme.background.transparent.secondary};
|
||||
border: none;
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
|
||||
&::placeholder,
|
||||
&::-webkit-input-placeholder {
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
font-family: ${({ theme }) => theme.font.family};
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
}
|
||||
|
||||
:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export function PhoneCellEdit({
|
||||
autoFocus,
|
||||
value,
|
||||
onSubmit,
|
||||
}: PhoneCellEditProps) {
|
||||
const [internalValue, setInternalValue] = useState<string | undefined>(value);
|
||||
|
||||
const wrapperRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
function handleSubmit() {
|
||||
if (
|
||||
internalValue === undefined ||
|
||||
isPossiblePhoneNumber(internalValue ?? '')
|
||||
) {
|
||||
onSubmit(internalValue ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
useRegisterCloseCellHandlers(wrapperRef, handleSubmit);
|
||||
|
||||
return (
|
||||
<StyledContainer ref={wrapperRef}>
|
||||
<StyledCustomPhoneInput
|
||||
autoFocus={autoFocus}
|
||||
placeholder="Phone number"
|
||||
value={value}
|
||||
onChange={setInternalValue}
|
||||
/>
|
||||
</StyledContainer>
|
||||
);
|
||||
}
|
||||
@ -1,59 +0,0 @@
|
||||
import { ChangeEvent, useEffect, useRef, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { textInputStyle } from '@/ui/theme/constants/effects';
|
||||
|
||||
import { useRegisterCloseCellHandlers } from '../../hooks/useRegisterCloseCellHandlers';
|
||||
|
||||
export const StyledInput = styled.input`
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
${textInputStyle}
|
||||
`;
|
||||
|
||||
type OwnProps = {
|
||||
placeholder?: string;
|
||||
autoFocus?: boolean;
|
||||
value: string;
|
||||
onSubmit: (newText: string) => void;
|
||||
};
|
||||
|
||||
export function TextCellEdit({
|
||||
placeholder,
|
||||
autoFocus,
|
||||
value,
|
||||
onSubmit,
|
||||
}: OwnProps) {
|
||||
const [internalText, setInternalText] = useState(value);
|
||||
|
||||
const wrapperRef = useRef(null);
|
||||
|
||||
function handleSubmit() {
|
||||
onSubmit(internalText);
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
setInternalText(value);
|
||||
}
|
||||
|
||||
function handleChange(event: ChangeEvent<HTMLInputElement>) {
|
||||
setInternalText(event.target.value);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setInternalText(value);
|
||||
}, [value]);
|
||||
|
||||
useRegisterCloseCellHandlers(wrapperRef, handleSubmit, handleCancel);
|
||||
|
||||
return (
|
||||
<StyledInput
|
||||
autoComplete="off"
|
||||
ref={wrapperRef}
|
||||
placeholder={placeholder}
|
||||
onChange={handleChange}
|
||||
autoFocus={autoFocus}
|
||||
value={internalText}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -1,12 +1,12 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { PhoneCellEdit } from '@/ui/table/editable-cell/type/components/PhoneCellEdit';
|
||||
import { PhoneInput } from '@/ui/input/components/PhoneInput';
|
||||
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator';
|
||||
|
||||
const meta: Meta<typeof PhoneCellEdit> = {
|
||||
const meta: Meta<typeof PhoneInput> = {
|
||||
title: 'UI/Table/EditableCell/PhoneCellEdit',
|
||||
component: PhoneCellEdit,
|
||||
component: PhoneInput,
|
||||
decorators: [ComponentWithRecoilScopeDecorator],
|
||||
args: {
|
||||
value: '+33714446494',
|
||||
@ -18,6 +18,6 @@ const meta: Meta<typeof PhoneCellEdit> = {
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof PhoneCellEdit>;
|
||||
type Story = StoryObj<typeof PhoneInput>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
import { useCurrentCellEditMode } from '../editable-cell/hooks/useCurrentCellEditMode';
|
||||
import { useEditableCell } from '../editable-cell/hooks/useEditableCell';
|
||||
|
||||
import { useMoveSoftFocus } from './useMoveSoftFocus';
|
||||
|
||||
export function useCellInputEventHandlers<T>({
|
||||
onSubmit,
|
||||
onCancel,
|
||||
}: {
|
||||
onSubmit?: (newValue: T) => void;
|
||||
onCancel?: () => void;
|
||||
}) {
|
||||
const { closeEditableCell } = useEditableCell();
|
||||
const { isCurrentCellInEditMode } = useCurrentCellEditMode();
|
||||
const { moveRight, moveLeft, moveDown } = useMoveSoftFocus();
|
||||
|
||||
return {
|
||||
handleClickOutside: (event: MouseEvent | TouchEvent, newValue: T) => {
|
||||
if (isCurrentCellInEditMode) {
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
onSubmit?.(newValue);
|
||||
|
||||
closeEditableCell();
|
||||
}
|
||||
},
|
||||
handleEscape: () => {
|
||||
closeEditableCell();
|
||||
onCancel?.();
|
||||
},
|
||||
handleEnter: (newValue: T) => {
|
||||
onSubmit?.(newValue);
|
||||
closeEditableCell();
|
||||
moveDown();
|
||||
},
|
||||
handleTab: (newValue: T) => {
|
||||
onSubmit?.(newValue);
|
||||
closeEditableCell();
|
||||
moveRight();
|
||||
},
|
||||
handleShiftTab: (newValue: T) => {
|
||||
onSubmit?.(newValue);
|
||||
closeEditableCell();
|
||||
moveLeft();
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user