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:
@ -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';
|
||||||
|
|||||||
@ -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<{
|
||||||
|
|||||||
@ -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';
|
||||||
|
|
||||||
|
|||||||
@ -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';
|
||||||
|
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -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')}
|
||||||
|
|||||||
@ -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';
|
||||||
|
|
||||||
|
|||||||
@ -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';
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
@ -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';
|
||||||
|
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
@ -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';
|
||||||
|
|
||||||
|
|||||||
@ -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)`
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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 });
|
||||||
|
|||||||
@ -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()}
|
||||||
@ -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>;
|
||||||
|
}
|
||||||
@ -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();
|
||||||
}
|
}
|
||||||
@ -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 = {};
|
||||||
@ -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
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
import { RoundedLink } from '@/ui/link/components/RoundedLink';
|
|
||||||
|
|
||||||
export function FieldDisplayURL({ URL }: { URL: string | undefined }) {
|
|
||||||
return <RoundedLink href={URL ? 'https://' + URL : ''}>{URL}</RoundedLink>;
|
|
||||||
}
|
|
||||||
@ -1,9 +1,7 @@
|
|||||||
import { useContext } from 'react';
|
import { 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>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -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>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,28 @@
|
|||||||
|
import { useEditableField } from './useEditableField';
|
||||||
|
|
||||||
|
export function useFieldInputEventHandlers<T>({
|
||||||
|
onSubmit,
|
||||||
|
onCancel,
|
||||||
|
}: {
|
||||||
|
onSubmit?: (newValue: T) => void;
|
||||||
|
onCancel?: () => void;
|
||||||
|
}) {
|
||||||
|
const { closeEditableField, isFieldInEditMode } = useEditableField();
|
||||||
|
|
||||||
|
return {
|
||||||
|
handleClickOutside: (_event: MouseEvent | TouchEvent, newValue: T) => {
|
||||||
|
if (isFieldInEditMode) {
|
||||||
|
onSubmit?.(newValue);
|
||||||
|
closeEditableField();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleEscape: () => {
|
||||||
|
closeEditableField();
|
||||||
|
onCancel?.();
|
||||||
|
},
|
||||||
|
handleEnter: (newValue: T) => {
|
||||||
|
onSubmit?.(newValue);
|
||||||
|
closeEditableField();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import { EditableField } from '@/ui/editable-field/components/EditableField';
|
import { 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';
|
||||||
|
|
||||||
|
|||||||
@ -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';
|
||||||
|
|
||||||
|
|||||||
@ -1,65 +0,0 @@
|
|||||||
import { useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
import { EditableField } from '@/ui/editable-field/components/EditableField';
|
|
||||||
import { FieldRecoilScopeContext } from '@/ui/editable-field/states/recoil-scope-contexts/FieldRecoilScopeContext';
|
|
||||||
import { IconComponent } from '@/ui/icon/types/IconComponent';
|
|
||||||
import { PhoneInputDisplay } from '@/ui/input/phone/components/PhoneInputDisplay';
|
|
||||||
import { TextInputEdit } from '@/ui/input/text/components/TextInputEdit';
|
|
||||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
|
||||||
|
|
||||||
type OwnProps = {
|
|
||||||
Icon?: IconComponent;
|
|
||||||
placeholder?: string;
|
|
||||||
value: string | null | undefined;
|
|
||||||
onSubmit?: (newValue: string) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function PhoneEditableField({
|
|
||||||
Icon,
|
|
||||||
placeholder,
|
|
||||||
value,
|
|
||||||
onSubmit,
|
|
||||||
}: OwnProps) {
|
|
||||||
const [internalValue, setInternalValue] = useState(value);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setInternalValue(value);
|
|
||||||
}, [value]);
|
|
||||||
|
|
||||||
async function handleChange(newValue: string) {
|
|
||||||
setInternalValue(newValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleSubmit() {
|
|
||||||
if (!internalValue) return;
|
|
||||||
|
|
||||||
onSubmit?.(internalValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleCancel() {
|
|
||||||
setInternalValue(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<RecoilScope SpecificContext={FieldRecoilScopeContext}>
|
|
||||||
<EditableField
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
onCancel={handleCancel}
|
|
||||||
IconLabel={Icon}
|
|
||||||
editModeContent={
|
|
||||||
<TextInputEdit
|
|
||||||
placeholder={placeholder ?? ''}
|
|
||||||
autoFocus
|
|
||||||
value={internalValue ?? ''}
|
|
||||||
onChange={(newValue: string) => {
|
|
||||||
handleChange(newValue);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
displayModeContent={<PhoneInputDisplay value={internalValue ?? ''} />}
|
|
||||||
isDisplayModeContentEmpty={!(internalValue !== '')}
|
|
||||||
useEditButton
|
|
||||||
/>
|
|
||||||
</RecoilScope>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
import type { Meta, StoryObj } from '@storybook/react';
|
|
||||||
|
|
||||||
import { IconPhone } from '@/ui/icon';
|
|
||||||
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
|
|
||||||
|
|
||||||
import { PhoneEditableField } from '../PhoneEditableField';
|
|
||||||
|
|
||||||
const meta: Meta<typeof PhoneEditableField> = {
|
|
||||||
title: 'UI/EditableField/PhoneEditableField',
|
|
||||||
component: PhoneEditableField,
|
|
||||||
decorators: [ComponentWithRouterDecorator],
|
|
||||||
argTypes: {
|
|
||||||
Icon: {
|
|
||||||
type: 'boolean',
|
|
||||||
mapping: {
|
|
||||||
true: IconPhone,
|
|
||||||
false: undefined,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
args: {
|
|
||||||
value: '+33714446494',
|
|
||||||
Icon: IconPhone,
|
|
||||||
placeholder: 'Phone',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default meta;
|
|
||||||
type Story = StoryObj<typeof PhoneEditableField>;
|
|
||||||
|
|
||||||
export const Default: Story = {};
|
|
||||||
48
front/src/modules/ui/input/components/BooleanInput.tsx
Normal file
48
front/src/modules/ui/input/components/BooleanInput.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -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 = {
|
||||||
@ -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}>
|
||||||
@ -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
|
||||||
67
front/src/modules/ui/input/hooks/useRegisterInputEvents.ts
Normal file
67
front/src/modules/ui/input/hooks/useRegisterInputEvents.ts
Normal 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],
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -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>;
|
|
||||||
}
|
|
||||||
@ -197,4 +197,4 @@ function TextInputComponent(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TextInput = forwardRef(TextInputComponent);
|
export const TextInputSettings = forwardRef(TextInputComponent);
|
||||||
@ -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) => {
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
@ -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';
|
||||||
|
|
||||||
|
|||||||
@ -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';
|
||||||
|
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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>;
|
||||||
|
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 = {};
|
||||||
|
|||||||
@ -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();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -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,
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user