Refacto/remaining inplace input cells (#531)
* Add inplace date input component * Add inplace phone input component * Add inplace double text input component * Add inplace chip input component * Remove useless styled component * Reduce code through props destructuring
This commit is contained in:
@ -12,20 +12,12 @@ type OwnProps = {
|
||||
editModeVerticalPosition?: 'over' | 'below';
|
||||
};
|
||||
|
||||
export function EditableCell({
|
||||
editModeHorizontalAlign = 'left',
|
||||
editModeVerticalPosition = 'over',
|
||||
editModeContent,
|
||||
nonEditModeContent,
|
||||
}: OwnProps) {
|
||||
export function EditableCell(props: OwnProps) {
|
||||
const setSoftFocusOnCurrentCell = useSetSoftFocusOnCurrentCell();
|
||||
const hasSoftFocus = useIsSoftFocusOnCurrentCell();
|
||||
return (
|
||||
<InplaceInput
|
||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||
editModeVerticalPosition={editModeVerticalPosition}
|
||||
editModeContent={editModeContent}
|
||||
nonEditModeContent={nonEditModeContent}
|
||||
{...props}
|
||||
setSoftFocusOnCurrentInplaceInput={setSoftFocusOnCurrentCell}
|
||||
hasSoftFocus={!!hasSoftFocus}
|
||||
/>
|
||||
|
||||
@ -1,93 +0,0 @@
|
||||
import { ChangeEvent, ComponentType, ReactNode, useRef, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { textInputStyle } from '@/ui/themes/effects';
|
||||
|
||||
import { EditableCell } from '../EditableCell';
|
||||
|
||||
export type EditableChipProps = {
|
||||
placeholder?: string;
|
||||
value: string;
|
||||
picture: string;
|
||||
changeHandler: (updated: string) => void;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
ChipComponent: ComponentType<{
|
||||
name: string;
|
||||
picture: string;
|
||||
isOverlapped?: boolean;
|
||||
}>;
|
||||
commentCount?: number;
|
||||
onCommentClick?: (event: React.MouseEvent<HTMLDivElement>) => void;
|
||||
rightEndContents?: ReactNode[];
|
||||
};
|
||||
|
||||
// TODO: refactor
|
||||
const StyledInplaceInput = styled.input`
|
||||
width: 100%;
|
||||
|
||||
${textInputStyle}
|
||||
`;
|
||||
|
||||
const NoEditModeContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const RightContainer = styled.div`
|
||||
margin-left: ${(props) => props.theme.spacing(1)};
|
||||
`;
|
||||
|
||||
function EditableChip({
|
||||
value,
|
||||
placeholder,
|
||||
changeHandler,
|
||||
picture,
|
||||
editModeHorizontalAlign,
|
||||
ChipComponent,
|
||||
rightEndContents,
|
||||
}: EditableChipProps) {
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const [inputValue, setInputValue] = useState(value);
|
||||
|
||||
const handleRightEndContentClick = (
|
||||
event: React.MouseEvent<HTMLDivElement>,
|
||||
) => {
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
return (
|
||||
<EditableCell
|
||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||
editModeContent={
|
||||
<StyledInplaceInput
|
||||
placeholder={placeholder || ''}
|
||||
autoFocus
|
||||
ref={inputRef}
|
||||
value={inputValue}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
setInputValue(event.target.value);
|
||||
changeHandler(event.target.value);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
nonEditModeContent={
|
||||
<NoEditModeContainer>
|
||||
<ChipComponent name={inputValue} picture={picture} />
|
||||
<RightContainer>
|
||||
{rightEndContents &&
|
||||
rightEndContents.length > 0 &&
|
||||
rightEndContents.map((content, index) => (
|
||||
<div key={index} onClick={handleRightEndContentClick}>
|
||||
{content}
|
||||
</div>
|
||||
))}
|
||||
</RightContainer>
|
||||
</NoEditModeContainer>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditableChip;
|
||||
@ -0,0 +1,33 @@
|
||||
import { ComponentType, ReactNode } from 'react';
|
||||
|
||||
import { InplaceChipInput } from '../../inplace-input/types/InplaceChipInput';
|
||||
import { useIsSoftFocusOnCurrentCell } from '../hooks/useIsSoftFocusOnCurrentCell';
|
||||
import { useSetSoftFocusOnCurrentCell } from '../hooks/useSetSoftFocusOnCurrentCell';
|
||||
|
||||
export type OwnProps = {
|
||||
placeholder?: string;
|
||||
value: string;
|
||||
picture: string;
|
||||
changeHandler: (upChipd: string) => void;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
ChipComponent: ComponentType<{
|
||||
name: string;
|
||||
picture: string;
|
||||
isOverlapped?: boolean;
|
||||
}>;
|
||||
commentCount?: number;
|
||||
onCommentClick?: (event: React.MouseEvent<HTMLDivElement>) => void;
|
||||
rightEndContents?: ReactNode[];
|
||||
};
|
||||
|
||||
export function EditableChipCell(props: OwnProps) {
|
||||
const setSoftFocusOnCurrentCell = useSetSoftFocusOnCurrentCell();
|
||||
const hasSoftFocus = useIsSoftFocusOnCurrentCell();
|
||||
return (
|
||||
<InplaceChipInput
|
||||
{...props}
|
||||
setSoftFocusOnCurrentInplaceInput={setSoftFocusOnCurrentCell}
|
||||
hasSoftFocus={hasSoftFocus}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -1,81 +0,0 @@
|
||||
import { forwardRef, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { humanReadableDate } from '@/utils/utils';
|
||||
|
||||
import DatePicker from '../../form/DatePicker';
|
||||
import { EditableCell } from '../EditableCell';
|
||||
|
||||
export type EditableDateProps = {
|
||||
value: Date;
|
||||
changeHandler: (date: Date) => void;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
};
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin: 0px ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
export type StyledCalendarContainerProps = {
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
};
|
||||
|
||||
const StyledCalendarContainer = styled.div<StyledCalendarContainerProps>`
|
||||
background: ${({ theme }) => theme.background.secondary};
|
||||
border: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
border-radius: 8px;
|
||||
box-shadow: ${({ theme }) => theme.boxShadow.strong};
|
||||
left: -10px;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
z-index: 1;
|
||||
`;
|
||||
export function EditableDate({
|
||||
value,
|
||||
changeHandler,
|
||||
editModeHorizontalAlign,
|
||||
}: EditableDateProps) {
|
||||
const [inputValue, setInputValue] = useState(value);
|
||||
|
||||
type DivProps = React.HTMLProps<HTMLDivElement>;
|
||||
|
||||
const DateDisplay = forwardRef<HTMLDivElement, DivProps>(
|
||||
({ value, onClick }, ref) => (
|
||||
<div onClick={onClick} ref={ref}>
|
||||
{value && humanReadableDate(new Date(value as string))}
|
||||
</div>
|
||||
),
|
||||
);
|
||||
|
||||
type DatePickerContainerProps = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const DatePickerContainer = ({ children }: DatePickerContainerProps) => {
|
||||
return <StyledCalendarContainer>{children}</StyledCalendarContainer>;
|
||||
};
|
||||
|
||||
return (
|
||||
<EditableCell
|
||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||
editModeContent={
|
||||
<StyledContainer>
|
||||
<DatePicker
|
||||
date={inputValue}
|
||||
onChangeHandler={(date: Date) => {
|
||||
changeHandler(date);
|
||||
setInputValue(date);
|
||||
}}
|
||||
customInput={<DateDisplay />}
|
||||
customCalendarContainer={DatePickerContainer}
|
||||
/>
|
||||
</StyledContainer>
|
||||
}
|
||||
nonEditModeContent={
|
||||
<div>{inputValue && humanReadableDate(inputValue)}</div>
|
||||
}
|
||||
></EditableCell>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
import { InplaceDateInput } from '../../inplace-input/types/InplaceDateInput';
|
||||
import { useIsSoftFocusOnCurrentCell } from '../hooks/useIsSoftFocusOnCurrentCell';
|
||||
import { useSetSoftFocusOnCurrentCell } from '../hooks/useSetSoftFocusOnCurrentCell';
|
||||
|
||||
type OwnProps = {
|
||||
placeholder?: string;
|
||||
value: Date;
|
||||
changeHandler: (date: Date) => void;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
};
|
||||
|
||||
export function EditableDateCell(props: OwnProps) {
|
||||
const setSoftFocusOnCurrentCell = useSetSoftFocusOnCurrentCell();
|
||||
const hasSoftFocus = useIsSoftFocusOnCurrentCell();
|
||||
return (
|
||||
<InplaceDateInput
|
||||
{...props}
|
||||
setSoftFocusOnCurrentInplaceInput={setSoftFocusOnCurrentCell}
|
||||
hasSoftFocus={hasSoftFocus}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -1,72 +0,0 @@
|
||||
import { ChangeEvent, ReactElement, useRef } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { textInputStyle } from '@/ui/themes/effects';
|
||||
|
||||
import { EditableCell } from '../EditableCell';
|
||||
|
||||
type OwnProps = {
|
||||
firstValue: string;
|
||||
secondValue: string;
|
||||
firstValuePlaceholder: string;
|
||||
secondValuePlaceholder: string;
|
||||
nonEditModeContent: ReactElement;
|
||||
onChange: (firstValue: string, secondValue: string) => void;
|
||||
};
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
& > input:last-child {
|
||||
border-left: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
padding-left: ${({ theme }) => theme.spacing(2)};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledEditInplaceInput = styled.input`
|
||||
height: 18px;
|
||||
margin: 0;
|
||||
width: 45%;
|
||||
|
||||
${textInputStyle}
|
||||
`;
|
||||
|
||||
export function EditableDoubleText({
|
||||
firstValue,
|
||||
secondValue,
|
||||
firstValuePlaceholder,
|
||||
secondValuePlaceholder,
|
||||
nonEditModeContent,
|
||||
onChange,
|
||||
}: OwnProps) {
|
||||
const firstValueInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
return (
|
||||
<EditableCell
|
||||
editModeContent={
|
||||
<StyledContainer>
|
||||
<StyledEditInplaceInput
|
||||
autoFocus
|
||||
placeholder={firstValuePlaceholder}
|
||||
ref={firstValueInputRef}
|
||||
value={firstValue}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
onChange(event.target.value, secondValue);
|
||||
}}
|
||||
/>
|
||||
<StyledEditInplaceInput
|
||||
placeholder={secondValuePlaceholder}
|
||||
ref={firstValueInputRef}
|
||||
value={secondValue}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
onChange(firstValue, event.target.value);
|
||||
}}
|
||||
/>
|
||||
</StyledContainer>
|
||||
}
|
||||
nonEditModeContent={nonEditModeContent}
|
||||
></EditableCell>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
import { ReactElement } from 'react';
|
||||
|
||||
import { InplaceDoubleTextInput } from '../../inplace-input/types/InplaceDoubleTextInput';
|
||||
import { useIsSoftFocusOnCurrentCell } from '../hooks/useIsSoftFocusOnCurrentCell';
|
||||
import { useSetSoftFocusOnCurrentCell } from '../hooks/useSetSoftFocusOnCurrentCell';
|
||||
|
||||
type OwnProps = {
|
||||
firstValue: string;
|
||||
secondValue: string;
|
||||
firstValuePlaceholder: string;
|
||||
secondValuePlaceholder: string;
|
||||
nonEditModeContent: ReactElement;
|
||||
onChange: (firstValue: string, secondValue: string) => void;
|
||||
};
|
||||
|
||||
export function EditableDoubleTextCell(props: OwnProps) {
|
||||
const setSoftFocusOnCurrentCell = useSetSoftFocusOnCurrentCell();
|
||||
const hasSoftFocus = useIsSoftFocusOnCurrentCell();
|
||||
return (
|
||||
<InplaceDoubleTextInput
|
||||
{...props}
|
||||
setSoftFocusOnCurrentInplaceInput={setSoftFocusOnCurrentCell}
|
||||
hasSoftFocus={hasSoftFocus}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -1,70 +0,0 @@
|
||||
import { ChangeEvent, MouseEvent, useRef, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { isValidPhoneNumber, parsePhoneNumber } from 'libphonenumber-js';
|
||||
|
||||
import { textInputStyle } from '@/ui/themes/effects';
|
||||
|
||||
import { RawLink } from '../../links/RawLink';
|
||||
import { EditableCell } from '../EditableCell';
|
||||
|
||||
type OwnProps = {
|
||||
placeholder?: string;
|
||||
value: string;
|
||||
changeHandler: (updated: string) => void;
|
||||
};
|
||||
|
||||
const StyledRawLink = styled(RawLink)`
|
||||
overflow: hidden;
|
||||
|
||||
a {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
`;
|
||||
|
||||
// TODO: refactor
|
||||
const StyledEditInplaceInput = styled.input`
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
${textInputStyle}
|
||||
`;
|
||||
|
||||
export function EditablePhone({ value, placeholder, changeHandler }: OwnProps) {
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const [inputValue, setInputValue] = useState(value);
|
||||
|
||||
return (
|
||||
<EditableCell
|
||||
editModeContent={
|
||||
<StyledEditInplaceInput
|
||||
autoFocus
|
||||
placeholder={placeholder || ''}
|
||||
ref={inputRef}
|
||||
value={inputValue}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
setInputValue(event.target.value);
|
||||
changeHandler(event.target.value);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
nonEditModeContent={
|
||||
<>
|
||||
{isValidPhoneNumber(inputValue) ? (
|
||||
<StyledRawLink
|
||||
href={parsePhoneNumber(inputValue, 'FR')?.getURI()}
|
||||
onClick={(event: MouseEvent<HTMLElement>) => {
|
||||
event.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{parsePhoneNumber(inputValue, 'FR')?.formatInternational() ||
|
||||
inputValue}
|
||||
</StyledRawLink>
|
||||
) : (
|
||||
<StyledRawLink href="#">{inputValue}</StyledRawLink>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
import { InplacePhoneInput } from '../../inplace-input/types/InplacePhoneInput';
|
||||
import { useIsSoftFocusOnCurrentCell } from '../hooks/useIsSoftFocusOnCurrentCell';
|
||||
import { useSetSoftFocusOnCurrentCell } from '../hooks/useSetSoftFocusOnCurrentCell';
|
||||
|
||||
type OwnProps = {
|
||||
placeholder?: string;
|
||||
value: string;
|
||||
changeHandler: (Phone: string) => void;
|
||||
};
|
||||
|
||||
export function EditablePhoneCell(props: OwnProps) {
|
||||
const setSoftFocusOnCurrentCell = useSetSoftFocusOnCurrentCell();
|
||||
const hasSoftFocus = useIsSoftFocusOnCurrentCell();
|
||||
return (
|
||||
<InplacePhoneInput
|
||||
{...props}
|
||||
setSoftFocusOnCurrentInplaceInput={setSoftFocusOnCurrentCell}
|
||||
hasSoftFocus={hasSoftFocus}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
export const EditableRelationCreateButton = styled.button`
|
||||
align-items: center;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
font-family: 'Inter';
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
height: 31px;
|
||||
padding-bottom: ${({ theme }) => theme.spacing(1)};
|
||||
padding-left: ${({ theme }) => theme.spacing(1)};
|
||||
padding-top: ${({ theme }) => theme.spacing(1)};
|
||||
user-select: none;
|
||||
width: 100%;
|
||||
`;
|
||||
@ -9,20 +9,12 @@ type OwnProps = {
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
};
|
||||
|
||||
export function EditableTextCell({
|
||||
editModeHorizontalAlign = 'left',
|
||||
content,
|
||||
changeHandler,
|
||||
placeholder,
|
||||
}: OwnProps) {
|
||||
export function EditableTextCell(props: OwnProps) {
|
||||
const setSoftFocusOnCurrentCell = useSetSoftFocusOnCurrentCell();
|
||||
const hasSoftFocus = useIsSoftFocusOnCurrentCell();
|
||||
return (
|
||||
<InplaceTextInput
|
||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||
content={content}
|
||||
changeHandler={changeHandler}
|
||||
placeholder={placeholder}
|
||||
{...props}
|
||||
setSoftFocusOnCurrentInplaceInput={setSoftFocusOnCurrentCell}
|
||||
hasSoftFocus={hasSoftFocus}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user