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:
Lucas Bordeau
2023-09-12 02:11:20 +02:00
committed by GitHub
parent 509ffddc57
commit a766c60aa5
90 changed files with 618 additions and 461 deletions

View File

@ -7,7 +7,7 @@ import { currentUserState } from '@/auth/states/currentUserState';
import { import {
AutosizeTextInput, AutosizeTextInput,
AutosizeTextInputVariant, AutosizeTextInputVariant,
} from '@/ui/input/autosize-text/components/AutosizeTextInput'; } from '@/ui/input/components/AutosizeTextInput';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { Activity, useCreateCommentMutation } from '~/generated/graphql'; import { Activity, useCreateCommentMutation } from '~/generated/graphql';
import { isNonEmptyString } from '~/utils/isNonEmptyString'; import { isNonEmptyString } from '~/utils/isNonEmptyString';

View File

@ -4,7 +4,7 @@ import {
Checkbox, Checkbox,
CheckboxShape, CheckboxShape,
CheckboxSize, CheckboxSize,
} from '@/ui/input/checkbox/components/Checkbox'; } from '@/ui/input/components/Checkbox';
import { ActivityType } from '~/generated/graphql'; import { ActivityType } from '~/generated/graphql';
const StyledEditableTitleInput = styled.input<{ const StyledEditableTitleInput = styled.input<{

View File

@ -4,10 +4,7 @@ import styled from '@emotion/styled';
import { ActivityTargetChips } from '@/activities/components/ActivityTargetChips'; import { ActivityTargetChips } from '@/activities/components/ActivityTargetChips';
import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer'; import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer';
import { IconCalendar, IconComment } from '@/ui/icon'; import { IconCalendar, IconComment } from '@/ui/icon';
import { import { Checkbox, CheckboxShape } from '@/ui/input/components/Checkbox';
Checkbox,
CheckboxShape,
} from '@/ui/input/checkbox/components/Checkbox';
import { OverflowingTextWithTooltip } from '@/ui/tooltip/OverflowingTextWithTooltip'; import { OverflowingTextWithTooltip } from '@/ui/tooltip/OverflowingTextWithTooltip';
import { beautifyExactDate, hasDatePassed } from '~/utils/date-utils'; import { beautifyExactDate, hasDatePassed } from '~/utils/date-utils';

View File

@ -1,9 +1,6 @@
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { import { Checkbox, CheckboxShape } from '@/ui/input/components/Checkbox';
Checkbox,
CheckboxShape,
} from '@/ui/input/checkbox/components/Checkbox';
import { OverflowingTextWithTooltip } from '@/ui/tooltip/OverflowingTextWithTooltip'; import { OverflowingTextWithTooltip } from '@/ui/tooltip/OverflowingTextWithTooltip';
import { ActivityType } from '~/generated/graphql'; import { ActivityType } from '~/generated/graphql';

View File

@ -6,7 +6,7 @@ import { motion } from 'framer-motion';
import { MainButton } from '@/ui/button/components/MainButton'; import { MainButton } from '@/ui/button/components/MainButton';
import { IconBrandGoogle } from '@/ui/icon'; import { IconBrandGoogle } from '@/ui/icon';
import { TextInput } from '@/ui/input/text/components/TextInput'; import { TextInputSettings } from '@/ui/input/text/components/TextInputSettings';
import { AnimatedEaseIn } from '@/ui/utilities/animation/components/AnimatedEaseIn'; import { AnimatedEaseIn } from '@/ui/utilities/animation/components/AnimatedEaseIn';
import { Logo } from '../../components/Logo'; import { Logo } from '../../components/Logo';
@ -132,7 +132,7 @@ export function SignInUpForm() {
fieldState: { error }, fieldState: { error },
}) => ( }) => (
<StyledInputContainer> <StyledInputContainer>
<TextInput <TextInputSettings
autoFocus autoFocus
value={value} value={value}
placeholder="Email" placeholder="Email"
@ -170,7 +170,7 @@ export function SignInUpForm() {
fieldState: { error }, fieldState: { error },
}) => ( }) => (
<StyledInputContainer> <StyledInputContainer>
<TextInput <TextInputSettings
autoFocus autoFocus
value={value} value={value}
type="password" type="password"

View File

@ -12,7 +12,7 @@ import { GET_PEOPLE } from '@/people/graphql/queries/getPeople';
import { LightIconButton } from '@/ui/button/components/LightIconButton'; import { LightIconButton } from '@/ui/button/components/LightIconButton';
import { IconPlus } from '@/ui/icon'; import { IconPlus } from '@/ui/icon';
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope'; import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
import { TextInput } from '@/ui/input/text/components/TextInput'; import { TextInputSettings } from '@/ui/input/text/components/TextInputSettings';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { import {
@ -134,13 +134,13 @@ export function AddPersonToCompany({
<div ref={refs.setFloating} style={floatingStyles}> <div ref={refs.setFloating} style={floatingStyles}>
{isCreationDropdownOpen ? ( {isCreationDropdownOpen ? (
<StyledInputContainer> <StyledInputContainer>
<TextInput <TextInputSettings
onKeyDown={handleInputKeyDown} onKeyDown={handleInputKeyDown}
value={username.firstName} value={username.firstName}
onChange={handleUsernameChange('firstName')} onChange={handleUsernameChange('firstName')}
placeholder="First Name" placeholder="First Name"
/> />
<TextInput <TextInputSettings
onKeyDown={handleInputKeyDown} onKeyDown={handleInputKeyDown}
value={username.lastName} value={username.lastName}
onChange={handleUsernameChange('lastName')} onChange={handleUsernameChange('lastName')}

View File

@ -10,10 +10,7 @@ import { GenericEditableField } from '@/ui/editable-field/components/GenericEdit
import { EditableFieldDefinitionContext } from '@/ui/editable-field/contexts/EditableFieldDefinitionContext'; import { EditableFieldDefinitionContext } from '@/ui/editable-field/contexts/EditableFieldDefinitionContext';
import { EditableFieldEntityIdContext } from '@/ui/editable-field/contexts/EditableFieldEntityIdContext'; import { EditableFieldEntityIdContext } from '@/ui/editable-field/contexts/EditableFieldEntityIdContext';
import { EditableFieldMutationContext } from '@/ui/editable-field/contexts/EditableFieldMutationContext'; import { EditableFieldMutationContext } from '@/ui/editable-field/contexts/EditableFieldMutationContext';
import { import { Checkbox, CheckboxVariant } from '@/ui/input/components/Checkbox';
Checkbox,
CheckboxVariant,
} from '@/ui/input/checkbox/components/Checkbox';
import { useUpdateOnePipelineProgressMutation } from '~/generated/graphql'; import { useUpdateOnePipelineProgressMutation } from '~/generated/graphql';
import { getLogoUrlFromDomainName } from '~/utils'; import { getLogoUrlFromDomainName } from '~/utils';

View File

@ -1,7 +1,7 @@
import { useState } from 'react'; import { useState } from 'react';
import { FieldRecoilScopeContext } from '@/ui/editable-field/states/recoil-scope-contexts/FieldRecoilScopeContext'; import { FieldRecoilScopeContext } from '@/ui/editable-field/states/recoil-scope-contexts/FieldRecoilScopeContext';
import { DoubleTextInputEdit } from '@/ui/input/double-text/components/DoubleTextInputEdit'; import { DoubleTextInputEdit } from '@/ui/input/components/DoubleTextInputEdit';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { Person, useUpdateOnePersonMutation } from '~/generated/graphql'; import { Person, useUpdateOnePersonMutation } from '~/generated/graphql';

View File

@ -1,13 +1,13 @@
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { currentUserState } from '@/auth/states/currentUserState'; import { currentUserState } from '@/auth/states/currentUserState';
import { TextInput } from '@/ui/input/text/components/TextInput'; import { TextInputSettings } from '@/ui/input/text/components/TextInputSettings';
export function EmailField() { export function EmailField() {
const currentUser = useRecoilValue(currentUserState); const currentUser = useRecoilValue(currentUserState);
return ( return (
<TextInput <TextInputSettings
value={currentUser?.email} value={currentUser?.email}
disabled disabled
fullWidth fullWidth

View File

@ -5,7 +5,7 @@ import debounce from 'lodash.debounce';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { currentUserState } from '@/auth/states/currentUserState'; import { currentUserState } from '@/auth/states/currentUserState';
import { TextInput } from '@/ui/input/text/components/TextInput'; import { TextInputSettings } from '@/ui/input/text/components/TextInputSettings';
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser'; import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
import { useUpdateUserMutation } from '~/generated/graphql'; import { useUpdateUserMutation } from '~/generated/graphql';
@ -86,14 +86,14 @@ export function NameFields({
return ( return (
<StyledComboInputContainer> <StyledComboInputContainer>
<TextInput <TextInputSettings
label="First Name" label="First Name"
value={firstName} value={firstName}
onChange={setFirstName} onChange={setFirstName}
placeholder="Tim" placeholder="Tim"
fullWidth fullWidth
/> />
<TextInput <TextInputSettings
label="Last Name" label="Last Name"
value={lastName} value={lastName}
onChange={setLastName} onChange={setLastName}

View File

@ -1,7 +1,7 @@
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { currentUserState } from '@/auth/states/currentUserState'; import { currentUserState } from '@/auth/states/currentUserState';
import { Toggle } from '@/ui/input/toggle/components/Toggle'; import { Toggle } from '@/ui/input/components/Toggle';
import { useSnackBar } from '@/ui/snack-bar/hooks/useSnackBar'; import { useSnackBar } from '@/ui/snack-bar/hooks/useSnackBar';
import { useUpdateAllowImpersonationMutation } from '~/generated/graphql'; import { useUpdateAllowImpersonationMutation } from '~/generated/graphql';

View File

@ -5,7 +5,7 @@ import debounce from 'lodash.debounce';
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import { currentUserState } from '@/auth/states/currentUserState'; import { currentUserState } from '@/auth/states/currentUserState';
import { TextInput } from '@/ui/input/text/components/TextInput'; import { TextInputSettings } from '@/ui/input/text/components/TextInputSettings';
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser'; import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
import { useUpdateWorkspaceMutation } from '~/generated/graphql'; import { useUpdateWorkspaceMutation } from '~/generated/graphql';
@ -68,7 +68,7 @@ export function NameField({ autoSave = true, onNameUpdate }: OwnProps) {
return ( return (
<StyledComboInputContainer> <StyledComboInputContainer>
<TextInput <TextInputSettings
label="Name" label="Name"
value={displayName} value={displayName}
onChange={setDisplayName} onChange={setDisplayName}

View File

@ -1,7 +1,7 @@
import { Column, FormatterProps, useRowSelection } from 'react-data-grid'; import { Column, FormatterProps, useRowSelection } from 'react-data-grid';
import type { RawData } from '@/spreadsheet-import/types'; import type { RawData } from '@/spreadsheet-import/types';
import { Radio } from '@/ui/input/radio/components/Radio'; import { Radio } from '@/ui/input/components/Radio';
const SELECT_COLUMN_KEY = 'select-row'; const SELECT_COLUMN_KEY = 'select-row';

View File

@ -3,8 +3,8 @@ import styled from '@emotion/styled';
import { ContinueButton } from '@/spreadsheet-import/components/ContinueButton'; import { ContinueButton } from '@/spreadsheet-import/components/ContinueButton';
import { Heading } from '@/spreadsheet-import/components/Heading'; import { Heading } from '@/spreadsheet-import/components/Heading';
import { Radio } from '@/ui/input/radio/components/Radio'; import { Radio } from '@/ui/input/components/Radio';
import { RadioGroup } from '@/ui/input/radio/components/RadioGroup'; import { RadioGroup } from '@/ui/input/components/RadioGroup';
import { Modal } from '@/ui/modal/components/Modal'; import { Modal } from '@/ui/modal/components/Modal';
const StyledContent = styled(Modal.Content)` const StyledContent = styled(Modal.Content)`

View File

@ -11,7 +11,7 @@ import { addErrorsAndRunHooks } from '@/spreadsheet-import/utils/dataMutations';
import { Button } from '@/ui/button/components/Button'; import { Button } from '@/ui/button/components/Button';
import { useDialog } from '@/ui/dialog/hooks/useDialog'; import { useDialog } from '@/ui/dialog/hooks/useDialog';
import { IconTrash } from '@/ui/icon'; import { IconTrash } from '@/ui/icon';
import { Toggle } from '@/ui/input/toggle/components/Toggle'; import { Toggle } from '@/ui/input/components/Toggle';
import { Modal } from '@/ui/modal/components/Modal'; import { Modal } from '@/ui/modal/components/Modal';
import { generateColumns } from './components/columns'; import { generateColumns } from './components/columns';

View File

@ -4,12 +4,9 @@ import styled from '@emotion/styled';
import { MatchColumnSelect } from '@/spreadsheet-import/components/MatchColumnSelect'; import { MatchColumnSelect } from '@/spreadsheet-import/components/MatchColumnSelect';
import type { Data, Fields } from '@/spreadsheet-import/types'; import type { Data, Fields } from '@/spreadsheet-import/types';
import { import { Checkbox, CheckboxVariant } from '@/ui/input/components/Checkbox';
Checkbox, import { Toggle } from '@/ui/input/components/Toggle';
CheckboxVariant, import { TextInputSettings } from '@/ui/input/text/components/TextInputSettings';
} from '@/ui/input/checkbox/components/Checkbox';
import { TextInput } from '@/ui/input/text/components/TextInput';
import { Toggle } from '@/ui/input/toggle/components/Toggle';
import { AppTooltip } from '@/ui/tooltip/AppTooltip'; import { AppTooltip } from '@/ui/tooltip/AppTooltip';
import type { Meta } from '../types'; import type { Meta } from '../types';
@ -149,7 +146,7 @@ export const generateColumns = <T extends string>(
} }
default: default:
component = ( component = (
<TextInput <TextInputSettings
value={row[columnKey] as string} value={row[columnKey] as string}
onChange={(value: string) => { onChange={(value: string) => {
onRowChange({ ...row, [columnKey]: value }); onRowChange({ ...row, [columnKey]: value });

View File

@ -7,7 +7,7 @@ type OwnProps = {
value: string | null; value: string | null;
}; };
export function PhoneInputDisplay({ value }: OwnProps) { export function PhoneDisplay({ value }: OwnProps) {
return value && isValidPhoneNumber(value) ? ( return value && isValidPhoneNumber(value) ? (
<ContactLink <ContactLink
href={parsePhoneNumber(value, 'FR')?.getURI()} href={parsePhoneNumber(value, 'FR')?.getURI()}

View File

@ -0,0 +1,16 @@
import styled from '@emotion/styled';
const StyledTextInputDisplay = styled.div`
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
`;
type OwnProps = {
text: string;
};
export function TextDisplay({ text }: OwnProps) {
return <StyledTextInputDisplay>{text}</StyledTextInputDisplay>;
}

View File

@ -33,7 +33,7 @@ const checkUrlType = (url: string) => {
return LinkType.Url; return LinkType.Url;
}; };
export function InplaceInputURLDisplayMode({ value }: OwnProps) { export function URLDisplay({ value }: OwnProps) {
function handleClick(event: MouseEvent<HTMLElement>) { function handleClick(event: MouseEvent<HTMLElement>) {
event.stopPropagation(); event.stopPropagation();
} }

View File

@ -2,11 +2,11 @@ import { Meta, StoryObj } from '@storybook/react';
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator'; import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
import { PhoneInputDisplay } from '../PhoneInputDisplay'; // Adjust the import path as needed import { PhoneDisplay } from '../PhoneDisplay'; // Adjust the import path as needed
const meta: Meta = { const meta: Meta = {
title: 'UI/Input/PhoneInputDisplay', title: 'UI/Input/PhoneInputDisplay',
component: PhoneInputDisplay, component: PhoneDisplay,
decorators: [ComponentWithRouterDecorator], decorators: [ComponentWithRouterDecorator],
args: { args: {
value: '+33788901234', value: '+33788901234',
@ -15,6 +15,6 @@ const meta: Meta = {
export default meta; export default meta;
type Story = StoryObj<typeof PhoneInputDisplay>; type Story = StoryObj<typeof PhoneDisplay>;
export const Default: Story = {}; export const Default: Story = {};

View File

@ -83,8 +83,6 @@ type OwnProps = {
isDisplayModeContentEmpty?: boolean; isDisplayModeContentEmpty?: boolean;
isDisplayModeFixHeight?: boolean; isDisplayModeFixHeight?: boolean;
disableHoverEffect?: boolean; disableHoverEffect?: boolean;
onSubmit?: () => void;
onCancel?: () => void;
}; };
export function EditableField({ export function EditableField({
@ -99,8 +97,6 @@ export function EditableField({
displayModeContentOnly, displayModeContentOnly,
isDisplayModeFixHeight, isDisplayModeFixHeight,
disableHoverEffect, disableHoverEffect,
onSubmit,
onCancel,
}: OwnProps) { }: OwnProps) {
const [isHovered, setIsHovered] = useState(false); const [isHovered, setIsHovered] = useState(false);
@ -115,10 +111,13 @@ export function EditableField({
const { isFieldInEditMode, openEditableField } = useEditableField(); const { isFieldInEditMode, openEditableField } = useEditableField();
function handleDisplayModeClick() { function handleDisplayModeClick() {
openEditableField(customEditHotkeyScope); if (!displayModeContentOnly) {
openEditableField(customEditHotkeyScope);
}
} }
const showEditButton = !isFieldInEditMode && isHovered && useEditButton; const showEditButton =
!isFieldInEditMode && isHovered && useEditButton && !displayModeContentOnly;
return ( return (
<StyledEditableFieldBaseContainer <StyledEditableFieldBaseContainer
@ -137,10 +136,8 @@ export function EditableField({
</StyledLabelAndIconContainer> </StyledLabelAndIconContainer>
<StyledValueContainer> <StyledValueContainer>
{isFieldInEditMode && !displayModeContentOnly ? ( {isFieldInEditMode ? (
<EditableFieldEditMode onSubmit={onSubmit} onCancel={onCancel}> <EditableFieldEditMode>{editModeContent}</EditableFieldEditMode>
{editModeContent}
</EditableFieldEditMode>
) : ( ) : (
<StyledClickableContainer onClick={handleDisplayModeClick}> <StyledClickableContainer onClick={handleDisplayModeClick}>
<EditableFieldDisplayMode <EditableFieldDisplayMode

View File

@ -1,7 +1,6 @@
import { useRef } from 'react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useRegisterCloseFieldHandlers } from '../hooks/useRegisterCloseFieldHandlers'; import { overlayBackground } from '@/ui/theme/constants/effects';
const StyledEditableFieldEditModeContainer = styled.div<OwnProps>` const StyledEditableFieldEditModeContainer = styled.div<OwnProps>`
align-items: center; align-items: center;
@ -14,28 +13,28 @@ const StyledEditableFieldEditModeContainer = styled.div<OwnProps>`
z-index: 10; 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 = { type OwnProps = {
children: React.ReactNode; children: React.ReactNode;
onOutsideClick?: () => void;
onCancel?: () => void;
onSubmit?: () => void;
}; };
export function EditableFieldEditMode({ export function EditableFieldEditMode({ children }: OwnProps) {
children,
onCancel,
onSubmit,
}: OwnProps) {
const wrapperRef = useRef(null);
useRegisterCloseFieldHandlers(wrapperRef, onSubmit, onCancel);
return ( return (
<StyledEditableFieldEditModeContainer <StyledEditableFieldEditModeContainer data-testid="editable-field-edit-mode-container">
data-testid="editable-field-edit-mode-container" <StyledEditableFieldInput>{children}</StyledEditableFieldInput>
ref={wrapperRef}
>
{children}
</StyledEditableFieldEditModeContainer> </StyledEditableFieldEditModeContainer>
); );
} }

View File

@ -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>;
}

View File

@ -1,9 +1,7 @@
import { useContext } from 'react'; import { useContext } from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import { IconCheck, IconX } from '@/ui/icon'; import { BooleanInput } from '@/ui/input/components/BooleanInput';
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext'; import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext'; import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext';
@ -12,16 +10,6 @@ import { genericEntityFieldFamilySelector } from '../states/selectors/genericEnt
import { FieldDefinition } from '../types/FieldDefinition'; import { FieldDefinition } from '../types/FieldDefinition';
import { FieldBooleanMetadata } from '../types/FieldMetadata'; 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() { export function GenericEditableBooleanFieldDisplayMode() {
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext); const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
const currentEditableFieldDefinition = useContext( const currentEditableFieldDefinition = useContext(
@ -37,32 +25,20 @@ export function GenericEditableBooleanFieldDisplayMode() {
}), }),
); );
const theme = useTheme();
const updateField = useUpdateGenericEntityField(); const updateField = useUpdateGenericEntityField();
function toggleValue() { function handleSubmit(newValue: boolean) {
const newToggledValue = !fieldValue;
setFieldValue(newToggledValue);
if (currentEditableFieldEntityId && updateField) { if (currentEditableFieldEntityId && updateField) {
updateField( updateField(
currentEditableFieldEntityId, currentEditableFieldEntityId,
currentEditableFieldDefinition, currentEditableFieldDefinition,
newToggledValue, newValue,
); );
// TODO: use optimistic effect instead, but needs generic refactor
setFieldValue(newValue);
} }
} }
return ( return <BooleanInput value={fieldValue} onToggle={handleSubmit} />;
<StyledEditableBooleanFieldContainer onClick={toggleValue}>
{fieldValue ? (
<IconCheck size={theme.icon.size.sm} />
) : (
<IconX size={theme.icon.size.sm} />
)}
<StyledEditableBooleanFieldValue>
{fieldValue ? 'True' : 'False'}
</StyledEditableBooleanFieldValue>
</StyledEditableBooleanFieldContainer>
);
} }

View File

@ -1,7 +1,7 @@
import { useContext } from 'react'; import { useContext } from 'react';
import { useRecoilValue } from 'recoil'; 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 { parseDate } from '~/utils/date-utils';
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext'; import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';

View File

@ -1,7 +1,7 @@
import { useContext } from 'react'; import { useContext } from 'react';
import { useRecoilValue } from 'recoil'; 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 { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext'; import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
@ -35,7 +35,7 @@ export function GenericEditablePhoneField() {
useEditButton useEditButton
IconLabel={currentEditableFieldDefinition.Icon} IconLabel={currentEditableFieldDefinition.Icon}
editModeContent={<GenericEditablePhoneFieldEditMode />} editModeContent={<GenericEditablePhoneFieldEditMode />}
displayModeContent={<PhoneInputDisplay value={fieldValue} />} displayModeContent={<PhoneDisplay value={fieldValue} />}
isDisplayModeContentEmpty={!fieldValue} isDisplayModeContentEmpty={!fieldValue}
/> />
</RecoilScope> </RecoilScope>

View File

@ -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 { useRecoilState } from 'recoil';
import { TextInputEdit } from '@/ui/input/text/components/TextInputEdit'; import { PhoneInput } from '@/ui/input/components/PhoneInput';
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext'; import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext'; import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext';
import { useRegisterCloseFieldHandlers } from '../hooks/useRegisterCloseFieldHandlers'; import { useFieldInputEventHandlers } from '../hooks/useFieldInputEventHandlers';
import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField'; import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField';
import { genericEntityFieldFamilySelector } from '../states/selectors/genericEntityFieldFamilySelector'; import { genericEntityFieldFamilySelector } from '../states/selectors/genericEntityFieldFamilySelector';
import { EditableFieldHotkeyScope } from '../types/EditableFieldHotkeyScope';
import { FieldDefinition } from '../types/FieldDefinition'; import { FieldDefinition } from '../types/FieldDefinition';
import { FieldPhoneMetadata } from '../types/FieldMetadata'; import { FieldPhoneMetadata } from '../types/FieldMetadata';
@ -27,46 +29,34 @@ export function GenericEditablePhoneFieldEditMode() {
}), }),
); );
const [internalValue, setInternalValue] = useState(fieldValue);
const updateField = useUpdateGenericEntityField(); const updateField = useUpdateGenericEntityField();
const wrapperRef = useRef(null); function handleSubmit(newValue: string) {
if (!isPossiblePhoneNumber(newValue)) return;
useRegisterCloseFieldHandlers(wrapperRef, handleSubmit, onCancel); setFieldValue(newValue);
function handleSubmit() {
if (internalValue === fieldValue) return;
setFieldValue(internalValue);
if (currentEditableFieldEntityId && updateField) { if (currentEditableFieldEntityId && updateField) {
updateField( updateField(
currentEditableFieldEntityId, currentEditableFieldEntityId,
currentEditableFieldDefinition, currentEditableFieldDefinition,
internalValue, newValue,
); );
} }
} }
function onCancel() { const { handleEnter, handleEscape, handleClickOutside } =
setFieldValue(fieldValue); useFieldInputEventHandlers({
} onSubmit: handleSubmit,
});
function handleChange(newValue: string) {
setInternalValue(newValue);
}
return ( return (
<div ref={wrapperRef}> <PhoneInput
<TextInputEdit value={fieldValue ?? ''}
autoFocus onClickOutside={handleClickOutside}
placeholder={currentEditableFieldDefinition.metadata.placeHolder} onEnter={handleEnter}
value={internalValue} onEscape={handleEscape}
onChange={(newValue: string) => { hotkeyScope={EditableFieldHotkeyScope.EditableField}
handleChange(newValue); />
}}
/>
</div>
); );
} }

View File

@ -1,6 +1,7 @@
import { useContext } from 'react'; import { useContext } from 'react';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { TextDisplay } from '@/ui/content-display/components/TextDisplay';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext'; import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
@ -33,7 +34,7 @@ export function GenericEditableTextField() {
<EditableField <EditableField
IconLabel={currentEditableFieldDefinition.Icon} IconLabel={currentEditableFieldDefinition.Icon}
editModeContent={<GenericEditableTextFieldEditMode />} editModeContent={<GenericEditableTextFieldEditMode />}
displayModeContent={fieldValue} displayModeContent={<TextDisplay text={fieldValue} />}
isDisplayModeContentEmpty={!fieldValue} isDisplayModeContentEmpty={!fieldValue}
/> />
</RecoilScope> </RecoilScope>

View File

@ -1,13 +1,14 @@
import { useContext, useRef, useState } from 'react'; import { useContext } from 'react';
import { useRecoilState } from 'recoil'; 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 { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext'; import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext';
import { useRegisterCloseFieldHandlers } from '../hooks/useRegisterCloseFieldHandlers'; import { useFieldInputEventHandlers } from '../hooks/useFieldInputEventHandlers';
import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField'; import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField';
import { genericEntityFieldFamilySelector } from '../states/selectors/genericEntityFieldFamilySelector'; import { genericEntityFieldFamilySelector } from '../states/selectors/genericEntityFieldFamilySelector';
import { EditableFieldHotkeyScope } from '../types/EditableFieldHotkeyScope';
import { FieldDefinition } from '../types/FieldDefinition'; import { FieldDefinition } from '../types/FieldDefinition';
import { FieldTextMetadata } from '../types/FieldMetadata'; import { FieldTextMetadata } from '../types/FieldMetadata';
@ -27,46 +28,35 @@ export function GenericEditableTextFieldEditMode() {
}), }),
); );
const [internalValue, setInternalValue] = useState(fieldValue);
const updateField = useUpdateGenericEntityField(); const updateField = useUpdateGenericEntityField();
const wrapperRef = useRef(null); function handleSubmit(newValue: string) {
useRegisterCloseFieldHandlers(wrapperRef, handleSubmit, onCancel);
function handleSubmit() {
if (internalValue === fieldValue) return;
setFieldValue(internalValue);
if (currentEditableFieldEntityId && updateField) { if (currentEditableFieldEntityId && updateField) {
updateField( updateField(
currentEditableFieldEntityId, currentEditableFieldEntityId,
currentEditableFieldDefinition, currentEditableFieldDefinition,
internalValue, newValue,
); );
// TODO: use optimistic effect instead, but needs generic refactor
setFieldValue(newValue);
} }
} }
function onCancel() { const { handleEnter, handleEscape, handleClickOutside } =
setFieldValue(fieldValue); useFieldInputEventHandlers({
} onSubmit: handleSubmit,
});
function handleChange(newValue: string) {
setInternalValue(newValue);
}
return ( return (
<div ref={wrapperRef}> <TextInput
<TextInputEdit placeholder={currentEditableFieldDefinition.metadata.placeHolder}
autoFocus autoFocus
placeholder={currentEditableFieldDefinition.metadata.placeHolder} value={fieldValue ?? ''}
value={internalValue} onClickOutside={handleClickOutside}
onChange={(newValue: string) => { onEnter={handleEnter}
handleChange(newValue); onEscape={handleEscape}
}} hotkeyScope={EditableFieldHotkeyScope.EditableField}
/> />
</div>
); );
} }

View File

@ -1,6 +1,7 @@
import { useContext } from 'react'; import { useContext } from 'react';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { URLDisplay } from '@/ui/content-display/components/URLDisplay';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext'; import { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
@ -11,7 +12,6 @@ import { FieldDefinition } from '../types/FieldDefinition';
import { FieldNumberMetadata } from '../types/FieldMetadata'; import { FieldNumberMetadata } from '../types/FieldMetadata';
import { EditableField } from './EditableField'; import { EditableField } from './EditableField';
import { FieldDisplayURL } from './FieldDisplayURL';
import { GenericEditableURLFieldEditMode } from './GenericEditableURLFieldEditMode'; import { GenericEditableURLFieldEditMode } from './GenericEditableURLFieldEditMode';
export function GenericEditableURLField() { export function GenericEditableURLField() {
@ -35,7 +35,7 @@ export function GenericEditableURLField() {
useEditButton useEditButton
IconLabel={currentEditableFieldDefinition.Icon} IconLabel={currentEditableFieldDefinition.Icon}
editModeContent={<GenericEditableURLFieldEditMode />} editModeContent={<GenericEditableURLFieldEditMode />}
displayModeContent={<FieldDisplayURL URL={fieldValue} />} displayModeContent={<URLDisplay value={fieldValue} />}
isDisplayModeContentEmpty={!fieldValue} isDisplayModeContentEmpty={!fieldValue}
isDisplayModeFixHeight isDisplayModeFixHeight
/> />

View File

@ -1,13 +1,14 @@
import { useContext, useRef, useState } from 'react'; import { useContext } from 'react';
import { useRecoilState } from 'recoil'; 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 { EditableFieldDefinitionContext } from '../contexts/EditableFieldDefinitionContext';
import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext'; import { EditableFieldEntityIdContext } from '../contexts/EditableFieldEntityIdContext';
import { useRegisterCloseFieldHandlers } from '../hooks/useRegisterCloseFieldHandlers'; import { useFieldInputEventHandlers } from '../hooks/useFieldInputEventHandlers';
import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField'; import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField';
import { genericEntityFieldFamilySelector } from '../states/selectors/genericEntityFieldFamilySelector'; import { genericEntityFieldFamilySelector } from '../states/selectors/genericEntityFieldFamilySelector';
import { EditableFieldHotkeyScope } from '../types/EditableFieldHotkeyScope';
import { FieldDefinition } from '../types/FieldDefinition'; import { FieldDefinition } from '../types/FieldDefinition';
import { FieldURLMetadata } from '../types/FieldMetadata'; import { FieldURLMetadata } from '../types/FieldMetadata';
@ -29,46 +30,34 @@ export function GenericEditableURLFieldEditMode() {
}), }),
); );
const [internalValue, setInternalValue] = useState(fieldValue);
const updateField = useUpdateGenericEntityField(); const updateField = useUpdateGenericEntityField();
const wrapperRef = useRef(null); function handleSubmit(newValue: string) {
setFieldValue(newValue);
useRegisterCloseFieldHandlers(wrapperRef, handleSubmit, onCancel);
function handleSubmit() {
if (internalValue === fieldValue) return;
setFieldValue(internalValue);
if (currentEditableFieldEntityId && updateField) { if (currentEditableFieldEntityId && updateField) {
updateField( updateField(
currentEditableFieldEntityId, currentEditableFieldEntityId,
currentEditableFieldDefinition, currentEditableFieldDefinition,
internalValue, newValue,
); );
} }
} }
function onCancel() { const { handleEnter, handleEscape, handleClickOutside } =
setFieldValue(fieldValue); useFieldInputEventHandlers({
} onSubmit: handleSubmit,
});
function handleChange(newValue: string) {
setInternalValue(newValue);
}
return ( return (
<div ref={wrapperRef}> <TextInput
<TextInputEdit placeholder={currentEditableFieldDefinition.metadata.placeHolder}
autoFocus autoFocus
placeholder={currentEditableFieldDefinition.metadata.placeHolder} value={fieldValue ?? ''}
value={internalValue} onClickOutside={handleClickOutside}
onChange={(newValue: string) => { onEnter={handleEnter}
handleChange(newValue); onEscape={handleEscape}
}} hotkeyScope={EditableFieldHotkeyScope.EditableField}
/> />
</div>
); );
} }

View File

@ -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();
},
};
}

View File

@ -1,7 +1,7 @@
import { EditableField } from '@/ui/editable-field/components/EditableField'; import { EditableField } from '@/ui/editable-field/components/EditableField';
import { FieldRecoilScopeContext } from '@/ui/editable-field/states/recoil-scope-contexts/FieldRecoilScopeContext'; import { FieldRecoilScopeContext } from '@/ui/editable-field/states/recoil-scope-contexts/FieldRecoilScopeContext';
import { IconComponent } from '@/ui/icon/types/IconComponent'; 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 { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { parseDate } from '~/utils/date-utils'; import { parseDate } from '~/utils/date-utils';

View File

@ -1,6 +1,6 @@
import { useEffect, useState } from 'react'; 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 { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { parseDate } from '~/utils/date-utils'; import { parseDate } from '~/utils/date-utils';

View File

@ -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>
);
}

View File

@ -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 = {};

View File

@ -0,0 +1,48 @@
import { useEffect, useState } from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { IconCheck, IconX } from '@/ui/icon';
const StyledEditableBooleanFieldContainer = styled.div`
align-items: center;
cursor: pointer;
display: flex;
`;
const StyledEditableBooleanFieldValue = styled.div`
margin-left: ${({ theme }) => theme.spacing(1)};
`;
type OwnProps = {
value: boolean;
onToggle?: (newValue: boolean) => void;
};
export function BooleanInput({ value, onToggle }: OwnProps) {
const [internalValue, setInternalValue] = useState(value);
useEffect(() => {
setInternalValue(value);
}, [value]);
function handleClick() {
setInternalValue(!internalValue);
onToggle?.(!internalValue);
}
const theme = useTheme();
return (
<StyledEditableBooleanFieldContainer onClick={handleClick}>
{internalValue ? (
<IconCheck size={theme.icon.size.sm} />
) : (
<IconX size={theme.icon.size.sm} />
)}
<StyledEditableBooleanFieldValue>
{internalValue ? 'True' : 'False'}
</StyledEditableBooleanFieldValue>
</StyledEditableBooleanFieldContainer>
);
}

View File

@ -1,7 +1,7 @@
import { ChangeEvent } from 'react'; import { ChangeEvent } from 'react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { StyledInput } from '@/ui/table/editable-cell/type/components/TextCellEdit'; import { StyledInput } from '@/ui/input/components/TextInput';
import { ComputeNodeDimensionsEffect } from '@/ui/utilities/dimensions/components/ComputeNodeDimensionsEffect'; import { ComputeNodeDimensionsEffect } from '@/ui/utilities/dimensions/components/ComputeNodeDimensionsEffect';
export type DoubleTextInputEditProps = { export type DoubleTextInputEditProps = {

View File

@ -1,8 +1,8 @@
import { useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import PhoneInput, { isPossiblePhoneNumber } from 'react-phone-number-input'; import ReactPhoneNumberInput from 'react-phone-number-input';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useRegisterCloseCellHandlers } from '../../hooks/useRegisterCloseCellHandlers'; import { useRegisterInputEvents } from '../hooks/useRegisterInputEvents';
import 'react-phone-number-input/style.css'; import 'react-phone-number-input/style.css';
@ -16,14 +16,7 @@ const StyledContainer = styled.div`
justify-content: center; justify-content: center;
`; `;
export type PhoneCellEditProps = { const StyledCustomPhoneInput = styled(ReactPhoneNumberInput)`
placeholder?: string;
autoFocus?: boolean;
value: string;
onSubmit: (newText: string) => void;
};
const StyledCustomPhoneInput = styled(PhoneInput)`
--PhoneInput-color--focus: transparent; --PhoneInput-color--focus: transparent;
--PhoneInputCountryFlag-borderColor--focus: transparent; --PhoneInputCountryFlag-borderColor--focus: transparent;
--PhoneInputCountrySelect-marginRight: ${({ theme }) => theme.spacing(2)}; --PhoneInputCountrySelect-marginRight: ${({ theme }) => theme.spacing(2)};
@ -75,25 +68,46 @@ const StyledCustomPhoneInput = styled(PhoneInput)`
} }
`; `;
export function PhoneCellEdit({ export type PhoneCellEditProps = {
placeholder?: string;
autoFocus?: boolean;
value: string;
onEnter: (newText: string) => void;
onEscape: (newText: string) => void;
onTab?: (newText: string) => void;
onShiftTab?: (newText: string) => void;
onClickOutside: (event: MouseEvent | TouchEvent, inputValue: string) => void;
hotkeyScope: string;
};
export function PhoneInput({
autoFocus, autoFocus,
value, value,
onSubmit, onEnter,
onEscape,
onTab,
onShiftTab,
onClickOutside,
hotkeyScope,
}: PhoneCellEditProps) { }: PhoneCellEditProps) {
const [internalValue, setInternalValue] = useState<string | undefined>(value); const [internalValue, setInternalValue] = useState<string | undefined>(value);
const wrapperRef = useRef<HTMLDivElement | null>(null); const wrapperRef = useRef<HTMLDivElement | null>(null);
function handleSubmit() { useEffect(() => {
if ( setInternalValue(value);
internalValue === undefined || }, [value]);
isPossiblePhoneNumber(internalValue ?? '')
) {
onSubmit(internalValue ?? '');
}
}
useRegisterCloseCellHandlers(wrapperRef, handleSubmit); useRegisterInputEvents({
inputRef: wrapperRef,
inputValue: internalValue ?? '',
onEnter,
onEscape,
onClickOutside,
onTab,
onShiftTab,
hotkeyScope,
});
return ( return (
<StyledContainer ref={wrapperRef}> <StyledContainer ref={wrapperRef}>

View File

@ -3,7 +3,7 @@ import styled from '@emotion/styled';
import { textInputStyle } from '@/ui/theme/constants/effects'; import { textInputStyle } from '@/ui/theme/constants/effects';
import { useRegisterCloseCellHandlers } from '../../hooks/useRegisterCloseCellHandlers'; import { useRegisterInputEvents } from '../hooks/useRegisterInputEvents';
export const StyledInput = styled.input` export const StyledInput = styled.input`
margin: 0; margin: 0;
@ -15,27 +15,29 @@ type OwnProps = {
placeholder?: string; placeholder?: string;
autoFocus?: boolean; autoFocus?: boolean;
value: string; value: string;
onSubmit: (newText: string) => void; onEnter: (newText: string) => void;
onEscape: (newText: string) => void;
onTab?: (newText: string) => void;
onShiftTab?: (newText: string) => void;
onClickOutside: (event: MouseEvent | TouchEvent, inputValue: string) => void;
hotkeyScope: string;
}; };
export function TextCellEdit({ export function TextInput({
placeholder, placeholder,
autoFocus, autoFocus,
value, value,
onSubmit, hotkeyScope,
onEnter,
onEscape,
onTab,
onShiftTab,
onClickOutside,
}: OwnProps) { }: OwnProps) {
const [internalText, setInternalText] = useState(value); const [internalText, setInternalText] = useState(value);
const wrapperRef = useRef(null); const wrapperRef = useRef(null);
function handleSubmit() {
onSubmit(internalText);
}
function handleCancel() {
setInternalText(value);
}
function handleChange(event: ChangeEvent<HTMLInputElement>) { function handleChange(event: ChangeEvent<HTMLInputElement>) {
setInternalText(event.target.value); setInternalText(event.target.value);
} }
@ -44,7 +46,16 @@ export function TextCellEdit({
setInternalText(value); setInternalText(value);
}, [value]); }, [value]);
useRegisterCloseCellHandlers(wrapperRef, handleSubmit, handleCancel); useRegisterInputEvents({
inputRef: wrapperRef,
inputValue: internalText,
onEnter,
onEscape,
onClickOutside,
onTab,
onShiftTab,
hotkeyScope,
});
return ( return (
<StyledInput <StyledInput

View File

@ -0,0 +1,67 @@
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { isDefined } from '~/utils/isDefined';
export function useRegisterInputEvents<T>({
inputRef,
inputValue,
onEscape,
onEnter,
onTab,
onShiftTab,
onClickOutside,
hotkeyScope,
}: {
inputRef: React.RefObject<any>;
inputValue: T;
onEscape: (inputValue: T) => void;
onEnter: (inputValue: T) => void;
onTab?: (inputValue: T) => void;
onShiftTab?: (inputValue: T) => void;
onClickOutside?: (event: MouseEvent | TouchEvent, inputValue: T) => void;
hotkeyScope: string;
}) {
useListenClickOutside({
refs: [inputRef],
callback: (event) => {
onClickOutside?.(event, inputValue);
},
enabled: isDefined(onClickOutside),
});
useScopedHotkeys(
'enter',
() => {
onEnter?.(inputValue);
},
hotkeyScope,
[onEnter, inputValue],
);
useScopedHotkeys(
'esc',
() => {
onEscape?.(inputValue);
},
hotkeyScope,
[onEscape, inputValue],
);
useScopedHotkeys(
'tab',
() => {
onTab?.(inputValue);
},
hotkeyScope,
[onTab, inputValue],
);
useScopedHotkeys(
'shift+tab',
() => {
onShiftTab?.(inputValue);
},
hotkeyScope,
[onShiftTab, inputValue],
);
}

View File

@ -1,16 +0,0 @@
import styled from '@emotion/styled';
const StyledTextInputDisplay = styled.div`
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
`;
export type TextInputDisplayProps = {
children: React.ReactNode;
};
export function TextInputDisplay({ children }: TextInputDisplayProps) {
return <StyledTextInputDisplay>{children}</StyledTextInputDisplay>;
}

View File

@ -197,4 +197,4 @@ function TextInputComponent(
); );
} }
export const TextInput = forwardRef(TextInputComponent); export const TextInputSettings = forwardRef(TextInputComponent);

View File

@ -6,28 +6,28 @@ import { userEvent, within } from '@storybook/testing-library';
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
import { TextInput } from '../TextInput'; import { TextInputSettings } from '../TextInputSettings';
const changeJestFn = jest.fn(); const changeJestFn = jest.fn();
const meta: Meta<typeof TextInput> = { const meta: Meta<typeof TextInputSettings> = {
title: 'UI/Input/TextInput', title: 'UI/Input/TextInput',
component: TextInput, component: TextInputSettings,
decorators: [ComponentDecorator], decorators: [ComponentDecorator],
args: { value: '', onChange: changeJestFn, placeholder: 'Placeholder' }, args: { value: '', onChange: changeJestFn, placeholder: 'Placeholder' },
}; };
export default meta; export default meta;
type Story = StoryObj<typeof TextInput>; type Story = StoryObj<typeof TextInputSettings>;
function FakeTextInput({ function FakeTextInput({
onChange, onChange,
value: initialValue, value: initialValue,
...props ...props
}: React.ComponentProps<typeof TextInput>) { }: React.ComponentProps<typeof TextInputSettings>) {
const [value, setValue] = useState(initialValue); const [value, setValue] = useState(initialValue);
return ( return (
<TextInput <TextInputSettings
{...props} {...props}
value={value} value={value}
onChange={(text) => { onChange={(text) => {

View File

@ -1,7 +1,7 @@
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { IconComponent } from '@/ui/icon/types/IconComponent'; import { IconComponent } from '@/ui/icon/types/IconComponent';
import { Checkbox } from '@/ui/input/checkbox/components/Checkbox'; import { Checkbox } from '@/ui/input/components/Checkbox';
import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent'; import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent';
import { StyledMenuItemBase } from '../internals/components/StyledMenuItemBase'; import { StyledMenuItemBase } from '../internals/components/StyledMenuItemBase';

View File

@ -1,7 +1,7 @@
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { Checkbox } from '@/ui/input/checkbox/components/Checkbox'; import { Checkbox } from '@/ui/input/components/Checkbox';
import { import {
StyledMenuItemBase, StyledMenuItemBase,

View File

@ -1,5 +1,5 @@
import { IconComponent } from '@/ui/icon/types/IconComponent'; import { IconComponent } from '@/ui/icon/types/IconComponent';
import { Toggle } from '@/ui/input/toggle/components/Toggle'; import { Toggle } from '@/ui/input/components/Toggle';
import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent'; import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent';
import { StyledMenuItemBase } from '../internals/components/StyledMenuItemBase'; import { StyledMenuItemBase } from '../internals/components/StyledMenuItemBase';

View File

@ -4,7 +4,7 @@ import { AnimatePresence, LayoutGroup } from 'framer-motion';
import debounce from 'lodash.debounce'; import debounce from 'lodash.debounce';
import { Button } from '@/ui/button/components/Button'; import { Button } from '@/ui/button/components/Button';
import { TextInput } from '@/ui/input/text/components/TextInput'; import { TextInputSettings } from '@/ui/input/text/components/TextInputSettings';
import { Modal } from '@/ui/modal/components/Modal'; import { Modal } from '@/ui/modal/components/Modal';
import { import {
Section, Section,
@ -99,7 +99,7 @@ export function ConfirmationModal({
</Section> </Section>
{confirmationValue && ( {confirmationValue && (
<Section> <Section>
<TextInput <TextInputSettings
value={inputConfirmationValue} value={inputConfirmationValue}
onChange={handleInputConfimrationValueChange} onChange={handleInputConfimrationValueChange}
placeholder={confirmationPlaceholder} placeholder={confirmationPlaceholder}

View File

@ -3,7 +3,7 @@ import styled from '@emotion/styled';
import { useSetRecoilState } from 'recoil'; import { useSetRecoilState } from 'recoil';
import { actionBarOpenState } from '@/ui/action-bar/states/actionBarIsOpenState'; 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'; import { useCurrentRowSelected } from '../hooks/useCurrentRowSelected';

View File

@ -1,6 +1,6 @@
import styled from '@emotion/styled'; 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'; import { useSelectAllRows } from '../hooks/useSelectAllRows';

View File

@ -1,7 +1,7 @@
import { useEditableCell } from '../hooks/useEditableCell'; import { useEditableCell } from '../hooks/useEditableCell';
import { useSetSoftFocusOnCurrentCell } from '../hooks/useSetSoftFocusOnCurrentCell'; import { useSetSoftFocusOnCurrentCell } from '../hooks/useSetSoftFocusOnCurrentCell';
import { EditableCellDisplayContainer } from './EditableCellContainer'; import { EditableCellDisplayContainer } from './EditableCellDisplayContainer';
export function EditableCellDisplayMode({ export function EditableCellDisplayMode({
children, children,

View File

@ -6,7 +6,7 @@ import { isNonTextWritingKey } from '@/ui/utilities/hotkey/utils/isNonTextWritin
import { TableHotkeyScope } from '../../types/TableHotkeyScope'; import { TableHotkeyScope } from '../../types/TableHotkeyScope';
import { useEditableCell } from '../hooks/useEditableCell'; import { useEditableCell } from '../hooks/useEditableCell';
import { EditableCellDisplayContainer } from './EditableCellContainer'; import { EditableCellDisplayContainer } from './EditableCellDisplayContainer';
type OwnProps = PropsWithChildren<unknown>; type OwnProps = PropsWithChildren<unknown>;

View File

@ -2,7 +2,7 @@ import { useRef } from 'react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { Key } from 'ts-key-enum'; 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 { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';

View File

@ -6,11 +6,10 @@ import { useMoveSoftFocus } from '@/ui/table/hooks/useMoveSoftFocus';
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope'; import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { StyledInput } from '../../../../input/components/TextInput';
import { useEditableCell } from '../../hooks/useEditableCell'; import { useEditableCell } from '../../hooks/useEditableCell';
import { useRegisterCloseCellHandlers } from '../../hooks/useRegisterCloseCellHandlers'; import { useRegisterCloseCellHandlers } from '../../hooks/useRegisterCloseCellHandlers';
import { StyledInput } from './TextCellEdit';
type OwnProps = { type OwnProps = {
firstValue: string; firstValue: string;
secondValue: string; secondValue: string;

View File

@ -2,13 +2,13 @@ import styled from '@emotion/styled';
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import type { ViewFieldBooleanMetadata } from '@/ui/editable-field/types/ViewField'; 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 { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField'; import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector'; import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
import type { ColumnDefinition } from '../../../types/ColumnDefinition'; import type { ColumnDefinition } from '../../../types/ColumnDefinition';
import { EditableCellDisplayContainer } from '../../components/EditableCellContainer'; import { EditableCellDisplayContainer } from '../../components/EditableCellDisplayContainer';
type OwnProps = { type OwnProps = {
columnDefinition: ColumnDefinition<ViewFieldBooleanMetadata>; columnDefinition: ColumnDefinition<ViewFieldBooleanMetadata>;
@ -26,14 +26,6 @@ const StyledCellBaseContainer = styled.div`
width: 100%; 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) { export function GenericEditableBooleanCell({ columnDefinition }: OwnProps) {
const currentRowEntityId = useCurrentRowEntityId(); const currentRowEntityId = useCurrentRowEntityId();
@ -48,6 +40,7 @@ export function GenericEditableBooleanCell({ columnDefinition }: OwnProps) {
function handleClick() { function handleClick() {
const newValue = !fieldValue; const newValue = !fieldValue;
try { try {
setFieldValue(newValue); setFieldValue(newValue);
@ -64,11 +57,7 @@ export function GenericEditableBooleanCell({ columnDefinition }: OwnProps) {
return ( return (
<StyledCellBaseContainer> <StyledCellBaseContainer>
<EditableCellDisplayContainer onClick={handleClick}> <EditableCellDisplayContainer onClick={handleClick}>
{fieldValue ? <IconCheck /> : <IconX />} <BooleanInput value={fieldValue} />
<StyledCellBooleancontainer>
{fieldValue !== undefined &&
capitalizeFirstLetter(fieldValue.toString())}
</StyledCellBooleancontainer>
</EditableCellDisplayContainer> </EditableCellDisplayContainer>
</StyledCellBaseContainer> </StyledCellBaseContainer>
); );

View File

@ -1,14 +1,15 @@
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import type { ViewFieldChipMetadata } from '@/ui/editable-field/types/ViewField'; import type { ViewFieldChipMetadata } from '@/ui/editable-field/types/ViewField';
import { useCellInputEventHandlers } from '@/ui/table/hooks/useCellInputEventHandlers';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId'; import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField'; import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector'; 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 type { ColumnDefinition } from '../../../types/ColumnDefinition';
import { TextCellEdit } from './TextCellEdit';
type OwnProps = { type OwnProps = {
columnDefinition: ColumnDefinition<ViewFieldChipMetadata>; columnDefinition: ColumnDefinition<ViewFieldChipMetadata>;
}; };
@ -38,12 +39,27 @@ export function GenericEditableChipCellEditMode({
} }
} }
const {
handleEnter,
handleEscape,
handleTab,
handleShiftTab,
handleClickOutside,
} = useCellInputEventHandlers({
onSubmit: handleSubmit,
});
return ( return (
<TextCellEdit <TextInput
placeholder={columnDefinition.metadata.placeHolder ?? ''} placeholder={columnDefinition.metadata.placeHolder ?? ''}
autoFocus autoFocus
value={fieldValue ?? ''} value={fieldValue ?? ''}
onSubmit={handleSubmit} onClickOutside={handleClickOutside}
onEnter={handleEnter}
onEscape={handleEscape}
onTab={handleTab}
onShiftTab={handleShiftTab}
hotkeyScope={TableHotkeyScope.CellEditMode}
/> />
); );
} }

View File

@ -1,7 +1,7 @@
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import type { ViewFieldDateMetadata } from '@/ui/editable-field/types/ViewField'; 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 { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId'; import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector'; import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';

View File

@ -1,7 +1,7 @@
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { TextDisplay } from '@/ui/content-display/components/TextDisplay';
import type { ViewFieldDoubleTextMetadata } from '@/ui/editable-field/types/ViewField'; 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 { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId'; import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector'; import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
@ -40,7 +40,7 @@ export function GenericEditableDoubleTextCell({ columnDefinition }: OwnProps) {
columnDefinition={columnDefinition} columnDefinition={columnDefinition}
/> />
} }
nonEditModeContent={<TextInputDisplay>{displayName}</TextInputDisplay>} nonEditModeContent={<TextDisplay text={displayName} />}
></EditableCell> ></EditableCell>
); );
} }

View File

@ -1,7 +1,7 @@
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import type { ViewFieldEmailMetadata } from '@/ui/editable-field/types/ViewField'; 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 { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId'; import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector'; import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';

View File

@ -1,14 +1,15 @@
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import type { ViewFieldEmailMetadata } from '@/ui/editable-field/types/ViewField'; import type { ViewFieldEmailMetadata } from '@/ui/editable-field/types/ViewField';
import { useCellInputEventHandlers } from '@/ui/table/hooks/useCellInputEventHandlers';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId'; import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField'; import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector'; 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 type { ColumnDefinition } from '../../../types/ColumnDefinition';
import { TextCellEdit } from './TextCellEdit';
type OwnProps = { type OwnProps = {
columnDefinition: ColumnDefinition<ViewFieldEmailMetadata>; columnDefinition: ColumnDefinition<ViewFieldEmailMetadata>;
}; };
@ -38,12 +39,27 @@ export function GenericEditableEmailCellEditMode({
} }
} }
const {
handleEnter,
handleEscape,
handleTab,
handleShiftTab,
handleClickOutside,
} = useCellInputEventHandlers({
onSubmit: handleSubmit,
});
return ( return (
<TextCellEdit <TextInput
placeholder={columnDefinition.metadata.placeHolder ?? ''} placeholder={columnDefinition.metadata.placeHolder ?? ''}
autoFocus autoFocus
value={fieldValue ?? ''} value={fieldValue ?? ''}
onSubmit={handleSubmit} onClickOutside={handleClickOutside}
onEnter={handleEnter}
onEscape={handleEscape}
onTab={handleTab}
onShiftTab={handleShiftTab}
hotkeyScope={TableHotkeyScope.CellEditMode}
/> />
); );
} }

View File

@ -1,14 +1,15 @@
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import type { ViewFieldMoneyMetadata } from '@/ui/editable-field/types/ViewField'; import type { ViewFieldMoneyMetadata } from '@/ui/editable-field/types/ViewField';
import { useCellInputEventHandlers } from '@/ui/table/hooks/useCellInputEventHandlers';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId'; import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField'; import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector'; 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 type { ColumnDefinition } from '../../../types/ColumnDefinition';
import { TextCellEdit } from './TextCellEdit';
type OwnProps = { type OwnProps = {
columnDefinition: ColumnDefinition<ViewFieldMoneyMetadata>; columnDefinition: ColumnDefinition<ViewFieldMoneyMetadata>;
}; };
@ -53,7 +54,26 @@ export function GenericEditableMoneyCellEditMode({
} }
} }
const {
handleEnter,
handleEscape,
handleTab,
handleShiftTab,
handleClickOutside,
} = useCellInputEventHandlers({
onSubmit: handleSubmit,
});
return ( return (
<TextCellEdit autoFocus value={fieldValue ?? ''} onSubmit={handleSubmit} /> <TextInput
autoFocus
value={fieldValue ?? ''}
onClickOutside={handleClickOutside}
onEnter={handleEnter}
onEscape={handleEscape}
onTab={handleTab}
onShiftTab={handleShiftTab}
hotkeyScope={TableHotkeyScope.CellEditMode}
/>
); );
} }

View File

@ -1,18 +1,19 @@
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import type { ViewFieldNumberMetadata } from '@/ui/editable-field/types/ViewField'; import type { ViewFieldNumberMetadata } from '@/ui/editable-field/types/ViewField';
import { useCellInputEventHandlers } from '@/ui/table/hooks/useCellInputEventHandlers';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId'; import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField'; import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector'; import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
import { import {
canBeCastAsPositiveIntegerOrNull, canBeCastAsPositiveIntegerOrNull,
castAsPositiveIntegerOrNull, castAsPositiveIntegerOrNull,
} from '~/utils/cast-as-positive-integer-or-null'; } from '~/utils/cast-as-positive-integer-or-null';
import { TextInput } from '../../../../input/components/TextInput';
import type { ColumnDefinition } from '../../../types/ColumnDefinition'; import type { ColumnDefinition } from '../../../types/ColumnDefinition';
import { TextCellEdit } from './TextCellEdit';
type OwnProps = { type OwnProps = {
columnDefinition: ColumnDefinition<ViewFieldNumberMetadata>; columnDefinition: ColumnDefinition<ViewFieldNumberMetadata>;
}; };
@ -74,7 +75,26 @@ export function GenericEditableNumberCellEditMode({
} }
} }
const {
handleEnter,
handleEscape,
handleTab,
handleShiftTab,
handleClickOutside,
} = useCellInputEventHandlers({
onSubmit: handleSubmit,
});
return ( return (
<TextCellEdit autoFocus value={fieldValue ?? ''} onSubmit={handleSubmit} /> <TextInput
autoFocus
value={fieldValue ?? ''}
onClickOutside={handleClickOutside}
onEnter={handleEnter}
onEscape={handleEscape}
onTab={handleTab}
onShiftTab={handleShiftTab}
hotkeyScope={TableHotkeyScope.CellEditMode}
/>
); );
} }

View File

@ -1,7 +1,7 @@
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import type { ViewFieldPhoneMetadata } from '@/ui/editable-field/types/ViewField'; import { PhoneDisplay } from '@/ui/content-display/components/PhoneDisplay';
import { PhoneInputDisplay } from '@/ui/input/phone/components/PhoneInputDisplay'; import { ViewFieldPhoneMetadata } from '@/ui/editable-field/types/ViewField';
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell'; import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId'; import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector'; import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
@ -35,7 +35,7 @@ export function GenericEditablePhoneCell({
editModeContent={ editModeContent={
<GenericEditablePhoneCellEditMode columnDefinition={columnDefinition} /> <GenericEditablePhoneCellEditMode columnDefinition={columnDefinition} />
} }
nonEditModeContent={<PhoneInputDisplay value={fieldValue} />} nonEditModeContent={<PhoneDisplay value={fieldValue} />}
></EditableCell> ></EditableCell>
); );
} }

View File

@ -1,14 +1,16 @@
import { isPossiblePhoneNumber } from 'libphonenumber-js';
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import type { ViewFieldPhoneMetadata } from '@/ui/editable-field/types/ViewField'; import type { ViewFieldPhoneMetadata } from '@/ui/editable-field/types/ViewField';
import { useCellInputEventHandlers } from '@/ui/table/hooks/useCellInputEventHandlers';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId'; import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField'; import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector'; 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 type { ColumnDefinition } from '../../../types/ColumnDefinition';
import { PhoneCellEdit } from './PhoneCellEdit';
type OwnProps = { type OwnProps = {
columnDefinition: ColumnDefinition<ViewFieldPhoneMetadata>; columnDefinition: ColumnDefinition<ViewFieldPhoneMetadata>;
}; };
@ -28,22 +30,39 @@ export function GenericEditablePhoneCellEditMode({
const updateField = useUpdateEntityField(); const updateField = useUpdateEntityField();
function handleSubmit(newText: string) { function handleSubmit(newValue: string) {
if (newText === fieldValue) return; if (!isPossiblePhoneNumber(newValue)) return;
setFieldValue(newText); if (newValue === fieldValue) return;
setFieldValue(newValue);
if (currentRowEntityId && updateField) { if (currentRowEntityId && updateField) {
updateField(currentRowEntityId, columnDefinition, newText); updateField(currentRowEntityId, columnDefinition, newValue);
} }
} }
const {
handleEnter,
handleEscape,
handleTab,
handleShiftTab,
handleClickOutside,
} = useCellInputEventHandlers({
onSubmit: handleSubmit,
});
return ( return (
<PhoneCellEdit <PhoneInput
placeholder={columnDefinition.metadata.placeHolder ?? ''} placeholder={columnDefinition.metadata.placeHolder ?? ''}
autoFocus autoFocus
value={fieldValue ?? ''} value={fieldValue ?? ''}
onSubmit={handleSubmit} onClickOutside={handleClickOutside}
onEnter={handleEnter}
onEscape={handleEscape}
onShiftTab={handleShiftTab}
onTab={handleTab}
hotkeyScope={TableHotkeyScope.CellEditMode}
/> />
); );
} }

View File

@ -1,7 +1,7 @@
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { TextDisplay } from '@/ui/content-display/components/TextDisplay';
import type { ViewFieldTextMetadata } from '@/ui/editable-field/types/ViewField'; 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 { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId'; import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector'; import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
@ -34,7 +34,7 @@ export function GenericEditableTextCell({
editModeContent={ editModeContent={
<GenericEditableTextCellEditMode columnDefinition={columnDefinition} /> <GenericEditableTextCellEditMode columnDefinition={columnDefinition} />
} }
nonEditModeContent={<TextInputDisplay>{fieldValue}</TextInputDisplay>} nonEditModeContent={<TextDisplay text={fieldValue} />}
></EditableCell> ></EditableCell>
); );
} }

View File

@ -1,14 +1,15 @@
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import type { ViewFieldTextMetadata } from '@/ui/editable-field/types/ViewField'; import type { ViewFieldTextMetadata } from '@/ui/editable-field/types/ViewField';
import { useCellInputEventHandlers } from '@/ui/table/hooks/useCellInputEventHandlers';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId'; import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField'; import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector'; 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 type { ColumnDefinition } from '../../../types/ColumnDefinition';
import { TextCellEdit } from './TextCellEdit';
type OwnProps = { type OwnProps = {
columnDefinition: ColumnDefinition<ViewFieldTextMetadata>; columnDefinition: ColumnDefinition<ViewFieldTextMetadata>;
}; };
@ -38,12 +39,27 @@ export function GenericEditableTextCellEditMode({
} }
} }
const {
handleEnter,
handleEscape,
handleTab,
handleShiftTab,
handleClickOutside,
} = useCellInputEventHandlers({
onSubmit: handleSubmit,
});
return ( return (
<TextCellEdit <TextInput
placeholder={columnDefinition.metadata.placeHolder ?? ''} placeholder={columnDefinition.metadata.placeHolder ?? ''}
autoFocus autoFocus
value={fieldValue ?? ''} value={fieldValue ?? ''}
onSubmit={handleSubmit} onClickOutside={handleClickOutside}
onEnter={handleEnter}
onEscape={handleEscape}
onTab={handleTab}
onShiftTab={handleShiftTab}
hotkeyScope={TableHotkeyScope.CellEditMode}
/> />
); );
} }

View File

@ -1,7 +1,7 @@
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { URLDisplay } from '@/ui/content-display/components/URLDisplay';
import type { ViewFieldURLMetadata } from '@/ui/editable-field/types/ViewField'; 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 { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId'; import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector'; import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
@ -36,9 +36,7 @@ export function GenericEditableURLCell({
editModeContent={ editModeContent={
<GenericEditableURLCellEditMode columnDefinition={columnDefinition} /> <GenericEditableURLCellEditMode columnDefinition={columnDefinition} />
} }
nonEditModeContent={ nonEditModeContent={<URLDisplay value={sanitizeURL(fieldValue)} />}
<InplaceInputURLDisplayMode value={sanitizeURL(fieldValue)} />
}
></EditableCell> ></EditableCell>
); );
} }

View File

@ -1,15 +1,16 @@
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import type { ViewFieldURLMetadata } from '@/ui/editable-field/types/ViewField'; import type { ViewFieldURLMetadata } from '@/ui/editable-field/types/ViewField';
import { useCellInputEventHandlers } from '@/ui/table/hooks/useCellInputEventHandlers';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId'; import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField'; import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector'; import { tableEntityFieldFamilySelector } from '@/ui/table/states/selectors/tableEntityFieldFamilySelector';
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
import { isURL } from '~/utils/is-url'; import { isURL } from '~/utils/is-url';
import { TextInput } from '../../../../input/components/TextInput';
import type { ColumnDefinition } from '../../../types/ColumnDefinition'; import type { ColumnDefinition } from '../../../types/ColumnDefinition';
import { TextCellEdit } from './TextCellEdit';
type OwnProps = { type OwnProps = {
columnDefinition: ColumnDefinition<ViewFieldURLMetadata>; columnDefinition: ColumnDefinition<ViewFieldURLMetadata>;
}; };
@ -39,12 +40,27 @@ export function GenericEditableURLCellEditMode({ columnDefinition }: OwnProps) {
} }
} }
const {
handleEnter,
handleEscape,
handleTab,
handleShiftTab,
handleClickOutside,
} = useCellInputEventHandlers({
onSubmit: handleSubmit,
});
return ( return (
<TextCellEdit <TextInput
placeholder={columnDefinition.metadata.placeHolder ?? ''} placeholder={columnDefinition.metadata.placeHolder ?? ''}
autoFocus autoFocus
value={fieldValue ?? ''} value={fieldValue ?? ''}
onSubmit={handleSubmit} onClickOutside={handleClickOutside}
onEnter={handleEnter}
onEscape={handleEscape}
onTab={handleTab}
onShiftTab={handleShiftTab}
hotkeyScope={TableHotkeyScope.CellEditMode}
/> />
); );
} }

View File

@ -1,12 +1,12 @@
import type { Meta, StoryObj } from '@storybook/react'; 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 { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator'; import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator';
const meta: Meta<typeof PhoneCellEdit> = { const meta: Meta<typeof PhoneInput> = {
title: 'UI/Table/EditableCell/PhoneCellEdit', title: 'UI/Table/EditableCell/PhoneCellEdit',
component: PhoneCellEdit, component: PhoneInput,
decorators: [ComponentWithRecoilScopeDecorator], decorators: [ComponentWithRecoilScopeDecorator],
args: { args: {
value: '+33714446494', value: '+33714446494',
@ -18,6 +18,6 @@ const meta: Meta<typeof PhoneCellEdit> = {
}; };
export default meta; export default meta;
type Story = StoryObj<typeof PhoneCellEdit>; type Story = StoryObj<typeof PhoneInput>;
export const Default: Story = {}; export const Default: Story = {};

View File

@ -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();
},
};
}

View File

@ -9,10 +9,12 @@ export function useListenClickOutside<T extends Element>({
refs, refs,
callback, callback,
mode = ClickOutsideMode.dom, mode = ClickOutsideMode.dom,
enabled = true,
}: { }: {
refs: Array<React.RefObject<T>>; refs: Array<React.RefObject<T>>;
callback: (event: MouseEvent | TouchEvent) => void; callback: (event: MouseEvent | TouchEvent) => void;
mode?: ClickOutsideMode; mode?: ClickOutsideMode;
enabled?: boolean;
}) { }) {
useEffect(() => { useEffect(() => {
function handleClickOutside(event: MouseEvent | TouchEvent) { function handleClickOutside(event: MouseEvent | TouchEvent) {
@ -61,20 +63,22 @@ export function useListenClickOutside<T extends Element>({
} }
} }
document.addEventListener('click', handleClickOutside, { capture: true }); if (enabled) {
document.addEventListener('touchend', handleClickOutside, { document.addEventListener('click', handleClickOutside, { capture: true });
capture: true, document.addEventListener('touchend', handleClickOutside, {
}); capture: true,
});
return () => { return () => {
document.removeEventListener('click', handleClickOutside, { document.removeEventListener('click', handleClickOutside, {
capture: true, capture: true,
}); });
document.removeEventListener('touchend', handleClickOutside, { document.removeEventListener('touchend', handleClickOutside, {
capture: true, capture: true,
}); });
}; };
}, [refs, callback, mode]); }
}, [refs, callback, mode, enabled]);
} }
export const useListenClickOutsideByClassName = ({ export const useListenClickOutsideByClassName = ({
classNames, classNames,

View File

@ -1,7 +1,7 @@
import { Context } from 'react'; import { Context } from 'react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import DatePicker from '@/ui/input/date/components/DatePicker'; import DatePicker from '@/ui/input/components/DatePicker';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { useUpsertFilter } from '@/ui/view-bar/hooks/useUpsertFilter'; import { useUpsertFilter } from '@/ui/view-bar/hooks/useUpsertFilter';
import { filterDefinitionUsedInDropdownScopedState } from '@/ui/view-bar/states/filterDefinitionUsedInDropdownScopedState'; import { filterDefinitionUsedInDropdownScopedState } from '@/ui/view-bar/states/filterDefinitionUsedInDropdownScopedState';

View File

@ -3,7 +3,7 @@ import styled from '@emotion/styled';
import { Button } from '@/ui/button/components/Button'; import { Button } from '@/ui/button/components/Button';
import { IconCopy, IconLink } from '@/ui/icon'; import { IconCopy, IconLink } from '@/ui/icon';
import { TextInput } from '@/ui/input/text/components/TextInput'; import { TextInputSettings } from '@/ui/input/text/components/TextInputSettings';
import { useSnackBar } from '@/ui/snack-bar/hooks/useSnackBar'; import { useSnackBar } from '@/ui/snack-bar/hooks/useSnackBar';
const StyledContainer = styled.div` const StyledContainer = styled.div`
@ -29,7 +29,7 @@ export function WorkspaceInviteLink({ inviteLink }: OwnProps) {
return ( return (
<StyledContainer> <StyledContainer>
<StyledLinkContainer> <StyledLinkContainer>
<TextInput value={inviteLink} disabled fullWidth /> <TextInputSettings value={inviteLink} disabled fullWidth />
</StyledLinkContainer> </StyledLinkContainer>
<Button <Button
Icon={IconLink} Icon={IconLink}

View File

@ -14,7 +14,7 @@ import { currentUserState } from '@/auth/states/currentUserState';
import { ProfilePictureUploader } from '@/settings/profile/components/ProfilePictureUploader'; import { ProfilePictureUploader } from '@/settings/profile/components/ProfilePictureUploader';
import { PageHotkeyScope } from '@/types/PageHotkeyScope'; import { PageHotkeyScope } from '@/types/PageHotkeyScope';
import { MainButton } from '@/ui/button/components/MainButton'; import { MainButton } from '@/ui/button/components/MainButton';
import { TextInput } from '@/ui/input/text/components/TextInput'; import { TextInputSettings } from '@/ui/input/text/components/TextInputSettings';
import { useSnackBar } from '@/ui/snack-bar/hooks/useSnackBar'; import { useSnackBar } from '@/ui/snack-bar/hooks/useSnackBar';
import { H2Title } from '@/ui/typography/components/H2Title'; import { H2Title } from '@/ui/typography/components/H2Title';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
@ -145,7 +145,7 @@ export function CreateProfile() {
field: { onChange, onBlur, value }, field: { onChange, onBlur, value },
fieldState: { error }, fieldState: { error },
}) => ( }) => (
<TextInput <TextInputSettings
autoFocus autoFocus
label="First Name" label="First Name"
value={value} value={value}
@ -165,7 +165,7 @@ export function CreateProfile() {
field: { onChange, onBlur, value }, field: { onChange, onBlur, value },
fieldState: { error }, fieldState: { error },
}) => ( }) => (
<TextInput <TextInputSettings
label="Last Name" label="Last Name"
value={value} value={value}
onBlur={onBlur} onBlur={onBlur}

View File

@ -11,7 +11,7 @@ import { Title } from '@/auth/components/Title';
import { WorkspaceLogoUploader } from '@/settings/workspace/components/WorkspaceLogoUploader'; import { WorkspaceLogoUploader } from '@/settings/workspace/components/WorkspaceLogoUploader';
import { PageHotkeyScope } from '@/types/PageHotkeyScope'; import { PageHotkeyScope } from '@/types/PageHotkeyScope';
import { MainButton } from '@/ui/button/components/MainButton'; import { MainButton } from '@/ui/button/components/MainButton';
import { TextInput } from '@/ui/input/text/components/TextInput'; import { TextInputSettings } from '@/ui/input/text/components/TextInputSettings';
import { useSnackBar } from '@/ui/snack-bar/hooks/useSnackBar'; import { useSnackBar } from '@/ui/snack-bar/hooks/useSnackBar';
import { H2Title } from '@/ui/typography/components/H2Title'; import { H2Title } from '@/ui/typography/components/H2Title';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
@ -122,7 +122,7 @@ export function CreateWorkspace() {
field: { onChange, onBlur, value }, field: { onChange, onBlur, value },
fieldState: { error }, fieldState: { error },
}) => ( }) => (
<TextInput <TextInputSettings
autoFocus autoFocus
value={value} value={value}
placeholder="Apple" placeholder="Apple"