Proposal Date picker overflow (#4996)

Unfortunately, it is not possible in CSS to have an overflow:visible
over x-axis while having an overflow:hidden over y-axis, leading to the
following issue:

<img width="1512" alt="image"
src="https://github.com/twentyhq/twenty/assets/12035771/9b84cbbb-c6c4-4fd6-a630-a24f01eccf73">

I'm refactoring the RecordInlineCell and RecordTableCell to use
useFloating + createPortal to open the cell.
This commit is contained in:
Charles Bochet
2024-04-17 11:35:45 +02:00
committed by GitHub
parent 340af9a244
commit 67db7d85c0
14 changed files with 154 additions and 161 deletions

View File

@ -1,7 +1,5 @@
import { RefObject, useEffect, useRef, useState } from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { flip, offset, useFloating } from '@floating-ui/react';
import { Key } from 'ts-key-enum';
import { FieldAddressDraftValue } from '@/object-record/record-field/types/FieldInputDraftValue';
@ -57,8 +55,6 @@ export const AddressInput = ({
onClickOutside,
onChange,
}: AddressInputProps) => {
const theme = useTheme();
const [internalValue, setInternalValue] = useState(value);
const addressStreet1InputRef = useRef<HTMLInputElement>(null);
const addressStreet2InputRef = useRef<HTMLInputElement>(null);
@ -81,16 +77,6 @@ export const AddressInput = ({
const wrapperRef = useRef<HTMLDivElement>(null);
const { refs, floatingStyles } = useFloating({
placement: 'top-start',
middleware: [
flip(),
offset({
mainAxis: theme.spacingMultiplicator * 2,
}),
],
});
const getChangeHandler =
(field: keyof FieldAddressDraftValue) => (updatedAddressPart: string) => {
const updatedAddress = { ...value, [field]: updatedAddressPart };
@ -192,63 +178,61 @@ export const AddressInput = ({
}, [value]);
return (
<div ref={refs.setFloating} style={floatingStyles}>
<StyledAddressContainer ref={wrapperRef}>
<StyledAddressContainer ref={wrapperRef}>
<TextInput
autoFocus
value={internalValue.addressStreet1 ?? ''}
ref={inputRefs['addressStreet1']}
label="ADDRESS 1"
fullWidth
onChange={getChangeHandler('addressStreet1')}
onFocus={getFocusHandler('addressStreet1')}
disableHotkeys
/>
<TextInput
value={internalValue.addressStreet2 ?? ''}
ref={inputRefs['addressStreet2']}
label="ADDRESS 2"
fullWidth
onChange={getChangeHandler('addressStreet2')}
onFocus={getFocusHandler('addressStreet2')}
disableHotkeys
/>
<StyledHalfRowContainer>
<TextInput
autoFocus
value={internalValue.addressStreet1 ?? ''}
ref={inputRefs['addressStreet1']}
label="ADDRESS 1"
value={internalValue.addressCity ?? ''}
ref={inputRefs['addressCity']}
label="CITY"
fullWidth
onChange={getChangeHandler('addressStreet1')}
onFocus={getFocusHandler('addressStreet1')}
onChange={getChangeHandler('addressCity')}
onFocus={getFocusHandler('addressCity')}
disableHotkeys
/>
<TextInput
value={internalValue.addressStreet2 ?? ''}
ref={inputRefs['addressStreet2']}
label="ADDRESS 2"
value={internalValue.addressState ?? ''}
ref={inputRefs['addressState']}
label="STATE"
fullWidth
onChange={getChangeHandler('addressStreet2')}
onFocus={getFocusHandler('addressStreet2')}
onChange={getChangeHandler('addressState')}
onFocus={getFocusHandler('addressState')}
disableHotkeys
/>
<StyledHalfRowContainer>
<TextInput
value={internalValue.addressCity ?? ''}
ref={inputRefs['addressCity']}
label="CITY"
fullWidth
onChange={getChangeHandler('addressCity')}
onFocus={getFocusHandler('addressCity')}
disableHotkeys
/>
<TextInput
value={internalValue.addressState ?? ''}
ref={inputRefs['addressState']}
label="STATE"
fullWidth
onChange={getChangeHandler('addressState')}
onFocus={getFocusHandler('addressState')}
disableHotkeys
/>
</StyledHalfRowContainer>
<StyledHalfRowContainer>
<TextInput
value={internalValue.addressPostcode ?? ''}
ref={inputRefs['addressPostcode']}
label="POST CODE"
fullWidth
onChange={getChangeHandler('addressPostcode')}
onFocus={getFocusHandler('addressPostcode')}
disableHotkeys
/>
<CountrySelect
onChange={getChangeHandler('addressCountry')}
selectedCountryName={internalValue.addressCountry ?? ''}
/>
</StyledHalfRowContainer>
</StyledAddressContainer>
</div>
</StyledHalfRowContainer>
<StyledHalfRowContainer>
<TextInput
value={internalValue.addressPostcode ?? ''}
ref={inputRefs['addressPostcode']}
label="POST CODE"
fullWidth
onChange={getChangeHandler('addressPostcode')}
onFocus={getFocusHandler('addressPostcode')}
disableHotkeys
/>
<CountrySelect
onChange={getChangeHandler('addressCountry')}
selectedCountryName={internalValue.addressCountry ?? ''}
/>
</StyledHalfRowContainer>
</StyledAddressContainer>
);
};

View File

@ -1,10 +1,7 @@
import { useRef, useState } from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { flip, offset, useFloating } from '@floating-ui/react';
import { Nullable } from 'twenty-ui';
import { DateDisplay } from '@/ui/field/display/components/DateDisplay';
import { InternalDatePicker } from '@/ui/input/components/internal/date/components/InternalDatePicker';
import {
MONTH_AND_YEAR_DROPDOWN_ID,
@ -19,16 +16,9 @@ const StyledCalendarContainer = styled.div`
border: 1px solid ${({ theme }) => theme.border.color.light};
border-radius: ${({ theme }) => theme.border.radius.md};
box-shadow: ${({ theme }) => theme.boxShadow.strong};
top: 0;
position: absolute;
z-index: 1;
`;
const StyledInputContainer = styled.div`
padding: ${({ theme }) => theme.spacing(0)} ${({ theme }) => theme.spacing(2)};
width: 100%;
`;
export type DateInputProps = {
@ -55,22 +45,10 @@ export const DateInput = ({
isDateTimeInput,
onClear,
}: DateInputProps) => {
const theme = useTheme();
const [internalValue, setInternalValue] = useState(value);
const wrapperRef = useRef<HTMLDivElement>(null);
const { refs, floatingStyles } = useFloating({
placement: 'bottom-start',
middleware: [
flip(),
offset({
mainAxis: theme.spacingMultiplicator * -6,
}),
],
});
const handleChange = (newDate: Date | null) => {
setInternalValue(newDate);
onChange?.(newDate);
@ -104,27 +82,20 @@ export const DateInput = ({
return (
<div ref={wrapperRef}>
<div ref={refs.setReference}>
<StyledInputContainer>
<DateDisplay value={internalValue ?? new Date()} />
</StyledInputContainer>
</div>
<div ref={refs.setFloating} style={floatingStyles}>
<StyledCalendarContainer>
<InternalDatePicker
date={internalValue ?? new Date()}
onChange={handleChange}
onMouseSelect={(newDate: Date | null) => {
onEnter(newDate);
}}
clearable={clearable ? clearable : false}
isDateTimeInput={isDateTimeInput}
onEnter={onEnter}
onEscape={onEscape}
onClear={handleClear}
/>
</StyledCalendarContainer>
</div>
<StyledCalendarContainer>
<InternalDatePicker
date={internalValue ?? new Date()}
onChange={handleChange}
onMouseSelect={(newDate: Date | null) => {
onEnter(newDate);
}}
clearable={clearable ? clearable : false}
isDateTimeInput={isDateTimeInput}
onEnter={onEnter}
onEscape={onEscape}
onClear={handleClear}
/>
</StyledCalendarContainer>
</div>
);
};

View File

@ -20,7 +20,6 @@ const StyledInnerContainer = styled.div`
display: flex;
flex-direction: column;
width: ${() => (useIsMobile() ? `100%` : '348px')};
overflow-x: hidden;
`;
const StyledIntermediateContainer = styled.div`